fix(browser): 导入彻底无感——分区轮换替代文件热替换,搁置/重启路径全部删除
This commit is contained in:
@@ -2,12 +2,20 @@
|
||||
* 本机浏览器登录态导入表单(面板浮层与设置页二级页共用)。
|
||||
* 选择本机浏览器(Chrome/Edge/Brave/Chromium)的一个 Profile,
|
||||
* 把各站点登录态(Cookie / Local Storage)迁移到受管浏览器分区。
|
||||
* 导入全程无感:无需重启应用;已导入的来源常驻展示,可一键「同步」复导。
|
||||
* 自含数据源:直接调用 window.api(browserDetectSources / browserImportProfile)。
|
||||
*/
|
||||
import { useEffect, useState, type JSX } from 'react'
|
||||
import { LuChevronDown } from 'react-icons/lu'
|
||||
import { LuChevronDown, LuRefreshCw } from 'react-icons/lu'
|
||||
import type { BrowserImportResult, BrowserSource, BrowserUiState } from '@shared/types'
|
||||
|
||||
function formatAt(iso: string): string {
|
||||
const d = new Date(iso)
|
||||
if (Number.isNaN(d.getTime())) return ''
|
||||
const pad = (n: number): string => String(n).padStart(2, '0')
|
||||
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
|
||||
}
|
||||
|
||||
export function BrowserImportForm(): JSX.Element {
|
||||
const [state, setState] = useState<BrowserUiState | null>(null)
|
||||
const [sources, setSources] = useState<BrowserSource[] | null>(null)
|
||||
@@ -51,29 +59,53 @@ export function BrowserImportForm(): JSX.Element {
|
||||
}
|
||||
}, [])
|
||||
|
||||
const runImport = (): void => {
|
||||
if (!selected) return
|
||||
const runImport = (target: { sourceId: string; profileDir: string } | null): void => {
|
||||
if (!target || busy) return
|
||||
setBusy(true)
|
||||
setError(null)
|
||||
setResult(null)
|
||||
window.api
|
||||
.browserImportProfile(selected.sourceId, selected.profileDir)
|
||||
.browserImportProfile(target.sourceId, target.profileDir)
|
||||
.then(setResult)
|
||||
.catch((err) => setError(err instanceof Error ? err.message : String(err)))
|
||||
.finally(() => setBusy(false))
|
||||
}
|
||||
|
||||
const imported = state?.imported ?? null
|
||||
|
||||
return (
|
||||
<div className="text-[12px] text-ink-2">
|
||||
<p className="mb-3 leading-relaxed text-ink-3">
|
||||
迁移 Cookie / Local Storage 登录态。导入前请
|
||||
<span className="font-medium text-ink-2">完全退出</span>源浏览器,并关闭本面板所有标签页。
|
||||
迁移 Cookie / Local Storage 登录态,导入后立即生效。导入前请
|
||||
<span className="font-medium text-ink-2">完全退出</span>源浏览器(含后台进程)。
|
||||
</p>
|
||||
|
||||
{state?.imported && (
|
||||
<div className="mb-3 rounded border border-line bg-inset px-2.5 py-2 text-[11.5px] text-ink-3">
|
||||
当前导入来源:{state.imported.browser} · {state.imported.profile}
|
||||
{state.imported.cookies ? '(含 Cookie)' : ''}
|
||||
{imported && (
|
||||
<div className="mb-3 rounded border border-line bg-inset px-2.5 py-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="shrink-0 text-[11px] text-ink-3">已导入</span>
|
||||
<span className="min-w-0 flex-1 truncate text-[12px] text-ink">
|
||||
{imported.browser} · {imported.profile}
|
||||
{imported.profileDir && <span className="ml-1.5 text-[10.5px] text-ink-3">{imported.profileDir}</span>}
|
||||
</span>
|
||||
{imported.sourceId && imported.profileDir && (
|
||||
<button
|
||||
type="button"
|
||||
disabled={busy}
|
||||
onClick={() => runImport({ sourceId: imported.sourceId!, profileDir: imported.profileDir! })}
|
||||
className="flex shrink-0 items-center gap-1 rounded border border-line-strong px-2 py-0.5 text-[11px] text-ink-2 transition-colors hover:bg-hover hover:text-ink disabled:opacity-40"
|
||||
>
|
||||
<LuRefreshCw size={11} className={busy ? 'animate-spin' : undefined} />
|
||||
同步
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<div className="mt-1 text-[10.5px] text-ink-3">
|
||||
{formatAt(imported.at)}
|
||||
{imported.cookieCount ? ` · Cookie ${imported.cookieCount} 条` : imported.cookies ? ' · 含 Cookie' : ''}
|
||||
{imported.localStorage ? ' · LocalStorage' : ''}
|
||||
</div>
|
||||
{imported.note && <div className="mt-1 text-[10.5px] leading-relaxed text-ink-3">{imported.note}</div>}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -128,26 +160,12 @@ export function BrowserImportForm(): JSX.Element {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 操作区:始终集中在底部一行;被搁置时以「重启并完成导入」为主操作 */}
|
||||
<div className="mt-3 flex items-center justify-end gap-2">
|
||||
{result?.deferred && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => void window.api.appRelaunch()}
|
||||
className="rounded bg-accent px-3.5 py-1.5 text-[12px] font-medium text-page transition-opacity hover:opacity-90"
|
||||
>
|
||||
重启并完成导入
|
||||
</button>
|
||||
)}
|
||||
<div className="mt-3 flex items-center justify-end">
|
||||
<button
|
||||
type="button"
|
||||
disabled={!selected || busy}
|
||||
onClick={runImport}
|
||||
className={`rounded px-3.5 py-1.5 text-[12px] transition-colors disabled:opacity-40 ${
|
||||
result?.deferred
|
||||
? 'border border-line-strong text-ink-2 hover:bg-hover hover:text-ink'
|
||||
: 'bg-accent font-medium text-page hover:opacity-90'
|
||||
}`}
|
||||
onClick={() => runImport(selected)}
|
||||
className="rounded bg-accent px-3.5 py-1.5 text-[12px] font-medium text-page transition-opacity hover:opacity-90 disabled:opacity-40"
|
||||
>
|
||||
{busy ? '导入中…' : '导入浏览器数据'}
|
||||
</button>
|
||||
|
||||
@@ -58,7 +58,6 @@ declare global {
|
||||
stop: () => Promise<void>
|
||||
getState: () => Promise<SessionState>
|
||||
versions: () => Promise<{ app: string; electron: string; node: string; chrome: string; user: string }>
|
||||
appRelaunch: () => Promise<void>
|
||||
listSessions: () => Promise<SessionMeta[]>
|
||||
newSession: () => Promise<void>
|
||||
openSession: (path: string) => Promise<void>
|
||||
|
||||
Reference in New Issue
Block a user