feat: Refactor frontend authentication and tenant switching to use global state
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import type { Dispatch, SetStateAction } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useAppState } from '../lib/store/appState';
|
||||
import { TAG_FILTER_UNTAGGED } from './workspaceUtils';
|
||||
import { listDocuments } from '../lib/api/apiClient';
|
||||
import type { Identifier } from '../types/identifiers';
|
||||
@@ -15,7 +16,6 @@ import useNotifyApiError from '../hooks/useNotifyApiError';
|
||||
|
||||
interface UseDocumentsSearchArgs {
|
||||
api: ApiClient;
|
||||
token?: string | null;
|
||||
selectedFolder?: Identifier | 'root' | null;
|
||||
locationPathname?: string;
|
||||
isDocumentsRoute?: boolean;
|
||||
@@ -65,7 +65,6 @@ interface UseDocumentsSearchResult {
|
||||
|
||||
const useDocumentsSearch = ({
|
||||
api,
|
||||
token,
|
||||
selectedFolder,
|
||||
locationPathname,
|
||||
isDocumentsRoute,
|
||||
@@ -75,6 +74,7 @@ const useDocumentsSearch = ({
|
||||
setSearchIncludeDescendants,
|
||||
documentsManager,
|
||||
}: UseDocumentsSearchArgs): UseDocumentsSearchResult => {
|
||||
const { token } = useAppState();
|
||||
const [searchQuery, setSearchQuery] = useState<string>('');
|
||||
const [activeTagFilters, setActiveTagFilters] = useState<Identifier[]>([]);
|
||||
const [activeCorrespondentFilters, setActiveCorrespondentFilters] = useState<Identifier[]>([]);
|
||||
|
||||
@@ -3,15 +3,9 @@ import type { MutableRefObject } from 'react';
|
||||
import { clearAuthToken, logoutSession, refreshSession, setAuthToken } from '../../lib/api/apiClient';
|
||||
import { useStatusToast } from '../../lib/context/StatusToastContext';
|
||||
|
||||
type AppStatus = string;
|
||||
import { useAppDispatch, useAppState } from '../../lib/store/appState';
|
||||
|
||||
type AppDispatch = (action: { type: string;[key: string]: unknown }) => void;
|
||||
|
||||
interface UseAuthManagerArgs {
|
||||
token?: string | null;
|
||||
appStatus: AppStatus;
|
||||
appDispatch: AppDispatch;
|
||||
}
|
||||
interface UseAuthManagerArgs { }
|
||||
|
||||
interface UseAuthManagerResult {
|
||||
tokenRef: MutableRefObject<string | null>;
|
||||
@@ -19,11 +13,9 @@ interface UseAuthManagerResult {
|
||||
handleLogout: () => Promise<void>;
|
||||
}
|
||||
|
||||
const useAuthManager = ({
|
||||
token,
|
||||
appStatus,
|
||||
appDispatch,
|
||||
}: UseAuthManagerArgs): UseAuthManagerResult => {
|
||||
const useAuthManager = (_: UseAuthManagerArgs = {}): UseAuthManagerResult => {
|
||||
const { token, status: appStatus } = useAppState();
|
||||
const appDispatch = useAppDispatch();
|
||||
const tokenRef = useRef<string | null>(token);
|
||||
const initialRefreshAttemptedRef = useRef(Boolean(token));
|
||||
const { showToast } = useStatusToast();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
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 {
|
||||
@@ -74,7 +75,6 @@ interface DocumentTagExtras {
|
||||
import useNotifyApiError from '../../hooks/useNotifyApiError';
|
||||
|
||||
interface UseDocumentMutationsArgs {
|
||||
token?: string | null;
|
||||
documentLookup: Map<DocumentId, Document>;
|
||||
folderLabelMap: Map<FolderId, string>;
|
||||
ensureFolderData: EnsureFolderData;
|
||||
@@ -143,7 +143,6 @@ const normalizeDocumentId = (value: unknown): DocumentId | null => {
|
||||
};
|
||||
|
||||
const useDocumentMutations = ({
|
||||
token,
|
||||
documentLookup,
|
||||
folderLabelMap,
|
||||
ensureFolderData,
|
||||
@@ -338,10 +337,6 @@ const useDocumentMutations = ({
|
||||
|
||||
const handleThumbnailRegeneration = useCallback(
|
||||
async (documentId: DocumentId) => {
|
||||
if (!token) {
|
||||
showToast('Log in to manage assets.', 'error');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await queueDocumentReanalysis(documentId, { force: true });
|
||||
showToast('Document re-analysis queued.', 'info');
|
||||
@@ -351,7 +346,7 @@ const useDocumentMutations = ({
|
||||
notifyApiError(error, message);
|
||||
}
|
||||
},
|
||||
[token, refreshCurrentFolder, notifyApiError, showToast],
|
||||
[refreshCurrentFolder, notifyApiError, showToast],
|
||||
);
|
||||
|
||||
const handleDocumentsDelete = useCallback(
|
||||
@@ -360,11 +355,6 @@ const useDocumentMutations = ({
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!token) {
|
||||
showToast('Log in to manage documents.', 'error');
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
await Promise.all(documentIds.map((documentId) => trashDocument(documentId)));
|
||||
|
||||
@@ -386,8 +376,6 @@ const useDocumentMutations = ({
|
||||
}
|
||||
},
|
||||
[
|
||||
token,
|
||||
|
||||
removeDocumentsFromCaches,
|
||||
previewDocumentId,
|
||||
closeDocumentPreview,
|
||||
@@ -625,12 +613,6 @@ const useDocumentMutations = ({
|
||||
|
||||
const handleFolderDelete = useCallback(
|
||||
async (folderId?: FolderId, { showMessage = true }: MessageOptions = {}) => {
|
||||
if (!token) {
|
||||
if (showMessage) {
|
||||
showToast('Log in to manage folders.', 'error');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (!folderId || folderId === 'root') {
|
||||
if (showMessage) {
|
||||
showToast('The root folder cannot be removed.', 'error');
|
||||
@@ -695,7 +677,6 @@ const useDocumentMutations = ({
|
||||
}
|
||||
},
|
||||
[
|
||||
token,
|
||||
ensureFolderData,
|
||||
selectedFolder,
|
||||
folderNodes,
|
||||
|
||||
@@ -145,15 +145,8 @@ const useDocumentsWorkspace = ({
|
||||
: [];
|
||||
const { showToast } = useStatusToast();
|
||||
const notifyApiError = useNotifyApiError();
|
||||
|
||||
const [creatingFolder, setCreatingFolder] = useState(false);
|
||||
|
||||
const { tokenRef, handleLogout } = useAuthManager({
|
||||
token,
|
||||
appStatus,
|
||||
appDispatch,
|
||||
});
|
||||
|
||||
const { handleLogout } = useAuthManager({});
|
||||
|
||||
const tagRemovalCursorActiveRef = useRef(false);
|
||||
const tenantIdRef = useRef(currentTenantId);
|
||||
@@ -287,63 +280,59 @@ const useDocumentsWorkspace = ({
|
||||
folderId: FolderNodeId,
|
||||
options: { includeDocuments?: boolean } = {}
|
||||
) => {
|
||||
try {
|
||||
const path = folderId === 'root' ? 'root' : folderId;
|
||||
const includeDocuments = options.includeDocuments ?? true;
|
||||
const params: Record<string, unknown> = {
|
||||
include_documents: includeDocuments,
|
||||
sort: activeSortFieldRef.current,
|
||||
dir: activeSortDirectionRef.current,
|
||||
};
|
||||
|
||||
const data = await listFolderContents(path, params);
|
||||
const path = folderId === 'root' ? 'root' : folderId;
|
||||
const includeDocuments = options.includeDocuments ?? true;
|
||||
const params: Record<string, unknown> = {
|
||||
include_documents: includeDocuments,
|
||||
sort: activeSortFieldRef.current,
|
||||
dir: activeSortDirectionRef.current,
|
||||
};
|
||||
|
||||
// Only update UI state if we are fetching for the currently selected folder
|
||||
if (folderId === selectedFolder) {
|
||||
// Update documents state if included
|
||||
if (includeDocuments) {
|
||||
setDocuments((data.documents || []) as Document[]);
|
||||
}
|
||||
setCurrentSubfolders((data.subfolders || []) as any[]);
|
||||
const data = await listFolderContents(path, params);
|
||||
|
||||
// Update selection state based on new documents
|
||||
if (includeDocuments) {
|
||||
const docs = (data.documents || []) as Document[];
|
||||
const subfolders = (data.subfolders || []) as any[];
|
||||
// Only update UI state if we are fetching for the currently selected folder
|
||||
if (folderId === selectedFolder) {
|
||||
// Update documents state if included
|
||||
if (includeDocuments) {
|
||||
setDocuments((data.documents || []) as Document[]);
|
||||
}
|
||||
setCurrentSubfolders((data.subfolders || []) as any[]);
|
||||
|
||||
const availableDocKeys = docs
|
||||
.map((doc) => createDocumentEntryKey(doc?.id as Identifier))
|
||||
.filter(Boolean);
|
||||
const availableDocKeySet = new Set(availableDocKeys);
|
||||
const availableFolderKeys = new Set(
|
||||
subfolders
|
||||
.map((folder) => createFolderEntryKey(folder?.id as Identifier))
|
||||
.filter(Boolean),
|
||||
);
|
||||
// Update selection state based on new documents
|
||||
if (includeDocuments) {
|
||||
const docs = (data.documents || []) as Document[];
|
||||
const subfolders = (data.subfolders || []) as any[];
|
||||
|
||||
setSelectedEntries((previous) => {
|
||||
const previousFolderKeys = previous
|
||||
.filter(isFolderEntry)
|
||||
.filter((key) => availableFolderKeys.has(key));
|
||||
const previousDocKeys = previous.filter(isDocumentEntry);
|
||||
const nextDocKeys = previousDocKeys.filter((key) => availableDocKeySet.has(key));
|
||||
const mergedSelection = [...previousFolderKeys, ...nextDocKeys];
|
||||
return mergedSelection;
|
||||
});
|
||||
}
|
||||
const availableDocKeys = docs
|
||||
.map((doc) => createDocumentEntryKey(doc?.id as Identifier))
|
||||
.filter(Boolean);
|
||||
const availableDocKeySet = new Set(availableDocKeys);
|
||||
const availableFolderKeys = new Set(
|
||||
subfolders
|
||||
.map((folder) => createFolderEntryKey(folder?.id as Identifier))
|
||||
.filter(Boolean),
|
||||
);
|
||||
|
||||
setSelectedEntries((previous) => {
|
||||
const previousFolderKeys = previous
|
||||
.filter(isFolderEntry)
|
||||
.filter((key) => availableFolderKeys.has(key));
|
||||
const previousDocKeys = previous.filter(isDocumentEntry);
|
||||
const nextDocKeys = previousDocKeys.filter((key) => availableDocKeySet.has(key));
|
||||
const mergedSelection = [...previousFolderKeys, ...nextDocKeys];
|
||||
return mergedSelection;
|
||||
});
|
||||
}
|
||||
return data; // Return data for consumers (e.g. useDocumentMutations)
|
||||
} catch (error) {
|
||||
notifyApiError(error, 'Failed to fetch folder contents');
|
||||
throw error;
|
||||
}
|
||||
return data; // Return data for consumers (e.g. useDocumentMutations)
|
||||
|
||||
},
|
||||
[
|
||||
activeSortFieldRef,
|
||||
activeSortDirectionRef,
|
||||
setDocuments,
|
||||
setSelectedEntries,
|
||||
notifyApiError,
|
||||
setCurrentSubfolders,
|
||||
selectedFolder,
|
||||
]
|
||||
@@ -351,9 +340,11 @@ const useDocumentsWorkspace = ({
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedFolder) {
|
||||
ensureFolderData(selectedFolder);
|
||||
ensureFolderData(selectedFolder).catch((error) => {
|
||||
notifyApiError(error, 'Failed to fetch folder contents');
|
||||
});
|
||||
}
|
||||
}, [selectedFolder, documentsSortField, documentsSortDirection, ensureFolderData]);
|
||||
}, [selectedFolder, documentsSortField, documentsSortDirection, ensureFolderData, notifyApiError]);
|
||||
|
||||
const {
|
||||
searchQuery,
|
||||
@@ -369,7 +360,6 @@ const useDocumentsWorkspace = ({
|
||||
documentsFilterValue,
|
||||
} = useDocumentsSearch({
|
||||
api: apiClient,
|
||||
token,
|
||||
selectedFolder,
|
||||
locationPathname: location.pathname,
|
||||
isDocumentsRoute,
|
||||
@@ -542,9 +532,7 @@ const useDocumentsWorkspace = ({
|
||||
refreshPasskeys,
|
||||
registerPasskey,
|
||||
revokePasskey,
|
||||
} = usePasskeys({
|
||||
token,
|
||||
});
|
||||
} = usePasskeys({});
|
||||
|
||||
const resolveTargetDocumentIds = useCallback(
|
||||
(candidateIds) => {
|
||||
@@ -585,7 +573,6 @@ const useDocumentsWorkspace = ({
|
||||
resetUploadsState,
|
||||
handleFileSelection,
|
||||
} = useDocumentUploads({
|
||||
token,
|
||||
selectedFolder,
|
||||
currentFolderName,
|
||||
ensureFolderData,
|
||||
@@ -709,7 +696,6 @@ const useDocumentsWorkspace = ({
|
||||
handleDocumentIssuedUpdate,
|
||||
handleTagRemove,
|
||||
} = useDocumentMutations({
|
||||
token,
|
||||
documentLookup,
|
||||
folderLabelMap,
|
||||
ensureFolderData,
|
||||
@@ -749,7 +735,6 @@ const useDocumentsWorkspace = ({
|
||||
handleFolderDelete,
|
||||
folderClickHandlers,
|
||||
} = useFolderTreeActions({
|
||||
token,
|
||||
folderNodes,
|
||||
setFolderNodes,
|
||||
selectedFolder,
|
||||
@@ -1078,13 +1063,7 @@ const useDocumentsWorkspace = ({
|
||||
|
||||
const { handleTenantSelect } = useTenantManager({
|
||||
currentTenantId,
|
||||
resetWorkspaceState,
|
||||
refreshTags,
|
||||
refreshCorrespondents,
|
||||
loadFolder,
|
||||
handleDocumentsViewModeChange,
|
||||
tokenRef,
|
||||
tenantIdRef,
|
||||
});
|
||||
|
||||
const sessionContext = {
|
||||
|
||||
@@ -1,68 +1,65 @@
|
||||
import { MutableRefObject, useCallback } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import type { FolderId, TenantId } from '../../types/identifiers';
|
||||
import type { TenantId } from '../../types/identifiers';
|
||||
import { useStatusToast } from '../../lib/context/StatusToastContext';
|
||||
import { useAppDispatch } from '../../lib/store/appState';
|
||||
|
||||
import { api, listTenants, switchTenant } from '../../lib/api/apiClient';
|
||||
|
||||
import useNotifyApiError from '../../hooks/useNotifyApiError';
|
||||
|
||||
interface TenantOption {
|
||||
id?: TenantId;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
import useNotifyApiError from '../../hooks/useNotifyApiError';
|
||||
|
||||
interface UseTenantManagerOptions {
|
||||
currentTenantId: TenantId | null;
|
||||
resetWorkspaceState: () => void;
|
||||
refreshTags: () => Promise<void>;
|
||||
refreshCorrespondents: () => Promise<void>;
|
||||
loadFolder: (folderId: FolderId, options?: { preserveSearch?: boolean }) => Promise<void>;
|
||||
handleDocumentsViewModeChange: (mode: string) => void;
|
||||
tokenRef?: MutableRefObject<string | null>;
|
||||
tenantIdRef?: MutableRefObject<TenantId | null>;
|
||||
}
|
||||
|
||||
const useTenantManager = ({
|
||||
currentTenantId,
|
||||
resetWorkspaceState,
|
||||
refreshTags,
|
||||
refreshCorrespondents,
|
||||
loadFolder,
|
||||
handleDocumentsViewModeChange,
|
||||
tokenRef,
|
||||
tenantIdRef,
|
||||
}: UseTenantManagerOptions) => {
|
||||
const { showToast } = useStatusToast();
|
||||
const notifyApiError = useNotifyApiError();
|
||||
const navigate = useNavigate();
|
||||
const appDispatch = useAppDispatch();
|
||||
|
||||
const handleTenantSelect = useCallback(
|
||||
async (tenantOption: TenantOption | null, { refreshOnly = false }: { refreshOnly?: boolean } = {}) => {
|
||||
const requestedTenantId = tenantOption?.id ?? null;
|
||||
|
||||
// 1. Guard Clauses
|
||||
if (!refreshOnly && (!requestedTenantId || requestedTenantId === currentTenantId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 2. Refresh Logic
|
||||
if (refreshOnly) {
|
||||
const data = await listTenants();
|
||||
appDispatch({
|
||||
type: 'SET_TENANTS',
|
||||
tenants: data,
|
||||
});
|
||||
appDispatch({ type: 'SET_TENANTS', tenants: data });
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. Switch Logic
|
||||
const data = await switchTenant(requestedTenantId);
|
||||
|
||||
if (!data?.access_token) {
|
||||
throw new Error('Missing access token in tenant switch response.');
|
||||
}
|
||||
|
||||
appDispatch({ type: 'LOGOUT' });
|
||||
resetWorkspaceState();
|
||||
// 4. Reset UI to safe state BEFORE updating global auth
|
||||
// This prevents old components from reacting to state changes.
|
||||
|
||||
|
||||
// 5. Update Global State IMMEDIATELY
|
||||
// Don't wait for navigation. Data consistency comes first.
|
||||
handleDocumentsViewModeChange('list');
|
||||
api.defaults.headers.common.Authorization = `Bearer ${data.access_token}`;
|
||||
|
||||
appDispatch({
|
||||
type: 'LOGIN_SUCCESS',
|
||||
@@ -70,26 +67,16 @@ const useTenantManager = ({
|
||||
tenant: data.tenant || null,
|
||||
});
|
||||
|
||||
api.defaults.headers.common.Authorization = `Bearer ${data.access_token}`;
|
||||
if (tokenRef) {
|
||||
tokenRef.current = data.access_token;
|
||||
}
|
||||
if (tenantIdRef) {
|
||||
tenantIdRef.current = data?.tenant?.id ?? null;
|
||||
}
|
||||
|
||||
if (Array.isArray(data?.tenants)) {
|
||||
appDispatch({ type: 'SET_TENANTS', tenants: data.tenants });
|
||||
}
|
||||
|
||||
handleDocumentsViewModeChange('list');
|
||||
navigate('/documents', { replace: true });
|
||||
|
||||
await Promise.all([refreshTags(), refreshCorrespondents()]);
|
||||
await loadFolder('root', { preserveSearch: false });
|
||||
|
||||
const tenantLabel = data?.tenant?.name || data?.tenant?.id || 'tenant';
|
||||
showToast(`Switched to ${tenantLabel}.`, 'info');
|
||||
|
||||
// 5. Handle UI/Navigation changes AFTER state is secure
|
||||
navigate('/documents', { replace: true });
|
||||
|
||||
} catch (error) {
|
||||
notifyApiError(error, 'Failed to switch tenant.');
|
||||
}
|
||||
@@ -98,15 +85,9 @@ const useTenantManager = ({
|
||||
appDispatch,
|
||||
currentTenantId,
|
||||
handleDocumentsViewModeChange,
|
||||
loadFolder,
|
||||
navigate,
|
||||
notifyApiError,
|
||||
refreshCorrespondents,
|
||||
refreshTags,
|
||||
resetWorkspaceState,
|
||||
showToast,
|
||||
tenantIdRef,
|
||||
tokenRef,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@ interface FolderClickHandlers {
|
||||
import useNotifyApiError from '../../../hooks/useNotifyApiError';
|
||||
|
||||
interface UseFolderTreeActionsOptions {
|
||||
token?: string | null;
|
||||
folderNodes: Map<FolderKey, FolderNode>;
|
||||
setFolderNodes: (updater: (prev: Map<FolderKey, FolderNode>) => Map<FolderKey, FolderNode>) => void;
|
||||
selectedFolder: FolderKey;
|
||||
@@ -49,7 +48,6 @@ interface UseFolderTreeActionsOptions {
|
||||
}
|
||||
|
||||
const useFolderTreeActions = ({
|
||||
token,
|
||||
folderNodes,
|
||||
setFolderNodes,
|
||||
selectedFolder,
|
||||
@@ -181,10 +179,6 @@ const useFolderTreeActions = ({
|
||||
|
||||
const handleFolderRename = useCallback(
|
||||
async (folderId: FolderKey, nextName: string) => {
|
||||
if (!token) {
|
||||
showToast('Log in to rename folders.', 'error');
|
||||
return false;
|
||||
}
|
||||
const trimmed = nextName?.trim?.() || '';
|
||||
if (!trimmed) {
|
||||
showToast('Folder name cannot be empty.', 'error');
|
||||
@@ -214,16 +208,11 @@ const useFolderTreeActions = ({
|
||||
notifyApiError,
|
||||
setFolderNodes,
|
||||
showToast,
|
||||
token,
|
||||
],
|
||||
);
|
||||
|
||||
const handleFolderCreate = useCallback(
|
||||
async (name: string, parentId?: FolderKey | null) => {
|
||||
if (!token) {
|
||||
showToast('Log in to create folders.', 'error');
|
||||
return false;
|
||||
}
|
||||
if (!name.trim()) {
|
||||
showToast('Folder name cannot be empty.', 'error');
|
||||
return false;
|
||||
@@ -291,18 +280,11 @@ const useFolderTreeActions = ({
|
||||
setCreatingFolder,
|
||||
setFolderNodes,
|
||||
showToast,
|
||||
token,
|
||||
],
|
||||
);
|
||||
|
||||
const handleFolderDelete = useCallback(
|
||||
async (folderId: FolderKey, { showMessage = true }: MessageOptions = {}) => {
|
||||
if (!token) {
|
||||
if (showMessage) {
|
||||
showToast('Log in to manage folders.', 'error');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (!folderId || folderId === 'root') {
|
||||
if (showMessage) {
|
||||
showToast('The root folder cannot be removed.', 'error');
|
||||
@@ -352,7 +334,6 @@ const useFolderTreeActions = ({
|
||||
}
|
||||
},
|
||||
[
|
||||
token,
|
||||
folderNodes,
|
||||
notifyApiError,
|
||||
selectedFolder,
|
||||
|
||||
@@ -91,7 +91,6 @@ const mapFilesToEntries = (filesInput?: FileList | File[] | null): FileEntry[] =
|
||||
};
|
||||
|
||||
interface UseDocumentUploadsArgs {
|
||||
token?: string | null;
|
||||
selectedFolder?: FolderId;
|
||||
currentFolderName?: string | null;
|
||||
ensureFolderData: (folderId: FolderId, options?: { [key: string]: unknown }) => Promise<void>;
|
||||
@@ -121,7 +120,6 @@ interface UseDocumentUploadsResult {
|
||||
import useNotifyApiError from '../../../hooks/useNotifyApiError';
|
||||
|
||||
const useDocumentUploads = ({
|
||||
token,
|
||||
selectedFolder,
|
||||
currentFolderName,
|
||||
ensureFolderData,
|
||||
@@ -370,19 +368,6 @@ const useDocumentUploads = ({
|
||||
|
||||
const queueItems = appendQueueItems(entries, targetFolderId);
|
||||
|
||||
if (!token) {
|
||||
queueItems.forEach((item) => {
|
||||
const patch = {
|
||||
status: 'error',
|
||||
error: 'Please log in before uploading.',
|
||||
code: null,
|
||||
};
|
||||
updateQueueItem(item.id, patch);
|
||||
Object.assign(item, patch);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
folderPathCacheRef.current.clear();
|
||||
|
||||
@@ -461,7 +446,6 @@ const useDocumentUploads = ({
|
||||
}
|
||||
},
|
||||
[
|
||||
token,
|
||||
ensureFolderPathOnServer,
|
||||
uploadFile,
|
||||
refreshCurrentFolder,
|
||||
@@ -497,7 +481,6 @@ const useDocumentUploads = ({
|
||||
|
||||
useFileDrop({
|
||||
shellRef,
|
||||
token,
|
||||
currentFolderName,
|
||||
selectedFolder,
|
||||
handleFileDrop,
|
||||
|
||||
@@ -9,7 +9,6 @@ interface DropOverlayState {
|
||||
|
||||
interface UseFileDropOptions {
|
||||
shellRef: MutableRefObject<HTMLElement | null>;
|
||||
token?: string | null;
|
||||
currentFolderName: string | null;
|
||||
selectedFolder: FolderId;
|
||||
handleFileDrop: (dataTransfer: DataTransfer, folderId: FolderId) => Promise<void>;
|
||||
@@ -21,7 +20,6 @@ interface UseFileDropOptions {
|
||||
|
||||
const useFileDrop = ({
|
||||
shellRef,
|
||||
token,
|
||||
currentFolderName,
|
||||
selectedFolder,
|
||||
handleFileDrop,
|
||||
@@ -32,12 +30,6 @@ const useFileDrop = ({
|
||||
}: UseFileDropOptions) => {
|
||||
|
||||
useEffect(() => {
|
||||
if (!token) {
|
||||
setDropOverlayState((prev) => ({ ...prev, active: false }));
|
||||
dragCounterRef.current = 0;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const handleDragEnter = (event: DragEvent) => {
|
||||
if (!hasFiles(event)) return;
|
||||
event.preventDefault();
|
||||
@@ -86,7 +78,6 @@ const useFileDrop = ({
|
||||
setDropOverlayState((prev) => ({ ...prev, active: false }));
|
||||
};
|
||||
}, [
|
||||
token,
|
||||
handleFileDrop,
|
||||
currentFolderName,
|
||||
defaultFolderName,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import { useAppState } from '../lib/store/appState';
|
||||
import { useStatusToast } from '../lib/context/StatusToastContext';
|
||||
/* global PublicKeyCredentialCreationOptions, CredentialCreationOptions */
|
||||
|
||||
@@ -69,9 +70,7 @@ type RevokePasskeyResult =
|
||||
|
||||
import useNotifyApiError from '../hooks/useNotifyApiError';
|
||||
|
||||
interface UsePasskeysArgs {
|
||||
token?: string | null;
|
||||
}
|
||||
interface UsePasskeysArgs { }
|
||||
|
||||
interface UsePasskeysResult {
|
||||
passkeys: PasskeyRecord[];
|
||||
@@ -87,7 +86,8 @@ interface UsePasskeysResult {
|
||||
) => Promise<RevokePasskeyResult>;
|
||||
}
|
||||
|
||||
const usePasskeys = ({ token }: UsePasskeysArgs): UsePasskeysResult => {
|
||||
const usePasskeys = (_: UsePasskeysArgs = {}): UsePasskeysResult => {
|
||||
const { token } = useAppState();
|
||||
const [passkeys, setPasskeys] = useState<PasskeyRecord[]>([]);
|
||||
const [passkeysSupported, setPasskeysSupported] = useState<boolean | null>(null);
|
||||
const [passkeysLoading, setPasskeysLoading] = useState(false);
|
||||
|
||||
Reference in New Issue
Block a user