152 lines
3.8 KiB
TypeScript
152 lines
3.8 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
import { shallowEqual } from "@xstate/react";
|
|
|
|
import type { LoginStatus } from "@/data/dto/auth";
|
|
import { useAppNavigator } from "@/router/use-app-navigator";
|
|
import { usePaymentDispatch, usePaymentSelector } from "@/stores/payment";
|
|
import { useUserSelector } from "@/stores/user/user-context";
|
|
import { getDefaultPayChannelForCountryCode } from "@/lib/payment/default_pay_channel";
|
|
import {
|
|
getFirstRechargeOfferBannerDismissed,
|
|
recordFirstRechargeOfferBannerDismissed,
|
|
} from "@/lib/chat/first_recharge_offer_banner";
|
|
|
|
export interface FirstRechargeOfferBannerEligibility {
|
|
historyLoaded: boolean;
|
|
loginStatus: LoginStatus;
|
|
isFirstRecharge: boolean;
|
|
dismissed: boolean;
|
|
}
|
|
|
|
export interface UseFirstRechargeOfferBannerInput {
|
|
historyLoaded: boolean;
|
|
loginStatus: LoginStatus;
|
|
}
|
|
|
|
export interface UseFirstRechargeOfferBannerOutput {
|
|
visible: boolean;
|
|
discountPercent: number;
|
|
close: () => void;
|
|
claim: () => void;
|
|
}
|
|
|
|
interface DismissalState {
|
|
userId: string | null;
|
|
loaded: boolean;
|
|
dismissed: boolean;
|
|
}
|
|
|
|
const FIRST_RECHARGE_DEFAULT_DISCOUNT_PERCENT = 50;
|
|
|
|
export function shouldShowFirstRechargeOfferBanner(
|
|
input: FirstRechargeOfferBannerEligibility,
|
|
): boolean {
|
|
if (!input.historyLoaded) return false;
|
|
if (input.loginStatus === "notLoggedIn" || input.loginStatus === "guest") {
|
|
return false;
|
|
}
|
|
if (!input.isFirstRecharge) return false;
|
|
return !input.dismissed;
|
|
}
|
|
|
|
export function useFirstRechargeOfferBanner({
|
|
historyLoaded,
|
|
loginStatus,
|
|
}: UseFirstRechargeOfferBannerInput): UseFirstRechargeOfferBannerOutput {
|
|
const navigator = useAppNavigator();
|
|
const payment = usePaymentSelector(
|
|
(state) => ({
|
|
isFirstRecharge: state.context.isFirstRecharge,
|
|
status: String(state.value),
|
|
}),
|
|
shallowEqual,
|
|
);
|
|
const paymentDispatch = usePaymentDispatch();
|
|
const user = useUserSelector(
|
|
(state) => ({
|
|
countryCode: state.context.currentUser?.countryCode,
|
|
id: state.context.currentUser?.id ?? null,
|
|
}),
|
|
shallowEqual,
|
|
);
|
|
const isAuthenticatedUser =
|
|
loginStatus !== "notLoggedIn" && loginStatus !== "guest";
|
|
const userId = user.id;
|
|
const hasUserIdentity = !isAuthenticatedUser || Boolean(userId);
|
|
const [dismissalState, setDismissalState] = useState<DismissalState>(() => ({
|
|
userId,
|
|
loaded: false,
|
|
dismissed: false,
|
|
}));
|
|
const discountPercent = FIRST_RECHARGE_DEFAULT_DISCOUNT_PERCENT;
|
|
|
|
useEffect(() => {
|
|
if (!isAuthenticatedUser) return;
|
|
if (payment.status !== "idle") return;
|
|
const defaultPayChannel = getDefaultPayChannelForCountryCode(
|
|
user.countryCode,
|
|
);
|
|
paymentDispatch({ type: "PaymentInit", payChannel: defaultPayChannel });
|
|
}, [
|
|
payment.status,
|
|
paymentDispatch,
|
|
isAuthenticatedUser,
|
|
user.countryCode,
|
|
]);
|
|
|
|
useEffect(() => {
|
|
let cancelled = false;
|
|
|
|
const loadDismissal = async () => {
|
|
const dismissed = await getFirstRechargeOfferBannerDismissed(userId);
|
|
if (cancelled) return;
|
|
|
|
setDismissalState({
|
|
userId,
|
|
loaded: true,
|
|
dismissed,
|
|
});
|
|
};
|
|
|
|
void loadDismissal();
|
|
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
}, [userId]);
|
|
|
|
const dismissalLoaded =
|
|
dismissalState.userId === userId && dismissalState.loaded;
|
|
const visible =
|
|
hasUserIdentity &&
|
|
dismissalLoaded &&
|
|
shouldShowFirstRechargeOfferBanner({
|
|
historyLoaded,
|
|
loginStatus,
|
|
isFirstRecharge: payment.isFirstRecharge,
|
|
dismissed: dismissalState.dismissed,
|
|
});
|
|
|
|
function close(): void {
|
|
setDismissalState({
|
|
userId,
|
|
loaded: true,
|
|
dismissed: true,
|
|
});
|
|
void recordFirstRechargeOfferBannerDismissed(userId);
|
|
}
|
|
|
|
function claim(): void {
|
|
navigator.openSubscription({ type: "vip", returnTo: "chat" });
|
|
}
|
|
|
|
return {
|
|
visible,
|
|
discountPercent,
|
|
close,
|
|
claim,
|
|
};
|
|
}
|