File size: 2,126 Bytes
65d68f5 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | "use client";
import { useEffect, useMemo, useState } from "react";
import { useStudentActivityVersion } from "@/hooks/useStudentActivity";
import {
getSyncedProgress,
syncProgressEvents,
} from "@/lib/api/progress-sync";
import { learnerScopeId } from "@/lib/phase3-learning";
import {
listActivities,
mergeActivityEvents,
type ActivityEvent,
} from "@/lib/student-activity";
export type ActivitySyncState = "device" | "syncing" | "synced" | "error";
/**
* One account-aware activity ledger for Home and Progress. Offline events stay
* visible immediately, then upload idempotently and merge with activity from
* the student's other devices.
*/
export function useSyncedStudentActivity(limit = 50) {
const version = useStudentActivityVersion();
const scopeId = learnerScopeId();
const localEvents = useMemo(() => {
void version;
return listActivities(Math.min(limit, 100));
}, [limit, version]);
const [remoteEvents, setRemoteEvents] = useState<ActivityEvent[]>([]);
const [syncState, setSyncState] = useState<ActivitySyncState>(() =>
scopeId ? "syncing" : "device",
);
useEffect(() => {
let cancelled = false;
async function refresh() {
// Defer state changes so the effect never performs a synchronous state
// write during React's commit phase.
await Promise.resolve();
if (cancelled) return;
if (!scopeId) {
setRemoteEvents([]);
setSyncState("device");
return;
}
setSyncState("syncing");
try {
if (localEvents.length) await syncProgressEvents(localEvents);
const remote = await getSyncedProgress();
if (cancelled) return;
setRemoteEvents(remote.recent_events);
setSyncState("synced");
} catch {
if (!cancelled) setSyncState("error");
}
}
void refresh();
return () => {
cancelled = true;
};
}, [localEvents, scopeId]);
const events = useMemo(
() => mergeActivityEvents(remoteEvents, localEvents).slice(0, limit),
[limit, localEvents, remoteEvents],
);
return { events, syncState, version };
}
|