Files
ppc_amnet/amnet-native/app/card/PhyscialCardScan.tsx
T

87 lines
3.8 KiB
TypeScript

import {StatusBar} from "expo-status-bar";
import {useNavigation} from "@react-navigation/native";
import {Card, Text, useTheme} from "react-native-paper";
import {useCallback, useLayoutEffect, useState} from "react";
import {Platform, TextInput, TouchableOpacity, View} from "react-native";
import RealmContext from "../../models/RealmContext";
import {useModalStyles} from "../../utils/ModalStyles";
import {AMCard} from "../../models/AMCard";
import {AMPhysicalCardData} from "../../models/AMPhysicalCardData";
import {felicaCardReadAsync, icCard0008Number, ICType} from "../../lib/iccard";
const {useRealm} = RealmContext;
export default function PhysicalCardScan() {
const realm = useRealm();
const theme = useTheme();
const navigation = useNavigation();
const modalStyles = useModalStyles();
const [cardName, setCardName] = useState<string>('');
const handleScan = useCallback(async () => {
const cardInfo = await felicaCardReadAsync();
if (!cardInfo) {
return;
}
realm.write(() => {
// check if the card has already been registered (convert a manually entered card to a physically scanned card)
const accessCode = cardInfo.type === ICType.Amusement ? cardInfo.accessCode : icCard0008Number(cardInfo.idm);
const cardEntity = realm.objects<AMCard>(AMCard.schema.name).find(x => x.code === accessCode) ?? realm.create<AMCard>(AMCard.schema.name, AMCard.generate(accessCode, cardName));
// set physical card props
cardEntity.name = cardName;
cardEntity.added = new Date();
cardEntity.physicalCardData ??= realm.objectForPrimaryKey<AMPhysicalCardData>(AMPhysicalCardData.schema.name, cardInfo.idm) ?? realm.create<AMPhysicalCardData>(AMPhysicalCardData.schema.name, {
cardIDm: cardInfo.idm,
cardType: cardInfo.type,
cardVendor: cardInfo.type === ICType.Amusement ? cardInfo.vendor : null
});
});
// dismiss the parent screen (this screen can only be accessed from modal -> stack)
navigation.getParent()?.goBack();
}, [realm, navigation, cardName]);
useLayoutEffect(() => {
const canSave = cardName.length;
const themeOptions = canSave ? modalStyles.headerText : {color: theme.colors.outline};
navigation.setOptions({
headerRight: () => <TouchableOpacity disabled={!canSave} onPress={() => handleScan()}>
<Text variant="bodyMedium" style={{...themeOptions}}>Scan</Text>
</TouchableOpacity>
})
}, [navigation, modalStyles, cardName]);
return (<>
{Platform.OS == 'ios' && <StatusBar style="light"/>}
<View style={modalStyles.rootLayout}>
<Card mode="contained">
<Card.Content>
<View style={modalStyles.formItemRow}>
<Text variant="bodyMedium" style={modalStyles.formItemName}>
Card Name
</Text>
<TextInput style={modalStyles.formItemInput}
autoFocus
numberOfLines={1}
value={cardName}
onChangeText={setCardName}
placeholder="Primary Card"
placeholderTextColor={theme.colors.outline}/>
</View>
</Card.Content>
</Card>
<Text variant="bodyMedium" style={modalStyles.formFootnote}>
Ensure you have either an Amusement IC or Transport card ready to scan.
After the scan has completed, the card will be saved to your device.
</Text>
</View>
</>)
}