Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose Communication Client Calling + AI integration possibilities. #201

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5,871 changes: 5,870 additions & 1 deletion Project/package-lock.json

Large diffs are not rendered by default.

89 changes: 67 additions & 22 deletions Project/src/MakeCall/CallCaption.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import React, { useEffect, useState } from "react";
import { Features } from '@azure/communication-calling';
import { Features, CallKind } from '@azure/communication-calling';
import { Dropdown } from 'office-ui-fabric-react/lib/Dropdown';
import { Toggle } from '@fluentui/react/lib/Toggle';
import { TooltipHost } from '@fluentui/react/lib/Tooltip';
import { Icon } from '@fluentui/react/lib/Icon';
import CommunicationAI from "./CommunicationAI/CommunicationAI";

// CallCaption react function component
const CallCaption = ({ call }) => {
Expand All @@ -9,14 +13,22 @@ const CallCaption = ({ call }) => {
const [currentSpokenLanguage, setCurrentSpokenLanguage] = useState(captions.activeSpokenLanguage);
const [currentCaptionLanguage, setCurrentCaptionLanguage] = useState(captions.activeCaptionLanguage);

const [captionHistory, setCaptionHistory] = useState([]);
const [communicationAI, setCommunicationAI] = useState(false);
const [isAgentSpeaking, setIsAgentSpeaking] = useState(false);
const [isUserSpeaking, setIsUserSpeaking] = useState(false);

let localMri = (call.kind === CallKind.Call) ? window.identityMri.communicationUserId : window.identityMri.rawId;

useEffect(() => {
try {
window.captionHistory = [];
startCaptions(captions);
}
catch(e) {
catch (e) {
console.log("Captions not configured for this release version")
}

return () => {
// cleanup
captions.off('CaptionsActiveChanged', captionsActiveHandler);
Expand Down Expand Up @@ -64,6 +76,12 @@ const CallCaption = ({ call }) => {
mri = captionData.speaker.identifier.phoneNumber;
}

if (mri.trim() == localMri && !isAgentSpeaking) {
setIsAgentSpeaking(true)
} else {
setIsUserSpeaking(true)
}

let captionAreasContainer = document.getElementById('captionsArea');
const newClassName = `prefix${mri.replace(/:/g, '').replace(/-/g, '').replace(/\+/g, '')}`;
const captionText = `${captionData.timestamp.toUTCString()}
Expand All @@ -87,53 +105,80 @@ const CallCaption = ({ call }) => {
if (captionData.resultType === 'Final') {
foundCaptionContainer.setAttribute('isNotFinal', 'false');
}
window.captionHistory.push(`${captionData.speaker.displayName}: ${captionData.captionText ?? captionData.spokenText}`);
mri == localMri ? setIsAgentSpeaking(false) : setIsUserSpeaking(false)
}
};

const spokenLanguageSelectionChanged = async (event, item) => {
const spokenLanguages = captions.supportedSpokenLanguages;
const language = spokenLanguages.find(language => { return language === item.key });
await captions.setSpokenLanguage(language);
setCurrentSpokenLanguage(language);
setCurrentSpokenLanguage(language);
};

const SpokenLanguageDropdown = () => {
const keyedSupportedSpokenLanguages = captions.supportedSpokenLanguages.map(language => ({key: language, text: language}));
const SpokenLanguageDropdown = () => {
const keyedSupportedSpokenLanguages = captions.supportedSpokenLanguages.map(language => ({ key: language, text: language }));
return <Dropdown
selectedKey={currentSpokenLanguage}
onChange={spokenLanguageSelectionChanged}
label={'Spoken Language'}
options={keyedSupportedSpokenLanguages}
styles={{ label: {color: '#edebe9'}, dropdown: { width: 100 } }}
/>
selectedKey={currentSpokenLanguage}
onChange={spokenLanguageSelectionChanged}
label={'Spoken Language'}
options={keyedSupportedSpokenLanguages}
styles={{ label: { color: '#edebe9' }, dropdown: { width: 100 } }}
/>
}

const captionLanguageSelectionChanged = async (event, item) => {
const captionLanguages = captions.supportedCaptionLanguages;
const language = captionLanguages.find(language => { return language === item.key });
await captions.setCaptionLanguage(language);
setCurrentCaptionLanguage(language);
setCurrentCaptionLanguage(language);
};

const CaptionLanguageDropdown = () => {
const keyedSupportedCaptionLanguages = captions.supportedCaptionLanguages.map(language => ({key: language, text: language}));
const keyedSupportedCaptionLanguages = captions.supportedCaptionLanguages.map(language => ({ key: language, text: language }));
return <Dropdown
selectedKey={currentCaptionLanguage}
onChange={captionLanguageSelectionChanged}
label={'Caption Language'}
options={keyedSupportedCaptionLanguages}
styles={{ label: {color: '#edebe9'}, dropdown: { width: 100, overflow: 'scroll' } }}
/>
selectedKey={currentCaptionLanguage}
onChange={captionLanguageSelectionChanged}
label={'Caption Language'}
options={keyedSupportedCaptionLanguages}
styles={{ label: { color: '#edebe9' }, dropdown: { width: 100, overflow: 'scroll' } }}
/>
}

return (
<>
{captions && <SpokenLanguageDropdown/>}
{captions && captions.captionsType === 'TeamsCaptions' && <CaptionLanguageDropdown/>}
{captions && <SpokenLanguageDropdown />}
{captions && captions.captionsType === 'TeamsCaptions' && <CaptionLanguageDropdown />}
<div className="scrollable-captions-container">
<div id="captionsArea" className="captions-area">
</div>
</div>
<div className="participants-panel mt-1 mb-3">
<Toggle label={
<div>
Communication AI{' '}
<TooltipHost content={`Turn on Communication AI`}>
<Icon iconName="Info" aria-label="Info tooltip" />
</TooltipHost>
</div>
}
styles={{
text: { color: '#edebe9' },
label: { color: '#edebe9' },
}}
inlineLabel
onText="On"
offText="Off"
defaultChecked={communicationAI}
onChange={() => { setCommunicationAI(oldValue => !oldValue) }}
/>

{
communicationAI &&
<CommunicationAI call={call} isAgentSpeaking={isAgentSpeaking} isUserSpeaking={isUserSpeaking} />
}
</div>
</>
);
};
Expand Down
125 changes: 125 additions & 0 deletions Project/src/MakeCall/CommunicationAI/AgentSupportForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import React, { useEffect, useState } from "react";
import {
TextField, PrimaryButton, Checkbox, MessageBar,
MessageBarType,
} from 'office-ui-fabric-react'
import { v4 as uuidv4 } from 'uuid';

export const AgentSupportForm = ({name, address, phoneNumber, dateOfPurchase, issue, productUnderWarranty}) => {
const [userFullName, setUserFullName] = useState("");
const [userAddress, setUserAddress] = useState("");
const [userPhoneNumber, setUserPhoneNumber] = useState("");
const [userDateOfPurchase, setUserDateOfPurchase] = useState("");
const [IssueDescription, setIssueDescription] = useState("");
const [underWarranty, setUnderWarranty] = useState(false);
const [issueTicket, setIssueTicket] = useState("");
const [isSubmitted, setIsSubmitted] = useState(false)

useEffect(()=> {
if (name != userFullName) {setUserFullName(name)}
if (address != userAddress) {setUserAddress(address)}
if (phoneNumber != userPhoneNumber) {setUserPhoneNumber(phoneNumber)}
if (dateOfPurchase != userDateOfPurchase) {setUserDateOfPurchase(dateOfPurchase)}
if (issue != IssueDescription) {setIssueDescription(issue)}
if (productUnderWarranty != underWarranty) {setUnderWarranty(productUnderWarranty)}


if (userFullName && userAddress && userPhoneNumber && userDateOfPurchase && IssueDescription) {
console.log(`Updating Ticket number`);
setIssueTicket(uuidv4())
} else {
console.log(`NOT Updating Ticket number`);
}
}, [name, address, phoneNumber, dateOfPurchase, issue, productUnderWarranty])

const handleSubmit = (e) => {
e.preventDefault();
setIsSubmitted(true)
setUserFullName("");
setUserAddress("")
setUserPhoneNumber("")
setUserDateOfPurchase("")
setIssueDescription("")
setUnderWarranty(false)
setIssueTicket("")
setTimeout(() => {
setIsSubmitted(false)
}, 5000)
}

const FormSubmitted = () => {
return (<MessageBar
messageBarType={MessageBarType.success}
isMultiline={false}
>
Ticket created successfully
</MessageBar>)
};

return <>
<div className="ms-Grid-col ms-Grid-col ms-sm6 ms-md6 ms-lg6" >
{isSubmitted && FormSubmitted()}
<div className="ms-Grid-row">
<div className="ms-Grid-row">
<TextField
placeholder="FullName"
value={userFullName}
className="text-left"
onChange={(e) => { setUserFullName(e.target.value)}}
/>
</div>
<div className="ms-Grid-row">
<TextField
placeholder="Address"
value={userAddress}
className="text-left"
onChange={(e) => { setUserAddress(e.target.value)}}
/>
</div>
<div className="ms-Grid-row">
<TextField
placeholder="PhoneNumber"
value={userPhoneNumber}
className="text-left"
onChange={(e) => { setUserPhoneNumber(e.target.value)}} />
</div>
<div className="ms-Grid-row">
<TextField
placeholder="Date of Purchase"
value={userDateOfPurchase}
className="text-left"
onChange={(e) => { setUserDateOfPurchase(e.target.value)}} />
</div>
<div className="ms-Grid-row">
<TextField
placeholder="Issue Description"
multiline rows={5}
value={IssueDescription}
className="text-left"
onChange={(e) => { setIssueDescription(e.target.value)}} />
</div>
<div className="ms-Grid-row">
<Checkbox label="Product under Warranty" checked={underWarranty} onChange={(e, checked) => {setUnderWarranty(checked)}} />
</div>

<div className="ms-Grid-row">
<TextField
placeholder="Issue Ticket#"
value={issueTicket}
className="text-left"
onChange={(e) => { setIssueTicket(e.target.value)}}
/>
</div>
</div>
<div className="ms-Grid-row">
<div className="ms-Grid-col">
<PrimaryButton className="primary-button mt-5 text-left"

onClick={(e) => {handleSubmit(e)}}>
Submit Ticket
</PrimaryButton>
</div>
</div>
</div>
</>
}
Loading
Loading