mirror of
https://gitea.tendokyu.moe/ppc/amnet.git
synced 2026-09-22 22:28:22 +03:00
87 lines
3.8 KiB
TypeScript
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>
|
|
</>)
|
|
} |