Fix authenticated media flows and upload preflight handling
This commit is contained in:
@@ -1,12 +1,17 @@
|
||||
/**
|
||||
* Card view for displaying document summary, preview, and metadata.
|
||||
*/
|
||||
import { useState } from 'react';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import type { JSX } from 'react';
|
||||
import { Download, FileText, Trash2 } from 'lucide-react';
|
||||
|
||||
import type { DmsDocument } from '../types';
|
||||
import { contentMarkdownUrl, downloadUrl, thumbnailUrl } from '../lib/api';
|
||||
import {
|
||||
downloadBlobFile,
|
||||
downloadDocumentContentMarkdown,
|
||||
downloadDocumentFile,
|
||||
getDocumentThumbnailBlob,
|
||||
} from '../lib/api';
|
||||
|
||||
/**
|
||||
* Defines properties accepted by the document card component.
|
||||
@@ -79,12 +84,59 @@ export default function DocumentCard({
|
||||
onFilterTag,
|
||||
}: DocumentCardProps): JSX.Element {
|
||||
const [isTrashing, setIsTrashing] = useState<boolean>(false);
|
||||
const [thumbnailObjectUrl, setThumbnailObjectUrl] = useState<string | null>(null);
|
||||
const thumbnailObjectUrlRef = useRef<string | null>(null);
|
||||
const createdDate = new Date(document.created_at).toLocaleString();
|
||||
const status = statusPresentation(document.status);
|
||||
const compactPath = compactLogicalPath(document.logical_path, 180);
|
||||
const trashDisabled = isTrashView || document.status === 'trashed' || isTrashing;
|
||||
const trashTitle = trashDisabled ? 'Already in trash' : 'Move to trash';
|
||||
|
||||
/**
|
||||
* Loads thumbnail preview through authenticated fetch and revokes replaced object URLs.
|
||||
*/
|
||||
useEffect(() => {
|
||||
const revokeThumbnailObjectUrl = (): void => {
|
||||
if (!thumbnailObjectUrlRef.current) {
|
||||
return;
|
||||
}
|
||||
URL.revokeObjectURL(thumbnailObjectUrlRef.current);
|
||||
thumbnailObjectUrlRef.current = null;
|
||||
};
|
||||
|
||||
if (!document.preview_available) {
|
||||
revokeThumbnailObjectUrl();
|
||||
setThumbnailObjectUrl(null);
|
||||
return;
|
||||
}
|
||||
|
||||
let cancelled = false;
|
||||
const loadThumbnail = async (): Promise<void> => {
|
||||
try {
|
||||
const blob = await getDocumentThumbnailBlob(document.id);
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
revokeThumbnailObjectUrl();
|
||||
const objectUrl = URL.createObjectURL(blob);
|
||||
thumbnailObjectUrlRef.current = objectUrl;
|
||||
setThumbnailObjectUrl(objectUrl);
|
||||
} catch {
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
revokeThumbnailObjectUrl();
|
||||
setThumbnailObjectUrl(null);
|
||||
}
|
||||
};
|
||||
|
||||
void loadThumbnail();
|
||||
return () => {
|
||||
cancelled = true;
|
||||
revokeThumbnailObjectUrl();
|
||||
};
|
||||
}, [document.id, document.preview_available]);
|
||||
|
||||
return (
|
||||
<article
|
||||
className={`document-card ${isSelected ? 'selected' : ''}`}
|
||||
@@ -119,8 +171,8 @@ export default function DocumentCard({
|
||||
</label>
|
||||
</header>
|
||||
<div className="document-preview">
|
||||
{document.preview_available ? (
|
||||
<img src={thumbnailUrl(document.id)} alt={document.original_filename} loading="lazy" />
|
||||
{document.preview_available && thumbnailObjectUrl ? (
|
||||
<img src={thumbnailObjectUrl} alt={document.original_filename} loading="lazy" />
|
||||
) : (
|
||||
<div className="document-preview-fallback">{document.extension || 'file'}</div>
|
||||
)}
|
||||
@@ -173,7 +225,13 @@ export default function DocumentCard({
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
window.open(downloadUrl(document.id), '_blank', 'noopener,noreferrer');
|
||||
void (async (): Promise<void> => {
|
||||
try {
|
||||
const payload = await downloadDocumentFile(document.id);
|
||||
downloadBlobFile(payload.blob, payload.filename);
|
||||
} catch {
|
||||
}
|
||||
})();
|
||||
}}
|
||||
>
|
||||
<Download aria-hidden="true" />
|
||||
@@ -186,7 +244,13 @@ export default function DocumentCard({
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
window.open(contentMarkdownUrl(document.id), '_blank', 'noopener,noreferrer');
|
||||
void (async (): Promise<void> => {
|
||||
try {
|
||||
const payload = await downloadDocumentContentMarkdown(document.id);
|
||||
downloadBlobFile(payload.blob, payload.filename);
|
||||
} catch {
|
||||
}
|
||||
})();
|
||||
}}
|
||||
>
|
||||
<FileText aria-hidden="true" />
|
||||
|
||||
Reference in New Issue
Block a user