Backfill start-window indicator, account-delete spinner, app-password Help popup
- Backfill: show the sidebar spinner + "Preparing…" banner during the brief window
after account creation when state=backfilling but total is not yet counted
(total===0), so a freshly-added account shows progress immediately instead of
looking done until a refresh. The stuck-at-100% guard is preserved.
- Settings: a centered spinner overlay ("Removing account…") covers the screen from
confirm until the (slow) server cascade + accounts refetch complete, so the row
no longer appears frozen before it disappears.
- Add-account wizard: the manual password hint now includes Outlook and gains a
"Help" link opening a popup with per-provider direct links to enable 2-step
verification and generate the app password (Gmail, Outlook, Yahoo, iCloud).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U72FZiF1W1pb8XxyWBTREA
This commit is contained in:
@@ -251,9 +251,15 @@ function AccountsSection() {
|
||||
const [editing, setEditing] = useState<{ id: string; mode: "account" | "signature" } | null>(
|
||||
null,
|
||||
);
|
||||
// Which account is being deleted — drives the blocking spinner overlay until the
|
||||
// (slow) server cascade finishes and the refetched list no longer includes it.
|
||||
const [deletingId, setDeletingId] = useState<string | null>(null);
|
||||
const remove = useMutation({
|
||||
mutationFn: (id: string) => api.deleteAccount(id),
|
||||
onSuccess: () => queryClient.invalidateQueries({ queryKey: ["accounts"] }),
|
||||
onSettled: async () => {
|
||||
await queryClient.invalidateQueries({ queryKey: ["accounts"] });
|
||||
setDeletingId(null);
|
||||
},
|
||||
});
|
||||
const downloadAll = useMutation({
|
||||
mutationFn: (id: string) => api.updateAccount(id, { sync_window: "all" }),
|
||||
@@ -325,7 +331,10 @@ function AccountsSection() {
|
||||
<button
|
||||
className="text-xs text-red-500"
|
||||
onClick={() => {
|
||||
if (confirm(t("settings.confirmRemoveAccount"))) remove.mutate(a.id);
|
||||
if (confirm(t("settings.confirmRemoveAccount"))) {
|
||||
setDeletingId(a.id);
|
||||
remove.mutate(a.id);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{t("common.remove")}
|
||||
@@ -336,6 +345,18 @@ function AccountsSection() {
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
{deletingId && (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40 backdrop-blur-sm">
|
||||
<div className="flex flex-col items-center gap-3 rounded-xl bg-app-elevated px-8 py-6 shadow-2xl">
|
||||
<span
|
||||
className="h-8 w-8 animate-spin rounded-full border-2 border-app-border border-t-app-accent"
|
||||
role="status"
|
||||
aria-label={t("settings.removingAccount")}
|
||||
/>
|
||||
<span className="text-sm text-app-muted">{t("settings.removingAccount")}</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user