From 6cc9867a63fb62e44e12ee4a460baa7c3f72ae31 Mon Sep 17 00:00:00 2001 From: neon <18559658+neonsolstice@users.noreply.github.com> Date: Tue, 12 May 2026 05:30:04 +0000 Subject: [PATCH] fix: remove KoreaderBookSyncCard, fix missing initial library load, add mediaSrc CSP, return null from getBookProgress instead of 404 --- .../components/detail/tabs/DetailsTab.vue | 8 -- .../__tests__/useLibraryBooks.spec.ts | 8 +- .../book/composables/useLibraryBooks.ts | 6 +- .../components/KoreaderBookSyncCard.vue | 80 ------------------- .../__tests__/useKoreaderBookProgress.spec.ts | 16 +++- .../composables/useKoreaderBookProgress.ts | 4 - .../composables/__tests__/useFoliate.spec.ts | 8 +- .../reader/epub/composables/useFoliate.ts | 2 + client/src/views/HomeView.vue | 6 +- server/src/main.ts | 1 + .../modules/koreader/koreader.controller.ts | 22 +---- 11 files changed, 31 insertions(+), 130 deletions(-) delete mode 100644 client/src/features/koreader/components/KoreaderBookSyncCard.vue diff --git a/client/src/features/book/components/detail/tabs/DetailsTab.vue b/client/src/features/book/components/detail/tabs/DetailsTab.vue index 70f48838f..3750859e6 100644 --- a/client/src/features/book/components/detail/tabs/DetailsTab.vue +++ b/client/src/features/book/components/detail/tabs/DetailsTab.vue @@ -15,7 +15,6 @@ import { STATUS_OPTIONS, STATUS_ICONS, STATUS_COLORS, useBookStatus } from '@/fe import BookDownloadButton from '@/features/book/components/BookDownloadButton.vue' import DiscoverRow from '@/features/book/components/detail/DiscoverRow.vue' import BookCoverPlaceholder from '@/features/book/components/BookCoverPlaceholder.vue' -import KoreaderBookSyncCard from '@/features/koreader/components/KoreaderBookSyncCard.vue' import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover' import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu' @@ -1404,13 +1403,6 @@ watch( - -

Synopsis

