An easy-to-use platform for EEG experimentation in the classroom
1import React, { Component } from 'react';
2import { Button } from '../ui/button';
3import Mousetrap from 'mousetrap';
4import ViewerComponent from '../ViewerComponent';
5import SignalQualityIndicatorComponent from '../SignalQualityIndicatorComponent';
6import PreviewExperimentComponent from '../PreviewExperimentComponent';
7import PreviewButton from '../PreviewButtonComponent';
8import { HelpSidebar, HelpButton } from './HelpSidebar';
9import { getExperimentFromType } from '../../utils/labjs/functions';
10import { ExperimentActions, DeviceActions } from '../../actions';
11import {
12 DEVICES,
13 DEVICE_AVAILABILITY,
14 EXPERIMENTS,
15 PLOTTING_INTERVAL,
16 CONNECTION_STATUS,
17} from '../../constants/constants';
18import {
19 ExperimentParameters,
20 Device,
21 SignalQualityData,
22} from '../../constants/interfaces';
23import { Observable } from 'rxjs';
24
25interface Props {
26 ExperimentActions: typeof ExperimentActions;
27 connectedDevice: Record<string, unknown>;
28 signalQualityObservable: Observable<SignalQualityData> | null | undefined;
29 deviceType: DEVICES;
30 deviceAvailability: DEVICE_AVAILABILITY;
31 connectionStatus: CONNECTION_STATUS;
32 DeviceActions: typeof DeviceActions;
33 availableDevices: Array<Device>;
34 type: EXPERIMENTS;
35 isRunning: boolean;
36 params: ExperimentParameters;
37 subject: string;
38 group: string;
39 session: number;
40 title: string;
41 openRunComponent: () => void;
42}
43
44interface State {
45 isPreviewing: boolean;
46 isSidebarVisible: boolean;
47}
48
49export default class PreTestComponent extends Component<Props, State> {
50 constructor(props: Props) {
51 super(props);
52 this.state = {
53 isPreviewing: false,
54 isSidebarVisible: true,
55 };
56 this.handlePreview = this.handlePreview.bind(this);
57 this.handleSidebarToggle = this.handleSidebarToggle.bind(this);
58 this.endPreview = this.endPreview.bind(this);
59 }
60
61 componentDidMount() {
62 Mousetrap.bind('esc', this.props.ExperimentActions.Stop);
63 }
64
65 componentWillUnmount() {
66 Mousetrap.unbind('esc');
67 }
68
69 endPreview() {
70 this.setState({ isPreviewing: false });
71 }
72
73 handlePreview(e) {
74 e.target.blur();
75 this.setState((prevState) => ({
76 ...prevState,
77 isSidebarVisible: false,
78 isPreviewing: !prevState.isPreviewing,
79 }));
80 }
81
82 handleSidebarToggle() {
83 this.setState((prevState) => ({
84 ...prevState,
85 isSidebarVisible: !prevState.isSidebarVisible,
86 }));
87 }
88
89 renderSignalQualityOrPreview() {
90 if (this.state.isPreviewing) {
91 return (
92 <PreviewExperimentComponent
93 {...getExperimentFromType(this.props.type)}
94 isPreviewing={this.state.isPreviewing}
95 onEnd={this.endPreview}
96 type={this.props.type}
97 params={this.props.params}
98 title={this.props.title}
99 />
100 );
101 }
102 return (
103 <div className="p-2">
104 <SignalQualityIndicatorComponent
105 signalQualityObservable={this.props.signalQualityObservable}
106 plottingInterval={PLOTTING_INTERVAL}
107 />
108 <ul className="mt-2 space-y-1">
109 <li>
110 <span className="text-signal-great">●</span> Strong Signal
111 </li>
112 <li>
113 <span className="text-signal-ok">●</span> Mediocre signal
114 </li>
115 <li>
116 <span className="text-signal-bad">●</span> Weak Signal
117 </li>
118 <li>
119 <span className="text-signal-none">●</span> No Signal
120 </li>
121 </ul>
122 </div>
123 );
124 }
125
126 renderHelpButton() {
127 if (!this.state.isSidebarVisible) {
128 return <HelpButton onClick={this.handleSidebarToggle} />;
129 }
130 }
131
132 render() {
133 return (
134 <div className="relative flex h-screen bg-gradient-to-b from-[#f9f9f9] to-[#f0f0ff]">
135 {this.state.isSidebarVisible && (
136 <div className="absolute right-0 top-0 h-full w-64 z-10">
137 <HelpSidebar handleClose={this.handleSidebarToggle} />
138 </div>
139 )}
140 <div className="flex-1 p-[3%]">
141 <div className="flex items-center justify-between mb-4">
142 <h1>Collect</h1>
143 <div className="flex gap-2">
144 <PreviewButton
145 isPreviewing={this.state.isPreviewing}
146 onClick={(e) => this.handlePreview(e)}
147 />
148 <Button
149 variant="default"
150 disabled={
151 this.props.connectionStatus !== CONNECTION_STATUS.CONNECTED
152 }
153 onClick={this.props.openRunComponent}
154 >
155 Run & Record Experiment
156 </Button>
157 </div>
158 </div>
159 <div className="flex gap-4">
160 <div className="w-1/2 h-full items-center mb-5">
161 {this.renderSignalQualityOrPreview()}
162 </div>
163 <div className="w-1/2">
164 <ViewerComponent
165 signalQualityObservable={this.props.signalQualityObservable}
166 deviceType={this.props.deviceType}
167 plottingInterval={PLOTTING_INTERVAL}
168 />
169 {this.renderHelpButton()}
170 </div>
171 </div>
172 </div>
173 </div>
174 );
175 }
176}