feat(sidebar): show free chat allowance

This commit is contained in:
2026-07-03 16:34:38 +08:00
parent 8f17ea9c9f
commit 1fcad49baa
5 changed files with 88 additions and 16 deletions
+8 -4
View File
@@ -13,6 +13,10 @@ import { BackButton } from "@/app/_components";
import { MobileShell } from "@/app/_components/core"; import { MobileShell } from "@/app/_components/core";
import { ROUTES } from "@/router/routes"; import { ROUTES } from "@/router/routes";
import {
FREE_PRIVATE_MESSAGE_LABEL,
FREE_STANDARD_CHAT_LABEL,
} from "./coins-rules.constants";
import styles from "./coins-rules-screen.module.css"; import styles from "./coins-rules-screen.module.css";
interface CoinRuleItem { interface CoinRuleItem {
@@ -26,13 +30,13 @@ const COIN_RULES: readonly CoinRuleItem[] = [
{ {
title: "Standard Chat", title: "Standard Chat",
cost: "2 coins / message", cost: "2 coins / message",
free: "50 free messages weekly", free: FREE_STANDARD_CHAT_LABEL,
icon: MessageCircle, icon: MessageCircle,
}, },
{ {
title: "Private Chat", title: "Private Chat",
cost: "10 coins / message", cost: "10 coins / message",
free: "1 free message daily", free: FREE_PRIVATE_MESSAGE_LABEL,
icon: Sparkles, icon: Sparkles,
}, },
{ {
@@ -92,8 +96,8 @@ export function CoinsRulesScreen() {
<div> <div>
<h2 className={styles.freeTitle}>Free Allowance</h2> <h2 className={styles.freeTitle}>Free Allowance</h2>
<p className={styles.freeText}> <p className={styles.freeText}>
Standard chat includes 50 free messages weekly. Private chat Standard chat includes {FREE_STANDARD_CHAT_LABEL}. Private chat
includes 1 free message daily. includes {FREE_PRIVATE_MESSAGE_LABEL}.
</p> </p>
</div> </div>
</section> </section>
@@ -0,0 +1,5 @@
export const FREE_STANDARD_CHAT_DAILY_COUNT = 30;
export const FREE_PRIVATE_MESSAGE_DAILY_COUNT = 2;
export const FREE_STANDARD_CHAT_LABEL = `${FREE_STANDARD_CHAT_DAILY_COUNT} free messages daily`;
export const FREE_PRIVATE_MESSAGE_LABEL = `${FREE_PRIVATE_MESSAGE_DAILY_COUNT} free private messages daily`;
@@ -106,6 +106,53 @@
align-items: stretch; align-items: stretch;
} }
.freeAllowance {
display: flex;
grid-column: 1 / -1;
align-items: center;
justify-content: space-between;
gap: var(--spacing-md, 12px);
margin-top: clamp(2px, 0.741vw, 4px);
padding: clamp(10px, 2.222vw, 12px) clamp(12px, 2.963vw, 16px);
border: 1px solid rgba(246, 87, 160, 0.12);
border-radius: var(--responsive-card-radius-sm, 18px);
background: rgba(255, 255, 255, 0.72);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.78);
}
.freeAllowanceTitle {
margin: 0;
color: #6d5d64;
font-size: var(--responsive-caption, 13px);
font-weight: 820;
line-height: 1.2;
white-space: nowrap;
}
.freeAllowanceItems {
display: flex;
flex-wrap: wrap;
justify-content: flex-end;
gap: clamp(8px, 1.852vw, 10px);
}
.freeAllowanceItem {
display: inline-flex;
align-items: baseline;
gap: 3px;
color: #51464b;
font-size: var(--responsive-caption, 13px);
font-weight: 720;
line-height: 1.2;
white-space: nowrap;
}
.freeAllowanceItem strong {
color: #f657a0;
font-size: var(--responsive-body, 16px);
font-weight: 900;
}
.activateButton, .activateButton,
.topUpButton { .topUpButton {
display: inline-flex; display: inline-flex;
@@ -186,4 +233,13 @@
.rulesButton { .rulesButton {
font-size: var(--responsive-body, 14px); font-size: var(--responsive-body, 14px);
} }
.freeAllowance {
align-items: flex-start;
flex-direction: column;
}
.freeAllowanceItems {
justify-content: flex-start;
}
} }
@@ -2,6 +2,11 @@
import { FaCoins } from "react-icons/fa6"; import { FaCoins } from "react-icons/fa6";
import {
FREE_PRIVATE_MESSAGE_DAILY_COUNT,
FREE_STANDARD_CHAT_DAILY_COUNT,
} from "@/app/coins-rules/coins-rules.constants";
import styles from "./sidebar-wallet-card.module.css"; import styles from "./sidebar-wallet-card.module.css";
export interface SidebarWalletCardProps { export interface SidebarWalletCardProps {
@@ -58,6 +63,20 @@ export function SidebarWalletCard({
Top Up Top Up
</button> </button>
</div> </div>
<div className={styles.freeAllowance} aria-label="Free allowance">
<p className={styles.freeAllowanceTitle}>Free Allowance</p>
<div className={styles.freeAllowanceItems}>
<span className={styles.freeAllowanceItem}>
<strong>{FREE_STANDARD_CHAT_DAILY_COUNT}</strong>
<span> chats / day</span>
</span>
<span className={styles.freeAllowanceItem}>
<strong>{FREE_PRIVATE_MESSAGE_DAILY_COUNT}</strong>
<span> private / day</span>
</span>
</div>
</div>
</section> </section>
); );
} }
-12
View File
@@ -4,7 +4,6 @@ import { useEffect } from "react";
import { MobileShell } from "@/app/_components/core"; import { MobileShell } from "@/app/_components/core";
import { useAppNavigator } from "@/router/use-app-navigator"; import { useAppNavigator } from "@/router/use-app-navigator";
import { useAuthState } from "@/stores/auth/auth-context";
import { pwaUtil } from "@/utils"; import { pwaUtil } from "@/utils";
import { import {
@@ -15,8 +14,6 @@ import {
} from "./components"; } from "./components";
import styles from "./components/splash-screen.module.css"; import styles from "./components/splash-screen.module.css";
const AUTO_OPEN_CHAT_DELAY_MS = 1800;
// 尽量早地缓存 beforeinstallprompt,避免用户停留 splash 时浏览器事件已触发并丢失。 // 尽量早地缓存 beforeinstallprompt,避免用户停留 splash 时浏览器事件已触发并丢失。
if (typeof window !== "undefined") { if (typeof window !== "undefined") {
pwaUtil.prepareInstallPrompt(); pwaUtil.prepareInstallPrompt();
@@ -24,7 +21,6 @@ if (typeof window !== "undefined") {
export function SplashScreen() { export function SplashScreen() {
const navigator = useAppNavigator(); const navigator = useAppNavigator();
const state = useAuthState();
const handleStartChat = () => { const handleStartChat = () => {
navigator.openChat({ replace: true }); navigator.openChat({ replace: true });
@@ -34,14 +30,6 @@ export function SplashScreen() {
pwaUtil.prepareInstallPrompt(); pwaUtil.prepareInstallPrompt();
}, []); }, []);
useEffect(() => {
const timer = window.setTimeout(() => {
navigator.openChat({ replace: true });
}, AUTO_OPEN_CHAT_DELAY_MS);
return () => window.clearTimeout(timer);
}, [navigator]);
return ( return (
<MobileShell background="var(--color-sidebar-background)"> <MobileShell background="var(--color-sidebar-background)">
<div className={styles.wrapper}> <div className={styles.wrapper}>