feat(private-room): connect moments feed
Docker Image / Build and Push Docker Image (push) Successful in 9m29s

This commit is contained in:
2026-07-08 16:29:08 +08:00
parent 77b496da2b
commit c46b9b4cdd
41 changed files with 3107 additions and 281 deletions
@@ -0,0 +1,96 @@
"use client";
import {
type Dispatch,
type ReactNode,
createContext,
useContext,
useMemo,
} from "react";
import { useMachine } from "@xstate/react";
import { privateRoomMachine } from "./private-room-machine";
import type {
PrivateRoomEvent,
PrivateRoomState as MachineContext,
} from "./private-room-machine";
export interface PrivateRoomContextState {
status: string;
profile: MachineContext["profile"];
items: MachineContext["items"];
nextCursor: string | null;
hasMore: boolean;
creditBalance: number;
errorMessage: string | null;
unlockingMomentId: string | null;
unlockErrorMessage: string | null;
pendingConfirmMomentId: string | null;
unlockPaywallRequest: MachineContext["unlockPaywallRequest"];
unlockSuccessNonce: number;
isLoading: boolean;
isLoadingMore: boolean;
isUnlocking: boolean;
}
const PrivateRoomStateCtx = createContext<PrivateRoomContextState | null>(null);
const PrivateRoomDispatchCtx = createContext<Dispatch<PrivateRoomEvent> | null>(
null,
);
export interface PrivateRoomProviderProps {
children: ReactNode;
}
export function PrivateRoomProvider({ children }: PrivateRoomProviderProps) {
const [state, send] = useMachine(privateRoomMachine);
const privateRoomState = useMemo<PrivateRoomContextState>(
() => ({
status: String(state.value),
profile: state.context.profile,
items: state.context.items,
nextCursor: state.context.nextCursor,
hasMore: state.context.hasMore,
creditBalance: state.context.creditBalance,
errorMessage: state.context.errorMessage,
unlockingMomentId: state.context.unlockingMomentId,
unlockErrorMessage: state.context.unlockErrorMessage,
pendingConfirmMomentId: state.context.pendingConfirmMomentId,
unlockPaywallRequest: state.context.unlockPaywallRequest,
unlockSuccessNonce: state.context.unlockSuccessNonce,
isLoading: state.matches("loading"),
isLoadingMore: state.matches("loadingMore"),
isUnlocking: state.matches("unlocking"),
}),
[state],
);
return (
<PrivateRoomStateCtx.Provider value={privateRoomState}>
<PrivateRoomDispatchCtx.Provider value={send}>
{children}
</PrivateRoomDispatchCtx.Provider>
</PrivateRoomStateCtx.Provider>
);
}
export function usePrivateRoomState(): PrivateRoomContextState {
const ctx = useContext(PrivateRoomStateCtx);
if (!ctx) {
throw new Error(
"usePrivateRoomState must be used inside <PrivateRoomProvider>",
);
}
return ctx;
}
export function usePrivateRoomDispatch(): Dispatch<PrivateRoomEvent> {
const ctx = useContext(PrivateRoomDispatchCtx);
if (!ctx) {
throw new Error(
"usePrivateRoomDispatch must be used inside <PrivateRoomProvider>",
);
}
return ctx;
}