Erste Schritte mit Anrufen der UI-Bibliothek für Azure Communication Services an Teams Voice Apps
Dieses Projekt zielt darauf ab, Entwickler*innen anzuleiten, einen Anruf aus dem Calling Web SDK von Azure Communication Services an die Teams-Anrufwarteschleife und automatische Telefonzentrale mithilfe der Azure Communication UI-Bibliothek zu initiieren.
Je nach Ihren Anforderungen müssen Sie Ihren Kunden möglicherweise eine einfache Möglichkeit bieten, Sie ohne komplizierte Einrichtung zu erreichen.
Anrufe an die Teams-Anrufwarteschleife und automatische Telefonzentrale sind ein einfaches, aber effektives Konzept, das die sofortige Interaktion mit Kundensupport, Finanzberater und anderen kundenorientierten Teams erleichtert. Das Ziel dieses Tutorials ist es, Sie beim Initiieren von Interaktionen mit Ihren Kunden zu unterstützen, wenn diese auf eine Schaltfläche im Web klicken.
Wenn Sie es ausprobieren möchten, können Sie den Code von GitHub herunterladen.
In diesem Tutorial werden folgende Schritte erläutert:
- Steuern der Audio- und Videoerfahrung Ihrer Kunden abhängig von Ihrem Kundenszenario
- Lernen Sie, wie Sie ein Widget zum Starten von Anrufen in Ihrer Webapp mithilfe der UI-Bibliothek erstellen.
Voraussetzungen
Diese Schritte sind erforderlich, um diesem Tutorial zu folgen. Wenden Sie sich für die letzten beiden Elemente an Ihren Teams-Administrator, um sicherzustellen, dass Sie entsprechend eingerichtet sind.
- Visual Studio Code auf einer der unterstützten Plattformen
- Node.js, Active LTS (Langzeitsupport) und Node 20 werden empfohlen. Verwenden Sie den Befehl
node --version
, um Ihre Version zu überprüfen. - Eine Azure Communication Services-Ressource. Eine Kommunikationsressource erstellen
- Führen Sie die Teams-Mandanteneinrichtung für Interoperabilität mit Ihrer Azure Communication Services-Ressource durch.
- Arbeiten mit Teams-Anrufwarteschleifen und Azure Communication Services.
- Arbeiten mit automatischen Teams-Telefonzentralen und Azure Communication Services.
Überprüfen auf Node und Visual Studio Code
Sie können überprüfen, ob Node mit diesem Befehl ordnungsgemäß installiert wurde.
node -v
Die Ausgabe informiert Sie über die Version, die Sie haben. Der Befehl schlägt fehl, wenn Node nicht installiert und zu PATH
hinzugefügt wurde. Genau wie bei Node können Sie mit diesem Befehl überprüfen, ob VS Code installiert wurde.
code --version
Wie bei Node schlägt dieser Befehl fehl, wenn ein Problem beim Installieren von VS Code auf Ihrem Computer aufgetreten ist.
Erste Schritte
Dieses Tutorial enthält 7 Schritte, und am Ende kann die App eine Teams-Sprachanwendung aufrufen. Führen Sie die folgenden Schritte durch:
- Einrichten des Projekts
- Abrufen der Abhängigkeiten
- Anfängliches App-Setup
- Erstellen des Widgets
- Formatieren des Widgets
- Einrichten von Identitätswerten
- Ausführen der App
1. Einrichten des Projekts
Verwenden Sie diesen Schritt nur, wenn Sie eine neue Anwendung erstellen.
Zum Einrichten der react-App verwenden wir das Befehlszeilentool create-react-app
. Dieses Tool erstellt eine einfach auszuführende TypeScript-Anwendung, die von React unterstützt wird.
Um sicherzustellen, dass Node auf Ihrem Computer installiert ist, führen Sie diesen Befehl in PowerShell oder dem Terminal aus, um Ihre Node-Version anzuzeigen:
node -v
Wenn create-react-app
auf Ihrem Computer nicht installiert ist, führen Sie den folgenden Befehl aus, um es als globalen Befehl zu installieren:
npm install -g create-react-app
Führen Sie nach der Installation dieses Befehls diesen nächsten Befehl aus, um eine neue React-Anwendung zu erstellen und das Beispiel zu erstellen in:
# 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
Nach Abschluss dieser Befehle sollten Sie das erstellte Projekt in VS Code öffnen. Sie können das Projekt mit dem folgenden Befehl öffnen.
code .
2. Abrufen der Abhängigkeiten
Anschließend müssen Sie das Abhängigkeitsarray in package.json
aktualisieren, um einige Pakete von Azure Communication Services einzuschließen, damit die geschaffene Widget-Darstellung funktioniert:
"@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",
Führen Sie den folgenden Node-Paket-Manager-Befehl aus, um die erforderlichen Pakete zu installieren.
npm install
Nachdem Sie diese Pakete installiert haben, müssen Sie mit dem Schreiben des Codes beginnen, der die Anwendung erstellt. In diesem Tutorial ändern wir die Dateien im Verzeichnis src
.
3. Anfängliches App-Setup
Um zu beginnen, ersetzen wir den bereitgestellten Inhalt von App.tsx
durch eine Hauptseite, die Folgendes leistet:
- Speichern aller Azure Communication-Informationen, die wir zum Erstellen eines CallAdapter-Objekts für unsere Anrufoberfläche benötigen
- Zeigen Sie unser Widget an, das dem Endbenutzer verfügbar gemacht wird.
Ihre Datei App.tsx
sollte wie folgt aussehen:
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;
In diesem Codeausschnitt registrieren Sie die beiden neuen Symbole <Dismiss20Regular/>
und <CallAdd20Regular>
. Diese neuen Symbole werden in der Widgetkomponente verwendet, die wir im nächsten Abschnitt erstellen.
4. Erstellen des Widgets
Jetzt müssen wir ein Widget erstellen, das in drei verschiedenen Modi angezeigt werden kann:
- Warten: Dieser Widgetstatus ist der Zustand, in dem sich die Komponente vor und nach dem Tätigen eines Anrufs befindet
- Setup: Dieser Zustand ist der Fall, wenn das Widget Informationen vom Benutzer wie seinen Namen anfragt.
- In einem Aufruf: Das Widget wird hier durch die UI-Bibliothek Call Composite ersetzt. Das ist der Widgetmodus, wenn der Benutzer die Voice-App anruft oder mit einem Agent spricht.
Erstellen wir einen Ordner mit dem Namen src/components
. Erstellen Sie in diesem Ordner eine neue Datei mit dem Namen CallingWidgetComponent.tsx
. Diese Datei sollte wie der folgende Codeschnipsel aussehen:
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>
);
};
In CallAdapterOptions
werden einige Audiodateien referenziert. Diese Dateien dienen der Verwendung der Funktion für Anrufsounds in CallComposite
. Wenn Sie an der Verwendung der Sounds interessiert sind, sehen Sie sich den vollständigen Code an, um die Audiodateien herunterzuladen.
5. Formatieren des Widgets
Wir müssen einige Stile schreiben, um sicherzustellen, dass das Widget angemessen aussieht und unseren zusammengesetzter Anruf darin Platz hat. Diese Stile sollten bereits im Widget verwendet werden, wenn Sie den Codeausschnitt kopieren, den wir zur Datei CallingWidgetComponent.tsx
hinzugefügt haben.
Jetzt erstellen Sie in diesem Ordner einen neuen Ordner namens src/styles
und dann eine Datei namens CallingWidgetComponent.styles.ts
. Die Datei sollte wie folgender Codeschnipsel aussehen:
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. Einrichten von Identitätswerten
Wechseln Sie vor dem Ausführen der App zu App.tsx
, und ersetzen Sie die Platzhalterwerte dort durch Ihre Azure Communication Services-Identitäten und den Bezeichner des Ressourcenkontos für Ihre Teams-Sprachanwendung. Hier sind Eingabewerte für token
, userId
und 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. Ausführen der App
Zu guter Letzt können wir die Anwendung ausführen, um unsere Anrufe zu tätigen! Führen Sie die folgenden Befehle aus, um unsere Abhängigkeiten zu installieren und unsere App auszuführen.
# Install the new dependencies
npm install
# run the React app
npm run start
Sobald die Anwendung ausgeführt wird, können Sie sie in Ihrem Browser auf http://localhost:3000
sehen. Der folgende Begrüßungsbildschirm sollte angezeigt werden:
Wenn Sie dann die Widgetschaltfläche ausführen, sollten Sie ein kleines Menü sehen:
Nachdem Sie den Namen ausgefüllt haben, klicken Sie auf „Anruf starten“, und der Anruf sollte beginnen. Das Widget sollte nach dem Starten eines Anrufs wie folgt aussehen:
Nächste Schritte
Weitere Informationen zu Teams-Voice-Anwendungen finden Sie in unserer Dokumentation zu automatischen Teams-Telefonzentralen und Teams-Anrufwarteschlangen. Oder sehen Sie sich unser Tutorial über das Erstellen einer ähnlichen Erfahrung mit JavaScript-Bündeln an.
Schnellstart: Verknüpfen Ihrer Anruf-App mit einer Teams-Anrufwarteschlange
Schnellstart: Verknüpfen Ihrer Anruf-App mit einer automatischen Teams-Telefonzentrale