import { defineStore } from "pinia"; import { apiRequest } from "../data/api"; import { db } from "../data/db"; import { currentLedgerPreferenceId, type LedgerRecord, type UserPreference } from "../data/ledgers"; import { useAuthStore } from "./auth"; export type LedgerMember = { id: string; name: string; role: "owner" | "member"; joinedAt: string; }; let ledgerLoadPromise: Promise | null = null; export const useLedgerStore = defineStore("ledgers", { state: () => ({ ledgers: [] as LedgerRecord[], currentLedgerId: "", currentMembers: [] as LedgerMember[], loaded: false, }), getters: { currentLedger(state) { return state.ledgers.find((ledger) => ledger.id === state.currentLedgerId) ?? null; }, currentRole(): "owner" | "member" | null { return this.currentLedger?.role ?? null; }, personalLedger(state) { return state.ledgers.find((ledger) => ledger.isPersonal) ?? null; }, }, actions: { async loadLedgers() { if (this.loaded) return; if (ledgerLoadPromise) return ledgerLoadPromise; ledgerLoadPromise = (async () => { const auth = useAuthStore(); if (!auth.user) return; const result = await apiRequest<{ ledgers: LedgerRecord[] }>("/api/ledgers"); const serverLedgers = result.ledgers.map((ledger) => ({ ...ledger })); this.ledgers = serverLedgers; const allowedIds = new Set(this.ledgers.map((ledger) => ledger.id)); const localEntries = await db.entries.toArray(); const inaccessibleEntries = localEntries .filter((entry) => entry.ownerId !== auth.user!.id && !entry.ledgerIds.some((ledgerId) => allowedIds.has(ledgerId))) .map((entry) => entry.id); const projectedEntries = localEntries .filter((entry) => entry.ownerId !== auth.user!.id && entry.ledgerIds.some((ledgerId) => !allowedIds.has(ledgerId))) .map((entry) => ({ ...entry, ledgerIds: entry.ledgerIds.filter((ledgerId) => allowedIds.has(ledgerId)) })); const inaccessibleOperations = await db.syncOperations .filter((item) => !item.ledgerIds.some((ledgerId) => allowedIds.has(ledgerId))) .primaryKeys(); await db.transaction("rw", db.ledgers, db.entries, db.syncOperations, async () => { await db.ledgers.clear(); if (serverLedgers.length) await db.ledgers.bulkPut(serverLedgers); if (projectedEntries.length) await db.entries.bulkPut(projectedEntries); if (inaccessibleEntries.length) await db.entries.bulkDelete(inaccessibleEntries); if (inaccessibleOperations.length) await db.syncOperations.bulkDelete(inaccessibleOperations); }); const preferenceId = currentLedgerPreferenceId(auth.user.id); const preference = await db.userPreferences.get(preferenceId); const preferredLedger = this.ledgers.find((ledger) => ledger.id === preference?.value); this.currentLedgerId = preferredLedger?.id ?? this.ledgers[0]?.id ?? ""; if (this.currentLedgerId !== preference?.value) await this.persistCurrentLedger(); this.loaded = true; await this.loadCurrentMembers(); })(); try { await ledgerLoadPromise; } finally { ledgerLoadPromise = null; } }, async reloadLedgers() { this.loaded = false; this.currentMembers = []; await this.loadLedgers(); }, async setCurrentLedger(ledgerId: string) { const ledger = this.ledgers.find((item) => item.id === ledgerId && item.archivedAt === null); if (!ledger) throw new Error("Ledger not found"); this.currentLedgerId = ledger.id; await this.persistCurrentLedger(); await this.loadCurrentMembers(); }, async persistCurrentLedger() { const auth = useAuthStore(); if (!this.currentLedgerId || !auth.user) return; const preference: UserPreference = { id: currentLedgerPreferenceId(auth.user.id), userId: auth.user.id, key: "currentLedgerId", value: this.currentLedgerId, updatedAt: new Date().toISOString(), }; await db.userPreferences.put(preference); }, async loadCurrentMembers() { if (!this.currentLedgerId) { this.currentMembers = []; return; } const result = await apiRequest<{ members: LedgerMember[] }>( `/api/ledgers/${encodeURIComponent(this.currentLedgerId)}/members`, ); this.currentMembers = result.members; }, memberName(userId: string) { return this.currentMembers.find((member) => member.id === userId)?.name ?? "历史用户"; }, async createLedger(input: Pick) { const result = await apiRequest<{ ledger: LedgerRecord }>("/api/ledgers", { method: "POST", body: input }); await this.reloadLedgers(); await this.setCurrentLedger(result.ledger.id); return result.ledger; }, async updateLedger(input: Pick) { await apiRequest(`/api/ledgers/${encodeURIComponent(input.id)}`, { method: "PATCH", body: input }); await this.reloadLedgers(); return this.ledgers.find((ledger) => ledger.id === input.id)!; }, }, });