Download src/hooks/useEpisodes.ts from lerobot/LeLab: direct link, hf CLI and curl.
- Browser
- Download file 3.74 kB
-
https://huggingface.co/spaces/lerobot/LeLab/resolve/main/src/hooks/useEpisodes.ts
- Command line
-
hf download hf://spaces/lerobot/LeLab/src/hooks/useEpisodes.ts
-
curl -L -o useEpisodes.ts https://huggingface.co/spaces/lerobot/LeLab/resolve/main/src/hooks/useEpisodes.ts
3.74 kB
| import { useCallback, useEffect, useState } from "react"; | |
| import { useApi } from "@/contexts/ApiContext"; | |
| import { | |
| EpisodeDetail, | |
| EpisodeListResponse, | |
| getEpisode, | |
| listEpisodes, | |
| } from "@/lib/datasetApi"; | |
| /** Episode listing for one local dataset. `repoId` null → idle. */ | |
| export const useEpisodes = (repoId: string | null) => { | |
| const { baseUrl, fetchWithHeaders } = useApi(); | |
| const [data, setData] = useState<EpisodeListResponse | null>(null); | |
| const [loading, setLoading] = useState(false); | |
| const [error, setError] = useState<string | null>(null); | |
| const [version, setVersion] = useState(0); | |
| useEffect(() => { | |
| setData(null); | |
| setError(null); | |
| if (!repoId) { | |
| setLoading(false); | |
| return; | |
| } | |
| const controller = new AbortController(); | |
| setLoading(true); | |
| listEpisodes(baseUrl, fetchWithHeaders, repoId, controller.signal) | |
| .then((r) => { | |
| if (controller.signal.aborted) return; | |
| // The backend reports a readable dataset it couldn't parse as | |
| // success:false rather than an HTTP error, so check the flag too. | |
| if (!r.success) { | |
| setData(null); | |
| setError(r.message ?? "Could not read dataset"); | |
| return; | |
| } | |
| setData(r); | |
| }) | |
| .catch((e) => { | |
| if (controller.signal.aborted) return; | |
| setData(null); | |
| setError(e instanceof Error ? e.message : "Could not read dataset"); | |
| }) | |
| .finally(() => { | |
| if (!controller.signal.aborted) setLoading(false); | |
| }); | |
| return () => controller.abort(); | |
| }, [baseUrl, fetchWithHeaders, repoId, version]); | |
| const refresh = useCallback(() => setVersion((v) => v + 1), []); | |
| /** Show a task edit right away, before the backend has written it. */ | |
| const applyTask = useCallback( | |
| (task: string, episodeIndex: number | null) => | |
| setData((d) => | |
| d && { | |
| ...d, | |
| episodes: d.episodes.map((ep) => | |
| episodeIndex === null || ep.episode_index === episodeIndex ? { ...ep, tasks: [task] } : ep, | |
| ), | |
| }, | |
| ), | |
| [], | |
| ); | |
| // Effects run after render: do not let the new dataset auto-select an | |
| // episode from the old list before this effect clears it. | |
| return { data: data?.repo_id === repoId ? data : null, loading, error, refresh, applyTask }; | |
| }; | |
| /** One episode's detail. Either arg null → idle. */ | |
| export const useEpisodeDetail = (repoId: string | null, episodeIndex: number | null) => { | |
| const { baseUrl, fetchWithHeaders } = useApi(); | |
| const [detail, setDetail] = useState<EpisodeDetail | null>(null); | |
| const [loading, setLoading] = useState(false); | |
| const [error, setError] = useState<string | null>(null); | |
| useEffect(() => { | |
| if (!repoId || episodeIndex === null) { | |
| setDetail(null); | |
| return; | |
| } | |
| const controller = new AbortController(); | |
| setLoading(true); | |
| setError(null); | |
| getEpisode(baseUrl, fetchWithHeaders, repoId, episodeIndex, controller.signal) | |
| .then((r) => { | |
| if (controller.signal.aborted) return; | |
| if (!r.success) { | |
| setDetail(null); | |
| setError(r.message ?? "Could not load episode"); | |
| return; | |
| } | |
| setDetail(r); | |
| }) | |
| .catch((e) => { | |
| if (controller.signal.aborted) return; | |
| setDetail(null); | |
| setError(e instanceof Error ? e.message : "Could not load episode"); | |
| }) | |
| .finally(() => { | |
| if (!controller.signal.aborted) setLoading(false); | |
| }); | |
| // Clicking through episodes quickly would otherwise let a slow earlier | |
| // response land after a faster later one. | |
| return () => controller.abort(); | |
| }, [baseUrl, fetchWithHeaders, repoId, episodeIndex]); | |
| return { detail, loading, error }; | |
| }; | |