189 lines
5.1 KiB
TypeScript
189 lines
5.1 KiB
TypeScript
import type { DoneActorEvent, ErrorActorEvent } from "xstate";
|
|
|
|
import type { PrivateAlbumUnlockResponse } from "@/data/schemas/private-zone";
|
|
|
|
import {
|
|
getPendingAlbum,
|
|
patchAlbumInList,
|
|
removeAlbum,
|
|
} from "../helper/albums";
|
|
import {
|
|
toPaywallRequest,
|
|
toPrivateZoneErrorMessage,
|
|
toUnlockErrorMessage,
|
|
} from "../helper/unlock";
|
|
import {
|
|
albumMachineSetup,
|
|
failedState,
|
|
idleState,
|
|
loadingState,
|
|
} from "./album-flow";
|
|
import { createPrivateZoneActorActionSetup } from "./setup";
|
|
|
|
const requestUnlockAction = albumMachineSetup.assign(({ event }) =>
|
|
event.type === "PrivateZoneUnlockRequested"
|
|
? {
|
|
pendingConfirmAlbumId: event.albumId,
|
|
unlockErrorMessage: null,
|
|
unlockPaywallRequest: null,
|
|
}
|
|
: {},
|
|
);
|
|
|
|
const cancelUnlockAction = albumMachineSetup.assign({
|
|
pendingConfirmAlbumId: null,
|
|
});
|
|
|
|
const clearUnlockPaywallAction = albumMachineSetup.assign({
|
|
unlockPaywallRequest: null,
|
|
});
|
|
|
|
const beginUnlockAction = albumMachineSetup.assign(({ context }) => ({
|
|
unlockingAlbumId: context.pendingConfirmAlbumId,
|
|
unlockErrorMessage: null,
|
|
unlockPaywallRequest: null,
|
|
}));
|
|
|
|
export const privateZoneMachineSetup = albumMachineSetup.extend({
|
|
actions: {
|
|
requestUnlock: requestUnlockAction,
|
|
cancelUnlock: cancelUnlockAction,
|
|
clearUnlockPaywall: clearUnlockPaywallAction,
|
|
beginUnlock: beginUnlockAction,
|
|
},
|
|
});
|
|
|
|
const unlockDoneSetup =
|
|
createPrivateZoneActorActionSetup<
|
|
DoneActorEvent<PrivateAlbumUnlockResponse>
|
|
>();
|
|
const unlockErrorSetup =
|
|
createPrivateZoneActorActionSetup<ErrorActorEvent>();
|
|
|
|
const applyUnlockSuccessAction = unlockDoneSetup.assign(
|
|
({ context, event }) => ({
|
|
items: patchAlbumInList(context.items, event.output),
|
|
creditBalance: event.output.creditBalance,
|
|
pendingConfirmAlbumId: null,
|
|
unlockingAlbumId: null,
|
|
unlockErrorMessage: null,
|
|
unlockSuccessNonce: context.unlockSuccessNonce + 1,
|
|
}),
|
|
);
|
|
|
|
const applyUnlockPaywallAction = unlockDoneSetup.assign(
|
|
({ context, event }) => ({
|
|
items: patchAlbumInList(context.items, event.output),
|
|
creditBalance: event.output.creditBalance,
|
|
pendingConfirmAlbumId: null,
|
|
unlockingAlbumId: null,
|
|
unlockErrorMessage: null,
|
|
unlockPaywallRequest: toPaywallRequest(event.output),
|
|
}),
|
|
);
|
|
|
|
const applyCostChangedAction = unlockDoneSetup.assign(({ event }) => ({
|
|
pendingConfirmAlbumId: null,
|
|
unlockingAlbumId: null,
|
|
unlockErrorMessage: toUnlockErrorMessage(event.output),
|
|
}));
|
|
|
|
const applyAlbumNotFoundAction = unlockDoneSetup.assign(
|
|
({ context, event }) => ({
|
|
items: removeAlbum(context.items, event.output.albumId),
|
|
creditBalance: event.output.creditBalance,
|
|
pendingConfirmAlbumId: null,
|
|
unlockingAlbumId: null,
|
|
unlockErrorMessage: toUnlockErrorMessage(event.output),
|
|
}),
|
|
);
|
|
|
|
const applyUnlockFailureAction = unlockDoneSetup.assign(
|
|
({ context, event }) => ({
|
|
items: patchAlbumInList(context.items, event.output),
|
|
creditBalance: event.output.creditBalance,
|
|
pendingConfirmAlbumId: null,
|
|
unlockingAlbumId: null,
|
|
unlockErrorMessage: toUnlockErrorMessage(event.output),
|
|
}),
|
|
);
|
|
|
|
const applyUnlockActorErrorAction = unlockErrorSetup.assign(({ event }) => ({
|
|
pendingConfirmAlbumId: null,
|
|
unlockingAlbumId: null,
|
|
unlockErrorMessage: toPrivateZoneErrorMessage(event.error),
|
|
}));
|
|
|
|
const readyState = privateZoneMachineSetup.createStateConfig({
|
|
on: {
|
|
PrivateZoneInit: "loading",
|
|
PrivateZoneRefresh: "loading",
|
|
PrivateZoneUnlockRequested: { actions: "requestUnlock" },
|
|
PrivateZoneUnlockCancelled: { actions: "cancelUnlock" },
|
|
PrivateZoneUnlockConfirmed: [
|
|
{
|
|
guard: "hasPendingAlbum",
|
|
target: "unlocking",
|
|
},
|
|
],
|
|
PrivateZoneUnlockPaywallConsumed: { actions: "clearUnlockPaywall" },
|
|
},
|
|
});
|
|
|
|
const unlockingState = privateZoneMachineSetup.createStateConfig({
|
|
entry: "beginUnlock",
|
|
invoke: {
|
|
src: "unlockAlbum",
|
|
input: ({ context }) => {
|
|
const pendingAlbum = getPendingAlbum(context);
|
|
return {
|
|
albumId: pendingAlbum?.albumId ?? "",
|
|
expectedCost: pendingAlbum?.unlockCost ?? 0,
|
|
};
|
|
},
|
|
onDone: [
|
|
{
|
|
guard: ({ event }) => event.output.unlocked && !event.output.locked,
|
|
target: "ready",
|
|
actions: applyUnlockSuccessAction,
|
|
},
|
|
{
|
|
guard: ({ event }) =>
|
|
event.output.reason === "insufficient_credits",
|
|
target: "ready",
|
|
actions: applyUnlockPaywallAction,
|
|
},
|
|
{
|
|
guard: ({ event }) => event.output.reason === "cost_changed",
|
|
target: "loading",
|
|
actions: applyCostChangedAction,
|
|
},
|
|
{
|
|
guard: ({ event }) => event.output.reason === "not_found",
|
|
target: "ready",
|
|
actions: applyAlbumNotFoundAction,
|
|
},
|
|
{
|
|
target: "ready",
|
|
actions: applyUnlockFailureAction,
|
|
},
|
|
],
|
|
onError: {
|
|
target: "ready",
|
|
actions: applyUnlockActorErrorAction,
|
|
},
|
|
},
|
|
});
|
|
|
|
export const privateZoneRootStateConfig =
|
|
privateZoneMachineSetup.createStateConfig({
|
|
initial: "idle",
|
|
states: {
|
|
idle: idleState,
|
|
loading: loadingState,
|
|
failed: failedState,
|
|
ready: readyState,
|
|
unlocking: unlockingState,
|
|
},
|
|
});
|