diff --git a/app/(dashboard)/browser/content.tsx b/app/(dashboard)/browser/content.tsx index ea8399e..90fadcd 100644 --- a/app/(dashboard)/browser/content.tsx +++ b/app/(dashboard)/browser/content.tsx @@ -14,6 +14,8 @@ import { useBucket } from "@/hooks/use-bucket" import { useMessage } from "@/lib/feedback/message" import { buildBucketPath } from "@/lib/bucket-path" import { useTasks } from "@/contexts/task-context" +import { ObjectPreviewModal } from "@/components/object/preview-modal" +import { useObject } from "@/hooks/use-object" interface BrowserContentProps { bucketName: string @@ -34,18 +36,11 @@ export function BrowserContent({ bucketName, keyPath = "", preview = false, prev const [infoOpen, setInfoOpen] = React.useState(false) const [infoKey, setInfoKey] = React.useState(null) - const [autoPreview, setAutoPreview] = React.useState(false) const [uploadPickerOpen, setUploadPickerOpen] = React.useState(false) const [refreshTrigger, setRefreshTrigger] = React.useState(0) - - // Handle initial preview params - set infoOpen and trigger auto-preview - React.useEffect(() => { - if (preview && previewKey) { - setInfoKey(previewKey) - setInfoOpen(true) - setAutoPreview(true) - } - }, [preview, previewKey]) + const [showPreview, setShowPreview] = React.useState(false) + const [previewObject, setPreviewObject] = React.useState | null>(null) + const objectApi = useObject(bucketName) React.useEffect(() => { if (!bucketName) return @@ -82,40 +77,24 @@ export function BrowserContent({ bucketName, keyPath = "", preview = false, prev setInfoOpen(true) } - const updatePreviewParams = (showPreview: boolean, key?: string) => { - const currentParams = new URLSearchParams(searchParams.toString()) - const params = new URLSearchParams(searchParams.toString()) - if (showPreview && key) { - params.set("preview", "true") - params.set("previewKey", key) - } else { - params.delete("preview") - params.delete("previewKey") - } - if (params.toString() === currentParams.toString()) return - router.replace(`/browser?${params.toString()}`, { scroll: false }) - } - const handleInfoOpenChange = (open: boolean) => { setInfoOpen(open) - if (!open) { - setAutoPreview(false) - updatePreviewParams(false) - } - } - - const handlePreviewChange = (showPreview: boolean) => { - if (showPreview && infoKey) { - updatePreviewParams(true, infoKey) - } else { - updatePreviewParams(false) - } } const handleRefresh = () => { setRefreshTrigger((n) => n + 1) } + const handleOpenPreview = React.useCallback( + async ({ key, data }: { key?: string; data?: Record }) => { + if (!key) return + const info = data ?? (await objectApi.getObjectInfo(key)) + setPreviewObject(info as Record) + setShowPreview(true) + }, + [objectApi], + ) + const tasks = useTasks() const debounceTimerRef = React.useRef | null>(null) const prevCompletedIdsRef = React.useRef(new Set()) @@ -126,10 +105,7 @@ export function BrowserContent({ bucketName, keyPath = "", preview = false, prev if (!currentIds.has(id)) prevCompletedIdsRef.current.delete(id) } const completedForBucket = tasks.filter( - (t) => - (t.kind === "upload" || t.kind === "delete") && - t.bucketName === bucketName && - t.status === "completed", + (t) => (t.kind === "upload" || t.kind === "delete") && t.bucketName === bucketName && t.status === "completed", ) const newCompletions = completedForBucket.filter((t) => !prevCompletedIdsRef.current.has(t.id)) if (newCompletions.length > 0) { @@ -178,6 +154,7 @@ export function BrowserContent({ bucketName, keyPath = "", preview = false, prev onUploadClick={() => setUploadPickerOpen(true)} onRefresh={handleRefresh} refreshTrigger={refreshTrigger} + onPreview={handleOpenPreview} /> ) : ( @@ -190,8 +167,7 @@ export function BrowserContent({ bucketName, keyPath = "", preview = false, prev open={infoOpen} onOpenChange={handleInfoOpenChange} onRefresh={handleRefresh} - autoPreview={autoPreview} - onPreviewChange={handlePreviewChange} + onPreview={handleOpenPreview} /> + + setShowPreview(show)} object={previewObject} /> ) } diff --git a/components/object/info.tsx b/components/object/info.tsx index b525715..a54fbb3 100644 --- a/components/object/info.tsx +++ b/components/object/info.tsx @@ -21,7 +21,6 @@ import { useDialog } from "@/lib/feedback/dialog" import { exportFile } from "@/lib/export-file" import { getContentType } from "@/lib/mime-types" import { ObjectVersions } from "@/components/object/versions" -import { ObjectPreviewModal } from "@/components/object/preview-modal" import { GetObjectCommand, HeadObjectCommand } from "@aws-sdk/client-s3" import { getSignedUrl } from "@aws-sdk/s3-request-presigner" import { useS3 } from "@/contexts/s3-context" @@ -35,18 +34,10 @@ interface ObjectInfoProps { open: boolean onOpenChange: (open: boolean) => void onRefresh?: () => void - autoPreview?: boolean - onPreviewChange?: (showPreview: boolean) => void + onPreview: (params: { key?: string; data?: Record }) => void } -export function ObjectInfo({ - bucketName, - objectKey, - open, - onOpenChange, - autoPreview = false, - onPreviewChange, -}: ObjectInfoProps) { +export function ObjectInfo({ bucketName, objectKey, open, onOpenChange, onPreview }: ObjectInfoProps) { const { t } = useTranslation() const message = useMessage() const dialog = useDialog() @@ -62,9 +53,7 @@ export function ObjectInfo({ const [signedUrl, setSignedUrl] = React.useState("") const [showTagView, setShowTagView] = React.useState(false) const [showRetentionView, setShowRetentionView] = React.useState(false) - const [showPreview, setShowPreview] = React.useState(false) const [showVersions, setShowVersions] = React.useState(false) - const [previewObject, setPreviewObject] = React.useState | null>(null) const [tagFormValue, setTagFormValue] = React.useState({ Key: "", Value: "", @@ -198,7 +187,6 @@ export function ObjectInfo({ React.useEffect(() => { if (open && objectKey) { - setPreviewObject(null) const key = objectKey loadObjectInfoRef .current(key) @@ -209,7 +197,6 @@ export function ObjectInfo({ }) } else { setObject(null) - setPreviewObject(null) } }, [open, objectKey, message, t]) @@ -230,19 +217,6 @@ export function ObjectInfo({ } } - // Auto-open preview once when autoPreview is true and object data is first loaded - const hasAutoPreviewedRef = React.useRef(false) - React.useEffect(() => { - if (!open) { - hasAutoPreviewedRef.current = false - return - } - if (autoPreview && object && !hasAutoPreviewedRef.current) { - hasAutoPreviewedRef.current = true - setShowPreview(true) - } - }, [autoPreview, object, open]) - React.useEffect(() => { return () => { if (previewParamSyncTimerRef.current) { @@ -251,29 +225,6 @@ export function ObjectInfo({ } }, []) - const schedulePreviewParamSync = React.useCallback( - (show: boolean) => { - if (previewParamSyncTimerRef.current) { - clearTimeout(previewParamSyncTimerRef.current) - previewParamSyncTimerRef.current = null - } - - previewParamSyncTimerRef.current = setTimeout(() => { - onPreviewChange?.(show) - previewParamSyncTimerRef.current = null - }, 140) - }, - [onPreviewChange], - ) - - // Open preview dialog - const openPreview = () => { - if (!object) return - setPreviewObject(object) - setShowPreview(true) - schedulePreviewParamSync(true) - } - const handlePreviewVersion = async (versionId: string) => { if (!object?.Key) return try { @@ -297,27 +248,12 @@ export function ObjectInfo({ { expiresIn: 3600 }, ), ]) - setPreviewObject({ - ...head, - Key: key, - SignedUrl: signed, - VersionId: version, - }) - setShowPreview(true) - schedulePreviewParamSync(true) + const data = { ...head, SignedUrl: signed } + onPreview({ key, data }) } catch (err) { message.error((err as Error)?.message ?? t("Failed to fetch versions")) } } - - const handlePreviewShowChange = React.useCallback( - (show: boolean) => { - setShowPreview(show) - schedulePreviewParamSync(show) - }, - [schedulePreviewParamSync], - ) - const toggleLegalHold = async () => { if (!object?.Key) return try { @@ -432,7 +368,7 @@ export function ObjectInfo({ {t("Download")} - @@ -723,7 +659,6 @@ export function ObjectInfo({ - void refreshTrigger?: number + onPreview: (params: { key?: string; data?: Record }) => void } export function ObjectList({ @@ -70,6 +71,7 @@ export function ObjectList({ pageSize = 25, onRefresh, refreshTrigger = 0, + onPreview, }: ObjectListProps) { const { t } = useTranslation() const message = useMessage() @@ -286,13 +288,15 @@ export function ObjectList({
{row.original.type === "object" ? ( <> -