refactor(app): split page orchestration flows
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { isPrivateRoomAuthRequired } from "../use-private-room-flow";
|
||||
|
||||
describe("isPrivateRoomAuthRequired", () => {
|
||||
it.each(["guest", "notLoggedIn"] as const)(
|
||||
"requires auth for %s users",
|
||||
(loginStatus) => {
|
||||
expect(isPrivateRoomAuthRequired(loginStatus)).toBe(true);
|
||||
},
|
||||
);
|
||||
|
||||
it.each(["email", "facebook", "google", "apple"] as const)(
|
||||
"allows %s users to top up directly",
|
||||
(loginStatus) => {
|
||||
expect(isPrivateRoomAuthRequired(loginStatus)).toBe(false);
|
||||
},
|
||||
);
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import type { CSSProperties } from "react";
|
||||
import { useEffect, useMemo, useRef } from "react";
|
||||
import { useMemo } from "react";
|
||||
import Image from "next/image";
|
||||
import {
|
||||
ImageIcon,
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
|
||||
import type { PrivateRoomMoment } from "@/data/dto/private-room";
|
||||
import { AppBottomNav, MobileShell } from "@/app/_components/core";
|
||||
import { useGuestLoginBootstrap } from "@/hooks/use-guest-login-bootstrap";
|
||||
import { ROUTES } from "@/router/routes";
|
||||
import { useAppNavigator } from "@/router/use-app-navigator";
|
||||
import { useAuthDispatch, useAuthState } from "@/stores/auth/auth-context";
|
||||
@@ -20,9 +19,14 @@ import {
|
||||
usePrivateRoomDispatch,
|
||||
usePrivateRoomState,
|
||||
} from "@/stores/private-room";
|
||||
import { useUserDispatch } from "@/stores/user/user-context";
|
||||
|
||||
import styles from "./private-room-screen.module.css";
|
||||
import {
|
||||
isPrivateRoomAuthRequired,
|
||||
usePrivateRoomBootstrapFlow,
|
||||
usePrivateRoomUnlockPaywallNavigation,
|
||||
usePrivateRoomUnlockSuccessRefresh,
|
||||
} from "./use-private-room-flow";
|
||||
|
||||
const FALLBACK_PROFILE = {
|
||||
name: "Elio Silvestri",
|
||||
@@ -37,70 +41,23 @@ export function PrivateRoomScreen() {
|
||||
const navigator = useAppNavigator();
|
||||
const authState = useAuthState();
|
||||
const authDispatch = useAuthDispatch();
|
||||
const userDispatch = useUserDispatch();
|
||||
const state = usePrivateRoomState();
|
||||
const dispatch = usePrivateRoomDispatch();
|
||||
const previousLoginStatusRef = useRef(authState.loginStatus);
|
||||
const lastUnlockSuccessNonceRef = useRef(0);
|
||||
|
||||
useGuestLoginBootstrap({
|
||||
dispatch: authDispatch,
|
||||
usePrivateRoomBootstrapFlow({
|
||||
authDispatch,
|
||||
hasInitialized: authState.hasInitialized,
|
||||
isLoading: authState.isLoading,
|
||||
isAuthLoading: authState.isLoading,
|
||||
loginStatus: authState.loginStatus,
|
||||
roomDispatch: dispatch,
|
||||
roomStatus: state.status,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!authState.hasInitialized || authState.isLoading) return;
|
||||
if (authState.loginStatus === "notLoggedIn") return;
|
||||
|
||||
const previousLoginStatus = previousLoginStatusRef.current;
|
||||
previousLoginStatusRef.current = authState.loginStatus;
|
||||
|
||||
if (state.status === "idle") {
|
||||
dispatch({ type: "PrivateRoomInit" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
previousLoginStatus !== authState.loginStatus &&
|
||||
state.status !== "loading"
|
||||
) {
|
||||
dispatch({ type: "PrivateRoomRefresh" });
|
||||
}
|
||||
}, [
|
||||
authState.hasInitialized,
|
||||
authState.isLoading,
|
||||
authState.loginStatus,
|
||||
dispatch,
|
||||
state.status,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
const request = state.unlockPaywallRequest;
|
||||
if (!request) return;
|
||||
|
||||
if (
|
||||
authState.loginStatus === "guest" ||
|
||||
authState.loginStatus === "notLoggedIn"
|
||||
) {
|
||||
navigator.openAuth(ROUTES.privateRoom);
|
||||
} else {
|
||||
navigator.openSubscription({ type: "topup" });
|
||||
}
|
||||
dispatch({ type: "PrivateRoomUnlockPaywallConsumed" });
|
||||
}, [
|
||||
authState.loginStatus,
|
||||
dispatch,
|
||||
navigator,
|
||||
state.unlockPaywallRequest,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (state.unlockSuccessNonce <= lastUnlockSuccessNonceRef.current) return;
|
||||
lastUnlockSuccessNonceRef.current = state.unlockSuccessNonce;
|
||||
userDispatch({ type: "UserFetch" });
|
||||
}, [state.unlockSuccessNonce, userDispatch]);
|
||||
usePrivateRoomUnlockPaywallNavigation({
|
||||
loginStatus: authState.loginStatus,
|
||||
roomDispatch: dispatch,
|
||||
unlockPaywallRequest: state.unlockPaywallRequest,
|
||||
});
|
||||
usePrivateRoomUnlockSuccessRefresh(state.unlockSuccessNonce);
|
||||
|
||||
const profile = state.profile;
|
||||
const displayName =
|
||||
@@ -117,10 +74,7 @@ export function PrivateRoomScreen() {
|
||||
);
|
||||
|
||||
const handleTopUpClick = () => {
|
||||
if (
|
||||
authState.loginStatus === "guest" ||
|
||||
authState.loginStatus === "notLoggedIn"
|
||||
) {
|
||||
if (isPrivateRoomAuthRequired(authState.loginStatus)) {
|
||||
navigator.openAuth(ROUTES.privateRoom);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
"use client";
|
||||
|
||||
import { type Dispatch, useEffect, useRef } from "react";
|
||||
|
||||
import type { LoginStatus } from "@/data/dto/auth";
|
||||
import { useGuestLoginBootstrap } from "@/hooks/use-guest-login-bootstrap";
|
||||
import { ROUTES } from "@/router/routes";
|
||||
import { useAppNavigator } from "@/router/use-app-navigator";
|
||||
import type { AuthEvent } from "@/stores/auth/auth-events";
|
||||
import type { PrivateRoomEvent } from "@/stores/private-room";
|
||||
import type { PrivateRoomUnlockPaywallRequest } from "@/stores/private-room/private-room-state";
|
||||
import { useUserDispatch } from "@/stores/user/user-context";
|
||||
|
||||
export interface UsePrivateRoomBootstrapFlowInput {
|
||||
authDispatch: Dispatch<AuthEvent>;
|
||||
hasInitialized: boolean;
|
||||
isAuthLoading: boolean;
|
||||
loginStatus: LoginStatus;
|
||||
roomDispatch: Dispatch<PrivateRoomEvent>;
|
||||
roomStatus: string;
|
||||
}
|
||||
|
||||
export interface UsePrivateRoomUnlockPaywallNavigationInput {
|
||||
loginStatus: LoginStatus;
|
||||
roomDispatch: Dispatch<PrivateRoomEvent>;
|
||||
unlockPaywallRequest: PrivateRoomUnlockPaywallRequest | null;
|
||||
}
|
||||
|
||||
export function isPrivateRoomAuthRequired(loginStatus: LoginStatus): boolean {
|
||||
return loginStatus === "guest" || loginStatus === "notLoggedIn";
|
||||
}
|
||||
|
||||
export function usePrivateRoomBootstrapFlow({
|
||||
authDispatch,
|
||||
hasInitialized,
|
||||
isAuthLoading,
|
||||
loginStatus,
|
||||
roomDispatch,
|
||||
roomStatus,
|
||||
}: UsePrivateRoomBootstrapFlowInput): void {
|
||||
const previousLoginStatusRef = useRef(loginStatus);
|
||||
|
||||
useGuestLoginBootstrap({
|
||||
dispatch: authDispatch,
|
||||
hasInitialized,
|
||||
isLoading: isAuthLoading,
|
||||
loginStatus,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!hasInitialized || isAuthLoading) return;
|
||||
if (loginStatus === "notLoggedIn") return;
|
||||
|
||||
const previousLoginStatus = previousLoginStatusRef.current;
|
||||
previousLoginStatusRef.current = loginStatus;
|
||||
|
||||
if (roomStatus === "idle") {
|
||||
roomDispatch({ type: "PrivateRoomInit" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (previousLoginStatus !== loginStatus && roomStatus !== "loading") {
|
||||
roomDispatch({ type: "PrivateRoomRefresh" });
|
||||
}
|
||||
}, [
|
||||
hasInitialized,
|
||||
isAuthLoading,
|
||||
loginStatus,
|
||||
roomDispatch,
|
||||
roomStatus,
|
||||
]);
|
||||
}
|
||||
|
||||
export function usePrivateRoomUnlockPaywallNavigation({
|
||||
loginStatus,
|
||||
roomDispatch,
|
||||
unlockPaywallRequest,
|
||||
}: UsePrivateRoomUnlockPaywallNavigationInput): void {
|
||||
const navigator = useAppNavigator();
|
||||
|
||||
useEffect(() => {
|
||||
if (!unlockPaywallRequest) return;
|
||||
|
||||
if (isPrivateRoomAuthRequired(loginStatus)) {
|
||||
navigator.openAuth(ROUTES.privateRoom);
|
||||
} else {
|
||||
navigator.openSubscription({ type: "topup" });
|
||||
}
|
||||
roomDispatch({ type: "PrivateRoomUnlockPaywallConsumed" });
|
||||
}, [loginStatus, navigator, roomDispatch, unlockPaywallRequest]);
|
||||
}
|
||||
|
||||
export function usePrivateRoomUnlockSuccessRefresh(
|
||||
unlockSuccessNonce: number,
|
||||
): void {
|
||||
const userDispatch = useUserDispatch();
|
||||
const lastUnlockSuccessNonceRef = useRef(0);
|
||||
|
||||
useEffect(() => {
|
||||
if (unlockSuccessNonce <= lastUnlockSuccessNonceRef.current) return;
|
||||
lastUnlockSuccessNonceRef.current = unlockSuccessNonce;
|
||||
userDispatch({ type: "UserFetch" });
|
||||
}, [unlockSuccessNonce, userDispatch]);
|
||||
}
|
||||
Reference in New Issue
Block a user