diff --git a/client/src/features/book/composables/__tests__/useLibraryBooks.spec.ts b/client/src/features/book/composables/__tests__/useLibraryBooks.spec.ts index e2328a4f5..0c7b19cf4 100644 --- a/client/src/features/book/composables/__tests__/useLibraryBooks.spec.ts +++ b/client/src/features/book/composables/__tests__/useLibraryBooks.spec.ts @@ -293,10 +293,11 @@ describe('useLibraryBooks', () => { it('includes filter, collapseSeries, and trimmed q in the request body', async () => { const { useLibraryBooks } = await import('../useLibraryBooks') - const libraryId = ref(7) + const libraryId = ref(null) const collapseEnabled = ref(true) const q = ref(' needle ') - const { filter, sort, load } = useLibraryBooks(libraryId, collapseEnabled, q) + const { filter, sort } = useLibraryBooks(libraryId, collapseEnabled, q) + filter.value = { type: 'group', join: 'AND', @@ -305,7 +306,8 @@ describe('useLibraryBooks', () => { sort.value = [{ field: 'author', dir: 'desc' }] mockOkResponse(makePage([], 0)) - await load(true) + libraryId.value = 7 + await new Promise((r) => setTimeout(r, 0)) const [url, init] = fetchMock.mock.calls[0]! expect(url).toBe('/api/v1/libraries/7/books') diff --git a/client/src/features/book/composables/useLibraryBooks.ts b/client/src/features/book/composables/useLibraryBooks.ts index 5019770a1..5299e109c 100644 --- a/client/src/features/book/composables/useLibraryBooks.ts +++ b/client/src/features/book/composables/useLibraryBooks.ts @@ -141,12 +141,8 @@ export function useLibraryBooks(libraryId: Ref, collapseEnabled: pagination.page = 0 } - watch(libraryId, (newId, oldId) => { - if (newId !== oldId) load(true) - }) - watch( - sort, + [libraryId, sort] as const, () => { void load(true) }, diff --git a/client/src/features/koreader/components/KoreaderBookSyncCard.vue b/client/src/features/koreader/components/KoreaderBookSyncCard.vue deleted file mode 100644 index 3ce3f393c..000000000 --- a/client/src/features/koreader/components/KoreaderBookSyncCard.vue +++ /dev/null @@ -1,80 +0,0 @@ - - - diff --git a/client/src/features/koreader/composables/__tests__/useKoreaderBookProgress.spec.ts b/client/src/features/koreader/composables/__tests__/useKoreaderBookProgress.spec.ts index 8306a8f1f..992528cc1 100644 --- a/client/src/features/koreader/composables/__tests__/useKoreaderBookProgress.spec.ts +++ b/client/src/features/koreader/composables/__tests__/useKoreaderBookProgress.spec.ts @@ -80,8 +80,20 @@ describe('useKoreaderBookProgress', () => { expect(loading.value).toBe(false) }) - it('fetchBookProgress sets bookProgress to null on 404', async () => { - apiMock.mockResolvedValueOnce(makeResponse({}, { ok: false, status: 404 })) + it('fetchBookProgress sets bookProgress to null when server returns null (no koreader data)', async () => { + apiMock.mockResolvedValueOnce(makeResponse(null)) + + const { useKoreaderBookProgress } = await import('../useKoreaderBookProgress') + const { bookProgress, fetchBookProgress } = useKoreaderBookProgress() + bookProgress.value = makeBookProgress() + + await fetchBookProgress(42) + + expect(bookProgress.value).toBeNull() + }) + + it('fetchBookProgress sets bookProgress to null on error response', async () => { + apiMock.mockResolvedValueOnce(makeResponse({}, { ok: false, status: 500 })) const { useKoreaderBookProgress } = await import('../useKoreaderBookProgress') const { bookProgress, fetchBookProgress } = useKoreaderBookProgress() diff --git a/client/src/features/koreader/composables/useKoreaderBookProgress.ts b/client/src/features/koreader/composables/useKoreaderBookProgress.ts index eb5db357f..5d06da40b 100644 --- a/client/src/features/koreader/composables/useKoreaderBookProgress.ts +++ b/client/src/features/koreader/composables/useKoreaderBookProgress.ts @@ -10,10 +10,6 @@ export function useKoreaderBookProgress() { loading.value = true try { const res = await api(`/api/v1/koreader/books/${bookId}/progress`) - if (res.status === 404) { - bookProgress.value = null - return - } if (!res.ok) throw new Error('Failed to fetch book progress') bookProgress.value = await res.json() } catch { diff --git a/client/src/features/reader/epub/composables/__tests__/useFoliate.spec.ts b/client/src/features/reader/epub/composables/__tests__/useFoliate.spec.ts index 7a435dc10..1908727a5 100644 --- a/client/src/features/reader/epub/composables/__tests__/useFoliate.spec.ts +++ b/client/src/features/reader/epub/composables/__tests__/useFoliate.spec.ts @@ -98,21 +98,21 @@ describe('useFoliate.open', () => { expect(mockGoToFraction).toHaveBeenCalledWith(0.42) }) - it('does not navigate when cfi is null and fraction is 0', async () => { + it('navigates to position 0 when cfi is null and fraction is 0', async () => { const foliate = useFoliate(() => container) await foliate.open(1, 1, 'epub', null, 0) - expect(mockGoTo).not.toHaveBeenCalled() + expect(mockGoTo).toHaveBeenCalledWith(0) expect(mockGoToFraction).not.toHaveBeenCalled() }) - it('does not navigate when cfi is null and fraction is undefined', async () => { + it('navigates to position 0 when cfi is null and fraction is undefined', async () => { const foliate = useFoliate(() => container) await foliate.open(1, 1, 'epub', null, undefined) - expect(mockGoTo).not.toHaveBeenCalled() + expect(mockGoTo).toHaveBeenCalledWith(0) expect(mockGoToFraction).not.toHaveBeenCalled() }) diff --git a/client/src/features/reader/epub/composables/useFoliate.ts b/client/src/features/reader/epub/composables/useFoliate.ts index 2fd987c4c..5b2d9b5f5 100644 --- a/client/src/features/reader/epub/composables/useFoliate.ts +++ b/client/src/features/reader/epub/composables/useFoliate.ts @@ -136,6 +136,8 @@ export function useFoliate( await view.goTo(cfi).catch(() => {}) } else if (fallbackFraction !== undefined && fallbackFraction > 0) { view.goToFraction?.(fallbackFraction) + } else { + await view.goTo(0).catch(() => {}) } } catch (e) { console.error('[useFoliate]', e) diff --git a/client/src/views/HomeView.vue b/client/src/views/HomeView.vue index 8e2c71a4a..05f6ca008 100644 --- a/client/src/views/HomeView.vue +++ b/client/src/views/HomeView.vue @@ -1,5 +1,5 @@