"use client"; import { useCallback, useEffect, useRef, useState, type ChangeEvent, } from "react"; import Upload from "../../../../components/controls/Upload"; import { useMessages, useTranslation } from "../../../../contexts/MessagesContext"; import { useCreateFlow } from "../../context/CreateFlowContext"; import { CreateFlowHeaderLockup } from "../../components/CreateFlowHeaderLockup"; import { CreateFlowStepShell } from "../../components/CreateFlowStepShell"; import { CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS } from "../../components/createFlowLayoutTokens"; import { fetchAuthSession } from "../../../../../lib/create/api"; import { createFlowUploadFailureMessageKey, uploadCreateFlowFile, } from "../../../../../lib/create/uploadToServer"; import { COMMUNITY_AVATAR_ACCEPT, messageKeyForCreateFlowUploadReason, validateCreateFlowUploadFile, } from "../../../../../lib/create/createFlowUploadValidation"; import { clearPendingCommunityAvatarFile, readPendingCommunityAvatarFile, storePendingCommunityAvatarFile, } from "../../../../../lib/create/pendingCommunityAvatarUpload"; function hasCommunityAvatarUrl(url: string | undefined): boolean { return typeof url === "string" && url.trim().length > 0; } /** Create Community — Figma Flow — Upload. */ export function CommunityUploadScreen() { const m = useMessages(); const u = m.create.community.communityUpload; const { markCreateFlowInteraction, state, updateState } = useCreateFlow(); const tUpload = useTranslation("create.upload"); const fileInputRef = useRef(null); const [signedIn, setSignedIn] = useState(null); const [localPreviewUrl, setLocalPreviewUrl] = useState(null); const [localFileName, setLocalFileName] = useState(null); const [busy, setBusy] = useState(false); const [errorMessage, setErrorMessage] = useState(null); useEffect(() => { let cancelled = false; void fetchAuthSession().then(({ user }) => { if (!cancelled) setSignedIn(Boolean(user)); }); return () => { cancelled = true; }; }, []); useEffect( () => () => { if (localPreviewUrl) URL.revokeObjectURL(localPreviewUrl); }, [localPreviewUrl], ); const serverAvatarUrl = hasCommunityAvatarUrl(state.communityAvatarUrl) ? state.communityAvatarUrl!.trim() : null; const serverAvatarUrlRef = useRef(serverAvatarUrl); serverAvatarUrlRef.current = serverAvatarUrl; useEffect(() => { if (serverAvatarUrl) { setLocalPreviewUrl((prev) => { if (prev) URL.revokeObjectURL(prev); return null; }); } }, [serverAvatarUrl]); useEffect(() => { let cancelled = false; void (async () => { try { const file = await readPendingCommunityAvatarFile(); if (cancelled || !file) return; const validated = await validateCreateFlowUploadFile( file, "communityAvatar", ); if (validated.ok === false) { await clearPendingCommunityAvatarFile(); return; } if (cancelled) return; if (serverAvatarUrlRef.current) return; const objectUrl = URL.createObjectURL(file); if (cancelled) { URL.revokeObjectURL(objectUrl); return; } setLocalPreviewUrl((prev) => { if (prev) URL.revokeObjectURL(prev); return objectUrl; }); setLocalFileName(file.name); if (!state.communityAvatarFileName) { updateState({ communityAvatarFileName: file.name }); } } catch { // Missing IndexedDB / quota: leave the picker empty. } })(); return () => { cancelled = true; }; }, [state.communityAvatarFileName, updateState]); const resolveUploadError = useCallback( (err: unknown) => { const key = createFlowUploadFailureMessageKey(err); if (key === "errors.tooLarge") return u.fileTooLarge; return tUpload(key); }, [tUpload, u.fileTooLarge], ); const handleFileChange = useCallback( async (e: ChangeEvent) => { const file = e.target.files?.[0]; e.target.value = ""; if (!file) return; markCreateFlowInteraction(); setErrorMessage(null); if (signedIn === true) { setBusy(true); try { const { url } = await uploadCreateFlowFile(file, "communityAvatar"); setLocalPreviewUrl((prev) => { if (prev) URL.revokeObjectURL(prev); return null; }); setLocalFileName(file.name); updateState({ communityAvatarUrl: url, communityAvatarFileName: file.name, }); } catch (err) { setErrorMessage(resolveUploadError(err)); } finally { setBusy(false); } return; } const validated = await validateCreateFlowUploadFile( file, "communityAvatar", ); if (validated.ok === false) { setErrorMessage( validated.reason === "tooLarge" ? u.fileTooLarge : tUpload(messageKeyForCreateFlowUploadReason(validated.reason)), ); return; } try { await storePendingCommunityAvatarFile(file); setLocalPreviewUrl((prev) => { if (prev) URL.revokeObjectURL(prev); return URL.createObjectURL(file); }); setLocalFileName(file.name); updateState({ communityAvatarFileName: file.name }); } catch { setErrorMessage(tUpload("errors.generic")); } }, [ markCreateFlowInteraction, resolveUploadError, signedIn, tUpload, u.fileTooLarge, updateState, ], ); const handlePickFile = useCallback(() => { if (!busy) fileInputRef.current?.click(); }, [busy]); const displaySrc = serverAvatarUrl ?? localPreviewUrl; const hasPreview = typeof displaySrc === "string" && displaySrc.length > 0; const fileName = localFileName ?? (typeof state.communityAvatarFileName === "string" && state.communityAvatarFileName.trim().length > 0 ? state.communityAvatarFileName.trim() : undefined); return (
); }