An easy-to-use platform for EEG experimentation in the classroom
at main 176 lines 5.2 kB view raw
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}