refactor(app): tighten feature boundaries

This commit is contained in:
2026-06-30 14:50:31 +08:00
parent 1fed21fa2f
commit ba6ce57e62
17 changed files with 126 additions and 52 deletions
-55
View File
@@ -1,55 +0,0 @@
"use client";
const STORAGE_KEY = "cozsweet.chat.pendingImageReturn";
const MAX_AGE_MS = 30 * 60 * 1000;
export interface PendingChatImageReturn {
reason: "image_paywall";
messageId: string;
returnUrl: string;
createdAt: number;
}
export function savePendingChatImageReturn(input: {
messageId: string;
returnUrl: string;
}): void {
if (typeof window === "undefined") return;
const payload: PendingChatImageReturn = {
reason: "image_paywall",
messageId: input.messageId,
returnUrl: input.returnUrl,
createdAt: Date.now(),
};
window.sessionStorage.setItem(STORAGE_KEY, JSON.stringify(payload));
}
export function consumePendingChatImageReturn(): PendingChatImageReturn | null {
if (typeof window === "undefined") return null;
const raw = window.sessionStorage.getItem(STORAGE_KEY);
window.sessionStorage.removeItem(STORAGE_KEY);
if (!raw) return null;
try {
const value = JSON.parse(raw) as Partial<PendingChatImageReturn>;
if (value.reason !== "image_paywall") return null;
if (typeof value.messageId !== "string" || value.messageId.length === 0) {
return null;
}
if (typeof value.returnUrl !== "string" || value.returnUrl.length === 0) {
return null;
}
if (typeof value.createdAt !== "number") return null;
if (Date.now() - value.createdAt > MAX_AGE_MS) return null;
return {
reason: value.reason,
messageId: value.messageId,
returnUrl: value.returnUrl,
createdAt: value.createdAt,
};
} catch {
return null;
}
}
-1
View File
@@ -25,6 +25,5 @@ export * from "./private-message-card";
export * from "./pwa-install-dialog";
export * from "./pwa-install-overlay";
export * from "./text-bubble";
export * from "./user-message-avatar";
export * from "./voice-bubble";
export * from "./voice-unlock-options-dialog";
+1 -1
View File
@@ -1,7 +1,7 @@
"use client";
import Image from "next/image";
import { UserMessageAvatar } from "./user-message-avatar";
import { UserMessageAvatar } from "@/app/_components";
import styles from "./message-avatar.module.css";
export interface MessageAvatarProps {
@@ -1,47 +0,0 @@
"use client";
import Image from "next/image";
import styles from "./message-avatar.module.css";
export interface UserMessageAvatarProps {
avatarUrl?: string | null;
className?: string;
size?: number;
}
export function UserMessageAvatar({
avatarUrl,
className,
size = 43,
}: UserMessageAvatarProps) {
const avatarClassName = [styles.avatar, className].filter(Boolean).join(" ");
const avatarStyle = { width: size, height: size };
if (avatarUrl && avatarUrl.length > 0) {
return (
<div
className={avatarClassName}
style={avatarStyle}
aria-label="User avatar"
>
<Image src={avatarUrl} alt="" width={size} height={size} />
</div>
);
}
return (
<div
className={avatarClassName}
style={avatarStyle}
aria-label="Guest avatar"
>
<Image
src="/images/chat/pic-chat-guest.png"
alt="Guest"
width={size}
height={size}
/>
</div>
);
}
@@ -5,16 +5,14 @@
*
* 设计:
* - 不走 Server `redirect()`:避免把 fbid 暴露在 URL 里。
* - deviceId / fbid 都走 `AuthStorage.getInstance().setXxx`(与 `token_interceptor.ts` 同源)
* - avatarUrl 写入 UserStorage 的头像 slot,外部浏览器首次恢复时可带给后端。
* - deviceId / fbid / avatarUrl 统一交给 `lib/chat` 落地。
* - 写入完成后 `router.replace('/chat')`URL 不留深链痕迹。
*/
import { useEffect, useState } from "react";
import { useRouter } from "next/navigation";
import { AuthStorage } from "@/data/storage/auth/auth_storage";
import { UserStorage } from "@/data/storage/user/user_storage";
import { persistExternalBrowserChatDeepLink } from "@/lib/chat/chat_external_browser";
import { ROUTES } from "@/router/routes";
import { Logger } from "@/utils";
@@ -37,13 +35,11 @@ export default function DeepLinkPersist({
useEffect(() => {
void (async () => {
try {
await AuthStorage.getInstance().setDeviceId(deviceId);
if (fbid && fbid.length > 0) {
await AuthStorage.getInstance().setFacebookId(fbid);
}
if (avatarUrl && avatarUrl.length > 0) {
await UserStorage.getInstance().setAvatarUrl(avatarUrl);
}
await persistExternalBrowserChatDeepLink({
deviceId,
facebookId: fbid,
avatarUrl,
});
} catch (err) {
// 写不进 localStorage 时(例如隐私模式)也不阻塞跳转;记录到 console 即可。
log.warn("[DeepLinkPersist] failed to persist", err);
@@ -6,9 +6,9 @@ import { MobileShell } from "@/app/_components/core";
import { ROUTE_BUILDERS, ROUTES } from "@/router/routes";
import { useAuthState } from "@/stores/auth/auth-context";
import { useChatDispatch, useChatState } from "@/stores/chat/chat-context";
import { savePendingChatImageReturn } from "@/lib/navigation/chat_image_return_session";
import { getChatPaywallNavigationUrl } from "../../chat-screen.helpers";
import { savePendingChatImageReturn } from "../../chat-image-return-session";
import { FullscreenImageViewer, HistoryUnlockDialog } from "../../components";
import styles from "./chat-image-viewer-screen.module.css";