refactor(router): centralize app route transitions
This commit is contained in:
+148
-75
@@ -1,8 +1,14 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
import { NavigationStorage } from "@/data/storage/navigation";
|
||||
import type { SubscriptionReturnTo } from "@/lib/navigation/subscription_exit";
|
||||
import {
|
||||
consumeSubscriptionExitUrl,
|
||||
peekSubscriptionExplicitExitUrl,
|
||||
} from "@/lib/navigation/subscription_exit";
|
||||
import { getDefaultPayChannelForCountryCode } from "@/lib/payment/default_pay_channel";
|
||||
import { useAuthState } from "@/stores/auth/auth-context";
|
||||
import { useUserState } from "@/stores/user/user-context";
|
||||
@@ -25,8 +31,13 @@ import type {
|
||||
export interface AppNavigator {
|
||||
push: (href: string, options?: { scroll?: boolean }) => void;
|
||||
replace: (href: string, options?: { scroll?: boolean }) => void;
|
||||
back: () => void;
|
||||
openAuth: (redirectTo?: string) => void;
|
||||
openChat: (options?: { replace?: boolean; scroll?: boolean }) => void;
|
||||
openChatImage: (messageId: string) => void;
|
||||
openSubscription: (input: OpenSubscriptionInput) => void;
|
||||
exitSubscription: (returnTo: SubscriptionReturnTo) => void;
|
||||
exitSubscriptionAfterSuccess: (returnTo: SubscriptionReturnTo) => void;
|
||||
startMessageUnlock: (input: StartMessageUnlockInput) => void;
|
||||
openSubscriptionForPendingUnlock: (
|
||||
input: OpenSubscriptionForPendingUnlockInput,
|
||||
@@ -41,100 +52,162 @@ export function useAppNavigator(): AppNavigator {
|
||||
const userState = useUserState();
|
||||
const isAuthenticatedUser = resolveIsAuthenticatedUser(authState.loginStatus);
|
||||
|
||||
function push(href: string, options?: { scroll?: boolean }): void {
|
||||
const push = useCallback((href: string, options?: { scroll?: boolean }): void => {
|
||||
router.push(href, options);
|
||||
}
|
||||
}, [router]);
|
||||
|
||||
function replace(href: string, options?: { scroll?: boolean }): void {
|
||||
const replace = useCallback((href: string, options?: { scroll?: boolean }): void => {
|
||||
router.replace(href, options);
|
||||
}
|
||||
}, [router]);
|
||||
|
||||
function openAuth(redirectTo: string = ROUTES.chat): void {
|
||||
const back = useCallback((): void => {
|
||||
router.back();
|
||||
}, [router]);
|
||||
|
||||
const openAuth = useCallback((redirectTo: string = ROUTES.chat): void => {
|
||||
router.push(ROUTE_BUILDERS.authWithRedirect(redirectTo));
|
||||
}
|
||||
}, [router]);
|
||||
|
||||
function getDefaultPayChannel() {
|
||||
const openChat = useCallback((options: { replace?: boolean; scroll?: boolean } = {}): void => {
|
||||
const navOptions = { scroll: options.scroll ?? false };
|
||||
if (options.replace) {
|
||||
router.replace(ROUTES.chat, navOptions);
|
||||
return;
|
||||
}
|
||||
router.push(ROUTES.chat, navOptions);
|
||||
}, [router]);
|
||||
|
||||
const openChatImage = useCallback((messageId: string): void => {
|
||||
router.push(ROUTE_BUILDERS.chatImage(messageId), { scroll: false });
|
||||
}, [router]);
|
||||
|
||||
const getDefaultPayChannel = useCallback(() => {
|
||||
return getDefaultPayChannelForCountryCode(userState.currentUser?.countryCode);
|
||||
}
|
||||
}, [userState.currentUser?.countryCode]);
|
||||
|
||||
function openSubscription({
|
||||
type,
|
||||
payChannel = getDefaultPayChannel(),
|
||||
returnTo = null,
|
||||
replace: shouldReplace = false,
|
||||
}: OpenSubscriptionInput): void {
|
||||
const target = ROUTE_BUILDERS.subscription(type, {
|
||||
payChannel,
|
||||
returnTo: returnTo ?? undefined,
|
||||
});
|
||||
|
||||
const nextUrl = resolveAuthenticatedNavigation({
|
||||
loginStatus: authState.loginStatus,
|
||||
targetUrl: target,
|
||||
});
|
||||
|
||||
if (shouldReplace) {
|
||||
router.replace(nextUrl);
|
||||
return;
|
||||
}
|
||||
router.push(nextUrl);
|
||||
}
|
||||
|
||||
function startMessageUnlock({
|
||||
messageId,
|
||||
kind,
|
||||
returnUrl,
|
||||
stage = "auth",
|
||||
onAuthenticated,
|
||||
}: StartMessageUnlockInput): void {
|
||||
if (isAuthenticatedUser) {
|
||||
onAuthenticated();
|
||||
return;
|
||||
}
|
||||
|
||||
void (async () => {
|
||||
await NavigationStorage.savePendingChatUnlock({
|
||||
messageId,
|
||||
kind,
|
||||
returnUrl,
|
||||
stage,
|
||||
});
|
||||
router.push(ROUTE_BUILDERS.authWithRedirect(returnUrl));
|
||||
})();
|
||||
}
|
||||
|
||||
function openSubscriptionForPendingUnlock({
|
||||
messageId,
|
||||
kind,
|
||||
returnUrl,
|
||||
type,
|
||||
payChannel = getDefaultPayChannel(),
|
||||
}: OpenSubscriptionForPendingUnlockInput): void {
|
||||
void (async () => {
|
||||
await NavigationStorage.savePendingChatUnlock({
|
||||
messageId,
|
||||
kind,
|
||||
returnUrl,
|
||||
stage: "payment",
|
||||
});
|
||||
openSubscription({
|
||||
type,
|
||||
const openSubscription = useCallback(
|
||||
({
|
||||
type,
|
||||
payChannel = getDefaultPayChannel(),
|
||||
returnTo = null,
|
||||
replace: shouldReplace = false,
|
||||
}: OpenSubscriptionInput): void => {
|
||||
const target = ROUTE_BUILDERS.subscription(type, {
|
||||
payChannel,
|
||||
returnTo: "chat",
|
||||
returnTo: returnTo ?? undefined,
|
||||
});
|
||||
})();
|
||||
}
|
||||
|
||||
return {
|
||||
const nextUrl = resolveAuthenticatedNavigation({
|
||||
loginStatus: authState.loginStatus,
|
||||
targetUrl: target,
|
||||
});
|
||||
|
||||
if (shouldReplace) {
|
||||
router.replace(nextUrl);
|
||||
return;
|
||||
}
|
||||
router.push(nextUrl);
|
||||
},
|
||||
[authState.loginStatus, getDefaultPayChannel, router],
|
||||
);
|
||||
|
||||
const startMessageUnlock = useCallback(
|
||||
({
|
||||
messageId,
|
||||
kind,
|
||||
returnUrl,
|
||||
stage = "auth",
|
||||
onAuthenticated,
|
||||
}: StartMessageUnlockInput): void => {
|
||||
if (isAuthenticatedUser) {
|
||||
onAuthenticated();
|
||||
return;
|
||||
}
|
||||
|
||||
void (async () => {
|
||||
await NavigationStorage.savePendingChatUnlock({
|
||||
messageId,
|
||||
kind,
|
||||
returnUrl,
|
||||
stage,
|
||||
});
|
||||
router.push(ROUTE_BUILDERS.authWithRedirect(returnUrl));
|
||||
})();
|
||||
},
|
||||
[isAuthenticatedUser, router],
|
||||
);
|
||||
|
||||
const openSubscriptionForPendingUnlock = useCallback(
|
||||
({
|
||||
messageId,
|
||||
kind,
|
||||
returnUrl,
|
||||
type,
|
||||
payChannel = getDefaultPayChannel(),
|
||||
}: OpenSubscriptionForPendingUnlockInput): void => {
|
||||
void (async () => {
|
||||
await NavigationStorage.savePendingChatUnlock({
|
||||
messageId,
|
||||
kind,
|
||||
returnUrl,
|
||||
stage: "payment",
|
||||
});
|
||||
openSubscription({
|
||||
type,
|
||||
payChannel,
|
||||
returnTo: "chat",
|
||||
});
|
||||
})();
|
||||
},
|
||||
[getDefaultPayChannel, openSubscription],
|
||||
);
|
||||
|
||||
const exitSubscription = useCallback((returnTo: SubscriptionReturnTo): void => {
|
||||
void (async () => {
|
||||
router.replace(await consumeSubscriptionExitUrl(returnTo));
|
||||
})();
|
||||
}, [router]);
|
||||
|
||||
const exitSubscriptionAfterSuccess = useCallback((returnTo: SubscriptionReturnTo): void => {
|
||||
void (async () => {
|
||||
const pendingExitUrl = await peekSubscriptionExplicitExitUrl();
|
||||
if (pendingExitUrl) {
|
||||
router.replace(pendingExitUrl);
|
||||
return;
|
||||
}
|
||||
router.replace(await consumeSubscriptionExitUrl(returnTo));
|
||||
})();
|
||||
}, [router]);
|
||||
|
||||
return useMemo(() => ({
|
||||
push,
|
||||
replace,
|
||||
back,
|
||||
openAuth,
|
||||
openChat,
|
||||
openChatImage,
|
||||
openSubscription,
|
||||
exitSubscription,
|
||||
exitSubscriptionAfterSuccess,
|
||||
startMessageUnlock,
|
||||
openSubscriptionForPendingUnlock,
|
||||
getDefaultPayChannel,
|
||||
isAuthenticatedUser,
|
||||
};
|
||||
}), [
|
||||
back,
|
||||
exitSubscription,
|
||||
exitSubscriptionAfterSuccess,
|
||||
getDefaultPayChannel,
|
||||
isAuthenticatedUser,
|
||||
openAuth,
|
||||
openChat,
|
||||
openChatImage,
|
||||
openSubscription,
|
||||
openSubscriptionForPendingUnlock,
|
||||
push,
|
||||
replace,
|
||||
startMessageUnlock,
|
||||
]);
|
||||
}
|
||||
|
||||
export type { Route };
|
||||
|
||||
Reference in New Issue
Block a user