forked from Tadrisino/MathApp_frontend
69 lines
3.1 KiB
TypeScript
69 lines
3.1 KiB
TypeScript
import { useSyncExternalStore } from 'react';
|
|
import { api, ApiError, type HomeResponse } from '@/api';
|
|
import { KEYS, store } from '@/lib/storage';
|
|
|
|
/**
|
|
* کتابی که دانشآموز در هر پایه انتخاب کرده («هندسه»، «حسابان»، …). یک منبعِ حقیقت برای هر دو تبِ «آزمونها» و
|
|
* «درسها»: هر دو از `home` در AppData میخوانند و `home` همیشه نقشهی همین کتاب است، پس جابهجایی در یکی در
|
|
* دیگری هم دیده میشود.
|
|
*
|
|
* هر پایه جدا نگه داشته میشود (با عوضکردنِ پایه، انتخابِ پایهی قبلی نباید به دوازدهم بپرد) و در
|
|
* localStorage میماند تا بعد از بستنِ اپ همان کتاب باز شود. منبعِ حقیقتِ فهرستِ کتابها سرور است
|
|
* (`home.subjects`)؛ این فقط «کدام» را میگوید.
|
|
*/
|
|
type SubjectMap = Record<string, string>;
|
|
|
|
function read(): SubjectMap {
|
|
try {
|
|
const parsed: unknown = JSON.parse(store.get(KEYS.subjects) ?? '{}');
|
|
return parsed && typeof parsed === 'object' ? (parsed as SubjectMap) : {};
|
|
} catch {
|
|
return {};
|
|
}
|
|
}
|
|
|
|
let current: SubjectMap = read();
|
|
const listeners = new Set<() => void>();
|
|
|
|
export const subjectStore = {
|
|
get: (gradeId: number): string | null => current[String(gradeId)] ?? null,
|
|
set(gradeId: number, key: string | null) {
|
|
const id = String(gradeId);
|
|
if ((current[id] ?? null) === key) return;
|
|
const next = { ...current };
|
|
if (key) next[id] = key;
|
|
else delete next[id];
|
|
current = next;
|
|
store.set(KEYS.subjects, JSON.stringify(next));
|
|
listeners.forEach((l) => l());
|
|
},
|
|
subscribe(listener: () => void) {
|
|
listeners.add(listener);
|
|
return () => listeners.delete(listener);
|
|
},
|
|
snapshot: () => current,
|
|
};
|
|
|
|
/** کتابِ انتخابشدهی این پایه؛ null یعنی هنوز انتخابی نشده (سرور اولین کتاب را میدهد). */
|
|
export function useSelectedSubject(gradeId: number | null | undefined): string | null {
|
|
const map = useSyncExternalStore(subjectStore.subscribe, subjectStore.snapshot, subjectStore.snapshot);
|
|
return gradeId == null ? null : map[String(gradeId)] ?? null;
|
|
}
|
|
|
|
/**
|
|
* نقشهی کتابِ انتخابشده. کلیدِ ذخیرهشدهای که سرور دیگر نمیشناسد (کتاب برداشته شده یا پایه عوض شده)
|
|
* ۴۰۰ میگیرد؛ آن را پاک میکنیم و نقشهی پیشفرض را میگیریم تا کاربر صفحهی خالی نبیند.
|
|
*/
|
|
export async function fetchHome(gradeId: number | null | undefined): Promise<HomeResponse> {
|
|
const wanted = gradeId == null ? null : subjectStore.get(gradeId);
|
|
try {
|
|
return await api.home(wanted ?? undefined);
|
|
} catch (err) {
|
|
if (wanted && gradeId != null && err instanceof ApiError && err.status === 400) {
|
|
subjectStore.set(gradeId, null);
|
|
return api.home();
|
|
}
|
|
throw err;
|
|
}
|
|
}
|