feat(auth): 独立登录会话开关(设置-外观)—— 同浏览器多账号互不覆盖
复用 iframe 嵌入的按标签页隔离机制:新增 ?isolate=1 标志,authStorage() 从「仅嵌入」放宽为 isEmbedActive() || isIsolatedSession(),独立态走 sessionStorage 故本标签页登录态不被其它标签页覆盖。设置→外观→「登录会话」 加 Switch,开启时先 migrateAuthToSession() 把 localStorage 受保护键快照搬进 sessionStorage 再翻标志,无感不登出;AuthSyncWatcher 跨标签监听对隔离态实时 忽略,运行时开关即刻生效。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
a9f01bec79
commit
a85d2480b4
5 files changed
+146
-15
No files matched your search
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { DropletIcon, LayoutGridIcon, LayoutIcon, ListIcon, MoonIcon, PanelLeftIcon, SunIcon } from "lucide-react";
|
||||
import { useMemo, type ComponentType, type SVGProps } from "react";
|
||||
import { useMemo, useState, type ComponentType, type SVGProps } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { toast } from "sonner";
|
||||
|
||||
@@ -20,6 +20,8 @@ import { Separator } from "@/components/ui/separator";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { getStoredAuth } from "@/core/auth";
|
||||
import { migrateAuthToSession } from "@/core/auth/scoped-storage";
|
||||
import { isIsolatedSession, setIsolatedSession } from "@/core/embed/embed-session";
|
||||
import { enUS, isLocale, zhCN, type Locale } from "@/core/i18n";
|
||||
import { useI18n } from "@/core/i18n/hooks";
|
||||
import { useLocalSettings } from "@/core/settings";
|
||||
@@ -64,6 +66,29 @@ export function AppearanceSettingsPage({ onClose }: { onClose?: () => void }) {
|
||||
const cleanupWords =
|
||||
citationDisplay?.cleanup_words ?? settings.display.citationCleanupWords ?? [];
|
||||
|
||||
// 独立登录会话(本标签页):开关切换鉴权存储 localStorage ↔ sessionStorage(见 scoped-storage)。
|
||||
const [isolatedSession, setIsolatedSessionState] = useState(() => isIsolatedSession());
|
||||
const handleToggleIsolatedSession = (checked: boolean) => {
|
||||
if (checked) {
|
||||
// 先把当前账号从 localStorage 快照搬进 sessionStorage,再翻标志,避免切换存储后被登出。
|
||||
migrateAuthToSession();
|
||||
setIsolatedSession(true);
|
||||
setIsolatedSessionState(true);
|
||||
toast.success("已开启独立登录会话", {
|
||||
description:
|
||||
"本标签页的登录已与其它标签页隔离:在别的窗口登录或切换其它账号都不会影响这里,你也可以在此单独登录另一个账号。该独立会话只存在于当前标签页,关闭此标签后即结束(需重新登录)。",
|
||||
duration: 9000,
|
||||
});
|
||||
} else {
|
||||
setIsolatedSession(false);
|
||||
setIsolatedSessionState(false);
|
||||
toast.info("已关闭独立登录会话", {
|
||||
description: "本标签页恢复与其它标签页共享同一登录。如页面显示未及时同步,请刷新本页。",
|
||||
duration: 6000,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleSetGlobalDefault = async (
|
||||
value: "light" | "dark" | "lightblue",
|
||||
) => {
|
||||
@@ -395,6 +420,28 @@ export function AppearanceSettingsPage({ onClose }: { onClose?: () => void }) {
|
||||
|
||||
<Separator />
|
||||
|
||||
<SettingsSection
|
||||
title="登录会话"
|
||||
description="控制本标签页的登录身份是否与浏览器其它标签页隔离。"
|
||||
>
|
||||
<div className="flex items-start justify-between gap-4 rounded-lg border p-4">
|
||||
<div>
|
||||
<div className="text-sm font-medium">独立登录会话(本标签页)</div>
|
||||
<p className="text-muted-foreground mt-1 max-w-md text-xs leading-relaxed">
|
||||
开启后,本标签页的登录状态仅保存在当前标签页:在别的窗口登录或切换其它账号都不会影响这里,
|
||||
你也可以在此单独登录另一个账号。关闭浏览器标签后该独立会话即结束(需重新登录)。
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={isolatedSession}
|
||||
onCheckedChange={handleToggleIsolatedSession}
|
||||
aria-label="独立登录会话"
|
||||
/>
|
||||
</div>
|
||||
</SettingsSection>
|
||||
|
||||
<Separator />
|
||||
|
||||
<SettingsSection
|
||||
title="对话体验"
|
||||
description="问答过程中的辅助显示。"
|
||||
|
||||
Reference in new issue
Block a user