Harden server draft sync (Save & Exit + post-login transfer)

This commit is contained in:
adilallo
2026-04-06 22:46:00 -06:00
parent b6b833e80f
commit a4f0b449b6
24 changed files with 457 additions and 102 deletions
+40 -19
View File
@@ -1,11 +1,6 @@
"use client";
import {
Suspense,
useEffect,
useState,
type ReactNode,
} from "react";
import { Suspense, useEffect, useState, type ReactNode } from "react";
import { usePathname, useRouter } from "next/navigation";
import { CreateFlowProvider, useCreateFlow } from "./context/CreateFlowContext";
import { useCreateFlowNavigation } from "./hooks/useCreateFlowNavigation";
@@ -15,8 +10,15 @@ import { getStepIndex } from "./utils/flowSteps";
import CreateFlowFooter from "../components/utility/CreateFlowFooter";
import Button from "../components/buttons/Button";
import { fetchAuthSession } from "../../lib/create/api";
import messages from "../../messages/en/index";
import { useAuthModal } from "../contexts/AuthModalContext";
import { PostLoginDraftTransfer } from "./PostLoginDraftTransfer";
import { SignedInDraftHydration } from "./SignedInDraftHydration";
import Alert from "../components/modals/Alert";
import {
CreateFlowDraftSaveBannerProvider,
useCreateFlowDraftSaveBanner,
} from "./context/CreateFlowDraftSaveBannerContext";
/** First step where Save & Exit is offered (after informational + name / `text`). */
const SAVE_EXIT_FROM_STEP_INDEX = getStepIndex("select");
@@ -37,19 +39,18 @@ function CreateFlowSessionShell({ children }: { children: ReactNode }) {
}, []);
const sessionResolved = sessionUser !== undefined;
const enableAnonymousPersistence =
sessionResolved && sessionUser === null;
const enableAnonymousPersistence = sessionResolved && sessionUser === null;
return (
<CreateFlowProvider
enableAnonymousPersistence={enableAnonymousPersistence}
>
<CreateFlowLayoutContent
sessionUser={sessionUser}
sessionResolved={sessionResolved}
>
{children}
</CreateFlowLayoutContent>
<CreateFlowProvider enableAnonymousPersistence={enableAnonymousPersistence}>
<CreateFlowDraftSaveBannerProvider>
<CreateFlowLayoutContent
sessionUser={sessionUser}
sessionResolved={sessionResolved}
>
{children}
</CreateFlowLayoutContent>
</CreateFlowDraftSaveBannerProvider>
</CreateFlowProvider>
);
}
@@ -74,6 +75,8 @@ function CreateFlowLayoutContent({
goToPreviousStep,
} = useCreateFlowNavigation();
const { state, clearState } = useCreateFlow();
const { draftSaveBannerMessage, setDraftSaveBannerMessage } =
useCreateFlowDraftSaveBanner();
const runAuthenticatedExit = useCreateFlowExit({
state,
@@ -81,6 +84,7 @@ function CreateFlowLayoutContent({
clearState,
router,
user: sessionUser ?? null,
setDraftSaveBannerMessage,
});
const handleExit = async (opts?: { saveDraft?: boolean }) => {
@@ -104,8 +108,7 @@ function CreateFlowLayoutContent({
const isCompletedStep = currentStep === "completed";
const isRightRailStep = currentStep === "right-rail";
const useFullHeightMain = isCompletedStep || isRightRailStep;
const stepIdx =
currentStep != null ? getStepIndex(currentStep) : -1;
const stepIdx = currentStep != null ? getStepIndex(currentStep) : -1;
const saveDraftOnExit =
Boolean(sessionUser) && stepIdx >= SAVE_EXIT_FROM_STEP_INDEX;
@@ -113,6 +116,24 @@ function CreateFlowLayoutContent({
<div
className={`bg-black flex flex-col ${useFullHeightMain ? "h-screen overflow-hidden" : "min-h-screen"}`}
>
{draftSaveBannerMessage ? (
<div className="w-full shrink-0 px-[var(--spacing-measures-spacing-500,20px)] pt-[var(--spacing-measures-spacing-300,12px)] md:px-[var(--measures-spacing-1800,64px)] z-[100]">
<Alert
type="banner"
status="danger"
title={messages.create.topNav.draftSaveBannerTitle}
description={draftSaveBannerMessage}
onClose={() => setDraftSaveBannerMessage(null)}
className="w-full max-w-[960px] mx-auto"
/>
</div>
) : null}
<Suspense fallback={null}>
<SignedInDraftHydration
sessionUser={sessionUser}
sessionResolved={sessionResolved}
/>
</Suspense>
<Suspense fallback={null}>
<PostLoginDraftTransfer sessionUser={sessionUser} />
</Suspense>