173 lines
5.6 KiB
TypeScript
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>
|
|
);
|
|
}
|