27 Commits
Author SHA1 Message Date
ppc 6c47de61d9 add alt icon for android 2024-11-21 22:50:40 +00:00
ppc 7a4d924227 add build-properties plugin 2024-11-21 22:50:22 +00:00
ppc 2eb61d74a7 assetlinks.json fix 2024-11-18 22:13:28 +00:00
ppc 89101c5d5f more fun 2024-11-18 22:00:54 +00:00
ppc 2fe9238d02 update dll metadata 2024-11-18 22:00:54 +00:00
ppc d1b626ffac add android asset link validation file 2024-11-18 22:00:53 +00:00
ppc fe7ab1c50f prepare android release bundling 2024-11-18 22:00:53 +00:00
ppc 5a3a7be906 add android intent filters 2024-11-18 22:00:53 +00:00
ppc 703067e6d6 whitespace removal 2024-11-18 22:00:53 +00:00
ppc 67f90397b0 upgrade server to .net9 2024-11-18 22:00:53 +00:00
ppc 8ba439721f android-specific adjustments 2024-11-18 22:00:53 +00:00
ppc 35a8c28c0e more variable naming 2024-11-09 20:21:33 +00:00
ppc a0b44c165b improve variable naming 2024-11-09 20:14:25 +00:00
ppc 489a93adfe update card selection, automatically check for server startup to end 2024-11-09 20:11:00 +00:00
ppc 6172f2bbd7 fix site on ios 12 2024-11-09 16:47:07 +00:00
ppc 414a199552 target es2019 (safari 12+) 2024-11-09 15:53:27 +00:00
ppc a583b72798 update expo sdk version 2024-11-06 16:04:15 +00:00
ppc 33cd321325 improve card selection when changing games 2024-11-06 16:02:59 +00:00
ppc 85b900ea30 fix haptic feedback not happening on iOS (see https://github.com/facebook/react-native/issues/43388) 2024-10-26 18:08:31 +01:00
ppc 4038de583d fix card unselection on reload 2024-10-26 18:07:57 +01:00
ppc b08a7cd125 show oldest cards at the top 2024-10-14 09:41:15 +01:00
ppc 4b6037fe24 fix bad selection logic 2024-09-28 09:22:43 +01:00
ppc 4702052cc4 prevent invalid array access 2024-09-26 12:13:46 +01:00
ppc 969edd3675 add game-default card indicator 2024-09-26 12:09:38 +01:00
ppc 0d200a22fc update vulnerable packages (CVE-2024-47068, CVE-2024-45812 and CVE-2024-45811) 2024-09-24 12:44:26 +01:00
ppc c30abfe482 auto-select card last used with specific game 2024-09-20 11:57:06 +01:00
ppc 6628071f5b fix dark mode inconsistency 2024-09-20 11:55:27 +01:00
27 changed files with 2744 additions and 1797 deletions
+5 -5
View File
@@ -4,18 +4,18 @@
<PublishAot>true</PublishAot> <PublishAot>true</PublishAot>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<EventSourceSupport>false</EventSourceSupport> <EventSourceSupport>false</EventSourceSupport>
<TargetFramework>net8.0-windows</TargetFramework> <TargetFramework>net9.0-windows</TargetFramework>
<OptimizationPreference>Size</OptimizationPreference> <OptimizationPreference>Size</OptimizationPreference>
<NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile> <NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<AssemblyName>amnet</AssemblyName> <Product>AMNet</Product>
<ProductName>AMNet</ProductName>
<CompanyName>ppc</CompanyName> <CompanyName>ppc</CompanyName>
<Title>AMNet Server</Title> <AssemblyName>amnet</AssemblyName>
<Description>Server component for the AMNet IC Card switcher</Description> <AssemblyTitle>AMNet Server</AssemblyTitle>
<Copyright>Copyright 2024 (c) ppc</Copyright> <Copyright>Copyright 2024 (c) ppc</Copyright>
<Description>Server component for the AMNet IC Card switcher</Description>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
+2 -1
View File
@@ -1,6 +1,7 @@
using System; using System;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Threading;
namespace AMNet.Server; namespace AMNet.Server;
@@ -9,7 +10,7 @@ namespace AMNet.Server;
/// </summary> /// </summary>
internal class CardPresenter internal class CardPresenter
{ {
private readonly object _cardLock = new(); private readonly Lock _cardLock = new();
private StoredCard _currentCard; private StoredCard _currentCard;
public record StoredCard(byte[] Value, string OriginalValue, long ExpiresAt) public record StoredCard(byte[] Value, string OriginalValue, long ExpiresAt)
+56 -3
View File
@@ -1,7 +1,7 @@
{ {
"name": "AMNet", "name": "AMNet",
"scheme": "amnet", "scheme": "amnet",
"version": "1.1.0", "version": "1.2.0",
"slug": "amnet-native", "slug": "amnet-native",
"icon": "./assets/icon.png", "icon": "./assets/icon.png",
"userInterfaceStyle": "automatic", "userInterfaceStyle": "automatic",
@@ -19,7 +19,60 @@
} }
}, },
"android": { "android": {
"package": "moe.ppc.amnet" "package": "moe.ppc.amnet",
"versionCode": 3,
"adaptiveIcon": {
"foregroundImage": "./assets/icon-android-alt.png",
"backgroundColor": "#DED8FD"
},
"splash": {
"image": "./assets/icon-android-alt.png",
"backgroundColor": "#DED8FD",
"dark": {
"backgroundColor": "#3B5ACC"
}
},
"intentFilters": [
{
"autoVerify": true,
"action": "VIEW",
"data": [
{
"scheme": "http",
"host": "card.ppc.moe",
"pathPrefix": "/#/servers"
},
{
"scheme": "http",
"host": "card.ppc.moe",
"pathPrefix": "/#/cards"
},
{
"scheme": "https",
"host": "card.ppc.moe",
"pathPrefix": "/#/servers"
},
{
"scheme": "https",
"host": "card.ppc.moe",
"pathPrefix": "/#/cards"
}
],
"category": [
"BROWSABLE",
"DEFAULT"
]
}
]
}, },
"plugins": [] "plugins": [
[
"expo-build-properties",
{
"android": {
"usesCleartextTraffic": true
}
}
]
]
} }
+11 -9
View File
@@ -1,7 +1,7 @@
import {generateCardNumber} from "amnet-shared"; import {generateCardNumber} from "amnet-shared";
import {useModalStyles} from "../utils/ModalStyles"; import {useModalStyles} from "../utils/ModalStyles";
import {useCallback, useLayoutEffect, useState} from "react"; import {useCallback, useLayoutEffect, useState} from "react";
import {TextInput, TouchableOpacity, View} from "react-native"; import {Platform, TextInput, TouchableOpacity, View} from "react-native";
import {Button, Card, Divider, Paragraph, useTheme} from "react-native-paper"; import {Button, Card, Divider, Paragraph, useTheme} from "react-native-paper";
import {AMCard} from "../models/AMCard"; import {AMCard} from "../models/AMCard";
@@ -36,13 +36,15 @@ export default function CardAddModal({navigation, route}) {
}, [realm]); }, [realm]);
// header effects // header effects
useLayoutEffect(() => { if (Platform.OS === 'ios') {
navigation.setOptions({ useLayoutEffect(() => {
headerLeft: () => <TouchableOpacity onPress={() => navigation.goBack()}> navigation.setOptions({
<Paragraph style={modalStyles.headerText}>Cancel</Paragraph> headerLeft: () => <TouchableOpacity onPress={() => navigation.goBack()}>
</TouchableOpacity>, <Paragraph style={modalStyles.headerText}>Cancel</Paragraph>
}); </TouchableOpacity>,
}, [navigation, modalStyles]); });
}, [navigation, modalStyles]);
}
useLayoutEffect(() => { useLayoutEffect(() => {
const canSave = cardName.length && cardNumber.length === 20; const canSave = cardName.length && cardNumber.length === 20;
@@ -103,7 +105,7 @@ export default function CardAddModal({navigation, route}) {
placeholderTextColor={theme.colors.outline} placeholderTextColor={theme.colors.outline}
value={cardNumber.match(/.{1,4}/g)?.join(" ")} value={cardNumber.match(/.{1,4}/g)?.join(" ")}
onChangeText={val => setCardNumber(val?.replaceAll(/\D/g, '').substring(0, 20) ?? '')} onChangeText={val => setCardNumber(val?.replaceAll(/\D/g, '').substring(0, 20) ?? '')}
style={{...modalStyles.formItemInput, color: cardNumberReadOnly ? theme.colors.onSurfaceDisabled : undefined}}/> style={{...modalStyles.formItemInput, color: cardNumberReadOnly ? theme.colors.onSurfaceDisabled : theme.colors.onSurface}}/>
</View> </View>
</Card.Content> </Card.Content>
</Card> </Card>
+17 -9
View File
@@ -1,15 +1,16 @@
import * as Clipboard from "expo-clipboard"; import * as Clipboard from "expo-clipboard";
import {FlatList, View} from "react-native"; import {FlatList, Platform, View} from "react-native";
import React, {useCallback, useMemo} from "react"; import React, {useCallback, useMemo} from "react";
import {Button, useTheme} from "react-native-paper"; import {Button, useTheme} from "react-native-paper";
import {CreditCard, Plus} from "lucide-react-native"; import {CreditCard, Plus} from "lucide-react-native";
import ContextMenu from "react-native-context-menu-view"; import ContextMenu from "react-native-context-menu-view";
import {AMCard} from "../models/AMCard";
import {AimeCard} from "../components/AimeCard"; import {AimeCard} from "../components/AimeCard";
import RealmContext from "../models/RealmContext";
import {EmptyStatePlaceholder} from "../components/EmptyStatePlaceholder"; import {EmptyStatePlaceholder} from "../components/EmptyStatePlaceholder";
import {AMCard} from "../models/AMCard";
import RealmContext from "../models/RealmContext";
const {useQuery, useRealm} = RealmContext; const {useQuery, useRealm} = RealmContext;
function CardEntry(props: { card: AMCard, navigation: any }) { function CardEntry(props: { card: AMCard, navigation: any }) {
@@ -43,7 +44,7 @@ function CardEntry(props: { card: AMCard, navigation: any }) {
actions={[ actions={[
{title: "Copy Card Number", systemIcon: "doc.on.doc"}, {title: "Copy Card Number", systemIcon: "doc.on.doc"},
{ {
title: "", title: Platform.OS === 'ios' ? "" : "Additional Actions",
inlineChildren: true, inlineChildren: true,
actions: [ actions: [
{title: "Edit Card", systemIcon: "square.and.pencil"}, {title: "Edit Card", systemIcon: "square.and.pencil"},
@@ -55,16 +56,16 @@ function CardEntry(props: { card: AMCard, navigation: any }) {
} }
export default function CardManager({navigation}) { export default function CardManager({navigation}) {
const query = useQuery(AMCard);
const cards = useMemo(() => query.sorted("added", true), [query]);
const theme = useTheme(); const theme = useTheme();
const query = useQuery(AMCard);
const cards = useMemo(() => query.sorted("added", false), [query]);
if (!cards.length) { if (!cards.length) {
return ( return (
<View style={{flex: 1, gap: 10, justifyContent: "center", alignItems: "center"}}> <View style={{flex: 1, gap: 10, justifyContent: "center", alignItems: "center"}}>
<EmptyStatePlaceholder message="No cards added yet" icon={<CreditCard color="gray"/>}/> <EmptyStatePlaceholder message="No cards added yet" icon={<CreditCard color="gray"/>}/>
<Button mode="text" onPress={() => navigation.navigate('add-card')} <Button mode="text"
onPress={() => navigation.navigate('add-card')}
icon={() => <Plus size={18} color={theme.colors.primary}/>}>Add Card</Button> icon={() => <Plus size={18} color={theme.colors.primary}/>}>Add Card</Button>
</View> </View>
) )
@@ -76,7 +77,14 @@ export default function CardManager({navigation}) {
data={cards} data={cards}
keyExtractor={i => i._id.toString()} keyExtractor={i => i._id.toString()}
contentContainerStyle={{paddingBottom: 120}} contentContainerStyle={{paddingBottom: 120}}
renderItem={i => <CardEntry card={i.item} navigation={navigation}/>}
ItemSeparatorComponent={() => <View style={{height: 15}}/>} ItemSeparatorComponent={() => <View style={{height: 15}}/>}
renderItem={i => <CardEntry card={i.item} navigation={navigation}/>}/> ListFooterComponent={() => Platform.OS === "ios" ? null : (
<Button mode="text"
onPress={() => navigation.navigate('add-card')}
style={{marginTop: 15, marginBottom: 25}}
icon={() => <Plus size={18} color={theme.colors.primary}/>}>Add Card</Button>
)}
/>
) )
} }
+10 -8
View File
@@ -1,7 +1,7 @@
import {useModalStyles} from "../utils/ModalStyles"; import {useModalStyles} from "../utils/ModalStyles";
import {Card, Paragraph, useTheme} from "react-native-paper"; import {Card, Paragraph, useTheme} from "react-native-paper";
import {TextInput, TouchableOpacity, View} from "react-native";
import {useCallback, useEffect, useLayoutEffect, useState} from "react"; import {useCallback, useEffect, useLayoutEffect, useState} from "react";
import {Platform, TextInput, TouchableOpacity, View} from "react-native";
import RealmContext from "../models/RealmContext"; import RealmContext from "../models/RealmContext";
import {AMServer} from "../models/AMServer"; import {AMServer} from "../models/AMServer";
@@ -20,13 +20,15 @@ export default function ServerAddModal({route, navigation}) {
handleDeepLink(realm, navigation, route, setServerAddress); handleDeepLink(realm, navigation, route, setServerAddress);
}, []); }, []);
useLayoutEffect(() => { if (Platform.OS === 'ios') {
navigation.setOptions({ useLayoutEffect(() => {
headerLeft: () => <TouchableOpacity onPress={() => navigation.goBack()}> navigation.setOptions({
<Paragraph style={modalStyles.headerText}>Cancel</Paragraph> headerLeft: () => <TouchableOpacity onPress={() => navigation.goBack()}>
</TouchableOpacity>, <Paragraph style={modalStyles.headerText}>Cancel</Paragraph>
}); </TouchableOpacity>,
}, [navigation, modalStyles]); });
}, [navigation, modalStyles]);
}
useLayoutEffect(() => { useLayoutEffect(() => {
let urlValid = false; let urlValid = false;
+153 -105
View File
@@ -1,15 +1,33 @@
import {BSON} from "realm"; import {BSON} from "realm";
import {useSafeAreaInsets} from "react-native-safe-area-context";
import React, {useCallback, useEffect, useLayoutEffect, useMemo, useState} from "react"; import React, {useCallback, useEffect, useLayoutEffect, useMemo, useState} from "react";
import {ActivityIndicator, Button, Paragraph, Title, useTheme} from "react-native-paper"; import {ActivityIndicator, Button, Paragraph, Title, useTheme} from "react-native-paper";
import {Alert, RefreshControl, ScrollView, Share, StyleSheet, TouchableOpacity, View} from "react-native";
import { import {
CircleCheck, CircleDot, CreditCard, Nfc, Plus, SmartphoneNfcIcon, Alert,
Share as ShareIcon, WifiOff, TvMinimalPlay Platform,
RefreshControl,
ScrollView,
Share,
StyleSheet,
Text,
TouchableOpacity,
View
} from "react-native";
import {
CircleCheck,
CircleDot,
CreditCard,
Nfc,
Plus,
SmartphoneNfcIcon,
Share as ShareIcon,
WifiOff,
TvMinimalPlay,
Star
} from "lucide-react-native"; } from "lucide-react-native";
import {EmptyStatePlaceholder} from "../components/EmptyStatePlaceholder"; import {EmptyStatePlaceholder} from "../components/EmptyStatePlaceholder";
import {AMUIStatusAlert} from "../components/AMUIStatusAlert"; import {AMUIStatusAlert} from "../components/AMUIStatusAlert";
import {AMUICardDetail} from "../components/AMUICard";
import {AimeCard} from "../components/AimeCard"; import {AimeCard} from "../components/AimeCard";
import RealmContext from "../models/RealmContext"; import RealmContext from "../models/RealmContext";
@@ -29,38 +47,33 @@ const {useRealm, useQuery, useObject} = RealmContext;
export default function ServerCardIn({route, navigation}) { export default function ServerCardIn({route, navigation}) {
const realm = useRealm(); const realm = useRealm();
const theme = useTheme(); const theme = useTheme();
const safeArea = useSafeAreaInsets();
// card listing state let {serverId} = route.params;
const query = useQuery(AMCard); const server = useObject(AMServer, ObjectId.createFromHexString(serverId));
const cards = useMemo(() => query.sorted("lastUsed", true), [query]); const cards = useQuery({
type: AMCard,
const [selectedCard, setSelectedCard] = useState<AMCard>(null); query: q => q.sorted("lastUsed", true)
const [cardInCompleted, setCardInCompleted] = useState<boolean>(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 // server state
const [serverRefreshing, setServerRefreshing] = useState<boolean>(false); const [serverRefreshing, setServerRefreshing] = useState<boolean>(false);
const [serverReloadCounter, setServerReloadCounter] = useState<number>(0); const [serverReloadCounter, setServerReloadCounter] = useState<number>(0);
const [connectionTestResult, setConnectionTestResult] = useState<boolean | null>(null); const [serverConnectionSuccess, setServerConnectionSuccess] = useState<boolean | null>(null);
let {serverId} = route.params; const [userSelectedCard, setUserSelectedCard] = useState<AMCard>(null);
const server = useObject(AMServer, ObjectId.createFromHexString(serverId)); const [cardInCompleted, setCardInCompleted] = useState<boolean>(null);
const disableCardIn = useMemo(() => cardInCompleted !== null || !serverConnectionSuccess, [cardInCompleted, serverConnectionSuccess]);
const serverDefaultCard = useMemo(() => {
if (server?.isValid() !== true || !server.gameId) {
return null;
}
return cards.find(x => x.lastUsedGameId === server.gameId);
}, [cards, server?.gameId]);
const selectedCard = useMemo(() => [userSelectedCard, serverDefaultCard, cards.at(0)].find(x => x?.isValid() == true) ?? null, [userSelectedCard, serverDefaultCard, cards]);
const unselectedCards = useMemo(() => cards.filter(x => x.isValid() === true && x._id.toHexString() !== selectedCard?._id.toHexString()), [cards, selectedCard]);
useLayoutEffect(() => { useLayoutEffect(() => {
if (server?.isValid() !== true) { if (server?.isValid() !== true) {
@@ -71,65 +84,80 @@ export default function ServerCardIn({route, navigation}) {
title: server.serverName, title: server.serverName,
headerRight: () => ( headerRight: () => (
<TouchableOpacity onPress={() => Share.share({url: server.shareUrl})}> <TouchableOpacity onPress={() => Share.share({url: server.shareUrl})}>
<ShareIcon/> <ShareIcon color={theme.colors.primary}/>
</TouchableOpacity> </TouchableOpacity>
) )
}); });
}, [navigation, server]); }, [navigation, server]);
// card selection, server connection checks // invalid card/server selection
useEffect(() => { useEffect(() => {
if (server?.isValid() !== true) { if (server?.isValid() !== true) {
navigation.goBack(); navigation.goBack();
return;
}
}, [server]);
// don't show error on invalid server, just means it was deleted useEffect(() => {
if (!server) { if (userSelectedCard?.isValid() === false) {
Alert.alert("Server not found", "The server you are looking for does not exist."); setUserSelectedCard(null);
} }
}, [userSelectedCard]);
// handle auto-refresh
useEffect(() => {
if (!serverConnectionSuccess || server?.isValid() !== true) {
return; return;
} }
if (selectedCard?.isValid() !== true) { const metrics = server as AMServerInstanceMetrics;
setSelectedCard(cards.length ? cards[0] : null); if (metrics.sessionUptime > 0 && metrics.timeSinceLastPoll === null) {
const timeout = setTimeout(() => setServerReloadCounter(serverReloadCounter + 1), 5000);
return () => clearTimeout(timeout);
} }
}, [serverConnectionSuccess, server]);
// server info update
useEffect(() => {
const abortController = new AbortController(); const abortController = new AbortController();
fetchServerInfo(server.serverAddress, abortController.signal).then(r => { fetchServerInfo(server.serverAddress, abortController.signal).then(r => {
if (r) { if (!r) {
setConnectionTestResult(true); setServerConnectionSuccess(false);
realm.write(() => { return;
server.lastUsed = new Date();
Object.keys(r).forEach(x => server[x] = r[x]);
});
} else {
setConnectionTestResult(false);
} }
setServerConnectionSuccess(true);
realm.write(() => {
server.lastUsed = new Date();
Object.keys(r).forEach(x => server[x] = r[x]);
});
}).catch(() => { }).catch(() => {
setConnectionTestResult(false); setServerConnectionSuccess(false);
}).finally(() => { }).finally(() => {
setServerRefreshing(false); setServerRefreshing(false);
}); });
setTimeout(() => abortController.abort(), 5000); const timeout = setTimeout(() => abortController.abort(), 5000);
return () => abortController.abort(); return () => {
clearTimeout(timeout);
abortController.abort();
}
}, [serverId, serverReloadCounter, realm]); }, [serverId, serverReloadCounter, realm]);
// card in callback
const cardInCallback = useCallback(() => { const cardInCallback = useCallback(() => {
setCardInCompleted(false); setCardInCompleted(false);
const abortController = new AbortController(); const abortController = new AbortController();
performCardIn(server, {cardId: selectedCard.code}, abortController.signal).then(async r => { performCardIn(server, {cardId: selectedCard.code}, abortController.signal).then(async r => {
const now = new Date(); const now = new Date();
setCardInCompleted(r);
realm.write(() => { realm.write(() => {
server.lastUsed = now; server.lastUsed = now;
selectedCard.lastUsed = now; selectedCard.lastUsed = now;
selectedCard.lastUsedGameId = server.gameId;
}); });
setSelectedCard(selectedCard); setCardInCompleted(r);
}).catch(() => { }).catch(() => {
setCardInCompleted(false); setCardInCompleted(false);
}).finally(() => { }).finally(() => {
@@ -143,12 +171,13 @@ export default function ServerCardIn({route, navigation}) {
const performRefresh = () => { const performRefresh = () => {
setServerRefreshing(true); setServerRefreshing(true);
setServerReloadCounter(serverReloadCounter + 1); setServerReloadCounter(serverReloadCounter + 1);
};
if (server?.isValid() !== true) {
return null;
} }
const gameName = useMemo(() => getGameName(server.gameId), [server.gameId]); if (serverConnectionSuccess === null) {
const disableCardIn = useMemo(() => cardInCompleted !== null || !connectionTestResult, [cardInCompleted, connectionTestResult]);
if (connectionTestResult === null) {
return ( return (
<View style={{flex: 1, justifyContent: "center", alignItems: "center", gap: 10}}> <View style={{flex: 1, justifyContent: "center", alignItems: "center", gap: 10}}>
<ActivityIndicator animating/> <ActivityIndicator animating/>
@@ -158,59 +187,63 @@ export default function ServerCardIn({route, navigation}) {
} }
return ( return (
<ScrollView contentInsetAdjustmentBehavior="automatic" <ScrollView contentInsetAdjustmentBehavior="automatic" refreshControl={<RefreshControl refreshing={serverRefreshing} onRefresh={performRefresh}/>}>
contentContainerStyle={{...styles.container, paddingTop: connectionTestResult ? 0 : 10}} <View style={styles.container}>
refreshControl={<RefreshControl refreshing={serverRefreshing} tintColor={theme.colors.onSurface} onRefresh={performRefresh}/>}> <StatusAlert server={server} connectionEstablished={serverConnectionSuccess}/>
<StatusAlert server={server} connectionEstablished={connectionTestResult}/> <View style={styles.section}>
<View style={styles.section}> <Title style={styles.sectionTitle}>{server.gameId ? `Play ${getGameName(server.gameId)}` : "Selected Card"}</Title>
<Title style={styles.sectionTitle}>{gameName ? `Play ${gameName}` : "Selected Card"}</Title> {selectedCard ? <>
{selectedCard ? <> <AimeCard actionDisabled
<AimeCard actionDisabled card={selectedCard}
card={selectedCard} actionIcon={<CircleCheck color="green" size={24}/>}>
actionIcon={<CircleCheck color="green" size={24}/>}/> {serverDefaultCard?.code == selectedCard.code && <DefaultCardDetail/>}
<Button textColor="white" </AimeCard>
onPress={cardInCallback} <Button textColor="white"
disabled={disableCardIn} 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={styles.emptyPlaceholderView}>
<EmptyStatePlaceholder message="No card selected" icon={<CreditCard color="gray"/>}/>
<Button mode="text"
icon={() => <Plus color={theme.colors.primary} size={18}/>}
onPress={() => navigation.navigate('add-card')}>
Add card
</Button>
</View>}
</View>
{cards.length > 0 && (
<View style={styles.section}>
<Title style={styles.sectionTitle}>Additional Cards</Title>
<Button mode="contained"
textColor="white"
buttonColor={theme.colors.tertiary}
icon={() => <Plus color="white" size={18}/>}
contentStyle={{flexDirection: 'row-reverse'}} contentStyle={{flexDirection: 'row-reverse'}}
mode={connectionTestResult ? "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={styles.emptyPlaceholderView}>
<EmptyStatePlaceholder message="No card selected" icon={<CreditCard color="gray"/>}/>
<Button mode="text"
icon={() => <Plus color={theme.colors.primary} size={18}/>}
onPress={() => navigation.navigate('add-card')}> onPress={() => navigation.navigate('add-card')}>
Add card Add card
</Button> </Button>
</View>}
{unselectedCards.length
? unselectedCards.map((card: AMCard) => (
<AimeCard card={card}
key={card._id.toHexString()}
actionIcon={<CircleDot color={theme.colors.tertiary} size={24}/>}
onPress={() => setUserSelectedCard(card)}>
{serverDefaultCard?.code == card.code && <DefaultCardDetail/>}
</AimeCard>))
: <View style={styles.emptyPlaceholderView}>
<EmptyStatePlaceholder message="No additional cards"
icon={<SmartphoneNfcIcon color="gray"/>}/>
</View>}
</View>)}
</View> </View>
{cards.length > 0 && (
<View style={styles.section}>
<Title style={styles.sectionTitle}>Additional Cards</Title>
<Button mode="contained"
textColor="white"
buttonColor={theme.colors.tertiary}
icon={() => <Plus color="white" size={18}/>}
contentStyle={{flexDirection: 'row-reverse'}}
onPress={() => navigation.navigate('add-card')}>
Add card
</Button>
{unselectedCards.length
? unselectedCards.map((card: AMCard) => (
<AimeCard card={card}
key={card._id.toHexString()}
actionIcon={<CircleDot color={theme.colors.tertiary} size={24}/>}
onPress={() => setSelectedCard(card)}/>))
: <View style={styles.emptyPlaceholderView}>
<EmptyStatePlaceholder message="No additional cards"
icon={<SmartphoneNfcIcon color="gray"/>}/>
</View>}
</View>)}
</ScrollView> </ScrollView>
) )
} }
@@ -245,6 +278,20 @@ function StatusAlert(props: {
return null; return null;
} }
function DefaultCardDetail() {
return (
<>
{/* spacer */}
<View></View>
<AMUICardDetail>
<Star size={18} color="goldenrod"/>
<Text style={{color: "goldenrod"}}>Previously used with this game</Text>
</AMUICardDetail>
</>
)
}
const styles = StyleSheet.create({ const styles = StyleSheet.create({
section: { section: {
gap: 10 gap: 10
@@ -257,6 +304,7 @@ const styles = StyleSheet.create({
gap: 15 gap: 15
}, },
container: { container: {
paddingTop: Platform.OS === 'android' ? 120 : 0,
paddingHorizontal: 16, paddingHorizontal: 16,
paddingBottom: 80, paddingBottom: 80,
gap: 20 gap: 20
+11 -3
View File
@@ -1,7 +1,7 @@
import {Button, Paragraph, useTheme} from "react-native-paper"; import {Button, Paragraph, useTheme} from "react-native-paper";
import {FlatList, NativeSyntheticEvent, Share, View} from "react-native";
import React, {useCallback, useLayoutEffect, useMemo, useState} from "react"; import React, {useCallback, useLayoutEffect, useMemo, useState} from "react";
import {Globe, History, Joystick, Plus, ServerOff} from "lucide-react-native"; import {Globe, History, Joystick, Plus, ServerOff} from "lucide-react-native";
import {FlatList, NativeSyntheticEvent, Platform, ScrollView, Share, View} from "react-native";
import ContextMenu, {ContextMenuOnPressNativeEvent} from "react-native-context-menu-view"; import ContextMenu, {ContextMenuOnPressNativeEvent} from "react-native-context-menu-view";
import {EmptyStatePlaceholder} from "../components/EmptyStatePlaceholder"; import {EmptyStatePlaceholder} from "../components/EmptyStatePlaceholder";
import {AMUICard, AMUICardDetail} from "../components/AMUICard"; import {AMUICard, AMUICardDetail} from "../components/AMUICard";
@@ -43,7 +43,7 @@ function ServerCard(props: { server: AMServer, navigation: any }) {
<ContextMenu onPress={pageContextMenuCallback} actions={[ <ContextMenu onPress={pageContextMenuCallback} actions={[
{title: `Select ${props.server.serverName}`, systemIcon: "chevron.right"}, {title: `Select ${props.server.serverName}`, systemIcon: "chevron.right"},
{ {
title: "", title: Platform.OS === "ios" ? "" : "Additional Actions",
inlineChildren: true, inlineChildren: true,
actions: [ actions: [
{title: "Share Server", systemIcon: "square.and.arrow.up"}, {title: "Share Server", systemIcon: "square.and.arrow.up"},
@@ -98,6 +98,8 @@ export default function ServerManager({navigation}) {
value: searchQuery, value: searchQuery,
autoCorrect: "none", autoCorrect: "none",
autoCapitalize: "none", autoCapitalize: "none",
textColor: theme.colors.onSurface,
headerIconColor: theme.colors.primary,
onChangeText: e => setSearchQuery(e.nativeEvent.text) onChangeText: e => setSearchQuery(e.nativeEvent.text)
}, },
}) })
@@ -121,7 +123,13 @@ export default function ServerManager({navigation}) {
keyExtractor={i => i._id.toString()} keyExtractor={i => i._id.toString()}
style={{padding: 16, paddingTop: 24}} style={{padding: 16, paddingTop: 24}}
contentContainerStyle={{paddingBottom: 80}} contentContainerStyle={{paddingBottom: 80}}
renderItem={i => <ServerCard navigation={navigation} server={i.item}/>}
ItemSeparatorComponent={() => <View style={{height: 15}}/>} ItemSeparatorComponent={() => <View style={{height: 15}}/>}
renderItem={i => <ServerCard navigation={navigation} server={i.item}/>}/> ListFooterComponent={() => Platform.OS === "ios" ? null : (
<Button mode="text"
onPress={() => navigation.navigate('add-server')}
style={{marginTop: 15, marginBottom: 25}}
icon={() => <Plus size={18} color={theme.colors.primary}/>}>Add Server</Button>
)}/>
) )
} }
Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

+3 -1
View File
@@ -1,4 +1,4 @@
import {useMemo} from "react"; import {ReactNode, useMemo} from "react";
import {Text} from "react-native"; import {Text} from "react-native";
import {CreditCard, History} from "lucide-react-native"; import {CreditCard, History} from "lucide-react-native";
@@ -8,6 +8,7 @@ import {AMUICard, AMUICardActionProps, AMUICardDetail} from "./AMUICard";
export interface AimeCardProps extends AMUICardActionProps { export interface AimeCardProps extends AMUICardActionProps {
card: AMCardData, card: AMCardData,
children?: ReactNode,
showFullCardNumber?: boolean showFullCardNumber?: boolean
} }
@@ -29,6 +30,7 @@ export function AimeCard(props: AimeCardProps) {
<TimeAgo style={{color: "gray"}} dateTo={props.card.lastUsed}/> <TimeAgo style={{color: "gray"}} dateTo={props.card.lastUsed}/>
</AMUICardDetail> </AMUICardDetail>
)} )}
{props.children}
</AMUICard> </AMUICard>
); );
} }
+3 -1
View File
@@ -16,6 +16,7 @@ export class AMCard extends Realm.Object<AMCard> implements AMCardData {
added: Date; added: Date;
lastUsed?: Date; lastUsed?: Date;
lastUsedGameId?: string | null;
static generate(code: string, name: string) { static generate(code: string, name: string) {
return { return {
@@ -35,7 +36,8 @@ export class AMCard extends Realm.Object<AMCard> implements AMCardData {
code: "string", code: "string",
name: {type: "string", default: "New Card"}, name: {type: "string", default: "New Card"},
added: {type: "date", default: new Date()}, added: {type: "date", default: new Date()},
lastUsed: {type: "date", optional: true} lastUsed: {type: "date", optional: true},
lastUsedGameId: {type: "string", optional: true}
} }
}; };
} }
+1 -1
View File
@@ -4,7 +4,7 @@ import {AMServer} from "./AMServer";
// @ts-ignore // @ts-ignore
export default createRealmContext({ export default createRealmContext({
schemaVersion: 2, schemaVersion: 3,
schema: [AMCard, AMServer], schema: [AMCard, AMServer],
deleteRealmIfMigrationNeeded: false deleteRealmIfMigrationNeeded: false
}); });
+2169 -1467
View File
File diff suppressed because it is too large Load Diff
+7 -6
View File
@@ -17,26 +17,27 @@
"@types/react": "~18.2.79", "@types/react": "~18.2.79",
"amnet-shared": "file:../amnet-shared", "amnet-shared": "file:../amnet-shared",
"date-fns": "^3.6.0", "date-fns": "^3.6.0",
"expo": "^51.0.0", "expo": "^51.0.38",
"expo-blur": "~13.0.2", "expo-blur": "~13.0.2",
"expo-build-properties": "~0.12.5",
"expo-clipboard": "~6.0.3", "expo-clipboard": "~6.0.3",
"expo-constants": "~16.0.2", "expo-constants": "~16.0.2",
"expo-dev-client": "~4.0.10", "expo-dev-client": "~4.0.29",
"expo-linking": "~6.3.1", "expo-linking": "~6.3.1",
"expo-status-bar": "~1.12.1", "expo-status-bar": "~1.12.1",
"expo-system-ui": "~3.0.7", "expo-system-ui": "~3.0.7",
"lucide-react-native": "^0.427.0", "lucide-react-native": "^0.427.0",
"patch-package": "^8.0.0", "patch-package": "^8.0.0",
"react": "18.2.0", "react": "18.2.0",
"react-native": "0.74.1", "react-native": "0.74.5",
"react-native-context-menu-view": "^1.16.0", "react-native-context-menu-view": "^1.16.0",
"react-native-gesture-handler": "^2.18.1", "react-native-gesture-handler": "~2.16.1",
"react-native-get-random-values": "^1.11.0", "react-native-get-random-values": "^1.11.0",
"react-native-paper": "^5.12.5", "react-native-paper": "^5.12.5",
"react-native-reanimated": "^3.15.0", "react-native-reanimated": "~3.10.1",
"react-native-safe-area-context": "4.10.5", "react-native-safe-area-context": "4.10.5",
"react-native-screens": "3.31.1", "react-native-screens": "3.31.1",
"react-native-svg": "^15.5.0", "react-native-svg": "15.2.0",
"react-native-url-polyfill": "^2.0.0", "react-native-url-polyfill": "^2.0.0",
"realm": "^12.12.1", "realm": "^12.12.1",
"typescript": "~5.3.3" "typescript": "~5.3.3"
+70 -70
View File
@@ -541,9 +541,9 @@
} }
}, },
"node_modules/@rollup/rollup-android-arm-eabi": { "node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.21.0", "version": "4.22.4",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz",
"integrity": "sha512-WTWD8PfoSAJ+qL87lE7votj3syLavxunWhzCnx3XFxFiI/BA/r3X7MUM8dVrH8rb2r4AiO8jJsr3ZjdaftmnfA==", "integrity": "sha512-Fxamp4aEZnfPOcGA8KSNEohV8hX7zVHOemC8jVBoBUHu5zpJK/Eu3uJwt6BMgy9fkvzxDaurgj96F/NiLukF2w==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@@ -555,9 +555,9 @@
] ]
}, },
"node_modules/@rollup/rollup-android-arm64": { "node_modules/@rollup/rollup-android-arm64": {
"version": "4.21.0", "version": "4.22.4",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.4.tgz",
"integrity": "sha512-a1sR2zSK1B4eYkiZu17ZUZhmUQcKjk2/j9Me2IDjk1GHW7LB5Z35LEzj9iJch6gtUfsnvZs1ZNyDW2oZSThrkA==", "integrity": "sha512-VXoK5UMrgECLYaMuGuVTOx5kcuap1Jm8g/M83RnCHBKOqvPPmROFJGQaZhGccnsFtfXQ3XYa4/jMCJvZnbJBdA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -569,9 +569,9 @@
] ]
}, },
"node_modules/@rollup/rollup-darwin-arm64": { "node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.21.0", "version": "4.22.4",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.4.tgz",
"integrity": "sha512-zOnKWLgDld/svhKO5PD9ozmL6roy5OQ5T4ThvdYZLpiOhEGY+dp2NwUmxK0Ld91LrbjrvtNAE0ERBwjqhZTRAA==", "integrity": "sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -583,9 +583,9 @@
] ]
}, },
"node_modules/@rollup/rollup-darwin-x64": { "node_modules/@rollup/rollup-darwin-x64": {
"version": "4.21.0", "version": "4.22.4",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.4.tgz",
"integrity": "sha512-7doS8br0xAkg48SKE2QNtMSFPFUlRdw9+votl27MvT46vo44ATBmdZdGysOevNELmZlfd+NEa0UYOA8f01WSrg==", "integrity": "sha512-aJJyYKQwbHuhTUrjWjxEvGnNNBCnmpHDvrb8JFDbeSH3m2XdHcxDd3jthAzvmoI8w/kSjd2y0udT+4okADsZIw==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -597,9 +597,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-arm-gnueabihf": { "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.21.0", "version": "4.22.4",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.4.tgz",
"integrity": "sha512-pWJsfQjNWNGsoCq53KjMtwdJDmh/6NubwQcz52aEwLEuvx08bzcy6tOUuawAOncPnxz/3siRtd8hiQ32G1y8VA==", "integrity": "sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@@ -611,9 +611,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-arm-musleabihf": { "node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.21.0", "version": "4.22.4",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.4.tgz",
"integrity": "sha512-efRIANsz3UHZrnZXuEvxS9LoCOWMGD1rweciD6uJQIx2myN3a8Im1FafZBzh7zk1RJ6oKcR16dU3UPldaKd83w==", "integrity": "sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@@ -625,9 +625,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-arm64-gnu": { "node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.21.0", "version": "4.22.4",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.4.tgz",
"integrity": "sha512-ZrPhydkTVhyeGTW94WJ8pnl1uroqVHM3j3hjdquwAcWnmivjAwOYjTEAuEDeJvGX7xv3Z9GAvrBkEzCgHq9U1w==", "integrity": "sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -639,9 +639,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-arm64-musl": { "node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.21.0", "version": "4.22.4",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.4.tgz",
"integrity": "sha512-cfaupqd+UEFeURmqNP2eEvXqgbSox/LHOyN9/d2pSdV8xTrjdg3NgOFJCtc1vQ/jEke1qD0IejbBfxleBPHnPw==", "integrity": "sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -653,9 +653,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": { "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
"version": "4.21.0", "version": "4.22.4",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.4.tgz",
"integrity": "sha512-ZKPan1/RvAhrUylwBXC9t7B2hXdpb/ufeu22pG2psV7RN8roOfGurEghw1ySmX/CmDDHNTDDjY3lo9hRlgtaHg==", "integrity": "sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg==",
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
@@ -667,9 +667,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-riscv64-gnu": { "node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.21.0", "version": "4.22.4",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.4.tgz",
"integrity": "sha512-H1eRaCwd5E8eS8leiS+o/NqMdljkcb1d6r2h4fKSsCXQilLKArq6WS7XBLDu80Yz+nMqHVFDquwcVrQmGr28rg==", "integrity": "sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA==",
"cpu": [ "cpu": [
"riscv64" "riscv64"
], ],
@@ -681,9 +681,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-s390x-gnu": { "node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.21.0", "version": "4.22.4",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.4.tgz",
"integrity": "sha512-zJ4hA+3b5tu8u7L58CCSI0A9N1vkfwPhWd/puGXwtZlsB5bTkwDNW/+JCU84+3QYmKpLi+XvHdmrlwUwDA6kqw==", "integrity": "sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q==",
"cpu": [ "cpu": [
"s390x" "s390x"
], ],
@@ -695,9 +695,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-x64-gnu": { "node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.21.0", "version": "4.22.4",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.4.tgz",
"integrity": "sha512-e2hrvElFIh6kW/UNBQK/kzqMNY5mO+67YtEh9OA65RM5IJXYTWiXjX6fjIiPaqOkBthYF1EqgiZ6OXKcQsM0hg==", "integrity": "sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -709,9 +709,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-x64-musl": { "node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.21.0", "version": "4.22.4",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.22.4.tgz",
"integrity": "sha512-1vvmgDdUSebVGXWX2lIcgRebqfQSff0hMEkLJyakQ9JQUbLDkEaMsPTLOmyccyC6IJ/l3FZuJbmrBw/u0A0uCQ==", "integrity": "sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -723,9 +723,9 @@
] ]
}, },
"node_modules/@rollup/rollup-win32-arm64-msvc": { "node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.21.0", "version": "4.22.4",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.4.tgz",
"integrity": "sha512-s5oFkZ/hFcrlAyBTONFY1TWndfyre1wOMwU+6KCpm/iatybvrRgmZVM+vCFwxmC5ZhdlgfE0N4XorsDpi7/4XQ==", "integrity": "sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -737,9 +737,9 @@
] ]
}, },
"node_modules/@rollup/rollup-win32-ia32-msvc": { "node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.21.0", "version": "4.22.4",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.4.tgz",
"integrity": "sha512-G9+TEqRnAA6nbpqyUqgTiopmnfgnMkR3kMukFBDsiyy23LZvUCpiUwjTRx6ezYCjJODXrh52rBR9oXvm+Fp5wg==", "integrity": "sha512-SiWG/1TuUdPvYmzmYnmd3IEifzR61Tragkbx9D3+R8mzQqDBz8v+BvZNDlkiTtI9T15KYZhP0ehn3Dld4n9J5g==",
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
@@ -751,9 +751,9 @@
] ]
}, },
"node_modules/@rollup/rollup-win32-x64-msvc": { "node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.21.0", "version": "4.22.4",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.4.tgz",
"integrity": "sha512-2jsCDZwtQvRhejHLfZ1JY6w6kEuEtfF9nzYsZxzSlNVKDX+DpsDJ+Rbjkm74nvg2rdx0gwBS+IMdvwJuq3S9pQ==", "integrity": "sha512-j8pPKp53/lq9lMXN57S8cFz0MynJk8OWNuUnXct/9KCpKU7DgU3bYMJhwWmcqC0UU29p8Lr0/7KEVcaM6bf47Q==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -1423,9 +1423,9 @@
} }
}, },
"node_modules/micromatch": { "node_modules/micromatch": {
"version": "4.0.7", "version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
"integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -1723,9 +1723,9 @@
} }
}, },
"node_modules/rollup": { "node_modules/rollup": {
"version": "4.21.0", "version": "4.22.4",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.0.tgz", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.22.4.tgz",
"integrity": "sha512-vo+S/lfA2lMS7rZ2Qoubi6I5hwZwzXeUIctILZLbHI+laNtvhhOIon2S1JksA5UEDQ7l3vberd0fxK44lTYjbQ==", "integrity": "sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -1739,22 +1739,22 @@
"npm": ">=8.0.0" "npm": ">=8.0.0"
}, },
"optionalDependencies": { "optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.21.0", "@rollup/rollup-android-arm-eabi": "4.22.4",
"@rollup/rollup-android-arm64": "4.21.0", "@rollup/rollup-android-arm64": "4.22.4",
"@rollup/rollup-darwin-arm64": "4.21.0", "@rollup/rollup-darwin-arm64": "4.22.4",
"@rollup/rollup-darwin-x64": "4.21.0", "@rollup/rollup-darwin-x64": "4.22.4",
"@rollup/rollup-linux-arm-gnueabihf": "4.21.0", "@rollup/rollup-linux-arm-gnueabihf": "4.22.4",
"@rollup/rollup-linux-arm-musleabihf": "4.21.0", "@rollup/rollup-linux-arm-musleabihf": "4.22.4",
"@rollup/rollup-linux-arm64-gnu": "4.21.0", "@rollup/rollup-linux-arm64-gnu": "4.22.4",
"@rollup/rollup-linux-arm64-musl": "4.21.0", "@rollup/rollup-linux-arm64-musl": "4.22.4",
"@rollup/rollup-linux-powerpc64le-gnu": "4.21.0", "@rollup/rollup-linux-powerpc64le-gnu": "4.22.4",
"@rollup/rollup-linux-riscv64-gnu": "4.21.0", "@rollup/rollup-linux-riscv64-gnu": "4.22.4",
"@rollup/rollup-linux-s390x-gnu": "4.21.0", "@rollup/rollup-linux-s390x-gnu": "4.22.4",
"@rollup/rollup-linux-x64-gnu": "4.21.0", "@rollup/rollup-linux-x64-gnu": "4.22.4",
"@rollup/rollup-linux-x64-musl": "4.21.0", "@rollup/rollup-linux-x64-musl": "4.22.4",
"@rollup/rollup-win32-arm64-msvc": "4.21.0", "@rollup/rollup-win32-arm64-msvc": "4.22.4",
"@rollup/rollup-win32-ia32-msvc": "4.21.0", "@rollup/rollup-win32-ia32-msvc": "4.22.4",
"@rollup/rollup-win32-x64-msvc": "4.21.0", "@rollup/rollup-win32-x64-msvc": "4.22.4",
"fsevents": "~2.3.2" "fsevents": "~2.3.2"
} }
}, },
+99 -87
View File
@@ -20,6 +20,7 @@
"amnet-shared": "file:../amnet-shared", "amnet-shared": "file:../amnet-shared",
"class-variance-authority": "^0.7.0", "class-variance-authority": "^0.7.0",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"core-js": "^3.39.0",
"detectincognitojs": "^1.3.5", "detectincognitojs": "^1.3.5",
"dexie": "^4.0.8", "dexie": "^4.0.8",
"dexie-react-hooks": "^1.1.7", "dexie-react-hooks": "^1.1.7",
@@ -50,7 +51,7 @@
"tailwindcss": "^3.4.9", "tailwindcss": "^3.4.9",
"typescript": "^5.5.3", "typescript": "^5.5.3",
"typescript-eslint": "^8.0.0", "typescript-eslint": "^8.0.0",
"vite": "^5.4.0" "vite": "^5.4.6"
} }
}, },
"../amnet-native": { "../amnet-native": {
@@ -1547,9 +1548,9 @@
} }
}, },
"node_modules/@rollup/rollup-android-arm-eabi": { "node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.20.0", "version": "4.22.4",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.20.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz",
"integrity": "sha512-TSpWzflCc4VGAUJZlPpgAJE1+V60MePDQnBd7PPkpuEmOy8i87aL6tinFGKBFKuEDikYpig72QzdT3QPYIi+oA==", "integrity": "sha512-Fxamp4aEZnfPOcGA8KSNEohV8hX7zVHOemC8jVBoBUHu5zpJK/Eu3uJwt6BMgy9fkvzxDaurgj96F/NiLukF2w==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@@ -1561,9 +1562,9 @@
] ]
}, },
"node_modules/@rollup/rollup-android-arm64": { "node_modules/@rollup/rollup-android-arm64": {
"version": "4.20.0", "version": "4.22.4",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.20.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.4.tgz",
"integrity": "sha512-u00Ro/nok7oGzVuh/FMYfNoGqxU5CPWz1mxV85S2w9LxHR8OoMQBuSk+3BKVIDYgkpeOET5yXkx90OYFc+ytpQ==", "integrity": "sha512-VXoK5UMrgECLYaMuGuVTOx5kcuap1Jm8g/M83RnCHBKOqvPPmROFJGQaZhGccnsFtfXQ3XYa4/jMCJvZnbJBdA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1575,9 +1576,9 @@
] ]
}, },
"node_modules/@rollup/rollup-darwin-arm64": { "node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.20.0", "version": "4.22.4",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.20.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.4.tgz",
"integrity": "sha512-uFVfvzvsdGtlSLuL0ZlvPJvl6ZmrH4CBwLGEFPe7hUmf7htGAN+aXo43R/V6LATyxlKVC/m6UsLb7jbG+LG39Q==", "integrity": "sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1589,9 +1590,9 @@
] ]
}, },
"node_modules/@rollup/rollup-darwin-x64": { "node_modules/@rollup/rollup-darwin-x64": {
"version": "4.20.0", "version": "4.22.4",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.20.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.4.tgz",
"integrity": "sha512-xbrMDdlev53vNXexEa6l0LffojxhqDTBeL+VUxuuIXys4x6xyvbKq5XqTXBCEUA8ty8iEJblHvFaWRJTk/icAQ==", "integrity": "sha512-aJJyYKQwbHuhTUrjWjxEvGnNNBCnmpHDvrb8JFDbeSH3m2XdHcxDd3jthAzvmoI8w/kSjd2y0udT+4okADsZIw==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -1603,9 +1604,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-arm-gnueabihf": { "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.20.0", "version": "4.22.4",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.20.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.4.tgz",
"integrity": "sha512-jMYvxZwGmoHFBTbr12Xc6wOdc2xA5tF5F2q6t7Rcfab68TT0n+r7dgawD4qhPEvasDsVpQi+MgDzj2faOLsZjA==", "integrity": "sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@@ -1617,9 +1618,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-arm-musleabihf": { "node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.20.0", "version": "4.22.4",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.20.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.4.tgz",
"integrity": "sha512-1asSTl4HKuIHIB1GcdFHNNZhxAYEdqML/MW4QmPS4G0ivbEcBr1JKlFLKsIRqjSwOBkdItn3/ZDlyvZ/N6KPlw==", "integrity": "sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@@ -1631,9 +1632,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-arm64-gnu": { "node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.20.0", "version": "4.22.4",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.20.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.4.tgz",
"integrity": "sha512-COBb8Bkx56KldOYJfMf6wKeYJrtJ9vEgBRAOkfw6Ens0tnmzPqvlpjZiLgkhg6cA3DGzCmLmmd319pmHvKWWlQ==", "integrity": "sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1645,9 +1646,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-arm64-musl": { "node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.20.0", "version": "4.22.4",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.20.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.4.tgz",
"integrity": "sha512-+it+mBSyMslVQa8wSPvBx53fYuZK/oLTu5RJoXogjk6x7Q7sz1GNRsXWjn6SwyJm8E/oMjNVwPhmNdIjwP135Q==", "integrity": "sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1659,9 +1660,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": { "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
"version": "4.20.0", "version": "4.22.4",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.20.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.4.tgz",
"integrity": "sha512-yAMvqhPfGKsAxHN8I4+jE0CpLWD8cv4z7CK7BMmhjDuz606Q2tFKkWRY8bHR9JQXYcoLfopo5TTqzxgPUjUMfw==", "integrity": "sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg==",
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
@@ -1673,9 +1674,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-riscv64-gnu": { "node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.20.0", "version": "4.22.4",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.20.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.4.tgz",
"integrity": "sha512-qmuxFpfmi/2SUkAw95TtNq/w/I7Gpjurx609OOOV7U4vhvUhBcftcmXwl3rqAek+ADBwSjIC4IVNLiszoj3dPA==", "integrity": "sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA==",
"cpu": [ "cpu": [
"riscv64" "riscv64"
], ],
@@ -1687,9 +1688,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-s390x-gnu": { "node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.20.0", "version": "4.22.4",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.20.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.4.tgz",
"integrity": "sha512-I0BtGXddHSHjV1mqTNkgUZLnS3WtsqebAXv11D5BZE/gfw5KoyXSAXVqyJximQXNvNzUo4GKlCK/dIwXlz+jlg==", "integrity": "sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q==",
"cpu": [ "cpu": [
"s390x" "s390x"
], ],
@@ -1701,9 +1702,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-x64-gnu": { "node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.20.0", "version": "4.22.4",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.20.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.4.tgz",
"integrity": "sha512-y+eoL2I3iphUg9tN9GB6ku1FA8kOfmF4oUEWhztDJ4KXJy1agk/9+pejOuZkNFhRwHAOxMsBPLbXPd6mJiCwew==", "integrity": "sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -1715,9 +1716,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-x64-musl": { "node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.20.0", "version": "4.22.4",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.20.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.22.4.tgz",
"integrity": "sha512-hM3nhW40kBNYUkZb/r9k2FKK+/MnKglX7UYd4ZUy5DJs8/sMsIbqWK2piZtVGE3kcXVNj3B2IrUYROJMMCikNg==", "integrity": "sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -1729,9 +1730,9 @@
] ]
}, },
"node_modules/@rollup/rollup-win32-arm64-msvc": { "node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.20.0", "version": "4.22.4",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.20.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.4.tgz",
"integrity": "sha512-psegMvP+Ik/Bg7QRJbv8w8PAytPA7Uo8fpFjXyCRHWm6Nt42L+JtoqH8eDQ5hRP7/XW2UiIriy1Z46jf0Oa1kA==", "integrity": "sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1743,9 +1744,9 @@
] ]
}, },
"node_modules/@rollup/rollup-win32-ia32-msvc": { "node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.20.0", "version": "4.22.4",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.20.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.4.tgz",
"integrity": "sha512-GabekH3w4lgAJpVxkk7hUzUf2hICSQO0a/BLFA11/RMxQT92MabKAqyubzDZmMOC/hcJNlc+rrypzNzYl4Dx7A==", "integrity": "sha512-SiWG/1TuUdPvYmzmYnmd3IEifzR61Tragkbx9D3+R8mzQqDBz8v+BvZNDlkiTtI9T15KYZhP0ehn3Dld4n9J5g==",
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
@@ -1757,9 +1758,9 @@
] ]
}, },
"node_modules/@rollup/rollup-win32-x64-msvc": { "node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.20.0", "version": "4.22.4",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.20.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.4.tgz",
"integrity": "sha512-aJ1EJSuTdGnM6qbVC4B5DSmozPTqIag9fSzXRNNo+humQLG89XpPgdt16Ia56ORD7s+H8Pmyx44uczDQ0yDzpg==", "integrity": "sha512-j8pPKp53/lq9lMXN57S8cFz0MynJk8OWNuUnXct/9KCpKU7DgU3bYMJhwWmcqC0UU29p8Lr0/7KEVcaM6bf47Q==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -2682,6 +2683,17 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/core-js": {
"version": "3.39.0",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.39.0.tgz",
"integrity": "sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g==",
"hasInstallScript": true,
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/core-js"
}
},
"node_modules/cross-spawn": { "node_modules/cross-spawn": {
"version": "7.0.3", "version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -3644,9 +3656,9 @@
} }
}, },
"node_modules/micromatch": { "node_modules/micromatch": {
"version": "4.0.7", "version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
"integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"braces": "^3.0.3", "braces": "^3.0.3",
@@ -3886,9 +3898,9 @@
} }
}, },
"node_modules/picocolors": { "node_modules/picocolors": {
"version": "1.0.1", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz",
"integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==",
"license": "ISC" "license": "ISC"
}, },
"node_modules/picomatch": { "node_modules/picomatch": {
@@ -3922,9 +3934,9 @@
} }
}, },
"node_modules/postcss": { "node_modules/postcss": {
"version": "8.4.41", "version": "8.4.47",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz",
"integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==", "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==",
"funding": [ "funding": [
{ {
"type": "opencollective", "type": "opencollective",
@@ -3942,8 +3954,8 @@
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"nanoid": "^3.3.7", "nanoid": "^3.3.7",
"picocolors": "^1.0.1", "picocolors": "^1.1.0",
"source-map-js": "^1.2.0" "source-map-js": "^1.2.1"
}, },
"engines": { "engines": {
"node": "^10 || ^12 || >=14" "node": "^10 || ^12 || >=14"
@@ -4327,9 +4339,9 @@
} }
}, },
"node_modules/rollup": { "node_modules/rollup": {
"version": "4.20.0", "version": "4.22.4",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.20.0.tgz", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.22.4.tgz",
"integrity": "sha512-6rbWBChcnSGzIlXeIdNIZTopKYad8ZG8ajhl78lGRLsI2rX8IkaotQhVas2Ma+GPxJav19wrSzvRvuiv0YKzWw==", "integrity": "sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -4343,22 +4355,22 @@
"npm": ">=8.0.0" "npm": ">=8.0.0"
}, },
"optionalDependencies": { "optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.20.0", "@rollup/rollup-android-arm-eabi": "4.22.4",
"@rollup/rollup-android-arm64": "4.20.0", "@rollup/rollup-android-arm64": "4.22.4",
"@rollup/rollup-darwin-arm64": "4.20.0", "@rollup/rollup-darwin-arm64": "4.22.4",
"@rollup/rollup-darwin-x64": "4.20.0", "@rollup/rollup-darwin-x64": "4.22.4",
"@rollup/rollup-linux-arm-gnueabihf": "4.20.0", "@rollup/rollup-linux-arm-gnueabihf": "4.22.4",
"@rollup/rollup-linux-arm-musleabihf": "4.20.0", "@rollup/rollup-linux-arm-musleabihf": "4.22.4",
"@rollup/rollup-linux-arm64-gnu": "4.20.0", "@rollup/rollup-linux-arm64-gnu": "4.22.4",
"@rollup/rollup-linux-arm64-musl": "4.20.0", "@rollup/rollup-linux-arm64-musl": "4.22.4",
"@rollup/rollup-linux-powerpc64le-gnu": "4.20.0", "@rollup/rollup-linux-powerpc64le-gnu": "4.22.4",
"@rollup/rollup-linux-riscv64-gnu": "4.20.0", "@rollup/rollup-linux-riscv64-gnu": "4.22.4",
"@rollup/rollup-linux-s390x-gnu": "4.20.0", "@rollup/rollup-linux-s390x-gnu": "4.22.4",
"@rollup/rollup-linux-x64-gnu": "4.20.0", "@rollup/rollup-linux-x64-gnu": "4.22.4",
"@rollup/rollup-linux-x64-musl": "4.20.0", "@rollup/rollup-linux-x64-musl": "4.22.4",
"@rollup/rollup-win32-arm64-msvc": "4.20.0", "@rollup/rollup-win32-arm64-msvc": "4.22.4",
"@rollup/rollup-win32-ia32-msvc": "4.20.0", "@rollup/rollup-win32-ia32-msvc": "4.22.4",
"@rollup/rollup-win32-x64-msvc": "4.20.0", "@rollup/rollup-win32-x64-msvc": "4.22.4",
"fsevents": "~2.3.2" "fsevents": "~2.3.2"
} }
}, },
@@ -4451,9 +4463,9 @@
} }
}, },
"node_modules/source-map-js": { "node_modules/source-map-js": {
"version": "1.2.0", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
@@ -4892,15 +4904,15 @@
} }
}, },
"node_modules/vite": { "node_modules/vite": {
"version": "5.4.0", "version": "5.4.7",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.0.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.7.tgz",
"integrity": "sha512-5xokfMX0PIiwCMCMb9ZJcMyh5wbBun0zUzKib+L65vAZ8GY9ePZMXxFrHbr/Kyll2+LSCY7xtERPpxkBDKngwg==", "integrity": "sha512-5l2zxqMEPVENgvzTuBpHer2awaetimj2BGkhBPdnwKbPNOlHsODU+oiazEZzLK7KhAnOrO+XGYJYn4ZlUhDtDQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"esbuild": "^0.21.3", "esbuild": "^0.21.3",
"postcss": "^8.4.40", "postcss": "^8.4.43",
"rollup": "^4.13.0" "rollup": "^4.20.0"
}, },
"bin": { "bin": {
"vite": "bin/vite.js" "vite": "bin/vite.js"
+2 -1
View File
@@ -22,6 +22,7 @@
"amnet-shared": "file:../amnet-shared", "amnet-shared": "file:../amnet-shared",
"class-variance-authority": "^0.7.0", "class-variance-authority": "^0.7.0",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"core-js": "^3.39.0",
"detectincognitojs": "^1.3.5", "detectincognitojs": "^1.3.5",
"dexie": "^4.0.8", "dexie": "^4.0.8",
"dexie-react-hooks": "^1.1.7", "dexie-react-hooks": "^1.1.7",
@@ -52,6 +53,6 @@
"tailwindcss": "^3.4.9", "tailwindcss": "^3.4.9",
"typescript": "^5.5.3", "typescript": "^5.5.3",
"typescript-eslint": "^8.0.0", "typescript-eslint": "^8.0.0",
"vite": "^5.4.0" "vite": "^5.4.6"
} }
} }
@@ -0,0 +1,10 @@
[
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "moe.ppc.amnet",
"sha256_cert_fingerprints": ["9E:4C:42:6F:9E:27:85:62:E5:DB:75:23:60:4A:B9:8D:B5:39:C8:1D:AD:FE:8E:D3:DC:BA:6E:55:A9:C6:59:05"]
}
}
]
+21 -9
View File
@@ -1,19 +1,31 @@
import * as React from "react" import * as React from "react";
export function useMediaQuery(query: string) { export function useMediaQuery(query: string) {
const [value, setValue] = React.useState(false) const [value, setValue] = React.useState(false);
React.useEffect(() => { React.useEffect(() => {
function onChange(event: MediaQueryListEvent) { function onChange(event: MediaQueryListEvent) {
setValue(event.matches) setValue(event.matches);
} }
const result = matchMedia(query) const result = matchMedia(query);
result.addEventListener("change", onChange)
setValue(result.matches)
return () => result.removeEventListener("change", onChange) // addEventListener isn't available on older versions of safari
}, [query]) if (result.addEventListener) {
result.addEventListener("change", onChange);
} else {
result.addListener(onChange);
}
setValue(result.matches);
return value return () => {
if (result.removeEventListener) {
result.removeEventListener("change", onChange);
} else {
result.removeListener(onChange);
}
};
}, [query]);
return value;
} }
+11 -1
View File
@@ -1,5 +1,10 @@
import './index.css' import './index.css'
// import polyfills from core-js for older browsers
import 'core-js/actual/promise';
import 'core-js/actual/symbol';
import 'core-js/actual/object/assign';
import {StrictMode} from "react"; import {StrictMode} from "react";
import {createRoot} from "react-dom/client"; import {createRoot} from "react-dom/client";
import {createHashRouter, Navigate, RouterProvider} from "react-router-dom"; import {createHashRouter, Navigate, RouterProvider} from "react-router-dom";
@@ -10,6 +15,7 @@ import {ThemeProvider} from "@/components/theme-provider.tsx";
import CardManager from "@/pages/card-manager.tsx"; import CardManager from "@/pages/card-manager.tsx";
import ServerCardIn from "@/pages/card-in-screen.tsx"; import ServerCardIn from "@/pages/card-in-screen.tsx";
import ServerListing from "@/pages/server-listing.tsx"; import ServerListing from "@/pages/server-listing.tsx";
import Privacy from "@/pages/privacy.tsx";
// iOS PWAs show the navigation bar when changing routes // iOS PWAs show the navigation bar when changing routes
// HashRouter doesn't have this issue as the URL never changes // HashRouter doesn't have this issue as the URL never changes
@@ -33,7 +39,11 @@ const router = createHashRouter([
path: x, path: x,
element: <CardManager/> element: <CardManager/>
}; };
}) }),
{
path: "/privacy",
element: <Privacy/>
}
]) ])
createRoot(document.getElementById('root')!).render( createRoot(document.getElementById('root')!).render(
+39
View File
@@ -0,0 +1,39 @@
// Google Play forces a Privacy Policy even for closed tests...
import {Server} from "lucide-react";
import {useNavigate} from "react-router-dom";
import {Button} from "@/components/ui/button.tsx";
import {AMFooter} from "@/components/am-footer.tsx";
import {AMHeader} from "@/components/am-header.tsx";
function PrivacyPolicy() {
const navigate = useNavigate();
return (
<main className="flex flex-col gap-5 container min-h-screen py-10">
<AMHeader title="AMNet">
<Button variant="ghost" size={"default"} onClick={() => navigate('/')}>
<Server className="h-4 w-4 sm:mr-2"/>
<span className="hidden sm:block">Manage Servers</span>
</Button>
</AMHeader>
<div className="flex flex-col gap-4 w-full flex-grow">
<h4 className="font-semibold text-2xl">Privacy</h4>
<p>The AMNet card switcher collects and stores the following information locally (on-device):</p>
<ul className="list-disc">
<li>Server Addresses</li>
<li>Server Information (including machine name, identifiers and metrics that are cached and updated on each load)</li>
<li>Card Names and Access Codes</li>
</ul>
<p>The card switcher does <strong>NOT</strong> send any data to external servers or services, except to perform the card-in action where only the card number is transmitted.</p>
</div>
<AMFooter/>
</main>
)
}
export default PrivacyPolicy;
+1 -1
View File
@@ -7,7 +7,7 @@
"DOM", "DOM",
"DOM.Iterable" "DOM.Iterable"
], ],
"module": "ESNext", "module": "ES2020",
"skipLibCheck": true, "skipLibCheck": true,
"baseUrl": ".", "baseUrl": ".",
"paths": { "paths": {
+5
View File
@@ -11,6 +11,7 @@ export default defineConfig({
}, },
}, },
build: { build: {
target: "ES2018",
rollupOptions: { rollupOptions: {
output: { output: {
manualChunks(id: string) { manualChunks(id: string) {
@@ -21,6 +22,10 @@ export default defineConfig({
if (id.includes('react')) { if (id.includes('react')) {
return 'react'; return 'react';
} }
if (id.includes('core-js')) {
return "polyfills";
}
} }
} }
} }
+29
View File
@@ -0,0 +1,29 @@
diff --git a/amnet-native/android/app/build.gradle b/amnet-native/android/app/build.gradle
index 6ee4a34..eb8c5f0 100644
--- a/amnet-native/android/app/build.gradle
+++ b/amnet-native/android/app/build.gradle
@@ -124,15 +124,21 @@ android {
keyAlias 'androiddebugkey'
keyPassword 'android'
}
+ release {
+ if (project.hasProperty('AMNET_UPLOAD_STORE_FILE')) {
+ storeFile file(AMNET_UPLOAD_STORE_FILE)
+ storePassword AMNET_UPLOAD_STORE_PASSWORD
+ keyAlias AMNET_UPLOAD_KEY_ALIAS
+ keyPassword AMNET_UPLOAD_KEY_PASSWORD
+ }
+ }
}
buildTypes {
debug {
signingConfig signingConfigs.debug
}
release {
- // Caution! In production, you need to generate your own keystore file.
- // see https://reactnative.dev/docs/signed-apk-android.
- signingConfig signingConfigs.debug
+ signingConfig signingConfigs.release
shrinkResources (findProperty('android.enableShrinkResourcesInReleaseBuilds')?.toBoolean() ?: false)
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"