-
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?
Conversation
packages/esm-service-queues-app/src/queue-screen/queue-screen.component.tsx
Outdated
Show resolved
Hide resolved
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 comment
The 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 useCallback
:
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 SpeechSynthesisUtterance
API is locale-aware (it probably is), we should consider internationalising utterance
using something like:
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 ticketsToCallOut = locationFilteredTickets.filter((item) => item.status == 'calling'); |
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.
const ticketsToCallOut = locationFilteredTickets.filter((item) => item.status == 'calling'); | |
const ticketsToCallOut = locationFilteredTickets.filter((item) => item.status === 'calling'); |
if (typeof mutate === 'function') { | ||
mutate(); | ||
} |
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.
if (typeof mutate === 'function') { | |
mutate(); | |
} | |
mutate?.(); |
@@ -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 comment
The reason will be displayed to describe this comment to others. Learn more.
We need a better error state here. Consider using the ErrorState
component from the framework:
return <ErrorState error={error} headerTitle={t('queueScreenError', 'Queue screen error')} />;
Thanks for taking a stab at this, @FelixKiprotich350. I've left a few suggestions around optimising the code, handling internationalisation, and error states. It'd be great to also extend the QueueScreen test to account for the new functionality. LMK if you need help with that. |
const { activeTickets, isLoading, error, mutate } = useActiveTickets(); | ||
const speaker = window.speechSynthesis; | ||
const [isSpeaking, setIsSpeaking] = useState(false); | ||
const selectedLocation = useSelectedQueueLocationUuid(); |
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.
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 comment
The reason will be displayed to describe this comment to others. Learn more.
Consider memoizing rowData
:
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 id
instead of index
? Maybe a UUID?
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.
@denniskigen currently the response does not have uuid thats why i opted to use index
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 comment
The reason will be displayed to describe this comment to others. Learn more.
Did you mean to filter activeTickets
by location here? If not, then we probably don't need the alias and we should use activeTickets
directly.
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.
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 comment
The 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 comment
The reason will be displayed to describe this comment to others. Learn more.
well gotten
This option needs to be behind some kind of configuration. I don't think it should be enabled by default. I'm also slightly suspicious that embedding this in a frontend module is the right approach. While it works for a small clinic, I don't think it is a great idea for larger clinics. Basically, this is the type of feature that needs some community discussion before it's mergeable, but I like it! |
This will be helpful. |
Yea sure ,,, with the community discussions i believe we will have good ideas on how best we can implement this and configure dynamically |
this is true @ibacher , but how do you see it given that the feature can be used dynamically with the help of some configurations. Given an example of a large clinic/facility with multiple queue locations and service points, where every point or Queue has a screen to call tickets specifically for that point? |
@FelixKiprotich350 If I had exact thoughts on how this kind of feature should work, I would've included them. That's why I think we need a community discussion on Talk, because I'm not exactly sure what the best behaviour is here. My guess is that there's a lot of additional layers that are needed, i.e., for a user viewing the queue to select to be a system that automatically calls out tickets so that, e.g., a clinic manager or clinician viewing the queue in an office doesn't need to be bothered. Similarly, with a larger clinic, there may be two or more, e.g., nurse stations working on the same queue, in which case only one should probably be announcing. But this requires more thought than is ideal to document on a PR in comments, hence, a Talk post would be ideal. |
@ibacher Agree..... You can guide on how to go about it |
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}`; |
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.
This should be removed
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.
noted
I tend to agree with @ibacher , thanks... fyi @mseaton @chibongho |
Could we wire up some test coverage for this new functionality? |
yes sure i can do that |
Agree with @ibacher that more discussion is needed. Since there's no Talk link posted here I assume there's no Talk thread and will share my thoughts here. My suggestion would be that there should just be a page for this functionality. If someone wants their computer to make announcements for the queue, they just leave a tab open on that page. Eventually, and this would need design, the full vision might be a "queue display" page which has a toggle for whether to make audio announcements and which has a button that will open a separate window that is intended to be displayed full-screen on a monitor facing the patients in the waiting room. Like Google Slides "presenter view" feature. |
…queues
Requirements
Summary
This feature aims at voice calling ticket number in Service Queue by using Text-to-Speech web api
Screenshots
https://drive.google.com/file/d/1i_mW3YTR6XcOHsdrGbUiQ0bLMz8M8Ju5/view?usp=sharing
Related Issue
https://openmrs.atlassian.net/browse/O3-3995
Other