fix(books): sync book context during lazy loading

Ensure the book context stays in sync when more books are loaded by lazy loading. Stop unregistering the loadMore handler when the list view unmounts so that metadata navigation continues working seamlessly after leaving the primary list view.

Closes #187
This commit is contained in:
neon
2026-06-01 15:54:49 +00:00
parent fe936822ba
commit 6d1cac52b3
2 changed files with 78 additions and 15 deletions
@@ -0,0 +1,69 @@
import { defineComponent, ref } from 'vue'
import { mount } from '@vue/test-utils'
import { afterEach, describe, expect, it, vi } from 'vitest'
import type { BookCard } from '@bookorbit/types'
import { useBookNavigation } from '../useBookNavigation'
import { useBookViewContext } from '../useBookViewContext'
function makeBook(id: number): BookCard {
return {
id,
status: 'active',
title: `Book ${id}`,
authors: [],
seriesName: null,
seriesIndex: null,
files: [],
publishedYear: null,
language: null,
genres: [],
tags: [],
rating: null,
readingProgress: null,
readStatus: null,
addedAt: '2024-01-01T00:00:00Z',
updatedAt: null,
metadataScore: null,
hasCover: false,
hasMetadataLocks: false,
lockedFields: [],
subtitle: null,
publisher: null,
pageCount: null,
isbn13: null,
narrators: [],
}
}
describe('useBookViewContext', () => {
afterEach(() => {
const nav = useBookNavigation()
nav.setBookContext([], 0)
nav.registerLoadMore(null)
})
it('keeps metadata navigation working after the source list view unmounts', async () => {
const nav = useBookNavigation()
const books = ref<BookCard[]>([makeBook(1), makeBook(2)])
const total = ref(4)
const loadMore = vi.fn(async () => {
books.value = [makeBook(1), makeBook(2), makeBook(3), makeBook(4)]
})
const Harness = defineComponent({
setup() {
useBookViewContext(books, total, loadMore)
return {}
},
template: '<div />',
})
const wrapper = mount(Harness)
wrapper.unmount()
const nextId = await nav.getNextId(2)
expect(loadMore).toHaveBeenCalledTimes(1)
expect(nextId).toBe(3)
})
})
@@ -1,28 +1,22 @@
import { onMounted, onUnmounted, watch, type Ref } from 'vue'
import { onMounted, watch, type Ref } from 'vue'
import type { BookCard } from '@bookorbit/types'
import { useBookNavigation } from './useBookNavigation'
export function useBookViewContext(books: Ref<BookCard[]>, total: Ref<number>, loadMore: () => Promise<unknown> | unknown) {
const { setBookContext, registerLoadMore } = useBookNavigation()
const syncBookContext = () => {
setBookContext(
books.value.map((book) => book.id),
total.value,
)
}
watch(
[books, total],
([items, count]) => {
setBookContext(
items.map((book) => book.id),
count,
)
},
{ immediate: true },
)
watch([books, total], () => syncBookContext(), { immediate: true })
onMounted(() => {
registerLoadMore(async () => {
await loadMore()
syncBookContext()
})
})
onUnmounted(() => {
registerLoadMore(null)
})
}