Files
community-rule/app/(app)/create/components/CreateFlowPendingAvatarFlush.tsx
T
adilallo 8212423ec3 Keep a guest community logo on review and on the published rule.
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.
2026-11-05 20:02:40 -07:00

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;
}