uva-covid19-testing-kiosk/App.tsx

185 lines
6.0 KiB
TypeScript
Raw Normal View History

import {BarCodeEvent, BarCodeScanner, PermissionResponse} from 'expo-barcode-scanner';
import React, {ReactElement, useEffect, useState} from 'react';
import {AppRegistry, SafeAreaView, View} from 'react-native';
import {
Appbar,
Button, DarkTheme,
DefaultTheme,
HelperText,
Provider as PaperProvider,
Snackbar, Subheading,
TextInput,
Title
} from 'react-native-paper';
import {expo as appExpo} from './app.json';
import {CancelButton} from './components/Common';
import {BarCodeDisplay, PrintButton, PrintingMessage} from './components/Print';
import {ScanButton, Scanner} from './components/Scan';
import {colors, styles} from './components/Styles';
import {BarcodeScannerAppState} from './models/BarcodeScannerAppState';
import {ElementProps, StateProps} from './models/ElementProps';
const theme = {
...DefaultTheme,
colors: colors,
}
export default function Main() {
const [appState, setAppState] = useState<BarcodeScannerAppState>(BarcodeScannerAppState.INITIAL);
const [barCodeData, setBarCodeData] = useState<string>('');
const [date, setDate] = useState<Date>(new Date());
const [locationStr, setLocationStr] = useState<string>('4321');
useEffect(() => {
BarCodeScanner.requestPermissionsAsync().then((value: PermissionResponse) => {
if (value.granted) {
setAppState(BarcodeScannerAppState.DEFAULT);
} else {
setAppState(BarcodeScannerAppState.ERROR);
}
});
}, []);
const _doNothing = () => {};
const _scan = () => setAppState(BarcodeScannerAppState.SCANNING);
const _print = () => setAppState(BarcodeScannerAppState.PRINTING);
const _home = () => setAppState(BarcodeScannerAppState.DEFAULT);
const _settings = () => setAppState(BarcodeScannerAppState.SETTINGS);
const handleBarCodeScanned = (e: BarCodeEvent) => {
// Make sure the data is the right length.
const barCodeString = e.data;
const pattern = /^[\d]{14}$/;
if (pattern.test(barCodeString)) {
setBarCodeData(e.data);
setDate(new Date());
setAppState(BarcodeScannerAppState.SCANNED);
} else {
setAppState(BarcodeScannerAppState.ERROR);
}
};
function ErrorMessage(props: ElementProps): ReactElement {
return <View style={styles.fullScreen}>
<View style={styles.container}>
<ScanButton onClicked={_scan}/>
</View>
<Snackbar
visible={appState === BarcodeScannerAppState.ERROR}
onDismiss={_doNothing}
style={styles.error}
>Something went wrong. Try again.</Snackbar>
</View>
}
function LoadingMessage(props: ElementProps): ReactElement {
return <Snackbar
visible={appState === BarcodeScannerAppState.INITIAL}
onDismiss={_doNothing}
>Loading...</Snackbar>;
}
function SuccessMessage(props: ElementProps): ReactElement {
return <Title>Your barcode label has printed successfully.</Title>;
}
function ActionButtons(props: ElementProps): ReactElement {
return <View>
<PrintButton onClicked={_print}/>
<CancelButton onClicked={_home}/>
</View>
}
function SettingsModal(props: ElementProps): ReactElement {
const [inputStr, setInputStr] = useState<string>(locationStr);
const pattern = /^[\d]{4}$/;
const hasErrors = () => {
return !pattern.test(inputStr);
};
return <View style={styles.settings}>
<Title style={styles.headingInverse}>Settings</Title>
<View style={{marginBottom: 10}}>
<Subheading style={{color: DefaultTheme.colors.text, marginBottom: 60}}>
Please do NOT change this unless you know what you are doing. Entering an incorrect location number may prevent patients from getting accurate info about their test results.
</Subheading>
<TextInput
label="Location #"
value={inputStr}
onChangeText={inputStr => setInputStr(inputStr)}
mode="outlined"
theme={DefaultTheme}
/>
<HelperText type="error" visible={hasErrors()}>
Location number must be exactly 4 digits. No other characters are allowed.
</HelperText>
<Button
icon="save"
mode="contained"
color={colors.primary}
style={{marginBottom: 10}}
disabled={hasErrors()}
onPress={() => {
setLocationStr(inputStr);
_home();
}}
>Save</Button>
<Button
icon="cancel"
mode="outlined"
color={colors.primary}
onPress={_home}
>Cancel</Button>
</View>
</View>
}
function App(props: StateProps): ReactElement {
switch (props.appState) {
case BarcodeScannerAppState.INITIAL:
return <LoadingMessage/>;
case BarcodeScannerAppState.DEFAULT:
return <View style={styles.container}>
<ScanButton onClicked={_scan}/>
</View>;
case BarcodeScannerAppState.PRINTED:
return <SuccessMessage/>;
case BarcodeScannerAppState.PRINTING:
return <PrintingMessage
onCancel={_home}
id={barCodeData}
date={date} location={locationStr}
/>;
case BarcodeScannerAppState.SCANNED:
return <View style={styles.container}>
<BarCodeDisplay id={barCodeData} date={date} location={locationStr}/>
<ActionButtons/>
</View>;
case BarcodeScannerAppState.SCANNING:
return <Scanner
onScanned={handleBarCodeScanned}
onCancel={_home}
/>;
case BarcodeScannerAppState.SETTINGS:
return <SettingsModal/>;
default:
return <ErrorMessage/>;
}
}
return (
<PaperProvider theme={theme}>
<Appbar.Header dark={true}>
<Appbar.Content title={`${appExpo.description} #${locationStr}`}/>
<Appbar.Action icon="home" onPress={_home}/>
<Appbar.Action icon="settings" onPress={_settings}/>
</Appbar.Header>
<SafeAreaView style={styles.safeAreaView}>
<App appState={appState}/>
</SafeAreaView>
</PaperProvider>
);
};
AppRegistry.registerComponent(appExpo.name, () => Main);