Skip to content

Commit

Permalink
Merge pull request #41 from the-collab-lab/eb-voice-to-text-issue-17
Browse files Browse the repository at this point in the history
added voice to text feature to manageList and Home page
  • Loading branch information
EmmaBin authored Sep 30, 2024
2 parents fed03fa + 6426c08 commit d171c59
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 2 deletions.
97 changes: 96 additions & 1 deletion src/utils/hooks.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useState } from 'react';
import { useEffect, useState, useRef } from 'react';

/**
* Set some state in React, and also persist that value in localStorage.
Expand All @@ -19,3 +19,98 @@ export function useStateWithStorage(storageKey, initialValue) {

return [value, setValue];
}

export function useVoiceToText() {
const [text, setText] = useState('');
const [isListening, setIsListening] = useState(false);
const recognitionRef = useRef(null);

useEffect(() => {
if (
!('SpeechRecognition' in window || 'webkitSpeechRecognition' in window)
) {
console.error('SpeechRecognition is not supported by this browser.');
return;
}

recognitionRef.current = new (window.SpeechRecognition ||
window.webkitSpeechRecognition ||
window.mozSpeechRecognition ||
window.msSpeechRecognition)();

recognitionRef.current.lang = 'en-US';
recognitionRef.current.interimResults = false;
recognitionRef.current.maxAlternatives = 1;

recognitionRef.current.onresult = (event) => {
const transcript = event.results[0][0].transcript;
setText(transcript);
};
recognitionRef.current.onend = () => {
setIsListening(false);
};

recognitionRef.current.onerror = (error) => {
console.error('Speech recognition error:', error);
setIsListening(false);
};

return () => {
recognitionRef.current.stop();
recognitionRef.current = null;
};
}, []);

function detectBrowser() {
let userAgent = navigator.userAgent;
if (userAgent.indexOf('Edg') > -1) {
return 'Microsoft Edge';
} else if (userAgent.indexOf('Chrome') > -1) {
return 'Chrome';
} else if (userAgent.indexOf('Firefox') > -1) {
return 'Firefox';
} else if (userAgent.indexOf('Safari') > -1) {
return 'Safari';
} else if (userAgent.indexOf('Opera') > -1) {
return 'Opera';
} else if (
userAgent.indexOf('Trident') > -1 ||
userAgent.indexOf('MSIE') > -1
) {
return 'Internet Explorer';
}

return 'Unknown';
}

const startListening = async () => {
try {
// Request microphone access
await navigator.mediaDevices.getUserMedia({ audio: true });
console.log('Microphone access granted. Starting speech recognition...');

const browserName = detectBrowser();
if (browserName === 'Safari') {
window.alert('Start Voice Input is not working in your browser.');
return;
}

if (!isListening && recognitionRef.current) {
recognitionRef.current.start();
setIsListening(true);
}
} catch (err) {
console.error('Error accessing microphone:', err);
setError('Failed to access microphone. Please enable mic permissions.');
}
};

const stopListening = () => {
if (isListening && recognitionRef.current) {
recognitionRef.current.stop();
setIsListening(false);
}
};

return { text, isListening, startListening, stopListening };
}
11 changes: 11 additions & 0 deletions src/views/Home.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { SingleList } from '../components';
import { Fragment, useState } from 'react';
import { createList, useAuth } from '../api';
import { useNavigate } from 'react-router-dom';
import { useVoiceToText } from '../utils';

export function Home({ data, setListPath }) {
const [listName, setListName] = useState('');
Expand All @@ -11,6 +12,13 @@ export function Home({ data, setListPath }) {
const userId = user?.uid;
const userEmail = user?.email;
const navigate = useNavigate();
const { text, isListening, startListening } = useVoiceToText();

useEffect(() => {
if (text) {
setListName(text);
}
}, [text]);

async function handleSubmit(e) {
e.preventDefault();
Expand Down Expand Up @@ -60,6 +68,9 @@ export function Home({ data, setListPath }) {
value={listName}
onChange={(e) => setListName(e.target.value)}
/>
<button type="button" onClick={startListening}>
{isListening ? 'Listening...' : 'Start Voice Input'}
</button>
<button>Submit</button>
<p>{error}</p>
</form>
Expand Down
24 changes: 23 additions & 1 deletion src/views/ManageList.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useState } from 'react';
import { useState, useEffect } from 'react';
import { addItem, shareList } from '../api';
import { useVoiceToText } from '../utils';

export function ManageList({ userId, list }) {
const [formData, setFormData] = useState({
Expand All @@ -8,6 +9,15 @@ export function ManageList({ userId, list }) {
});

const [email, setEmail] = useState('');

const { text, isListening, startListening } = useVoiceToText();

useEffect(() => {
if (text) {
setFormData((prev) => ({ ...prev, name: text }));
}
}, [text]);

function handleChange(e) {
e.preventDefault();
setFormData((prev) => ({
Expand Down Expand Up @@ -91,6 +101,12 @@ export function ManageList({ userId, list }) {
} catch (error) {}
}

function handleVoiceTransform() {
if (!isListening) {
startListening();
}
}

return (
<>
<p>
Expand All @@ -109,6 +125,12 @@ export function ManageList({ userId, list }) {
required
></input>

<button type="button" onClick={handleVoiceTransform}>
{isListening ? 'Listening...' : 'Start Voice Input'}
</button>

<br></br>

<label htmlFor="frequency">
When will you need this item again?:
</label>
Expand Down

0 comments on commit d171c59

Please sign in to comment.