From 7308e3c97cecb60376549a62bdfb4f47ec09e85d Mon Sep 17 00:00:00 2001 From: Max Litruv Boonzaayer Date: Tue, 11 Nov 2025 23:53:49 +1100 Subject: [PATCH] fix file by id in the parent field --- src/main/MainApp.ts | 3 ++ src/main/services/LibraryService.ts | 14 +++++++ src/preload/index.ts | 4 ++ src/renderer/src/App.tsx | 37 ++++++++++++++++++- .../src/components/FileDetailPanel.tsx | 2 +- src/shared/ipc.ts | 3 ++ 6 files changed, 60 insertions(+), 3 deletions(-) diff --git a/src/main/MainApp.ts b/src/main/MainApp.ts index 1b517e9..9e8240f 100644 --- a/src/main/MainApp.ts +++ b/src/main/MainApp.ts @@ -225,6 +225,9 @@ export class MainApp { ipcMain.handle(IPC_CHANNELS.libraryScan, async () => this.requireLibrary().scanLibrary()); ipcMain.handle(IPC_CHANNELS.libraryList, async () => this.requireLibrary().listFiles()); + ipcMain.handle(IPC_CHANNELS.libraryGetById, async (_event: IpcMainInvokeEvent, fileId: number) => + this.requireLibrary().getFileById(fileId) + ); ipcMain.handle(IPC_CHANNELS.libraryDuplicates, async () => this.requireLibrary().listDuplicates()); ipcMain.handle(IPC_CHANNELS.searchQuery, async (_event: IpcMainInvokeEvent, query: string) => this.requireSearch().search(query) diff --git a/src/main/services/LibraryService.ts b/src/main/services/LibraryService.ts index 581fd8f..c820796 100644 --- a/src/main/services/LibraryService.ts +++ b/src/main/services/LibraryService.ts @@ -214,6 +214,20 @@ export class LibraryService { return this.database.listFiles(); } + /** + * Attempts to resolve a file summary by id. Returns null when the record no longer exists. + */ + public getFileById(fileId: number): AudioFileSummary | null { + try { + return this.database.getFileById(fileId); + } catch (error) { + if (error instanceof Error) { + console.warn(`Failed to resolve file by id ${fileId}:`, error.message); + } + return null; + } + } + /** * Returns groups of files that have identical checksums (potential duplicates). */ diff --git a/src/preload/index.ts b/src/preload/index.ts index ba63ac5..66af901 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -29,6 +29,10 @@ const api: RendererApi = { async listAudioFiles(): Promise { return ipcRenderer.invoke(IPC_CHANNELS.libraryList); }, + /** Retrieves a single audio file summary by id, returning null when the record no longer exists. */ + async getAudioFileById(fileId: number): Promise { + return ipcRenderer.invoke(IPC_CHANNELS.libraryGetById, fileId); + }, async listDuplicates(): Promise<{ checksum: string; files: AudioFileSummary[] }[]> { return ipcRenderer.invoke(IPC_CHANNELS.libraryDuplicates); }, diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx index 2a929a2..c380a4d 100644 --- a/src/renderer/src/App.tsx +++ b/src/renderer/src/App.tsx @@ -69,7 +69,7 @@ function App(): JSX.Element { [library.files, library.selectedFileId] ); - const parentFile = useMemo(() => { + const parentFileFromStore = useMemo(() => { const parentId = selectedFile?.parentFileId ?? null; if (parentId === null) { return null; @@ -77,6 +77,39 @@ function App(): JSX.Element { return library.files.find((file) => file.id === parentId) ?? null; }, [library.files, selectedFile?.parentFileId]); + const [resolvedParentFile, setResolvedParentFile] = useState(parentFileFromStore); + + useEffect(() => { + const parentId = selectedFile?.parentFileId ?? null; + if (parentId === null) { + setResolvedParentFile(null); + return; + } + if (parentFileFromStore) { + setResolvedParentFile(parentFileFromStore); + return; + } + + let cancelled = false; + (async () => { + try { + const fetched = await window.api.getAudioFileById(parentId); + if (!cancelled) { + setResolvedParentFile(fetched ?? null); + } + } catch (error) { + console.warn('Unable to resolve parent file details', { parentId, error }); + if (!cancelled) { + setResolvedParentFile(null); + } + } + })(); + + return () => { + cancelled = true; + }; + }, [parentFileFromStore, selectedFile?.parentFileId]); + const selectedFiles = useMemo( () => library.files.filter((file) => library.selectedFileIds.has(file.id)), [library.files, library.selectedFileIds] @@ -307,7 +340,7 @@ function App(): JSX.Element { <> - 🔗 {parentFile ? parentFile.customName || parentFile.displayName : `File #${file.parentFileId}`} + 🔗 {parentFile ? parentFile.customName || parentFile.displayName : `Source missing (#${file.parentFileId})`} ) : null} diff --git a/src/shared/ipc.ts b/src/shared/ipc.ts index b1d459a..7bfa275 100644 --- a/src/shared/ipc.ts +++ b/src/shared/ipc.ts @@ -5,6 +5,7 @@ export const IPC_CHANNELS = { dialogSelectLibrary: 'dialog:select-library', libraryScan: 'library:scan', libraryList: 'library:list', + libraryGetById: 'library:get-by-id', libraryDuplicates: 'library:duplicates', libraryRename: 'library:rename', libraryMove: 'library:move', @@ -39,6 +40,8 @@ export interface RendererApi { rescanLibrary(): Promise; /** Fetches the current list of audio files. */ listAudioFiles(): Promise; + /** Retrieves a single audio file summary by id, or null if it cannot be resolved. */ + getAudioFileById(fileId: number): Promise; /** Fetches groups of duplicate files based on checksum. */ listDuplicates(): Promise<{ checksum: string; files: import('./models').AudioFileSummary[] }[]>; /** Requests a rename for the target file. */