MCPcopy Create free account
hub / github.com/bjsi/incremental-everything / registerQueueSessionTracking

Function registerQueueSessionTracking

src/lib/queue_session.ts:331–756  ·  view source on GitHub ↗
(plugin: ReactRNPlugin)

Source from the content-addressed store, hash-verified

329 } else {
330 session.currentCardFirstRep = undefined;
331 session.currentCardTotalTime = 0;
332 session.currentCardRepCount = 0;
333 session.currentCardInterval = undefined;
334 }
335}
336
337export function registerQueueSessionTracking(plugin: ReactRNPlugin) {
338 // One-shot startup migration / rollover: covers users who installed this
339 // build but haven't reviewed yet. Fire-and-forget; widget reads will pick
340 // up the new state via useSyncedStorageState reactivity.
341 maybeRollOver(plugin);
342
343 // Startup recovery: if the plugin restarted (page refresh, crash) while a
344 // session was in progress, ACTIVE_SESSION_KEY holds the last synced snapshot
345 // but currentSession is null. Rescue it into synced history so the work is
346 // not silently lost.
347 (async () => {
348 try {
349 const orphaned = await plugin.storage.getSession<PracticedQueueSession>(ACTIVE_SESSION_KEY);
350 if (orphaned && (orphaned.flashcardsCount > 0 || orphaned.incRemsCount > 0)) {
351 if (!orphaned.endTime) orphaned.endTime = Date.now();
352 const history =
353 ((await plugin.storage.getSynced(PRACTICED_QUEUES_HISTORY_KEY)) as PracticedQueueSession[]) || [];
354 const deduped = history.filter((s): s is PracticedQueueSession => !!s && s.id !== orphaned.id);
355 await plugin.storage.setSynced(PRACTICED_QUEUES_HISTORY_KEY, [orphaned, ...deduped]);
356 await plugin.storage.setSession(ACTIVE_SESSION_KEY, null);
357 console.log(
358 `[QueueSession] Recovered orphaned session: id=${orphaned.id} scope="${orphaned.scopeName}" fc=${orphaned.flashcardsCount} ir=${orphaned.incRemsCount}`
359 );
360 maybeRollOver(plugin);
361 }
362 } catch (err) {
363 console.error('[QueueSession] Orphaned session recovery failed:', err);
364 }
365 })();
366
367 // Heartbeat monitor: auto-save Mastery Drill session if the popup is closed without QueueExit.
368 // Popup drill only: the regular-queue drill writes no heartbeat, so a timestamp left behind
369 // by an earlier popup would end each of its sessions 5 s after it starts.
370 setInterval(async () => {
371 if (currentSession && currentSession.scopeName === 'Mastery Drill' && !isNativeDrillActive()) {
372 if (Date.now() - currentSession.startTime < 5000) return; // grace period
373 const lastHeartbeat = await plugin.storage.getSession<number>('finalDrillHeartbeat');
374 if (lastHeartbeat) {
375 if (Date.now() - lastHeartbeat > 5000) {
376 await saveCurrentSession(plugin, 'Heartbeat Stale');
377 }
378 }
379 }
380 }, 2500);
381
382 // Cluster sibling-transition poll: inside a cluster, QueueLoadCard does NOT fire per
383 // sibling — only card_info_bar.tsx sees each sibling via getWidgetContext().
384 // The widget broadcasts `clusterVisibleCardId`; we poll for changes and advance the
385 // per-card panel state (currentCardId/prevCardId + lifetime stats) so the UI tracks
386 // the sibling actually on screen rather than being stuck on the cluster anchor.
387 setInterval(async () => {
388 const session = currentSession;

Callers 1

registerEventListenersFunction · 0.90

Calls 14

safeRemTextToStringFunction · 0.90
isMobileDeviceFunction · 0.90
parseWeightsStringFunction · 0.90
computeFSRSStateFunction · 0.90
maybeRollOverFunction · 0.85
saveCurrentSessionFunction · 0.85
loadCardStatsFunction · 0.85
syncLiveSessionFunction · 0.85
applyIncRemEngagementEndFunction · 0.85
applyIncRemRepFunction · 0.85
clearEditorIdleSaveFunction · 0.85

Tested by

no test coverage detected