From 82f49494d6cbaf33bbf17fb6193412d54e9c1bd2 Mon Sep 17 00:00:00 2001 From: g-collab4 Date: Wed, 9 Apr 2025 11:07:30 -0400 Subject: [PATCH] voice stuff --- client/package.json | 1 + client/src/components/layout/index.tsx | 2 + .../main/voiceRecognition/index.tsx | 26 +++++++ client/src/hooks/useVoiceRecognition.ts | 76 +++++++++++++++++++ package-lock.json | 31 ++++++++ package.json | 4 + 6 files changed, 140 insertions(+) create mode 100644 client/src/components/main/voiceRecognition/index.tsx create mode 100644 client/src/hooks/useVoiceRecognition.ts diff --git a/client/package.json b/client/package.json index 7a26de8..2ba4365 100644 --- a/client/package.json +++ b/client/package.json @@ -16,6 +16,7 @@ "react-markdown": "^10.1.0", "react-router-dom": "^6.28.1", "react-scripts": "5.0.1", + "react-speech-recognition": "^4.0.0", "rehype-highlight": "^7.0.2", "rehype-katex": "^7.0.1", "rehype-raw": "^7.0.0", diff --git a/client/src/components/layout/index.tsx b/client/src/components/layout/index.tsx index a30d217..e5eae19 100644 --- a/client/src/components/layout/index.tsx +++ b/client/src/components/layout/index.tsx @@ -3,6 +3,7 @@ import './index.css'; import { Outlet } from 'react-router-dom'; import SideBarNav from '../main/sideBarNav'; import Header from '../header'; +import VoiceNavigator from '../main/voiceRecognition'; /** * Main component represents the layout of the main page, including a sidebar and the main content area. @@ -13,6 +14,7 @@ const Layout = () => (
+
diff --git a/client/src/components/main/voiceRecognition/index.tsx b/client/src/components/main/voiceRecognition/index.tsx new file mode 100644 index 0000000..5b5c2c3 --- /dev/null +++ b/client/src/components/main/voiceRecognition/index.tsx @@ -0,0 +1,26 @@ +import useVoiceRecognition from '../../../hooks/useVoiceRecognition'; + +const VoiceNavigator = () => { + const { transcript, listening, browserSupportsSpeechRecognition, startL, stopL } = + useVoiceRecognition(); + + if (!browserSupportsSpeechRecognition) { + return

Your browser doesn't support speech recognition.

; + } + + return ( +
+

Status: {listening ? '🎙️ Listening...' : '🛑 Not listening'}

+ + + +
+ Transcript: {transcript} +
+
+ ); +}; + +export default VoiceNavigator; diff --git a/client/src/hooks/useVoiceRecognition.ts b/client/src/hooks/useVoiceRecognition.ts new file mode 100644 index 0000000..03c2360 --- /dev/null +++ b/client/src/hooks/useVoiceRecognition.ts @@ -0,0 +1,76 @@ +import SpeechRecognition, { useSpeechRecognition } from 'react-speech-recognition'; +import { useEffect } from 'react'; +import { useNavigate } from 'react-router-dom'; + +const useVoiceRecognition = () => { + const navigate = useNavigate(); + const startL = () => SpeechRecognition.startListening({ continuous: true }); + const stopL = () => SpeechRecognition.stopListening(); + + const commands = [ + { + command: ['Go to home', 'Open home', 'Home page'], + callback: () => navigate('/home'), + }, + { + command: [ + 'Open chats', + 'Go to chats', + 'Chat page', + 'Chats', + 'Messages', + 'Go to messages', + 'Messages page', + ], + callback: () => navigate('/messages'), + }, + { + command: ['Open questions', 'Go to questions', 'Questions', 'Question page'], + callback: () => navigate(`/questionPage`), + }, + { + command: ['Open tags', 'Go to tags', 'Tags', 'Tags page'], + callback: () => navigate(`/tags`), + }, + { + command: ['Open users', 'Go to users', 'Users', 'Users page'], + callback: () => navigate(`/users`), + }, + { + command: ['Open games', 'Go to games', 'Games', 'Games page'], + callback: () => navigate(`/games`), + }, + { + command: ['Open communities', 'Go to communities', 'Communities', 'Communities page'], + callback: () => navigate(`/communities/all`), + }, + { + command: ['Go back', 'Navigate back'], + callback: () => navigate(-1), + }, + { + command: 'Stop listening', + callback: () => SpeechRecognition.stopListening(), + }, + ]; + + const { transcript, listening, browserSupportsSpeechRecognition } = useSpeechRecognition({ + commands, + }); + + useEffect(() => { + if (browserSupportsSpeechRecognition) { + SpeechRecognition.startListening({ continuous: true }); + } + }, [browserSupportsSpeechRecognition]); + + return { + transcript, + listening, + browserSupportsSpeechRecognition, + startL, + stopL, + }; +}; + +export default useVoiceRecognition; diff --git a/package-lock.json b/package-lock.json index cc40e71..c4dfc71 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,12 +22,16 @@ "react-hotkeys-hook": "^4.6.1", "react-icons": "^5.5.0", "react-markdown": "^10.1.0", + "react-speech-recognition": "^4.0.0", "rehype-highlight": "^7.0.2", "rehype-katex": "^7.0.1", "rehype-raw": "^7.0.0", "rehype-sanitize": "^6.0.0", "remark-gfm": "^4.0.1", "remark-math": "^6.0.0" + }, + "devDependencies": { + "@types/react-speech-recognition": "^3.9.6" } }, "client": { @@ -47,6 +51,7 @@ "react-markdown": "^10.1.0", "react-router-dom": "^6.28.1", "react-scripts": "5.0.1", + "react-speech-recognition": "^4.0.0", "rehype-highlight": "^7.0.2", "rehype-katex": "^7.0.1", "rehype-raw": "^7.0.0", @@ -4824,6 +4829,13 @@ "@types/ms": "*" } }, + "node_modules/@types/dom-speech-recognition": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@types/dom-speech-recognition/-/dom-speech-recognition-0.0.6.tgz", + "integrity": "sha512-o7pAVq9UQPJL5RDjO1f/fcpfFHdgiMnR4PoIU2N/ZQrYOS3C5rzdOJMsrpqeBCbii2EE9mERXgqspQqPDdPahw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/eslint": { "version": "8.56.12", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.12.tgz", @@ -5087,6 +5099,16 @@ "@types/react": "^18.0.0" } }, + "node_modules/@types/react-speech-recognition": { + "version": "3.9.6", + "resolved": "https://registry.npmjs.org/@types/react-speech-recognition/-/react-speech-recognition-3.9.6.tgz", + "integrity": "sha512-cdzwXIZXWyp8zfM2XI7APDW1rZf4Nz73T4SIS2y+cC7zHnZluCdumYKH6HacxgxJH+zemAq2oXbHWXcyW0eT3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/dom-speech-recognition": "*" + } + }, "node_modules/@types/resolve": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", @@ -24781,6 +24803,15 @@ "node": ">=10" } }, + "node_modules/react-speech-recognition": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/react-speech-recognition/-/react-speech-recognition-4.0.0.tgz", + "integrity": "sha512-hz1OsRhjAW70rOMVXN84PR+1L2I1j8xS1TXpwpd4vlDaRY9i/LbAaxEklqscgObECTTuyxNeGBdVdcq/pX3bqQ==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", diff --git a/package.json b/package.json index 7726711..dd39826 100644 --- a/package.json +++ b/package.json @@ -17,11 +17,15 @@ "react-hotkeys-hook": "^4.6.1", "react-icons": "^5.5.0", "react-markdown": "^10.1.0", + "react-speech-recognition": "^4.0.0", "rehype-highlight": "^7.0.2", "rehype-katex": "^7.0.1", "rehype-raw": "^7.0.0", "rehype-sanitize": "^6.0.0", "remark-gfm": "^4.0.1", "remark-math": "^6.0.0" + }, + "devDependencies": { + "@types/react-speech-recognition": "^3.9.6" } }