WIP: Displays QRCode instead of 1D barcode
Former-commit-id: 5a7dd6b7582fa2dea2131d4997eb6d0b8234eead
This commit is contained in:
parent
5729def6ca
commit
0a16a7b4f9
|
@ -533,3 +533,70 @@ buck-out/
|
|||
# Expo
|
||||
.expo/*
|
||||
web-build/
|
||||
|
||||
# The following contents were automatically generated by expo-cli during eject
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# OSX
|
||||
#
|
||||
.DS_Store
|
||||
|
||||
# Xcode
|
||||
#
|
||||
build/
|
||||
*.pbxuser
|
||||
!default.pbxuser
|
||||
*.mode1v3
|
||||
!default.mode1v3
|
||||
*.mode2v3
|
||||
!default.mode2v3
|
||||
*.perspectivev3
|
||||
!default.perspectivev3
|
||||
xcuserdata
|
||||
*.xccheckout
|
||||
*.moved-aside
|
||||
DerivedData
|
||||
*.hmap
|
||||
*.ipa
|
||||
*.xcuserstate
|
||||
project.xcworkspace
|
||||
|
||||
# Android/IntelliJ
|
||||
#
|
||||
build/
|
||||
.idea
|
||||
.gradle
|
||||
local.properties
|
||||
*.iml
|
||||
|
||||
# node.js
|
||||
#
|
||||
node_modules/
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
|
||||
# BUCK
|
||||
buck-out/
|
||||
\.buckd/
|
||||
*.keystore
|
||||
|
||||
# fastlane
|
||||
#
|
||||
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
|
||||
# screenshots whenever they are needed.
|
||||
# For more information about the recommended setup visit:
|
||||
# https://docs.fastlane.tools/best-practices/source-control/
|
||||
|
||||
*/fastlane/report.xml
|
||||
*/fastlane/Preview.html
|
||||
*/fastlane/screenshots
|
||||
|
||||
# Bundle artifacts
|
||||
*.jsbundle
|
||||
|
||||
# CocoaPods
|
||||
/ios/Pods/
|
||||
|
||||
# Expo
|
||||
.expo/*
|
||||
web-build/
|
||||
|
|
130
App.tsx
130
App.tsx
|
@ -1,16 +1,24 @@
|
|||
// @refresh reset
|
||||
import AsyncStorage from '@react-native-community/async-storage';
|
||||
import {format, parse} from 'date-fns';
|
||||
import {BarCodeEvent, BarCodeScanner, PermissionResponse} from 'expo-barcode-scanner';
|
||||
import React, {ReactElement, useEffect, useState} from 'react';
|
||||
import {AppRegistry, SafeAreaView, View} from 'react-native';
|
||||
// @ts-ignore
|
||||
import * as firebase from 'firebase';
|
||||
import 'firebase/firestore';
|
||||
import React, {ReactElement, useCallback, useEffect, useState} from 'react';
|
||||
import {AppRegistry, SafeAreaView, View, YellowBox} from 'react-native';
|
||||
import {
|
||||
Appbar,
|
||||
Button, DarkTheme,
|
||||
Button,
|
||||
DefaultTheme,
|
||||
HelperText,
|
||||
Provider as PaperProvider,
|
||||
Snackbar, Subheading,
|
||||
Snackbar,
|
||||
Subheading,
|
||||
TextInput,
|
||||
Title
|
||||
} from 'react-native-paper';
|
||||
import QRCode from 'react-native-qrcode-svg';
|
||||
import {expo as appExpo} from './app.json';
|
||||
import {CancelButton} from './components/Common';
|
||||
import {BarCodeDisplay, PrintButton, PrintingMessage} from './components/Print';
|
||||
|
@ -18,6 +26,29 @@ import {IdNumberInput, InputIdButton, ScanButton, Scanner} from './components/Sc
|
|||
import {colors, styles} from './components/Styles';
|
||||
import {BarcodeScannerAppState} from './models/BarcodeScannerAppState';
|
||||
import {ElementProps, StateProps} from './models/ElementProps';
|
||||
import {Sample} from './models/Sample';
|
||||
|
||||
const firebaseConfig = {
|
||||
apiKey: 'api_key_goes_here',
|
||||
authDomain: 'auth_domain_goes_here',
|
||||
databaseURL: 'database_url_goes_here',
|
||||
projectId: 'project_id_goes_here',
|
||||
storageBucket: 'storage_bucket_goes_here',
|
||||
messagingSenderId: 'sender_id_goes_here',
|
||||
appId: 'app_id_goes_here'
|
||||
};
|
||||
|
||||
// Initialize Firebase if not already initialized.
|
||||
if (firebase.apps.length === 0) {
|
||||
firebase.initializeApp(firebaseConfig);
|
||||
}
|
||||
|
||||
YellowBox.ignoreWarnings([
|
||||
'Setting a timer for a long period of time', // Ignore Firebase timer warnings
|
||||
]);
|
||||
|
||||
const db = firebase.firestore();
|
||||
const samplesCollection = db.collection('samples');
|
||||
|
||||
const theme = {
|
||||
...DefaultTheme,
|
||||
|
@ -26,10 +57,13 @@ const theme = {
|
|||
|
||||
export default function Main() {
|
||||
const [appState, setAppState] = useState<BarcodeScannerAppState>(BarcodeScannerAppState.INITIAL);
|
||||
const [barCodeData, setBarCodeData] = useState<string>('');
|
||||
const [date, setDate] = useState<Date>(new Date());
|
||||
const [sampleId, setSampleId] = useState<string>('');
|
||||
const [barCodeId, setBarCodeId] = useState<string>('');
|
||||
const [sampleDate, setSampleDate] = useState<Date>(new Date());
|
||||
const [locationStr, setLocationStr] = useState<string>('4321');
|
||||
const [svgQrCode, setSvgQrCode] = useState<any>();
|
||||
const [errorMessage, setErrorMessage] = useState<string>('');
|
||||
const [samples, setSamples] = useState<Sample[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
BarCodeScanner.requestPermissionsAsync().then((value: PermissionResponse) => {
|
||||
|
@ -39,9 +73,26 @@ export default function Main() {
|
|||
setAppState(BarcodeScannerAppState.ERROR);
|
||||
}
|
||||
});
|
||||
|
||||
const unsubscribe = samplesCollection.onSnapshot(querySnapshot => {
|
||||
// Transform and sort the data returned from Firebase
|
||||
const samplesFirestore = querySnapshot
|
||||
.docChanges()
|
||||
.filter(({type}) => type === 'added')
|
||||
.map(({doc}) => {
|
||||
const sample = doc.data();
|
||||
return {...sample, createdAt: sample.createdAt.toDate()} as Sample;
|
||||
})
|
||||
.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
||||
|
||||
appendSamples(samplesFirestore);
|
||||
});
|
||||
|
||||
return () => unsubscribe()
|
||||
}, []);
|
||||
|
||||
const _doNothing = () => {};
|
||||
const _doNothing = () => {
|
||||
};
|
||||
const _scan = () => {
|
||||
setErrorMessage('');
|
||||
setAppState(BarcodeScannerAppState.SCANNING);
|
||||
|
@ -51,6 +102,7 @@ export default function Main() {
|
|||
setAppState(BarcodeScannerAppState.INPUT);
|
||||
};
|
||||
const _print = () => setAppState(BarcodeScannerAppState.PRINTING);
|
||||
const _printed = () => setAppState(BarcodeScannerAppState.PRINTED);
|
||||
const _home = () => setAppState(BarcodeScannerAppState.DEFAULT);
|
||||
const _settings = () => setAppState(BarcodeScannerAppState.SETTINGS);
|
||||
|
||||
|
@ -62,15 +114,31 @@ export default function Main() {
|
|||
const pattern = /^[\d]{14}$|^[\d]{9}$/;
|
||||
if (pattern.test(barCodeString)) {
|
||||
const cardId = e.data.slice(0, 9);
|
||||
setBarCodeData(cardId);
|
||||
setDate(new Date());
|
||||
setBarCodeId(cardId);
|
||||
setSampleDate(new Date());
|
||||
setAppState(BarcodeScannerAppState.SCANNED);
|
||||
setSampleId([barCodeId, format(sampleDate, 'yyyyMMddHHmm'), locationStr].join('-'));
|
||||
|
||||
console.log('sampleId', sampleId);
|
||||
new QRCode({value: sampleId, ecl: 'H', getRef: c => {
|
||||
setSvgQrCode(c);
|
||||
console.log('svgQrCode', svgQrCode);
|
||||
}});
|
||||
} else {
|
||||
setErrorMessage(`The barcode data "${e.data}" is not from a valid ID card.`);
|
||||
setAppState(BarcodeScannerAppState.ERROR);
|
||||
}
|
||||
};
|
||||
|
||||
const appendSamples = useCallback((newSamples) => {
|
||||
setSamples((previousSamples) => previousSamples.concat(newSamples));
|
||||
}, [samples]);
|
||||
|
||||
const sendDataToFirebase = async (newSamples: Sample[]) => {
|
||||
const writes = newSamples.map(s => samplesCollection.doc(s.id).set(s));
|
||||
await Promise.all(writes);
|
||||
}
|
||||
|
||||
function ErrorMessage(props: ElementProps): ReactElement {
|
||||
return <View style={styles.fullScreen}>
|
||||
<View style={styles.container}>
|
||||
|
@ -114,7 +182,8 @@ export default function Main() {
|
|||
<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.
|
||||
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 #"
|
||||
|
@ -157,16 +226,45 @@ export default function Main() {
|
|||
<InputIdButton onClicked={_inputIdNumber}/>
|
||||
</View>;
|
||||
case BarcodeScannerAppState.PRINTED:
|
||||
// Upload any changes to Firebase
|
||||
AsyncStorage.getAllKeys().then(keys => {
|
||||
const newSamples = keys
|
||||
.filter(s => /^[\d]{9}-[\d]{12}-[\d]{4}$/.test(s))
|
||||
.map(s => {
|
||||
const propsArray = s.split('-');
|
||||
return {
|
||||
id: s,
|
||||
barcodeId: propsArray[0],
|
||||
createdAt: parse(propsArray[1], 'yyyyMMddHHmm', new Date()),
|
||||
locationId: propsArray[2],
|
||||
} as Sample;
|
||||
});
|
||||
sendDataToFirebase(newSamples);
|
||||
});
|
||||
|
||||
_home();
|
||||
|
||||
return <SuccessMessage/>;
|
||||
case BarcodeScannerAppState.PRINTING:
|
||||
return <PrintingMessage
|
||||
onCancel={_home}
|
||||
id={barCodeData}
|
||||
date={date} location={locationStr}
|
||||
/>;
|
||||
return <View style={styles.container}>
|
||||
<PrintingMessage
|
||||
onCancel={_printed}
|
||||
id={sampleId}
|
||||
barCodeId={barCodeId}
|
||||
date={sampleDate}
|
||||
location={locationStr}
|
||||
svg={svgQrCode}
|
||||
/>
|
||||
</View>;
|
||||
case BarcodeScannerAppState.SCANNED:
|
||||
return <View style={styles.container}>
|
||||
<BarCodeDisplay id={barCodeData} date={date} location={locationStr}/>
|
||||
<BarCodeDisplay
|
||||
id={sampleId}
|
||||
barCodeId={barCodeId}
|
||||
date={sampleDate}
|
||||
location={locationStr}
|
||||
svg={svgQrCode}
|
||||
/>
|
||||
<ActionButtons/>
|
||||
</View>;
|
||||
case BarcodeScannerAppState.SCANNING:
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import React, {ReactElement, useEffect, useState} from 'react';
|
||||
import {Text, View} from 'react-native';
|
||||
// @ts-ignore
|
||||
import Barcode from 'react-native-barcode-builder';
|
||||
import {Button, Title} from 'react-native-paper';
|
||||
import QRCode from 'react-native-qrcode-svg';
|
||||
import {BarCodeProps, ButtonProps, PrintingProps} from '../models/ElementProps';
|
||||
import {Sample} from '../models/Sample';
|
||||
import {colors, styles} from './Styles';
|
||||
import AsyncStorage from '@react-native-community/async-storage';
|
||||
import * as Print from 'expo-print';
|
||||
|
@ -15,44 +15,18 @@ enum PrintStatus {
|
|||
DONE = 'DONE',
|
||||
}
|
||||
|
||||
const _renderBarCodeRects = (props: PrintingProps): string => {
|
||||
const dataStr = _propsToDataString(props);
|
||||
const rects: string[] = [];
|
||||
|
||||
// TODO: set base width from label width in pixels
|
||||
// 2 inches = 190 pixels
|
||||
// for now, just guesstimate.
|
||||
const baseWidth = 7;
|
||||
|
||||
for (let i = 0; i < dataStr.length; i++) {
|
||||
// TODO: Convert dataStr to barcode rectangles with x, y, width, height
|
||||
// barcodejs library has some useful stuff?
|
||||
// Or maybe somehow use something in the guts of react-native-barcode-builder?
|
||||
// For now, just put in some dummy x values and widths.
|
||||
rects.push(`<rect width="${Math.floor(Math.random() * baseWidth)}" height="20" x="${baseWidth * i}" y="70" fill="black" />`);
|
||||
}
|
||||
|
||||
return rects.join(' ')
|
||||
}
|
||||
|
||||
const _propsToDataString = (props: BarCodeProps): string => {
|
||||
return props.id + format(props.date, 'yyyymmdd') + props.location;
|
||||
}
|
||||
|
||||
const _save = (props: PrintingProps): Promise<void> => {
|
||||
const storageKey = _propsToDataString(props);
|
||||
const storageVal = {
|
||||
const storageVal: Sample = {
|
||||
id: props.id,
|
||||
date: props.date,
|
||||
location: props.location,
|
||||
barcodeId: props.barCodeId,
|
||||
createdAt: props.date,
|
||||
locationId: props.location,
|
||||
};
|
||||
console.log('storageKey', storageKey);
|
||||
console.log('storageVal', storageVal);
|
||||
return AsyncStorage.setItem(storageKey, JSON.stringify(storageVal));
|
||||
return AsyncStorage.setItem(props.id, JSON.stringify(storageVal));
|
||||
}
|
||||
|
||||
const _print = (props: PrintingProps): Promise<void> => {
|
||||
const dataStr = _propsToDataString(props);
|
||||
console.log('props.svg', props.svg);
|
||||
return Print.printAsync({
|
||||
html: `
|
||||
<style>
|
||||
|
@ -90,13 +64,11 @@ const _print = (props: PrintingProps): Promise<void> => {
|
|||
}
|
||||
</style>
|
||||
<div class="box">
|
||||
<p>ID#: ${props.id}</p>
|
||||
<p>ID#: ${props.barCodeId}</p>
|
||||
<p>Date: ${props.date.toLocaleDateString()} ${props.date.toLocaleTimeString()}</p>
|
||||
<p>Loc#: ${props.location}</p>
|
||||
<svg width="190" height="90" id="barCode">
|
||||
${_renderBarCodeRects(props)}
|
||||
</svg>
|
||||
<p>${dataStr}</p>
|
||||
${props.svg}
|
||||
<p>${props.id}</p>
|
||||
</div>
|
||||
`,
|
||||
});
|
||||
|
@ -142,11 +114,15 @@ export const PrintingMessage = (props: PrintingProps): ReactElement => {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
return <View style={styles.container}>
|
||||
<View style={styles.preview}>
|
||||
<BarCodeDisplay id={props.id} date={props.date} location={props.location} />
|
||||
<BarCodeDisplay id={props.id} date={props.date} location={props.location} />
|
||||
<BarCodeDisplay
|
||||
id={props.id}
|
||||
barCodeId={props.barCodeId}
|
||||
date={props.date}
|
||||
location={props.location}
|
||||
svg={props.svg}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.container}>
|
||||
<Title style={styles.heading}>{statusStr}</Title>
|
||||
|
@ -165,11 +141,11 @@ export const PrintingMessage = (props: PrintingProps): ReactElement => {
|
|||
|
||||
|
||||
export const BarCodeDisplay = (props: BarCodeProps): ReactElement => {
|
||||
const data = _propsToDataString(props);
|
||||
console.log('BarCodeDisplay props.svg', props.svg);
|
||||
return <View style={styles.printPreview}>
|
||||
<Text style={styles.label}>ID#: {props.id}</Text>
|
||||
<Text style={styles.label}>Date: {props.date.toLocaleDateString()}, {props.date.toLocaleTimeString()}</Text>
|
||||
<Text style={styles.label}>Location {props.location}</Text>
|
||||
<Barcode width={1.1} height={40} text={data} value={data} format={'CODE128'}/>
|
||||
<QRCode value={props.id} />
|
||||
</View>;
|
||||
}
|
||||
|
|
|
@ -11,8 +11,10 @@ export interface StateProps extends ElementProps {
|
|||
|
||||
export interface BarCodeProps extends ElementProps {
|
||||
id: string;
|
||||
barCodeId: string;
|
||||
date: Date;
|
||||
location: string;
|
||||
svg: any;
|
||||
}
|
||||
|
||||
export interface ButtonProps extends ElementProps {
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
export interface Sample {
|
||||
id: string;
|
||||
barcodeId: string;
|
||||
locationId: string;
|
||||
createdAt: Date;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -15,17 +15,18 @@
|
|||
"expo-splash-screen": "^0.5.0",
|
||||
"expo-status-bar": "^1.0.2",
|
||||
"expo-updates": "~0.2.10",
|
||||
"firebase": "7.9.0",
|
||||
"jsbarcode": "^3.11.0",
|
||||
"react": "~16.11.0",
|
||||
"react-dom": "~16.11.0",
|
||||
"react-native": "~0.62.2",
|
||||
"react-native-barcode-builder": "github:cdesch/react-native-barcode-builder#master",
|
||||
"react-native-canvas": "^0.1.37",
|
||||
"react-native-easy-grid": "^0.2.2",
|
||||
"react-native-gesture-handler": "~1.6.1",
|
||||
"react-native-html-to-pdf": "^0.8.0",
|
||||
"react-native-paper": "^4.1.0",
|
||||
"react-native-print": "^0.6.0",
|
||||
"react-native-qrcode-svg": "^6.0.6",
|
||||
"react-native-reanimated": "~1.9.0",
|
||||
"react-native-screens": "~2.9.0",
|
||||
"react-native-share": "^3.7.0",
|
||||
|
|
Loading…
Reference in New Issue