mirror of
https://gitea.tendokyu.moe/ppc/amnet.git
synced 2026-09-22 22:28:22 +03:00
130 lines
5.2 KiB
TypeScript
130 lines
5.2 KiB
TypeScript
import {StatusBar} from "expo-status-bar";
|
|
import {generateCardNumber, DecodeAMAccessCode} from "amnet-shared";
|
|
import {useCallback, useEffect, useLayoutEffect, useState} from "react";
|
|
import {Button, Card, Divider, Text, useTheme} from "react-native-paper";
|
|
import {Alert, Platform, TextInput, TouchableOpacity, View} from "react-native";
|
|
|
|
import {AMCard} from "../../models/AMCard";
|
|
import RealmContext from "../../models/RealmContext";
|
|
import {useModalStyles} from "../../utils/ModalStyles";
|
|
|
|
const {useRealm} = RealmContext;
|
|
|
|
export default function CardAddModal({navigation, route}) {
|
|
const realm = useRealm();
|
|
const theme = useTheme();
|
|
const modalStyles = useModalStyles();
|
|
|
|
// state values
|
|
const [cardName, setCardName] = useState('');
|
|
const [cardNumber, setCardNumber] = useState('');
|
|
|
|
const cardAddCallback = useCallback(async (cardId: string, cardName: string) => {
|
|
try {
|
|
// validate AM card numbers
|
|
cardId.startsWith('5') && DecodeAMAccessCode(cardId);
|
|
} catch {
|
|
const shouldContinue = await new Promise<boolean>(done => {
|
|
Alert.alert(
|
|
"Invalid Access Code",
|
|
"It looks like the access code isn't from an Amusement IC card. Please double-check the code, and if it's correct, note that some networks might still reject it.",
|
|
[
|
|
{
|
|
style: "destructive",
|
|
text: "Continue Anyway",
|
|
onPress: () => done(true)
|
|
},
|
|
{
|
|
style: "cancel",
|
|
text: "Cancel",
|
|
onPress: () => done(false)
|
|
}
|
|
], {
|
|
cancelable: true,
|
|
onDismiss: () => done(false)
|
|
});
|
|
})
|
|
|
|
if (!shouldContinue) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
const duplicateCard = realm.objects<AMCard>(AMCard.schema.name).find(x => x.code === cardId);
|
|
|
|
if (duplicateCard) {
|
|
realm.write(() => {
|
|
duplicateCard.name = cardName;
|
|
});
|
|
} else {
|
|
realm.write(() => {
|
|
realm.create<AMCard>(AMCard.schema.name, AMCard.generate(cardId, cardName));
|
|
});
|
|
}
|
|
|
|
if (Platform.OS === 'ios') {
|
|
navigation.getParent()?.goBack();
|
|
} else {
|
|
navigation.goBack();
|
|
}
|
|
}, [realm]);
|
|
|
|
useLayoutEffect(() => {
|
|
const canSave = cardName.length && cardNumber.length === 20;
|
|
const themeOptions = canSave ? modalStyles.headerText : {color: theme.colors.outline};
|
|
|
|
navigation.setOptions({
|
|
headerRight: () => <TouchableOpacity disabled={!canSave}
|
|
onPress={() => cardAddCallback(cardNumber, cardName)}>
|
|
<Text variant="bodyMedium" style={{...themeOptions}}>Save</Text>
|
|
</TouchableOpacity>
|
|
})
|
|
}, [navigation, modalStyles, cardName, cardNumber]);
|
|
|
|
// check params for autoGenerate request
|
|
useEffect(() => {
|
|
if (route.params?.autoGenerate) {
|
|
setCardNumber(generateCardNumber());
|
|
}
|
|
}, []);
|
|
|
|
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>
|
|
|
|
<Divider style={{marginVertical: 15}}/>
|
|
|
|
<View style={modalStyles.formItemRow}>
|
|
<Text variant="bodyMedium" style={modalStyles.formItemName}>
|
|
Access Code
|
|
</Text>
|
|
<TextInput numberOfLines={1}
|
|
keyboardType="numeric"
|
|
placeholder="0000 0000 0000 0000 0000"
|
|
placeholderTextColor={theme.colors.outline}
|
|
value={cardNumber.match(/.{1,4}/g)?.join(" ")}
|
|
onChangeText={val => setCardNumber(val?.replaceAll(/\D/g, '').substring(0, 20) ?? '')}
|
|
style={{...modalStyles.formItemInput, color: theme.colors.onSurface}}/>
|
|
</View>
|
|
</Card.Content>
|
|
</Card>
|
|
|
|
<Button mode="text" onPress={() => setCardNumber(generateCardNumber())}>Generate Access Code</Button>
|
|
</View>
|
|
</>)
|
|
}
|