refactor: centralize API error notifications with a new useNotifyApiError hook
This commit is contained in:
@@ -12,6 +12,8 @@ import type { Document } from '../types/documents';
|
|||||||
|
|
||||||
type NavigateHandler = (path: string, options?: { replace?: boolean }) => void;
|
type NavigateHandler = (path: string, options?: { replace?: boolean }) => void;
|
||||||
|
|
||||||
|
import useNotifyApiError from '../hooks/useNotifyApiError';
|
||||||
|
|
||||||
interface UseDocumentPreviewArgs {
|
interface UseDocumentPreviewArgs {
|
||||||
routeDocumentId?: DocumentId | null;
|
routeDocumentId?: DocumentId | null;
|
||||||
documentsManager: {
|
documentsManager: {
|
||||||
@@ -22,7 +24,6 @@ interface UseDocumentPreviewArgs {
|
|||||||
ingest: (docs: unknown[]) => { canonical: Document[]; changed: boolean };
|
ingest: (docs: unknown[]) => { canonical: Document[]; changed: boolean };
|
||||||
};
|
};
|
||||||
selectedFolder?: FolderId | null;
|
selectedFolder?: FolderId | null;
|
||||||
notifyApiError: (error: unknown, message: string) => void;
|
|
||||||
navigate: NavigateHandler;
|
navigate: NavigateHandler;
|
||||||
locationPathname: string;
|
locationPathname: string;
|
||||||
locationSearch: string;
|
locationSearch: string;
|
||||||
@@ -44,7 +45,6 @@ const useDocumentPreview = ({
|
|||||||
routeDocumentId,
|
routeDocumentId,
|
||||||
documentsManager,
|
documentsManager,
|
||||||
selectedFolder,
|
selectedFolder,
|
||||||
notifyApiError,
|
|
||||||
navigate,
|
navigate,
|
||||||
locationPathname,
|
locationPathname,
|
||||||
locationSearch,
|
locationSearch,
|
||||||
@@ -52,6 +52,7 @@ const useDocumentPreview = ({
|
|||||||
setActivePreviewId,
|
setActivePreviewId,
|
||||||
}: UseDocumentPreviewArgs): UseDocumentPreviewResult => {
|
}: UseDocumentPreviewArgs): UseDocumentPreviewResult => {
|
||||||
const previewReturnPathRef = useRef<string | null>(null);
|
const previewReturnPathRef = useRef<string | null>(null);
|
||||||
|
const notifyApiError = useNotifyApiError();
|
||||||
|
|
||||||
const resetPreviewState = useCallback(() => {
|
const resetPreviewState = useCallback(() => {
|
||||||
previewReturnPathRef.current = null;
|
previewReturnPathRef.current = null;
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ type ApiClient = {
|
|||||||
get: <T = unknown>(url: string, config?: { params?: Record<string, unknown> }) => Promise<{ data: T }>;
|
get: <T = unknown>(url: string, config?: { params?: Record<string, unknown> }) => Promise<{ data: T }>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
import useNotifyApiError from '../hooks/useNotifyApiError';
|
||||||
|
|
||||||
interface UseDocumentsSearchArgs {
|
interface UseDocumentsSearchArgs {
|
||||||
api: ApiClient;
|
api: ApiClient;
|
||||||
token?: string | null;
|
token?: string | null;
|
||||||
@@ -20,7 +22,6 @@ interface UseDocumentsSearchArgs {
|
|||||||
searchIncludeDescendants?: boolean;
|
searchIncludeDescendants?: boolean;
|
||||||
documentsSortField?: string;
|
documentsSortField?: string;
|
||||||
documentsSortDirection?: string;
|
documentsSortDirection?: string;
|
||||||
notifyApiError: (error: unknown, message: string) => void;
|
|
||||||
setSearchIncludeDescendants: (value: boolean) => void;
|
setSearchIncludeDescendants: (value: boolean) => void;
|
||||||
documentsManager: {
|
documentsManager: {
|
||||||
ingest: (docs: unknown[]) => { canonical: Document[]; changed: boolean };
|
ingest: (docs: unknown[]) => { canonical: Document[]; changed: boolean };
|
||||||
@@ -72,7 +73,6 @@ const useDocumentsSearch = ({
|
|||||||
searchIncludeDescendants,
|
searchIncludeDescendants,
|
||||||
documentsSortField,
|
documentsSortField,
|
||||||
documentsSortDirection,
|
documentsSortDirection,
|
||||||
notifyApiError,
|
|
||||||
setSearchIncludeDescendants,
|
setSearchIncludeDescendants,
|
||||||
documentsManager,
|
documentsManager,
|
||||||
}: UseDocumentsSearchArgs): UseDocumentsSearchResult => {
|
}: UseDocumentsSearchArgs): UseDocumentsSearchResult => {
|
||||||
@@ -82,6 +82,7 @@ const useDocumentsSearch = ({
|
|||||||
const [searchResultIds, setSearchResultIds] = useState<Identifier[] | null>(null);
|
const [searchResultIds, setSearchResultIds] = useState<Identifier[] | null>(null);
|
||||||
const [searchLoading, setSearchLoading] = useState<boolean>(false);
|
const [searchLoading, setSearchLoading] = useState<boolean>(false);
|
||||||
const [searchTrigger, setSearchTrigger] = useState<number>(0);
|
const [searchTrigger, setSearchTrigger] = useState<number>(0);
|
||||||
|
const notifyApiError = useNotifyApiError();
|
||||||
|
|
||||||
const toggleTagFilter = useCallback((tagId: Identifier) => {
|
const toggleTagFilter = useCallback((tagId: Identifier) => {
|
||||||
if (!tagId) return;
|
if (!tagId) return;
|
||||||
|
|||||||
@@ -4,19 +4,20 @@ import type { Correspondent } from '../../types/documents';
|
|||||||
|
|
||||||
import { listCorrespondents, createCorrespondent, updateCorrespondent, deleteCorrespondent } from '../../lib/api/apiClient';
|
import { listCorrespondents, createCorrespondent, updateCorrespondent, deleteCorrespondent } from '../../lib/api/apiClient';
|
||||||
|
|
||||||
|
import useNotifyApiError from '../../hooks/useNotifyApiError';
|
||||||
|
|
||||||
interface UseCorrespondentsOptions {
|
interface UseCorrespondentsOptions {
|
||||||
notifyApiError: (error: unknown, fallback: string) => void;
|
|
||||||
tenantIdRef: MutableRefObject<string | null>;
|
tenantIdRef: MutableRefObject<string | null>;
|
||||||
mapDocumentCaches?: (mapper: (doc: any) => any) => void;
|
mapDocumentCaches?: (mapper: (doc: any) => any) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const useCorrespondents = ({
|
const useCorrespondents = ({
|
||||||
notifyApiError,
|
|
||||||
tenantIdRef,
|
tenantIdRef,
|
||||||
mapDocumentCaches,
|
mapDocumentCaches,
|
||||||
}: UseCorrespondentsOptions) => {
|
}: UseCorrespondentsOptions) => {
|
||||||
const [correspondents, setCorrespondents] = useState<Correspondent[]>([]);
|
const [correspondents, setCorrespondents] = useState<Correspondent[]>([]);
|
||||||
const { showToast } = useStatusToast();
|
const { showToast } = useStatusToast();
|
||||||
|
const notifyApiError = useNotifyApiError();
|
||||||
|
|
||||||
const refreshCorrespondents = useCallback(async () => {
|
const refreshCorrespondents = useCallback(async () => {
|
||||||
const requestTenantId = tenantIdRef.current;
|
const requestTenantId = tenantIdRef.current;
|
||||||
|
|||||||
@@ -40,8 +40,6 @@ type RemoveDocumentsFromCaches = (documentIds: DocumentId[]) => void;
|
|||||||
|
|
||||||
type CloseDocumentPreview = () => void;
|
type CloseDocumentPreview = () => void;
|
||||||
|
|
||||||
type NotifyApiError = (error: unknown, fallbackMessage?: string) => void;
|
|
||||||
|
|
||||||
interface Tag {
|
interface Tag {
|
||||||
id: DocumentId;
|
id: DocumentId;
|
||||||
label: string;
|
label: string;
|
||||||
@@ -73,6 +71,8 @@ interface DocumentTagExtras {
|
|||||||
input?: { value?: string } | null;
|
input?: { value?: string } | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import useNotifyApiError from '../../hooks/useNotifyApiError';
|
||||||
|
|
||||||
interface UseDocumentMutationsArgs {
|
interface UseDocumentMutationsArgs {
|
||||||
token?: string | null;
|
token?: string | null;
|
||||||
documentLookup: Map<DocumentId, Document>;
|
documentLookup: Map<DocumentId, Document>;
|
||||||
@@ -90,7 +90,6 @@ interface UseDocumentMutationsArgs {
|
|||||||
focusedDocumentId: DocumentId | null;
|
focusedDocumentId: DocumentId | null;
|
||||||
setFocusedEntryKey: Dispatch<SetStateAction<string | null>>;
|
setFocusedEntryKey: Dispatch<SetStateAction<string | null>>;
|
||||||
focusedEntryKey: string | null;
|
focusedEntryKey: string | null;
|
||||||
notifyApiError: NotifyApiError;
|
|
||||||
mapDocumentCaches: MapDocumentCaches;
|
mapDocumentCaches: MapDocumentCaches;
|
||||||
folderNodes: Map<FolderId, FolderNode>;
|
folderNodes: Map<FolderId, FolderNode>;
|
||||||
setFolderNodes: Dispatch<SetStateAction<Map<FolderId, FolderNode>>>;
|
setFolderNodes: Dispatch<SetStateAction<Map<FolderId, FolderNode>>>;
|
||||||
@@ -160,7 +159,6 @@ const useDocumentMutations = ({
|
|||||||
focusedDocumentId,
|
focusedDocumentId,
|
||||||
setFocusedEntryKey,
|
setFocusedEntryKey,
|
||||||
focusedEntryKey,
|
focusedEntryKey,
|
||||||
notifyApiError,
|
|
||||||
mapDocumentCaches,
|
mapDocumentCaches,
|
||||||
folderNodes,
|
folderNodes,
|
||||||
setFolderNodes,
|
setFolderNodes,
|
||||||
@@ -177,6 +175,7 @@ const useDocumentMutations = ({
|
|||||||
ingestDocuments,
|
ingestDocuments,
|
||||||
}: UseDocumentMutationsArgs): UseDocumentMutationsResult => {
|
}: UseDocumentMutationsArgs): UseDocumentMutationsResult => {
|
||||||
const { showToast } = useStatusToast();
|
const { showToast } = useStatusToast();
|
||||||
|
const notifyApiError = useNotifyApiError();
|
||||||
|
|
||||||
const moveDocumentsToFolder = useCallback(
|
const moveDocumentsToFolder = useCallback(
|
||||||
async (documentIds: Array<DocumentId | Document>, targetFolderId?: NullableFolderId) => {
|
async (documentIds: Array<DocumentId | Document>, targetFolderId?: NullableFolderId) => {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
useNavigate,
|
useNavigate,
|
||||||
} from 'react-router-dom';
|
} from 'react-router-dom';
|
||||||
import AssetManager, { getAssetFromVersion } from '../../lib/assets/AssetManager';
|
import AssetManager, { getAssetFromVersion } from '../../lib/assets/AssetManager';
|
||||||
import useApiError from '../../hooks/useApiError';
|
import useNotifyApiError from '../../hooks/useNotifyApiError';
|
||||||
import TagManager from '../../lib/assets/TagManager';
|
import TagManager from '../../lib/assets/TagManager';
|
||||||
import { useManagementModals } from '../../app/useManagementModals';
|
import { useManagementModals } from '../../app/useManagementModals';
|
||||||
import { useAppDispatch, useAppState } from '../../lib/store/appState';
|
import { useAppDispatch, useAppState } from '../../lib/store/appState';
|
||||||
@@ -144,19 +144,7 @@ const useDocumentsWorkspace = ({
|
|||||||
? (tenantOptionsRaw as TenantOption[])
|
? (tenantOptionsRaw as TenantOption[])
|
||||||
: [];
|
: [];
|
||||||
const { showToast } = useStatusToast();
|
const { showToast } = useStatusToast();
|
||||||
|
const notifyApiError = useNotifyApiError();
|
||||||
const reportApiError = useApiError({
|
|
||||||
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 [creatingFolder, setCreatingFolder] = useState(false);
|
||||||
|
|
||||||
@@ -389,7 +377,6 @@ const useDocumentsWorkspace = ({
|
|||||||
searchIncludeDescendants,
|
searchIncludeDescendants,
|
||||||
documentsSortField,
|
documentsSortField,
|
||||||
documentsSortDirection,
|
documentsSortDirection,
|
||||||
notifyApiError,
|
|
||||||
setSearchIncludeDescendants,
|
setSearchIncludeDescendants,
|
||||||
documentsManager,
|
documentsManager,
|
||||||
});
|
});
|
||||||
@@ -457,7 +444,6 @@ const useDocumentsWorkspace = ({
|
|||||||
routeDocumentId: previewDocumentId,
|
routeDocumentId: previewDocumentId,
|
||||||
documentsManager,
|
documentsManager,
|
||||||
selectedFolder,
|
selectedFolder,
|
||||||
notifyApiError,
|
|
||||||
navigate,
|
navigate,
|
||||||
locationPathname: location.pathname,
|
locationPathname: location.pathname,
|
||||||
locationSearch: location.search,
|
locationSearch: location.search,
|
||||||
@@ -506,7 +492,6 @@ const useDocumentsWorkspace = ({
|
|||||||
handleTagDelete,
|
handleTagDelete,
|
||||||
setTags,
|
setTags,
|
||||||
} = useTags({
|
} = useTags({
|
||||||
notifyApiError,
|
|
||||||
tenantIdRef,
|
tenantIdRef,
|
||||||
tagManager,
|
tagManager,
|
||||||
setActiveTagFilters,
|
setActiveTagFilters,
|
||||||
@@ -535,7 +520,6 @@ const useDocumentsWorkspace = ({
|
|||||||
handleCorrespondentDelete,
|
handleCorrespondentDelete,
|
||||||
setCorrespondents,
|
setCorrespondents,
|
||||||
} = useCorrespondents({
|
} = useCorrespondents({
|
||||||
notifyApiError,
|
|
||||||
tenantIdRef,
|
tenantIdRef,
|
||||||
mapDocumentCaches,
|
mapDocumentCaches,
|
||||||
});
|
});
|
||||||
@@ -548,7 +532,6 @@ const useDocumentsWorkspace = ({
|
|||||||
} = useDocumentCorrespondentActions({
|
} = useDocumentCorrespondentActions({
|
||||||
correspondents,
|
correspondents,
|
||||||
handleCorrespondentCreate,
|
handleCorrespondentCreate,
|
||||||
notifyApiError,
|
|
||||||
updateDocumentCaches,
|
updateDocumentCaches,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -562,7 +545,6 @@ const useDocumentsWorkspace = ({
|
|||||||
registerPasskey,
|
registerPasskey,
|
||||||
revokePasskey,
|
revokePasskey,
|
||||||
} = usePasskeys({
|
} = usePasskeys({
|
||||||
notifyApiError,
|
|
||||||
token,
|
token,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -594,7 +576,6 @@ const useDocumentsWorkspace = ({
|
|||||||
tagManager,
|
tagManager,
|
||||||
refreshTags,
|
refreshTags,
|
||||||
resolveTargetDocumentIds,
|
resolveTargetDocumentIds,
|
||||||
notifyApiError,
|
|
||||||
updateDocumentCaches,
|
updateDocumentCaches,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -612,7 +593,6 @@ const useDocumentsWorkspace = ({
|
|||||||
ensureFolderData,
|
ensureFolderData,
|
||||||
refreshCurrentFolder,
|
refreshCurrentFolder,
|
||||||
shellRef,
|
shellRef,
|
||||||
notifyApiError,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -747,7 +727,6 @@ const useDocumentsWorkspace = ({
|
|||||||
focusedDocumentId,
|
focusedDocumentId,
|
||||||
setFocusedEntryKey,
|
setFocusedEntryKey,
|
||||||
focusedEntryKey,
|
focusedEntryKey,
|
||||||
notifyApiError,
|
|
||||||
mapDocumentCaches,
|
mapDocumentCaches,
|
||||||
folderNodes,
|
folderNodes,
|
||||||
setFolderNodes,
|
setFolderNodes,
|
||||||
@@ -777,7 +756,6 @@ const useDocumentsWorkspace = ({
|
|||||||
setFolderNodes,
|
setFolderNodes,
|
||||||
selectedFolder,
|
selectedFolder,
|
||||||
setSelectedFolder,
|
setSelectedFolder,
|
||||||
notifyApiError,
|
|
||||||
navigate,
|
navigate,
|
||||||
handleFileDrop,
|
handleFileDrop,
|
||||||
moveDocumentsToFolder,
|
moveDocumentsToFolder,
|
||||||
@@ -1105,7 +1083,6 @@ const useDocumentsWorkspace = ({
|
|||||||
appDispatch,
|
appDispatch,
|
||||||
currentTenantId,
|
currentTenantId,
|
||||||
resetWorkspaceState,
|
resetWorkspaceState,
|
||||||
notifyApiError,
|
|
||||||
refreshTags,
|
refreshTags,
|
||||||
refreshCorrespondents,
|
refreshCorrespondents,
|
||||||
loadFolder,
|
loadFolder,
|
||||||
|
|||||||
@@ -9,9 +9,10 @@ interface TagManagerInterface {
|
|||||||
buildPayload: (input: { label?: string; color?: string | null }) => { label: string; color: string | null };
|
buildPayload: (input: { label?: string; color?: string | null }) => { label: string; color: string | null };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import useNotifyApiError from '../../hooks/useNotifyApiError';
|
||||||
|
|
||||||
interface UseTagsOptions {
|
interface UseTagsOptions {
|
||||||
// apiClient removed
|
// apiClient removed
|
||||||
notifyApiError: (error: unknown, fallback: string) => void;
|
|
||||||
tagManager: TagManagerInterface;
|
tagManager: TagManagerInterface;
|
||||||
tenantIdRef: MutableRefObject<TenantId | null>;
|
tenantIdRef: MutableRefObject<TenantId | null>;
|
||||||
setActiveTagFilters: (updater: (prev: Array<TagId>) => Array<TagId>) => void;
|
setActiveTagFilters: (updater: (prev: Array<TagId>) => Array<TagId>) => void;
|
||||||
@@ -20,7 +21,6 @@ interface UseTagsOptions {
|
|||||||
|
|
||||||
const useTags = ({
|
const useTags = ({
|
||||||
// apiClient removed
|
// apiClient removed
|
||||||
notifyApiError,
|
|
||||||
tagManager,
|
tagManager,
|
||||||
tenantIdRef,
|
tenantIdRef,
|
||||||
setActiveTagFilters,
|
setActiveTagFilters,
|
||||||
@@ -28,6 +28,7 @@ const useTags = ({
|
|||||||
}: UseTagsOptions) => {
|
}: UseTagsOptions) => {
|
||||||
const [tags, setTags] = useState<Tag[]>([]);
|
const [tags, setTags] = useState<Tag[]>([]);
|
||||||
const { showToast } = useStatusToast();
|
const { showToast } = useStatusToast();
|
||||||
|
const notifyApiError = useNotifyApiError();
|
||||||
|
|
||||||
const refreshTags = useCallback(async () => {
|
const refreshTags = useCallback(async () => {
|
||||||
const requestTenantId = tenantIdRef.current;
|
const requestTenantId = tenantIdRef.current;
|
||||||
|
|||||||
@@ -10,11 +10,12 @@ interface TenantOption {
|
|||||||
name?: string;
|
name?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import useNotifyApiError from '../../hooks/useNotifyApiError';
|
||||||
|
|
||||||
interface UseTenantManagerOptions {
|
interface UseTenantManagerOptions {
|
||||||
appDispatch: (action: any) => void;
|
appDispatch: (action: any) => void;
|
||||||
currentTenantId: TenantId | null;
|
currentTenantId: TenantId | null;
|
||||||
resetWorkspaceState: () => void;
|
resetWorkspaceState: () => void;
|
||||||
notifyApiError: (error: unknown, message: string) => void;
|
|
||||||
refreshTags: () => Promise<void>;
|
refreshTags: () => Promise<void>;
|
||||||
refreshCorrespondents: () => Promise<void>;
|
refreshCorrespondents: () => Promise<void>;
|
||||||
loadFolder: (folderId: FolderId, options?: { preserveSearch?: boolean }) => Promise<void>;
|
loadFolder: (folderId: FolderId, options?: { preserveSearch?: boolean }) => Promise<void>;
|
||||||
@@ -28,7 +29,6 @@ const useTenantManager = ({
|
|||||||
appDispatch,
|
appDispatch,
|
||||||
currentTenantId,
|
currentTenantId,
|
||||||
resetWorkspaceState,
|
resetWorkspaceState,
|
||||||
notifyApiError,
|
|
||||||
refreshTags,
|
refreshTags,
|
||||||
refreshCorrespondents,
|
refreshCorrespondents,
|
||||||
loadFolder,
|
loadFolder,
|
||||||
@@ -38,6 +38,7 @@ const useTenantManager = ({
|
|||||||
tenantIdRef,
|
tenantIdRef,
|
||||||
}: UseTenantManagerOptions) => {
|
}: UseTenantManagerOptions) => {
|
||||||
const { showToast } = useStatusToast();
|
const { showToast } = useStatusToast();
|
||||||
|
const notifyApiError = useNotifyApiError();
|
||||||
const handleTenantSelect = useCallback(
|
const handleTenantSelect = useCallback(
|
||||||
async (tenantOption: TenantOption | null, { refreshOnly = false }: { refreshOnly?: boolean } = {}) => {
|
async (tenantOption: TenantOption | null, { refreshOnly = false }: { refreshOnly?: boolean } = {}) => {
|
||||||
const requestedTenantId = tenantOption?.id ?? null;
|
const requestedTenantId = tenantOption?.id ?? null;
|
||||||
|
|||||||
@@ -11,10 +11,11 @@ interface CorrespondentOption {
|
|||||||
[key: string]: unknown;
|
[key: string]: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import useNotifyApiError from '../../../hooks/useNotifyApiError';
|
||||||
|
|
||||||
interface UseDocumentCorrespondentActionsArgs {
|
interface UseDocumentCorrespondentActionsArgs {
|
||||||
correspondents: CorrespondentOption[];
|
correspondents: CorrespondentOption[];
|
||||||
handleCorrespondentCreate: (payload: { name: string }) => Promise<CorrespondentOption | null>;
|
handleCorrespondentCreate: (payload: { name: string }) => Promise<CorrespondentOption | null>;
|
||||||
notifyApiError: (error: unknown, fallback: string) => void;
|
|
||||||
updateDocumentCaches?: (
|
updateDocumentCaches?: (
|
||||||
id: Identifier,
|
id: Identifier,
|
||||||
updater: (doc: { correspondents?: CorrespondentOption[] } | null) => { correspondents?: CorrespondentOption[] } | null,
|
updater: (doc: { correspondents?: CorrespondentOption[] } | null) => { correspondents?: CorrespondentOption[] } | null,
|
||||||
@@ -24,10 +25,10 @@ interface UseDocumentCorrespondentActionsArgs {
|
|||||||
const useDocumentCorrespondentActions = ({
|
const useDocumentCorrespondentActions = ({
|
||||||
correspondents,
|
correspondents,
|
||||||
handleCorrespondentCreate,
|
handleCorrespondentCreate,
|
||||||
notifyApiError,
|
|
||||||
updateDocumentCaches,
|
updateDocumentCaches,
|
||||||
}: UseDocumentCorrespondentActionsArgs) => {
|
}: UseDocumentCorrespondentActionsArgs) => {
|
||||||
const { showToast } = useStatusToast();
|
const { showToast } = useStatusToast();
|
||||||
|
const notifyApiError = useNotifyApiError();
|
||||||
|
|
||||||
const correspondentLookupByName = useMemo(() => {
|
const correspondentLookupByName = useMemo(() => {
|
||||||
const map = new Map<string, CorrespondentOption>();
|
const map = new Map<string, CorrespondentOption>();
|
||||||
|
|||||||
@@ -29,13 +29,14 @@ interface FolderClickHandlers {
|
|||||||
onDragLeave: (event: DragEvent<HTMLElement>) => void;
|
onDragLeave: (event: DragEvent<HTMLElement>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import useNotifyApiError from '../../../hooks/useNotifyApiError';
|
||||||
|
|
||||||
interface UseFolderTreeActionsOptions {
|
interface UseFolderTreeActionsOptions {
|
||||||
token?: string | null;
|
token?: string | null;
|
||||||
folderNodes: Map<FolderKey, FolderNode>;
|
folderNodes: Map<FolderKey, FolderNode>;
|
||||||
setFolderNodes: (updater: (prev: Map<FolderKey, FolderNode>) => Map<FolderKey, FolderNode>) => void;
|
setFolderNodes: (updater: (prev: Map<FolderKey, FolderNode>) => Map<FolderKey, FolderNode>) => void;
|
||||||
selectedFolder: FolderKey;
|
selectedFolder: FolderKey;
|
||||||
setSelectedFolder: (folderId: FolderKey) => void;
|
setSelectedFolder: (folderId: FolderKey) => void;
|
||||||
notifyApiError: (error: unknown, message?: string) => void;
|
|
||||||
navigate?: (path: string, options?: { replace?: boolean }) => void;
|
navigate?: (path: string, options?: { replace?: boolean }) => void;
|
||||||
handleFileDrop: (dataTransfer: DataTransfer, folderId: FolderKey) => Promise<void> | void;
|
handleFileDrop: (dataTransfer: DataTransfer, folderId: FolderKey) => Promise<void> | void;
|
||||||
moveDocumentsToFolder: (docIds: FolderId[], folderId: FolderKey) => Promise<void>;
|
moveDocumentsToFolder: (docIds: FolderId[], folderId: FolderKey) => Promise<void>;
|
||||||
@@ -53,7 +54,6 @@ const useFolderTreeActions = ({
|
|||||||
setFolderNodes,
|
setFolderNodes,
|
||||||
selectedFolder,
|
selectedFolder,
|
||||||
setSelectedFolder,
|
setSelectedFolder,
|
||||||
notifyApiError,
|
|
||||||
navigate,
|
navigate,
|
||||||
handleFileDrop,
|
handleFileDrop,
|
||||||
moveDocumentsToFolder,
|
moveDocumentsToFolder,
|
||||||
@@ -65,6 +65,7 @@ const useFolderTreeActions = ({
|
|||||||
setCreatingFolder,
|
setCreatingFolder,
|
||||||
}: UseFolderTreeActionsOptions) => {
|
}: UseFolderTreeActionsOptions) => {
|
||||||
const { showToast } = useStatusToast();
|
const { showToast } = useStatusToast();
|
||||||
|
const notifyApiError = useNotifyApiError();
|
||||||
|
|
||||||
const moveFolder = useCallback(
|
const moveFolder = useCallback(
|
||||||
async (folderId: FolderKey, targetFolderId: FolderKey | null) => {
|
async (folderId: FolderKey, targetFolderId: FolderKey | null) => {
|
||||||
|
|||||||
@@ -15,12 +15,13 @@ interface TagManager {
|
|||||||
buildPayload: (input: { label: string }) => Record<string, unknown>;
|
buildPayload: (input: { label: string }) => Record<string, unknown>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import useNotifyApiError from '../../../hooks/useNotifyApiError';
|
||||||
|
|
||||||
interface UseDocumentTaggingArgs {
|
interface UseDocumentTaggingArgs {
|
||||||
tags: TagRecord[];
|
tags: TagRecord[];
|
||||||
tagManager: TagManager;
|
tagManager: TagManager;
|
||||||
refreshTags: () => Promise<void> | void;
|
refreshTags: () => Promise<void> | void;
|
||||||
resolveTargetDocumentIds: (ids?: Identifier[] | null) => Identifier[];
|
resolveTargetDocumentIds: (ids?: Identifier[] | null) => Identifier[];
|
||||||
notifyApiError: (error: unknown, message: string) => void;
|
|
||||||
updateDocumentCaches?: (id: Identifier, updater: (doc: TagRecord | null) => TagRecord | null) => void;
|
updateDocumentCaches?: (id: Identifier, updater: (doc: TagRecord | null) => TagRecord | null) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,10 +44,10 @@ const useDocumentTagActions = ({
|
|||||||
tagManager,
|
tagManager,
|
||||||
refreshTags,
|
refreshTags,
|
||||||
resolveTargetDocumentIds,
|
resolveTargetDocumentIds,
|
||||||
notifyApiError,
|
|
||||||
updateDocumentCaches,
|
updateDocumentCaches,
|
||||||
}: UseDocumentTaggingArgs) => {
|
}: UseDocumentTaggingArgs) => {
|
||||||
const { showToast } = useStatusToast();
|
const { showToast } = useStatusToast();
|
||||||
|
const notifyApiError = useNotifyApiError();
|
||||||
|
|
||||||
const bulkTagOperation = useCallback(
|
const bulkTagOperation = useCallback(
|
||||||
async ({ labels, action, documentIds }: BulkTagOperationArgs): Promise<BulkTagOperationResult> => {
|
async ({ labels, action, documentIds }: BulkTagOperationArgs): Promise<BulkTagOperationResult> => {
|
||||||
|
|||||||
@@ -118,6 +118,8 @@ interface UseDocumentUploadsResult {
|
|||||||
clearUploadQueue: () => void;
|
clearUploadQueue: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import useNotifyApiError from '../../../hooks/useNotifyApiError';
|
||||||
|
|
||||||
const useDocumentUploads = ({
|
const useDocumentUploads = ({
|
||||||
token,
|
token,
|
||||||
selectedFolder,
|
selectedFolder,
|
||||||
@@ -125,7 +127,6 @@ const useDocumentUploads = ({
|
|||||||
ensureFolderData,
|
ensureFolderData,
|
||||||
refreshCurrentFolder,
|
refreshCurrentFolder,
|
||||||
shellRef,
|
shellRef,
|
||||||
notifyApiError,
|
|
||||||
}: UseDocumentUploadsArgs): UseDocumentUploadsResult => {
|
}: UseDocumentUploadsArgs): UseDocumentUploadsResult => {
|
||||||
const [dropOverlayState, setDropOverlayState] = useState<DropOverlayState>({
|
const [dropOverlayState, setDropOverlayState] = useState<DropOverlayState>({
|
||||||
active: false,
|
active: false,
|
||||||
@@ -136,6 +137,7 @@ const useDocumentUploads = ({
|
|||||||
const queueIdRef = useRef(0);
|
const queueIdRef = useRef(0);
|
||||||
const [uploadQueue, setUploadQueue] = useState<UploadQueueItem[]>([]);
|
const [uploadQueue, setUploadQueue] = useState<UploadQueueItem[]>([]);
|
||||||
const { showToast } = useStatusToast();
|
const { showToast } = useStatusToast();
|
||||||
|
const notifyApiError = useNotifyApiError();
|
||||||
|
|
||||||
const uploadFile = useCallback(
|
const uploadFile = useCallback(
|
||||||
async (file: File, targetFolderId: FolderId) => {
|
async (file: File, targetFolderId: FolderId) => {
|
||||||
|
|||||||
@@ -16,9 +16,7 @@ interface UseApiErrorOptions {
|
|||||||
onReport?: (payload: ReportPayload) => void;
|
onReport?: (payload: ReportPayload) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const noop = () => {};
|
export const normalizeMessage = (error: unknown): string => {
|
||||||
|
|
||||||
const normalizeMessage = (error: unknown): string => {
|
|
||||||
if (!error) return 'Something went wrong.';
|
if (!error) return 'Something went wrong.';
|
||||||
if (typeof (error as { trim?: () => string })?.trim === 'function') {
|
if (typeof (error as { trim?: () => string })?.trim === 'function') {
|
||||||
return (error as { trim: () => string }).trim();
|
return (error as { trim: () => string }).trim();
|
||||||
@@ -31,7 +29,7 @@ const normalizeMessage = (error: unknown): string => {
|
|||||||
|
|
||||||
const useApiError = ({
|
const useApiError = ({
|
||||||
logger = console,
|
logger = console,
|
||||||
onReport = noop,
|
onReport,
|
||||||
}: UseApiErrorOptions = {}) => {
|
}: UseApiErrorOptions = {}) => {
|
||||||
return useCallback(
|
return useCallback(
|
||||||
(
|
(
|
||||||
@@ -40,7 +38,9 @@ const useApiError = ({
|
|||||||
) => {
|
) => {
|
||||||
const normalizedMessage = message || normalizeMessage(error);
|
const normalizedMessage = message || normalizeMessage(error);
|
||||||
logger.error('[API]', normalizedMessage, error);
|
logger.error('[API]', normalizedMessage, error);
|
||||||
|
if (onReport) {
|
||||||
onReport({ message: normalizedMessage, variant, retry, error });
|
onReport({ message: normalizedMessage, variant, retry, error });
|
||||||
|
}
|
||||||
return normalizedMessage;
|
return normalizedMessage;
|
||||||
},
|
},
|
||||||
[logger, onReport],
|
[logger, onReport],
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { useCallback } from 'react';
|
||||||
|
import { useStatusToast, ToastVariant } from '../lib/context/StatusToastContext';
|
||||||
|
import { normalizeMessage } from './useApiError';
|
||||||
|
|
||||||
|
const useNotifyApiError = () => {
|
||||||
|
const { showToast } = useStatusToast();
|
||||||
|
|
||||||
|
return useCallback(
|
||||||
|
(error: unknown, fallbackMessage?: string, variant: ToastVariant = 'error') => {
|
||||||
|
const message = fallbackMessage || normalizeMessage(error);
|
||||||
|
console.error('[API]', message, error);
|
||||||
|
showToast(message, variant);
|
||||||
|
},
|
||||||
|
[showToast],
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useNotifyApiError;
|
||||||
@@ -15,8 +15,6 @@ import {
|
|||||||
} from '../lib/api/apiClient';
|
} from '../lib/api/apiClient';
|
||||||
import type { PasskeyId } from '../types/identifiers';
|
import type { PasskeyId } from '../types/identifiers';
|
||||||
|
|
||||||
type NotifyApiErrorFn = (error: unknown, message: string) => void;
|
|
||||||
|
|
||||||
type ApiError = {
|
type ApiError = {
|
||||||
response?: {
|
response?: {
|
||||||
status?: number;
|
status?: number;
|
||||||
@@ -69,8 +67,9 @@ type RevokePasskeyResult =
|
|||||||
| { ok: true }
|
| { ok: true }
|
||||||
| { ok: false; reason: RevokePasskeyFailureReason; message?: string };
|
| { ok: false; reason: RevokePasskeyFailureReason; message?: string };
|
||||||
|
|
||||||
|
import useNotifyApiError from '../hooks/useNotifyApiError';
|
||||||
|
|
||||||
interface UsePasskeysArgs {
|
interface UsePasskeysArgs {
|
||||||
notifyApiError: NotifyApiErrorFn;
|
|
||||||
token?: string | null;
|
token?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,13 +87,14 @@ interface UsePasskeysResult {
|
|||||||
) => Promise<RevokePasskeyResult>;
|
) => Promise<RevokePasskeyResult>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const usePasskeys = ({ notifyApiError, token }: UsePasskeysArgs): UsePasskeysResult => {
|
const usePasskeys = ({ token }: UsePasskeysArgs): UsePasskeysResult => {
|
||||||
const [passkeys, setPasskeys] = useState<PasskeyRecord[]>([]);
|
const [passkeys, setPasskeys] = useState<PasskeyRecord[]>([]);
|
||||||
const [passkeysSupported, setPasskeysSupported] = useState<boolean | null>(null);
|
const [passkeysSupported, setPasskeysSupported] = useState<boolean | null>(null);
|
||||||
const [passkeysLoading, setPasskeysLoading] = useState(false);
|
const [passkeysLoading, setPasskeysLoading] = useState(false);
|
||||||
const [registeringPasskey, setRegisteringPasskey] = useState(false);
|
const [registeringPasskey, setRegisteringPasskey] = useState(false);
|
||||||
const [revokingPasskeyId, setRevokingPasskeyId] = useState<PasskeyId | null>(null);
|
const [revokingPasskeyId, setRevokingPasskeyId] = useState<PasskeyId | null>(null);
|
||||||
const { showToast } = useStatusToast();
|
const { showToast } = useStatusToast();
|
||||||
|
const notifyApiError = useNotifyApiError();
|
||||||
|
|
||||||
const refreshPasskeys = useCallback(async (): Promise<void> => {
|
const refreshPasskeys = useCallback(async (): Promise<void> => {
|
||||||
if (!token) {
|
if (!token) {
|
||||||
|
|||||||
Reference in New Issue
Block a user