diff --git a/src/web-live-chat/src/lib/helpers/typedefs.js b/src/web-live-chat/src/lib/helpers/typedefs.js index 1997ab14..0d59fb35 100644 --- a/src/web-live-chat/src/lib/helpers/typedefs.js +++ b/src/web-live-chat/src/lib/helpers/typedefs.js @@ -118,6 +118,14 @@ IRichContent.prototype.text; * @param {ChatResponseModel} message */ +/** + * Invoked when speech to text is detected. + * + * @callback OnSpeechToTextDetected + * @param {string} text + */ + + // having to export an empty object here is annoying, // but required for vscode to pass on your types. export default {}; \ No newline at end of file diff --git a/src/web-live-chat/src/lib/services/agent-service.js b/src/web-live-chat/src/lib/services/agent-service.js index 3b58ce28..3b2f3763 100644 --- a/src/web-live-chat/src/lib/services/agent-service.js +++ b/src/web-live-chat/src/lib/services/agent-service.js @@ -1,10 +1,12 @@ import { agentListUrl, agentDetailUrl } from '$lib/services/api-endpoints.js'; +import { setAuthorization } from '$lib/helpers/http'; import axios from 'axios'; /** * @returns {Promise} */ export async function getAgents() { + setAuthorization(); const response = await axios.get(agentListUrl); return response.data; } @@ -14,6 +16,7 @@ export async function getAgents() { * @returns {Promise} */ export async function getAgent(id) { + setAuthorization(); let url = agentDetailUrl.replace("{id}", id); const response = await axios.get(url); return response.data; diff --git a/src/web-live-chat/src/lib/services/web-speech.js b/src/web-live-chat/src/lib/services/web-speech.js new file mode 100644 index 00000000..e9738530 --- /dev/null +++ b/src/web-live-chat/src/lib/services/web-speech.js @@ -0,0 +1,61 @@ +// https://developer.mozilla.org/en-US/docs/Web/API/Web_Speech_API/Using_the_Web_Speech_API +const SpeechRecognition = + window.SpeechRecognition || window.webkitSpeechRecognition; +const SpeechRecognitionEvent = + window.SpeechRecognitionEvent || window.webkitSpeechRecognitionEvent; + +const recognition = new SpeechRecognition(); +recognition.continuous = false; +recognition.lang = "en-US"; +recognition.interimResults = false; +recognition.maxAlternatives = 1; + +const synth = window.speechSynthesis; + + +const utterThis = new SpeechSynthesisUtterance(); +utterThis.pitch = 1; +utterThis.rate = 1; + +export const webSpeech = { + /** @type {import('$typedefs').OnSpeechToTextDetected} */ + onSpeechToTextDetected: () => {}, + + start() { + recognition.start(); + console.log("Ready to receive a voice command."); + }, + + /** @param {string} transcript */ + utter(transcript) { + setVoiceSynthesis(); + utterThis.text = transcript + synth.speak(utterThis); + } +} + +function setVoiceSynthesis() { + if (utterThis.voice == null) { + const voices = synth.getVoices(); + for (let i = 0; i < voices.length; i++) { + if (voices[i].name === "Microsoft Michelle Online (Natural) - English (United States)") { + utterThis.voice = voices[i]; + break; + } + } + } +} + +recognition.onresult = (event) => { + const text = event.results[0][0].transcript; + console.log(`Confidence: ${text} ${event.results[0][0].confidence}`); + webSpeech.onSpeechToTextDetected(text); +}; + +recognition.onnomatch = (event) => { + console.log("I didn't recognize that color."); +}; + +recognition.onerror = (event) => { + console.log(`Error occurred in recognition: ${event.error}`); +}; \ No newline at end of file diff --git a/src/web-live-chat/src/routes/(home)/+page.svelte b/src/web-live-chat/src/routes/(home)/+page.svelte index 3c7608cc..a2545069 100644 --- a/src/web-live-chat/src/routes/(home)/+page.svelte +++ b/src/web-live-chat/src/routes/(home)/+page.svelte @@ -1,6 +1,6 @@

We craft digital, graphic and dimensional thinking, to create category leading brand experiences that have meaning and add a value for our clients.

- +
\ No newline at end of file diff --git a/src/web-live-chat/src/routes/chat/+page.svelte b/src/web-live-chat/src/routes/chat/+page.svelte new file mode 100644 index 00000000..0a15c919 --- /dev/null +++ b/src/web-live-chat/src/routes/chat/+page.svelte @@ -0,0 +1,55 @@ + + + + +
+
+ {#each agents as agent} +
+ agentId = agent.id} + /> + +
{agent.description}
+
+ {/each} +
+
+
+ + +

We craft digital, graphic and dimensional thinking, to create category leading brand experiences that have meaning and add a value for our clients.

