Files
cozsweet-frontend-nextjs/src/app/sidebar/sidebar-screen.tsx
T

173 lines
5.6 KiB
TypeScript

"use client";
import { useEffect, useState } from "react";
import { Download, LogOut } from "lucide-react";
import { signOut } from "next-auth/react";
import { BackButton } from "@/app/_components";
import { MobileShell } from "@/app/_components/core";
import { ROUTES } from "@/router/routes";
import { useAppNavigator } from "@/router/use-app-navigator";
import { useAuthDispatch, useAuthState } from "@/stores/auth/auth-context";
import { useUserDispatch, useUserState } from "@/stores/user/user-context";
import { pwaUtil } from "@/utils";
import {
SidebarWalletCard,
UserHeader,
type SidebarUserState,
} from "./components";
import styles from "./components/sidebar-screen.module.css";
const FALLBACK_USERNAME = "User name";
export function SidebarScreen() {
const navigator = useAppNavigator();
const user = useUserState();
const userDispatch = useUserDispatch();
const auth = useAuthState();
const authDispatch = useAuthDispatch();
const [canShowInstallApp, setCanShowInstallApp] = useState(false);
useEffect(() => {
if (!auth.hasInitialized || auth.isLoading) return;
if (auth.loginStatus === "notLoggedIn") return;
userDispatch({ type: "UserFetch" });
}, [
auth.hasInitialized,
auth.isLoading,
auth.loginStatus,
userDispatch,
]);
useEffect(() => {
const updateInstallEntryVisibility = () => {
setCanShowInstallApp(pwaUtil.canShowInstallEntry());
};
pwaUtil.prepareInstallPrompt();
updateInstallEntryVisibility();
const unsubscribe = pwaUtil.subscribe(updateInstallEntryVisibility);
return () => {
unsubscribe();
};
}, []);
const handleInstallAppClick = async () => {
pwaUtil.prepareInstallPrompt();
const result = await pwaUtil.install();
setCanShowInstallApp(
result !== "accepted" &&
!pwaUtil.isInstalled() &&
pwaUtil.canShowInstallEntry(),
);
};
const handleLogoutClick = () => {
userDispatch({ type: "UserClearLocal" });
authDispatch({ type: "AuthLogoutSubmitted" });
void signOut({ redirect: false });
navigator.openChat({ replace: true });
};
// 状态派生:未登录 / 已登录未 VIP / 已登录 VIP
const sidebarState: SidebarUserState =
auth.loginStatus === "notLoggedIn"
? "guest"
: user.currentUser?.isVip === true
? "vip"
: "member";
const isInitializingUser =
sidebarState !== "guest" && user.currentUser == null;
const name = user.currentUser?.username ?? FALLBACK_USERNAME;
const avatarUrl = user.avatarUrl ?? user.currentUser?.avatarUrl ?? null;
return (
<MobileShell background="#fcf3f4">
<div className={styles.shell}>
<div className={styles.bgOrbOne} aria-hidden="true" />
<div className={styles.bgOrbTwo} aria-hidden="true" />
<div className={styles.topBar}>
<BackButton href={ROUTES.chat} variant="soft" />
</div>
<section className={`${styles.userSlot} ${styles.revealOne}`}>
<UserHeader
state={sidebarState}
name={name}
avatarUrl={avatarUrl}
isLoading={isInitializingUser}
onLoginClick={() => navigator.openAuth(ROUTES.sidebar)}
/>
</section>
<section className={`${styles.cardSlot} ${styles.revealTwo}`}>
<SidebarWalletCard
creditBalance={user.creditBalance}
dailyFreeChatRemaining={user.currentUser?.dailyFreeChatRemaining}
dailyFreePrivateRemaining={
user.currentUser?.dailyFreePrivateRemaining
}
onActivateVip={
sidebarState === "vip"
? undefined
: () => navigator.openSubscription({ type: "vip" })
}
onTopUp={() => navigator.openSubscription({ type: "topup" })}
onRulesClick={() => navigator.push(ROUTES.coinsRules)}
/>
</section>
{sidebarState !== "guest" ? (
<section className={`${styles.settingsSlot} ${styles.revealThree}`}>
<p className={styles.settingsLabel}>Other</p>
<div className={styles.settingsActions}>
{canShowInstallApp ? (
<button
type="button"
className={`${styles.logoutCard} ${styles.installCard}`}
onClick={() => void handleInstallAppClick()}
>
<span
className={`${styles.logoutIcon} ${styles.installIcon}`}
aria-hidden="true"
>
<Download size={19} strokeWidth={2.2} />
</span>
<span className={styles.logoutText}>
<span className={styles.installTitle}>Install app</span>
<span className={styles.installSubtitle}>
Add CozSweet to your Home Screen
</span>
</span>
</button>
) : null}
<button
type="button"
className={styles.logoutCard}
onClick={handleLogoutClick}
>
<span className={styles.logoutIcon} aria-hidden="true">
<LogOut size={19} strokeWidth={2.2} />
</span>
<span className={styles.logoutText}>
<span className={styles.logoutTitle}>Log out</span>
<span className={styles.logoutSubtitle}>
End the current session safely
</span>
</span>
</button>
</div>
</section>
) : null}
</div>
</MobileShell>
);
}