Invite people after review, then finish the Rule, with login before inviting and Invite your members on Profile.
This commit is contained in:
1 parent
88bfccd1e7
commit
70ceb73104
31 files changed
+1388
-401
No files matched your search
@@ -17,21 +17,27 @@ import { createFlowStepPath } from "../utils/createFlowPaths";
|
||||
type AppRouterLike = { push: (_href: string) => void };
|
||||
|
||||
type OpenLogin = (args: {
|
||||
variant: "default" | "saveProgress" | "keepRule";
|
||||
variant: "default" | "saveProgress" | "keepRule" | "invite";
|
||||
nextPath: string;
|
||||
backdropVariant: "blurredYellow";
|
||||
}) => void;
|
||||
|
||||
type SessionUser = { id: string; email: string };
|
||||
|
||||
export type FinalizeOptions = {
|
||||
/** When true, finish without sending the current email list. */
|
||||
skipInvites?: boolean;
|
||||
};
|
||||
|
||||
export type UseCreateFlowFinalizeResult = {
|
||||
publishBannerTitle: string | null;
|
||||
publishBannerMessage: string | null;
|
||||
setPublishBannerMessage: (_message: string | null) => void;
|
||||
isPublishing: boolean;
|
||||
finalize: () => Promise<void>;
|
||||
finalize: (_opts?: FinalizeOptions) => Promise<void>;
|
||||
};
|
||||
|
||||
/** Final Review → publish: banner + `isPublishing`, consumed by `CreateFlowLayoutClient`. */
|
||||
/** Invite / Finish → publish: banner + `isPublishing`, consumed by `CreateFlowLayoutClient`. */
|
||||
export function useCreateFlowFinalize({
|
||||
state,
|
||||
router,
|
||||
@@ -55,65 +61,67 @@ export function useCreateFlowFinalize({
|
||||
/** Guest publish succeeded; `skippedInvites` when stakeholder emails were not sent. */
|
||||
onGuestPublished?: (_info: { skippedInvites: boolean }) => void;
|
||||
}): UseCreateFlowFinalizeResult {
|
||||
const [publishBannerTitle, setPublishBannerTitle] = useState<string | null>(
|
||||
null,
|
||||
);
|
||||
const [publishBannerMessage, setPublishBannerMessage] = useState<
|
||||
string | null
|
||||
>(null);
|
||||
const [isPublishing, setIsPublishing] = useState(false);
|
||||
|
||||
const finalize = useCallback(async () => {
|
||||
if (sessionUser === undefined) return;
|
||||
|
||||
setPublishBannerMessage(null);
|
||||
setIsPublishing(true);
|
||||
const prepared = await stateWithPendingCommunityAvatar(state);
|
||||
if (prepared.ok === false) {
|
||||
setIsPublishing(false);
|
||||
setPublishBannerMessage(messages.create.upload.errors.generic);
|
||||
return;
|
||||
}
|
||||
if (prepared.state.communityAvatarUrl !== state.communityAvatarUrl) {
|
||||
updateState({ communityAvatarUrl: prepared.state.communityAvatarUrl });
|
||||
}
|
||||
const payloadResult = buildPublishPayload(prepared.state);
|
||||
if (payloadResult.ok === false) {
|
||||
setIsPublishing(false);
|
||||
setPublishBannerMessage(
|
||||
payloadResult.error === "missingCommunityName"
|
||||
? messages.create.reviewAndComplete.publish.missingCommunityName
|
||||
: payloadResult.error,
|
||||
const showFailedBanner = useCallback(
|
||||
(message: string, sendingInvites: boolean) => {
|
||||
const inviteCopy = messages.create.reviewAndComplete.confirmStakeholders;
|
||||
if (sendingInvites) {
|
||||
setPublishBannerTitle(inviteCopy.sendFailedTitle);
|
||||
setPublishBannerMessage(inviteCopy.sendFailedDescription);
|
||||
return;
|
||||
}
|
||||
setPublishBannerTitle(
|
||||
messages.create.reviewAndComplete.publish.publishBannerTitle,
|
||||
);
|
||||
return;
|
||||
}
|
||||
const { title, summary, document: ruleDocument } = payloadResult;
|
||||
setPublishBannerMessage(message);
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const editingId =
|
||||
typeof state.editingPublishedRuleId === "string"
|
||||
? state.editingPublishedRuleId.trim()
|
||||
: "";
|
||||
const finalize = useCallback(
|
||||
async (opts?: FinalizeOptions) => {
|
||||
if (sessionUser === undefined) return;
|
||||
const skipInvites = opts?.skipInvites === true;
|
||||
|
||||
if (editingId.length > 0 && sessionUser === null) {
|
||||
setIsPublishing(false);
|
||||
writeLastPublishedRule({
|
||||
id: editingId,
|
||||
title,
|
||||
summary: summary ?? null,
|
||||
document: ruleDocument,
|
||||
});
|
||||
updateState({ editingPublishedRuleId: undefined });
|
||||
writePendingKeepRuleLogin();
|
||||
onGuestPublished?.({ skippedInvites: false });
|
||||
router.push(createFlowStepPath("completed"));
|
||||
return;
|
||||
}
|
||||
setPublishBannerTitle(null);
|
||||
setPublishBannerMessage(null);
|
||||
setIsPublishing(true);
|
||||
const prepared = await stateWithPendingCommunityAvatar(state);
|
||||
if (prepared.ok === false) {
|
||||
setIsPublishing(false);
|
||||
showFailedBanner(messages.create.upload.errors.generic, false);
|
||||
return;
|
||||
}
|
||||
if (prepared.state.communityAvatarUrl !== state.communityAvatarUrl) {
|
||||
updateState({ communityAvatarUrl: prepared.state.communityAvatarUrl });
|
||||
}
|
||||
const payloadResult = buildPublishPayload(prepared.state);
|
||||
if (payloadResult.ok === false) {
|
||||
setIsPublishing(false);
|
||||
showFailedBanner(
|
||||
payloadResult.error === "missingCommunityName"
|
||||
? messages.create.reviewAndComplete.publish.missingCommunityName
|
||||
: payloadResult.error,
|
||||
false,
|
||||
);
|
||||
return;
|
||||
}
|
||||
const { title, summary, document: ruleDocument } = payloadResult;
|
||||
|
||||
if (editingId.length > 0) {
|
||||
const updateResult = await updatePublishedRule(editingId, {
|
||||
title,
|
||||
summary: summary ?? null,
|
||||
document: ruleDocument,
|
||||
});
|
||||
setIsPublishing(false);
|
||||
if (updateResult.ok === true) {
|
||||
const editingId =
|
||||
typeof state.editingPublishedRuleId === "string"
|
||||
? state.editingPublishedRuleId.trim()
|
||||
: "";
|
||||
|
||||
if (editingId.length > 0 && sessionUser === null) {
|
||||
setIsPublishing(false);
|
||||
writeLastPublishedRule({
|
||||
id: editingId,
|
||||
title,
|
||||
@@ -121,10 +129,87 @@ export function useCreateFlowFinalize({
|
||||
document: ruleDocument,
|
||||
});
|
||||
updateState({ editingPublishedRuleId: undefined });
|
||||
writePendingKeepRuleLogin();
|
||||
onGuestPublished?.({ skippedInvites: false });
|
||||
router.push(createFlowStepPath("completed"));
|
||||
return;
|
||||
}
|
||||
if (updateResult.status === 401) {
|
||||
|
||||
if (editingId.length > 0) {
|
||||
const updateResult = await updatePublishedRule(editingId, {
|
||||
title,
|
||||
summary: summary ?? null,
|
||||
document: ruleDocument,
|
||||
});
|
||||
setIsPublishing(false);
|
||||
if (updateResult.ok === true) {
|
||||
writeLastPublishedRule({
|
||||
id: editingId,
|
||||
title,
|
||||
summary: summary ?? null,
|
||||
document: ruleDocument,
|
||||
});
|
||||
updateState({ editingPublishedRuleId: undefined });
|
||||
router.push(createFlowStepPath("completed"));
|
||||
return;
|
||||
}
|
||||
if (updateResult.status === 401) {
|
||||
openLogin({
|
||||
variant: sessionUser === null ? "keepRule" : "default",
|
||||
nextPath:
|
||||
sessionUser === null
|
||||
? createFlowStepPath("completed")
|
||||
: loginReturnPath,
|
||||
backdropVariant: "blurredYellow",
|
||||
});
|
||||
return;
|
||||
}
|
||||
showFailedBanner(
|
||||
updateResult.error.trim() !== ""
|
||||
? updateResult.error
|
||||
: messages.create.reviewAndComplete.publish.genericPublishFailed,
|
||||
false,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const isGuest = sessionUser === null;
|
||||
const rawStakeholderEmails = skipInvites
|
||||
? []
|
||||
: (state.stakeholderEmails ?? []).filter(
|
||||
(e) => typeof e === "string" && e.trim() !== "",
|
||||
);
|
||||
const stakeholderEmails = isGuest ? [] : rawStakeholderEmails;
|
||||
const skippedGuestInvites = isGuest && rawStakeholderEmails.length > 0;
|
||||
const sendingInvites = stakeholderEmails.length > 0;
|
||||
|
||||
const publishResult = await publishRule({
|
||||
title,
|
||||
summary,
|
||||
document: ruleDocument,
|
||||
...(stakeholderEmails.length > 0 ? { stakeholderEmails } : {}),
|
||||
});
|
||||
setIsPublishing(false);
|
||||
if (publishResult.ok === true) {
|
||||
writeLastPublishedRule({
|
||||
id: publishResult.id,
|
||||
title,
|
||||
summary: summary ?? null,
|
||||
document: ruleDocument,
|
||||
});
|
||||
if (isGuest) {
|
||||
writePendingKeepRuleLogin();
|
||||
onGuestPublished?.({ skippedInvites: skippedGuestInvites });
|
||||
}
|
||||
router.push(
|
||||
createFlowStepPath("completed", {
|
||||
[CREATE_FLOW_COMPLETED_CELEBRATE_QUERY]:
|
||||
CREATE_FLOW_COMPLETED_CELEBRATE_VALUE,
|
||||
}),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (publishResult.status === 401) {
|
||||
openLogin({
|
||||
variant: sessionUser === null ? "keepRule" : "default",
|
||||
nextPath:
|
||||
@@ -135,76 +220,34 @@ export function useCreateFlowFinalize({
|
||||
});
|
||||
return;
|
||||
}
|
||||
setPublishBannerMessage(
|
||||
updateResult.error.trim() !== ""
|
||||
? updateResult.error
|
||||
showFailedBanner(
|
||||
publishResult.error.trim() !== ""
|
||||
? publishResult.error
|
||||
: messages.create.reviewAndComplete.publish.genericPublishFailed,
|
||||
sendingInvites,
|
||||
);
|
||||
return;
|
||||
}
|
||||
},
|
||||
[
|
||||
state,
|
||||
router,
|
||||
openLogin,
|
||||
updateState,
|
||||
loginReturnPath,
|
||||
sessionUser,
|
||||
onGuestPublished,
|
||||
showFailedBanner,
|
||||
],
|
||||
);
|
||||
|
||||
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,
|
||||
document: ruleDocument,
|
||||
...(stakeholderEmails.length > 0 ? { stakeholderEmails } : {}),
|
||||
});
|
||||
setIsPublishing(false);
|
||||
if (publishResult.ok === true) {
|
||||
writeLastPublishedRule({
|
||||
id: publishResult.id,
|
||||
title,
|
||||
summary: summary ?? null,
|
||||
document: ruleDocument,
|
||||
});
|
||||
if (isGuest) {
|
||||
writePendingKeepRuleLogin();
|
||||
onGuestPublished?.({ skippedInvites: skippedGuestInvites });
|
||||
}
|
||||
router.push(
|
||||
createFlowStepPath("completed", {
|
||||
[CREATE_FLOW_COMPLETED_CELEBRATE_QUERY]:
|
||||
CREATE_FLOW_COMPLETED_CELEBRATE_VALUE,
|
||||
}),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (publishResult.status === 401) {
|
||||
openLogin({
|
||||
variant: sessionUser === null ? "keepRule" : "default",
|
||||
nextPath:
|
||||
sessionUser === null
|
||||
? createFlowStepPath("completed")
|
||||
: loginReturnPath,
|
||||
backdropVariant: "blurredYellow",
|
||||
});
|
||||
return;
|
||||
}
|
||||
setPublishBannerMessage(
|
||||
publishResult.error.trim() !== ""
|
||||
? publishResult.error
|
||||
: messages.create.reviewAndComplete.publish.genericPublishFailed,
|
||||
);
|
||||
}, [
|
||||
state,
|
||||
router,
|
||||
openLogin,
|
||||
updateState,
|
||||
loginReturnPath,
|
||||
sessionUser,
|
||||
onGuestPublished,
|
||||
]);
|
||||
const clearPublishBanner = useCallback((message: string | null) => {
|
||||
if (message === null) setPublishBannerTitle(null);
|
||||
setPublishBannerMessage(message);
|
||||
}, []);
|
||||
|
||||
return {
|
||||
publishBannerTitle,
|
||||
publishBannerMessage,
|
||||
setPublishBannerMessage,
|
||||
setPublishBannerMessage: clearPublishBanner,
|
||||
isPublishing,
|
||||
finalize,
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user