+ + +
+
\ No newline at end of file diff --git a/src/web-live-chat/src/routes/chat/[agentId]/[conversationId]/chat-box.svelte b/src/web-live-chat/src/routes/chat/[agentId]/[conversationId]/chat-box.svelte index b84ada86..77b9dd35 100644 --- a/src/web-live-chat/src/routes/chat/[agentId]/[conversationId]/chat-box.svelte +++ b/src/web-live-chat/src/routes/chat/[agentId]/[conversationId]/chat-box.svelte @@ -15,6 +15,7 @@ import { onMount } from 'svelte'; import Link from 'svelte-link'; import { signalr } from '$lib/services/signalr-service.js'; + import { webSpeech } from '$lib/services/web-speech.js'; import { sendMessageToHub, GetDialogs } from '$lib/services/conversation-service.js'; import { format } from '$lib/helpers/datetime'; import RcText from './rc-text.svelte'; @@ -43,6 +44,7 @@ // @ts-ignore let scrollbar; + let microphoneIcon = "microphone-off"; /** @type {import('$typedefs').ChatResponseModel[]} */ let dialogs = []; @@ -78,17 +80,36 @@ /** @param {import('$typedefs').ChatResponseModel} message */ function onMessageReceivedFromAssistant(message) { - dialogs.push(message); - refresh(); + webSpeech.utter(message.text); + // clean rich content elements + dialogs.forEach(dialog => { + if (dialog.rich_content.messaging_type == "quick_reply") { + dialog.rich_content.quick_repies = []; + } + }); + + dialogs.push(message); + refresh(); } - async function sendMessage() { + async function sendTextMessage() { await sendMessageToHub(params.agentId, params.conversationId, text); } + async function startListen() { + microphoneIcon = "microphone"; + webSpeech.onSpeechToTextDetected = async (transcript) => { + text = transcript; + await sendTextMessage(); + microphoneIcon = "microphone-off"; + } + webSpeech.start(); + } + function refresh() { // trigger UI render dialogs = dialogs; + setTimeout(() => { const { viewport } = scrollbar.elements(); viewport.scrollTo({ top: viewport.scrollHeight, behavior: 'smooth' }); // set scroll offset @@ -194,8 +215,16 @@
+
+ +
-
+
diff --git a/src/web-live-chat/src/routes/login/+page.svelte b/src/web-live-chat/src/routes/login/+page.svelte index c29cfbd3..8a6825c1 100644 --- a/src/web-live-chat/src/routes/login/+page.svelte +++ b/src/web-live-chat/src/routes/login/+page.svelte @@ -28,7 +28,7 @@ isOpen = true; msg = 'Authentication success'; status = 'success'; - goto(`/chat/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a`); + goto(`/chat`); }); } diff --git a/src/web-live-chat/static/images/users/avatar-1.jpg b/src/web-live-chat/static/images/users/avatar-1.jpg deleted file mode 100644 index c234cd23..00000000 Binary files a/src/web-live-chat/static/images/users/avatar-1.jpg and /dev/null differ diff --git a/src/web-live-chat/static/images/users/avatar-2.jpg b/src/web-live-chat/static/images/users/avatar-2.jpg deleted file mode 100644 index e45d7e7c..00000000 Binary files a/src/web-live-chat/static/images/users/avatar-2.jpg and /dev/null differ diff --git a/src/web-live-chat/static/images/users/avatar-3.jpg b/src/web-live-chat/static/images/users/avatar-3.jpg deleted file mode 100644 index 111a901a..00000000 Binary files a/src/web-live-chat/static/images/users/avatar-3.jpg and /dev/null differ diff --git a/src/web-live-chat/static/images/users/avatar-4.jpg b/src/web-live-chat/static/images/users/avatar-4.jpg deleted file mode 100644 index 97188892..00000000 Binary files a/src/web-live-chat/static/images/users/avatar-4.jpg and /dev/null differ diff --git a/src/web-live-chat/static/images/users/avatar-5.jpg b/src/web-live-chat/static/images/users/avatar-5.jpg deleted file mode 100644 index 544cb75a..00000000 Binary files a/src/web-live-chat/static/images/users/avatar-5.jpg and /dev/null differ diff --git a/src/web-live-chat/static/images/users/avatar-6.jpg b/src/web-live-chat/static/images/users/avatar-6.jpg deleted file mode 100644 index 53bcbc30..00000000 Binary files a/src/web-live-chat/static/images/users/avatar-6.jpg and /dev/null differ diff --git a/src/web-live-chat/static/images/users/avatar-7.jpg b/src/web-live-chat/static/images/users/avatar-7.jpg deleted file mode 100644 index c9e1027f..00000000 Binary files a/src/web-live-chat/static/images/users/avatar-7.jpg and /dev/null differ diff --git a/src/web-live-chat/static/images/users/avatar-8.jpg b/src/web-live-chat/static/images/users/avatar-8.jpg deleted file mode 100644 index 2ea42387..00000000 Binary files a/src/web-live-chat/static/images/users/avatar-8.jpg and /dev/null differ