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 {
|
import type {
|
||||||
Dispatch,
|
Dispatch,
|
||||||
MutableRefObject,
|
MutableRefObject,
|
||||||
SetStateAction,
|
SetStateAction,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import { fetchDocument } from '../lib/apiClient';
|
|
||||||
import type { DocumentId } from '../types/identifiers';
|
import type { DocumentId } from '../types/identifiers';
|
||||||
|
|
||||||
type FolderId = DocumentId | 'root';
|
type FolderId = DocumentId | 'root';
|
||||||
|
|
||||||
import type { Document } from '../types/documents';
|
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;
|
type NavigateHandler = (path: string, options?: { replace?: boolean }) => void;
|
||||||
|
|
||||||
interface UseDocumentPreviewArgs {
|
interface UseDocumentPreviewArgs {
|
||||||
@@ -42,13 +34,10 @@ interface UseDocumentPreviewArgs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface UseDocumentPreviewResult {
|
interface UseDocumentPreviewResult {
|
||||||
documentLinks: Map<DocumentId, DocumentLink>;
|
|
||||||
ensureDownloadUrl: (documentId: DocumentId, options?: { force?: boolean }) => Promise<DocumentLink | null>;
|
|
||||||
ensurePreviewData: (documentId: DocumentId) => Promise<Document | null>;
|
ensurePreviewData: (documentId: DocumentId) => Promise<Document | null>;
|
||||||
openDocumentPreview: (documentId: DocumentId, options?: { replace?: boolean }) => void;
|
openDocumentPreview: (documentId: DocumentId, options?: { replace?: boolean }) => void;
|
||||||
closeDocumentPreview: (folderId?: FolderId) => void;
|
closeDocumentPreview: (folderId?: FolderId) => void;
|
||||||
resetPreviewState: () => void;
|
resetPreviewState: () => void;
|
||||||
removeDocumentLinks: (ids: DocumentId[]) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const useDocumentPreview = ({
|
const useDocumentPreview = ({
|
||||||
@@ -62,106 +51,21 @@ const useDocumentPreview = ({
|
|||||||
detailPanelControlRef,
|
detailPanelControlRef,
|
||||||
setActivePreviewId,
|
setActivePreviewId,
|
||||||
}: UseDocumentPreviewArgs): UseDocumentPreviewResult => {
|
}: 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 previewReturnPathRef = useRef<string | null>(null);
|
||||||
|
|
||||||
const resetPreviewState = useCallback(() => {
|
const resetPreviewState = useCallback(() => {
|
||||||
setDocumentLinks(() => new Map());
|
|
||||||
previewInflightRef.current = new Map();
|
|
||||||
previewReturnPathRef.current = null;
|
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(
|
const ensurePreviewData = useCallback(
|
||||||
async (documentId: DocumentId): Promise<Document | null> => {
|
async (documentId: DocumentId): Promise<Document | null> => {
|
||||||
if (!documentId) return null;
|
if (!documentId) return null;
|
||||||
|
|
||||||
const findInCache = () => documentsManager.getById(documentId);
|
const doc = await documentsManager.ensure(documentId);
|
||||||
|
|
||||||
let doc = findInCache();
|
|
||||||
|
|
||||||
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) {
|
if (!doc) {
|
||||||
throw new Error('Document metadata unavailable.');
|
throw new Error('Document metadata unavailable.');
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!previewReturnPathRef.current) {
|
if (!previewReturnPathRef.current) {
|
||||||
const fallbackFolderId = doc?.folder_id || 'root';
|
const fallbackFolderId = doc?.folder_id || 'root';
|
||||||
@@ -169,13 +73,11 @@ const useDocumentPreview = ({
|
|||||||
fallbackFolderId === 'root' ? '/documents' : `/documents/folder/${fallbackFolderId}`;
|
fallbackFolderId === 'root' ? '/documents' : `/documents/folder/${fallbackFolderId}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
await ensureDownloadUrl(documentId, { force: false });
|
|
||||||
setActivePreviewId(documentId);
|
setActivePreviewId(documentId);
|
||||||
return doc;
|
return doc;
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
documentsManager,
|
documentsManager,
|
||||||
ensureDownloadUrl,
|
|
||||||
setActivePreviewId,
|
setActivePreviewId,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -228,13 +130,10 @@ const useDocumentPreview = ({
|
|||||||
}, [routeDocumentId, ensurePreviewData, notifyApiError, closeDocumentPreview]);
|
}, [routeDocumentId, ensurePreviewData, notifyApiError, closeDocumentPreview]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
documentLinks,
|
|
||||||
ensureDownloadUrl,
|
|
||||||
ensurePreviewData,
|
ensurePreviewData,
|
||||||
openDocumentPreview,
|
openDocumentPreview,
|
||||||
closeDocumentPreview,
|
closeDocumentPreview,
|
||||||
resetPreviewState,
|
resetPreviewState,
|
||||||
removeDocumentLinks,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,11 +2,6 @@ import { useMemo } from 'react';
|
|||||||
import type { WorkspaceSelectionValue } from '../../app/WorkspaceSelectionContext';
|
import type { WorkspaceSelectionValue } from '../../app/WorkspaceSelectionContext';
|
||||||
import type { Identifier } from '../../types/identifiers';
|
import type { Identifier } from '../../types/identifiers';
|
||||||
|
|
||||||
interface DocumentLinkLike {
|
|
||||||
url?: string | null;
|
|
||||||
mimeType?: string | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Breadcrumb {
|
export interface Breadcrumb {
|
||||||
id?: Identifier;
|
id?: Identifier;
|
||||||
name?: string;
|
name?: string;
|
||||||
@@ -68,8 +63,6 @@ export interface UseDocumentsPanelPropsArgs {
|
|||||||
handleBulkSelectionReanalyze?: (...args: unknown[]) => void;
|
handleBulkSelectionReanalyze?: (...args: unknown[]) => void;
|
||||||
folderOptions?: unknown[];
|
folderOptions?: unknown[];
|
||||||
moveDocumentsToFolder?: (...args: unknown[]) => void;
|
moveDocumentsToFolder?: (...args: unknown[]) => void;
|
||||||
documentLinks?: Map<Identifier, DocumentLinkLike>;
|
|
||||||
ensureDownloadUrl?: (documentId: Identifier, options?: { force?: boolean }) => Promise<DocumentLinkLike | null>;
|
|
||||||
selectionValue: WorkspaceSelectionValue;
|
selectionValue: WorkspaceSelectionValue;
|
||||||
currentTenantId?: Identifier | null;
|
currentTenantId?: Identifier | null;
|
||||||
}
|
}
|
||||||
@@ -124,8 +117,6 @@ const useDocumentsPanelProps = (props: UseDocumentsPanelPropsArgs) => {
|
|||||||
handleBulkSelectionReanalyze,
|
handleBulkSelectionReanalyze,
|
||||||
folderOptions,
|
folderOptions,
|
||||||
moveDocumentsToFolder,
|
moveDocumentsToFolder,
|
||||||
documentLinks,
|
|
||||||
ensureDownloadUrl,
|
|
||||||
selectionValue,
|
selectionValue,
|
||||||
currentTenantId,
|
currentTenantId,
|
||||||
} = props;
|
} = props;
|
||||||
@@ -181,8 +172,6 @@ const useDocumentsPanelProps = (props: UseDocumentsPanelPropsArgs) => {
|
|||||||
onBulkReanalyze: handleBulkSelectionReanalyze,
|
onBulkReanalyze: handleBulkSelectionReanalyze,
|
||||||
folderOptions,
|
folderOptions,
|
||||||
onMoveDocumentsToFolder: moveDocumentsToFolder,
|
onMoveDocumentsToFolder: moveDocumentsToFolder,
|
||||||
documentLinks,
|
|
||||||
ensureDownloadUrl,
|
|
||||||
selectionValue,
|
selectionValue,
|
||||||
currentTenantId,
|
currentTenantId,
|
||||||
}),
|
}),
|
||||||
@@ -233,8 +222,6 @@ const useDocumentsPanelProps = (props: UseDocumentsPanelPropsArgs) => {
|
|||||||
ensureAssetUrl,
|
ensureAssetUrl,
|
||||||
getDocumentAsset,
|
getDocumentAsset,
|
||||||
folderOptions,
|
folderOptions,
|
||||||
documentLinks,
|
|
||||||
ensureDownloadUrl,
|
|
||||||
selectionValue,
|
selectionValue,
|
||||||
currentTenantId,
|
currentTenantId,
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -51,8 +51,6 @@ interface DocumentsPanelProps extends DocumentsPanelInnerProps {
|
|||||||
selectionValue: WorkspaceSelectionValue;
|
selectionValue: WorkspaceSelectionValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DocumentLinkLike = { url?: string | null; mimeType?: string | null };
|
|
||||||
|
|
||||||
export interface DocumentsViewProps {
|
export interface DocumentsViewProps {
|
||||||
entries: DocumentsListEntry[];
|
entries: DocumentsListEntry[];
|
||||||
draggingDocumentIdsSet?: Set<Identifier> | null;
|
draggingDocumentIdsSet?: Set<Identifier> | null;
|
||||||
@@ -85,7 +83,6 @@ export interface DocumentsViewProps {
|
|||||||
// Desk specific (optional for now or handled via intersection)
|
// Desk specific (optional for now or handled via intersection)
|
||||||
tenantId?: Identifier | null;
|
tenantId?: Identifier | null;
|
||||||
viewId?: string | null;
|
viewId?: string | null;
|
||||||
ensureDownloadUrl?: (docId: Identifier, options?: { force?: boolean }) => Promise<any>;
|
|
||||||
activeTagFilters?: Array<Identifier | null>;
|
activeTagFilters?: Array<Identifier | null>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -432,12 +432,9 @@ const useDocumentsWorkspace = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
documentLinks,
|
|
||||||
ensureDownloadUrl,
|
|
||||||
openDocumentPreview,
|
openDocumentPreview,
|
||||||
closeDocumentPreview,
|
closeDocumentPreview,
|
||||||
resetPreviewState,
|
resetPreviewState,
|
||||||
removeDocumentLinks,
|
|
||||||
} = useDocumentPreview({
|
} = useDocumentPreview({
|
||||||
routeDocumentId: previewDocumentId,
|
routeDocumentId: previewDocumentId,
|
||||||
documentsManager,
|
documentsManager,
|
||||||
@@ -719,14 +716,12 @@ const useDocumentsWorkspace = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
removeDocumentsFromLookup(Array.from(idSet));
|
removeDocumentsFromLookup(Array.from(idSet));
|
||||||
removeDocumentLinks(Array.from(idSet));
|
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
setDocuments,
|
setDocuments,
|
||||||
setSearchResultIds,
|
setSearchResultIds,
|
||||||
setFolderContents,
|
setFolderContents,
|
||||||
removeDocumentsFromLookup,
|
removeDocumentsFromLookup,
|
||||||
removeDocumentLinks,
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1195,8 +1190,6 @@ const useDocumentsWorkspace = ({
|
|||||||
folderOptions,
|
folderOptions,
|
||||||
moveDocumentsToFolder,
|
moveDocumentsToFolder,
|
||||||
selectFolder,
|
selectFolder,
|
||||||
documentLinks,
|
|
||||||
ensureDownloadUrl,
|
|
||||||
selectionValue: selection,
|
selectionValue: selection,
|
||||||
currentTenantId,
|
currentTenantId,
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user