From 782b1e1d64a0a9b4d5afc6a519ac097d5d05b45d Mon Sep 17 00:00:00 2001 From: adilallo <39313955+adilallo@users.noreply.github.com> Date: Tue, 29 Sep 2026 14:48:19 -0600 Subject: [PATCH] Apply the saved review-card fill before paint so reload does not flash the teal placeholder. Co-authored-by: Cursor --- .../create/hooks/usePreviewSurfaceColor.ts | 34 ++++++++++++------- .../screens/review/FinalReviewScreen.tsx | 4 +-- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/app/(app)/create/hooks/usePreviewSurfaceColor.ts b/app/(app)/create/hooks/usePreviewSurfaceColor.ts index ef39c1c..4d32ebc 100644 --- a/app/(app)/create/hooks/usePreviewSurfaceColor.ts +++ b/app/(app)/create/hooks/usePreviewSurfaceColor.ts @@ -1,19 +1,21 @@ "use client"; -import { useEffect, useState } from "react"; -import { dominantColorFromImageUrl } from "../../../../lib/create/previewSurfaceColor"; +import { useEffect, useLayoutEffect, useState } from "react"; +import { + dominantColorFromImageUrl, + isPreviewSurfaceColor, +} from "../../../../lib/create/previewSurfaceColor"; import { resolvePreviewSurfaceFallback } from "../utils/resolvePreviewSurfaceFallback"; /** * Fill for the review preview card. An uploaded logo contributes its most * common color; no image uses a random light color stored on the draft so - * reload keeps the same fill. Unset until mount so the server and client - * share the fallback class. + * reload keeps the same fill. * * @param imageUrl Uploaded logo URL, or `null` when the user skipped the photo. * @param storedFallback Draft `previewSurfaceColor`, if already picked. * @param persistFallback Writes the fallback onto React state once. - * @returns A CSS color, or `undefined` before the client picks one. + * @returns A CSS color, or `undefined` before the client resolves one. * * @example * const color = usePreviewSurfaceColor(avatarUrl, state.previewSurfaceColor, persist); @@ -23,16 +25,22 @@ export function usePreviewSurfaceColor( storedFallback: string | undefined, persistFallback: (_color: string) => void, ): string | undefined { - const [color, setColor] = useState(undefined); + const [color, setColor] = useState(() => + storedFallback && isPreviewSurfaceColor(storedFallback) + ? storedFallback + : undefined, + ); + + useLayoutEffect(() => { + if (imageUrl) return; + const next = resolvePreviewSurfaceFallback(storedFallback); + persistFallback(next); + // eslint-disable-next-line react-hooks/set-state-in-effect -- apply before paint so reload does not flash the teal token + setColor((prev) => (prev === next ? prev : next)); + }, [imageUrl, persistFallback, storedFallback]); useEffect(() => { - if (!imageUrl) { - const next = resolvePreviewSurfaceFallback(storedFallback); - persistFallback(next); - // eslint-disable-next-line react-hooks/set-state-in-effect -- random fill is client-only; first paint stays on the fallback class - setColor(next); - return; - } + if (!imageUrl) return; let cancelled = false; void dominantColorFromImageUrl(imageUrl).then((extracted) => { diff --git a/app/(app)/create/screens/review/FinalReviewScreen.tsx b/app/(app)/create/screens/review/FinalReviewScreen.tsx index ae5c337..cee9ab7 100644 --- a/app/(app)/create/screens/review/FinalReviewScreen.tsx +++ b/app/(app)/create/screens/review/FinalReviewScreen.tsx @@ -311,12 +311,12 @@ export function FinalReviewScreen({ size={mdUp ? "L" : "M"} expanded={true} descriptionAtBottom - backgroundColor="bg-[var(--color-surface-invert-brand-teal)]" + backgroundColor="bg-transparent" surfaceColor={surfaceColor} logoUrl={avatarUrl ?? getAssetPath(vectorMarkPath("mutual-aid"))} logoAlt={ruleCardTitle} categories={finalReviewCategories} - className={CREATE_FLOW_REVIEW_RULE_LAYOUT_CLASS} + className={`${CREATE_FLOW_REVIEW_RULE_LAYOUT_CLASS}${surfaceColor ? "" : " invisible"}`} onClick={() => {}} />