refactor(stores): standardize state machine structure
This commit is contained in:
@@ -0,0 +1,212 @@
|
||||
import type { DoneActorEvent, ErrorActorEvent } from "xstate";
|
||||
|
||||
import type { PaymentPlansResponse } from "@/data/dto/payment";
|
||||
|
||||
import {
|
||||
consumeFirstRechargeState,
|
||||
hydratePlansResponseState,
|
||||
refreshPlansResponseState,
|
||||
selectPlanState,
|
||||
} from "../helper/catalog";
|
||||
import {
|
||||
resetOrderState,
|
||||
toPaymentErrorMessage,
|
||||
} from "../helper/order";
|
||||
import {
|
||||
basePaymentMachineSetup,
|
||||
createPaymentActorActionSetup,
|
||||
} from "./setup";
|
||||
|
||||
const initializeCatalogAction = basePaymentMachineSetup.assign(
|
||||
({ context, event }) => {
|
||||
if (event.type !== "PaymentInit") return {};
|
||||
return {
|
||||
planCatalog: event.catalog ?? context.planCatalog,
|
||||
...(event.payChannel ? { payChannel: event.payChannel } : {}),
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
const refreshCatalogAction = basePaymentMachineSetup.assign(
|
||||
({ context, event }) => {
|
||||
if (event.type !== "PaymentInit") return {};
|
||||
const planCatalog = event.catalog ?? context.planCatalog;
|
||||
const catalogChanged = planCatalog !== context.planCatalog;
|
||||
return {
|
||||
planCatalog,
|
||||
...(event.payChannel ? { payChannel: event.payChannel } : {}),
|
||||
...(catalogChanged
|
||||
? {
|
||||
plans: [],
|
||||
selectedPlanId: "",
|
||||
isFirstRecharge: false,
|
||||
autoRenew: planCatalog !== "tip",
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
const selectPlanAction = basePaymentMachineSetup.assign(
|
||||
({ context, event }) =>
|
||||
event.type === "PaymentPlanSelected"
|
||||
? selectPlanState(event.planId, context.plans)
|
||||
: {},
|
||||
);
|
||||
|
||||
const changePayChannelAction = basePaymentMachineSetup.assign(({ event }) =>
|
||||
event.type === "PaymentPayChannelChanged"
|
||||
? {
|
||||
...resetOrderState(),
|
||||
payChannel: event.payChannel,
|
||||
errorMessage: null,
|
||||
}
|
||||
: {},
|
||||
);
|
||||
|
||||
const changeAutoRenewAction = basePaymentMachineSetup.assign(({ event }) =>
|
||||
event.type === "PaymentAutoRenewChanged"
|
||||
? { autoRenew: event.autoRenew, errorMessage: null }
|
||||
: {},
|
||||
);
|
||||
|
||||
const changeAgreementAction = basePaymentMachineSetup.assign(({ event }) =>
|
||||
event.type === "PaymentAgreementChanged"
|
||||
? { agreed: event.agreed, errorMessage: null }
|
||||
: {},
|
||||
);
|
||||
|
||||
const showCreateOrderValidationErrorAction = basePaymentMachineSetup.assign({
|
||||
errorMessage: "Choose a plan and accept the agreement before continuing.",
|
||||
});
|
||||
|
||||
const clearPaymentErrorAction = basePaymentMachineSetup.assign({
|
||||
errorMessage: null,
|
||||
});
|
||||
|
||||
const resetOrderAction = basePaymentMachineSetup.assign(() =>
|
||||
resetOrderState(),
|
||||
);
|
||||
|
||||
const consumeFirstRechargeAction = basePaymentMachineSetup.assign(
|
||||
({ context }) => consumeFirstRechargeState(context),
|
||||
);
|
||||
|
||||
export const catalogMachineSetup = basePaymentMachineSetup.extend({
|
||||
actions: {
|
||||
initializeCatalog: initializeCatalogAction,
|
||||
refreshCatalog: refreshCatalogAction,
|
||||
selectPlan: selectPlanAction,
|
||||
changePayChannel: changePayChannelAction,
|
||||
changeAutoRenew: changeAutoRenewAction,
|
||||
changeAgreement: changeAgreementAction,
|
||||
showCreateOrderValidationError: showCreateOrderValidationErrorAction,
|
||||
clearPaymentError: clearPaymentErrorAction,
|
||||
resetOrder: resetOrderAction,
|
||||
consumeFirstRecharge: consumeFirstRechargeAction,
|
||||
},
|
||||
});
|
||||
|
||||
const cachedPlansDoneSetup =
|
||||
createPaymentActorActionSetup<
|
||||
DoneActorEvent<PaymentPlansResponse | null>
|
||||
>();
|
||||
const plansDoneSetup =
|
||||
createPaymentActorActionSetup<DoneActorEvent<PaymentPlansResponse>>();
|
||||
const plansErrorSetup = createPaymentActorActionSetup<ErrorActorEvent>();
|
||||
|
||||
const hydrateCachedPlansAction = cachedPlansDoneSetup.assign(
|
||||
({ context, event }) =>
|
||||
event.output
|
||||
? hydratePlansResponseState(event.output, context.selectedPlanId)
|
||||
: {},
|
||||
);
|
||||
|
||||
const hydratePlansAction = plansDoneSetup.assign(({ context, event }) =>
|
||||
hydratePlansResponseState(event.output, context.selectedPlanId),
|
||||
);
|
||||
|
||||
const refreshPlansAction = plansDoneSetup.assign(({ context, event }) =>
|
||||
refreshPlansResponseState(event.output, context.selectedPlanId),
|
||||
);
|
||||
|
||||
const applyPlansErrorAction = plansErrorSetup.assign(({ event }) => ({
|
||||
errorMessage: toPaymentErrorMessage(event.error),
|
||||
}));
|
||||
|
||||
export const idleState = catalogMachineSetup.createStateConfig({
|
||||
on: {
|
||||
PaymentInit: {
|
||||
target: "loadingCachedPlans",
|
||||
actions: "initializeCatalog",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const loadingCachedPlansState =
|
||||
catalogMachineSetup.createStateConfig({
|
||||
invoke: {
|
||||
src: "loadCachedPlans",
|
||||
input: ({ context }) => ({ catalog: context.planCatalog }),
|
||||
onDone: [
|
||||
{
|
||||
guard: ({ event }) => (event.output?.plans.length ?? 0) > 0,
|
||||
target: "refreshingPlans",
|
||||
actions: hydrateCachedPlansAction,
|
||||
},
|
||||
{ target: "loadingPlans" },
|
||||
],
|
||||
onError: { target: "loadingPlans" },
|
||||
},
|
||||
});
|
||||
|
||||
export const loadingPlansState = catalogMachineSetup.createStateConfig({
|
||||
invoke: {
|
||||
src: "refreshPlans",
|
||||
input: ({ context }) => ({ catalog: context.planCatalog }),
|
||||
onDone: {
|
||||
target: "ready",
|
||||
actions: hydratePlansAction,
|
||||
},
|
||||
onError: {
|
||||
target: "ready",
|
||||
actions: applyPlansErrorAction,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const refreshingPlansState = catalogMachineSetup.createStateConfig({
|
||||
invoke: {
|
||||
src: "refreshPlans",
|
||||
input: ({ context }) => ({ catalog: context.planCatalog }),
|
||||
onDone: {
|
||||
target: "ready",
|
||||
actions: refreshPlansAction,
|
||||
},
|
||||
onError: {
|
||||
target: "ready",
|
||||
actions: applyPlansErrorAction,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const readyState = catalogMachineSetup.createStateConfig({
|
||||
on: {
|
||||
PaymentInit: {
|
||||
target: "refreshingPlans",
|
||||
actions: "refreshCatalog",
|
||||
},
|
||||
PaymentPlanSelected: { actions: "selectPlan" },
|
||||
PaymentPayChannelChanged: { actions: "changePayChannel" },
|
||||
PaymentAutoRenewChanged: { actions: "changeAutoRenew" },
|
||||
PaymentAgreementChanged: { actions: "changeAgreement" },
|
||||
PaymentCreateOrderSubmitted: [
|
||||
{
|
||||
guard: "canCreateOrder",
|
||||
target: "creatingOrder",
|
||||
},
|
||||
{ actions: "showCreateOrderValidationError" },
|
||||
],
|
||||
PaymentErrorCleared: { actions: "clearPaymentError" },
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user