From e653e31078cc9566f5f71db65d04aa97ce2f3777 Mon Sep 17 00:00:00 2001 From: bflatastic Date: Tue, 13 Jan 2026 13:27:30 +0700 Subject: [PATCH] website: media page update --- .../src/pages/media/media.tsx | 338 ++++++++++-------- 1 file changed, 182 insertions(+), 156 deletions(-) diff --git a/frontend/apps/artcraft-website/src/pages/media/media.tsx b/frontend/apps/artcraft-website/src/pages/media/media.tsx index 85edcc0fc1..deb4354e45 100644 --- a/frontend/apps/artcraft-website/src/pages/media/media.tsx +++ b/frontend/apps/artcraft-website/src/pages/media/media.tsx @@ -12,6 +12,9 @@ import { faLink, faCheck, faArrowDownToLine, + faPencil, + faCircleInfo, + faImage, } from "@fortawesome/pro-solid-svg-icons"; import { addCorsParam, @@ -47,6 +50,8 @@ interface MediaData { createdAt: string | null; isVideo: boolean; isLoaded: boolean; + width?: number; + height?: number; } interface PromptData { @@ -106,6 +111,15 @@ const useCopyFeedback = () => { return { copied, triggerCopy }; }; +const InfoRow = ({ label, value }: { label: string; value: React.ReactNode }) => ( +
+ {label} + + {value} + +
+); + export default function MediaPage() { const { id: routeId } = useParams<{ id?: string }>(); const [searchParams] = useSearchParams(); @@ -121,13 +135,11 @@ export default function MediaPage() { const [mediaRecordLoading, setMediaRecordLoading] = useState(true); const [promptData, setPromptData] = useState(EMPTY_PROMPT_DATA); - const [isPromptHovered, setIsPromptHovered] = useState(false); const shareCopy = useCopyFeedback(); const promptCopy = useCopyFeedback(); const loadMedia = useCallback(async (id: string) => { - setMedia(prev => ({ ...prev, isLoaded: false })); setMediaRecordLoading(true); const mediaFilesApi = new MediaFilesApi(); @@ -140,12 +152,17 @@ export default function MediaPage() { const file = mediaResponse.data; const url = file.media_links?.cdn_url || null; - setMedia({ - url, - token: file.token || id, - createdAt: file.created_at || null, - isVideo: url ? isVideoUrl(url) : false, - isLoaded: false, + setMedia((prev) => { + const isSameUrl = prev.url === url; + return { + url, + token: file.token || id, + createdAt: file.created_at || null, + isVideo: url ? isVideoUrl(url) : false, + isLoaded: isSameUrl ? prev.isLoaded : false, + width: isSameUrl ? prev.width : undefined, + height: isSameUrl ? prev.height : undefined, + }; }); if (file.maybe_prompt_token) { @@ -183,30 +200,7 @@ export default function MediaPage() { } }, [mediaIdParam, loadMedia]); - useEffect(() => { - if (!media.url) return; - - if (media.isVideo) { - setMedia(prev => ({ ...prev, isLoaded: true })); - return; - } - - const img = new Image(); - img.src = addCorsParam(media.url) || media.url; - const handleLoad = () => setMedia(prev => ({ ...prev, isLoaded: true })); - - if (img.complete) { - handleLoad(); - } else { - img.addEventListener("load", handleLoad); - img.addEventListener("error", handleLoad); - } - - return () => { - img.removeEventListener("load", handleLoad); - img.removeEventListener("error", handleLoad); - }; - }, [media.url, media.isVideo]); + const handleCopyPrompt = useCallback(async () => { if (!promptData.text) return; @@ -231,49 +225,71 @@ export default function MediaPage() { }, [media.token, shareCopy]); const shareButtonIcon = shareCopy.copied ? faCheck : faLink; - const shareButtonText = shareCopy.copied ? "Share link copied" : "Copy Share Link"; + + const getResolutionLabel = (width?: number) => { + if (!width) return null; + if (width >= 1000) return `${Math.round(width / 1024)}K`; + return null; + }; return ( -
-
-
-
+
+
+
+ {/* Media Preview Area */} +
{mediaRecordLoading ? ( -
-
+
+
) : media.url ? ( -
+
{media.isVideo ? (