Sign-in was clearing the staged file before it uploaded, and review only read the server URL, so a chosen logo disappeared after the upload step.
62 lines
2.1 KiB
TypeScript
62 lines
2.1 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useRef, useState } from "react";
|
|
import { useCreateFlow } from "../context/CreateFlowContext";
|
|
import { flushPendingCommunityAvatar } from "../../../../lib/create/flushPendingCommunityAvatar";
|
|
|
|
/** Background tries after a transient upload failure. The file stays staged. */
|
|
const MAX_FLUSH_ATTEMPTS = 4;
|
|
|
|
/**
|
|
* After sign-in, uploads a community avatar staged in IndexedDB (anonymous pick)
|
|
* and writes `communityAvatarUrl` on success. A transient failure retries with
|
|
* backoff while this layout stays mounted.
|
|
*/
|
|
export function CreateFlowPendingAvatarFlush({
|
|
sessionUser,
|
|
sessionResolved,
|
|
}: {
|
|
sessionUser: { id: string; email: string } | null | undefined;
|
|
sessionResolved: boolean;
|
|
}) {
|
|
const { state, updateState } = useCreateFlow();
|
|
const [attempt, setAttempt] = useState(0);
|
|
/** One successful flush per signed-in user id (survives the effect re-running). */
|
|
const lastFlushedUserIdRef = useRef<string | null>(null);
|
|
const hasServerAvatar =
|
|
typeof state.communityAvatarUrl === "string" &&
|
|
state.communityAvatarUrl.trim().length > 0;
|
|
|
|
useEffect(() => {
|
|
if (!sessionResolved || !sessionUser) return;
|
|
if (lastFlushedUserIdRef.current === sessionUser.id) return;
|
|
if (hasServerAvatar) return;
|
|
if (attempt >= MAX_FLUSH_ATTEMPTS) return;
|
|
let cancelled = false;
|
|
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
|
|
void (async () => {
|
|
const result = await flushPendingCommunityAvatar();
|
|
if (cancelled) return;
|
|
if (result.status === "uploaded") {
|
|
updateState({ communityAvatarUrl: result.url });
|
|
lastFlushedUserIdRef.current = sessionUser.id;
|
|
return;
|
|
}
|
|
if (result.status !== "failed") return;
|
|
if (attempt + 1 >= MAX_FLUSH_ATTEMPTS) return;
|
|
const delay = Math.min(20_000, 5_000 * 2 ** attempt);
|
|
timer = setTimeout(() => {
|
|
if (!cancelled) setAttempt((current) => current + 1);
|
|
}, delay);
|
|
})();
|
|
|
|
return () => {
|
|
cancelled = true;
|
|
if (timer) clearTimeout(timer);
|
|
};
|
|
}, [attempt, hasServerAvatar, sessionResolved, sessionUser, updateState]);
|
|
|
|
return null;
|
|
}
|