move "most recent card used" tracking to separate table

This commit is contained in:
ppc
2025-03-29 10:21:09 +00:00
parent f2550a84e0
commit 1b73b60efa
6 changed files with 117 additions and 33 deletions
+17 -11
View File
@@ -28,6 +28,7 @@ import {AMCard} from "../models/AMCard";
import {felicaCardReadAsync, icCard0008Number, ICType} from "../lib/iccard";
import {
AMCardInRequest,
AMServerConnectionInfo,
AMServerInstanceMetrics,
fetchServerInfo,
@@ -36,6 +37,7 @@ import {
} from "amnet-shared";
import ObjectId = BSON.ObjectId;
import {AMCardRecentUse} from "../models/AMCardRecentUse";
const {useRealm, useQuery, useObject} = RealmContext;
@@ -69,13 +71,9 @@ export default function ServerCardIn({route, navigation}) {
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 serverDefaultCardQuery = useObject(AMCardRecentUse, server.gameId);
const serverDefaultCard = serverDefaultCardQuery?.card ?? null;
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]);
@@ -154,17 +152,25 @@ export default function ServerCardIn({route, navigation}) {
setCardInCompleted(false);
const abortController = new AbortController();
performCardIn(server, {
const request: AMCardInRequest = {
cardId: card.code,
physicalCardIDm: card.physicalCardData?.cardIDm ?? undefined
}, abortController.signal).then(r => {
};
performCardIn(server, request, abortController.signal).then(r => {
const now = new Date();
realm.write(() => {
server.lastUsed = now;
card.lastUsed = now;
card.lastUsedGameId = server.gameId;
// replace the "most recent card" record
if (server.gameId) {
if (serverDefaultCardQuery) {
realm.delete(serverDefaultCardQuery);
}
realm.create(AMCardRecentUse.schema.name, AMCardRecentUse.generate(server.gameId, card));
}
});
setCardInCompleted(r);
+2 -2
View File
@@ -5,8 +5,8 @@ import {CreditCard, Gem, History, Joystick, LucideIcon, RailSymbol} from "lucide
import TimeAgo from "./TimeAgo";
import {AMUICard, AMUICardActionProps, AMUICardDetail} from "./AMUICard";
import {AMCardData} from "../models/AMCard";
import {AmusementICVendor, ICType} from "../lib/iccard";
import {AMCardData} from "../models/AMCardData";
export interface ICCardProps extends AMUICardActionProps {
card: AMCardData,
@@ -68,7 +68,7 @@ function PhysicalICInfo(props: { card: AMCardData | null }) {
function ICVendorName(vendor: AmusementICVendor): string {
switch (vendor) {
case AmusementICVendor.Sega:
return "AiMe Card";
return "Aime Card";
case AmusementICVendor.Konami:
return "e-Amusement Pass";
+12 -15
View File
@@ -1,15 +1,7 @@
import {Realm} from "realm";
import {AmusementICVendor, ICType} from "../lib/iccard";
export interface AMCardData {
code: string;
name: string;
added: Date;
lastUsed?: Date | null;
isLimitedAimeCard: boolean;
physicalCardData?: AMPhysicalCardData | null;
}
import {AMCardRecentUse} from "./AMCardRecentUse";
import {AMCardData} from "./AMCardData";
export class AMCard extends Realm.Object<AMCard> implements AMCardData {
_id!: Realm.BSON.ObjectId;
@@ -18,9 +10,9 @@ export class AMCard extends Realm.Object<AMCard> implements AMCardData {
name: string;
added: Date;
lastUsed?: Date;
lastUsedGameId?: string | null;
lastUsed: Date;
recentUses: Realm.Results<AMCardRecentUse>;
physicalCardData?: AMPhysicalCardData | null = null;
get isLimitedAimeCard() {
@@ -43,6 +35,8 @@ export class AMCard extends Realm.Object<AMCard> implements AMCardData {
realm.delete(card.physicalCardData);
}
// remove any usage history tied to the card
realm.delete(card.recentUses);
realm.delete(card);
});
}
@@ -54,15 +48,18 @@ export class AMCard extends Realm.Object<AMCard> implements AMCardData {
properties: {
_id: "objectId",
code: "string",
name: {type: "string", default: "New Card"},
added: {type: "date", default: new Date()},
name: {type: "string", default: "New Card", indexed: true},
added: {type: "date", default: new Date(), indexed: true},
lastUsed: {type: "date", optional: true, indexed: true},
lastUsedGameId: {type: "string", optional: true, indexed: true},
usageHistory: {type: "linkingObjects", objectType: "cardUsageHistory", property: "_id"},
physicalCardData: {type: "object", objectType: "physicalCard", optional: true}
}
};
}
/**
* Holds physical card information when a card is imported via NFC/FeliCa
*/
export class AMPhysicalCardData extends Realm.Object<AMPhysicalCardData> {
cardIDm: string;
+11
View File
@@ -0,0 +1,11 @@
import {AMPhysicalCardData} from "./AMCard";
export interface AMCardData {
code: string;
name: string;
added: Date;
lastUsed?: Date | null;
isLimitedAimeCard: boolean;
physicalCardData?: AMPhysicalCardData | null;
}
+37
View File
@@ -0,0 +1,37 @@
import {Realm} from "realm";
import {AMCard} from "./AMCard";
/**
* Represents a usage of a card for determining which card was last used on a specific game.
*/
export class AMCardRecentUse extends Realm.Object<AMCardRecentUse> {
/**
* The game id that is being tracked
*/
gameId: string;
/**
* The card that was last used on the game.
* If the card is removed, this record will also be removed.
*/
card: AMCard;
static generate(gameId: string, card: AMCard) {
return {
gameId: gameId,
card: card
};
}
static schema: Realm.ObjectSchema = {
name: "cardUsageHistory",
primaryKey: "gameId",
properties: {
gameId: "string",
card: {type: "object", objectType: "card", optional: false}
}
}
}
+38 -5
View File
@@ -1,10 +1,43 @@
import {Realm} from "realm";
import {createRealmContext} from "@realm/react";
import {AMCard, AMPhysicalCardData} from "./AMCard";
import {AMServer} from "./AMServer";
import {AMCardRecentUse} from "./AMCardRecentUse";
import {AMCard, AMPhysicalCardData} from "./AMCard";
const migrationFunction = (oldRealm: Realm, newRealm: Realm) => {
if (oldRealm.schemaVersion < 5) {
const oldCards = oldRealm.objects("card");
const gameIdMap: Map<string, { card: any, lastUsed: Date }> = new Map();
// get last used card for each game
oldCards.forEach((card: any) => {
if (card.lastUsedGameId && card.lastUsed) {
const existing = gameIdMap.get(card.lastUsedGameId);
if (!existing || card.lastUsed > existing.lastUsed) {
gameIdMap.set(card.lastUsedGameId, { card, lastUsed: card.lastUsed });
}
}
});
// write new history records
const newCards = newRealm.objects("card");
newRealm.write(() => {
gameIdMap.forEach((value, gameId) => {
const newCard = newCards.filtered(`_id == $0`, value.card._id)[0];
newRealm.create("cardUsageHistory", {
gameId: gameId,
card: newCard
});
});
});
}
};
// @ts-ignore
export default createRealmContext({
schemaVersion: 4,
schema: [AMPhysicalCardData, AMCard, AMServer],
deleteRealmIfMigrationNeeded: false
});
schemaVersion: 5,
schema: [AMPhysicalCardData, AMCardRecentUse, AMCard, AMServer],
onMigration: migrationFunction,
deleteRealmIfMigrationNeeded: false,
});