Keep the review card’s fallback color on the draft so a reload does not pick a new one.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
56adcd59fd
commit
6d536880fe
8 files changed
+165
-8
No files matched your search
@@ -3,39 +3,60 @@
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill for the review preview card. An uploaded logo contributes its most
|
||||
* common color; no image uses a random light color. Unset until mount so the
|
||||
* server and client share the fallback class.
|
||||
* 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.
|
||||
*
|
||||
* @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.
|
||||
* @returns A CSS color, or `undefined` before the client picks one.
|
||||
*
|
||||
* @example
|
||||
* const color = usePreviewSurfaceColor(avatarUrl, state.previewSurfaceColor, persist);
|
||||
*/
|
||||
export function usePreviewSurfaceColor(
|
||||
imageUrl: string | null,
|
||||
storedFallback: string | undefined,
|
||||
persistFallback: (_color: string) => void,
|
||||
): string | undefined {
|
||||
const [color, setColor] = useState<string | undefined>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
if (!imageUrl) {
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect -- random fill is client-only; first paint stays on the fallback class
|
||||
setColor(randomPreviewSurfaceColor());
|
||||
setColor(storedOrRandom(storedFallback, persistFallback));
|
||||
return;
|
||||
}
|
||||
|
||||
let cancelled = false;
|
||||
void dominantColorFromImageUrl(imageUrl).then((next) => {
|
||||
if (cancelled) return;
|
||||
setColor(next ?? randomPreviewSurfaceColor());
|
||||
setColor(next ?? storedOrRandom(storedFallback, persistFallback));
|
||||
});
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [imageUrl]);
|
||||
}, [imageUrl, persistFallback, storedFallback]);
|
||||
|
||||
return color;
|
||||
}
|
||||
@@ -33,6 +33,7 @@ import {
|
||||
getAssetPath,
|
||||
vectorMarkPath,
|
||||
} from "../../../../../lib/assetUtils";
|
||||
import { isPreviewSurfaceColor } from "../../../../../lib/create/previewSurfaceColor";
|
||||
|
||||
const FACET_FALLBACK_ORDER: readonly TemplateFacetGroupKey[] = [
|
||||
"coreValues",
|
||||
@@ -240,7 +241,26 @@ export function FinalReviewScreen({
|
||||
state.communityAvatarUrl.trim().length > 0
|
||||
? state.communityAvatarUrl.trim()
|
||||
: null;
|
||||
const surfaceColor = usePreviewSurfaceColor(avatarUrl);
|
||||
const storedFallback =
|
||||
typeof state.previewSurfaceColor === "string"
|
||||
? state.previewSurfaceColor
|
||||
: undefined;
|
||||
const persistFallback = useCallback(
|
||||
(color: string) => {
|
||||
replaceState((prev) =>
|
||||
prev.previewSurfaceColor &&
|
||||
isPreviewSurfaceColor(prev.previewSurfaceColor)
|
||||
? prev
|
||||
: { ...prev, previewSurfaceColor: color },
|
||||
);
|
||||
},
|
||||
[replaceState],
|
||||
);
|
||||
const surfaceColor = usePreviewSurfaceColor(
|
||||
avatarUrl,
|
||||
storedFallback,
|
||||
persistFallback,
|
||||
);
|
||||
|
||||
return (
|
||||
<CreateFlowLockupCardStepShell
|
||||
|
||||
@@ -118,6 +118,11 @@ export interface CreateFlowState {
|
||||
* Set after successful `POST /api/uploads` with purpose `communityAvatar`.
|
||||
*/
|
||||
communityAvatarUrl?: string;
|
||||
/**
|
||||
* Light `hsl(...)` fill for the review card when no logo is uploaded.
|
||||
* Picked once and kept on the draft so reload does not change it.
|
||||
*/
|
||||
previewSurfaceColor?: string;
|
||||
/** Selected chip ids from `community-size` (MultiSelect). */
|
||||
selectedCommunitySizeIds?: string[];
|
||||
/** Selected chip ids from `community-structure` (organization types). */
|
||||
|
||||
Reference in new issue
Block a user