multiple groups per user

This commit is contained in:
azykov@mail.ru
2026-09-30 09:06:10 +03:00
parent 45158cde38
commit c197e5978a
21 changed files with 749 additions and 277 deletions
+34 -11
View File
@@ -1,6 +1,6 @@
import type { Card } from '@cardy/shared';
import type { Api } from './api.js';
import { cardImageIds, images, storage, type UsageMap, type VersionMap } from './storage.js';
import type { Api, GroupApi } from './api.js';
import { LEGACY_GROUP_ID, cardImageIds, images, storage, type Session, type UsageMap, type VersionMap } from './storage.js';
export const MAX_PARALLEL_DOWNLOADS = 5;
@@ -31,13 +31,35 @@ export function cardsNeedingImages(cards: Card[], versions: VersionMap, stored:
}
/**
* Downloads the list of cards and replaces the local list (only when the download succeeded).
* Re-reads the key owner's groups from the server and returns the updated session:
* - a legacy session (from before multi-group) gets its groups, and its local data moves to the first
* group (the one the group-less routes served);
* - local data of groups the user no longer belongs to is removed;
* - the current group falls back to the first group when it is gone.
*/
export async function refreshGroups(api: Api, session: Session): Promise<Session> {
const { groups } = await api.auth();
const firstId = groups[0]!.id;
if (!session.groups?.length) {
await storage.moveGroup(LEGACY_GROUP_ID, firstId);
} else {
for (const old of session.groups) if (!groups.some((g) => g.id === old.id)) await storage.removeGroup(old.id);
}
const current = groups.some((g) => g.id === session.currentGroupId) ? session.currentGroupId : firstId;
const { groupName: _legacy, ...rest } = session;
const next: Session = { ...rest, groups, currentGroupId: current };
await storage.setSession(next);
return next;
}
/**
* Downloads the group's list of cards and replaces the local list (only when the download succeeded).
* Cards gone from the server are removed locally with their images and usage.
*/
export async function refreshCardList(api: Api): Promise<Card[]> {
export async function refreshCardList(api: GroupApi, groupId: string): Promise<Card[]> {
const fresh = await api.cards();
const [old, usage, versions] = await Promise.all([storage.getCards(), storage.getUsage(), storage.getVersions()]);
await storage.setCards(fresh);
const [old, usage, versions] = await Promise.all([storage.getCards(groupId), storage.getUsage(groupId), storage.getVersions(groupId)]);
await storage.setCards(groupId, fresh);
const freshIds = new Set(fresh.map((c) => c.id));
const gone = old.filter((c) => !freshIds.has(c.id));
@@ -47,7 +69,7 @@ export async function refreshCardList(api: Api): Promise<Card[]> {
delete versions[c.id];
await images.prune(c.id);
}
await Promise.all([storage.setUsage(usage), storage.setVersions(versions)]);
await Promise.all([storage.setUsage(groupId, usage), storage.setVersions(groupId, versions)]);
}
return fresh;
}
@@ -62,13 +84,14 @@ export interface ImageSyncResult {
* A card's version is recorded only after all its files are stored, so failures are retried next time.
*/
export async function syncImages(
api: Api,
api: GroupApi,
groupId: string,
cards: Card[],
onCardDone?: (card: Card) => void,
onProgress?: (done: number, total: number) => void,
): Promise<ImageSyncResult> {
const stored = await images.storedKeys();
const changed = cardsNeedingImages(cards, await storage.getVersions(), stored);
const changed = cardsNeedingImages(cards, await storage.getVersions(groupId), stored);
const tasks = changed.flatMap((card) =>
cardImageIds(card)
.filter((id) => !stored.has(images.key(card.id, id)))
@@ -85,9 +108,9 @@ export async function syncImages(
if (failedCards.has(card.id)) return versionWrites;
versionWrites = versionWrites.then(async () => {
await images.prune(card.id, cardImageIds(card));
const versions = await storage.getVersions();
const versions = await storage.getVersions(groupId);
versions[card.id] = card.imagesVersion;
await storage.setVersions(versions);
await storage.setVersions(groupId, versions);
onCardDone?.(card);
});
return versionWrites;