feat: let guests publish a CommunityRule and claim it on this browser later
Finalize no longer requires a magic link. Guest rows stay off the catalog until sign-in on the same browser attaches ownership, and the login modal kebab no longer acts as a second close. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -20,6 +20,8 @@ type OpenLogin = (args: {
|
||||
backdropVariant: "blurredYellow";
|
||||
}) => void;
|
||||
|
||||
type SessionUser = { id: string; email: string };
|
||||
|
||||
export type UseCreateFlowFinalizeResult = {
|
||||
publishBannerMessage: string | null;
|
||||
setPublishBannerMessage: (_message: string | null) => void;
|
||||
@@ -34,6 +36,8 @@ export function useCreateFlowFinalize({
|
||||
openLogin,
|
||||
updateState,
|
||||
loginReturnPath,
|
||||
sessionUser,
|
||||
onGuestPublished,
|
||||
}: {
|
||||
state: CreateFlowState;
|
||||
router: AppRouterLike;
|
||||
@@ -41,6 +45,13 @@ export function useCreateFlowFinalize({
|
||||
updateState: (_patch: Partial<CreateFlowState>) => void;
|
||||
/** Session gate return path (`?syncDraft=1`) — differs for `/create/edit-rule` vs `/create/final-review`. */
|
||||
loginReturnPath: string;
|
||||
/**
|
||||
* `undefined` while `/api/auth/session` is in flight — finalize is a no-op
|
||||
* until it resolves. `null` is a guest (anonymous publish).
|
||||
*/
|
||||
sessionUser: SessionUser | null | undefined;
|
||||
/** Guest publish succeeded; `skippedInvites` when stakeholder emails were not sent. */
|
||||
onGuestPublished?: (_info: { skippedInvites: boolean }) => void;
|
||||
}): UseCreateFlowFinalizeResult {
|
||||
const [publishBannerMessage, setPublishBannerMessage] = useState<
|
||||
string | null
|
||||
@@ -48,6 +59,8 @@ export function useCreateFlowFinalize({
|
||||
const [isPublishing, setIsPublishing] = useState(false);
|
||||
|
||||
const finalize = useCallback(async () => {
|
||||
if (sessionUser === undefined) return;
|
||||
|
||||
setPublishBannerMessage(null);
|
||||
const payloadResult = buildPublishPayload(state);
|
||||
if (payloadResult.ok === false) {
|
||||
@@ -100,9 +113,13 @@ export function useCreateFlowFinalize({
|
||||
return;
|
||||
}
|
||||
|
||||
const stakeholderEmails = (state.stakeholderEmails ?? []).filter(
|
||||
const isGuest = sessionUser === null;
|
||||
const rawStakeholderEmails = (state.stakeholderEmails ?? []).filter(
|
||||
(e) => typeof e === "string" && e.trim() !== "",
|
||||
);
|
||||
const stakeholderEmails = isGuest ? [] : rawStakeholderEmails;
|
||||
const skippedGuestInvites = isGuest && rawStakeholderEmails.length > 0;
|
||||
|
||||
const publishResult = await publishRule({
|
||||
title,
|
||||
summary,
|
||||
@@ -117,6 +134,9 @@ export function useCreateFlowFinalize({
|
||||
summary: summary ?? null,
|
||||
document: ruleDocument,
|
||||
});
|
||||
if (isGuest) {
|
||||
onGuestPublished?.({ skippedInvites: skippedGuestInvites });
|
||||
}
|
||||
router.push(
|
||||
createFlowStepPath("completed", {
|
||||
[CREATE_FLOW_COMPLETED_CELEBRATE_QUERY]:
|
||||
@@ -138,7 +158,15 @@ export function useCreateFlowFinalize({
|
||||
? publishResult.error
|
||||
: messages.create.reviewAndComplete.publish.genericPublishFailed,
|
||||
);
|
||||
}, [state, router, openLogin, updateState, loginReturnPath]);
|
||||
}, [
|
||||
state,
|
||||
router,
|
||||
openLogin,
|
||||
updateState,
|
||||
loginReturnPath,
|
||||
sessionUser,
|
||||
onGuestPublished,
|
||||
]);
|
||||
|
||||
return {
|
||||
publishBannerMessage,
|
||||
|
||||
Reference in New Issue
Block a user