From e682ad3e5b20f37795dac2178e7acc69f103c8dd Mon Sep 17 00:00:00 2001 From: lsdsjy <1356263+lsdsjy@users.noreply.github.com> Date: Mon, 21 Sep 2026 19:53:58 +0800 Subject: [PATCH] fix(ui-settings-account): keep sign-in errors in the login dialog --- .../ui-settings-account/README.i18n.yaml | 4 +- packages/client/ui-settings-account/README.md | 2 +- .../client/ui-settings-account/README.zh.md | 2 +- .../src/client/AccountMenu.tsx | 12 +++--- .../tests/account.client.spec.tsx | 43 ++++++++++++++++++- .../tests/expected/login-failed-en.txt | 1 + 6 files changed, 54 insertions(+), 10 deletions(-) create mode 100644 packages/client/ui-settings-account/tests/expected/login-failed-en.txt diff --git a/packages/client/ui-settings-account/README.i18n.yaml b/packages/client/ui-settings-account/README.i18n.yaml index fb41dc5813..db8b7570f1 100644 --- a/packages/client/ui-settings-account/README.i18n.yaml +++ b/packages/client/ui-settings-account/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/ui-settings-account/README.md -README.md: 99053a5ffb5e85bb9754a1d2b00dffbadbaf51da -README.zh.md: aa89bce650374e5f2de15277cd53c0eb9cf8bc38 +README.md: 825bb63e59a08e2d9fb05a3a730c17ae53af9c51 +README.zh.md: 3949c16008bb664e816d649c6096768311c53331 diff --git a/packages/client/ui-settings-account/README.md b/packages/client/ui-settings-account/README.md index 99053a5ffb..825bb63e59 100644 --- a/packages/client/ui-settings-account/README.md +++ b/packages/client/ui-settings-account/README.md @@ -44,7 +44,7 @@ The Account section appears first in Settings only while signed in; it is hidden The [credentials subsystem](../../../docs/subsystems/credentials.md) owns storage APIs; the [architecture](../../../docs/architecture.md) explains application composition. -Account login uses a dismissible dialog before the model onboarding credential editor. The dialog uses a compact, right-aligned action row with the primary action last. The sidebar displays the profile name, falling back to the server-masked phone number or email when the name is absent; it stays blank while the profile is loading. Dialog copy and the sidebar account label are not text-selectable; the copy-link button remains available and restores its label two seconds after each successful copy. Clipboard failures show Copy failed on the link for two seconds without interrupting sign-in. Waiting shows a copyable authorization link and a loading indicator; timeout and failure require an explicit retry. Closing a waiting dialog cancels its Host attempt. The sidebar can reopen the same API-key editor through the settings coordinator. Browser login preopens a tab on the click and navigates it only when that attempt receives its authorization URL; blocked popups retain the copy-link route. The original tab retains the authorization window handle and closes it on failure, timeout, or cancellation. Login feedback stays in the original tab; callbacks never navigate to a second Web UI. +Account login uses a dismissible dialog before the model onboarding credential editor. The dialog uses a compact, right-aligned action row with the primary action last. The sidebar displays the profile name, falling back to the server-masked phone number or email when the name is absent; it stays blank while the profile is loading. Dialog copy and the sidebar account label are not text-selectable; the copy-link button remains available and restores its label two seconds after each successful copy. Clipboard failures show Copy failed on the link for two seconds without interrupting sign-in. Waiting shows a copyable authorization link and a loading indicator; timeout and failure require an explicit retry. Sign-in failures render in that dialog; the launcher's inline failure line reports only a failed sign-out. Closing a waiting dialog cancels its Host attempt. The sidebar can reopen the same API-key editor through the settings coordinator. Browser login preopens a tab on the click and navigates it only when that attempt receives its authorization URL; blocked popups retain the copy-link route. The original tab retains the authorization window handle and closes it on failure, timeout, or cancellation. Login feedback stays in the original tab; callbacks never navigate to a second Web UI. The balance card shows recharge funds and positive bonus credit in separate rows. Empty or nonpositive bonus wallets hide the bonus row and its divider; currencies retain their own amounts. diff --git a/packages/client/ui-settings-account/README.zh.md b/packages/client/ui-settings-account/README.zh.md index aa89bce650..3949c16008 100644 --- a/packages/client/ui-settings-account/README.zh.md +++ b/packages/client/ui-settings-account/README.zh.md @@ -44,7 +44,7 @@ Desktop 的用量和充值操作在 48px 返回栏下方打开隔离的原生 Pl [凭证子系统](../../../docs/subsystems/credentials.zh.md)定义存储接口;[架构](../../../docs/architecture.zh.md)说明应用组合。 -账号登录在模型引导的凭证编辑器之前显示可关闭的弹窗。弹窗底部采用紧凑的右对齐横排操作,主操作位于末尾。侧边栏优先显示昵称,缺少昵称时显示服务端返回的脱敏手机号或邮箱;资料加载期间保持留空。 弹窗文字和侧边栏账号状态文字不可选中,复制登录链接按钮始终可用,并在每次复制成功两秒后恢复原文案。剪贴板失败时,链接原位显示“复制失败”两秒,不中断登录。等待状态提供可复制的授权链接和加载图标;超时和失败均须用户手动重试。关闭等待弹窗会取消对应 Host 登录流程。侧边栏通过设置协调器重新打开同一个 API Key 编辑器。浏览器登录在点击时预先打开标签页,只有该次流程收到授权地址后才导航;弹窗被拦截时保留复制链接入口。原标签页保留授权窗口引用,并在失败、超时或取消时关闭它。登录反馈留在原标签页,回调不会导航到第二个 Web UI。 +账号登录在模型引导的凭证编辑器之前显示可关闭的弹窗。弹窗底部采用紧凑的右对齐横排操作,主操作位于末尾。侧边栏优先显示昵称,缺少昵称时显示服务端返回的脱敏手机号或邮箱;资料加载期间保持留空。 弹窗文字和侧边栏账号状态文字不可选中,复制登录链接按钮始终可用,并在每次复制成功两秒后恢复原文案。剪贴板失败时,链接原位显示“复制失败”两秒,不中断登录。等待状态提供可复制的授权链接和加载图标;超时和失败均须用户手动重试。登录失败在该弹窗内呈现;侧边栏入口的内联失败提示仅用于退出登录失败。关闭等待弹窗会取消对应 Host 登录流程。侧边栏通过设置协调器重新打开同一个 API Key 编辑器。浏览器登录在点击时预先打开标签页,只有该次流程收到授权地址后才导航;弹窗被拦截时保留复制链接入口。原标签页保留授权窗口引用,并在失败、超时或取消时关闭它。登录反馈留在原标签页,回调不会导航到第二个 Web UI。 余额卡片分行展示充值余额与正额赠金余额。赠送钱包为空或没有正余额时,隐藏赠金行及其分隔线;不同币种分别显示。 diff --git a/packages/client/ui-settings-account/src/client/AccountMenu.tsx b/packages/client/ui-settings-account/src/client/AccountMenu.tsx index a6e80fb7ac..79caf52db6 100644 --- a/packages/client/ui-settings-account/src/client/AccountMenu.tsx +++ b/packages/client/ui-settings-account/src/client/AccountMenu.tsx @@ -25,14 +25,16 @@ export function AccountMenu({ ? profile.value.name ?? profile.value.contact ?? t('signedIn') : t('signedIn') const [open, setOpen] = useState(false) const [busy, setBusy] = useState(false) - const [failed, setFailed] = useState(false) + const [logoutFailed, setLogoutFailed] = useState(false) const logout = async () => { setBusy(true) - setFailed(false) + setLogoutFailed(false) try { await signOut(); setOpen(false) } - catch { setFailed(true) } + catch { setLogoutFailed(true) } finally { setBusy(false) } } + // The plugin's start publishes `loginFailed` before it rejects, so the dialog owns the report. + const beginSignIn = (): void => { setOpen(false); void start().catch(() => undefined) } return