Read the review card fill from localStorage before picking a new one.
The review screen’s effect ran before draft hydration, so reload always generated a fresh color and overwrote the stored one. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
6d536880fe
commit
b12de611f7
5 files changed
+199
-27
No files matched your search
@@ -1,23 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import {
|
||||
dominantColorFromImageUrl,
|
||||
isPreviewSurfaceColor,
|
||||
randomPreviewSurfaceColor,
|
||||
} from "../../../../lib/create/previewSurfaceColor";
|
||||
|
||||
function storedOrRandom(
|
||||
storedFallback: string | undefined,
|
||||
persistFallback: (_color: string) => void,
|
||||
): string {
|
||||
if (storedFallback && isPreviewSurfaceColor(storedFallback)) {
|
||||
return storedFallback;
|
||||
}
|
||||
const next = randomPreviewSurfaceColor();
|
||||
persistFallback(next);
|
||||
return next;
|
||||
}
|
||||
import { dominantColorFromImageUrl } from "../../../../lib/create/previewSurfaceColor";
|
||||
import { resolvePreviewSurfaceFallback } from "../utils/resolvePreviewSurfaceFallback";
|
||||
|
||||
/**
|
||||
* Fill for the review preview card. An uploaded logo contributes its most
|
||||
@@ -27,7 +12,7 @@ function storedOrRandom(
|
||||
*
|
||||
* @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 the draft once.
|
||||
* @param persistFallback Writes the fallback onto React state once.
|
||||
* @returns A CSS color, or `undefined` before the client picks one.
|
||||
*
|
||||
* @example
|
||||
@@ -42,15 +27,23 @@ export function usePreviewSurfaceColor(
|
||||
|
||||
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(storedOrRandom(storedFallback, persistFallback));
|
||||
setColor(next);
|
||||
return;
|
||||
}
|
||||
|
||||
let cancelled = false;
|
||||
void dominantColorFromImageUrl(imageUrl).then((next) => {
|
||||
void dominantColorFromImageUrl(imageUrl).then((extracted) => {
|
||||
if (cancelled) return;
|
||||
setColor(next ?? storedOrRandom(storedFallback, persistFallback));
|
||||
if (extracted) {
|
||||
setColor(extracted);
|
||||
return;
|
||||
}
|
||||
const next = resolvePreviewSurfaceFallback(storedFallback);
|
||||
persistFallback(next);
|
||||
setColor(next);
|
||||
});
|
||||
|
||||
return () => {
|
||||
|
||||
@@ -34,6 +34,10 @@ import {
|
||||
vectorMarkPath,
|
||||
} from "../../../../../lib/assetUtils";
|
||||
import { isPreviewSurfaceColor } from "../../../../../lib/create/previewSurfaceColor";
|
||||
import {
|
||||
persistDraftPreviewSurfaceColor,
|
||||
readDraftPreviewSurfaceColor,
|
||||
} from "../../utils/resolvePreviewSurfaceFallback";
|
||||
|
||||
const FACET_FALLBACK_ORDER: readonly TemplateFacetGroupKey[] = [
|
||||
"coreValues",
|
||||
@@ -247,12 +251,19 @@ export function FinalReviewScreen({
|
||||
: undefined;
|
||||
const persistFallback = useCallback(
|
||||
(color: string) => {
|
||||
replaceState((prev) =>
|
||||
prev.previewSurfaceColor &&
|
||||
isPreviewSurfaceColor(prev.previewSurfaceColor)
|
||||
replaceState((prev) => {
|
||||
persistDraftPreviewSurfaceColor(color, prev);
|
||||
const keep =
|
||||
(prev.previewSurfaceColor &&
|
||||
isPreviewSurfaceColor(prev.previewSurfaceColor)
|
||||
? prev.previewSurfaceColor
|
||||
: undefined) ??
|
||||
readDraftPreviewSurfaceColor() ??
|
||||
color;
|
||||
return prev.previewSurfaceColor === keep
|
||||
? prev
|
||||
: { ...prev, previewSurfaceColor: color },
|
||||
);
|
||||
: { ...prev, previewSurfaceColor: keep };
|
||||
});
|
||||
},
|
||||
[replaceState],
|
||||
);
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import type { CreateFlowState } from "../types";
|
||||
import {
|
||||
isPreviewSurfaceColor,
|
||||
randomPreviewSurfaceColor,
|
||||
} from "../../../../lib/create/previewSurfaceColor";
|
||||
import {
|
||||
readAnonymousCreateFlowState,
|
||||
writeAnonymousCreateFlowState,
|
||||
} from "./anonymousDraftStorage";
|
||||
|
||||
function validPreviewSurfaceColor(
|
||||
value: string | undefined,
|
||||
): string | undefined {
|
||||
return typeof value === "string" && isPreviewSurfaceColor(value)
|
||||
? value
|
||||
: undefined;
|
||||
}
|
||||
|
||||
/** Existing draft fill from React state or localStorage, if it is a stored hsl. */
|
||||
export function readDraftPreviewSurfaceColor(
|
||||
storedFallback?: string,
|
||||
): string | undefined {
|
||||
return (
|
||||
validPreviewSurfaceColor(storedFallback) ??
|
||||
validPreviewSurfaceColor(
|
||||
readAnonymousCreateFlowState().previewSurfaceColor,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep a fallback fill on the anonymous draft immediately. The provider’s
|
||||
* first mirrored write is skipped, so waiting on React state loses the color
|
||||
* across reload. `state` is merged so a write before hydrate does not drop
|
||||
* other draft fields already in memory.
|
||||
*/
|
||||
export function persistDraftPreviewSurfaceColor(
|
||||
color: string,
|
||||
state: CreateFlowState = {},
|
||||
): void {
|
||||
const next = validPreviewSurfaceColor(color);
|
||||
if (!next) return;
|
||||
const current = readAnonymousCreateFlowState();
|
||||
const stored = validPreviewSurfaceColor(current.previewSurfaceColor);
|
||||
writeAnonymousCreateFlowState({
|
||||
...current,
|
||||
...state,
|
||||
previewSurfaceColor: stored ?? next,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reuse the draft fill when one exists; otherwise pick one and write it now.
|
||||
* Reads localStorage itself so a child effect can run before context hydrates.
|
||||
*/
|
||||
export function resolvePreviewSurfaceFallback(
|
||||
storedFallback?: string,
|
||||
): string {
|
||||
const existing = readDraftPreviewSurfaceColor(storedFallback);
|
||||
if (existing) return existing;
|
||||
const color = randomPreviewSurfaceColor();
|
||||
persistDraftPreviewSurfaceColor(color);
|
||||
return color;
|
||||
}
|
||||
Reference in new issue
Block a user