import {BSON} from "realm"; import {Alert, RefreshControl, ScrollView, StyleSheet, View} from "react-native"; import React, {useCallback, useEffect, useLayoutEffect, useMemo, useState} from "react"; import {ActivityIndicator, Button, Card, Paragraph, Title, useTheme} from "react-native-paper"; import {CircleCheck, CircleDot, CreditCard, Nfc, Plus, SmartphoneNfcIcon, WifiOff} from "lucide-react-native"; import {EmptyStatePlaceholder} from "../components/EmptyStatePlaceholder"; import {AimeCard} from "../components/AimeCard"; import RealmContext from "../models/RealmContext"; import {AMServer} from "../models/AMServer"; import {AMCard} from "../models/AMCard"; import ObjectId = BSON.ObjectId; const {useRealm, useQuery, useObject} = RealmContext; export default function ServerCardIn({route, navigation}) { const realm = useRealm(); const theme = useTheme(); // card listing state const query = useQuery(AMCard); const cards = useMemo(() => query.sorted("lastUsed", true), [query]); const [selectedCard, setSelectedCard] = useState(null); const [cardInCompleted, setCardInCompleted] = useState(null); const unselectedCards = useMemo(() => { // protect against selected card being removed switch (selectedCard?.isValid()) { case true: return cards.filter(x => !x._id.equals(selectedCard._id)); // clear out invalid case false: setSelectedCard(null); return cards; default: return cards; } }, [cards, selectedCard]); // server state const [serverRefreshing, setServerRefreshing] = useState(false); const [serverReloadCounter, setServerReloadCounter] = useState(0); const [connectionTestResult, setConnectionTestResult] = useState(null); let {serverId} = route.params; const server = useObject(AMServer, ObjectId.createFromHexString(serverId)); useLayoutEffect(() => { if (!server) { return; } navigation.setOptions({ title: server.serverName, }); }, [navigation, server]); // set selected card (once) useEffect(() => { if (selectedCard?.isValid() === true) { return; } setSelectedCard(cards.length ? cards[0] : null); }, [realm]); // refresh server info (server change or reload request) useEffect(() => { if (server?.isValid() !== true) { navigation.goBack(); // don't show error on invalid server, just means it was deleted if (!server) { Alert.alert("Server not found", "The server you are looking for does not exist."); } return; } const abortController = new AbortController(); server.getInfo(abortController.signal).then(r => { if (r) { setConnectionTestResult(true); realm.write(() => { server.lastUsed = new Date(); Object.keys(r).forEach(x => server[x] = r[x]); }); } else { setConnectionTestResult(false); } }).catch(() => { setConnectionTestResult(false); }).finally(() => { setServerRefreshing(false); }); setTimeout(() => abortController.abort(), 5000); return () => abortController.abort(); }, [serverId, serverReloadCounter, realm]); // card in callback const performCardIn = useCallback(() => { setCardInCompleted(false); const abortController = new AbortController(); server.submitCard(selectedCard.code, abortController.signal).then(async r => { const now = new Date(); setCardInCompleted(r); realm.write(() => { server.lastUsed = now; selectedCard.lastUsed = now; }); setSelectedCard(selectedCard); }).catch(() => { setCardInCompleted(false); }).finally(() => { setTimeout(() => setCardInCompleted(null), 3000); }); setTimeout(() => abortController.abort(), 5000); return () => abortController.abort(); }, [server, selectedCard]); const disableCardIn = useMemo(() => cardInCompleted !== null || !connectionTestResult, [cardInCompleted, connectionTestResult]); if (connectionTestResult === null) { return ( Connecting to server... ) } return ( { setServerRefreshing(true); setServerReloadCounter(serverReloadCounter + 1); }}/>}> {!connectionTestResult && ( Connection Failed There was an issue connecting to {server.serverAddress}. Check the network connection and try again. )} Selected Card {selectedCard ? <> }/> : }/> } {cards.length > 0 && ( Additional Cards {unselectedCards.length ? unselectedCards.map(card => ( } onPress={() => setSelectedCard(card)}/>)) : }/> } )} ) } const styles = StyleSheet.create({ section: { gap: 10 }, emptyPlaceholderView: { marginVertical: 30, gap: 15 }, container: { paddingHorizontal: 16, paddingBottom: 25, gap: 20 } })