Subject bar under the header in Quizzes and Lessons, synced between tabs; bidi fix for Latin math in RTL text
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import { createContext, useCallback, useContext, useEffect, useMemo, useState, type ReactNode } from 'react';
|
||||
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, type ReactNode } from 'react';
|
||||
import { api, type Character, type Grade, type HomeResponse, type UserProfile, type UserSummary } from '@/api';
|
||||
import { seyarFromSlug, type SeyarChar } from '@/mascots/seyar';
|
||||
import { companionStore, useCompanion } from '@/state/companion';
|
||||
import { fetchHome, subjectStore } from '@/state/subject';
|
||||
|
||||
/**
|
||||
* دادههایی که چند صفحه با هم لازم دارند: پروفایل، آمار، نقشه، همراهِ انتخابی
|
||||
@@ -18,7 +19,12 @@ interface AppData {
|
||||
/** همراهی که کاربر در صفحهی کاراکترها انتخاب کرده؛ پیشفرض سیگما. */
|
||||
companion: SeyarChar;
|
||||
unreadCount: number;
|
||||
refreshHome: () => Promise<void>;
|
||||
/** `gradeId` را فقط وقتی بدهید که پایه همین الان عوض شده و state هنوز رندر نشده. */
|
||||
refreshHome: (gradeId?: number) => Promise<void>;
|
||||
/** عوضِ کتابِ نقشه (هندسه، حسابان، …)؛ آزمونها و درسها هر دو همین را میخوانند. */
|
||||
selectSubject: (key: string) => void;
|
||||
/** کتابی که روی `home` است؛ بینقشه (هنوز نرسیده) null. */
|
||||
subjectKey: string | null;
|
||||
refreshSummary: () => Promise<void>;
|
||||
refreshUnread: () => Promise<void>;
|
||||
setProfile: (profile: UserProfile) => void;
|
||||
@@ -54,14 +60,35 @@ export function AppDataProvider({
|
||||
const setCompanionChar = companionStore.set;
|
||||
const [unreadCount, setUnreadCount] = useState(0);
|
||||
|
||||
const refreshHome = useCallback(async () => {
|
||||
const gradeId = profile.gradeId;
|
||||
// نقشهی کتابهایی که دیده شده؛ جابهجایی به کتابِ دیدهشده فوری است و پشتصحنه تازه میشود.
|
||||
const homeCache = useRef(new Map<string, HomeResponse>());
|
||||
|
||||
const refreshHome = useCallback(async (forGrade?: number) => {
|
||||
const target = forGrade ?? gradeId;
|
||||
try {
|
||||
setHome(await api.home());
|
||||
const fresh = await fetchHome(target);
|
||||
if (fresh.subject) homeCache.current.set(`${target}:${fresh.subject.key}`, fresh);
|
||||
setHome(fresh);
|
||||
setHomeError(null);
|
||||
} catch (err) {
|
||||
setHomeError(err);
|
||||
}
|
||||
}, []);
|
||||
}, [gradeId]);
|
||||
|
||||
const selectSubject = useCallback(
|
||||
(key: string) => {
|
||||
if (gradeId == null) return;
|
||||
subjectStore.set(gradeId, key);
|
||||
const cached = homeCache.current.get(`${gradeId}:${key}`);
|
||||
// کتابِ دیدهشده فوری میآید؛ دیدهنشده یک لحظه «در حال آوردن» است تا نقشهی کتابِ قبلی زیرِ نامِ
|
||||
// کتابِ تازه نماند.
|
||||
setHome(cached ?? null);
|
||||
setHomeError(null);
|
||||
void refreshHome();
|
||||
},
|
||||
[gradeId, refreshHome],
|
||||
);
|
||||
|
||||
const refreshSummary = useCallback(async () => {
|
||||
try {
|
||||
@@ -106,13 +133,15 @@ export function AppDataProvider({
|
||||
companion,
|
||||
unreadCount,
|
||||
refreshHome,
|
||||
selectSubject,
|
||||
subjectKey: home?.subject?.key ?? null,
|
||||
refreshSummary,
|
||||
refreshUnread,
|
||||
setProfile,
|
||||
setCompanion: (character) => setCompanionChar(seyarFromSlug(character.slug) ?? 'sigma'),
|
||||
setUnreadCount,
|
||||
}),
|
||||
[profile, grades, summary, home, homeError, companion, unreadCount, refreshHome, refreshSummary, refreshUnread],
|
||||
[profile, grades, summary, home, homeError, companion, unreadCount, refreshHome, selectSubject, refreshSummary, refreshUnread],
|
||||
);
|
||||
|
||||
return <AppDataContext.Provider value={value}>{children}</AppDataContext.Provider>;
|
||||
|
||||
Reference in New Issue
Block a user