Skip to content

Commit

Permalink
Make a recite fab button (#2)
Browse files Browse the repository at this point in the history
* Make a recite fab button

This change will add a recite fab button as the main action on the recite screen

* Major context refactor

* Fixing render errors

* Remove console log
  • Loading branch information
alexjpaz authored Dec 31, 2019
1 parent 36eedf5 commit a44ce0b
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 108 deletions.
10 changes: 8 additions & 2 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,22 @@ import './App.css';
import data from './datasources/foo.json';

import AppTheme from './AppTheme';

import { DefaultReciteContextProvider } from './ReciteContext';
import AppContext from './AppContext';

import AppViewport from './components/layout/AppViewport';

function App() {
const [ state ] = React.useState(data);
const [ state ] = React.useState(Object.assign({
}, data));

return (
<AppTheme>
<AppContext.Provider value={state}>
<AppViewport />
<DefaultReciteContextProvider>
<AppViewport />
</DefaultReciteContextProvider>
</AppContext.Provider>
</AppTheme>
);
Expand Down
7 changes: 6 additions & 1 deletion src/AppContext.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import React from 'react';

export const AppContext = React.createContext({});
export const defaultValue = {
name: "unknown",
phrases: [],
};

export const AppContext = React.createContext(defaultValue);

export default AppContext;
141 changes: 141 additions & 0 deletions src/ReciteContext.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import React from 'react';
export const defaultValue = {
state: null,
audioUrl: null,
recognition: null,
setRecordingState: () => {},
record: () => { console.log("Not implemented!"); },
};

export const ReciteContext = React.createContext(defaultValue);

export class DefaultReciteContextProvider extends React.Component {
constructor(props) {
super(props);

let mediaRecorder;

const setRecordingState = (recordingState) => {;
this.setState({
...this.state,
state: recordingState,
});
};

let setAudioUrl = (audioUrl) => {
this.setState({
...this.state,
audioUrl,
});
};

let setRecognition = (recognition) => {
this.setState((state) => ({
...this.state,
recognition
}));
};

const record = ({ targetLanguageCode }) => {
setRecognition("listening");


if(this.state.state !== "stopped") {
let { recognition } = this.state;

if(recognition) {
recognition.abort();
}

setRecordingState("stopped");
setRecognition(null);

if(mediaRecorder) {
mediaRecorder.stop();
}

return;
}

if(navigator.mediaDevices) {
let chunks = [];
navigator.mediaDevices.getUserMedia({ audio: true })
.then(function(stream) {

mediaRecorder = new MediaRecorder(stream);

mediaRecorder.start();

mediaRecorder.onstart = function(e) {
};

mediaRecorder.onstop = function(e) {
setRecordingState("stopped");

var blob = new Blob(chunks, { 'type' : 'audio/ogg; codecs=opus' });
chunks = [];

var audioURL = URL.createObjectURL(blob);

var audio = new Audio();
audio.src = audioURL;
audio.play();

setAudioUrl(audioURL);
};

mediaRecorder.ondataavailable = function(e) {
chunks.push(e.data);
}
});
}

const recognition = new window.webkitSpeechRecognition();
recognition.continuous = false;
recognition.interimResults = false;
recognition.lang = targetLanguageCode;

recognition.addEventListener('audiostart', () => {
setRecordingState("listening");
});

recognition.addEventListener('soundstart', (e) => {
setRecordingState("recording");
});

recognition.addEventListener('soundend', (e) => {
recognition.stop();

if(mediaRecorder) {
mediaRecorder.stop();
}


setRecognition(null);
});

recognition.start();

setRecognition("listening");

setRecognition(recognition);

};

this.state = {
...defaultValue,
state: "stopped",
record,
};
}

render() {
return (
<ReciteContext.Provider value={this.state}>
{ this.props.children }
</ReciteContext.Provider>
);
}
}

export default ReciteContext;
3 changes: 2 additions & 1 deletion src/components/MyBox/MyBox.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import Box from '@material-ui/core/Box';

import PhraseCard from './PhraseCard'
import RecitePhraseCard from './RecitePhraseCard'
import ReciteFabButton from './ReciteFabButton';

function MyBox() {
const data = useContext(AppContext);
Expand Down Expand Up @@ -58,7 +59,7 @@ function MyBox() {
<StickyFooterBox>
<CenteredBox>
<Box mb={2}>
{/* <ReciteFabButton phrase={state.phrase.target } /> */}
<ReciteFabButton phrase={state.phrase} />
</Box>
<ButtonGroup aria-label="outlined navigation button group">
<Button onClick={back}>Back</Button>
Expand Down
19 changes: 12 additions & 7 deletions src/components/MyBox/ReciteFabButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,31 @@ import Fab from '@material-ui/core/Fab';
import Mic from '@material-ui/icons/Mic';
import RecordVoiceOverIcon from '@material-ui/icons/RecordVoiceOver';

import AppContext from '../../AppContext';
import ReciteContext from '../../ReciteContext';

export default function ReciteFabButton(props) {

let appContext = React.useContext(ReciteContext);

let icon = <Mic />;
let text = "Recite";

if(props.state === "listening") {
if(appContext.state === "listening") {
icon = <RecordVoiceOverIcon />
text = "Recording";
text = "Listening";
}

if(props.state === "recording") {
if(appContext.state === "recording") {
icon = <RecordVoiceOverIcon />
text = "Recording";
}

const record = () => appContext.record(props.phrase);

return (
<Fab variant="extended" {...props} color="secondary">
<Mic />
Recite
<Fab variant="extended" {...props} color="secondary" onClick={record}>
{ icon }
{ text }
</Fab>
);
}
Expand Down
109 changes: 12 additions & 97 deletions src/components/MyBox/RecitePhraseCard.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import PlayCircleOutlineIcon from '@material-ui/icons/PlayCircleOutline';

import ReciteButton from './ReciteButton';

import ReciteContext from '../../ReciteContext';

const useStyles = makeStyles({
card: {
minWidth: 275,
Expand Down Expand Up @@ -43,13 +45,13 @@ function RecognitionText({ defaultText, recognition }) {
setText("< No Match >");
});

recognition.onresult = (e) => {
recognition.addEventListener('result', (e) => {
if(!e.results) return;

let { transcript, } = e.results[0][0];

setText(transcript)
}
});

return () => {
recognition.onresult = function() {};
Expand All @@ -62,9 +64,13 @@ function RecognitionText({ defaultText, recognition }) {
export default function RecitePhraseCard(props) {
const classes = useStyles();

const [ recognition, setRecognition ] = React.useState(null);
const [ recordingState, setRecordingState ] = React.useState("stopped");
const [ audioUrl, setAudioUrl ] = React.useState(null);
const reciteContext = React.useContext(ReciteContext);

const { audioUrl } = reciteContext;

const { recognition } = reciteContext;

const { state: recordingState } = reciteContext;

const playAudioUrl = (e) => {
e.preventDefault();
Expand All @@ -74,98 +80,7 @@ export default function RecitePhraseCard(props) {
audio.play();
};

let mediaRecorder = null;

// FIXME: goofy javascript errors :/
const isRecording = () => recordingState !== "stopped";

const stopRecording = () => {
if(recognition) {
recognition.abort();
}

setRecordingState("stopped");
setRecognition(null);

if(mediaRecorder) {
mediaRecorder.stop();
}

return;
};

const record = (e) => {
e.preventDefault();
e.stopPropagation();

if(isRecording()) {
return stopRecording();
}

setRecordingState("listening");

if(navigator.mediaDevices) {
let chunks = [];
navigator.mediaDevices.getUserMedia({ audio: true })
.then(function(stream) {

mediaRecorder = new MediaRecorder(stream);

mediaRecorder.start();

mediaRecorder.onstart = function(e) {
};

mediaRecorder.onstop = function(e) {
setRecordingState("stopped");

var blob = new Blob(chunks, { 'type' : 'audio/ogg; codecs=opus' });
chunks = [];

var audioURL = URL.createObjectURL(blob);

var audio = new Audio();
audio.src = audioURL;
audio.play();

setAudioUrl(audioURL);
};

mediaRecorder.ondataavailable = function(e) {
chunks.push(e.data);
}
});
}

const recognition = new window.webkitSpeechRecognition();
recognition.continuous = false;
recognition.interimResults = false;
recognition.lang = props.phrase.targetLanguageCode; // TODO

recognition.onresult = (e) => {
console.log('soundresult', e);
}

recognition.onsoundstart = (e) => {
setRecordingState("recording");
};

recognition.onsoundend = (e) => {
recognition.stop();

if(mediaRecorder) {
mediaRecorder.stop();
}


setRecognition(null);
};

recognition.start();

setRecognition(recognition);
};

const record = () => reciteContext.record(props.phrase);

const repeat = (e) => playAudioUrl(e);

Expand Down

0 comments on commit a44ce0b

Please sign in to comment.