From 745b6993de88ab3d94bc7fe1654c616539a584a8 Mon Sep 17 00:00:00 2001 From: Zerebos Date: Thu, 21 May 2026 02:33:06 -0400 Subject: [PATCH] Actually grab status from server --- src/api/queries/manga.ts | 17 ++++++ src/api/queries/queries.md | 1 + .../recent/components/UpdatesPanel.svelte | 59 +++++++++++++------ 3 files changed, 59 insertions(+), 18 deletions(-) diff --git a/src/api/queries/manga.ts b/src/api/queries/manga.ts index d2656eb..4eceda5 100644 --- a/src/api/queries/manga.ts +++ b/src/api/queries/manga.ts @@ -78,6 +78,23 @@ export const LIBRARY_UPDATE_STATUS = ` } `; +export const GET_LIBRARY_UPDATE_PANEL_STATUS = ` + query GetLibraryUpdatePanelStatus { + libraryUpdateStatus { + jobsInfo { + isRunning + finishedJobs + totalJobs + skippedMangasCount + skippedCategoriesCount + } + } + lastUpdateTimestamp { + timestamp + } + } +`; + export const GET_RESTORE_STATUS = ` query GetRestoreStatus($id: String!) { restoreStatus(id: $id) { mangaProgress state totalManga } diff --git a/src/api/queries/queries.md b/src/api/queries/queries.md index e8dadef..edbebc8 100644 --- a/src/api/queries/queries.md +++ b/src/api/queries/queries.md @@ -11,6 +11,7 @@ | `GET_DOWNLOADED_CHAPTERS_PAGES` | — | Page counts for all downloaded chapters — used for storage stats | | `GET_DOWNLOADS_PATH` | — | `downloadsPath` and `localSourcePath` from settings | | `LIBRARY_UPDATE_STATUS` | — | Current library update job — `jobsInfo` progress and `mangaUpdates` list with new chapters | +| `GET_LIBRARY_UPDATE_PANEL_STATUS` | — | Library updater status + server `lastUpdateTimestamp` for UI status displays | | `GET_RESTORE_STATUS` | `id: String!` | Backup restore job status by job ID — `mangaProgress`, `state`, `totalManga` | | `VALIDATE_BACKUP` | `backup: Upload!` | Validate a backup file before restore — returns missing sources and trackers | | `MANGAS_BY_GENRE` | `filter: MangaFilterInput`, `first: Int`, `offset: Int` | Paginated manga filtered by genre, ordered by `IN_LIBRARY_AT DESC` | diff --git a/src/features/recent/components/UpdatesPanel.svelte b/src/features/recent/components/UpdatesPanel.svelte index f63a865..2416169 100644 --- a/src/features/recent/components/UpdatesPanel.svelte +++ b/src/features/recent/components/UpdatesPanel.svelte @@ -2,7 +2,7 @@ import { onMount, onDestroy } from "svelte"; import { BookOpen, CircleNotch } from "phosphor-svelte"; import { gql } from "@api/client"; - import { GET_RECENTLY_UPDATED, GET_CHAPTERS } from "@api/queries"; + import { GET_RECENTLY_UPDATED, GET_CHAPTERS, GET_LIBRARY_UPDATE_PANEL_STATUS } from "@api/queries"; import { cache, CACHE_GROUPS, CACHE_KEYS } from "@core/cache"; import { store, openReader, setActiveManga, addToast } from "@store/state.svelte"; import { dayLabel } from "@core/util"; @@ -29,6 +29,8 @@ let updates = $state([]); let error = $state(null); let openingId = $state(null); + let updaterRunning = $state(false); + let lastUpdatedTs = $state(null); let ctrl: AbortController | null = null; const RECENT_UPDATES_TTL_MS = 60 * 1_000; @@ -57,13 +59,9 @@ return Object.entries(grouped).map(([label, items]) => ({ label, items })) as UpdateGroup[]; }); - const lastCheckedTs = $derived( - updates.length > 0 ? fetchedAtMs(updates[0]) : null - ); - - const lastCheckedLabel = $derived( - lastCheckedTs - ? new Date(lastCheckedTs).toLocaleString("en-US", { + const lastUpdatedLabel = $derived( + lastUpdatedTs + ? new Date(lastUpdatedTs).toLocaleString("en-US", { month: "short", day: "numeric", year: "numeric", @@ -73,6 +71,17 @@ : null ); + function parseServerTimestamp(value: unknown): number | null { + if (typeof value === "number") return Number.isFinite(value) ? value : null; + if (typeof value === "string") { + const numeric = Number(value); + if (Number.isFinite(numeric)) return numeric; + const parsed = new Date(value).getTime(); + return Number.isFinite(parsed) ? parsed : null; + } + return null; + } + function mangaStub(item: RecentUpdate): Manga { return { id: item.manga?.id ?? item.mangaId, @@ -99,21 +108,35 @@ const key = CACHE_KEYS.RECENT_UPDATES; if (force) cache.clear(key); - const res = await cache.get<{ chapters: { nodes: RecentUpdate[] } }>( - key, - () => gql<{ chapters: { nodes: RecentUpdate[] } }>(GET_RECENTLY_UPDATED, {}, nextCtrl.signal), - RECENT_UPDATES_TTL_MS, - CACHE_GROUPS.LIBRARY, - ); + const [updatesRes, statusRes] = await Promise.all([ + cache.get<{ chapters: { nodes: RecentUpdate[] } }>( + key, + () => gql<{ chapters: { nodes: RecentUpdate[] } }>(GET_RECENTLY_UPDATED, {}, nextCtrl.signal), + RECENT_UPDATES_TTL_MS, + CACHE_GROUPS.LIBRARY, + ), + gql<{ + libraryUpdateStatus: { + jobsInfo: { isRunning: boolean }; + }; + lastUpdateTimestamp: { timestamp: string | number | null } | null; + }>(GET_LIBRARY_UPDATE_PANEL_STATUS, {}, nextCtrl.signal).catch(() => null), + ]); + + updaterRunning = statusRes?.libraryUpdateStatus.jobsInfo.isRunning ?? false; + lastUpdatedTs = parseServerTimestamp(statusRes?.lastUpdateTimestamp?.timestamp ?? null); + if (nextCtrl.signal.aborted) return; - updates = res.chapters.nodes + updates = updatesRes.chapters.nodes .filter(item => item.manga?.inLibrary) .sort((a, b) => fetchedAtMs(b) - fetchedAtMs(a)); } catch (e: any) { if (nextCtrl.signal.aborted) return; error = e?.message ?? "Failed to load updates"; updates = []; + updaterRunning = false; + lastUpdatedTs = null; } finally { if (!nextCtrl.signal.aborted) loading = false; } @@ -150,11 +173,11 @@
- {#if loading}Checking for updates…{:else if error}Update check failed{:else}Up to date{/if} + {#if loading}Checking for updates…{:else if error}Update check failed{:else if updaterRunning}Library update in progress{:else}Up to date{/if}
- {#if !loading && lastCheckedLabel} - Last checked: {lastCheckedLabel} + {#if !loading && lastUpdatedLabel} + Last updated: {lastUpdatedLabel}
{/if} {#if !loading && updates.length > 0}