From 6ad9164be345af1832aa5b68528fb60bd571cbb1 Mon Sep 17 00:00:00 2001 From: zxyszx <299979470+zxyszx@users.noreply.github.com> Date: Tue, 11 Aug 2026 23:40:55 +0800 Subject: [PATCH] release: prepare v1.2.27 --- .github/release-notes/v1.2.27.md | 6 +++++ VERSION | 2 +- apps/web/src/pages/profile.tsx | 45 ++++++++++++++++++++++---------- 3 files changed, 38 insertions(+), 15 deletions(-) create mode 100644 .github/release-notes/v1.2.27.md diff --git a/.github/release-notes/v1.2.27.md b/.github/release-notes/v1.2.27.md new file mode 100644 index 0000000..6175489 --- /dev/null +++ b/.github/release-notes/v1.2.27.md @@ -0,0 +1,6 @@ +- 修复收信规则选择“移动到自定义文件夹”后立即跳回归档的问题。 +- 移除移动动作右侧显示 `Archive` 等英文内部值的旧输入框,避免系统值与中文选项重复出现。 +- 将移动目标整合为一个文件夹选择器,系统文件夹统一显示中文名称。 +- 文件夹选择器新增“系统文件夹”和“自定义文件夹”分组,自动列出当前邮箱或全部邮箱中已经创建的自定义文件夹。 +- 选择 Netflix、amazon 等自定义文件夹后会直接保存真实文件夹名称,不再错误保存为归档。 +- 已验证规则创建、列表摘要和后端数据均保留所选自定义文件夹。 diff --git a/VERSION b/VERSION index cba64f4..3b781da 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.2.26 +1.2.27 diff --git a/apps/web/src/pages/profile.tsx b/apps/web/src/pages/profile.tsx index cad3e3a..1132d3c 100644 --- a/apps/web/src/pages/profile.tsx +++ b/apps/web/src/pages/profile.tsx @@ -3,7 +3,7 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query" import { useNavigate, useSearchParams } from "react-router-dom" import { ArrowLeft, BarChart3, Ban, Bell, BellOff, BookOpen, ChevronDown, ChevronUp, Clock3, Code2, Contact, Copy, HardDrive, Image, Info, KeyRound, LogOut, Mail, MailCheck, MailX, Moon, PanelLeftOpen, PencilLine, PlayCircle, Plus, RefreshCcw, Search, SendHorizontal, Settings, ShieldCheck, SlidersHorizontal, Sun, Trash2, Users, 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 { api, APIToken, ExternalImapAccount, ExternalImapAccountPayload, ExternalImapFolder, ExternalImapOAuthProvider, ExternalImapStorageMode, ExternalImapSyncRun, ExternalImapTlsMode, ForwardingSettings, ForwardingVerifiedEmail, MailFolder, MailLabel, MailRule, MailRuleAction, MailRuleCondition, Mailbox, MailboxApplyOptions, MailSignature, MailStats, PermissionLimits } from "@/lib/api" import { cn, formatBytes } from "@/lib/utils" import { applyTheme, getInitialTheme } from "@/lib/theme" import { DisplayMode, useDisplayMode } from "@/lib/display-mode" @@ -23,7 +23,7 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Checkbox } from "@/components/ui/checkbox" import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog" import { Sheet, SheetContent, SheetTitle, SheetTrigger } from "@/components/ui/sheet" -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" +import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectSeparator, SelectTrigger, SelectValue } from "@/components/ui/select" import { Separator } from "@/components/ui/separator" import { ScrollArea } from "@/components/ui/scroll-area" import { ConfirmDialog } from "@/components/confirm-dialog" @@ -2292,7 +2292,10 @@ function RuleDialog({ open, onOpenChange, mailboxes, labels, verifiedEmails, pen const [stopProcessing, setStopProcessing] = React.useState(false) const selectedMailboxId = mailboxId === "all" ? "" : mailboxId const labelQuery = useQuery({ queryKey: ["labels", "rule-dialog", selectedMailboxId], queryFn: () => api.labels(selectedMailboxId), enabled: !!selectedMailboxId }) + const folderQueryMailboxId = selectedMailboxId || "all" + const folderQuery = useQuery({ queryKey: ["folders", "rule-dialog", folderQueryMailboxId], queryFn: () => api.folders(folderQueryMailboxId), enabled: open }) const availableLabels = selectedMailboxId ? (labelQuery.data?.items || []) : labels + const availableFolders = folderQuery.data?.items || [] React.useEffect(() => { if (!open) return @@ -2383,7 +2386,7 @@ function RuleDialog({ open, onOpenChange, mailboxes, labels, verifiedEmails, pen {(Object.keys(ruleActionLabels) as MailRuleAction["type"][]).map((value) => {ruleActionLabels[value]})} -
updateAction(index, patch)} />
+
updateAction(index, patch)} />
@@ -2414,7 +2417,7 @@ function RuleDialog({ open, onOpenChange, mailboxes, labels, verifiedEmails, pen ) } -function RuleActionValue({ action, labels, verifiedEmails, onChange }: { action: MailRuleAction; labels: MailLabel[]; verifiedEmails: string[]; onChange: (patch: Partial) => void }) { +function RuleActionValue({ action, folders, labels, verifiedEmails, onChange }: { action: MailRuleAction; folders: MailFolder[]; labels: MailLabel[]; verifiedEmails: string[]; onChange: (patch: Partial) => void }) { if (action.type === "label") { if (labels.length > 0) { return ( @@ -2428,20 +2431,26 @@ function RuleActionValue({ action, labels, verifiedEmails, onChange }: { action: } if (action.type === "move") { const value = action.value || "Archive" + const customFolders = ruleCustomFolders(folders, value) return ( -
- onChange({ value: next })}> + + + + 系统文件夹 收件箱 归档 垃圾邮件 - 回收站 - 自定义文件夹 - - - onChange({ value: event.target.value })} placeholder="输入或选择文件夹名" /> -
+ 已删除 + + + + 自定义文件夹 + {customFolders.map((folder) => {folder})} + {customFolders.length === 0 &&
暂无自定义文件夹
} +
+ + ) } if (action.type === "forward") { @@ -2450,6 +2459,14 @@ function RuleActionValue({ action, labels, verifiedEmails, onChange }: { action: return } +function ruleCustomFolders(folders: MailFolder[], selectedValue: string) { + const names = folders + .map((folder) => folder.name.trim()) + .filter((name) => name && !commonRuleFolders.some((systemName) => systemName.toLowerCase() === name.toLowerCase()) && !["Sent", "Drafts"].some((systemName) => systemName.toLowerCase() === name.toLowerCase())) + if (selectedValue && !commonRuleFolders.includes(selectedValue) && !names.some((name) => name.toLowerCase() === selectedValue.toLowerCase())) names.push(selectedValue) + return Array.from(new Set(names)).sort((a, b) => a.localeCompare(b, "zh-CN", { numeric: true, sensitivity: "base" })) +} + function RuleForwardTargets({ value, emails, onChange }: { value: string; emails: string[]; onChange: (value: string) => void }) { const selected = React.useMemo(() => verifiedRuleForwardTargets(value, emails), [emails, value]) return onChange(targets.join(", "))} placement="top" />