refactor: Decouple frontend API calls from a shared client instance

This commit is contained in:
2025-12-04 21:59:39 +01:00
parent d06fc133e7
commit 4308ff220b
13 changed files with 248 additions and 136 deletions
@@ -2,7 +2,7 @@ import { useCallback, useRef, useState } from 'react';
import type { Dispatch, MutableRefObject, SetStateAction } from 'react';
import useFileDrop from './useFileDrop';
import { DEFAULT_FOLDER_NAME, hasFiles } from '../../app/workspaceUtils';
import { fetchDocument } from '../../lib/apiClient';
import { fetchDocument, uploadDocument, resolveFolderPath } from '../../lib/apiClient';
import type { Identifier } from '../../types/identifiers';
type FolderId = Identifier | 'root' | null;
@@ -26,17 +26,6 @@ type UploadQueueItem = {
conflictDocumentId: Identifier | null;
};
interface UploadResponse {
reused?: boolean;
document?: unknown;
folder?: { id?: FolderId };
}
interface ApiClient {
post<T = UploadResponse>(url: string, payload: unknown): Promise<{ data: T; status?: number }>;
get<T = { document?: unknown }>(url: string): Promise<{ data: T }>;
}
type StatusLevel = 'success' | 'info' | 'warning' | 'error' | string;
type NotifyApiError = (error: unknown, fallbackMessage?: string) => void;
type SetStatusMessage = (message: string, level?: StatusLevel) => void;
@@ -59,7 +48,7 @@ interface FileSystemDirectoryReaderLike {
) => void;
}
interface FileSystemFileEntryLike {
interface FileSystemFileEntryLike extends FileSystemEntry {
isFile: true;
isDirectory: false;
name: string;
@@ -69,12 +58,19 @@ interface FileSystemFileEntryLike {
) => void;
}
interface FileSystemDirectoryEntryLike {
interface FileSystemDirectoryEntryLike extends FileSystemEntry {
isFile: false;
isDirectory: true;
name: string;
createReader: () => FileSystemDirectoryReaderLike;
}
const isFileEntry = (entry: FileSystemEntryLike): entry is FileSystemFileEntryLike => {
return entry.isFile && !entry.isDirectory;
};
const isDirectoryEntry = (entry: FileSystemEntryLike): entry is FileSystemDirectoryEntryLike => {
return entry.isDirectory && !entry.isFile && 'createReader' in entry;
};
const mapFilesToEntries = (filesInput?: FileList | File[] | null): FileEntry[] => {
if (!filesInput) {
@@ -96,7 +92,6 @@ const mapFilesToEntries = (filesInput?: FileList | File[] | null): FileEntry[] =
};
interface UseDocumentUploadsArgs {
apiClient: ApiClient;
token?: string | null;
selectedFolder?: FolderId;
currentFolderName?: string | null;
@@ -126,7 +121,6 @@ interface UseDocumentUploadsResult {
}
const useDocumentUploads = ({
apiClient,
token,
selectedFolder,
currentFolderName,
@@ -158,11 +152,10 @@ const useDocumentUploads = ({
}
try {
const { data, status } = await apiClient.post('/documents', formData);
const duplicate = data?.reused || status === 200;
const document = data?.document ?? data ?? null;
const { reused, document, status } = await uploadDocument(formData);
const duplicate = reused || status === 200;
return {
document,
document: document ?? null,
duplicate,
statusCode: status ?? (duplicate ? 200 : 201),
conflictDocumentId: null,
@@ -192,7 +185,7 @@ const useDocumentUploads = ({
throw wrapped;
}
},
[apiClient, notifyApiError, setStatusMessage],
[notifyApiError, setStatusMessage],
);
const appendQueueItems = useCallback((entries: FileEntry[], targetFolderId?: FolderId) => {
@@ -244,15 +237,12 @@ const useDocumentUploads = ({
segments: trimmedSegments,
};
const { data } = await apiClient.post<{ folder?: { id?: FolderId | null } }>(
'/folders/path',
payload,
);
const resolvedId = (data?.folder?.id ?? null) as FolderId;
const { folder } = await resolveFolderPath(payload);
const resolvedId = (folder?.id ?? null) as FolderId;
cache.set(cacheKey, resolvedId);
return resolvedId;
},
[apiClient],
[],
);
const extractFilesFromDataTransfer = useCallback(async (dataTransfer: DataTransfer) => {
@@ -294,10 +284,10 @@ const useDocumentUploads = ({
const walkEntry = async (entry: FileSystemEntryLike | null, ancestors: string[] = []) => {
if (!entry) return;
if (entry.isFile) {
if (isFileEntry(entry)) {
const file = await new Promise<File>((resolve, reject) => {
try {
(entry as unknown as FileSystemFileEntryLike).file(resolve, reject);
entry.file(resolve, reject);
} catch (error) {
console.warn('[Uploads] entry.file failed', error);
reject(error as Error);
@@ -306,9 +296,9 @@ const useDocumentUploads = ({
pushFile(file, ancestors);
return;
}
if (entry.isDirectory) {
if (isDirectoryEntry(entry)) {
const nextAncestors = entry.name ? [...ancestors, entry.name] : [...ancestors];
const reader = (entry as unknown as FileSystemDirectoryEntryLike).createReader();
const reader = entry.createReader();
const entries = await readAllEntries(reader);
for (const child of entries) {
await walkEntry(child, nextAncestors);