mirror of
https://gitea.tendokyu.moe/ppc/amnet.git
synced 2026-10-01 03:07:55 +03:00
add physical card scan to server page
This commit is contained in:
@@ -1,27 +1,19 @@
|
||||
import {BSON} from "realm";
|
||||
import React, {useCallback, useEffect, useLayoutEffect, useMemo, useState} from "react";
|
||||
import {ActivityIndicator, Button, Paragraph, Title, useTheme} from "react-native-paper";
|
||||
import {
|
||||
Platform,
|
||||
RefreshControl,
|
||||
ScrollView,
|
||||
Share,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View
|
||||
} from "react-native";
|
||||
import {ActivityIndicator, Button, IconButton, Paragraph, Title, useTheme} from "react-native-paper";
|
||||
import {Platform, RefreshControl, ScrollView, Share, StyleSheet, Text, TouchableOpacity, View} from "react-native";
|
||||
import {
|
||||
CircleCheck,
|
||||
CircleDot,
|
||||
CreditCard,
|
||||
Nfc,
|
||||
Plus,
|
||||
SmartphoneNfcIcon,
|
||||
Share as ShareIcon,
|
||||
WifiOff,
|
||||
TvMinimalPlay,
|
||||
Star
|
||||
Star,
|
||||
SmartphoneNfc,
|
||||
SquareArrowOutUpRight
|
||||
} from "lucide-react-native";
|
||||
|
||||
import {EmptyStatePlaceholder} from "../components/EmptyStatePlaceholder";
|
||||
@@ -33,14 +25,8 @@ import RealmContext from "../models/RealmContext";
|
||||
import {AMServer} from "../models/AMServer";
|
||||
import {AMCard} from "../models/AMCard";
|
||||
|
||||
import {icCard0008Number} from "../lib/iccard";
|
||||
import {
|
||||
AMServerConnectionInfo,
|
||||
AMServerInstanceMetrics,
|
||||
fetchServerInfo,
|
||||
getGameName,
|
||||
performCardIn
|
||||
} from "amnet-shared";
|
||||
import {felicaCardReadAsync, icCard0008Number, ICType} from "../lib/iccard";
|
||||
import {AMServerConnectionInfo, AMServerInstanceMetrics, fetchServerInfo, getGameName, performCardIn} from "amnet-shared";
|
||||
|
||||
import ObjectId = BSON.ObjectId;
|
||||
|
||||
@@ -153,7 +139,7 @@ export default function ServerCardIn({route, navigation}) {
|
||||
const cardId = selectedCard.physicalCardData && server.preferredCardIdFormat === "idm" ? icCard0008Number(selectedCard.physicalCardData.cardIDm) : selectedCard.code;
|
||||
const abortController = new AbortController();
|
||||
|
||||
performCardIn(server, {cardId}, abortController.signal).then(async r => {
|
||||
performCardIn(server, {cardId}, abortController.signal).then(r => {
|
||||
const now = new Date();
|
||||
realm.write(() => {
|
||||
server.lastUsed = now;
|
||||
@@ -172,6 +158,30 @@ export default function ServerCardIn({route, navigation}) {
|
||||
return () => abortController.abort();
|
||||
}, [server, selectedCard]);
|
||||
|
||||
const performPhysicalCardIn = useCallback(async () => {
|
||||
const card = await felicaCardReadAsync();
|
||||
|
||||
if (!card) {
|
||||
return;
|
||||
}
|
||||
|
||||
setCardInCompleted(false);
|
||||
|
||||
const cardId = server.preferredCardIdFormat === "idm" || card.type !== ICType.Amusement ? icCard0008Number(card.idm) : card.accessCode;
|
||||
const abortController = new AbortController();
|
||||
|
||||
performCardIn(server, {cardId}, abortController.signal).then(r => {
|
||||
setCardInCompleted(r);
|
||||
}).catch(() => {
|
||||
setCardInCompleted(false);
|
||||
}).finally(() => {
|
||||
setTimeout(() => setCardInCompleted(null), 3000);
|
||||
});
|
||||
|
||||
setTimeout(() => abortController.abort(), 5000);
|
||||
return () => abortController.abort();
|
||||
}, [server]);
|
||||
|
||||
const performRefresh = () => {
|
||||
setServerRefreshing(true);
|
||||
setServerReloadCounter(serverReloadCounter + 1);
|
||||
@@ -191,7 +201,8 @@ export default function ServerCardIn({route, navigation}) {
|
||||
}
|
||||
|
||||
return (
|
||||
<ScrollView contentInsetAdjustmentBehavior="automatic" refreshControl={<RefreshControl refreshing={serverRefreshing} onRefresh={performRefresh}/>}>
|
||||
<ScrollView contentInsetAdjustmentBehavior="automatic"
|
||||
refreshControl={<RefreshControl refreshing={serverRefreshing} onRefresh={performRefresh}/>}>
|
||||
<View style={styles.container}>
|
||||
<StatusAlert server={server} connectionEstablished={serverConnectionSuccess}/>
|
||||
<View style={styles.section}>
|
||||
@@ -202,15 +213,27 @@ export default function ServerCardIn({route, navigation}) {
|
||||
actionIcon={<CircleCheck color="green" size={24}/>}>
|
||||
{serverDefaultCard?.code == selectedCard.code && <DefaultCardDetail/>}
|
||||
</ICCard>
|
||||
<Button textColor="white"
|
||||
onPress={cardInCallback}
|
||||
disabled={disableCardIn}
|
||||
contentStyle={{flexDirection: 'row-reverse'}}
|
||||
mode={serverConnectionSuccess ? "contained" : "outlined"}
|
||||
buttonColor={cardInCompleted === false ? "red" : "green"}
|
||||
icon={() => <Nfc color={disableCardIn ? "gray" : "white"} size={18}/>}>
|
||||
{cardInCompleted === null ? "Card In" : cardInCompleted ? "Card In Success" : "Failed"}
|
||||
</Button>
|
||||
<View style={{display: "flex", flexDirection: "row", gap: 5}}>
|
||||
<Button textColor="white"
|
||||
onPress={cardInCallback}
|
||||
disabled={disableCardIn}
|
||||
style={{flex: 1}}
|
||||
contentStyle={{flexDirection: 'row-reverse'}}
|
||||
mode={serverConnectionSuccess ? "contained" : "outlined"}
|
||||
buttonColor={cardInCompleted === false ? "red" : "green"}
|
||||
icon={() => <SquareArrowOutUpRight color={disableCardIn ? "gray" : "white"} size={18}/>}>
|
||||
{cardInCompleted === null ? "Card In" : cardInCompleted ? "Card In Success" : "Failed"}
|
||||
</Button>
|
||||
{Platform.OS === 'ios' && (
|
||||
<IconButton
|
||||
mode="outlined"
|
||||
iconColor="#03a9f4"
|
||||
disabled={disableCardIn}
|
||||
onPress={performPhysicalCardIn}
|
||||
style={{borderRadius: 10, margin: 0, height: "100%", flex: 0.2}}
|
||||
icon={() => <SmartphoneNfc color={disableCardIn ? "gray" : "white"} size={18}/>}/>
|
||||
)}
|
||||
</View>
|
||||
</>
|
||||
: <View style={styles.emptyPlaceholderView}>
|
||||
<EmptyStatePlaceholder message="No card selected" icon={<CreditCard color="gray"/>}/>
|
||||
|
||||
@@ -35,7 +35,7 @@ function CardAddMethodSelector() {
|
||||
manually or generating new ones.</Paragraph>
|
||||
</View>
|
||||
|
||||
<MethodCard color={theme.colors.primary}
|
||||
<MethodCard color="#03a9f4"
|
||||
icon={<SmartphoneNfc/>}
|
||||
title="Scan Physical Card"
|
||||
description="Amusement IC or Transport cards supported"
|
||||
@@ -46,13 +46,11 @@ function CardAddMethodSelector() {
|
||||
<MethodCard color={theme.colors.secondary}
|
||||
icon={<SquareAsterisk/>}
|
||||
title="Enter Details Manually"
|
||||
//description="Manually enter card details (or generate a new one)"
|
||||
onPress={() => navigation.navigate('Manual')}/>
|
||||
|
||||
<MethodCard color="cornflowerblue"
|
||||
<MethodCard color="#4caf50"
|
||||
icon={<SquarePlus/>}
|
||||
title="Generate New Card"
|
||||
//description="Create a card from scratch"
|
||||
onPress={() => navigation.navigate('Manual', {autoGenerate: true})}/>
|
||||
</View>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user