diff --git a/src/components/api/recogComponent.tsx b/src/components/api/recogComponent.tsx index f927cd0..e4b2ce9 100644 --- a/src/components/api/recogComponent.tsx +++ b/src/components/api/recogComponent.tsx @@ -17,6 +17,7 @@ import { STTRenderer } from '../sttRenderer'; import { PlaybackStatus, StreamTextStatus } from '../../react-redux&middleware/redux/types/apiTypes'; import Swal from 'sweetalert2'; import { useRecognition } from './returnAPI'; +import { selectSelectedModel } from '../../react-redux&middleware/redux/reducers/modelSelectionReducers'; export const RecogComponent: React.FC = (props) => { @@ -82,9 +83,10 @@ export const RecogComponent: React.FC = (props) => { const sRecog = useSelector((state: RootState) => { return state.SRecognitionReducer as SRecognition; }) + const selectedModelOption = useSelector(selectSelectedModel); // if else for whisper transcript, apiStatus for 4=whisper and control status for listening - const transcript = useRecognition(sRecog, apiStatus, controlStatus, azureStatus, streamTextStatus, scribearServerStatus, playbackStatus); + const transcript = useRecognition(sRecog, apiStatus, controlStatus, azureStatus, streamTextStatus, scribearServerStatus, selectedModelOption, playbackStatus); // console.log("Recog component received new transcript: ", transcript) return STTRenderer(transcript); }; diff --git a/src/components/api/returnAPI.tsx b/src/components/api/returnAPI.tsx index 141783f..6a1a608 100644 --- a/src/components/api/returnAPI.tsx +++ b/src/components/api/returnAPI.tsx @@ -1,6 +1,6 @@ // import * as sdk from 'microsoft-cognitiveservices-speech-sdk' import installCOIServiceWorker from './coi-serviceworker' -import { API, PlaybackStatus} from '../../react-redux&middleware/redux/typesImports'; +import { API, PlaybackStatus } from '../../react-redux&middleware/redux/typesImports'; import { ApiStatus, AzureStatus, @@ -9,7 +9,7 @@ import { StreamTextStatus, ScribearServerStatus } from '../../react-redux&middleware/redux/typesImports'; -import {useEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import { batch, useDispatch, useSelector } from 'react-redux'; import { AzureRecognizer } from './azure/azureRecognizer'; @@ -23,6 +23,7 @@ import { WhisperRecognizer } from './whisper/whisperRecognizer'; import { PlaybackRecognizer } from './playback/playbackRecognizer'; import { ScribearRecognizer } from './scribearServer/scribearRecognizer'; +import type { SelectedOption } from '../../react-redux&middleware/redux/types/modelSelection'; // import { PlaybackReducer } from '../../react-redux&middleware/redux/reducers/apiReducers'; // controls what api to send and what to do when error handling. @@ -55,9 +56,9 @@ export const returnRecogAPI = (api : ApiStatus, control : ControlStatus, azure : */ -const createRecognizer = (currentApi: number, control: ControlStatus, azure: AzureStatus, streamTextConfig: StreamTextStatus, scribearServerStatus: ScribearServerStatus,playbackStatus:PlaybackStatus): Recognizer => { +const createRecognizer = (currentApi: number, control: ControlStatus, azure: AzureStatus, streamTextConfig: StreamTextStatus, scribearServerStatus: ScribearServerStatus, selectedModelOption: SelectedOption, playbackStatus: PlaybackStatus): Recognizer => { if (currentApi === API.SCRIBEAR_SERVER) { - return new ScribearRecognizer(scribearServerStatus, control.speechLanguage.CountryCode); + return new ScribearRecognizer(scribearServerStatus, selectedModelOption, control.speechLanguage.CountryCode); } else if (currentApi === API.PLAYBACK) { return new PlaybackRecognizer(playbackStatus); } @@ -65,7 +66,7 @@ const createRecognizer = (currentApi: number, control: ControlStatus, azure: Azu return new WebSpeechRecognizer(null, control.speechLanguage.CountryCode); } else if (currentApi === API.AZURE_TRANSLATION) { return new AzureRecognizer(null, control.speechLanguage.CountryCode, azure); - } + } else if (currentApi === API.AZURE_CONVERSATION) { throw new Error("Not implemented"); } @@ -92,9 +93,9 @@ const updateTranscript = (dispatch: Dispatch) => (newFinalBlocks: Array { for (const block of newFinalBlocks) { - dispatch({type: "transcript/new_final_block", payload: block}); + dispatch({ type: "transcript/new_final_block", payload: block }); } - dispatch({type: 'transcript/update_in_progress_block', payload: newInProgressBlock}); + dispatch({ type: 'transcript/update_in_progress_block', payload: newInProgressBlock }); }) } @@ -111,8 +112,8 @@ const updateTranscript = (dispatch: Dispatch) => (newFinalBlocks: Array { +export const useRecognition = (sRecog: SRecognition, api: ApiStatus, control: ControlStatus, + azure: AzureStatus, streamTextConfig: StreamTextStatus, scribearServerStatus, selectedModelOption: SelectedOption, playbackStatus: PlaybackStatus) => { const [recognizer, setRecognizer] = useState(); // TODO: Add a reset button to utitlize resetTranscript @@ -129,9 +130,9 @@ export const useRecognition = (sRecog : SRecognition, api : ApiStatus, control : console.log("UseRecognition, switching to new recognizer: ", api.currentApi); let newRecognizer: Recognizer | null; - try{ + try { // Create new recognizer, and subscribe to its events - newRecognizer = createRecognizer(api.currentApi, control, azure, streamTextConfig, scribearServerStatus, playbackStatus); + newRecognizer = createRecognizer(api.currentApi, control, azure, streamTextConfig, scribearServerStatus, selectedModelOption, playbackStatus); newRecognizer.onTranscribed(updateTranscript(dispatch)); setRecognizer(newRecognizer) @@ -148,7 +149,7 @@ export const useRecognition = (sRecog : SRecognition, api : ApiStatus, control : // Stop current recognizer when switching to another one, if possible newRecognizer?.stop(); } - }, [api.currentApi, azure, control, streamTextConfig, playbackStatus, scribearServerStatus]); + }, [api.currentApi, azure, control, streamTextConfig, playbackStatus, scribearServerStatus, selectedModelOption]); // Start / stop recognizer, if listening toggled useEffect(() => { @@ -173,7 +174,7 @@ export const useRecognition = (sRecog : SRecognition, api : ApiStatus, control : }, [azure.phrases]); // TODO: whisper's transcript is not in redux store but only in sessionStorage at the moment. - let transcript : string = useSelector((state: RootState) => { + let transcript: string = useSelector((state: RootState) => { return state.TranscriptReducer.transcripts[0].toString() }); // if (api.currentApi === API.WHISPER) { diff --git a/src/components/api/scribearServer/scribearRecognizer.tsx b/src/components/api/scribearServer/scribearRecognizer.tsx index 5309681..0ae4508 100644 --- a/src/components/api/scribearServer/scribearRecognizer.tsx +++ b/src/components/api/scribearServer/scribearRecognizer.tsx @@ -1,7 +1,10 @@ import { Recognizer } from '../recognizer'; import { TranscriptBlock } from '../../../react-redux&middleware/redux/types/TranscriptTypes'; import { ScribearServerStatus } from '../../../react-redux&middleware/redux/typesImports'; -import RecordRTC, {StereoAudioRecorder} from 'recordrtc'; +import RecordRTC, { StereoAudioRecorder } from 'recordrtc'; +import { store } from '../../../store' +import { setModelOptions, setSelectedModel } from '../../../react-redux&middleware/redux/reducers/modelSelectionReducers'; +import type { SelectedOption } from '../../../react-redux&middleware/redux/types/modelSelection'; enum BackendTranscriptBlockType { @@ -19,14 +22,18 @@ type BackendTranscriptBlock = { export class ScribearRecognizer implements Recognizer { - private scribearServerStatus : ScribearServerStatus - private socket : WebSocket | null = null - private transcribedCallback : any + private scribearServerStatus: ScribearServerStatus + private selectedModelOption: SelectedOption + private socket: WebSocket | null = null + private ready = false; + private transcribedCallback: any private errorCallback?: (e: Error) => void; - private language : string + private language: string private recorder?: RecordRTC; private kSampleRate = 16000; + urlParams = new URLSearchParams(window.location.search); + mode = this.urlParams.get('mode'); /** * Creates an Azure recognizer instance that listens to the default microphone @@ -34,14 +41,16 @@ export class ScribearRecognizer implements Recognizer { * @param audioSource Not implemented yet * @param language Expected language of the speech to be transcribed */ - constructor(scribearServerStatus: ScribearServerStatus, language:string) { + constructor(scribearServerStatus: ScribearServerStatus, selectedModelOption: SelectedOption, language: string) { console.log("ScribearRecognizer, new recognizer being created!") + this.language = language; + this.selectedModelOption = selectedModelOption; this.scribearServerStatus = scribearServerStatus; } private async _startRecording() { - let mic_stream = await navigator.mediaDevices.getUserMedia({audio: true, video: false}); + let mic_stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: false }); this.recorder = new RecordRTC(mic_stream, { type: 'audio', @@ -53,7 +62,7 @@ export class ScribearRecognizer implements Recognizer { }, recorderType: StereoAudioRecorder, numberOfAudioChannels: 1, - }); + }); this.recorder.startRecording(); } @@ -64,26 +73,47 @@ export class ScribearRecognizer implements Recognizer { */ start() { console.log("ScribearRecognizer.start()"); - if(this.socket) {return;} + if (this.socket) { return; } const scribearURL = new URL(this.scribearServerStatus.scribearServerAddress) - if (scribearURL.pathname != '/sink') { + if (scribearURL.pathname !== '/sink') { this._startRecording(); } this.socket = new WebSocket(this.scribearServerStatus.scribearServerAddress); - // this.socket.onopen = (e)=> {...} + this.socket.onopen = (event) => { + this.socket?.send(JSON.stringify({ + api_key: this.scribearServerStatus.scribearServerKey, + sourceToken: this.scribearServerStatus.scribearServerKey, + sessionToken: this.scribearServerStatus.scribearServerSessionToken, + })); + } + const inProgressBlock = new TranscriptBlock(); - - this.socket.onmessage = (event)=> { + + this.socket.onmessage = (event) => { + if (!this.ready && this.mode !== 'student') { + const message = JSON.parse(event.data); + console.log(message); + if (message['error'] || !Array.isArray(message)) return; + + store.dispatch(setModelOptions(message)); + + if (this.selectedModelOption) { + this.socket?.send(JSON.stringify(this.selectedModelOption)); + this.ready = true; + } + return; + } + const server_block: BackendTranscriptBlock = JSON.parse(event.data); // Todo: extract type of message (inprogress v final) and the text from the message const inProgress = server_block.type === BackendTranscriptBlockType.InProgress; const text = server_block.text; - if(inProgress) { + if (inProgress) { inProgressBlock.text = text; // replace text this.transcribedCallback([], inProgressBlock); } else { @@ -99,7 +129,7 @@ export class ScribearRecognizer implements Recognizer { console.error("WebSocket error event:", event); this.errorCallback?.(error); }; - + this.socket.onclose = (event) => { console.warn(`WebSocket closed: code=${event.code}, reason=${event.reason}`); this.socket = null; @@ -117,7 +147,7 @@ export class ScribearRecognizer implements Recognizer { stop() { console.log("ScribearRecognizer.stop()"); this.recorder?.stopRecording(); - if(! this.socket) {return;} + if (!this.socket) { return; } this.socket.close(); this.socket = null; } @@ -130,7 +160,7 @@ export class ScribearRecognizer implements Recognizer { onTranscribed(callback: (newFinalBlocks: Array, newInProgressBlock: TranscriptBlock) => void) { console.log("ScribearRecognizer.onTranscribed()"); // "recognizing" event signals that the in-progress block has been updated - this.transcribedCallback = callback; + this.transcribedCallback = callback; } /** diff --git a/src/components/navbars/sidebar/model/menu.tsx b/src/components/navbars/sidebar/model/menu.tsx new file mode 100644 index 0000000..72809ab --- /dev/null +++ b/src/components/navbars/sidebar/model/menu.tsx @@ -0,0 +1,56 @@ +import React from 'react'; + +import { List, ListItemText, Collapse, ListItem, MemoryIcon, Autocomplete, TextField, Tooltip } from '../../../../muiImports'; +import { useSelector } from 'react-redux'; +import type { RootState } from '../../../../store'; +import { API, type ApiStatus } from '../../../../react-redux&middleware/redux/typesImports'; +import { useDispatch } from 'react-redux'; +import { selectModelOptions, selectSelectedModel, setSelectedModel } from '../../../../react-redux&middleware/redux/reducers/modelSelectionReducers'; + +export default function ModelMenu(props) { + const dispatch = useDispatch(); + const APIStatus = useSelector((state: RootState) => { + return state.APIStatusReducer as ApiStatus; + }); + const modelOptions = useSelector(selectModelOptions); + const selected = useSelector(selectSelectedModel); + const modelSelectEnable = APIStatus.currentApi !== API.SCRIBEAR_SERVER; + + return ( +
+ {props.listItemHeader("Model", "model", MemoryIcon)} + + + + + + + + + { + dispatch(setSelectedModel(val)) + }} + defaultValue={selected} + getOptionLabel={(v) => v.display_name} + isOptionEqualToValue={(a, b) => a.model_key === b.model_key} + renderInput={(params) => } + renderOption={(props, option) => { + return + + {option.display_name} + + + }} + /> + + + + +
+ ); +} \ No newline at end of file diff --git a/src/components/navbars/sidebar/sidebar.tsx b/src/components/navbars/sidebar/sidebar.tsx index 6df9b4a..9caa0fb 100644 --- a/src/components/navbars/sidebar/sidebar.tsx +++ b/src/components/navbars/sidebar/sidebar.tsx @@ -6,10 +6,12 @@ import LangMenu from './language/menu'; import PhraseMenu from './phrase/menu'; import VisualizationMenu from './audioVisBar/menu'; import CaptionsMenu from './captions/menu'; +import ModelMenu from './model/menu'; export default function SideBar(props) { const [state, setState] = React.useState({ + model: false, display: false, lang: true, visualization: false, @@ -48,7 +50,10 @@ export default function SideBar(props) { - + + + + diff --git a/src/components/navbars/topbar/api/ScribearServerSettings.tsx b/src/components/navbars/topbar/api/ScribearServerSettings.tsx index 9068581..79c34ef 100644 --- a/src/components/navbars/topbar/api/ScribearServerSettings.tsx +++ b/src/components/navbars/topbar/api/ScribearServerSettings.tsx @@ -88,6 +88,29 @@ export default function ScribearServerSettings(props) { }} style={{ width: '100%' }} /> + + + + :not(style)': { pr: '1vw', width: '15vw' }, + }} + noValidate + autoComplete="off" + onSubmit={closePopup} // Prevent default submission behavior of refreshing page + > + diff --git a/src/components/navbars/topbar/apiDropdown.tsx b/src/components/navbars/topbar/apiDropdown.tsx index 76111c2..b5d1e60 100644 --- a/src/components/navbars/topbar/apiDropdown.tsx +++ b/src/components/navbars/topbar/apiDropdown.tsx @@ -14,11 +14,11 @@ import { useDispatch, useSelector } from "react-redux"; import { ScribearServerStatus } from '../../../react-redux&middleware/redux/typesImports'; const currTheme = createTheme({ - palette: { - primary: { - main: '#ffffff' - } - }, + palette: { + primary: { + main: '#ffffff' + } + }, }); export default function ApiDropdown(props) { @@ -40,28 +40,30 @@ export default function ApiDropdown(props) { // Automatically use scribear server as sink when in student mode or as sourcesink if in kiosk mode useEffect(() => { - function switchToScribeARServer(scribearServerAddress: string) { + function switchToScribeARServer(scribearServerAddress: string, token: string | undefined) { // Set new scribear server address let copyScribearServerStatus = Object.assign({}, scribearServerStatus); copyScribearServerStatus.scribearServerAddress = scribearServerAddress - - dispatch({type: 'CHANGE_SCRIBEAR_SERVER_ADDRESS', payload: {scribearServerAddress}}); - - // Switch to scribear server - let copyStatus = Object.assign({}, apiStatus); - copyStatus.currentApi = API.SCRIBEAR_SERVER; - copyStatus.webspeechStatus = STATUS.AVAILABLE; - copyStatus.azureConvoStatus = STATUS.AVAILABLE; - copyStatus.whisperStatus = STATUS.AVAILABLE; - copyStatus.streamTextStatus = STATUS.AVAILABLE; - copyStatus.playbackStatus = STATUS.AVAILABLE; - copyStatus.scribearServerStatus = STATUS.TRANSCRIBING; + copyScribearServerStatus.scribearServerKey = sourceToken as string; + copyScribearServerStatus.scribearServerSessionToken = token + + dispatch({ type: 'CHANGE_SCRIBEAR_SERVER_ADDRESS', payload: copyScribearServerStatus }); - dispatch({type: 'CHANGE_API_STATUS', payload: copyStatus}); - } + // Switch to scribear server + let copyStatus = Object.assign({}, apiStatus); + copyStatus.currentApi = API.SCRIBEAR_SERVER; + copyStatus.webspeechStatus = STATUS.AVAILABLE; + copyStatus.azureConvoStatus = STATUS.AVAILABLE; + copyStatus.whisperStatus = STATUS.AVAILABLE; + copyStatus.streamTextStatus = STATUS.AVAILABLE; + copyStatus.playbackStatus = STATUS.AVAILABLE; + copyStatus.scribearServerStatus = STATUS.TRANSCRIBING; + + dispatch({ type: 'CHANGE_API_STATUS', payload: copyStatus }); + } if (mode === 'kiosk') { - switchToScribeARServer(`ws://${kioskServerAddress}/sourcesink?sourceToken=${sourceToken}`); + switchToScribeARServer(`ws://${kioskServerAddress}/sourcesink`, undefined); } else if (mode === 'student') { console.log("Sending startSession POST with accessToken:", accessToken); fetch(`http://${serverAddress}/startSession`, { @@ -75,9 +77,9 @@ export default function ApiDropdown(props) { .then(data => { console.log('Session token:', data.sessionToken); - const scribearServerAddress = `ws://${serverAddress}/sink?sessionToken=${encodeURIComponent(data.sessionToken)}`; + const scribearServerAddress = `ws://${serverAddress}/sink`; - switchToScribeARServer(scribearServerAddress); + switchToScribeARServer(scribearServerAddress, data.sessionToken); }) .catch(error => { console.error('Error starting session:', error); @@ -97,7 +99,7 @@ export default function ApiDropdown(props) { // Make this a dropdown menu with the current api as the menu title return ( - + {props.apiDisplayName} @@ -141,7 +143,7 @@ export default function ApiDropdown(props) { transformOrigin={{ horizontal: 'center', vertical: 'top' }} anchorOrigin={{ horizontal: 'center', vertical: 'bottom' }} > - + ); diff --git a/src/muiImports.tsx b/src/muiImports.tsx index cc0ae89..a08a7b1 100644 --- a/src/muiImports.tsx +++ b/src/muiImports.tsx @@ -54,7 +54,7 @@ import SubtitlesIcon from '@mui/icons-material/Subtitles'; import FormatColorTextIcon from '@mui/icons-material/FormatColorText'; import Autocomplete from '@mui/material/Autocomplete'; import { Switch } from '@mui/material'; - +import MemoryIcon from '@mui/icons-material/Memory'; export { createTheme, Autocomplete, @@ -114,4 +114,5 @@ export { FormatColorTextIcon, ArchitectureIcon, Switch, + MemoryIcon } \ No newline at end of file diff --git a/src/react-redux&middleware/redux/reducers/apiReducers.tsx b/src/react-redux&middleware/redux/reducers/apiReducers.tsx index 38b01da..23abe2b 100644 --- a/src/react-redux&middleware/redux/reducers/apiReducers.tsx +++ b/src/react-redux&middleware/redux/reducers/apiReducers.tsx @@ -1,6 +1,6 @@ import { API, STATUS } from '../types/apiEnums'; import { ApiStatus, AzureStatus, PhraseList, PhraseListStatus } from "../typesImports"; -import { StreamTextStatus, WhisperStatus,ScribearServerStatus, PlaybackStatus } from "../types/apiTypes"; +import { StreamTextStatus, WhisperStatus, ScribearServerStatus, PlaybackStatus } from "../types/apiTypes"; const initialAPIStatusState: ApiStatus = { currentApi: API.WEBSPEECH, @@ -9,8 +9,8 @@ const initialAPIStatusState: ApiStatus = { azureConvoStatus: STATUS.AVAILABLE, whisperStatus: STATUS.AVAILABLE, streamTextStatus: STATUS.AVAILABLE, - scribearServerStatus : STATUS.AVAILABLE, - playbackStatus : STATUS.AVAILABLE + scribearServerStatus: STATUS.AVAILABLE, + playbackStatus: STATUS.AVAILABLE } const initialPhraseList: PhraseList = { @@ -47,7 +47,9 @@ const initialPlaybackStatus: PlaybackStatus = { } const initialScribearServerState: ScribearServerStatus = { - scribearServerAddress: 'ws://localhost:1234' + scribearServerAddress: 'ws://localhost:8080/sourcesink', + scribearServerKey: '', + scribearServerSessionToken: undefined, } const saveLocally = (varName: string, value: any) => { @@ -88,7 +90,7 @@ const getLocalState = (name: string) => { } else if (name === "scribearServerStatus") { return saveLocally("scribearServerStatus", initialScribearServerState); } - return {} ; + return {}; }; export const APIStatusReducer = (state = getLocalState("apiStatus") as ApiStatus, action) => { @@ -105,15 +107,17 @@ export const APIStatusReducer = (state = getLocalState("apiStatus") as ApiStatus } } -export const AzureReducer = (state = getLocalState("azureStatus") , action) => { +export const AzureReducer = (state = getLocalState("azureStatus"), action) => { switch (action.type) { case 'CHANGE_AZURE_LOGIN': return { ...state, ...action.payload }; case 'CHANGE_AZURE_STATUS': return { ...state, ...action.payload }; case 'CHANGE_LIST': - return { ...state, - phrases: action.payload } + return { + ...state, + phrases: action.payload + } default: return state; } @@ -124,10 +128,10 @@ export const WhisperReducer = (state = getLocalState("whisperStatus"), action) = } export const StreamTextReducer = (state = getLocalState("streamTextStatus"), action) => { - switch(action.type) { + switch (action.type) { case 'CHANGE_STREAMTEXT_STATUS': return { ...state, ...action.payload }; - + default: return state; } @@ -135,22 +139,22 @@ export const StreamTextReducer = (state = getLocalState("streamTextStatus"), act export const ScribearServerReducer = (state = getLocalState("scribearServerStatus"), action) => { switch (action.type) { case 'CHANGE_SCRIBEAR_SERVER_ADDRESS': - const newState = { ...state, ...action.payload }; + const newState = { ...state, ...action.payload }; saveLocally('scribearServerStatus', newState); return newState; - + default: return state; } } export const PlaybackReducer = (state = getLocalState("playbackStatus"), action) => { - - switch(action.type) { + + switch (action.type) { case 'CHANGE_PLAYBACK_STATUS': console.log("PlaybackReducer CHANGE_PLAYBACK_STATUS: status & action", state, action); return { ...state, ...action.payload }; - + default: return state; } @@ -195,13 +199,17 @@ export const PhraseListReducer = (state = initialPhraseListState, action) => { ...state, } } - case 'EDIT_PHRASE_LIST': - return { ...state, - currentPhraseList: action.payload} + case 'EDIT_PHRASE_LIST': + return { + ...state, + currentPhraseList: action.payload + } // existing cases - case 'SET_FILE_CONTENT': - return { ...state, - fileContent: action.payload} + case 'SET_FILE_CONTENT': + return { + ...state, + fileContent: action.payload + } case 'SET_PHRASE_OPTION_TO_CUSTOM': { const newPhraseListMap = new Map(state.phraseListMap); const phraseData: PhraseList = newPhraseListMap.get(action.payload.phraseName) || { diff --git a/src/react-redux&middleware/redux/reducers/modelSelectionReducers.tsx b/src/react-redux&middleware/redux/reducers/modelSelectionReducers.tsx new file mode 100644 index 0000000..20c1f00 --- /dev/null +++ b/src/react-redux&middleware/redux/reducers/modelSelectionReducers.tsx @@ -0,0 +1,52 @@ +import type { ModelOptions, ModelSelection, SelectedOption } from '../types/modelSelection' +import type { RootState } from '../typesImports' + +const initialState: ModelSelection = { options: [], selected: null } + + +const saveLocally = (key: string, value: any) => { + localStorage.setItem(key, JSON.stringify(value)); +} +const getLocalState = (key: string) => { + const localState = localStorage.getItem(key); + + if (localState) { + return Object.assign(initialState, JSON.parse(localState)); + } + + return initialState; +} + +export const ModelSelectionReducer = ( + state = getLocalState('modelSelection'), + action +) => { + let newState; + switch (action.type) { + case 'SET_MODEL_OPTIONS': + newState = { + ...state, + options: action.payload as ModelOptions + } + saveLocally('modelSelection', newState) + return newState; + case 'SET_SELECTED_MODEL': + newState = { + ...state, + selected: action.payload as SelectedOption + } + saveLocally('modelSelection', newState) + return newState; + default: + return state + } +} + +export const selectModelOptions = (state: RootState) => state.ModelSelectionReducer.options; +export const setModelOptions = (options: ModelOptions) => { + return { type: 'SET_MODEL_OPTIONS', payload: options } +} +export const selectSelectedModel = (state: RootState) => state.ModelSelectionReducer.selected; +export function setSelectedModel(selected: SelectedOption) { + return { type: 'SET_SELECTED_MODEL', payload: selected } +} \ No newline at end of file diff --git a/src/react-redux&middleware/redux/types/apiTypes.tsx b/src/react-redux&middleware/redux/types/apiTypes.tsx index 1c6c755..63c6dbf 100644 --- a/src/react-redux&middleware/redux/types/apiTypes.tsx +++ b/src/react-redux&middleware/redux/types/apiTypes.tsx @@ -65,6 +65,8 @@ export type PlaybackStatus = { } export type ScribearServerStatus = { - scribearServerAddress : string + scribearServerAddress: string + scribearServerKey: string + scribearServerSessionToken: string | undefined // Many IP (or hostname) and port in the future } \ No newline at end of file diff --git a/src/react-redux&middleware/redux/types/modelSelection.tsx b/src/react-redux&middleware/redux/types/modelSelection.tsx new file mode 100644 index 0000000..8a4603f --- /dev/null +++ b/src/react-redux&middleware/redux/types/modelSelection.tsx @@ -0,0 +1,18 @@ +export type ModelOptions = Array<{ + model_key: string; + display_name: string; + description: string; + available_features: string; +}>; + +export type SelectedOption = { + model_key: string; + display_name: string; + description: string; + available_features: string; +} | null; + +export type ModelSelection = { + options: ModelOptions, + selected: SelectedOption +}; diff --git a/src/react-redux&middleware/redux/typesImports.tsx b/src/react-redux&middleware/redux/typesImports.tsx index f55c8cd..b06f5f3 100644 --- a/src/react-redux&middleware/redux/typesImports.tsx +++ b/src/react-redux&middleware/redux/typesImports.tsx @@ -1,12 +1,15 @@ import { API, ApiType, STATUS, StatusType } from './types/apiEnums'; -import { ApiStatus, AzureStatus, PhraseList, PhraseListStatus, - StreamTextStatus, WhisperStatus, ScribearServerStatus, PlaybackStatus } from './types/apiTypes'; +import { + ApiStatus, AzureStatus, PhraseList, PhraseListStatus, + StreamTextStatus, WhisperStatus, ScribearServerStatus, PlaybackStatus +} from './types/apiTypes'; import { ControlStatus, LanguageList } from "./types/controlStatus"; import { SRecognition, ScribeHandler, ScribeRecognizer } from "./types/sRecognition"; import { Sentence, Word } from "./types/TranscriptTypes"; import { DisplayStatus } from "./types/displayStatus"; import { MainStream } from "./types/bucketStreamTypes"; +import type { ModelSelection } from './types/modelSelection'; export type { ApiStatus, @@ -44,9 +47,10 @@ export type RootState = { AzureReducer: AzureStatus ControlReducer: ControlStatus PhraseListReducer: PhraseListStatus - initialStreams : MainStream + initialStreams: MainStream WhisperReducer: WhisperStatus StreamTextReducer: StreamTextStatus - ScribearServerReducer : ScribearServerStatus - PlaybackReducer : PlaybackStatus + ScribearServerReducer: ScribearServerStatus + PlaybackReducer: PlaybackStatus, + ModelSelectionReducer: ModelSelection } \ No newline at end of file diff --git a/src/store.tsx b/src/store.tsx index 4a2f1ea..8b1f8a3 100644 --- a/src/store.tsx +++ b/src/store.tsx @@ -1,5 +1,7 @@ -import { APIStatusReducer, AzureReducer, PhraseListReducer, StreamTextReducer, - WhisperReducer,ScribearServerReducer,PlaybackReducer } from './react-redux&middleware/redux/reducers/apiReducers' +import { + APIStatusReducer, AzureReducer, PhraseListReducer, StreamTextReducer, + WhisperReducer, ScribearServerReducer, PlaybackReducer +} from './react-redux&middleware/redux/reducers/apiReducers' import { combineReducers } from 'redux' import { BucketStreamReducer } from './react-redux&middleware/redux/reducers/bucketStreamReducers' @@ -10,6 +12,7 @@ import { TranscriptReducer } from './react-redux&middleware/redux/reducers/trans import { configureStore } from '@reduxjs/toolkit' import thunkMiddleware from 'redux-thunk' +import { ModelSelectionReducer } from './react-redux&middleware/redux/reducers/modelSelectionReducers' const rootReducer = combineReducers({ DisplayReducer, @@ -23,7 +26,8 @@ const rootReducer = combineReducers({ TranscriptReducer, SRecognitionReducer, ScribearServerReducer, - PlaybackReducer + PlaybackReducer, + ModelSelectionReducer }) // export const store = createStore(rootReducer)