An easy-to-use platform for EEG experimentation in the classroom
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix typing and upgrade errors

+133 -122
+1
package.json
··· 24 24 "postlint-styles-fix": "prettier --single-quote --write '**/*.{css,scss}'", 25 25 "test": "cross-env jest --passWithNoTests", 26 26 "test-all": "npm run lint && npm run lint-styles && npm run typecheck && npm run build && npm test", 27 + "tsc": "tsc --noEmit", 27 28 "typecheck": "tsc --noEmit" 28 29 }, 29 30 "lint-staged": {
+3 -2
src/renderer/components/AnalyzeComponent.tsx
··· 13 13 } from 'semantic-ui-react'; 14 14 import { isNil, isArray, isString } from 'lodash'; 15 15 import Plot from 'react-plotly.js'; 16 + import type { Data as PlotlyData } from 'plotly.js'; 16 17 import styles from './styles/common.module.css'; 17 18 import { 18 19 DEVICES, ··· 95 96 // TODO: implement outlier display toggle 96 97 // displayOutlierVisible: boolean; 97 98 displayMode: string; 98 - dataToPlot: number[]; 99 + dataToPlot: PlotlyData[]; 99 100 layout: Record<string, any>; 100 101 helpMode: string; 101 102 dependentVariables: Array<{ ··· 118 119 eegFilePaths: [{ key: '', text: '', value: { name: '', dir: '' } }], 119 120 behaviorFilePaths: [{ key: '', text: '', value: '' }], 120 121 dependentVariables: [{ key: '', text: '', value: '' }], 121 - dataToPlot: [], 122 + dataToPlot: [] as PlotlyData[], 122 123 layout: {}, 123 124 selectedDependentVariable: '', 124 125 removeOutliers: true,
+13 -9
src/renderer/components/CleanComponent/index.tsx
··· 95 95 } 96 96 97 97 handleRecordingChange(event: Record<string, any>, data: DropdownProps) { 98 - if (isArray(data.value)) { 99 - const filePaths = data.value.filter<string>(isString); 98 + const { value } = data; 99 + if (isArray(value)) { 100 + const filePaths = (value as (string | number | boolean)[]).filter(isString) as string[]; 100 101 this.setState({ selectedFilePaths: filePaths }); 101 102 } 102 103 } 103 104 104 105 handleSubjectChange(event: Record<string, any>, data: DropdownProps) { 105 - if (!isNil(data) && isString(data.value)) { 106 - this.setState({ selectedSubject: data.value, selectedFilePaths: [] }); 106 + const { value } = data; 107 + if (!isNil(data) && isString(value)) { 108 + this.setState({ selectedSubject: value as string, selectedFilePaths: [] }); 107 109 } 108 110 } 109 111 ··· 143 145 (infoObj) => infoObj.name === 'Drop Percentage' 144 146 )?.value; 145 147 146 - if (drop && drop >= 2) { 148 + if (drop && typeof drop === 'number' && drop >= 2) { 147 149 return ( 148 150 <Link to="/analyze"> 149 151 <Button primary>Analyze Dataset</Button> ··· 208 210 closeOnChange 209 211 value={this.state.selectedFilePaths} 210 212 options={this.state.eegFilePaths.filter((filepath) => { 211 - if (isString(filepath.value)) { 212 - const subjectFromFilepath = filepath.value.split( 213 + const val = filepath.value; 214 + if (isString(val)) { 215 + const strVal = val as string; 216 + const subjectFromFilepath = strVal.split( 213 217 path.sep 214 - )[filepath.value.split(path.sep).length - 3]; 218 + )[strVal.split(path.sep).length - 3]; 215 219 return ( 216 220 this.state.selectedSubject === subjectFromFilepath 217 221 ); ··· 231 235 <Button 232 236 primary 233 237 disabled={isNil(this.props.epochsInfo)} 234 - onClick={this.props.PyodideActions.CleanEpochs} 238 + onClick={() => this.props.PyodideActions.CleanEpochs()} 235 239 > 236 240 Clean Data 237 241 </Button>
+1 -1
src/renderer/components/CollectComponent/ConnectModal.tsx
··· 160 160 <Modal.Content> 161 161 <Grid textAlign="center" columns="equal"> 162 162 <Grid.Column> 163 - {this.state.instructionProgress !== 0 && ( 163 + {(this.state.instructionProgress as number) !== 0 && ( 164 164 <Button 165 165 fluid 166 166 className={styles.secondaryButton}
+15 -10
src/renderer/components/DesignComponent/CustomDesignComponent.tsx
··· 148 148 value={this.state.params.description?.question} 149 149 placeholder="Explain your research question here." 150 150 onChange={(event, data) => { 151 - if (!isString(data.value)) { 151 + const val = data.value; 152 + if (!isString(val)) { 152 153 return; 153 154 } 154 - this.handleSetText(data.value, 'question'); 155 + this.handleSetText(val as string, 'question'); 155 156 }} 156 157 /> 157 158 </Form> ··· 172 173 value={this.state.params.description?.hypothesis} 173 174 placeholder="Describe your hypothesis here." 174 175 onChange={(event, data) => { 175 - if (!isString(data.value)) { 176 + const val = data.value; 177 + if (!isString(val)) { 176 178 return; 177 179 } 178 - this.handleSetText(data.value, 'hypothesis'); 180 + this.handleSetText(val as string, 'hypothesis'); 179 181 }} 180 182 /> 181 183 </Form> ··· 196 198 value={this.state.params.description?.methods} 197 199 placeholder="Explain how you will design your experiment to answer the question here." 198 200 onChange={(event, data) => { 199 - if (!isString(data.value)) { 201 + const val = data.value; 202 + if (!isString(val)) { 200 203 return; 201 204 } 202 - this.handleSetText(data.value, 'methods'); 205 + this.handleSetText(val as string, 'methods'); 203 206 }} 204 207 /> 205 208 </Form> ··· 563 566 value={this.state.params.intro} 564 567 placeholder="e.g., You will view a series of faces and houses. Press 1 when a face appears and 9 for a house. Press the the space bar on your keyboard to start doing the practice trials. If you want to skip the practice trials and go directly to the task, press the 'q' button on your keyboard." 565 568 onChange={(event, data) => { 566 - if (!isString(data.value)) { 569 + const val = data.value; 570 + if (!isString(val)) { 567 571 return; 568 572 } 569 573 this.setState({ 570 - params: { ...this.state.params, intro: data.value }, 574 + params: { ...this.state.params, intro: val as string }, 571 575 saved: false, 572 576 }); 573 577 }} ··· 593 597 value={this.state.params.taskHelp} 594 598 placeholder="e.g., Press 1 for a face and 9 for a house" 595 599 onChange={(event, data) => { 596 - if (!isString(data.value)) { 600 + const val = data.value; 601 + if (!isString(val)) { 597 602 return; 598 603 } 599 604 this.setState({ 600 - params: { ...this.state.params, taskHelp: data.value }, 605 + params: { ...this.state.params, taskHelp: val as string }, 601 606 saved: false, 602 607 }); 603 608 }}
+4 -3
src/renderer/components/DesignComponent/StimuliDesignColumn.tsx
··· 53 53 async handleSelectFolder() { 54 54 const dir = await loadFromSystemDialog(FILE_TYPES.STIMULUS_DIR); 55 55 if (dir && isString(dir)) { 56 - const images = readImages(dir); 56 + const images = await readImages(dir); 57 57 if (images.length < 1) { 58 58 toast.error('No images in folder!'); 59 59 } ··· 97 97 selection 98 98 value={this.props.response} 99 99 onChange={(event, data) => { 100 - if (data.value && isString(data.value)) { 100 + const val = data.value; 101 + if (val && isString(val)) { 101 102 this.props.onChange( 102 103 'response', 103 - data.value, 104 + val as string, 104 105 `stimulus${this.props.num}` 105 106 ); 106 107 }
+4 -3
src/renderer/components/DesignComponent/StimuliRow.tsx
··· 48 48 fluid 49 49 selection 50 50 value={response} 51 - onChange={(event, data) => 52 - onChange(num, 'response', isString(data.value) ? data.value : '') 53 - } 51 + onChange={(event, data) => { 52 + const val = data.value; 53 + onChange(num, 'response', isString(val) ? (val as string) : ''); 54 + }} 54 55 placeholder="Response" 55 56 options={RESPONSE_OPTIONS} 56 57 />
+2 -2
src/renderer/components/DesignComponent/index.tsx
··· 86 86 this.handleEEGEnabled = this.handleEEGEnabled.bind(this); 87 87 } 88 88 89 - componentDidMount() { 90 - this.setState({ recentWorkspaces: readWorkspaces() }); 89 + async componentDidMount() { 90 + this.setState({ recentWorkspaces: await readWorkspaces() }); 91 91 } 92 92 93 93 handleStepClick(step: string) {
+1 -1
src/renderer/components/ExperimentWindow.tsx
··· 30 30 const experimentClone = clonedeep(experimentObject); 31 31 const paramsClone = clonedeep(params); 32 32 experimentClone.parameters = paramsClone; 33 - const experimentToRun = lab.util.fromObject(experimentClone, lab); 33 + const experimentToRun = lab.core.deserialize(experimentClone, lab); 34 34 35 35 experimentToRun.parameters.title = title; 36 36 if (params.stimuli) {
+1 -1
src/renderer/components/HomeComponent/OverviewComponent.tsx
··· 89 89 }; 90 90 91 91 // Generic curreid enum type guard 92 - function isEnum<T>(en: T) { 92 + function isEnum<T extends object>(en: T) { 93 93 return (val: any): val is T[keyof T] => val in Object.values(en); 94 94 } 95 95
+27 -11
src/renderer/components/HomeComponent/index.tsx
··· 29 29 openWorkspaceDir, 30 30 deleteWorkspaceDir, 31 31 } from '../../utils/filesystem/storage'; 32 + import { ExperimentStateType } from '../../reducers/experimentReducer'; 32 33 import { 33 34 PyodideActions, 34 35 DeviceActions, ··· 74 75 isOverviewComponentOpen: boolean; 75 76 recentWorkspaces: Array<string>; 76 77 overviewExperimentType: EXPERIMENTS; 78 + workspaceStates: Record<string, ExperimentStateType | null>; 77 79 } 78 80 79 81 export default class Home extends Component<Props, State> { ··· 82 84 this.state = { 83 85 activeStep: this.props.activeStep || HOME_STEPS.RECENT, 84 86 recentWorkspaces: [], 87 + workspaceStates: {}, 85 88 isNewExperimentModalOpen: false, 86 89 isOverviewComponentOpen: false, 87 90 overviewExperimentType: EXPERIMENTS.NONE, ··· 95 98 this.handleDeleteWorkspace = this.handleDeleteWorkspace.bind(this); 96 99 } 97 100 98 - componentDidMount() { 101 + async componentDidMount() { 99 102 this.props.PyodideActions.Launch(); 100 - this.setState({ recentWorkspaces: readWorkspaces() }); 103 + const recentWorkspaces = await readWorkspaces(); 104 + this.setState({ recentWorkspaces }); 105 + await this.loadWorkspaceStates(recentWorkspaces); 106 + } 107 + 108 + async loadWorkspaceStates(workspaces: string[]) { 109 + const entries = await Promise.all( 110 + workspaces.map(async (dir) => [dir, await readAndParseState(dir)] as const) 111 + ); 112 + this.setState({ 113 + workspaceStates: Object.fromEntries(entries), 114 + }); 101 115 } 102 116 103 117 handleStepClick(step: string) { ··· 141 155 } 142 156 143 157 // Load recent workspace by copying saved 'experiment' redux state into current redux state 144 - handleLoadRecentWorkspace(dir: string) { 145 - const recentWorkspaceState = readAndParseState(dir); 146 - if (isNil(recentWorkspaceState)) { 158 + async handleLoadRecentWorkspace(dir: string) { 159 + const recentWorkspaceState = await readAndParseState(dir); 160 + if (recentWorkspaceState == null) { 147 161 toast.error( 148 162 'Workspace data is corrupted or missing. Please delete and create it again.' 149 163 ); ··· 157 171 experimentObject: getExperimentFromType(recentWorkspaceState.type) 158 172 .experimentObject, 159 173 }; 160 - this.props.ExperimentActions.SetState(deserializedWorkspaceState); 174 + this.props.ExperimentActions.SetState(deserializedWorkspaceState as any); 161 175 this.props.history.push(SCREENS.DESIGN.route); 162 176 } 163 177 ··· 182 196 const response = await window.electronAPI.showMessageBox(options); 183 197 if (response.response === 1) { 184 198 deleteWorkspaceDir(dir); 185 - this.setState({ recentWorkspaces: readWorkspaces() }); 199 + const recentWorkspaces = await readWorkspaces(); 200 + this.setState({ recentWorkspaces }); 201 + await this.loadWorkspaceStates(recentWorkspaces); 186 202 } 187 203 } 188 204 ··· 216 232 <Table.Body className={styles.experimentTable}> 217 233 {this.state.recentWorkspaces 218 234 .sort((a, b) => { 219 - const aState = readAndParseState(a); 220 - const bState = readAndParseState(b); 235 + const aState = this.state.workspaceStates[a]; 236 + const bState = this.state.workspaceStates[b]; 221 237 222 238 const aTime = aState?.dateModified || 0; 223 239 const bTime = bState?.dateModified || 0; ··· 225 241 return bTime - aTime; 226 242 }) 227 243 .map((dir) => { 228 - const workspaceState = readAndParseState(dir); 244 + const workspaceState = this.state.workspaceStates[dir]; 229 245 if (!workspaceState) { 230 246 return undefined; 231 247 } ··· 368 384 <Grid columns="two" relaxed padded> 369 385 <Grid.Row> 370 386 <Grid.Column> 371 - <Button onClick={this.props.PyodideActions.LoadTopo}> 387 + <Button onClick={() => this.props.PyodideActions.LoadTopo()}> 372 388 Generate Plot 373 389 </Button> 374 390 </Grid.Column>
+2 -4
src/renderer/components/PyodidePlotWidget.tsx
··· 40 40 this.props.plotMIMEBundle !== prevProps.plotMIMEBundle && 41 41 !isNil(this.props.plotMIMEBundle) 42 42 ) { 43 - const bundle: { 44 - [key: string]: string; 45 - } = this.props.plotMIMEBundle; 43 + const bundle = this.props.plotMIMEBundle as { [key: string]: string }; 46 44 const mimeType = richestMimetype( 47 45 bundle, 48 46 standardDisplayOrder, ··· 56 54 57 55 handleSave() { 58 56 const buf = Buffer.from(this.state.rawData, 'base64'); 59 - storePyodideImage(this.props.title, this.props.imageTitle, buf); 57 + storePyodideImage(this.props.title, this.props.imageTitle, buf.buffer as ArrayBuffer); 60 58 } 61 59 62 60 renderResults() {
+2 -6
src/renderer/components/SignalQualityIndicatorComponent.tsx
··· 34 34 } 35 35 36 36 componentWillUnmount() { 37 - if (!isNil(this.signalQualitySubscription)) { 38 - this.signalQualitySubscription.unsubscribe(); 39 - } 37 + this.signalQualitySubscription?.unsubscribe(); 40 38 } 41 39 42 40 subscribeToObservable(observable: any) { 43 - if (!isNil(this.signalQualitySubscription)) { 44 - this.signalQualitySubscription.unsubscribe(); 45 - } 41 + this.signalQualitySubscription?.unsubscribe(); 46 42 47 43 this.signalQualitySubscription = observable.subscribe( 48 44 (epoch) => {
+5 -5
src/renderer/components/TopNavComponent/index.tsx
··· 53 53 return styles.initialNavColumn; 54 54 } 55 55 56 - updateWorkspaces = () => { 57 - this.setState({ recentWorkspaces: readWorkspaces() }); 56 + updateWorkspaces = async () => { 57 + this.setState({ recentWorkspaces: await readWorkspaces() }); 58 58 }; 59 59 60 - handleLoadRecentWorkspace(dir: string) { 61 - const recentWorkspaceState = readAndParseState(dir); 62 - if (!isNil(recentWorkspaceState)) { 60 + async handleLoadRecentWorkspace(dir: string) { 61 + const recentWorkspaceState = await readAndParseState(dir); 62 + if (recentWorkspaceState != null) { 63 63 this.props.ExperimentActions.SetState(recentWorkspaceState); 64 64 } 65 65 }
+2 -6
src/renderer/components/ViewerComponent.tsx
··· 96 96 } 97 97 98 98 componentWillUnmount() { 99 - if (!isNil(this.signalQualitySubscription)) { 100 - this.signalQualitySubscription.unsubscribe(); 101 - } 99 + this.signalQualitySubscription?.unsubscribe(); 102 100 Mousetrap.unbind('up'); 103 101 Mousetrap.unbind('down'); 104 102 } ··· 109 107 } 110 108 111 109 subscribeToObservable(observable: any) { 112 - if (!isNil(this.signalQualitySubscription)) { 113 - this.signalQualitySubscription.unsubscribe(); 114 - } 110 + this.signalQualitySubscription?.unsubscribe(); 115 111 this.signalQualitySubscription = observable.subscribe( 116 112 (chunk) => { 117 113 this.graphView?.send('newData', chunk);
+7 -2
src/renderer/containers/Root.tsx
··· 2 2 import { Provider } from 'react-redux'; 3 3 import { ConnectedRouter } from 'connected-react-router'; 4 4 import { History } from 'history'; 5 + 6 + const Router = ConnectedRouter as React.ComponentType<{ 7 + history: History; 8 + children?: React.ReactNode; 9 + }>; 5 10 import { Store } from '../reducers/types'; 6 11 import Routes from '../routes'; 7 12 import { RootState } from '../reducers'; ··· 13 18 14 19 const Root = ({ store, history }: Props) => ( 15 20 <Provider store={store}> 16 - <ConnectedRouter history={history}> 21 + <Router history={history}> 17 22 <Routes /> 18 - </ConnectedRouter> 23 + </Router> 19 24 </Provider> 20 25 ); 21 26
+4 -4
src/renderer/epics/experimentEpics.ts
··· 45 45 > = (action$) => 46 46 action$.pipe( 47 47 filter(isActionOf(ExperimentActions.CreateNewWorkspace)), 48 - pluck<'payload', WorkSpaceInfo>('payload'), 48 + map((action) => action.payload as WorkSpaceInfo), 49 49 mergeMap(async (workspaceInfo) => { 50 50 await createWorkspaceDir(workspaceInfo.title); 51 51 return workspaceInfo; ··· 119 119 action$.pipe( 120 120 filter(isActionOf(ExperimentActions.Stop)), 121 121 filter(() => state$.value.experiment.isRunning), 122 - pluck<'payload', { data: string }>('payload'), 122 + map((action) => (action.payload as { data: string })), 123 123 map(({ data }) => { 124 124 if (!state$.value.experiment.title) { 125 125 return; ··· 149 149 action$.pipe( 150 150 filter(isActionOf(ExperimentActions.UpdateSession)), 151 151 mergeMap(() => 152 - from(readWorkspaceBehaviorData(state$.value.experiment.title!)) 152 + from(readWorkspaceBehaviorData(state$.value.experiment.title!) as Promise<any[]>) 153 153 ), 154 - map((behaviorFiles) => { 154 + map((behaviorFiles: any[]) => { 155 155 if (behaviorFiles.length > 0) { 156 156 const subjectFiles = behaviorFiles.filter((filepath) => 157 157 filepath.name.startsWith(state$.value.experiment.subject)
+1 -1
src/renderer/epics/index.ts
··· 3 3 import device from './deviceEpics'; 4 4 import experiment from './experimentEpics'; 5 5 6 - export default combineEpics(device, experiment, pyodide); 6 + export default combineEpics(device as any, experiment as any, pyodide as any);
+9 -8
src/renderer/epics/pyodideEpics.ts
··· 161 161 ) => 162 162 action$.pipe( 163 163 filter(isActionOf(PyodideActions.CleanEpochs)), 164 - mergeMap(() => cleanEpochsPlot(state$.value.pyodide.worker!)), 165 - map(() => 166 - saveEpochs( 164 + mergeMap(async () => { 165 + await cleanEpochsPlot(state$.value.pyodide.worker!); 166 + const dir = await getWorkspaceDir(state$.value.experiment.title); 167 + return saveEpochs( 167 168 state$.value.pyodide.worker!, 168 - getWorkspaceDir(state$.value.experiment.title), 169 + dir, 169 170 state$.value.experiment.subject 170 - ) 171 - ), 171 + ); 172 + }), 172 173 map(() => PyodideActions.GetEpochsInfo(PYODIDE_VARIABLE_NAMES.RAW_EPOCHS)) 173 174 ); 174 175 ··· 181 182 filter(isActionOf(PyodideActions.GetEpochsInfo)), 182 183 pluck('payload'), 183 184 mergeMap((varName) => 184 - requestEpochsInfo(state$.value.pyodide.worker!, varName) 185 + requestEpochsInfo(state$.value.pyodide.worker!, varName) as unknown as Promise<any[]> 185 186 ), 186 187 map((epochInfoArray) => 187 188 epochInfoArray.map((infoObj) => ({ ··· 199 200 > = (action$, state$) => 200 201 action$.pipe( 201 202 filter(isActionOf(PyodideActions.GetChannelInfo)), 202 - mergeMap(() => requestChannelInfo(state$.value.pyodide.worker!)), 203 + mergeMap(() => requestChannelInfo(state$.value.pyodide.worker!) as unknown as Promise<string>), 203 204 map((channelInfoString) => 204 205 PyodideActions.SetChannelInfo(parseSingleQuoteJSON(channelInfoString)) 205 206 )
+8 -8
src/renderer/experiments/multitasking/utils.ts
··· 1 1 import * as lab from 'lab.js'; 2 2 3 - export function initMultitaskingResponseHandlers(this: lab.flow.Loop) { 3 + export function initMultitaskingResponseHandlers(this: lab.flow.Loop<Record<string, any>>) { 4 4 if (!this.options.events) return; 5 5 6 - // @ts-expect-error 7 - this.options.events.keydown = (e: { code: string }) => { 8 - if (e.code === 'KeyQ') { 6 + this.options.events.keydown = (e: Event) => { 7 + const { code } = e as unknown as { code: string }; 8 + if (code === 'KeyQ') { 9 9 this.data.skipTraining = true; 10 10 this.end(); 11 11 } 12 12 13 - if (e.code === 'ArrowLeft' || e.code === 'ArrowRight') { 13 + if (code === 'ArrowLeft' || code === 'ArrowRight') { 14 14 const instructions = 15 15 document.querySelectorAll<HTMLElement>('div.instruction'); 16 16 let notFound = true; ··· 18 18 if (i.style.display === 'block' && notFound) { 19 19 const cur_id = parseInt(i.id.split('screen_')[1], 10); 20 20 let next_id; 21 - if (e.code === 'ArrowLeft') { 21 + if (code === 'ArrowLeft') { 22 22 next_id = cur_id - 1; 23 23 } 24 - if (e.code === 'ArrowRight') { 24 + if (code === 'ArrowRight') { 25 25 next_id = cur_id + 1; 26 26 } 27 27 if (next_id > 0 && next_id <= 10) { ··· 42 42 }; 43 43 } 44 44 45 - export function initTasks(this: lab.flow.Loop) { 45 + export function initTasks(this: lab.flow.Loop<Record<string, any>>) { 46 46 function shuffle(a) { 47 47 let j; 48 48 let x;
+1 -1
src/renderer/experiments/search/experiment.ts
··· 128 128 parameters: {}, 129 129 responses: {}, 130 130 messageHandlers: { 131 - run: function anonymous(this: lab.flow.Loop) { 131 + run: function anonymous(this: lab.flow.Loop<Record<string, any>>) { 132 132 this.data.response = 'noresponse'; 133 133 this.data.correct = false; 134 134 },
+2 -2
src/renderer/experiments/search/utils.ts
··· 70 70 // assign options values to parameters of this task 71 71 } 72 72 73 - export function initSearchTrials(this: lab.flow.Loop) { 73 + export function initSearchTrials(this: lab.flow.Loop<Record<string, any>>) { 74 74 this.options.templateParameters = constructTrials(10, 'main', 25); 75 75 this.options.shuffle = true; // already shuffled before 76 76 } 77 77 78 - export function initPracticeTrials(this: lab.flow.Loop) { 78 + export function initPracticeTrials(this: lab.flow.Loop<Record<string, any>>) { 79 79 this.options.templateParameters = constructTrials(1, 'practice', 25); 80 80 this.options.shuffle = true; // already shuffled before 81 81 }
+3 -3
src/renderer/store.ts
··· 32 32 ); 33 33 if (shouldIncludeLogger) { 34 34 const logger = createLogger({ level: 'info', collapsed: true }); 35 - return base.concat(logger); 35 + return base.concat(logger) as unknown as typeof base; 36 36 } 37 37 return base; 38 38 }, 39 - preloadedState: initialState, 39 + preloadedState: initialState as any, 40 40 }); 41 41 42 - epicMiddleware.run(rootEpic); 42 + epicMiddleware.run(rootEpic as any); 43 43 return store; 44 44 }; 45 45
+5 -6
src/renderer/utils/eeg/emotiv.ts
··· 62 62 debit: 1, 63 63 }); 64 64 } catch (err) { 65 - toast.error(`Authentication failed. ${err.message}`); 65 + toast.error(`Authentication failed. ${(err as Error).message}`); 66 66 return Promise.reject(err); 67 67 } 68 68 // Connect 69 69 try { 70 70 await client.controlDevice({ command: 'connect', headset: device.id }); 71 71 } catch (err) { 72 - toast.error(`Emotiv connection failed. ${err.message}`); 72 + toast.error(`Emotiv connection failed. ${(err as Error).message}`); 73 73 return Promise.reject(err); 74 74 } 75 75 // Create Session ··· 86 86 channels: EMOTIV_CHANNELS, 87 87 }; 88 88 } catch (err) { 89 - toast.error(`Session creation failed. ${err.message} `); 89 + toast.error(`Session creation failed. ${(err as Error).message} `); 90 90 return Promise.reject(err); 91 91 } 92 92 }; ··· 110 110 streams: ['eeg', 'dev'], 111 111 }); 112 112 } catch (err) { 113 - toast.error(`EEG connection failed. ${err.message}`); 113 + toast.error(`EEG connection failed. ${(err as Error).message}`); 114 114 } 115 115 116 116 return fromEvent(client, 'eeg').pipe(map(createEEGSample)); ··· 133 133 }), 134 134 bandpassFilter({ 135 135 nbChannels: channels.length, 136 - lowCutoff: 1, 137 - highCutoff: 50, 136 + cutoffFrequencies: [1, 50], 138 137 }), 139 138 withLatestFrom(signalQualityObservable, integrateSignalQuality), 140 139 parseEmotivSignalQuality(),
+5 -6
src/renderer/utils/eeg/muse.ts
··· 59 59 export const createRawMuseObservable = async () => { 60 60 await client.start(); 61 61 const eegStream = await client.eegReadings; 62 - const markers = await client.eventMarkers.pipe(startWith({ timestamp: 0 })); 63 - return from(zipSamples(eegStream)).pipe( 62 + const markers = await (client.eventMarkers as any).pipe(startWith({ timestamp: 0 })); 63 + return from(zipSamples(eegStream) as any).pipe( 64 64 // Remove nans if present (muse 2) 65 - map<EEGSample, EEGSample>((sample) => ({ 65 + map((sample: any) => ({ 66 66 ...sample, 67 67 data: sample.data.filter((val) => !isNaN(val)), 68 68 })), 69 69 filter((sample) => sample.data.length >= 4), 70 - withLatestFrom(markers, synchronizeTimestamp), 70 + withLatestFrom(markers as any, synchronizeTimestamp), 71 71 share() 72 72 ); 73 73 }; ··· 90 90 }), 91 91 bandpassFilter({ 92 92 nbChannels: channelNames.length, 93 - lowCutoff: 1, 94 - highCutoff: 50, 93 + cutoffFrequencies: [1, 50], 95 94 }), 96 95 addSignalQuality(), 97 96 parseMuseSignalQuality()
-11
src/renderer/utils/filesystem/select.ts
··· 4 4 */ 5 5 import { FILE_TYPES } from '../../constants/constants'; 6 6 7 - declare global { 8 - interface Window { 9 - electronAPI: { 10 - loadDialog: (fileType: string) => Promise<string | null>; 11 - showOpenDialog: ( 12 - options: Record<string, unknown> 13 - ) => Promise<{ canceled: boolean; filePaths: string[] }>; 14 - }; 15 - } 16 - } 17 - 18 7 export const loadFromSystemDialog = ( 19 8 fileType: FILE_TYPES 20 9 ): Promise<string | null> => window.electronAPI.loadDialog(fileType);
+5 -6
src/renderer/utils/labjs/functions.ts
··· 1 1 import * as lab from 'lab.js'; 2 - import { ComponentOptions } from 'lab.js/dist/types'; 3 2 import path from 'pathe'; 4 3 import { EXPERIMENTS } from '../../constants/constants'; 5 4 import { ··· 41 40 } 42 41 43 42 // Initializes a Loop component with a provided list of stimuli and other parameters extracted from experiment parameters 44 - export function initLoopWithStimuli(this: lab.flow.Loop) { 43 + export function initLoopWithStimuli(this: lab.flow.Loop<Record<string, any>>) { 45 44 const { 46 45 parameters: { stimuli, nbTrials, randomize }, 47 46 }: { parameters: ExperimentParameters } = this; 48 47 49 48 const balancedStimuli = balanceStimuliByCondition(stimuli, nbTrials); 50 49 51 - this.options.templateParameters = balancedStimuli; 50 + this.options.templateParameters = (balancedStimuli ?? []) as Record<string, any>[]; 52 51 this.options.shuffle = randomize === 'random'; 53 52 } 54 53 55 54 // Initializes a Loop component with a provided list of stimuli and other parameters extracted from experiment parameters 56 55 // uses nbPracticeTrials 57 - export function initPracticeLoopWithStimuli(this: lab.flow.Loop) { 56 + export function initPracticeLoopWithStimuli(this: lab.flow.Loop<Record<string, any>>) { 58 57 const { 59 58 parameters: { stimuli, nbPracticeTrials, randomize }, 60 59 }: { parameters: ExperimentParameters } = this; ··· 63 62 64 63 const balancedStimuli = balanceStimuliByCondition(stimuli, nbPracticeTrials); 65 64 66 - this.options.templateParameters = balancedStimuli; 65 + this.options.templateParameters = (balancedStimuli ?? []) as Record<string, any>[]; 67 66 this.options.shuffle = randomize === 'random'; 68 67 } 69 68 ··· 133 132 const { 134 133 options: { id }, 135 134 parameters: { response }, 136 - }: { parameters: Stimulus; options: ComponentOptions } = this; 135 + }: { parameters: Stimulus; options: lab.core.ComponentOptions } = this; 137 136 if (!id) return; 138 137 139 138 this.data.trial_number =