Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions fe/src/__tests__/AddNewView.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -768,4 +768,21 @@ describe('AddNewView pagination', () => {
expect(addBtn.text()).toContain('Added')
expect(addBtn.attributes('disabled')).toBeDefined()
})

// Regression: cold-add "root folder not configured" false negative.
// The addToLibrary() guard reads rootFoldersStore.folders synchronously, so
// the store must be populated on mount; otherwise the first add after a fresh
// page load wrongly redirects to /settings.
it('loads root folders on mount so the cold first add is not falsely rejected', async () => {
const apiModule = await import('@/services/api')
const getRootFolders = apiModule.apiService.getRootFolders as unknown as Mock
getRootFolders.mockClear()
getRootFolders.mockResolvedValue([{ id: 1, name: 'Books', path: '/books', isDefault: true }])

const router = createTestRouter()
mount(AddNewView, { global: { plugins: [createPinia(), router] } })
await flushPromises()

expect(getRootFolders).toHaveBeenCalled()
})
})
4 changes: 3 additions & 1 deletion fe/src/stores/rootFolders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ export const useRootFoldersStore = defineStore('rootFolders', () => {
}
} catch (err) {
logger.debug('Failed to load root folders:', err)
folders.value = []
// Preserve any previously-loaded folders on transient failure: a failed
// reload must not masquerade as "no root folders configured" (folders
// starts as [], so a first-ever failure still yields an empty list).
} finally {
loading.value = false
}
Expand Down
5 changes: 5 additions & 0 deletions fe/src/views/content/AddNewView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2998,6 +2998,11 @@ onMounted(async () => {
await configStore.loadApplicationSettings()
await configStore.loadApiConfigurations()

// Ensure root folders are loaded before addToLibrary()'s guard reads them.
// Without this, the first add after a cold page load sees an empty store and
// falsely reports "Root folder not configured" until a redirect populates it.
if (rootFoldersStore.folders.length === 0) await rootFoldersStore.load()

const defaultRegion = normalizeSearchRegion(configStore.applicationSettings?.defaultSearchRegion)
const defaultLanguage = normalizePreferredSearchLanguage(
configStore.applicationSettings?.defaultSearchLanguage,
Expand Down
4 changes: 4 additions & 0 deletions fe/src/views/library/AudiobookDetailView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -1171,6 +1171,10 @@ onMounted(async () => {
syncActiveTabFromRoute()
document.addEventListener('click', handleClickOutside)

// The path preview falls back to rootFoldersStore.defaultFolder; load it so a
// cold page load shows the correct destination rather than the legacy outputPath.
if (rootFoldersStore.folders.length === 0) await rootFoldersStore.load()

await loadAudiobook()

// subscribe to scan job updates
Expand Down
3 changes: 3 additions & 0 deletions fe/src/views/library/AudiobooksView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -1911,6 +1911,9 @@ onMounted(async () => {
libraryStore.fetchLibrary(),
configStore.loadApplicationSettings(),
loadQualityProfiles(),
// hasRootFolderConfigured gates the empty-state CTA on this store; load it
// so an empty library doesn't falsely show "Root Folder Not Configured".
rootFoldersStore.folders.length === 0 ? rootFoldersStore.load() : Promise.resolve(),
])

// Load persisted view mode (if available) before layout calc
Expand Down