refactor: centralize frontend status message handling with useStatusToast

This commit is contained in:
2025-12-07 23:46:10 +01:00
parent b29e856225
commit 595c170c00
13 changed files with 149 additions and 175 deletions
@@ -1,5 +1,6 @@
import { useCallback } from 'react';
import type { Dispatch, MutableRefObject, SetStateAction } from 'react';
import { useStatusToast } from '../../lib/context/StatusToastContext';
import { DEFAULT_FOLDER_NAME } from '../../app/workspaceUtils';
import { getEntryId, isDocumentEntry } from '../../app/entryKey';
import {
@@ -19,8 +20,6 @@ import type { Document, MessageOptions } from '../../types/documents';
type FolderId = FolderIdentifier | 'root';
type NullableFolderId = FolderId | null;
type StatusLevel = 'success' | 'error' | 'info' | string;
type DocumentCacheMapper = (
doc: Document | null,
) => Document | null;
@@ -43,8 +42,6 @@ type CloseDocumentPreview = () => void;
type NotifyApiError = (error: unknown, fallbackMessage?: string) => void;
type SetStatusMessage = (message: string, level?: StatusLevel) => void;
interface Tag {
id: DocumentId;
label: string;
@@ -94,7 +91,6 @@ interface UseDocumentMutationsArgs {
setFocusedEntryKey: Dispatch<SetStateAction<string | null>>;
focusedEntryKey: string | null;
notifyApiError: NotifyApiError;
setStatusMessage: SetStatusMessage;
mapDocumentCaches: MapDocumentCaches;
folderNodes: Map<FolderId, FolderNode>;
setFolderNodes: Dispatch<SetStateAction<Map<FolderId, FolderNode>>>;
@@ -165,7 +161,6 @@ const useDocumentMutations = ({
setFocusedEntryKey,
focusedEntryKey,
notifyApiError,
setStatusMessage,
mapDocumentCaches,
folderNodes,
setFolderNodes,
@@ -181,6 +176,8 @@ const useDocumentMutations = ({
extractDocumentFromResponse,
ingestDocuments,
}: UseDocumentMutationsArgs): UseDocumentMutationsResult => {
const { showToast } = useStatusToast();
const moveDocumentsToFolder = useCallback(
async (documentIds: Array<DocumentId | Document>, targetFolderId?: NullableFolderId) => {
const uniqueIds = Array.from(
@@ -254,7 +251,7 @@ const useDocumentMutations = ({
const count = uniqueIds.length;
const suffix = count === 1 ? '' : 's';
setStatusMessage(`Moved ${count} document${suffix} to ${targetLabel}.`, 'success');
showToast(`Moved ${count} document${suffix} to ${targetLabel}.`, 'success');
if (updatedDocsMap.size) {
mapDocumentCaches((doc) => {
@@ -335,7 +332,7 @@ const useDocumentMutations = ({
setFocusedEntryKey,
focusedEntryKey,
notifyApiError,
setStatusMessage,
showToast,
mapDocumentCaches,
],
);
@@ -343,19 +340,19 @@ const useDocumentMutations = ({
const handleThumbnailRegeneration = useCallback(
async (documentId: DocumentId) => {
if (!token) {
setStatusMessage('Log in to manage assets.', 'error');
showToast('Log in to manage assets.', 'error');
return;
}
try {
await queueDocumentReanalysis(documentId, { force: true });
setStatusMessage('Document re-analysis queued.', 'info');
showToast('Document re-analysis queued.', 'info');
await refreshCurrentFolder();
} catch (error) {
const message = (error as Record<string, any>)?.response?.data?.error || 'Failed to request thumbnail generation.';
notifyApiError(error, message);
}
},
[token, refreshCurrentFolder, notifyApiError, setStatusMessage],
[token, refreshCurrentFolder, notifyApiError, showToast],
);
const handleDocumentsDelete = useCallback(
@@ -365,7 +362,7 @@ const useDocumentMutations = ({
}
if (!token) {
setStatusMessage('Log in to manage documents.', 'error');
showToast('Log in to manage documents.', 'error');
return false;
}
@@ -380,7 +377,7 @@ const useDocumentMutations = ({
if (showMessage) {
const message = documentIds.length === 1 ? 'Document deleted.' : 'Documents deleted.';
setStatusMessage(message, 'success');
showToast(message, 'success');
}
return true;
} catch (error) {
@@ -396,7 +393,7 @@ const useDocumentMutations = ({
previewDocumentId,
closeDocumentPreview,
notifyApiError,
setStatusMessage,
showToast,
],
);
@@ -404,7 +401,7 @@ const useDocumentMutations = ({
async (documentId: DocumentId, nextTitle: string) => {
const trimmed = nextTitle?.trim?.() || '';
if (!trimmed) {
setStatusMessage('Document title cannot be empty.', 'error');
showToast('Document title cannot be empty.', 'error');
return false;
}
try {
@@ -422,7 +419,7 @@ const useDocumentMutations = ({
});
}
setStatusMessage('Document title updated.', 'success');
showToast('Document title updated.', 'success');
return true;
} catch (error) {
const message = (error as Record<string, any>)?.response?.data?.error || 'Failed to update document title.';
@@ -434,7 +431,7 @@ const useDocumentMutations = ({
extractDocumentFromResponse,
ingestDocuments,
notifyApiError,
setStatusMessage,
showToast,
updateDocumentCaches,
],
);
@@ -458,7 +455,7 @@ const useDocumentMutations = ({
}
const message = payload.issued_at ? 'Issued date updated.' : 'Issued date cleared.';
setStatusMessage(message, 'success');
showToast(message, 'success');
return true;
} catch (error) {
const message = (error as Record<string, any>)?.response?.data?.error || 'Failed to update issued date.';
@@ -470,7 +467,7 @@ const useDocumentMutations = ({
extractDocumentFromResponse,
ingestDocuments,
notifyApiError,
setStatusMessage,
showToast,
updateDocumentCaches,
],
);
@@ -505,7 +502,7 @@ const useDocumentMutations = ({
}
return { ...doc, tags: [...currentTags, cachedTag] };
});
setStatusMessage('Tag assigned.', 'success');
showToast('Tag assigned.', 'success');
return true;
} catch (error) {
const message = (error as Record<string, any>)?.response?.data?.error || 'Failed to assign tag.';
@@ -513,7 +510,7 @@ const useDocumentMutations = ({
return false;
}
},
[notifyApiError, setStatusMessage, updateDocumentCaches],
[notifyApiError, showToast, updateDocumentCaches],
);
const handleDocumentTagAdd = useCallback(
@@ -616,7 +613,7 @@ const useDocumentMutations = ({
try {
await deleteDocumentTag(documentId, tagId);
applyTagRemovalToCaches(documentId, tagId);
setStatusMessage('Tag removed.', 'success');
showToast('Tag removed.', 'success');
return true;
} catch (error) {
const message = (error as Record<string, any>)?.response?.data?.error || 'Failed to remove tag.';
@@ -624,20 +621,20 @@ const useDocumentMutations = ({
return false;
}
},
[applyTagRemovalToCaches, notifyApiError, setStatusMessage],
[applyTagRemovalToCaches, notifyApiError, showToast],
);
const handleFolderDelete = useCallback(
async (folderId?: FolderId, { showMessage = true }: MessageOptions = {}) => {
if (!token) {
if (showMessage) {
setStatusMessage('Log in to manage folders.', 'error');
showToast('Log in to manage folders.', 'error');
}
return false;
}
if (!folderId || folderId === 'root') {
if (showMessage) {
setStatusMessage('The root folder cannot be removed.', 'error');
showToast('The root folder cannot be removed.', 'error');
}
return false;
}
@@ -648,7 +645,7 @@ const useDocumentMutations = ({
const hasDocs = (contents.documents || []).length > 0;
if (hasChildren || hasDocs) {
if (showMessage) {
setStatusMessage('Folder must be empty before it can be deleted.', 'error');
showToast('Folder must be empty before it can be deleted.', 'error');
}
return false;
}
@@ -686,14 +683,14 @@ const useDocumentMutations = ({
}
if (showMessage) {
setStatusMessage('Folder deleted.', 'success');
showToast('Folder deleted.', 'success');
}
return true;
} catch (error) {
const message = (error as Record<string, any>)?.response?.data?.error || 'Failed to delete folder.';
notifyApiError(error, message);
if (showMessage) {
setStatusMessage(message, 'error');
showToast(message, 'error');
}
return false;
}
@@ -706,7 +703,7 @@ const useDocumentMutations = ({
setSelectedFolder,
setFolderNodes,
notifyApiError,
setStatusMessage,
showToast,
],
);