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
+6 -6
View File
@@ -1,5 +1,6 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import type { ReactNode } from 'react';
import { useStatusToast } from '../lib/context/StatusToastContext';
import TagsPanel from '../tags/TagsPanel';
import CorrespondentsPanel, { CorrespondentsPanelProps } from '../correspondents/CorrespondentsPanel';
import PanelHeader from '../components/PanelHeader';
@@ -30,7 +31,6 @@ interface UseManagementModalsArgs {
onCorrespondentCreate?: (...args: any[]) => void | Promise<void>;
onCorrespondentUpdate?: (...args: any[]) => void | Promise<void>;
onCorrespondentDelete?: (...args: any[]) => void | Promise<void>;
setStatusMessage?: (message: string, variant?: string) => void;
}
interface UseManagementModalsResult {
@@ -53,8 +53,8 @@ export const useManagementModals = ({
onCorrespondentCreate,
onCorrespondentUpdate,
onCorrespondentDelete,
setStatusMessage,
}: UseManagementModalsArgs): UseManagementModalsResult => {
const { showToast } = useStatusToast();
const [activeModal, setActiveModal] = useState<string | null>(null);
const openTagsModal = useCallback(() => setActiveModal(TAGS_MODAL), []);
@@ -117,7 +117,7 @@ export const useManagementModals = ({
onCreateTag={onTagCreate}
onUpdateTag={onTagUpdate}
onDeleteTag={onTagDelete}
onNotify={setStatusMessage}
onNotify={showToast}
/>
</div>
</div>
@@ -130,7 +130,7 @@ export const useManagementModals = ({
onTagDelete,
onTagUpdate,
refreshTags,
setStatusMessage,
showToast,
tags,
]);
@@ -199,7 +199,7 @@ export const useManagementModals = ({
onCreate={handleCorrespondentCreateSafe}
onUpdate={handleCorrespondentUpdateSafe}
onDelete={handleCorrespondentDeleteSafe}
onNotify={setStatusMessage}
onNotify={showToast}
/>
</div>
</div>
@@ -213,7 +213,7 @@ export const useManagementModals = ({
handleCorrespondentDeleteSafe,
handleCorrespondentUpdateSafe,
refreshCorrespondents,
setStatusMessage,
showToast,
]);
const managementModals = (
@@ -1,18 +1,16 @@
import { useCallback, useEffect, useRef } from 'react';
import type { MutableRefObject } from 'react';
import { clearAuthToken, logoutSession, refreshSession, setAuthToken } from '../../lib/api/apiClient';
import { useStatusToast } from '../../lib/context/StatusToastContext';
type AppStatus = string;
type AppDispatch = (action: { type: string; [key: string]: unknown }) => void;
type SetStatusMessage = (message: string, variant?: string) => void;
type AppDispatch = (action: { type: string;[key: string]: unknown }) => void;
interface UseAuthManagerArgs {
token?: string | null;
appStatus: AppStatus;
appDispatch: AppDispatch;
setStatusMessage: SetStatusMessage;
}
interface UseAuthManagerResult {
@@ -25,10 +23,10 @@ const useAuthManager = ({
token,
appStatus,
appDispatch,
setStatusMessage,
}: UseAuthManagerArgs): UseAuthManagerResult => {
const tokenRef = useRef<string | null>(token);
const initialRefreshAttemptedRef = useRef(Boolean(token));
const { showToast } = useStatusToast();
const refreshAccessToken = useCallback(async (): Promise<string> => {
console.log('[Auth] Attempting to refresh access token…');
@@ -61,7 +59,7 @@ const useAuthManager = ({
if (!token && !initialRefreshAttemptedRef.current && appStatus === 'logged-out') {
initialRefreshAttemptedRef.current = true;
console.log('[Auth] Attempting refresh at startup');
refreshAccessToken().catch(() => {});
refreshAccessToken().catch(() => { });
}
}, [token, appStatus, refreshAccessToken]);
@@ -73,9 +71,9 @@ const useAuthManager = ({
} finally {
clearAuthToken();
appDispatch({ type: 'LOGOUT' });
setStatusMessage('Logged out.', 'info');
showToast('Logged out.', 'info');
}
}, [appDispatch, setStatusMessage]);
}, [appDispatch, showToast]);
return { tokenRef, refreshAccessToken, handleLogout };
};
@@ -1,4 +1,5 @@
import { useCallback } from 'react';
import { useStatusToast } from '../../lib/context/StatusToastContext';
import { assignCorrespondentsBulk } from '../../lib/api/apiClient';
import type { Identifier } from '../../types/identifiers';
import type { MessageOptions } from '../../types/documents';
@@ -16,7 +17,6 @@ interface UseBulkDocumentActionsArgs {
resolveTargetDocumentIds: (ids?: Identifier[] | null) => Identifier[];
correspondentLookupByName: Map<string, { id?: Identifier }>;
handleCorrespondentCreate: (payload: { name: string }) => Promise<{ id?: Identifier } | null>;
setStatusMessage: (message: string, variant?: string) => void;
selectedDocumentIds?: Identifier[];
selectedFolderIds?: Identifier[];
handleDocumentsDelete: (ids: Identifier[], options?: MessageOptions) => Promise<boolean>;
@@ -29,7 +29,6 @@ const useBulkDocumentActions = ({
resolveTargetDocumentIds,
correspondentLookupByName,
handleCorrespondentCreate,
setStatusMessage,
selectedDocumentIds,
selectedFolderIds,
handleDocumentsDelete,
@@ -37,16 +36,18 @@ const useBulkDocumentActions = ({
clearDocumentSelection,
updateDocumentCaches,
}: UseBulkDocumentActionsArgs) => {
const { showToast } = useStatusToast();
const handleBulkCorrespondentAdd = useCallback(
async ({ name, input, documentIds }: { name?: string; input?: HTMLInputElement | null; documentIds?: Identifier[] }) => {
const trimmed = name?.trim?.() || '';
if (!trimmed) {
setStatusMessage('Correspondent name is required.', 'error');
showToast('Correspondent name is required.', 'error');
return;
}
const targets = resolveTargetDocumentIds(documentIds);
if (!targets.length) {
setStatusMessage('Select documents before assigning correspondents.', 'error');
showToast('Select documents before assigning correspondents.', 'error');
return;
}
@@ -60,7 +61,7 @@ const useBulkDocumentActions = ({
}
if (!target?.id) {
setStatusMessage('Unable to resolve correspondent.', 'error');
showToast('Unable to resolve correspondent.', 'error');
return;
}
@@ -94,12 +95,12 @@ const useBulkDocumentActions = ({
const assignedSuffix = assigned === 1 ? '' : 's';
if (removed > 0) {
const removedSuffix = removed === 1 ? '' : 's';
setStatusMessage(
showToast(
`Correspondent assigned (${assigned}) and replaced ${removed} link${removedSuffix}.`,
'success',
);
} else {
setStatusMessage(
showToast(
`Correspondent assigned to ${assigned} document${assignedSuffix}.`,
'success',
);
@@ -113,7 +114,7 @@ const useBulkDocumentActions = ({
correspondentLookupByName,
handleCorrespondentCreate,
resolveTargetDocumentIds,
setStatusMessage,
showToast,
updateDocumentCaches,
],
);
@@ -121,14 +122,14 @@ const useBulkDocumentActions = ({
const handleBulkCorrespondentRemove = useCallback(
async ({ assignments = [], documentIds }: { assignments?: CorrespondentAssignment[]; documentIds?: Identifier[] }) => {
if (!assignments.length) {
setStatusMessage('Select a correspondent to remove.', 'error');
showToast('Select a correspondent to remove.', 'error');
return;
}
const targets = resolveTargetDocumentIds(documentIds);
if (!targets.length) {
setStatusMessage('Select documents before removing correspondents.', 'error');
showToast('Select documents before removing correspondents.', 'error');
return;
}
@@ -162,18 +163,18 @@ const useBulkDocumentActions = ({
if (removed > 0) {
const removedSuffix = removed === 1 ? '' : 's';
setStatusMessage(
showToast(
`Correspondent removed from ${removed} link${removedSuffix}.`,
'success',
);
} else if (assigned > 0) {
const assignedSuffix = assigned === 1 ? '' : 's';
setStatusMessage(`Correspondent updated ${assigned} link${assignedSuffix}.`, 'info');
showToast(`Correspondent updated ${assigned} link${assignedSuffix}.`, 'info');
} else {
setStatusMessage('No correspondents changed.', 'info');
showToast('No correspondents changed.', 'info');
}
},
[resolveTargetDocumentIds, setStatusMessage, updateDocumentCaches],
[resolveTargetDocumentIds, showToast, updateDocumentCaches],
);
const handleDeleteSelection = useCallback(async () => {
@@ -217,7 +218,7 @@ const useBulkDocumentActions = ({
}
if (!docsOk || !foldersOk) {
setStatusMessage('Some items could not be deleted. Ensure folders are empty before deletion.', 'error');
showToast('Some items could not be deleted. Ensure folders are empty before deletion.', 'error');
return;
}
@@ -231,14 +232,14 @@ const useBulkDocumentActions = ({
successParts.push(folderIds.length === 1 ? 'Folder deleted.' : 'Folders deleted.');
}
setStatusMessage(successParts.join(' '), 'success');
showToast(successParts.join(' '), 'success');
}, [
clearDocumentSelection,
handleDocumentsDelete,
handleFolderDelete,
selectedDocumentIds,
selectedFolderIds,
setStatusMessage,
showToast,
]);
return {
@@ -1,22 +1,22 @@
import { MutableRefObject, useCallback, useState } from 'react';
import { useStatusToast } from '../../lib/context/StatusToastContext';
import type { Correspondent } from '../../types/documents';
import { listCorrespondents, createCorrespondent, updateCorrespondent, deleteCorrespondent } from '../../lib/api/apiClient';
interface UseCorrespondentsOptions {
notifyApiError: (error: unknown, fallback: string) => void;
setStatusMessage: (message: string, variant?: string) => void;
tenantIdRef: MutableRefObject<string | null>;
mapDocumentCaches?: (mapper: (doc: any) => any) => void;
}
const useCorrespondents = ({
notifyApiError,
setStatusMessage,
tenantIdRef,
mapDocumentCaches,
}: UseCorrespondentsOptions) => {
const [correspondents, setCorrespondents] = useState<Correspondent[]>([]);
const { showToast } = useStatusToast();
const refreshCorrespondents = useCallback(async () => {
const requestTenantId = tenantIdRef.current;
@@ -56,7 +56,7 @@ const useCorrespondents = ({
try {
await updateCorrespondent(correspondentId, payload);
await refreshCorrespondents();
setStatusMessage('Correspondent updated.', 'success');
showToast('Correspondent updated.', 'success');
return true;
} catch (error) {
const message = error.response?.data?.error || 'Failed to update correspondent.';
@@ -64,7 +64,7 @@ const useCorrespondents = ({
throw new Error(message);
}
},
[notifyApiError, refreshCorrespondents, setStatusMessage],
[notifyApiError, refreshCorrespondents, showToast],
);
const handleCorrespondentCreate = useCallback(
@@ -76,7 +76,7 @@ const useCorrespondents = ({
try {
const data = await createCorrespondent({ name: trimmed });
await refreshCorrespondents();
setStatusMessage('Correspondent created.', 'success');
showToast('Correspondent created.', 'success');
return data;
} catch (error) {
const message = error.response?.data?.error || 'Failed to create correspondent.';
@@ -84,7 +84,7 @@ const useCorrespondents = ({
throw new Error(message);
}
},
[notifyApiError, refreshCorrespondents, setStatusMessage],
[notifyApiError, refreshCorrespondents, showToast],
);
const handleCorrespondentDelete = useCallback(
@@ -110,7 +110,7 @@ const useCorrespondents = ({
mapDocumentCaches?.(stripFromDoc);
setStatusMessage('Correspondent deleted.', 'success');
showToast('Correspondent deleted.', 'success');
return true;
} catch (error) {
const message = error.response?.data?.error || 'Failed to delete correspondent.';
@@ -118,7 +118,7 @@ const useCorrespondents = ({
throw new Error(message);
}
},
[mapDocumentCaches, notifyApiError, refreshCorrespondents, setStatusMessage],
[mapDocumentCaches, notifyApiError, refreshCorrespondents, showToast],
);
return {
@@ -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,
],
);
@@ -38,7 +38,7 @@ import {
isDocumentEntry
} from '../../app/entryKey';
import useDocumentsSearch from '../../app/useDocumentsSearch';
import { useStatusToast, type ToastVariant } from '../../lib/context/StatusToastContext';
import { useStatusToast } from '../../lib/context/StatusToastContext';
import useAuthManager from './useAuthManager';
import useTenantManager from './useTenantManager';
import useDocuments from './useDocuments';
@@ -144,33 +144,26 @@ const useDocumentsWorkspace = ({
? (tenantOptionsRaw as TenantOption[])
: [];
const { showToast } = useStatusToast();
const setStatusMessage = useCallback(
(message?: string | null, variant: ToastVariant = 'info') => {
if (message) {
showToast(message, variant);
}
},
[showToast],
);
const handleApiReport = useCallback(
({ message, variant }) => setStatusMessage(message, variant),
[setStatusMessage],
);
const reportApiError = useApiError({
onReport: handleApiReport,
onReport: useCallback(
({ message, variant }) => showToast(message, variant),
[showToast],
),
});
const notifyApiError = useCallback(
(error, fallbackMessage, variant = 'error') =>
reportApiError(error, { message: fallbackMessage, variant }),
[reportApiError],
);
const [creatingFolder, setCreatingFolder] = useState(false);
const { tokenRef, handleLogout } = useAuthManager({
token,
appStatus,
appDispatch,
setStatusMessage,
});
@@ -514,9 +507,8 @@ const useDocumentsWorkspace = ({
setTags,
} = useTags({
notifyApiError,
setStatusMessage,
tagManager,
tenantIdRef,
tagManager,
setActiveTagFilters,
mapDocumentCaches,
});
@@ -544,7 +536,6 @@ const useDocumentsWorkspace = ({
setCorrespondents,
} = useCorrespondents({
notifyApiError,
setStatusMessage,
tenantIdRef,
mapDocumentCaches,
});
@@ -558,7 +549,6 @@ const useDocumentsWorkspace = ({
correspondents,
handleCorrespondentCreate,
notifyApiError,
setStatusMessage,
updateDocumentCaches,
});
@@ -573,7 +563,6 @@ const useDocumentsWorkspace = ({
revokePasskey,
} = usePasskeys({
notifyApiError,
setStatusMessage,
token,
});
@@ -606,7 +595,6 @@ const useDocumentsWorkspace = ({
refreshTags,
resolveTargetDocumentIds,
notifyApiError,
setStatusMessage,
updateDocumentCaches,
});
@@ -625,7 +613,6 @@ const useDocumentsWorkspace = ({
refreshCurrentFolder,
shellRef,
notifyApiError,
setStatusMessage,
});
const {
@@ -761,7 +748,6 @@ const useDocumentsWorkspace = ({
setFocusedEntryKey,
focusedEntryKey,
notifyApiError,
setStatusMessage,
mapDocumentCaches,
folderNodes,
setFolderNodes,
@@ -792,7 +778,6 @@ const useDocumentsWorkspace = ({
selectedFolder,
setSelectedFolder,
notifyApiError,
setStatusMessage,
navigate,
handleFileDrop,
moveDocumentsToFolder,
@@ -905,7 +890,6 @@ const useDocumentsWorkspace = ({
resolveTargetDocumentIds,
correspondentLookupByName,
handleCorrespondentCreate,
setStatusMessage,
selectedDocumentIds,
selectedFolderIds,
handleDocumentsDelete,
@@ -914,8 +898,6 @@ const useDocumentsWorkspace = ({
updateDocumentCaches,
});
const ensureAssetUrl = useCallback(
async (documentId, asset, { force = false } = {}) => {
if (!documentId || !asset?.id) {
@@ -942,8 +924,6 @@ const useDocumentsWorkspace = ({
[assetManager, updateDocumentCaches, notifyApiError],
);
const handlePromptCreateFolder = useCallback(async (parentId?: Identifier | null) => {
if (creatingFolder) {
return;
@@ -954,19 +934,19 @@ const useDocumentsWorkspace = ({
}
const trimmed = input.trim();
if (!trimmed) {
setStatusMessage('Folder name cannot be empty.', 'error');
showToast('Folder name cannot be empty.', 'error');
return;
}
setCreatingFolder(true);
try {
const success = await handleFolderCreate(trimmed, parentId);
if (!success) {
setStatusMessage('Unable to create folder. Check the status message for details.', 'error');
showToast('Unable to create folder. Check the status message for details.', 'error');
}
} finally {
setCreatingFolder(false);
}
}, [creatingFolder, handleFolderCreate, setStatusMessage]);
}, [creatingFolder, handleFolderCreate, showToast]);
const { managementModals, openTagsModal, openCorrespondentsModal } = useManagementModals({
locationPathname: location.pathname,
@@ -980,7 +960,6 @@ const useDocumentsWorkspace = ({
onCorrespondentCreate: handleCorrespondentCreate,
onCorrespondentUpdate: handleCorrespondentUpdate,
onCorrespondentDelete: handleCorrespondentDelete,
setStatusMessage,
});
const [settingsOpen, setSettingsOpen] = useState(false);
@@ -1126,7 +1105,6 @@ const useDocumentsWorkspace = ({
appDispatch,
currentTenantId,
resetWorkspaceState,
setStatusMessage,
notifyApiError,
refreshTags,
refreshCorrespondents,
@@ -1148,7 +1126,6 @@ const useDocumentsWorkspace = ({
};
const uiContext = {
setStatusMessage,
notifyApiError,
settingsOpen,
openSettings,
+8 -8
View File
@@ -1,4 +1,5 @@
import { MutableRefObject, useCallback, useState } from 'react';
import { useStatusToast } from '../../lib/context/StatusToastContext';
import type { TagId, TenantId } from '../../types/identifiers';
import type { Tag } from '../../types/documents';
@@ -11,7 +12,6 @@ interface TagManagerInterface {
interface UseTagsOptions {
// apiClient removed
notifyApiError: (error: unknown, fallback: string) => void;
setStatusMessage: (message: string, variant?: string) => void;
tagManager: TagManagerInterface;
tenantIdRef: MutableRefObject<TenantId | null>;
setActiveTagFilters: (updater: (prev: Array<TagId>) => Array<TagId>) => void;
@@ -21,13 +21,13 @@ interface UseTagsOptions {
const useTags = ({
// apiClient removed
notifyApiError,
setStatusMessage,
tagManager,
tenantIdRef,
setActiveTagFilters,
mapDocumentCaches,
}: UseTagsOptions) => {
const [tags, setTags] = useState<Tag[]>([]);
const { showToast } = useStatusToast();
const refreshTags = useCallback(async () => {
const requestTenantId = tenantIdRef.current;
@@ -66,7 +66,7 @@ const useTags = ({
try {
await updateTag(tagId, payload);
await refreshTags();
setStatusMessage('Tag updated.', 'success');
showToast('Tag updated.', 'success');
return true;
} catch (error) {
const message = error.response?.data?.error || 'Failed to update tag.';
@@ -74,7 +74,7 @@ const useTags = ({
throw new Error(message);
}
},
[notifyApiError, refreshTags, setStatusMessage],
[notifyApiError, refreshTags, showToast],
);
const handleTagCreate = useCallback(
@@ -83,14 +83,14 @@ const useTags = ({
try {
await createTag(payload);
await refreshTags();
setStatusMessage('Tag created.', 'success');
showToast('Tag created.', 'success');
} catch (error) {
const message = error.response?.data?.error || 'Failed to create tag.';
notifyApiError(error, message);
throw new Error(message);
}
},
[notifyApiError, refreshTags, setStatusMessage, tagManager],
[notifyApiError, refreshTags, showToast, tagManager],
);
const handleTagDelete = useCallback(
@@ -117,7 +117,7 @@ const useTags = ({
mapDocumentCaches?.(stripTagFromDoc);
await refreshTags();
setStatusMessage('Tag deleted.', 'success');
showToast('Tag deleted.', 'success');
return true;
} catch (error) {
const message = error.response?.data?.error || 'Failed to delete tag.';
@@ -125,7 +125,7 @@ const useTags = ({
throw new Error(message);
}
},
[mapDocumentCaches, notifyApiError, refreshTags, setActiveTagFilters, setStatusMessage],
[mapDocumentCaches, notifyApiError, refreshTags, setActiveTagFilters, showToast],
);
return {
@@ -1,6 +1,7 @@
import { MutableRefObject, useCallback } from 'react';
import type { NavigateFunction } from 'react-router-dom';
import type { FolderId, TenantId } from '../../types/identifiers';
import { useStatusToast } from '../../lib/context/StatusToastContext';
import { api, listTenants, switchTenant } from '../../lib/api/apiClient';
@@ -13,7 +14,6 @@ interface UseTenantManagerOptions {
appDispatch: (action: any) => void;
currentTenantId: TenantId | null;
resetWorkspaceState: () => void;
setStatusMessage: (message: string, variant?: string) => void;
notifyApiError: (error: unknown, message: string) => void;
refreshTags: () => Promise<void>;
refreshCorrespondents: () => Promise<void>;
@@ -28,7 +28,6 @@ const useTenantManager = ({
appDispatch,
currentTenantId,
resetWorkspaceState,
setStatusMessage,
notifyApiError,
refreshTags,
refreshCorrespondents,
@@ -38,6 +37,7 @@ const useTenantManager = ({
tokenRef,
tenantIdRef,
}: UseTenantManagerOptions) => {
const { showToast } = useStatusToast();
const handleTenantSelect = useCallback(
async (tenantOption: TenantOption | null, { refreshOnly = false }: { refreshOnly?: boolean } = {}) => {
const requestedTenantId = tenantOption?.id ?? null;
@@ -88,7 +88,7 @@ const useTenantManager = ({
await loadFolder('root', { preserveSearch: false });
const tenantLabel = data?.tenant?.name || data?.tenant?.id || 'tenant';
setStatusMessage(`Switched to ${tenantLabel}.`, 'info');
showToast(`Switched to ${tenantLabel}.`, 'info');
} catch (error) {
notifyApiError(error, 'Failed to switch tenant.');
}
@@ -103,7 +103,7 @@ const useTenantManager = ({
refreshCorrespondents,
refreshTags,
resetWorkspaceState,
setStatusMessage,
showToast,
tenantIdRef,
tokenRef,
],
@@ -1,4 +1,5 @@
import { useCallback, useMemo } from 'react';
import { useStatusToast } from '../../../lib/context/StatusToastContext';
import type { Identifier } from '../../../types/identifiers';
@@ -14,7 +15,6 @@ interface UseDocumentCorrespondentActionsArgs {
correspondents: CorrespondentOption[];
handleCorrespondentCreate: (payload: { name: string }) => Promise<CorrespondentOption | null>;
notifyApiError: (error: unknown, fallback: string) => void;
setStatusMessage: (message: string, variant?: string) => void;
updateDocumentCaches?: (
id: Identifier,
updater: (doc: { correspondents?: CorrespondentOption[] } | null) => { correspondents?: CorrespondentOption[] } | null,
@@ -25,9 +25,10 @@ const useDocumentCorrespondentActions = ({
correspondents,
handleCorrespondentCreate,
notifyApiError,
setStatusMessage,
updateDocumentCaches,
}: UseDocumentCorrespondentActionsArgs) => {
const { showToast } = useStatusToast();
const correspondentLookupByName = useMemo(() => {
const map = new Map<string, CorrespondentOption>();
correspondents.forEach((correspondent) => {
@@ -71,7 +72,7 @@ const useDocumentCorrespondentActions = ({
});
}
if (notify) {
setStatusMessage('Correspondent assigned.', 'success');
showToast('Correspondent assigned.', 'success');
}
return true;
} catch (error) {
@@ -80,7 +81,7 @@ const useDocumentCorrespondentActions = ({
throw new Error(message);
}
},
[correspondents, notifyApiError, setStatusMessage, updateDocumentCaches],
[correspondents, notifyApiError, showToast, updateDocumentCaches],
);
const handleCorrespondentRemove = useCallback(
@@ -103,7 +104,7 @@ const useDocumentCorrespondentActions = ({
});
}
if (notify) {
setStatusMessage('Correspondent removed.', 'success');
showToast('Correspondent removed.', 'success');
}
return true;
} catch (error) {
@@ -112,7 +113,7 @@ const useDocumentCorrespondentActions = ({
throw new Error(message);
}
},
[notifyApiError, setStatusMessage, updateDocumentCaches],
[notifyApiError, showToast, updateDocumentCaches],
);
const normalizeOption = (
@@ -138,7 +139,7 @@ const useDocumentCorrespondentActions = ({
}
const trimmed = name?.trim?.() || '';
if (!trimmed) {
setStatusMessage('Correspondent name is required.', 'error');
showToast('Correspondent name is required.', 'error');
return;
}
@@ -152,7 +153,7 @@ const useDocumentCorrespondentActions = ({
}
if (!target?.id) {
setStatusMessage('Unable to resolve correspondent.', 'error');
showToast('Unable to resolve correspondent.', 'error');
return;
}
@@ -166,7 +167,7 @@ const useDocumentCorrespondentActions = ({
input.value = '';
}
} catch (error) {
setStatusMessage('Failed to assign correspondent.', 'error');
showToast('Failed to assign correspondent.', 'error');
console.error('[documents] assign correspondent failed', error);
}
},
@@ -174,7 +175,7 @@ const useDocumentCorrespondentActions = ({
correspondentLookupByName,
handleCorrespondentCreate,
handleDocumentCorrespondentAttach,
setStatusMessage,
showToast,
],
);
@@ -1,5 +1,6 @@
import { useCallback, useMemo } from 'react';
import type { DragEvent } from 'react';
import { useStatusToast } from '../../../lib/context/StatusToastContext';
import { DEFAULT_FOLDER_NAME, hasFiles } from '../../../app/workspaceUtils';
import {
createFolder,
@@ -35,7 +36,6 @@ interface UseFolderTreeActionsOptions {
selectedFolder: FolderKey;
setSelectedFolder: (folderId: FolderKey) => void;
notifyApiError: (error: unknown, message?: string) => void;
setStatusMessage: (message: string, level?: string) => void;
navigate?: (path: string, options?: { replace?: boolean }) => void;
handleFileDrop: (dataTransfer: DataTransfer, folderId: FolderKey) => Promise<void> | void;
moveDocumentsToFolder: (docIds: FolderId[], folderId: FolderKey) => Promise<void>;
@@ -54,7 +54,6 @@ const useFolderTreeActions = ({
selectedFolder,
setSelectedFolder,
notifyApiError,
setStatusMessage,
navigate,
handleFileDrop,
moveDocumentsToFolder,
@@ -65,11 +64,13 @@ const useFolderTreeActions = ({
isInvalidFolderDrop,
setCreatingFolder,
}: UseFolderTreeActionsOptions) => {
const { showToast } = useStatusToast();
const moveFolder = useCallback(
async (folderId: FolderKey, targetFolderId: FolderKey | null) => {
const node = folderNodes.get(folderId);
if (!node) {
setStatusMessage('Folder metadata unavailable. Try refreshing.', 'error');
showToast('Folder metadata unavailable. Try refreshing.', 'error');
return;
}
@@ -134,7 +135,7 @@ const useFolderTreeActions = ({
setSelectedFolder(folderId);
}
setStatusMessage('Folder moved.', 'success');
showToast('Folder moved.', 'success');
} catch (error) {
const message = error.response?.data?.error || 'Failed to move folder.';
notifyApiError(error, message);
@@ -146,7 +147,7 @@ const useFolderTreeActions = ({
selectedFolder,
setFolderNodes,
setSelectedFolder,
setStatusMessage,
showToast,
],
);
@@ -180,12 +181,12 @@ const useFolderTreeActions = ({
const handleFolderRename = useCallback(
async (folderId: FolderKey, nextName: string) => {
if (!token) {
setStatusMessage('Log in to rename folders.', 'error');
showToast('Log in to rename folders.', 'error');
return false;
}
const trimmed = nextName?.trim?.() || '';
if (!trimmed) {
setStatusMessage('Folder name cannot be empty.', 'error');
showToast('Folder name cannot be empty.', 'error');
return false;
}
try {
@@ -200,7 +201,7 @@ const useFolderTreeActions = ({
return next;
});
setStatusMessage('Folder renamed.', 'success');
showToast('Folder renamed.', 'success');
return true;
} catch (error) {
const message = error.response?.data?.error || 'Failed to rename folder.';
@@ -211,7 +212,7 @@ const useFolderTreeActions = ({
[
notifyApiError,
setFolderNodes,
setStatusMessage,
showToast,
token,
],
);
@@ -219,11 +220,11 @@ const useFolderTreeActions = ({
const handleFolderCreate = useCallback(
async (name: string, parentId?: FolderKey | null) => {
if (!token) {
setStatusMessage('Log in to create folders.', 'error');
showToast('Log in to create folders.', 'error');
return false;
}
if (!name.trim()) {
setStatusMessage('Folder name cannot be empty.', 'error');
showToast('Folder name cannot be empty.', 'error');
return false;
}
@@ -243,7 +244,7 @@ const useFolderTreeActions = ({
if (!folderData?.id) {
throw new Error('Folder creation failed.');
}
setStatusMessage('Folder created.', 'success');
showToast('Folder created.', 'success');
setFolderNodes((prev) => {
const next = new Map(prev);
const parentId = folderData.parent_id ?? payload.parent_id ?? 'root';
@@ -278,7 +279,7 @@ const useFolderTreeActions = ({
} finally {
setCreatingFolder(false);
if (!succeeded) {
setStatusMessage('Folder creation failed.', 'error');
showToast('Folder creation failed.', 'error');
}
}
},
@@ -288,7 +289,7 @@ const useFolderTreeActions = ({
selectedFolder,
setCreatingFolder,
setFolderNodes,
setStatusMessage,
showToast,
token,
],
);
@@ -297,13 +298,13 @@ const useFolderTreeActions = ({
async (folderId: FolderKey, { 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;
}
@@ -337,14 +338,14 @@ const useFolderTreeActions = ({
}
if (showMessage) {
setStatusMessage('Folder deleted.', 'success');
showToast('Folder deleted.', 'success');
}
return true;
} catch (error) {
const message = error.response?.data?.error || 'Failed to delete folder.';
notifyApiError(error, message);
if (showMessage) {
setStatusMessage(message, 'error');
showToast(message, 'error');
}
return false;
}
@@ -356,7 +357,7 @@ const useFolderTreeActions = ({
selectedFolder,
setFolderNodes,
setSelectedFolder,
setStatusMessage,
showToast,
],
);
@@ -404,7 +405,7 @@ const useFolderTreeActions = ({
setDraggedFolderId(null);
const invalidMove = folderIds.some((sourceId) => isInvalidFolderDrop(sourceId, folderId));
if (invalidMove) {
setStatusMessage(
showToast(
'Cannot move a folder into itself or one of its descendants.',
'error',
);
@@ -492,7 +493,7 @@ const useFolderTreeActions = ({
selectedFolder,
setDraggedDocumentIds,
setDraggedFolderId,
setStatusMessage,
showToast,
],
);
@@ -1,4 +1,5 @@
import { useCallback } from 'react';
import { useStatusToast } from '../../../lib/context/StatusToastContext';
import type { Identifier } from '../../../types/identifiers';
@@ -20,7 +21,6 @@ interface UseDocumentTaggingArgs {
refreshTags: () => Promise<void> | void;
resolveTargetDocumentIds: (ids?: Identifier[] | null) => Identifier[];
notifyApiError: (error: unknown, message: string) => void;
setStatusMessage: (message: string, variant?: string) => void;
updateDocumentCaches?: (id: Identifier, updater: (doc: TagRecord | null) => TagRecord | null) => void;
}
@@ -44,9 +44,10 @@ const useDocumentTagActions = ({
refreshTags,
resolveTargetDocumentIds,
notifyApiError,
setStatusMessage,
updateDocumentCaches,
}: UseDocumentTaggingArgs) => {
const { showToast } = useStatusToast();
const bulkTagOperation = useCallback(
async ({ labels, action, documentIds }: BulkTagOperationArgs): Promise<BulkTagOperationResult> => {
const normalized = labels.map((label) => label.trim()).filter((label) => label.length > 0);
@@ -184,12 +185,12 @@ const useDocumentTagActions = ({
async ({ label, input, documentIds }: { label?: string; input?: HTMLInputElement | null; documentIds?: Identifier[] }) => {
const trimmed = label?.trim?.() || '';
if (!trimmed) {
setStatusMessage('Enter a tag label.', 'error');
showToast('Enter a tag label.', 'error');
return;
}
const targetIds = resolveTargetDocumentIds(documentIds);
if (!targetIds.length) {
setStatusMessage('Select documents before assigning tags.', 'error');
showToast('Select documents before assigning tags.', 'error');
return;
}
const result = await bulkTagOperation({
@@ -199,7 +200,7 @@ const useDocumentTagActions = ({
});
if (result?.ok) {
const { tagCount, docsCount } = result;
setStatusMessage(
showToast(
`Assigned ${tagCount} tag${tagCount === 1 ? '' : 's'} to ${docsCount} document${docsCount === 1 ? '' : 's'
}.`,
'success',
@@ -209,19 +210,19 @@ const useDocumentTagActions = ({
}
}
},
[bulkTagOperation, resolveTargetDocumentIds, setStatusMessage],
[bulkTagOperation, resolveTargetDocumentIds, showToast],
);
const handleBulkTagRemoveFromDetail = useCallback(
async ({ label, input, documentIds }: { label?: string; input?: HTMLInputElement | null; documentIds?: Identifier[] }) => {
const trimmed = label?.trim?.() || '';
if (!trimmed) {
setStatusMessage('Enter a tag label to remove.', 'error');
showToast('Enter a tag label to remove.', 'error');
return;
}
const targetIds = resolveTargetDocumentIds(documentIds);
if (!targetIds.length) {
setStatusMessage('Select documents before removing tags.', 'error');
showToast('Select documents before removing tags.', 'error');
return;
}
const result = await bulkTagOperation({
@@ -231,7 +232,7 @@ const useDocumentTagActions = ({
});
if (result?.ok) {
const { docsCount } = result;
setStatusMessage(
showToast(
`Removed tags from ${docsCount} document${docsCount === 1 ? '' : 's'}.`,
'success',
);
@@ -239,17 +240,17 @@ const useDocumentTagActions = ({
input.value = '';
}
} else if (result?.reason === 'tag-missing') {
setStatusMessage(`Tag “${result.label}” not found.`, 'error');
showToast(`Tag “${result.label}” not found.`, 'error');
}
},
[bulkTagOperation, resolveTargetDocumentIds, setStatusMessage],
[bulkTagOperation, resolveTargetDocumentIds, showToast],
);
const handleBulkSelectionReanalyze = useCallback(
async (documentIdsOverride: Identifier[] | null = null) => {
const targetIds = resolveTargetDocumentIds(documentIdsOverride);
if (!targetIds.length) {
setStatusMessage('Select documents before requesting re-analysis.', 'error');
showToast('Select documents before requesting re-analysis.', 'error');
return;
}
@@ -262,7 +263,7 @@ const useDocumentTagActions = ({
const queued = payload?.queued != null
? Number(payload.queued)
: targetIds.length;
setStatusMessage(
showToast(
`Queued re-analysis for ${queued} document${queued === 1 ? '' : 's'}.`,
'success',
);
@@ -272,7 +273,7 @@ const useDocumentTagActions = ({
notifyApiError(error, message);
}
},
[resolveTargetDocumentIds, notifyApiError, setStatusMessage],
[resolveTargetDocumentIds, notifyApiError, showToast],
);
return {
@@ -1,6 +1,7 @@
import { useCallback, useRef, useState } from 'react';
import type { Dispatch, MutableRefObject, SetStateAction } from 'react';
import useFileDrop from './useFileDrop';
import { useStatusToast } from '../../../lib/context/StatusToastContext';
import { DEFAULT_FOLDER_NAME, hasFiles } from '../../../app/workspaceUtils';
import { fetchDocument, uploadDocument, resolveFolderPath } from '../../../lib/api/apiClient';
import type { Identifier } from '../../../types/identifiers';
@@ -26,9 +27,7 @@ type UploadQueueItem = {
conflictDocumentId: Identifier | null;
};
type StatusLevel = 'success' | 'info' | 'warning' | 'error' | string;
type NotifyApiError = (error: unknown, fallbackMessage?: string) => void;
type SetStatusMessage = (message: string, level?: StatusLevel) => void;
type DropOverlayState = {
active: boolean;
@@ -99,7 +98,6 @@ interface UseDocumentUploadsArgs {
refreshCurrentFolder: () => Promise<void>;
shellRef: MutableRefObject<HTMLElement | null>;
notifyApiError?: NotifyApiError;
setStatusMessage?: SetStatusMessage;
}
interface UseDocumentUploadsResult {
@@ -128,7 +126,6 @@ const useDocumentUploads = ({
refreshCurrentFolder,
shellRef,
notifyApiError,
setStatusMessage,
}: UseDocumentUploadsArgs): UseDocumentUploadsResult => {
const [dropOverlayState, setDropOverlayState] = useState<DropOverlayState>({
active: false,
@@ -138,6 +135,7 @@ const useDocumentUploads = ({
const folderPathCacheRef = useRef<Map<string, FolderId>>(new Map());
const queueIdRef = useRef(0);
const [uploadQueue, setUploadQueue] = useState<UploadQueueItem[]>([]);
const { showToast } = useStatusToast();
const uploadFile = useCallback(
async (file: File, targetFolderId: FolderId) => {
@@ -180,12 +178,12 @@ const useDocumentUploads = ({
}
const message = error.response?.data?.error || `Failed to upload ${file.name}.`;
notifyApiError?.(error, message);
setStatusMessage?.(message, 'error');
showToast(message, 'error');
const wrapped = Object.assign(new Error(message), { response: error.response });
throw wrapped;
}
},
[notifyApiError, setStatusMessage],
[notifyApiError, showToast],
);
const appendQueueItems = useCallback((entries: FileEntry[], targetFolderId?: FolderId) => {
+9 -9
View File
@@ -1,4 +1,5 @@
import { useState, useCallback } from 'react';
import { useStatusToast } from '../lib/context/StatusToastContext';
/* global PublicKeyCredentialCreationOptions, CredentialCreationOptions */
import {
@@ -14,7 +15,6 @@ import {
} from '../lib/api/apiClient';
import type { PasskeyId } from '../types/identifiers';
type StatusMessageFn = (message: string, variant?: string) => void;
type NotifyApiErrorFn = (error: unknown, message: string) => void;
type ApiError = {
@@ -71,7 +71,6 @@ type RevokePasskeyResult =
interface UsePasskeysArgs {
notifyApiError: NotifyApiErrorFn;
setStatusMessage: StatusMessageFn;
token?: string | null;
}
@@ -89,12 +88,13 @@ interface UsePasskeysResult {
) => Promise<RevokePasskeyResult>;
}
const usePasskeys = ({ notifyApiError, setStatusMessage, token }: UsePasskeysArgs): UsePasskeysResult => {
const usePasskeys = ({ notifyApiError, token }: UsePasskeysArgs): UsePasskeysResult => {
const [passkeys, setPasskeys] = useState<PasskeyRecord[]>([]);
const [passkeysSupported, setPasskeysSupported] = useState<boolean | null>(null);
const [passkeysLoading, setPasskeysLoading] = useState(false);
const [registeringPasskey, setRegisteringPasskey] = useState(false);
const [revokingPasskeyId, setRevokingPasskeyId] = useState<PasskeyId | null>(null);
const { showToast } = useStatusToast();
const refreshPasskeys = useCallback(async (): Promise<void> => {
if (!token) {
@@ -122,7 +122,7 @@ const usePasskeys = ({ notifyApiError, setStatusMessage, token }: UsePasskeysArg
async ({ nickname }: { nickname?: string } = {}): Promise<RegisterPasskeyResult> => {
if (!isWebAuthnAvailable()) {
setPasskeysSupported(false);
setStatusMessage('Passkeys are not supported in this browser.', 'error');
showToast('Passkeys are not supported in this browser.', 'error');
return { ok: false, reason: 'unsupported' };
}
if (registeringPasskey) {
@@ -166,12 +166,12 @@ const usePasskeys = ({ notifyApiError, setStatusMessage, token }: UsePasskeysArg
await finishPasskeyRegistration(payload);
await refreshPasskeys();
setPasskeysSupported(true);
setStatusMessage('Passkey registered.', 'success');
showToast('Passkey registered.', 'success');
return { ok: true };
} catch (error) {
const typedError = error as ApiError;
if (typedError?.name === 'NotAllowedError') {
setStatusMessage('Passkey registration cancelled.', 'info');
showToast('Passkey registration cancelled.', 'info');
return { ok: false, reason: 'cancelled' };
}
@@ -187,7 +187,7 @@ const usePasskeys = ({ notifyApiError, setStatusMessage, token }: UsePasskeysArg
setRegisteringPasskey(false);
}
},
[notifyApiError, refreshPasskeys, registeringPasskey, setStatusMessage],
[notifyApiError, refreshPasskeys, registeringPasskey, showToast],
);
const revokePasskey = useCallback(
@@ -202,7 +202,7 @@ const usePasskeys = ({ notifyApiError, setStatusMessage, token }: UsePasskeysArg
try {
await deletePasskey(passkeyId, { reason });
await refreshPasskeys();
setStatusMessage('Passkey revoked.', 'success');
showToast('Passkey revoked.', 'success');
return { ok: true };
} catch (error) {
const message = (error as ApiError)?.response?.data?.error || 'Failed to revoke passkey.';
@@ -212,7 +212,7 @@ const usePasskeys = ({ notifyApiError, setStatusMessage, token }: UsePasskeysArg
setRevokingPasskeyId(null);
}
},
[notifyApiError, refreshPasskeys, setStatusMessage],
[notifyApiError, refreshPasskeys, showToast],
);
return {