Wprowadzenie do biblioteki interfejsu użytkownika usług Azure Communication Services wywołującej aplikacje Teams Voice Apps
Ten projekt ma na celu poprowadzenie deweloperów w celu zainicjowania wywołania z usług Azure Communication Services wywołujących internetowy zestaw SDK do kolejki wywołań usługi Teams i automatycznego uczestnictwa przy użyciu biblioteki interfejsu użytkownika usługi Azure Communication.
Zgodnie z wymaganiami może być konieczne zaoferowanie klientom łatwego sposobu dotarcia do Ciebie bez żadnej złożonej konfiguracji.
Wywoływanie kolejki połączeń w usłudze Teams i automatycznej obecności to prosta, ale skuteczna koncepcja, która ułatwia natychmiastową interakcję z pomocą techniczną klienta, doradcą finansowym i innymi zespołami dostępnymi dla klientów. Celem tego samouczka jest pomoc w inicjowaniu interakcji z klientami po kliknięciu przycisku w Internecie.
Jeśli chcesz go wypróbować, możesz pobrać kod z usługi GitHub.
W tym samouczku zostaną wykonane następujące czynności:
- Umożliwia kontrolowanie środowiska audio i wideo klientów w zależności od scenariusza klienta
- Dowiedz się, jak utworzyć widżet do uruchamiania wywołań w aplikacji internetowej przy użyciu biblioteki interfejsu użytkownika.
Wymagania wstępne
Te kroki są wymagane , aby wykonać czynności opisane w tym samouczku. Skontaktuj się z administratorem usługi Teams, aby uzyskać informacje o dwóch ostatnich elementach, aby upewnić się, że zostały skonfigurowane odpowiednio.
- Program Visual Studio Code na jednej z obsługiwanych platform.
- zaleca się Node.js, zalecane jest aktywne LTS (obsługa długoterminowa) i wersje node 20. Użyj polecenia ,
node --version
aby sprawdzić wersję. - Zasób usług Azure Communication Services. Tworzenie zasobu komunikacji
- Ukończ konfigurację dzierżawy usługi Teams na potrzeby współdziałania z zasobem usług Azure Communication Services
- Praca z kolejkami połączeń usługi Teams i usługami Azure Communication Services.
- Praca z automatycznymi uczestnikami usługi Teams i usługami Azure Communication Services.
Sprawdzanie węzłów i programu Visual Studio Code
Możesz sprawdzić, czy węzeł został poprawnie zainstalowany za pomocą tego polecenia.
node -v
Dane wyjściowe informują o posiadanej wersji, ale kończy się niepowodzeniem, jeśli węzeł nie został zainstalowany i dodany do elementu PATH
. Podobnie jak w przypadku środowiska Node możesz sprawdzić, czy program VS Code został zainstalowany za pomocą tego polecenia.
code --version
Podobnie jak w przypadku środowiska Node to polecenie kończy się niepowodzeniem, jeśli na maszynie wystąpił problem z instalacją programu VS Code.
Wprowadzenie
Ten samouczek zawiera 7 kroków, a na końcu aplikacja będzie mogła wywoływać aplikację głosową usługi Teams. Kroki to:
- Konfigurowanie projektu
- Pobieranie zależności
- Początkowa konfiguracja aplikacji
- Tworzenie widżetu
- Styl widżetu
- Konfigurowanie wartości tożsamości
- Uruchamianie aplikacji
1. Konfigurowanie projektu
Użyj tego kroku tylko wtedy, gdy tworzysz nową aplikację.
Aby skonfigurować aplikację react, użyjemy create-react-app
narzędzia wiersza polecenia. To narzędzie tworzy łatwą do uruchomienia aplikację TypeScript obsługiwaną przez platformę React.
Aby upewnić się, że na maszynie zainstalowano środowisko Node, uruchom to polecenie w programie PowerShell lub terminalu, aby wyświetlić wersję środowiska Node:
node -v
Jeśli na maszynie nie zainstalowano create-react-app
programu , uruchom następujące polecenie, aby zainstalować je jako polecenie globalne:
npm install -g create-react-app
Po zainstalowaniu tego polecenia uruchom następujące następne polecenie, aby utworzyć nową aplikację react w celu skompilowania przykładu w programie :
# Create an Azure Communication Services App powered by React.
npx create-react-app ui-library-calling-widget-app --template typescript
# Change to the directory of the newly created App.
cd ui-library-calling-widget-app
Po zakończeniu tych poleceń chcesz otworzyć utworzony projekt w programie VS Code. Projekt można otworzyć za pomocą następującego polecenia.
code .
2. Pobieranie zależności
Następnie należy zaktualizować tablicę zależności w pliku , package.json
aby uwzględnić niektóre pakiety z usług Azure Communication Services na potrzeby środowiska widżetu, które będziemy kompilować, aby działać:
"@azure/communication-calling": "^1.23.1",
"@azure/communication-chat": "^1.4.0",
"@azure/communication-react": "^1.15.0",
"@azure/communication-calling-effects": "1.0.1",
"@azure/communication-common": "2.3.0",
"@fluentui/react-icons": "~2.0.203",
"@fluentui/react": "~8.98.3",
Aby zainstalować wymagane pakiety, uruchom następujące polecenie Node Menedżer pakietów.
npm install
Po zainstalowaniu tych pakietów wszystko jest gotowe do rozpoczęcia pisania kodu, który kompiluje aplikację. W tym samouczku modyfikujemy pliki w src
katalogu.
3. Początkowa konfiguracja aplikacji
Aby rozpocząć, zastąp podaną App.tsx
zawartość stroną główną, która będzie:
- Przechowuj wszystkie informacje o komunikacji platformy Azure, które musimy utworzyć obiekt CallAdapter, aby zapewnić nasze środowisko wywoływania
- Wyświetl nasz widżet, który jest udostępniany użytkownikowi końcowemu.
Plik App.tsx
powinien wyglądać następująco:
src/App.tsx
import "./App.css";
import {
CommunicationIdentifier,
MicrosoftTeamsAppIdentifier,
} from "@azure/communication-common";
import {
Spinner,
Stack,
initializeIcons,
registerIcons,
Text,
} from "@fluentui/react";
import { CallAdd20Regular, Dismiss20Regular } from "@fluentui/react-icons";
import logo from "./logo.svg";
import { CallingWidgetComponent } from "./components/CallingWidgetComponent";
registerIcons({
icons: { dismiss: <Dismiss20Regular />, callAdd: <CallAdd20Regular /> },
});
initializeIcons();
function App() {
/**
* Token for local user.
*/
const token = "<Enter your ACS Token here>";
/**
* User identifier for local user.
*/
const userId: CommunicationIdentifier = {
communicationUserId: "Enter your ACS Id here",
};
/**
* Enter your Teams voice app identifier from the Teams admin center here
*/
const teamsAppIdentifier: MicrosoftTeamsAppIdentifier = {
teamsAppId: "<Enter your Teams Voice app id here>",
cloud: "public",
};
const widgetParams = {
userId,
token,
teamsAppIdentifier,
};
if (!token || !userId || !teamsAppIdentifier) {
return (
<Stack verticalAlign="center" style={{ height: "100%", width: "100%" }}>
<Spinner
label={"Getting user credentials from server"}
ariaLive="assertive"
labelPosition="top"
/>
</Stack>
);
}
return (
<Stack
style={{ height: "100%", width: "100%", padding: "3rem" }}
tokens={{ childrenGap: "1.5rem" }}
>
<Stack tokens={{ childrenGap: "1rem" }} style={{ margin: "auto" }}>
<Stack
style={{ padding: "3rem" }}
horizontal
tokens={{ childrenGap: "2rem" }}
>
<Text style={{ marginTop: "auto" }} variant="xLarge">
Welcome to a Calling Widget sample
</Text>
<img
style={{ width: "7rem", height: "auto" }}
src={logo}
alt="logo"
/>
</Stack>
<Text>
Welcome to a Calling Widget sample for the Azure Communication
Services UI Library. Sample has the ability to connect you through
Teams voice apps to a agent to help you.
</Text>
<Text>
As a user all you need to do is click the widget below, enter your
display name for the call - this will act as your caller id, and
action the <b>start call</b> button.
</Text>
</Stack>
<Stack
horizontal
tokens={{ childrenGap: "1.5rem" }}
style={{ overflow: "hidden", margin: "auto" }}
>
<CallingWidgetComponent
widgetAdapterArgs={widgetParams}
onRenderLogo={() => {
return (
<img
style={{ height: "4rem", width: "4rem", margin: "auto" }}
src={logo}
alt="logo"
/>
);
}}
/>
</Stack>
</Stack>
);
}
export default App;
W tym fragmencie kodu rejestrujemy dwie nowe ikony <Dismiss20Regular/>
i <CallAdd20Regular>
. Te nowe ikony są używane wewnątrz składnika widżetu, który tworzymy w następnej sekcji.
4. Tworzenie widżetu
Teraz musimy utworzyć widżet, który może być wyświetlany w trzech różnych trybach:
- Oczekiwanie: ten stan widżetu to sposób, w jaki składnik będzie znajdować się przed wywołaniem i po nim
- Konfiguracja: ten stan jest taki, gdy widżet prosi o informacje od użytkownika, takie jak jego nazwa.
- W wywołaniu: widżet został zastąpiony tutaj biblioteką interfejsu użytkownika Call Composite. Ten tryb widżetu jest wtedy, gdy użytkownik wywołuje aplikację Voice lub rozmawia z agentem.
Umożliwia utworzenie folderu o nazwie src/components
. W tym folderze utwórz nowy plik o nazwie CallingWidgetComponent.tsx
. Ten plik powinien wyglądać podobnie do następującego fragmentu kodu:
CallingWidgetComponent.tsx
import {
IconButton,
PrimaryButton,
Stack,
TextField,
useTheme,
Checkbox,
Icon,
Spinner,
} from "@fluentui/react";
import React, { useEffect, useRef, useState } from "react";
import {
callingWidgetSetupContainerStyles,
checkboxStyles,
startCallButtonStyles,
callingWidgetContainerStyles,
callIconStyles,
logoContainerStyles,
collapseButtonStyles,
} from "../styles/CallingWidgetComponent.styles";
import {
AzureCommunicationTokenCredential,
CommunicationUserIdentifier,
MicrosoftTeamsAppIdentifier,
} from "@azure/communication-common";
import {
CallAdapter,
CallAdapterState,
CallComposite,
CommonCallAdapterOptions,
StartCallIdentifier,
createAzureCommunicationCallAdapter,
} from "@azure/communication-react";
// lets add to our react imports as well
import { useMemo } from "react";
import { callingWidgetInCallContainerStyles } from "../styles/CallingWidgetComponent.styles";
/**
* Properties needed for our widget to start a call.
*/
export type WidgetAdapterArgs = {
token: string;
userId: CommunicationUserIdentifier;
teamsAppIdentifier: MicrosoftTeamsAppIdentifier;
};
export interface CallingWidgetComponentProps {
/**
* arguments for creating an AzureCommunicationCallAdapter for your Calling experience
*/
widgetAdapterArgs: WidgetAdapterArgs;
/**
* Custom render function for displaying logo.
* @returns
*/
onRenderLogo?: () => JSX.Element;
}
/**
* Widget for Calling Widget
* @param props
*/
export const CallingWidgetComponent = (
props: CallingWidgetComponentProps
): JSX.Element => {
const { onRenderLogo, widgetAdapterArgs } = props;
const [widgetState, setWidgetState] = useState<"new" | "setup" | "inCall">(
"new"
);
const [displayName, setDisplayName] = useState<string>();
const [consentToData, setConsentToData] = useState<boolean>(false);
const [useLocalVideo, setUseLocalVideo] = useState<boolean>(false);
const [adapter, setAdapter] = useState<CallAdapter>();
const callIdRef = useRef<string>();
const theme = useTheme();
// add this before the React template
const credential = useMemo(() => {
try {
return new AzureCommunicationTokenCredential(widgetAdapterArgs.token);
} catch {
console.error("Failed to construct token credential");
return undefined;
}
}, [widgetAdapterArgs.token]);
const adapterOptions: CommonCallAdapterOptions = useMemo(
() => ({
callingSounds: {
callEnded: { url: "/sounds/callEnded.mp3" },
callRinging: { url: "/sounds/callRinging.mp3" },
callBusy: { url: "/sounds/callBusy.mp3" },
},
}),
[]
);
const callAdapterArgs = useMemo(() => {
return {
userId: widgetAdapterArgs.userId,
credential: credential,
targetCallees: [
widgetAdapterArgs.teamsAppIdentifier,
] as StartCallIdentifier[],
displayName: displayName,
options: adapterOptions,
};
}, [
widgetAdapterArgs.userId,
widgetAdapterArgs.teamsAppIdentifier.teamsAppId,
credential,
displayName,
]);
useEffect(() => {
if (adapter) {
adapter.on("callEnded", () => {
/**
* We only want to reset the widget state if the call that ended is the same as the current call.
*/
if (
adapter.getState().acceptedTransferCallState &&
adapter.getState().acceptedTransferCallState?.id !== callIdRef.current
) {
return;
}
setDisplayName(undefined);
setWidgetState("new");
setConsentToData(false);
setAdapter(undefined);
adapter.dispose();
});
adapter.on("transferAccepted", (e) => {
console.log("transferAccepted", e);
});
adapter.onStateChange((state: CallAdapterState) => {
if (state?.call?.id && callIdRef.current !== state?.call?.id) {
callIdRef.current = state?.call?.id;
console.log(`Call Id: ${callIdRef.current}`);
}
});
}
}, [adapter]);
/** widget template for when widget is open, put any fields here for user information desired */
if (widgetState === "setup") {
return (
<Stack
styles={callingWidgetSetupContainerStyles(theme)}
tokens={{ childrenGap: "1rem" }}
>
<IconButton
styles={collapseButtonStyles}
iconProps={{ iconName: "Dismiss" }}
onClick={() => {
setDisplayName(undefined);
setConsentToData(false);
setUseLocalVideo(false);
setWidgetState("new");
}}
/>
<Stack tokens={{ childrenGap: "1rem" }} styles={logoContainerStyles}>
<Stack style={{ transform: "scale(1.8)" }}>
{onRenderLogo && onRenderLogo()}
</Stack>
</Stack>
<TextField
label={"Name"}
required={true}
placeholder={"Enter your name"}
onChange={(_, newValue) => {
setDisplayName(newValue);
}}
/>
<Checkbox
styles={checkboxStyles(theme)}
label={
"Use video - Checking this box will enable camera controls and screen sharing"
}
onChange={(_, checked?: boolean | undefined) => {
setUseLocalVideo(!!checked);
setUseLocalVideo(true);
}}
></Checkbox>
<Checkbox
required={true}
styles={checkboxStyles(theme)}
disabled={displayName === undefined}
label={
"By checking this box, you are consenting that we will collect data from the call for customer support reasons"
}
onChange={async (_, checked?: boolean | undefined) => {
setConsentToData(!!checked);
if (callAdapterArgs && callAdapterArgs.credential) {
setAdapter(
await createAzureCommunicationCallAdapter({
displayName: displayName ?? "",
userId: callAdapterArgs.userId,
credential: callAdapterArgs.credential,
targetCallees: callAdapterArgs.targetCallees,
options: callAdapterArgs.options,
})
);
}
}}
></Checkbox>
<PrimaryButton
styles={startCallButtonStyles(theme)}
onClick={() => {
if (displayName && consentToData && adapter) {
setWidgetState("inCall");
adapter?.startCall(callAdapterArgs.targetCallees, {
audioOptions: { muted: false },
});
}
}}
>
{!consentToData && `Enter your name`}
{consentToData && !adapter && (
<Spinner ariaLive="assertive" labelPosition="top" />
)}
{consentToData && adapter && `StartCall`}
</PrimaryButton>
</Stack>
);
}
if (widgetState === "inCall" && adapter) {
return (
<Stack styles={callingWidgetInCallContainerStyles(theme)}>
<CallComposite
adapter={adapter}
options={{
callControls: {
cameraButton: useLocalVideo,
screenShareButton: useLocalVideo,
moreButton: false,
peopleButton: false,
displayType: "compact",
},
localVideoTile: !useLocalVideo ? false : { position: "floating" },
}}
/>
</Stack>
);
}
return (
<Stack
horizontalAlign="center"
verticalAlign="center"
styles={callingWidgetContainerStyles(theme)}
onClick={() => {
setWidgetState("setup");
}}
>
<Stack
horizontalAlign="center"
verticalAlign="center"
style={{
height: "4rem",
width: "4rem",
borderRadius: "50%",
background: theme.palette.themePrimary,
}}
>
<Icon iconName="callAdd" styles={callIconStyles(theme)} />
</Stack>
</Stack>
);
};
W pliku CallAdapterOptions
zobaczymy niektóre pliki dźwiękowe, do których odwołuje się plik , te pliki służą do używania funkcji Wywoływanie dźwięków w systemie CallComposite
. Jeśli interesuje Cię używanie dźwięków, zobacz ukończony kod, aby pobrać pliki dźwiękowe.
5. Styl widżetu
Musimy napisać kilka stylów, aby upewnić się, że widżet wygląda odpowiednio i może przechowywać nasze wywołania złożone. Te style powinny być już używane w widżecie, jeśli skopiowaliśmy fragment kodu dodany do pliku CallingWidgetComponent.tsx
.
Utwórzmy nowy folder o nazwie src/styles
w tym folderze, utwórz plik o nazwie CallingWidgetComponent.styles.ts
. Plik powinien wyglądać podobnie do następującego fragmentu kodu:
import {
IButtonStyles,
ICheckboxStyles,
IIconStyles,
IStackStyles,
Theme,
} from "@fluentui/react";
export const checkboxStyles = (theme: Theme): ICheckboxStyles => {
return {
label: {
color: theme.palette.neutralPrimary,
},
};
};
export const callingWidgetContainerStyles = (theme: Theme): IStackStyles => {
return {
root: {
width: "5rem",
height: "5rem",
padding: "0.5rem",
boxShadow: theme.effects.elevation16,
borderRadius: "50%",
bottom: "1rem",
right: "1rem",
position: "absolute",
overflow: "hidden",
cursor: "pointer",
":hover": {
boxShadow: theme.effects.elevation64,
},
},
};
};
export const callingWidgetSetupContainerStyles = (
theme: Theme
): IStackStyles => {
return {
root: {
width: "18rem",
minHeight: "20rem",
maxHeight: "25rem",
padding: "0.5rem",
boxShadow: theme.effects.elevation16,
borderRadius: theme.effects.roundedCorner6,
bottom: 0,
right: "1rem",
position: "absolute",
overflow: "hidden",
cursor: "pointer",
background: theme.palette.white,
},
};
};
export const callIconStyles = (theme: Theme): IIconStyles => {
return {
root: {
paddingTop: "0.2rem",
color: theme.palette.white,
transform: "scale(1.6)",
},
};
};
export const startCallButtonStyles = (theme: Theme): IButtonStyles => {
return {
root: {
background: theme.palette.themePrimary,
borderRadius: theme.effects.roundedCorner6,
borderColor: theme.palette.themePrimary,
},
textContainer: {
color: theme.palette.white,
},
};
};
export const logoContainerStyles: IStackStyles = {
root: {
margin: "auto",
padding: "0.2rem",
height: "5rem",
width: "10rem",
zIndex: 0,
},
};
export const collapseButtonStyles: IButtonStyles = {
root: {
position: "absolute",
top: "0.2rem",
right: "0.2rem",
zIndex: 1,
},
};
export const callingWidgetInCallContainerStyles = (
theme: Theme
): IStackStyles => {
return {
root: {
width: "35rem",
height: "25rem",
padding: "0.5rem",
boxShadow: theme.effects.elevation16,
borderRadius: theme.effects.roundedCorner6,
bottom: 0,
right: "1rem",
position: "absolute",
overflow: "hidden",
cursor: "pointer",
background: theme.semanticColors.bodyBackground,
},
};
};
6. Konfigurowanie wartości tożsamości
Zanim uruchomimy aplikację, przejdź do App.tsx
pozycji i zastąp wartości symboli zastępczych tożsamościami usług Azure Communication Services i identyfikatorem konta zasobu dla aplikacji Teams Voice. Poniżej przedstawiono wartości wejściowe dla elementów token
i userId
teamsAppIdentifier
.
./src/App.tsx
/**
* Token for local user.
*/
const token = "<Enter your ACS Token here>";
/**
* User identifier for local user.
*/
const userId: CommunicationIdentifier = {
communicationUserId: "Enter your ACS Id here",
};
/**
* Enter your Teams voice app identifier from the Teams admin center here
*/
const teamsAppIdentifier: MicrosoftTeamsAppIdentifier = {
teamsAppId: "<Enter your Teams Voice app id here>",
cloud: "public",
};
7. Uruchamianie aplikacji
Na koniec możemy uruchomić aplikację, aby wykonać nasze wywołania. Uruchom następujące polecenia, aby zainstalować nasze zależności i uruchomić naszą aplikację.
# Install the new dependencies
npm install
# run the React app
npm run start
Po uruchomieniu aplikacji możesz ją http://localhost:3000
zobaczyć w przeglądarce. Powinien zostać wyświetlony następujący ekran powitalny:
Następnie po akcji przycisku widżetu powinno zostać wyświetlone małe menu:
Po wypełnieniu nazwy kliknij przycisk Rozpocznij wywołanie, a wywołanie powinno się rozpocząć. Widżet powinien wyglądać następująco po uruchomieniu wywołania:
Następne kroki
Aby uzyskać więcej informacji na temat aplikacji głosowych usługi Teams, zapoznaj się z naszą dokumentacją dotyczącą automatycznych uczestników usługi Teams i kolejek połączeń usługi Teams. Możesz też zapoznać się z naszym samouczkiem dotyczącym tworzenia podobnego środowiska z pakietami języka JavaScript.
Szybki start: dołączanie aplikacji wywołującej do kolejki wywołań usługi Teams