diff --git a/apps/web/src/pages/profile.tsx b/apps/web/src/pages/profile.tsx index c7e08c5..b40f345 100644 --- a/apps/web/src/pages/profile.tsx +++ b/apps/web/src/pages/profile.tsx @@ -1,7 +1,7 @@ import * as React from "react" import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query" import { useNavigate, useSearchParams } from "react-router-dom" -import { ArrowLeft, BarChart3, Ban, Clock3, Code2, Contact, Copy, Image, Info, KeyRound, Laptop, Link2, LogOut, Mail, MailCheck, MailX, MessageSquare, Moon, PanelLeftOpen, PencilLine, Plus, RefreshCcw, Repeat2, Search, SendHorizontal, Settings, Share2, ShieldCheck, SlidersHorizontal, Sun, Trash2, X } from "lucide-react" +import { ArrowLeft, BarChart3, Ban, ChevronDown, Clock3, Code2, Contact, Copy, Image, Info, KeyRound, Laptop, Link2, LogOut, Mail, MailCheck, MailX, MessageSquare, Moon, PanelLeftOpen, PencilLine, Plus, RefreshCcw, Repeat2, Search, SendHorizontal, Settings, Share2, ShieldCheck, SlidersHorizontal, Sun, Trash2, X } from "lucide-react" import { QRCodeSVG } from "qrcode.react" import { api, APIToken, ExternalImapAccount, ExternalImapAccountPayload, ExternalImapFolder, ExternalImapOAuthProvider, ExternalImapStorageMode, ExternalImapSyncRun, ExternalImapTlsMode, ForwardingSettings, ForwardingVerifiedEmail, MailLabel, MailRule, MailRuleAction, MailRuleCondition, Mailbox, MailboxApplyOptions, MailSignature, MailStats, PermissionLimits } from "@/lib/api" import { cn, formatBytes } from "@/lib/utils" @@ -1383,6 +1383,7 @@ function MailboxManagement({ const forwarding = useQuery({ queryKey: ["forwarding-settings"], queryFn: api.forwardingSettings, enabled: mailboxes.length > 0 }) const verifiedEmailItems = forwarding.data?.verifiedEmails || [] const verifiedEmails = React.useMemo(() => verifiedEmailItems.filter((item) => item.verified).map((item) => item.email), [verifiedEmailItems]) + const hasPendingVerifiedEmails = verifiedEmailItems.some((item) => !item.verified) const mailboxForwards = React.useMemo>(() => { const next: Record = {} for (const rule of forwarding.data?.mailboxRules || []) { @@ -1400,10 +1401,16 @@ function MailboxManagement({ const setForwardingCache = React.useCallback((settings: ForwardingSettings) => { qc.setQueryData(["forwarding-settings"], settings) }, [qc]) + const refreshForwardingSettings = React.useCallback(() => { + void qc.invalidateQueries({ queryKey: ["forwarding-settings"] }) + }, [qc]) const addVerifiedEmail = useMutation({ mutationFn: api.addForwardingVerifiedEmail, onSuccess: (settings, email) => { setForwardingCache(settings) + refreshForwardingSettings() + window.setTimeout(refreshForwardingSettings, 2500) + window.setTimeout(refreshForwardingSettings, 7000) addLog("添加验证邮箱", email) setVerifiedEmailDraft("") const item = settings.verifiedEmails.find((entry) => entry.email.toLowerCase() === email.trim().toLowerCase()) @@ -1418,6 +1425,9 @@ function MailboxManagement({ mutationFn: ({ id }: { id: string; email: string }) => api.resendForwardingVerifiedEmail(id), onSuccess: (settings, item) => { setForwardingCache(settings) + refreshForwardingSettings() + window.setTimeout(refreshForwardingSettings, 2500) + window.setTimeout(refreshForwardingSettings, 7000) addLog("重发验证邮件", item.email) const next = settings.verifiedEmails.find((entry) => entry.id === item.id) toast({ @@ -1467,6 +1477,11 @@ function MailboxManagement({ React.useEffect(() => { setAccountForwardTargets(forwardingTargetsFromSettings(forwarding.data)) }, [forwarding.data?.accountTargetEmail, forwarding.data?.accountTargetEmails]) + React.useEffect(() => { + if (!hasPendingVerifiedEmails) return + const timer = window.setInterval(() => { void forwarding.refetch() }, verifiedDialogOpen ? 3000 : 10000) + return () => window.clearInterval(timer) + }, [forwarding, hasPendingVerifiedEmails, verifiedDialogOpen]) React.useEffect(() => { writeLocalRecord("lanqin:seek-mailbox-notes", notes) }, [notes]) React.useEffect(() => { writeLocalLogs("lanqin:seek-mailbox-action-logs", logs) }, [logs]) @@ -1972,7 +1987,19 @@ function forwardingTargetsLabel(targets: string[]) { } function ForwardingTargetPicker({ emails, selected, onChange, disabled }: { emails: string[]; selected: string[]; onChange: (targets: string[]) => void; disabled?: boolean }) { + const [open, setOpen] = React.useState(false) + const [query, setQuery] = React.useState("") const selectedSet = React.useMemo(() => new Set(selected), [selected]) + const sortedEmails = React.useMemo(() => { + const collator = new Intl.Collator("en", { sensitivity: "base", numeric: true }) + return Array.from(new Set(emails.map((email) => email.trim()).filter(Boolean))).sort((a, b) => collator.compare(a, b)) + }, [emails]) + const selectedEmails = React.useMemo(() => sortedEmails.filter((email) => selectedSet.has(email)), [selectedSet, sortedEmails]) + const normalizedQuery = query.trim().toLowerCase() + const filteredEmails = normalizedQuery ? sortedEmails.filter((email) => email.toLowerCase().includes(normalizedQuery)) : sortedEmails + const label = selectedEmails.length + ? `已选择 ${selectedEmails.length} 个:${selectedEmails.slice(0, 2).join("、")}${selectedEmails.length > 2 ? ` 等 ${selectedEmails.length} 个` : ""}` + : "不转发,点击选择邮箱" function toggle(email: string, checked: boolean) { if (disabled) return const next = checked ? Array.from(new Set([...selected, email])) : selected.filter((item) => item !== email) @@ -1982,24 +2009,64 @@ function ForwardingTargetPicker({ emails, selected, onChange, disabled }: { emai return
暂无已验证邮箱
} return ( -
- {emails.map((email) => { - const checked = selectedSet.has(email) - return ( -
) }