Session fetch (useSession.ts): - Wrap the session ID in encodeURIComponent before interpolating into the fetch URL. Session IDs can contain characters like '+' or '/' that would corrupt the path without encoding. Export route (export.ts): - Add validation that redactedMessageUuids, when present, is an array. Previously only visibleMessageUuids was checked, so a malformed redactedMessageUuids value (e.g. a string or object) would silently pass validation and potentially cause downstream type errors in the HTML exporter. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
73 lines
2.1 KiB
TypeScript
73 lines
2.1 KiB
TypeScript
import { useState, useEffect, useCallback } from "react";
|
|
import type { SessionEntry, SessionDetailResponse } from "../lib/types";
|
|
|
|
interface SessionState {
|
|
sessions: SessionEntry[];
|
|
sessionsLoading: boolean;
|
|
sessionsError: string | null;
|
|
currentSession: SessionDetailResponse | null;
|
|
sessionLoading: boolean;
|
|
sessionError: string | null;
|
|
loadSessions: () => Promise<void>;
|
|
loadSession: (id: string) => Promise<void>;
|
|
}
|
|
|
|
export function useSession(): SessionState {
|
|
const [sessions, setSessions] = useState<SessionEntry[]>([]);
|
|
const [sessionsLoading, setSessionsLoading] = useState(true);
|
|
const [sessionsError, setSessionsError] = useState<string | null>(null);
|
|
const [currentSession, setCurrentSession] =
|
|
useState<SessionDetailResponse | null>(null);
|
|
const [sessionLoading, setSessionLoading] = useState(false);
|
|
const [sessionError, setSessionError] = useState<string | null>(null);
|
|
|
|
const loadSessions = useCallback(async () => {
|
|
setSessionsLoading(true);
|
|
setSessionsError(null);
|
|
try {
|
|
const res = await fetch("/api/sessions");
|
|
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
const data = await res.json();
|
|
setSessions(data.sessions);
|
|
} catch (err) {
|
|
setSessionsError(
|
|
err instanceof Error ? err.message : "Failed to load sessions"
|
|
);
|
|
} finally {
|
|
setSessionsLoading(false);
|
|
}
|
|
}, []);
|
|
|
|
const loadSession = useCallback(async (id: string) => {
|
|
setSessionLoading(true);
|
|
setSessionError(null);
|
|
try {
|
|
const res = await fetch(`/api/sessions/${encodeURIComponent(id)}`);
|
|
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
const data = await res.json();
|
|
setCurrentSession(data);
|
|
} catch (err) {
|
|
setSessionError(
|
|
err instanceof Error ? err.message : "Failed to load session"
|
|
);
|
|
} finally {
|
|
setSessionLoading(false);
|
|
}
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
loadSessions();
|
|
}, [loadSessions]);
|
|
|
|
return {
|
|
sessions,
|
|
sessionsLoading,
|
|
sessionsError,
|
|
currentSession,
|
|
sessionLoading,
|
|
sessionError,
|
|
loadSessions,
|
|
loadSession,
|
|
};
|
|
}
|