refactor(private-zone): use canonical product name
This commit is contained in:
+11
-11
@@ -2,18 +2,18 @@ import { describe, expect, it } from "vitest";
|
||||
import { createActor, waitFor } from "xstate";
|
||||
|
||||
import {
|
||||
createTestPrivateZoomMachine,
|
||||
loadPrivateZoom,
|
||||
createTestPrivateZoneMachine,
|
||||
loadPrivateZone,
|
||||
makeAlbum,
|
||||
makeAlbumsResponse,
|
||||
TEST_PRIVATE_ZOOM_INPUT,
|
||||
} from "./private-zoom-machine.test-utils";
|
||||
TEST_PRIVATE_ZONE_INPUT,
|
||||
} from "./private-zone-machine.test-utils";
|
||||
|
||||
describe("private zoom album flow", () => {
|
||||
describe("private zone album flow", () => {
|
||||
it("loads albums for the supplied character", async () => {
|
||||
let loadedCharacterId: string | null = null;
|
||||
const actor = createActor(
|
||||
createTestPrivateZoomMachine({
|
||||
createTestPrivateZoneMachine({
|
||||
onLoad: ({ characterId }) => {
|
||||
loadedCharacterId = characterId;
|
||||
},
|
||||
@@ -21,7 +21,7 @@ describe("private zoom album flow", () => {
|
||||
{ input: { characterId: "character_aria" } },
|
||||
).start();
|
||||
|
||||
actor.send({ type: "PrivateZoomInit" });
|
||||
actor.send({ type: "PrivateZoneInit" });
|
||||
await waitFor(actor, (snapshot) => snapshot.matches("ready"));
|
||||
|
||||
expect(actor.getSnapshot().context.characterId).toBe("character_aria");
|
||||
@@ -30,7 +30,7 @@ describe("private zoom album flow", () => {
|
||||
});
|
||||
|
||||
it("loads only the first non-paginated album page", async () => {
|
||||
const actor = await loadPrivateZoom({
|
||||
const actor = await loadPrivateZone({
|
||||
loadResponse: makeAlbumsResponse({
|
||||
items: Array.from({ length: 20 }, (_, index) => makeAlbum(index)),
|
||||
}),
|
||||
@@ -43,12 +43,12 @@ describe("private zoom album flow", () => {
|
||||
|
||||
it("moves to failed when the album actor rejects", async () => {
|
||||
const actor = createActor(
|
||||
createTestPrivateZoomMachine({
|
||||
createTestPrivateZoneMachine({
|
||||
loadError: new Error("albums unavailable"),
|
||||
}),
|
||||
{ input: TEST_PRIVATE_ZOOM_INPUT },
|
||||
{ input: TEST_PRIVATE_ZONE_INPUT },
|
||||
).start();
|
||||
actor.send({ type: "PrivateZoomInit" });
|
||||
actor.send({ type: "PrivateZoneInit" });
|
||||
await waitFor(actor, (snapshot) => snapshot.matches("failed"));
|
||||
expect(actor.getSnapshot().context.errorMessage).toBe(
|
||||
"albums unavailable",
|
||||
+13
-13
@@ -8,13 +8,13 @@ import {
|
||||
type PrivateAlbumUnlockResponseInput,
|
||||
type PrivateAlbumsResponse,
|
||||
type PrivateAlbumsResponseInput,
|
||||
} from "@/data/schemas/private-zoom";
|
||||
import { privateZoomMachine } from "@/stores/private-zoom/private-zoom-machine";
|
||||
} from "@/data/schemas/private-zone";
|
||||
import { privateZoneMachine } from "@/stores/private-zone/private-zone-machine";
|
||||
|
||||
export const ALBUM_ID = "a1b2c3d4-0000-0000-0000-000000000000";
|
||||
export const COVER_URL =
|
||||
"https://dbapi.banlv-ai.com/storage/v1/object/public/elio-schedules/01.jpg";
|
||||
export const TEST_PRIVATE_ZOOM_INPUT = {
|
||||
export const TEST_PRIVATE_ZONE_INPUT = {
|
||||
characterId: DEFAULT_CHARACTER_ID,
|
||||
};
|
||||
|
||||
@@ -65,7 +65,7 @@ export function makeUnlockResponse(
|
||||
});
|
||||
}
|
||||
|
||||
export function createTestPrivateZoomMachine(
|
||||
export function createTestPrivateZoneMachine(
|
||||
options: {
|
||||
loadResponse?: PrivateAlbumsResponse;
|
||||
unlockResponse?: PrivateAlbumUnlockResponse;
|
||||
@@ -74,7 +74,7 @@ export function createTestPrivateZoomMachine(
|
||||
onLoad?: (input: { characterId: string }) => void;
|
||||
} = {},
|
||||
) {
|
||||
return privateZoomMachine.provide({
|
||||
return privateZoneMachine.provide({
|
||||
actors: {
|
||||
loadAlbums: fromPromise<PrivateAlbumsResponse, { characterId: string }>(
|
||||
async ({ input }) => {
|
||||
@@ -91,24 +91,24 @@ export function createTestPrivateZoomMachine(
|
||||
});
|
||||
}
|
||||
|
||||
export async function loadPrivateZoom(
|
||||
options: Parameters<typeof createTestPrivateZoomMachine>[0] = {},
|
||||
export async function loadPrivateZone(
|
||||
options: Parameters<typeof createTestPrivateZoneMachine>[0] = {},
|
||||
) {
|
||||
const actor = createActor(createTestPrivateZoomMachine(options), {
|
||||
input: TEST_PRIVATE_ZOOM_INPUT,
|
||||
const actor = createActor(createTestPrivateZoneMachine(options), {
|
||||
input: TEST_PRIVATE_ZONE_INPUT,
|
||||
}).start();
|
||||
actor.send({ type: "PrivateZoomInit" });
|
||||
actor.send({ type: "PrivateZoneInit" });
|
||||
await waitFor(actor, (snapshot) => snapshot.matches("ready"));
|
||||
return actor;
|
||||
}
|
||||
|
||||
export async function unlockFirstAlbum(
|
||||
actor: Awaited<ReturnType<typeof loadPrivateZoom>>,
|
||||
actor: Awaited<ReturnType<typeof loadPrivateZone>>,
|
||||
) {
|
||||
actor.send({
|
||||
type: "PrivateZoomUnlockRequested",
|
||||
type: "PrivateZoneUnlockRequested",
|
||||
albumId: ALBUM_ID,
|
||||
});
|
||||
actor.send({ type: "PrivateZoomUnlockConfirmed" });
|
||||
actor.send({ type: "PrivateZoneUnlockConfirmed" });
|
||||
await waitFor(actor, (snapshot) => snapshot.matches("ready"));
|
||||
}
|
||||
+9
-9
@@ -3,14 +3,14 @@ import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
ALBUM_ID,
|
||||
COVER_URL,
|
||||
loadPrivateZoom,
|
||||
loadPrivateZone,
|
||||
makeUnlockResponse,
|
||||
unlockFirstAlbum,
|
||||
} from "./private-zoom-machine.test-utils";
|
||||
} from "./private-zone-machine.test-utils";
|
||||
|
||||
describe("private zoom unlock flow", () => {
|
||||
describe("private zone unlock flow", () => {
|
||||
it("patches album images after a successful unlock", async () => {
|
||||
const actor = await loadPrivateZoom();
|
||||
const actor = await loadPrivateZone();
|
||||
await unlockFirstAlbum(actor);
|
||||
expect(actor.getSnapshot().context.items[0]).toMatchObject({
|
||||
albumId: ALBUM_ID,
|
||||
@@ -24,7 +24,7 @@ describe("private zoom unlock flow", () => {
|
||||
});
|
||||
|
||||
it("exposes a paywall request when credits are low", async () => {
|
||||
const actor = await loadPrivateZoom({
|
||||
const actor = await loadPrivateZone({
|
||||
unlockResponse: makeUnlockResponse({
|
||||
locked: true,
|
||||
unlocked: false,
|
||||
@@ -47,7 +47,7 @@ describe("private zoom unlock flow", () => {
|
||||
|
||||
it("reloads when expected cost changes", async () => {
|
||||
let loadCount = 0;
|
||||
const actor = await loadPrivateZoom({
|
||||
const actor = await loadPrivateZone({
|
||||
onLoad: () => {
|
||||
loadCount += 1;
|
||||
},
|
||||
@@ -66,7 +66,7 @@ describe("private zoom unlock flow", () => {
|
||||
});
|
||||
|
||||
it("removes an album that no longer exists", async () => {
|
||||
const actor = await loadPrivateZoom({
|
||||
const actor = await loadPrivateZone({
|
||||
unlockResponse: makeUnlockResponse({
|
||||
locked: true,
|
||||
unlocked: false,
|
||||
@@ -80,7 +80,7 @@ describe("private zoom unlock flow", () => {
|
||||
});
|
||||
|
||||
it("keeps refunded persistence failures locked", async () => {
|
||||
const actor = await loadPrivateZoom({
|
||||
const actor = await loadPrivateZone({
|
||||
unlockResponse: makeUnlockResponse({
|
||||
locked: true,
|
||||
unlocked: false,
|
||||
@@ -99,7 +99,7 @@ describe("private zoom unlock flow", () => {
|
||||
});
|
||||
|
||||
it("returns to ready when the unlock actor rejects", async () => {
|
||||
const actor = await loadPrivateZoom({
|
||||
const actor = await loadPrivateZone({
|
||||
unlockError: new Error("unlock unavailable"),
|
||||
});
|
||||
await unlockFirstAlbum(actor);
|
||||
@@ -3,15 +3,15 @@ import {
|
||||
PrivateAlbumSchema,
|
||||
type PrivateAlbumsResponse,
|
||||
type PrivateAlbumUnlockResponse,
|
||||
} from "@/data/schemas/private-zoom";
|
||||
} from "@/data/schemas/private-zone";
|
||||
|
||||
import type { PrivateZoomState } from "../private-zoom-state";
|
||||
import type { PrivateZoneState } from "../private-zone-state";
|
||||
|
||||
export const PRIVATE_ZOOM_PAGE_SIZE = 20;
|
||||
export const PRIVATE_ZONE_PAGE_SIZE = 20;
|
||||
|
||||
export function applyAlbumsResponse(
|
||||
response: PrivateAlbumsResponse,
|
||||
): Partial<PrivateZoomState> {
|
||||
): Partial<PrivateZoneState> {
|
||||
return {
|
||||
items: response.items,
|
||||
creditBalance: response.creditBalance,
|
||||
@@ -20,7 +20,7 @@ export function applyAlbumsResponse(
|
||||
}
|
||||
|
||||
export function getPendingAlbum(
|
||||
context: PrivateZoomState,
|
||||
context: PrivateZoneState,
|
||||
): PrivateAlbum | null {
|
||||
if (!context.pendingConfirmAlbumId) return null;
|
||||
return (
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { PrivateAlbumUnlockResponse } from "@/data/schemas/private-zoom";
|
||||
import type { PrivateAlbumUnlockResponse } from "@/data/schemas/private-zone";
|
||||
|
||||
import type { PrivateZoomUnlockPaywallRequest } from "../private-zoom-state";
|
||||
import type { PrivateZoneUnlockPaywallRequest } from "../private-zone-state";
|
||||
|
||||
export function toPrivateZoomErrorMessage(error: unknown): string {
|
||||
export function toPrivateZoneErrorMessage(error: unknown): string {
|
||||
if (error instanceof Error && error.message) return error.message;
|
||||
return "Private Zoom is temporarily unavailable. Please try again.";
|
||||
return "Private Zone is temporarily unavailable. Please try again.";
|
||||
}
|
||||
|
||||
export function toUnlockErrorMessage(
|
||||
@@ -30,7 +30,7 @@ export function toUnlockErrorMessage(
|
||||
|
||||
export function toPaywallRequest(
|
||||
response: PrivateAlbumUnlockResponse,
|
||||
): PrivateZoomUnlockPaywallRequest {
|
||||
): PrivateZoneUnlockPaywallRequest {
|
||||
return {
|
||||
albumId: response.albumId,
|
||||
reason: response.reason,
|
||||
@@ -0,0 +1,7 @@
|
||||
/** @file Private Zone store barrel. */
|
||||
|
||||
export * from "./private-zone-context";
|
||||
export * from "./private-zone-events";
|
||||
export * from "./helper";
|
||||
export * from "./private-zone-machine";
|
||||
export * from "./private-zone-state";
|
||||
+6
-6
@@ -3,19 +3,19 @@ import { fromPromise } from "xstate";
|
||||
import type {
|
||||
PrivateAlbumsResponse,
|
||||
PrivateAlbumUnlockResponse,
|
||||
} from "@/data/schemas/private-zoom";
|
||||
import { getPrivateZoomRepository } from "@/data/repositories/private_zoom_repository";
|
||||
} from "@/data/schemas/private-zone";
|
||||
import { getPrivateZoneRepository } from "@/data/repositories/private_zone_repository";
|
||||
import { Result } from "@/utils/result";
|
||||
|
||||
import { PRIVATE_ZOOM_PAGE_SIZE } from "../../helper/albums";
|
||||
import { PRIVATE_ZONE_PAGE_SIZE } from "../../helper/albums";
|
||||
|
||||
export const loadPrivateAlbumsActor =
|
||||
fromPromise<PrivateAlbumsResponse, { characterId: string }>(async ({
|
||||
input,
|
||||
}) => {
|
||||
const result = await getPrivateZoomRepository().getAlbums({
|
||||
const result = await getPrivateZoneRepository().getAlbums({
|
||||
characterId: input.characterId,
|
||||
limit: PRIVATE_ZOOM_PAGE_SIZE,
|
||||
limit: PRIVATE_ZONE_PAGE_SIZE,
|
||||
});
|
||||
if (Result.isErr(result)) throw result.error;
|
||||
return result.data;
|
||||
@@ -25,7 +25,7 @@ export const unlockPrivateAlbumActor = fromPromise<
|
||||
PrivateAlbumUnlockResponse,
|
||||
{ albumId: string; expectedCost: number }
|
||||
>(async ({ input }) => {
|
||||
const result = await getPrivateZoomRepository().unlockAlbum(
|
||||
const result = await getPrivateZoneRepository().unlockAlbum(
|
||||
input.albumId,
|
||||
input.expectedCost,
|
||||
);
|
||||
+12
-12
@@ -1,43 +1,43 @@
|
||||
import type { DoneActorEvent, ErrorActorEvent } from "xstate";
|
||||
|
||||
import type { PrivateAlbumsResponse } from "@/data/schemas/private-zoom";
|
||||
import type { PrivateAlbumsResponse } from "@/data/schemas/private-zone";
|
||||
|
||||
import { applyAlbumsResponse } from "../helper/albums";
|
||||
import { toPrivateZoomErrorMessage } from "../helper/unlock";
|
||||
import { toPrivateZoneErrorMessage } from "../helper/unlock";
|
||||
import {
|
||||
basePrivateZoomMachineSetup,
|
||||
createPrivateZoomActorActionSetup,
|
||||
basePrivateZoneMachineSetup,
|
||||
createPrivateZoneActorActionSetup,
|
||||
} from "./setup";
|
||||
|
||||
const clearAlbumLoadStateAction = basePrivateZoomMachineSetup.assign({
|
||||
const clearAlbumLoadStateAction = basePrivateZoneMachineSetup.assign({
|
||||
errorMessage: null,
|
||||
unlockPaywallRequest: null,
|
||||
});
|
||||
|
||||
export const albumMachineSetup = basePrivateZoomMachineSetup.extend({
|
||||
export const albumMachineSetup = basePrivateZoneMachineSetup.extend({
|
||||
actions: {
|
||||
clearAlbumLoadState: clearAlbumLoadStateAction,
|
||||
},
|
||||
});
|
||||
|
||||
const albumsDoneSetup =
|
||||
createPrivateZoomActorActionSetup<
|
||||
createPrivateZoneActorActionSetup<
|
||||
DoneActorEvent<PrivateAlbumsResponse>
|
||||
>();
|
||||
const albumErrorSetup =
|
||||
createPrivateZoomActorActionSetup<ErrorActorEvent>();
|
||||
createPrivateZoneActorActionSetup<ErrorActorEvent>();
|
||||
|
||||
const applyAlbumsResponseAction = albumsDoneSetup.assign(({ event }) =>
|
||||
applyAlbumsResponse(event.output),
|
||||
);
|
||||
|
||||
const applyAlbumLoadErrorAction = albumErrorSetup.assign(({ event }) => ({
|
||||
errorMessage: toPrivateZoomErrorMessage(event.error),
|
||||
errorMessage: toPrivateZoneErrorMessage(event.error),
|
||||
}));
|
||||
|
||||
export const idleState = albumMachineSetup.createStateConfig({
|
||||
on: {
|
||||
PrivateZoomInit: "loading",
|
||||
PrivateZoneInit: "loading",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -59,7 +59,7 @@ export const loadingState = albumMachineSetup.createStateConfig({
|
||||
|
||||
export const failedState = albumMachineSetup.createStateConfig({
|
||||
on: {
|
||||
PrivateZoomInit: "loading",
|
||||
PrivateZoomRefresh: "loading",
|
||||
PrivateZoneInit: "loading",
|
||||
PrivateZoneRefresh: "loading",
|
||||
},
|
||||
});
|
||||
+14
-14
@@ -1,22 +1,22 @@
|
||||
import { setup, type ActionFunction, type EventObject } from "xstate";
|
||||
|
||||
import { getPendingAlbum } from "../helper/albums";
|
||||
import type { PrivateZoomEvent } from "../private-zoom-events";
|
||||
import type { PrivateZoomState } from "../private-zoom-state";
|
||||
import type { PrivateZoneEvent } from "../private-zone-events";
|
||||
import type { PrivateZoneState } from "../private-zone-state";
|
||||
import {
|
||||
loadPrivateAlbumsActor,
|
||||
unlockPrivateAlbumActor,
|
||||
} from "./actors/albums";
|
||||
|
||||
export interface PrivateZoomMachineInput {
|
||||
export interface PrivateZoneMachineInput {
|
||||
characterId: string;
|
||||
}
|
||||
|
||||
export const basePrivateZoomMachineSetup = setup({
|
||||
export const basePrivateZoneMachineSetup = setup({
|
||||
types: {
|
||||
context: {} as PrivateZoomState,
|
||||
events: {} as PrivateZoomEvent,
|
||||
input: {} as PrivateZoomMachineInput,
|
||||
context: {} as PrivateZoneState,
|
||||
events: {} as PrivateZoneEvent,
|
||||
input: {} as PrivateZoneMachineInput,
|
||||
},
|
||||
actors: {
|
||||
loadAlbums: loadPrivateAlbumsActor,
|
||||
@@ -27,10 +27,10 @@ export const basePrivateZoomMachineSetup = setup({
|
||||
},
|
||||
});
|
||||
|
||||
type PrivateZoomActorAction<TEvent extends EventObject> = ActionFunction<
|
||||
PrivateZoomState,
|
||||
type PrivateZoneActorAction<TEvent extends EventObject> = ActionFunction<
|
||||
PrivateZoneState,
|
||||
TEvent,
|
||||
PrivateZoomEvent,
|
||||
PrivateZoneEvent,
|
||||
undefined,
|
||||
never,
|
||||
never,
|
||||
@@ -39,22 +39,22 @@ type PrivateZoomActorAction<TEvent extends EventObject> = ActionFunction<
|
||||
never
|
||||
>;
|
||||
|
||||
export function createPrivateZoomActorActionSetup<
|
||||
export function createPrivateZoneActorActionSetup<
|
||||
TEvent extends EventObject,
|
||||
>() {
|
||||
const actorActionSetup = setup({
|
||||
types: {
|
||||
context: {} as PrivateZoomState,
|
||||
context: {} as PrivateZoneState,
|
||||
events: {} as TEvent,
|
||||
},
|
||||
});
|
||||
return {
|
||||
assign(
|
||||
assignment: Parameters<typeof actorActionSetup.assign>[0],
|
||||
): PrivateZoomActorAction<TEvent> {
|
||||
): PrivateZoneActorAction<TEvent> {
|
||||
return actorActionSetup.assign(
|
||||
assignment,
|
||||
) as unknown as PrivateZoomActorAction<TEvent>;
|
||||
) as unknown as PrivateZoneActorAction<TEvent>;
|
||||
},
|
||||
};
|
||||
}
|
||||
+18
-18
@@ -1,6 +1,6 @@
|
||||
import type { DoneActorEvent, ErrorActorEvent } from "xstate";
|
||||
|
||||
import type { PrivateAlbumUnlockResponse } from "@/data/schemas/private-zoom";
|
||||
import type { PrivateAlbumUnlockResponse } from "@/data/schemas/private-zone";
|
||||
|
||||
import {
|
||||
getPendingAlbum,
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
} from "../helper/albums";
|
||||
import {
|
||||
toPaywallRequest,
|
||||
toPrivateZoomErrorMessage,
|
||||
toPrivateZoneErrorMessage,
|
||||
toUnlockErrorMessage,
|
||||
} from "../helper/unlock";
|
||||
import {
|
||||
@@ -18,10 +18,10 @@ import {
|
||||
idleState,
|
||||
loadingState,
|
||||
} from "./album-flow";
|
||||
import { createPrivateZoomActorActionSetup } from "./setup";
|
||||
import { createPrivateZoneActorActionSetup } from "./setup";
|
||||
|
||||
const requestUnlockAction = albumMachineSetup.assign(({ event }) =>
|
||||
event.type === "PrivateZoomUnlockRequested"
|
||||
event.type === "PrivateZoneUnlockRequested"
|
||||
? {
|
||||
pendingConfirmAlbumId: event.albumId,
|
||||
unlockErrorMessage: null,
|
||||
@@ -44,7 +44,7 @@ const beginUnlockAction = albumMachineSetup.assign(({ context }) => ({
|
||||
unlockPaywallRequest: null,
|
||||
}));
|
||||
|
||||
export const privateZoomMachineSetup = albumMachineSetup.extend({
|
||||
export const privateZoneMachineSetup = albumMachineSetup.extend({
|
||||
actions: {
|
||||
requestUnlock: requestUnlockAction,
|
||||
cancelUnlock: cancelUnlockAction,
|
||||
@@ -54,11 +54,11 @@ export const privateZoomMachineSetup = albumMachineSetup.extend({
|
||||
});
|
||||
|
||||
const unlockDoneSetup =
|
||||
createPrivateZoomActorActionSetup<
|
||||
createPrivateZoneActorActionSetup<
|
||||
DoneActorEvent<PrivateAlbumUnlockResponse>
|
||||
>();
|
||||
const unlockErrorSetup =
|
||||
createPrivateZoomActorActionSetup<ErrorActorEvent>();
|
||||
createPrivateZoneActorActionSetup<ErrorActorEvent>();
|
||||
|
||||
const applyUnlockSuccessAction = unlockDoneSetup.assign(
|
||||
({ context, event }) => ({
|
||||
@@ -111,26 +111,26 @@ const applyUnlockFailureAction = unlockDoneSetup.assign(
|
||||
const applyUnlockActorErrorAction = unlockErrorSetup.assign(({ event }) => ({
|
||||
pendingConfirmAlbumId: null,
|
||||
unlockingAlbumId: null,
|
||||
unlockErrorMessage: toPrivateZoomErrorMessage(event.error),
|
||||
unlockErrorMessage: toPrivateZoneErrorMessage(event.error),
|
||||
}));
|
||||
|
||||
const readyState = privateZoomMachineSetup.createStateConfig({
|
||||
const readyState = privateZoneMachineSetup.createStateConfig({
|
||||
on: {
|
||||
PrivateZoomInit: "loading",
|
||||
PrivateZoomRefresh: "loading",
|
||||
PrivateZoomUnlockRequested: { actions: "requestUnlock" },
|
||||
PrivateZoomUnlockCancelled: { actions: "cancelUnlock" },
|
||||
PrivateZoomUnlockConfirmed: [
|
||||
PrivateZoneInit: "loading",
|
||||
PrivateZoneRefresh: "loading",
|
||||
PrivateZoneUnlockRequested: { actions: "requestUnlock" },
|
||||
PrivateZoneUnlockCancelled: { actions: "cancelUnlock" },
|
||||
PrivateZoneUnlockConfirmed: [
|
||||
{
|
||||
guard: "hasPendingAlbum",
|
||||
target: "unlocking",
|
||||
},
|
||||
],
|
||||
PrivateZoomUnlockPaywallConsumed: { actions: "clearUnlockPaywall" },
|
||||
PrivateZoneUnlockPaywallConsumed: { actions: "clearUnlockPaywall" },
|
||||
},
|
||||
});
|
||||
|
||||
const unlockingState = privateZoomMachineSetup.createStateConfig({
|
||||
const unlockingState = privateZoneMachineSetup.createStateConfig({
|
||||
entry: "beginUnlock",
|
||||
invoke: {
|
||||
src: "unlockAlbum",
|
||||
@@ -175,8 +175,8 @@ const unlockingState = privateZoomMachineSetup.createStateConfig({
|
||||
},
|
||||
});
|
||||
|
||||
export const privateZoomRootStateConfig =
|
||||
privateZoomMachineSetup.createStateConfig({
|
||||
export const privateZoneRootStateConfig =
|
||||
privateZoneMachineSetup.createStateConfig({
|
||||
initial: "idle",
|
||||
states: {
|
||||
idle: idleState,
|
||||
+24
-24
@@ -4,14 +4,14 @@ import type { Dispatch, ReactNode } from "react";
|
||||
import { createActorContext, shallowEqual } from "@xstate/react";
|
||||
import type { SnapshotFrom } from "xstate";
|
||||
|
||||
import { privateZoomMachine } from "./private-zoom-machine";
|
||||
import { privateZoneMachine } from "./private-zone-machine";
|
||||
import type {
|
||||
PrivateZoomEvent,
|
||||
PrivateZoomState as MachineContext,
|
||||
} from "./private-zoom-machine";
|
||||
PrivateZoneEvent,
|
||||
PrivateZoneState as MachineContext,
|
||||
} from "./private-zone-machine";
|
||||
|
||||
/** Route UI reads this projection instead of the Private Zoom snapshot. */
|
||||
export interface PrivateZoomContextState {
|
||||
/** Route UI reads this projection instead of the Private Zone snapshot. */
|
||||
export interface PrivateZoneContextState {
|
||||
characterId: string;
|
||||
status: string;
|
||||
items: MachineContext["items"];
|
||||
@@ -26,45 +26,45 @@ export interface PrivateZoomContextState {
|
||||
isUnlocking: boolean;
|
||||
}
|
||||
|
||||
type PrivateZoomSnapshot = SnapshotFrom<typeof privateZoomMachine>;
|
||||
type PrivateZoomSelector<T> = (snapshot: PrivateZoomSnapshot) => T;
|
||||
type PrivateZoneSnapshot = SnapshotFrom<typeof privateZoneMachine>;
|
||||
type PrivateZoneSelector<T> = (snapshot: PrivateZoneSnapshot) => T;
|
||||
|
||||
const PrivateZoomActorContext = createActorContext(privateZoomMachine);
|
||||
const PrivateZoneActorContext = createActorContext(privateZoneMachine);
|
||||
|
||||
export interface PrivateZoomProviderProps {
|
||||
export interface PrivateZoneProviderProps {
|
||||
children: ReactNode;
|
||||
characterId: string;
|
||||
}
|
||||
|
||||
export function PrivateZoomProvider({
|
||||
export function PrivateZoneProvider({
|
||||
children,
|
||||
characterId,
|
||||
}: PrivateZoomProviderProps) {
|
||||
}: PrivateZoneProviderProps) {
|
||||
return (
|
||||
<PrivateZoomActorContext.Provider options={{ input: { characterId } }}>
|
||||
<PrivateZoneActorContext.Provider options={{ input: { characterId } }}>
|
||||
{children}
|
||||
</PrivateZoomActorContext.Provider>
|
||||
</PrivateZoneActorContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function usePrivateZoomState(): PrivateZoomContextState {
|
||||
return usePrivateZoomSelector(selectPrivateZoomState, shallowEqual);
|
||||
export function usePrivateZoneState(): PrivateZoneContextState {
|
||||
return usePrivateZoneSelector(selectPrivateZoneState, shallowEqual);
|
||||
}
|
||||
|
||||
export function usePrivateZoomDispatch(): Dispatch<PrivateZoomEvent> {
|
||||
return PrivateZoomActorContext.useActorRef().send;
|
||||
export function usePrivateZoneDispatch(): Dispatch<PrivateZoneEvent> {
|
||||
return PrivateZoneActorContext.useActorRef().send;
|
||||
}
|
||||
|
||||
export function usePrivateZoomSelector<T>(
|
||||
selector: PrivateZoomSelector<T>,
|
||||
export function usePrivateZoneSelector<T>(
|
||||
selector: PrivateZoneSelector<T>,
|
||||
compare?: (previous: T, next: T) => boolean,
|
||||
): T {
|
||||
return PrivateZoomActorContext.useSelector(selector, compare);
|
||||
return PrivateZoneActorContext.useSelector(selector, compare);
|
||||
}
|
||||
|
||||
function selectPrivateZoomState(
|
||||
state: PrivateZoomSnapshot,
|
||||
): PrivateZoomContextState {
|
||||
function selectPrivateZoneState(
|
||||
state: PrivateZoneSnapshot,
|
||||
): PrivateZoneContextState {
|
||||
return {
|
||||
characterId: state.context.characterId,
|
||||
status: String(state.value),
|
||||
@@ -0,0 +1,8 @@
|
||||
/** Public events for one character-scoped Private Zone actor. */
|
||||
export type PrivateZoneEvent =
|
||||
| { type: "PrivateZoneInit" }
|
||||
| { type: "PrivateZoneRefresh" }
|
||||
| { type: "PrivateZoneUnlockRequested"; albumId: string }
|
||||
| { type: "PrivateZoneUnlockConfirmed" }
|
||||
| { type: "PrivateZoneUnlockCancelled" }
|
||||
| { type: "PrivateZoneUnlockPaywallConsumed" };
|
||||
@@ -0,0 +1,18 @@
|
||||
import {
|
||||
privateZoneMachineSetup,
|
||||
privateZoneRootStateConfig,
|
||||
} from "./machine/unlock-flow";
|
||||
import { createInitialPrivateZoneState } from "./private-zone-state";
|
||||
|
||||
export type { PrivateZoneEvent } from "./private-zone-events";
|
||||
export type { PrivateZoneState } from "./private-zone-state";
|
||||
export { initialState } from "./private-zone-state";
|
||||
|
||||
export const privateZoneMachine = privateZoneMachineSetup.createMachine({
|
||||
id: "privateZone",
|
||||
context: ({ input }) =>
|
||||
createInitialPrivateZoneState(input.characterId),
|
||||
...privateZoneRootStateConfig,
|
||||
});
|
||||
|
||||
export type PrivateZoneMachine = typeof privateZoneMachine;
|
||||
+7
-7
@@ -1,7 +1,7 @@
|
||||
import { DEFAULT_CHARACTER_ID } from "@/data/constants/character";
|
||||
import type { PrivateAlbum } from "@/data/schemas/private-zoom";
|
||||
import type { PrivateAlbum } from "@/data/schemas/private-zone";
|
||||
|
||||
export interface PrivateZoomUnlockPaywallRequest {
|
||||
export interface PrivateZoneUnlockPaywallRequest {
|
||||
albumId: string;
|
||||
reason: string;
|
||||
requiredCredits: number;
|
||||
@@ -9,7 +9,7 @@ export interface PrivateZoomUnlockPaywallRequest {
|
||||
shortfallCredits: number;
|
||||
}
|
||||
|
||||
export interface PrivateZoomState {
|
||||
export interface PrivateZoneState {
|
||||
characterId: string;
|
||||
items: readonly PrivateAlbum[];
|
||||
creditBalance: number;
|
||||
@@ -17,13 +17,13 @@ export interface PrivateZoomState {
|
||||
unlockingAlbumId: string | null;
|
||||
unlockErrorMessage: string | null;
|
||||
pendingConfirmAlbumId: string | null;
|
||||
unlockPaywallRequest: PrivateZoomUnlockPaywallRequest | null;
|
||||
unlockPaywallRequest: PrivateZoneUnlockPaywallRequest | null;
|
||||
unlockSuccessNonce: number;
|
||||
}
|
||||
|
||||
export function createInitialPrivateZoomState(
|
||||
export function createInitialPrivateZoneState(
|
||||
characterId: string,
|
||||
): PrivateZoomState {
|
||||
): PrivateZoneState {
|
||||
return {
|
||||
characterId,
|
||||
items: [],
|
||||
@@ -37,4 +37,4 @@ export function createInitialPrivateZoomState(
|
||||
};
|
||||
}
|
||||
|
||||
export const initialState = createInitialPrivateZoomState(DEFAULT_CHARACTER_ID);
|
||||
export const initialState = createInitialPrivateZoneState(DEFAULT_CHARACTER_ID);
|
||||
@@ -1,7 +0,0 @@
|
||||
/** @file Private Zoom store barrel. */
|
||||
|
||||
export * from "./private-zoom-context";
|
||||
export * from "./private-zoom-events";
|
||||
export * from "./helper";
|
||||
export * from "./private-zoom-machine";
|
||||
export * from "./private-zoom-state";
|
||||
@@ -1,8 +0,0 @@
|
||||
/** Public events for one character-scoped Private Zoom actor. */
|
||||
export type PrivateZoomEvent =
|
||||
| { type: "PrivateZoomInit" }
|
||||
| { type: "PrivateZoomRefresh" }
|
||||
| { type: "PrivateZoomUnlockRequested"; albumId: string }
|
||||
| { type: "PrivateZoomUnlockConfirmed" }
|
||||
| { type: "PrivateZoomUnlockCancelled" }
|
||||
| { type: "PrivateZoomUnlockPaywallConsumed" };
|
||||
@@ -1,18 +0,0 @@
|
||||
import {
|
||||
privateZoomMachineSetup,
|
||||
privateZoomRootStateConfig,
|
||||
} from "./machine/unlock-flow";
|
||||
import { createInitialPrivateZoomState } from "./private-zoom-state";
|
||||
|
||||
export type { PrivateZoomEvent } from "./private-zoom-events";
|
||||
export type { PrivateZoomState } from "./private-zoom-state";
|
||||
export { initialState } from "./private-zoom-state";
|
||||
|
||||
export const privateZoomMachine = privateZoomMachineSetup.createMachine({
|
||||
id: "privateZoom",
|
||||
context: ({ input }) =>
|
||||
createInitialPrivateZoomState(input.characterId),
|
||||
...privateZoomRootStateConfig,
|
||||
});
|
||||
|
||||
export type PrivateZoomMachine = typeof privateZoomMachine;
|
||||
Reference in New Issue
Block a user