-
Notifications
You must be signed in to change notification settings - Fork 211
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
(feat) Add text-to-speech voice call support for ticket numbers in queue screen #1317
base: main
Are you sure you want to change the base?
Changes from 2 commits
25d83db
dcf3214
db06a5e
5aec713
fc51a45
fca4e76
3b831e8
e685f49
d813ea1
a2e531c
219a960
1e094eb
b5f6d90
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -1,15 +1,69 @@ | ||||||||||
import React from 'react'; | ||||||||||
import React, { useState, useEffect } from 'react'; | ||||||||||
import { DataTableSkeleton } from '@carbon/react'; | ||||||||||
import { useTranslation } from 'react-i18next'; | ||||||||||
import { useActiveTickets } from './useActiveTickets'; | ||||||||||
import PatientQueueHeader from '../patient-queue-header/patient-queue-header.component'; | ||||||||||
import styles from './queue-screen.scss'; | ||||||||||
import { useSelectedQueueLocationUuid } from '../helpers/helpers'; | ||||||||||
|
||||||||||
interface QueueScreenProps {} | ||||||||||
|
||||||||||
const QueueScreen: React.FC<QueueScreenProps> = () => { | ||||||||||
const { t } = useTranslation(); | ||||||||||
const { activeTickets, isLoading, error } = useActiveTickets(); | ||||||||||
const { activeTickets, isLoading, error, mutate } = useActiveTickets(); | ||||||||||
const speaker = window.speechSynthesis; | ||||||||||
const [isSpeaking, setIsSpeaking] = useState(false); | ||||||||||
const selectedLocation = useSelectedQueueLocationUuid(); | ||||||||||
const locationFilteredTickets = activeTickets; | ||||||||||
|
||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did you mean to filter There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i did this because ill be making a follow up later to filter by location, am waiting for someone to include the locaion uuid in the response so that i can utilize that,,, just like the selectedlocation variable they are unused for now There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Best to not include it until then. It's best to keep things as easily understandable as possible. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. well gotten |
||||||||||
const rowData = locationFilteredTickets.map((ticket, index) => ({ | ||||||||||
id: `${index}`, | ||||||||||
room: ticket.room, | ||||||||||
ticketNumber: ticket.ticketNumber, | ||||||||||
status: ticket.status, | ||||||||||
})); | ||||||||||
|
||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider memoizing const rowData = useMemo(() =>
activeTickets.map((ticket, index) => ({
id: `${index}`,
room: ticket.room,
ticketNumber: ticket.ticketNumber,
status: ticket.status,
})),
[activeTickets]); Also, is there something unique in the response that we could use as the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @denniskigen currently the response does not have uuid thats why i opted to use index |
||||||||||
function readTicket(queue, speaker) { | ||||||||||
if ('speechSynthesis' in window) { | ||||||||||
const message = new SpeechSynthesisUtterance(); | ||||||||||
const utterance = | ||||||||||
'Ticket Number: ' + | ||||||||||
queue.ticketNumber.split('-')[0].split('') + | ||||||||||
', - ' + | ||||||||||
queue.ticketNumber.split('-')[1].split('') + | ||||||||||
', Please Proceed To Room, ' + | ||||||||||
queue.room; | ||||||||||
message.rate = 1; | ||||||||||
message.pitch = 1; | ||||||||||
message.text = utterance; | ||||||||||
return new Promise((resolve) => { | ||||||||||
message.onend = resolve; | ||||||||||
speaker.speak(message); | ||||||||||
}); | ||||||||||
} else { | ||||||||||
return Promise.resolve(); | ||||||||||
} | ||||||||||
} | ||||||||||
|
||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This could be moved into the component function body and memoized with a const readTicket = useCallback((queue) => {
if ('speechSynthesis' in window) {
const message = new SpeechSynthesisUtterance();
const [prefix, suffix] = queue.ticketNumber.split('-');
const utterance = `Ticket Number: ${prefix.split('')}, - ${suffix.split('')}, Please Proceed To Room, ${queue.room}`;
message.rate = 1;
message.pitch = 1;
message.text = utterance;
return new Promise<void>((resolve) => {
message.onend = resolve;
speaker.speak(message);
});
}
return Promise.resolve();
}, [speaker]); Also, if the const utterance = t(
'ticketAnnouncement',
'Ticket number: {{prefix}}, - {{suffix}}, please proceed to room {{room}}',
{
prefix: prefix.split('').join(' '),
suffix: suffix.split('').join(' '),
room: queue.room,
},
); |
||||||||||
useEffect(() => { | ||||||||||
const readableTickets = locationFilteredTickets.filter((item) => item.status == 'calling'); | ||||||||||
if (readableTickets.length > 0 && !isSpeaking) { | ||||||||||
denniskigen marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
setIsSpeaking(true); | ||||||||||
const readTickets = async () => { | ||||||||||
for (const ticket of readableTickets) { | ||||||||||
await readTicket(ticket, speaker); | ||||||||||
} | ||||||||||
setIsSpeaking(false); | ||||||||||
if (typeof mutate === 'function') { | ||||||||||
mutate(); | ||||||||||
} | ||||||||||
}; | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
||||||||||
readTickets(); | ||||||||||
} | ||||||||||
|
||||||||||
return () => {}; | ||||||||||
}, [locationFilteredTickets, isSpeaking]); | ||||||||||
|
||||||||||
if (isLoading) { | ||||||||||
return <DataTableSkeleton row={5} className={styles.queueScreen} role="progressbar" />; | ||||||||||
|
@@ -19,13 +73,6 @@ const QueueScreen: React.FC<QueueScreenProps> = () => { | |||||||||
return <div>Error</div>; | ||||||||||
} | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We need a better error state here. Consider using the return <ErrorState error={error} headerTitle={t('queueScreenError', 'Queue screen error')} />; |
||||||||||
|
||||||||||
const rowData = activeTickets.map((ticket, index) => ({ | ||||||||||
id: `${index}}`, | ||||||||||
room: ticket.room, | ||||||||||
ticketNumber: ticket.ticketNumber, | ||||||||||
status: ticket.status, | ||||||||||
})); | ||||||||||
|
||||||||||
return ( | ||||||||||
<div> | ||||||||||
<PatientQueueHeader title={t('queueScreen', 'Queue screen')} showLocationDropdown /> | ||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like we're not using this variable. If so, we should remove it.