Skip to content

Instantly share code, notes, and snippets.

@edwardlee-msft
Last active October 20, 2023 21:51
Show Gist options
  • Save edwardlee-msft/dd4832673aa462b6e6bcaab72d7ae01e to your computer and use it in GitHub Desktop.
Save edwardlee-msft/dd4832673aa462b6e6bcaab72d7ae01e to your computer and use it in GitHub Desktop.
import { AzureCommunicationTokenCredential, CommunicationUserIdentifier } from '@azure/communication-common';
import {
CallComposite,
ChatComposite,
CustomCallControlButtonCallback,
CustomCallControlButtonCallbackArgs,
CustomCallControlButtonProps,
fromFlatCommunicationIdentifier,
useAzureCommunicationCallAdapter,
useAzureCommunicationChatAdapter
} from '@azure/communication-react';
import React, { CSSProperties, useEffect, useMemo, useRef, useState } from 'react';
import { v4 as uuidv4 } from 'uuid';
import { ChatClient } from '@azure/communication-chat';
/**
* Authentication information needed for your client application to use
* Azure Communication Services.
*
* For this quickstart, you can obtain these from the Azure portal as described here:
* https://docs.microsoft.com/en-us/azure/communication-services/quickstarts/identity/quick-create-identity
*
* In a real application, your backend service would provide these to the client
* application after the user goes through your authentication flow.
*/
const ENDPOINT_URL = '<Azure Communication Services Resource Endpoint>';
const USER_ID = '<Azure Communication Services Identifier>';
const TOKEN = '<Azure Communication Services Access Token>';
/**
* Display name for the local participant.
* In a real application, this would be part of the user data that your
* backend services provides to the client application after the user
* goes through your authentication flow.
*/
const DISPLAY_NAME = '<Display Name>';
/**
* Entry point of your application.
*/
function App(): JSX.Element {
// Arguments that would usually be provided by your backend service or
// (indirectly) by the user.
const { endpointUrl, userId, token, displayName, groupId, threadId } = useAzureCommunicationServiceArgs();
// A well-formed token is required to initialize the chat and calling adapters.
const credential = useMemo(() => {
try {
return new AzureCommunicationTokenCredential(token);
} catch {
console.error('Failed to construct token credential');
return undefined;
}
}, [token]);
// Memoize arguments to `useAzureCommunicationCallAdapter` so that
// a new adapter is only created when an argument changes.
const callAdapterArgs = useMemo(
() => ({
userId: fromFlatCommunicationIdentifier(userId) as CommunicationUserIdentifier,
displayName,
credential,
locator: { groupId }
}),
[userId, credential, displayName, groupId]
);
const callAdapter = useAzureCommunicationCallAdapter(callAdapterArgs);
// Memoize arguments to `useAzureCommunicationChatAdapter` so that
// a new adapter is only created when an argument changes.
const chatAdapterArgs = useMemo(
() => ({
endpoint: endpointUrl,
userId: fromFlatCommunicationIdentifier(userId) as CommunicationUserIdentifier,
displayName,
credential,
threadId
}),
[endpointUrl, userId, displayName, credential, threadId]
);
const chatAdapter = useAzureCommunicationChatAdapter(chatAdapterArgs);
if (!!callAdapter && !!chatAdapter) {
return (
<div style={{ height: '100vh', display: 'flex'}}>
<div style={containerStyle}>
<ChatComposite adapter={chatAdapter} />
</div>
<div style={containerStyle}>
<CallComposite
adapter={callAdapter}
options={{
callControls: {
onFetchCustomButtonProps: onFetchCustomButtonPropsExample
}}}/>
</div>
</div>
);
}
if (credential === undefined) {
return <h3>Failed to construct credential. Provided token is malformed.</h3>;
}
return <h3>Initializing...</h3>;
}
const containerStyle: CSSProperties = {
border: 'solid 0.125rem olive',
margin: '0.5rem',
width: '50vw',
};
/**
* This hook returns all the arguments required to use the Azure Communication services
* that would be provided by your backend service after user authentication
* depending on the user-flow (e.g. which chat thread to use).
*/
function useAzureCommunicationServiceArgs(): {
endpointUrl: string;
userId: string;
token: string;
displayName: string;
groupId: string;
threadId: string;
} {
const [threadId, setThreadId] = useState('');
// For the quickstart, create a new thread with just the local participant in it.
useEffect(() => {
(async () => {
const client = new ChatClient(ENDPOINT_URL, new AzureCommunicationTokenCredential(TOKEN));
const { chatThread } = await client.createChatThread(
{
topic: 'Composites Quickstarts'
},
{
participants: [
{
id: fromFlatCommunicationIdentifier(USER_ID),
displayName: DISPLAY_NAME
}
]
}
);
setThreadId(chatThread?.id ?? '');
})();
}, []);
// For the quickstart, generate a random group ID.
// The group Id must be a UUID.
const groupId = useRef(uuidv4());
return {
endpointUrl: ENDPOINT_URL,
userId: USER_ID,
token: TOKEN,
displayName: DISPLAY_NAME,
groupId: groupId.current,
threadId
};
}
const onFetchCustomButtonPropsExample: CustomCallControlButtonCallback[] = [
(args: CustomCallControlButtonCallbackArgs): CustomCallControlButtonProps => {
return {
placement: 'primary',
// Icon that is registered by the composites.
iconName: 'Link',
strings: {
label: 'Feedback',
tooltipContent: 'Feedback'
},
showLabel: args.displayType !== 'compact',
onItemClick: () => {
window.open('https://feedback.azure.com/d365community/forum/81ff6d2b-0c25-ec11-b6e6-000d3a4f0858', '_blank')?.focus();
}
};
}
];
export default App;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment