mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-03-20 12:11:28 -07:00
feat(stats): add v1 immersion stats dashboard (#19)
This commit is contained in:
96
stats/src/hooks/useSessions.ts
Normal file
96
stats/src/hooks/useSessions.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { getStatsClient } from './useStatsApi';
|
||||
import { SESSION_CHART_EVENT_TYPES } from '../lib/session-events';
|
||||
import type { SessionSummary, SessionTimelinePoint, SessionEvent } from '../types/stats';
|
||||
|
||||
export function toErrorMessage(err: unknown): string {
|
||||
return err instanceof Error ? err.message : String(err);
|
||||
}
|
||||
|
||||
export function useSessions(limit = 50) {
|
||||
const [sessions, setSessions] = useState<SessionSummary[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
const client = getStatsClient();
|
||||
client
|
||||
.getSessions(limit)
|
||||
.then((nextSessions) => {
|
||||
if (cancelled) return;
|
||||
setSessions(nextSessions);
|
||||
})
|
||||
.catch((err) => {
|
||||
if (cancelled) return;
|
||||
setError(toErrorMessage(err));
|
||||
})
|
||||
.finally(() => {
|
||||
if (cancelled) return;
|
||||
setLoading(false);
|
||||
});
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [limit]);
|
||||
|
||||
return { sessions, loading, error };
|
||||
}
|
||||
|
||||
export interface KnownWordsTimelinePoint {
|
||||
linesSeen: number;
|
||||
knownWordsSeen: number;
|
||||
}
|
||||
|
||||
export function useSessionDetail(sessionId: number | null) {
|
||||
const [timeline, setTimeline] = useState<SessionTimelinePoint[]>([]);
|
||||
const [events, setEvents] = useState<SessionEvent[]>([]);
|
||||
const [knownWordsTimeline, setKnownWordsTimeline] = useState<KnownWordsTimelinePoint[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
setError(null);
|
||||
if (sessionId == null) {
|
||||
setTimeline([]);
|
||||
setEvents([]);
|
||||
setKnownWordsTimeline([]);
|
||||
setLoading(false);
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}
|
||||
setLoading(true);
|
||||
setTimeline([]);
|
||||
setEvents([]);
|
||||
setKnownWordsTimeline([]);
|
||||
const client = getStatsClient();
|
||||
Promise.all([
|
||||
client.getSessionTimeline(sessionId),
|
||||
client.getSessionEvents(sessionId, 500, [...SESSION_CHART_EVENT_TYPES]),
|
||||
client.getSessionKnownWordsTimeline(sessionId),
|
||||
])
|
||||
.then(([nextTimeline, nextEvents, nextKnownWords]) => {
|
||||
if (cancelled) return;
|
||||
setTimeline(nextTimeline);
|
||||
setEvents(nextEvents);
|
||||
setKnownWordsTimeline(nextKnownWords);
|
||||
})
|
||||
.catch((err) => {
|
||||
if (cancelled) return;
|
||||
setError(toErrorMessage(err));
|
||||
})
|
||||
.finally(() => {
|
||||
if (cancelled) return;
|
||||
setLoading(false);
|
||||
});
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [sessionId]);
|
||||
|
||||
return { timeline, events, knownWordsTimeline, loading, error };
|
||||
}
|
||||
Reference in New Issue
Block a user