fix file by id in the parent field

This commit is contained in:
2025-11-11 23:53:49 +11:00
parent 5b2090c102
commit 7308e3c97c
6 changed files with 60 additions and 3 deletions

View File

@@ -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)

View File

@@ -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).
*/

View File

@@ -29,6 +29,10 @@ const api: RendererApi = {
async listAudioFiles(): Promise<AudioFileSummary[]> {
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<AudioFileSummary | null> {
return ipcRenderer.invoke(IPC_CHANNELS.libraryGetById, fileId);
},
async listDuplicates(): Promise<{ checksum: string; files: AudioFileSummary[] }[]> {
return ipcRenderer.invoke(IPC_CHANNELS.libraryDuplicates);
},

View File

@@ -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<AudioFileSummary | null>(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 {
<>
<FileDetailPanel
file={selectedFile}
parentFile={parentFile}
parentFile={resolvedParentFile}
categories={library.categories}
onRename={handleRename}
onMove={handleMove}

View File

@@ -369,7 +369,7 @@ export function FileDetailPanel({ file, parentFile, categories, onRename, onMove
onClick={handleOpenParent}
title={parentFile ? `Open source file ${parentFile.displayName}` : 'Open source file'}
>
🔗 {parentFile ? parentFile.customName || parentFile.displayName : `File #${file.parentFileId}`}
🔗 {parentFile ? parentFile.customName || parentFile.displayName : `Source missing (#${file.parentFileId})`}
</button>
) : null}
</div>

View File

@@ -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<import('./models').LibraryScanSummary>;
/** Fetches the current list of audio files. */
listAudioFiles(): Promise<import('./models').AudioFileSummary[]>;
/** Retrieves a single audio file summary by id, or null if it cannot be resolved. */
getAudioFileById(fileId: number): Promise<import('./models').AudioFileSummary | null>;
/** Fetches groups of duplicate files based on checksum. */
listDuplicates(): Promise<{ checksum: string; files: import('./models').AudioFileSummary[] }[]>;
/** Requests a rename for the target file. */