fix(series): preserve book navigation context when navigating away

When transitioning from the series detail view to another view (like edit
metadata), route params update before the component unmounts. This causes
the seriesId computed property to change to null, triggering the seriesId
watcher to reload/reset the books state, which in turn calls setBookContext
with empty values.

Prevent this reset by ignoring null series IDs in both the seriesId watcher
and the books/total watcher, keeping the navigation context intact for the
next view.

Closes #540
This commit is contained in:
neon
2026-07-04 17:03:23 +00:00
parent 66f85cf6af
commit 4df0ed7423
2 changed files with 53 additions and 2 deletions
@@ -481,4 +481,52 @@ describe('SeriesDetailView', () => {
expect(mocks.api).not.toHaveBeenCalled()
expect(wrapper.find('[data-testid="lead-cover-artwork"]').exists()).toBe(false)
})
it('loads all books and navigates to edit metadata, preserving the setBookContext when navigating away', async () => {
const book1 = makeBook({ id: 101 })
const book2 = makeBook({ id: 102 })
mocks.items.value = [book1]
mocks.total.value = 2
mocks.hasMore.value = true
mocks.loadBooks.mockImplementation(async (opts?: unknown) => {
const reset = opts && typeof opts === 'object' && 'reset' in opts ? Boolean((opts as Record<string, unknown>).reset) : false
if (reset) {
mocks.items.value = [book1]
mocks.hasMore.value = true
return
}
mocks.items.value = [book1, book2]
mocks.hasMore.value = false
})
const wrapper = mountView()
await nextTick()
const editBtn = wrapper.findAll('button').find((b) => b.text().includes('Edit Metadata'))
expect(editBtn).toBeDefined()
mocks.setBookContext.mockClear()
mocks.loadBooks.mockClear()
await editBtn!.trigger('click')
await flushPromises()
expect(mocks.loadBooks).toHaveBeenCalled()
expect(mocks.setBookContext).toHaveBeenCalledWith([101, 102], 2)
expect(mocks.routerPush).toHaveBeenCalledWith({
name: 'book-detail',
params: { bookId: 101 },
query: { tab: 'edit' },
})
mocks.setBookContext.mockClear()
mocks.loadBooks.mockClear()
mocks.route.params.seriesId = ''
await nextTick()
expect(mocks.loadBooks).not.toHaveBeenCalled()
expect(mocks.setBookContext).not.toHaveBeenCalled()
})
})
@@ -405,6 +405,7 @@ onUnmounted(() => {
watch(
[books, total],
([newBooks, newTotal]) => {
if (seriesId.value === null) return
setBookContext(
newBooks.map((book) => book.id),
newTotal,
@@ -417,8 +418,10 @@ watch([sort, order, libraryId], () => {
void loadBooks({ reset: true, keepPreviousData: true })
})
watch(seriesId, () => {
void loadBooks({ reset: true })
watch(seriesId, (newSeriesId) => {
if (newSeriesId !== null) {
void loadBooks({ reset: true })
}
})
watch(