diff --git a/amnet-native/app/ServerCardIn.tsx b/amnet-native/app/ServerCardIn.tsx index d9bcacd..20fd2c8 100644 --- a/amnet-native/app/ServerCardIn.tsx +++ b/amnet-native/app/ServerCardIn.tsx @@ -1,9 +1,215 @@ -import {ScrollView} from "react-native"; +import {Alert, FlatList, RefreshControl, ScrollView, StyleSheet, View} from "react-native"; +import React, {useCallback, useEffect, useLayoutEffect, useMemo, useState} from "react"; +import {CircleCheck, CircleDot, CreditCard, Nfc, Plus, SmartphoneNfcIcon, WifiOff} from "lucide-react-native"; +import {ActivityIndicator, Button, Card, MD3Colors, Paragraph, Title, useTheme} from "react-native-paper"; +import {EmptyStatePlaceholder} from "../components/EmptyStatePlaceholder"; +import {AimeCard} from "../components/AimeCard"; +import {BSON} from "realm"; + +import RealmContext from "../models/RealmContext"; +import {AMServer} from "../models/AMServer"; +import {AMCard} from "../models/AMCard"; +import ObjectId = BSON.ObjectId; + +const {useRealm, useQuery} = 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"), [query]); + + const [selectedCard, setSelectedCard] = useState(null); + const [cardInCompleted, setCardInCompleted] = useState(null); + + const unselectedCards = useMemo(() => cards.filter(x => !x._id.equals(selectedCard?._id)), [cards, selectedCard]); + + // server state + const [serverInfo, setServerInfo] = useState(null); + const [serverRefreshing, setServerRefreshing] = useState(false); + const [serverReloadCounter, setServerReloadCounter] = useState(0); + const [connectionTestResult, setConnectionTestResult] = useState(null); + + const {serverId} = route.params; + + useLayoutEffect(() => { + if (!serverInfo) { + return; + } + + navigation.setOptions({ + title: serverInfo.serverName, + + }) + }, [navigation, serverInfo]); + + // set selected card (once) + useEffect(() => { + setSelectedCard(cards.length ? cards[0] : null); + }, [realm]); + + // refresh server info (server change or reload request) + useEffect(() => { + const server = realm.objectForPrimaryKey(AMServer, ObjectId.createFromHexString(serverId)); + + if (!server) { + navigation.goBack(); + + Alert.alert("Server not found", "The server you are looking for does not exist."); + return; + } + + setServerInfo(server); + + const abortController = new AbortController(); + server.connect(abortController.signal).then(r => { + if (r) { + setConnectionTestResult(true); + + realm.write(() => { + Object.apply(server, r); + server.lastUsed = new Date(); + }); + + // reset server to reflect changes + setServerInfo(server); + } else { + setConnectionTestResult(false); + } + }).catch(() => { + setConnectionTestResult(false); + }).finally(() => { + setServerRefreshing(false); + }); + + // pull abort on unmount + return () => abortController.abort(); + }, [serverId, serverReloadCounter, realm]); + + // card in callback + const performCardIn = useCallback(() => { + setCardInCompleted(false); + + const abortController = new AbortController(); + serverInfo.submitCard(selectedCard.code, abortController.signal).then(r => { + setCardInCompleted(r); + realm.write(() => { + selectedCard.lastUsed = new Date(); + setSelectedCard(selectedCard); + }); + }).catch(() => { + setCardInCompleted(false); + }).finally(() => { + setTimeout(() => setCardInCompleted(null), 3000); + }); + + setTimeout(() => abortController.abort(), 5000); + return () => abortController.abort(); + }, [serverInfo, selectedCard]); + + if (connectionTestResult === null) { + return ( + + + Connecting to server... + + ) + } + + // cached vars + const disableCardIn = cardInCompleted !== null || !connectionTestResult; -export default function ServerCardIn({navigation}) { return ( - - + { + setServerRefreshing(true); + setServerReloadCounter(serverReloadCounter + 1); + }}/>}> + {!connectionTestResult && ( + + + + + Connection Failed + + + There was an issue connecting to the AimeNet server at {serverInfo.serverAddress}. + Check connections and game status and try again. + + + + )} + + + Selected Card + {selectedCard ? <> + }/> + + + : + }/> + + } + + + {cards.length && ( + + Additional Cards + + + {unselectedCards.length + ? unselectedCards.map(card => ( + } + onPress={() => setSelectedCard(card)}/>)) + : + }/> + } + )} ) -} \ No newline at end of file +} + +const styles = StyleSheet.create({ + section: { + gap: 10 + }, + emptyPlaceholderView: { + marginVertical: 30, + gap: 15 + }, + container: { + paddingHorizontal: 16, + paddingBottom: 25, + gap: 20 + } +}) diff --git a/amnet-native/app/ServerManagerStack.tsx b/amnet-native/app/ServerManagerStack.tsx index 037bd44..9d9cc3d 100644 --- a/amnet-native/app/ServerManagerStack.tsx +++ b/amnet-native/app/ServerManagerStack.tsx @@ -31,7 +31,7 @@ export default function ServerManagerStack({navigation}) { }}/> - +