Remove document link and download URL management from frontend components
This commit is contained in:
@@ -1,23 +1,15 @@
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useCallback, useEffect, useRef } from 'react';
|
||||
import type {
|
||||
Dispatch,
|
||||
MutableRefObject,
|
||||
SetStateAction,
|
||||
} from 'react';
|
||||
import { fetchDocument } from '../lib/apiClient';
|
||||
import type { DocumentId } from '../types/identifiers';
|
||||
|
||||
type FolderId = DocumentId | 'root';
|
||||
|
||||
import type { Document } from '../types/documents';
|
||||
|
||||
type DocumentLink = {
|
||||
url?: string;
|
||||
mimeType?: string | null;
|
||||
filename?: string | null;
|
||||
expiresAt?: number;
|
||||
};
|
||||
|
||||
type NavigateHandler = (path: string, options?: { replace?: boolean }) => void;
|
||||
|
||||
interface UseDocumentPreviewArgs {
|
||||
@@ -42,13 +34,10 @@ interface UseDocumentPreviewArgs {
|
||||
}
|
||||
|
||||
interface UseDocumentPreviewResult {
|
||||
documentLinks: Map<DocumentId, DocumentLink>;
|
||||
ensureDownloadUrl: (documentId: DocumentId, options?: { force?: boolean }) => Promise<DocumentLink | null>;
|
||||
ensurePreviewData: (documentId: DocumentId) => Promise<Document | null>;
|
||||
openDocumentPreview: (documentId: DocumentId, options?: { replace?: boolean }) => void;
|
||||
closeDocumentPreview: (folderId?: FolderId) => void;
|
||||
resetPreviewState: () => void;
|
||||
removeDocumentLinks: (ids: DocumentId[]) => void;
|
||||
}
|
||||
|
||||
const useDocumentPreview = ({
|
||||
@@ -62,105 +51,20 @@ const useDocumentPreview = ({
|
||||
detailPanelControlRef,
|
||||
setActivePreviewId,
|
||||
}: UseDocumentPreviewArgs): UseDocumentPreviewResult => {
|
||||
const [documentLinks, setDocumentLinks] = useState<Map<DocumentId, DocumentLink>>(() => new Map());
|
||||
const previewInflightRef = useRef<Map<DocumentId, Promise<DocumentLink | null>>>(new Map());
|
||||
const previewReturnPathRef = useRef<string | null>(null);
|
||||
|
||||
const resetPreviewState = useCallback(() => {
|
||||
setDocumentLinks(() => new Map());
|
||||
previewInflightRef.current = new Map();
|
||||
previewReturnPathRef.current = null;
|
||||
}, []);
|
||||
|
||||
const removeDocumentLinks = useCallback((ids: DocumentId[]) => {
|
||||
if (!Array.isArray(ids) || ids.length === 0) {
|
||||
return;
|
||||
}
|
||||
setDocumentLinks((prev) => {
|
||||
if (!prev.size) {
|
||||
return prev;
|
||||
}
|
||||
let changed = false;
|
||||
const next = new Map(prev);
|
||||
ids.forEach((id) => {
|
||||
if (next.delete(id)) {
|
||||
changed = true;
|
||||
}
|
||||
previewInflightRef.current.delete(id);
|
||||
});
|
||||
return changed ? next : prev;
|
||||
});
|
||||
}, []);
|
||||
|
||||
const ensureDownloadUrl = useCallback(
|
||||
async (documentId: DocumentId, { force = false }: { force?: boolean } = {}): Promise<DocumentLink | null> => {
|
||||
if (!documentId) return null;
|
||||
|
||||
const existing = documentLinks.get(documentId);
|
||||
const now = Date.now();
|
||||
const expiresAt = existing?.expiresAt ?? null;
|
||||
if (!force && existing && (!expiresAt || expiresAt > now)) {
|
||||
return existing;
|
||||
}
|
||||
|
||||
if (!force && previewInflightRef.current.has(documentId)) {
|
||||
return previewInflightRef.current.get(documentId) || null;
|
||||
}
|
||||
|
||||
const request: Promise<DocumentLink | null> = (async () => {
|
||||
try {
|
||||
const docResponse = await fetchDocument(documentId);
|
||||
const download = docResponse?.current_version?.download || null;
|
||||
const downloadUrl = download?.url;
|
||||
if (!downloadUrl) {
|
||||
throw new Error('Document missing download url');
|
||||
}
|
||||
|
||||
const entry: DocumentLink = {
|
||||
url: downloadUrl,
|
||||
mimeType: docResponse?.mime_type || null,
|
||||
filename: docResponse?.filename,
|
||||
expiresAt: download?.expires_at,
|
||||
};
|
||||
setDocumentLinks((prev) => {
|
||||
const next = new Map(prev);
|
||||
next.set(documentId, entry);
|
||||
return next;
|
||||
});
|
||||
return entry;
|
||||
} catch (error) {
|
||||
notifyApiError(error, 'Unable to fetch document preview.');
|
||||
throw error;
|
||||
} finally {
|
||||
previewInflightRef.current.delete(documentId);
|
||||
}
|
||||
})();
|
||||
|
||||
previewInflightRef.current.set(documentId, request);
|
||||
return request;
|
||||
},
|
||||
[documentLinks, notifyApiError],
|
||||
);
|
||||
|
||||
const ensurePreviewData = useCallback(
|
||||
async (documentId: DocumentId): Promise<Document | null> => {
|
||||
if (!documentId) return null;
|
||||
|
||||
const findInCache = () => documentsManager.getById(documentId);
|
||||
|
||||
let doc = findInCache();
|
||||
const doc = await documentsManager.ensure(documentId);
|
||||
|
||||
if (!doc) {
|
||||
doc = await documentsManager.ensure(documentId);
|
||||
}
|
||||
|
||||
if (!doc) {
|
||||
const fetched = await fetchDocument(documentId);
|
||||
const { canonical } = documentsManager.ingest([fetched as unknown]);
|
||||
doc = (canonical[0] as Document | undefined) || null;
|
||||
if (!doc) {
|
||||
throw new Error('Document metadata unavailable.');
|
||||
}
|
||||
throw new Error('Document metadata unavailable.');
|
||||
}
|
||||
|
||||
if (!previewReturnPathRef.current) {
|
||||
@@ -169,13 +73,11 @@ const useDocumentPreview = ({
|
||||
fallbackFolderId === 'root' ? '/documents' : `/documents/folder/${fallbackFolderId}`;
|
||||
}
|
||||
|
||||
await ensureDownloadUrl(documentId, { force: false });
|
||||
setActivePreviewId(documentId);
|
||||
return doc;
|
||||
},
|
||||
[
|
||||
documentsManager,
|
||||
ensureDownloadUrl,
|
||||
setActivePreviewId,
|
||||
],
|
||||
);
|
||||
@@ -228,13 +130,10 @@ const useDocumentPreview = ({
|
||||
}, [routeDocumentId, ensurePreviewData, notifyApiError, closeDocumentPreview]);
|
||||
|
||||
return {
|
||||
documentLinks,
|
||||
ensureDownloadUrl,
|
||||
ensurePreviewData,
|
||||
openDocumentPreview,
|
||||
closeDocumentPreview,
|
||||
resetPreviewState,
|
||||
removeDocumentLinks,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -2,11 +2,6 @@ import { useMemo } from 'react';
|
||||
import type { WorkspaceSelectionValue } from '../../app/WorkspaceSelectionContext';
|
||||
import type { Identifier } from '../../types/identifiers';
|
||||
|
||||
interface DocumentLinkLike {
|
||||
url?: string | null;
|
||||
mimeType?: string | null;
|
||||
}
|
||||
|
||||
export interface Breadcrumb {
|
||||
id?: Identifier;
|
||||
name?: string;
|
||||
@@ -68,8 +63,6 @@ export interface UseDocumentsPanelPropsArgs {
|
||||
handleBulkSelectionReanalyze?: (...args: unknown[]) => void;
|
||||
folderOptions?: unknown[];
|
||||
moveDocumentsToFolder?: (...args: unknown[]) => void;
|
||||
documentLinks?: Map<Identifier, DocumentLinkLike>;
|
||||
ensureDownloadUrl?: (documentId: Identifier, options?: { force?: boolean }) => Promise<DocumentLinkLike | null>;
|
||||
selectionValue: WorkspaceSelectionValue;
|
||||
currentTenantId?: Identifier | null;
|
||||
}
|
||||
@@ -124,8 +117,6 @@ const useDocumentsPanelProps = (props: UseDocumentsPanelPropsArgs) => {
|
||||
handleBulkSelectionReanalyze,
|
||||
folderOptions,
|
||||
moveDocumentsToFolder,
|
||||
documentLinks,
|
||||
ensureDownloadUrl,
|
||||
selectionValue,
|
||||
currentTenantId,
|
||||
} = props;
|
||||
@@ -181,8 +172,6 @@ const useDocumentsPanelProps = (props: UseDocumentsPanelPropsArgs) => {
|
||||
onBulkReanalyze: handleBulkSelectionReanalyze,
|
||||
folderOptions,
|
||||
onMoveDocumentsToFolder: moveDocumentsToFolder,
|
||||
documentLinks,
|
||||
ensureDownloadUrl,
|
||||
selectionValue,
|
||||
currentTenantId,
|
||||
}),
|
||||
@@ -233,8 +222,6 @@ const useDocumentsPanelProps = (props: UseDocumentsPanelPropsArgs) => {
|
||||
ensureAssetUrl,
|
||||
getDocumentAsset,
|
||||
folderOptions,
|
||||
documentLinks,
|
||||
ensureDownloadUrl,
|
||||
selectionValue,
|
||||
currentTenantId,
|
||||
],
|
||||
|
||||
@@ -51,8 +51,6 @@ interface DocumentsPanelProps extends DocumentsPanelInnerProps {
|
||||
selectionValue: WorkspaceSelectionValue;
|
||||
}
|
||||
|
||||
export type DocumentLinkLike = { url?: string | null; mimeType?: string | null };
|
||||
|
||||
export interface DocumentsViewProps {
|
||||
entries: DocumentsListEntry[];
|
||||
draggingDocumentIdsSet?: Set<Identifier> | null;
|
||||
@@ -85,7 +83,6 @@ export interface DocumentsViewProps {
|
||||
// Desk specific (optional for now or handled via intersection)
|
||||
tenantId?: Identifier | null;
|
||||
viewId?: string | null;
|
||||
ensureDownloadUrl?: (docId: Identifier, options?: { force?: boolean }) => Promise<any>;
|
||||
activeTagFilters?: Array<Identifier | null>;
|
||||
}
|
||||
|
||||
|
||||
@@ -432,12 +432,9 @@ const useDocumentsWorkspace = ({
|
||||
);
|
||||
|
||||
const {
|
||||
documentLinks,
|
||||
ensureDownloadUrl,
|
||||
openDocumentPreview,
|
||||
closeDocumentPreview,
|
||||
resetPreviewState,
|
||||
removeDocumentLinks,
|
||||
} = useDocumentPreview({
|
||||
routeDocumentId: previewDocumentId,
|
||||
documentsManager,
|
||||
@@ -719,14 +716,12 @@ const useDocumentsWorkspace = ({
|
||||
});
|
||||
|
||||
removeDocumentsFromLookup(Array.from(idSet));
|
||||
removeDocumentLinks(Array.from(idSet));
|
||||
},
|
||||
[
|
||||
setDocuments,
|
||||
setSearchResultIds,
|
||||
setFolderContents,
|
||||
removeDocumentsFromLookup,
|
||||
removeDocumentLinks,
|
||||
],
|
||||
);
|
||||
|
||||
@@ -1195,8 +1190,6 @@ const useDocumentsWorkspace = ({
|
||||
folderOptions,
|
||||
moveDocumentsToFolder,
|
||||
selectFolder,
|
||||
documentLinks,
|
||||
ensureDownloadUrl,
|
||||
selectionValue: selection,
|
||||
currentTenantId,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user