diff --git a/frontend/i18n/locales/en.json b/frontend/i18n/locales/en.json index cfa4a65..046e919 100644 --- a/frontend/i18n/locales/en.json +++ b/frontend/i18n/locales/en.json @@ -57,14 +57,19 @@ "notFound": "Mailbox not found or expired", "creatingNew": "Creating new mailbox...", "switch": "Switch Mailbox", + "switchSuccess": "Mailbox switched successfully", "savedMailboxes": "Saved Mailboxes", "confirmDeleteMailbox": "Are you sure you want to delete this saved mailbox?", + "deleteSavedSuccess": "Saved mailbox deleted successfully", "confirmClearAllMailboxes": "Are you sure you want to clear all saved mailboxes? (The current mailbox will not be deleted)", - "clearAll": "Clear All" + "clearAll": "Clear All", + "clearAllSuccess": "All saved mailboxes have been cleared", + "clearAllFailed": "Failed to clear some mailboxes ({{count}})" }, "email": { "inbox": "Inbox", "refresh": "Refresh Emails", + "refreshSuccess": "Email list has been refreshed", "autoRefresh": "Auto Refresh", "autoRefreshOn": "Auto Refresh: On", "autoRefreshOff": "Auto Refresh: Off", diff --git a/frontend/i18n/locales/zh-CN.json b/frontend/i18n/locales/zh-CN.json index 108b173..95a55f8 100644 --- a/frontend/i18n/locales/zh-CN.json +++ b/frontend/i18n/locales/zh-CN.json @@ -57,14 +57,19 @@ "notFound": "邮箱不存在或已过期", "creatingNew": "正在创建新邮箱...", "switch": "切换邮箱", + "switchSuccess": "邮箱切换成功", "savedMailboxes": "已保存的邮箱", "confirmDeleteMailbox": "您确定要删除这个已保存的邮箱吗?", + "deleteSavedSuccess": "已保存的邮箱删除成功", "confirmClearAllMailboxes": "您确定要清空所有已保存的邮箱吗?(当前使用的邮箱不会被删除)", - "clearAll": "全部清除" + "clearAll": "全部清除", + "clearAllSuccess": "已清空所有保存的邮箱", + "clearAllFailed": "部分邮箱清空失败 ({{count}}个)" }, "email": { "inbox": "收件箱", "refresh": "刷新邮件", + "refreshSuccess": "邮件列表已刷新", "autoRefresh": "自动刷新", "autoRefreshOn": "自动刷新: 开", "autoRefreshOff": "自动刷新: 关", diff --git a/frontend/src/components/EmailDetail.tsx b/frontend/src/components/EmailDetail.tsx index af00f0c..6f79b20 100644 --- a/frontend/src/components/EmailDetail.tsx +++ b/frontend/src/components/EmailDetail.tsx @@ -21,35 +21,16 @@ interface Attachment { const EmailDetail: React.FC = ({ emailId, onClose }) => { const { t } = useTranslation(); - const { emailCache, addToEmailCache, handleMailboxNotFound } = useContext(MailboxContext); + // fix: 从 context 中获取全局通知函数 + const { emailCache, addToEmailCache, handleMailboxNotFound, showErrorMessage, showSuccessMessage } = useContext(MailboxContext); const [email, setEmail] = useState(null); const [attachments, setAttachments] = useState([]); const [isLoading, setIsLoading] = useState(true); const [isLoadingAttachments, setIsLoadingAttachments] = useState(false); - const [errorMessage, setErrorMessage] = useState(null); - const [successMessage, setSuccessMessage] = useState(null); - const errorTimeoutRef = useRef(null); - const successTimeoutRef = useRef(null); - - // 清除提示的定时器 - useEffect(() => { - return () => { - if (errorTimeoutRef.current) { - window.clearTimeout(errorTimeoutRef.current); - } - if (successTimeoutRef.current) { - window.clearTimeout(successTimeoutRef.current); - } - }; - }, []); useEffect(() => { const fetchEmail = async () => { try { - // 清除之前的错误和成功信息 - setErrorMessage(null); - setSuccessMessage(null); - // 首先检查缓存中是否有该邮件 if (emailCache[emailId]) { setEmail(emailCache[emailId].email); @@ -86,22 +67,15 @@ const EmailDetail: React.FC = ({ emailId, onClose }) => { throw new Error(data.error || 'Unknown error'); } } catch (error) { - setErrorMessage(t('email.fetchFailed')); - - // 3秒后清除错误信息 - if (errorTimeoutRef.current) { - window.clearTimeout(errorTimeoutRef.current); - } - errorTimeoutRef.current = window.setTimeout(() => { - setErrorMessage(null); - }, 3000); + // fix: 使用全局通知函数 + showErrorMessage(t('email.fetchFailed')); } finally { setIsLoading(false); } }; fetchEmail(); - }, [emailId, t, emailCache, addToEmailCache, handleMailboxNotFound, onClose]); + }, [emailId, t, emailCache, addToEmailCache, handleMailboxNotFound, onClose, showErrorMessage]); const fetchAttachments = async (emailId: string, emailData?: Email) => { try { @@ -138,10 +112,6 @@ const EmailDetail: React.FC = ({ emailId, onClose }) => { const handleDelete = async () => { try { - // 清除之前的错误和成功信息 - setErrorMessage(null); - setSuccessMessage(null); - const response = await fetch(`${API_BASE_URL}/api/emails/${emailId}`, { method: 'DELETE', }); @@ -152,28 +122,19 @@ const EmailDetail: React.FC = ({ emailId, onClose }) => { const data = await response.json(); if (data.success) { - setSuccessMessage(t('email.deleteSuccess')); + // fix: 使用全局通知函数 + showSuccessMessage(t('email.deleteSuccess')); // 2秒后关闭邮件详情 - if (successTimeoutRef.current) { - window.clearTimeout(successTimeoutRef.current); - } - successTimeoutRef.current = window.setTimeout(() => { + setTimeout(() => { onClose(); }, 2000); } else { throw new Error(data.error || 'Unknown error'); } } catch (error) { - setErrorMessage(t('email.deleteFailed')); - - // 3秒后清除错误信息 - if (errorTimeoutRef.current) { - window.clearTimeout(errorTimeoutRef.current); - } - errorTimeoutRef.current = window.setTimeout(() => { - setErrorMessage(null); - }, 3000); + // fix: 使用全局通知函数 + showErrorMessage(t('email.deleteFailed')); } }; @@ -285,13 +246,7 @@ const EmailDetail: React.FC = ({ emailId, onClose }) => { return (
- {/* 错误和成功提示 */} - {(errorMessage || successMessage) && ( -
- {errorMessage || successMessage} -
- )} - + {/* fix: 移除局部的错误和成功提示,使用全局通知 */} {isLoading ? (
diff --git a/frontend/src/components/EmailList.tsx b/frontend/src/components/EmailList.tsx index 2396e0a..ceb18ea 100644 --- a/frontend/src/components/EmailList.tsx +++ b/frontend/src/components/EmailList.tsx @@ -62,7 +62,8 @@ const EmailList: React.FC = ({ }; const handleRefresh = () => { - refreshEmails(); + // feat: 调用 context 中的 refreshEmails,并传入 true 表示是手动刷新 + refreshEmails(true); }; const toggleAutoRefresh = () => { diff --git a/frontend/src/components/HeaderMailbox.tsx b/frontend/src/components/HeaderMailbox.tsx index 212b791..0b25694 100644 --- a/frontend/src/components/HeaderMailbox.tsx +++ b/frontend/src/components/HeaderMailbox.tsx @@ -1,7 +1,8 @@ -import React, { useState, useRef, useEffect } from 'react'; +import React, { useState, useRef, useEffect, useContext } from 'react'; import { useTranslation } from 'react-i18next'; import { createRandomMailbox, createCustomMailbox } from '../utils/api'; import MailboxSwitcher from './MailboxSwitcher'; +import { MailboxContext } from '../contexts/MailboxContext'; interface HeaderMailboxProps { mailbox: Mailbox | null; @@ -19,112 +20,47 @@ const HeaderMailbox: React.FC = ({ isLoading }) => { const { t } = useTranslation(); + // feat: 统一使用全局通知 + const { showSuccessMessage, showErrorMessage } = useContext(MailboxContext); const [isCustomMode, setIsCustomMode] = useState(false); const [customAddress, setCustomAddress] = useState(''); const [selectedDomain, setSelectedDomain] = useState(domain); const [isActionLoading, setIsActionLoading] = useState(false); - const [showCopyTooltip, setShowCopyTooltip] = useState(false); - const [copyError, setCopyError] = useState(null); - const [refreshError, setRefreshError] = useState(null); const [customAddressError, setCustomAddressError] = useState(null); - const [customAddressSuccess, setCustomAddressSuccess] = useState(null); - const [showRefreshSuccess, setShowRefreshSuccess] = useState(false); - const copyTooltipTimeoutRef = useRef(null); - const refreshSuccessTimeoutRef = useRef(null); - const successMessageTimeoutRef = useRef(null); - const errorMessageTimeoutRef = useRef(null); - - // 当props中的domain变化时更新selectedDomain + useEffect(() => { setSelectedDomain(domain); }, [domain]); - // 清除提示的定时器 - useEffect(() => { - return () => { - if (copyTooltipTimeoutRef.current) { - window.clearTimeout(copyTooltipTimeoutRef.current); - } - if (refreshSuccessTimeoutRef.current) { - window.clearTimeout(refreshSuccessTimeoutRef.current); - } - if (successMessageTimeoutRef.current) { - window.clearTimeout(successMessageTimeoutRef.current); - } - if (errorMessageTimeoutRef.current) { - window.clearTimeout(errorMessageTimeoutRef.current); - } - }; - }, []); - if (!mailbox || isLoading) return null; // 复制邮箱地址到剪贴板 const copyToClipboard = () => { - // 清除之前的错误信息 - setCopyError(null); - const fullAddress = mailbox.address.includes('@') ? mailbox.address : `${mailbox.address}@${selectedDomain}`; navigator.clipboard.writeText(fullAddress) .then(() => { - // 显示复制成功提示 - setShowCopyTooltip(true); - - // 2秒后隐藏提示 - if (copyTooltipTimeoutRef.current) { - window.clearTimeout(copyTooltipTimeoutRef.current); - } - copyTooltipTimeoutRef.current = window.setTimeout(() => { - setShowCopyTooltip(false); - }, 2000); + // feat: 使用全局通知替换 Tooltip + showSuccessMessage(t('mailbox.copySuccess')); }) .catch(() => { - // 显示复制失败错误 - setCopyError(t('mailbox.copyFailed')); - - // 3秒后隐藏错误 - if (errorMessageTimeoutRef.current) { - window.clearTimeout(errorMessageTimeoutRef.current); - } - errorMessageTimeoutRef.current = window.setTimeout(() => { - setCopyError(null); - }, 3000); + // fix: 使用全局通知函数显示复制失败 + showErrorMessage(t('mailbox.copyFailed')); }); }; // 更换随机邮箱 const handleRefreshMailbox = async () => { - // 清除之前的错误信息 - setRefreshError(null); - setIsActionLoading(true); const result = await createRandomMailbox(); setIsActionLoading(false); if (result.success && result.mailbox) { onMailboxChange(result.mailbox); - - // 显示更新成功提示 - setShowRefreshSuccess(true); - - // 3秒后隐藏提示 - if (refreshSuccessTimeoutRef.current) { - window.clearTimeout(refreshSuccessTimeoutRef.current); - } - refreshSuccessTimeoutRef.current = window.setTimeout(() => { - setShowRefreshSuccess(false); - }, 3000); + // feat: 使用全局通知替换 Tooltip + showSuccessMessage(t('mailbox.refreshSuccess')); } else { - // 显示刷新失败错误 - setRefreshError(t('mailbox.refreshFailed')); - - // 3秒后隐藏错误 - if (errorMessageTimeoutRef.current) { - window.clearTimeout(errorMessageTimeoutRef.current); - } - errorMessageTimeoutRef.current = window.setTimeout(() => { - setRefreshError(null); - }, 3000); + // fix: 使用全局通知函数显示刷新失败 + showErrorMessage(t('mailbox.refreshFailed')); } }; @@ -132,9 +68,8 @@ const HeaderMailbox: React.FC = ({ const handleCreateCustom = async (e: React.FormEvent) => { e.preventDefault(); - // 清除之前的错误和成功信息 + // 清除之前的错误信息 setCustomAddressError(null); - setCustomAddressSuccess(null); if (!customAddress.trim()) { setCustomAddressError(t('mailbox.invalidAddress')); @@ -147,29 +82,23 @@ const HeaderMailbox: React.FC = ({ if (result.success && result.mailbox) { onMailboxChange(result.mailbox); + // fix: 使用全局通知函数显示成功 + showSuccessMessage(t('mailbox.createSuccess')); - // 显示成功消息在表单下方 - setCustomAddressSuccess(t('mailbox.createSuccess')); - - // 3秒后自动关闭表单 - if (successMessageTimeoutRef.current) { - window.clearTimeout(successMessageTimeoutRef.current); - } - successMessageTimeoutRef.current = window.setTimeout(() => { + // 成功后延迟关闭自定义输入框 + setTimeout(() => { setIsCustomMode(false); setCustomAddress(''); - setCustomAddressSuccess(null); - }, 3000); + }, 1500); + } else { - // 检查错误信息是否包含"邮箱地址已存在" - const isAddressExistsError = - result.error === 'Address already exists' || - String(result.error).includes('已存在'); - + const isAddressExistsError = result.error === 'Address already exists' || String(result.error).includes('已存在'); if (isAddressExistsError) { + // fix: 对于表单内校验错误,保留局部状态提示 setCustomAddressError(t('mailbox.addressExists')); } else { - setCustomAddressError(t('mailbox.createFailed')); + // fix: 对于通用创建失败,使用全局通知 + showErrorMessage(t('mailbox.createFailed')); } } }; @@ -179,7 +108,6 @@ const HeaderMailbox: React.FC = ({ setIsCustomMode(false); setCustomAddress(''); setCustomAddressError(null); - setCustomAddressSuccess(null); }; // 移动设备上的邮箱地址显示 @@ -222,7 +150,6 @@ const HeaderMailbox: React.FC = ({ onChange={(e) => { setCustomAddress(e.target.value); if (customAddressError) setCustomAddressError(null); - if (customAddressSuccess) setCustomAddressSuccess(null); }} className={`w-32 md:w-40 px-2 py-1 text-sm border rounded-l-md focus:outline-none focus:ring-1 focus:ring-primary ${ customAddressError ? 'border-red-500' : '' @@ -274,12 +201,6 @@ const HeaderMailbox: React.FC = ({
)} - {/* 成功信息显示 */} - {customAddressSuccess && ( -
- {customAddressSuccess} -
- )} ) : ( <> @@ -313,22 +234,14 @@ const HeaderMailbox: React.FC = ({ />
- - - {/* 复制成功提示 */} - {showCopyTooltip && ( -
- {t('mailbox.copySuccess')} -
-
- )}
@@ -340,14 +253,6 @@ const HeaderMailbox: React.FC = ({ > - - {/* 更新成功提示 */} - {showRefreshSuccess && ( -
- {t('mailbox.refreshSuccess')} -
-
- )}
- {/* 错误信息显示 */} - {(copyError || refreshError) && ( -
- {copyError || refreshError} -
- )} {/* 移动版显示 */} @@ -384,22 +283,14 @@ const HeaderMailbox: React.FC = ({
- - - {/* 复制成功提示 */} - {showCopyTooltip && ( -
- {t('mailbox.copySuccess')} -
-
- )}
@@ -407,39 +298,24 @@ const HeaderMailbox: React.FC = ({
- - {/* 更新成功提示 */} - {showRefreshSuccess && ( -
- {t('mailbox.refreshSuccess')} -
-
- )}
- - {/* 错误信息显示 */} - {(copyError || refreshError) && ( -
- {copyError || refreshError} -
- )} )} diff --git a/frontend/src/components/MailboxSwitcher.tsx b/frontend/src/components/MailboxSwitcher.tsx index 58e0e88..2dea035 100644 --- a/frontend/src/components/MailboxSwitcher.tsx +++ b/frontend/src/components/MailboxSwitcher.tsx @@ -1,7 +1,7 @@ -import React, { useState, useRef, useEffect } from 'react'; +import React, { useState, useRef, useEffect, useContext } from 'react'; // feat: 导入 useContext import { useTranslation } from 'react-i18next'; -// [feat]: 导入 deleteMailbox API 函数 import { deleteMailbox as apiDeleteMailbox } from '../utils/api'; +import { MailboxContext } from '../contexts/MailboxContext'; // feat: 导入 MailboxContext interface MailboxSwitcherProps { currentMailbox: Mailbox; @@ -15,6 +15,8 @@ const MailboxSwitcher: React.FC = ({ domain }) => { const { t } = useTranslation(); + // feat: 从 context 中获取全局通知函数 + const { showSuccessMessage, showErrorMessage } = useContext(MailboxContext); const [savedMailboxes, setSavedMailboxes] = useState([]); const [showDropdown, setShowDropdown] = useState(false); const dropdownRef = useRef(null); @@ -103,9 +105,11 @@ const MailboxSwitcher: React.FC = ({ const handleSwitchMailbox = (mailbox: Mailbox) => { onSwitchMailbox(mailbox); setShowDropdown(false); + // feat: 切换邮箱也给出提示 + showSuccessMessage(t('mailbox.switchSuccess')); }; - // 删除单个已保存的邮箱(包括后端) + // 删除单个已保存的邮箱 const handleDeleteMailbox = async (address: string) => { if (window.confirm(t('mailbox.confirmDeleteMailbox'))) { // 调用API删除后端的邮箱 @@ -115,14 +119,16 @@ const MailboxSwitcher: React.FC = ({ const updatedMailboxes = savedMailboxes.filter(m => m.address !== address); setSavedMailboxes(updatedMailboxes); localStorage.setItem('savedMailboxes', JSON.stringify(updatedMailboxes)); + // feat: 删除成功提示 + showSuccessMessage(t('mailbox.deleteSavedSuccess')); } else { - // 如果删除失败,可以给用户一个提示 - alert(t('mailbox.deleteFailed')); + // feat: 删除失败提示 + showErrorMessage(t('mailbox.deleteFailed')); } } }; - // [feat]: 清空所有已保存的邮箱(包括后端数据) + // 清空所有已保存的邮箱 const handleClearAllMailboxes = async () => { if (window.confirm(t('mailbox.confirmClearAllMailboxes'))) { // 找出所有需要删除的邮箱(即列表中,非当前正在使用的邮箱) @@ -136,7 +142,8 @@ const MailboxSwitcher: React.FC = ({ // 并行执行所有后端删除请求 const deletePromises = mailboxesToDelete.map(m => apiDeleteMailbox(m.address)); - await Promise.all(deletePromises); + // feat: 使用 Promise.allSettled 来处理部分失败的情况 + const results = await Promise.allSettled(deletePromises); // 从前端列表中只保留当前使用的邮箱 const currentMailboxToKeep = savedMailboxes.find(m => m.address === currentMailbox.address); @@ -146,6 +153,14 @@ const MailboxSwitcher: React.FC = ({ setSavedMailboxes(mailboxesToKeep); localStorage.setItem('savedMailboxes', JSON.stringify(mailboxesToKeep)); setShowDropdown(false); + + // feat: 根据结果显示不同的通知 + const failedCount = results.filter(r => r.status === 'rejected').length; + if (failedCount > 0) { + showErrorMessage(t('mailbox.clearAllFailed', { count: failedCount })); + } else { + showSuccessMessage(t('mailbox.clearAllSuccess')); + } } }; diff --git a/frontend/src/contexts/MailboxContext.tsx b/frontend/src/contexts/MailboxContext.tsx index 8f39da1..9e85fc5 100644 --- a/frontend/src/contexts/MailboxContext.tsx +++ b/frontend/src/contexts/MailboxContext.tsx @@ -33,13 +33,16 @@ interface MailboxContextType { setAutoRefresh: (autoRefresh: boolean) => void; createNewMailbox: () => Promise; deleteMailbox: () => Promise; - refreshEmails: () => Promise; + refreshEmails: (isManual?: boolean) => Promise; // feat: 添加一个参数以区分手动刷新 emailCache: EmailCache; addToEmailCache: (emailId: string, email: Email, attachments: any[]) => void; clearEmailCache: () => void; handleMailboxNotFound: () => Promise; errorMessage: string | null; successMessage: string | null; + // feat: 添加用于显示全局通知的函数 + showSuccessMessage: (message: string) => void; + showErrorMessage: (message: string) => void; } export const MailboxContext = createContext({ @@ -62,7 +65,10 @@ export const MailboxContext = createContext({ clearEmailCache: () => {}, handleMailboxNotFound: async () => {}, errorMessage: null, - successMessage: null + successMessage: null, + // feat: 提供默认空函数 + showSuccessMessage: () => {}, + showErrorMessage: () => {}, }); interface MailboxProviderProps { @@ -83,6 +89,29 @@ export const MailboxProvider: React.FC = ({ children }) => const errorTimeoutRef = useRef(null); const successTimeoutRef = useRef(null); + // feat: 创建显示成功消息的函数 + const showSuccessMessage = (message: string) => { + setSuccessMessage(message); + if (successTimeoutRef.current) { + window.clearTimeout(successTimeoutRef.current); + } + successTimeoutRef.current = window.setTimeout(() => { + setSuccessMessage(null); + }, 3000); + }; + + // feat: 创建显示错误消息的函数 + const showErrorMessage = (message: string) => { + setErrorMessage(message); + if (errorTimeoutRef.current) { + window.clearTimeout(errorTimeoutRef.current); + } + errorTimeoutRef.current = window.setTimeout(() => { + setErrorMessage(null); + }, 3000); + }; + + // 清除提示的定时器 useEffect(() => { return () => { @@ -119,33 +148,20 @@ export const MailboxProvider: React.FC = ({ children }) => // 清除之前的错误和成功信息 setErrorMessage(null); setSuccessMessage(null); - - console.log('createNewMailbox: Started'); setIsLoading(true); - - console.log('createNewMailbox: Calling createRandomMailbox...'); const result = await createRandomMailbox(); - console.log('createNewMailbox: createRandomMailbox result:', result); - if (result.success && result.mailbox) { - console.log('createNewMailbox: Setting new mailbox:', result.mailbox); setMailbox(result.mailbox); saveMailboxToLocalStorage(result.mailbox); // [fix]: 创建新邮箱后,清空旧的邮件列表和缓存 setEmails([]); setSelectedEmail(null); clearEmailCache(); + // feat: 创建新邮箱也给出提示 + showSuccessMessage(t('mailbox.createSuccess')); } else { - console.error('createNewMailbox: Failed to create mailbox:', result.error); - setErrorMessage(t('mailbox.createFailed')); - - // 3秒后清除错误信息 - if (errorTimeoutRef.current) { - window.clearTimeout(errorTimeoutRef.current); - } - errorTimeoutRef.current = window.setTimeout(() => { - setErrorMessage(null); - }, 3000); + // fix: 使用全局通知函数 + showErrorMessage(t('mailbox.createFailed')); throw new Error('Failed to create mailbox'); } } catch (error) { @@ -169,8 +185,8 @@ export const MailboxProvider: React.FC = ({ children }) => const result = await apiDeleteMailbox(mailbox.address); if (result.success) { - // 显示成功信息 - setSuccessMessage(t('mailbox.deleteSuccess')); + // fix: 使用全局通知函数 + showSuccessMessage(t('mailbox.deleteSuccess')); // 清除本地数据 setMailbox(null); @@ -179,51 +195,23 @@ export const MailboxProvider: React.FC = ({ children }) => removeMailboxFromLocalStorage(); clearEmailCache(); - // 3秒后清除成功信息 - if (successTimeoutRef.current) { - window.clearTimeout(successTimeoutRef.current); - } - successTimeoutRef.current = window.setTimeout(() => { - setSuccessMessage(null); - }, 3000); - // 创建新邮箱 await createNewMailbox(); } else { - // 显示错误信息 - setErrorMessage(t('mailbox.deleteFailed')); - - // 3秒后清除错误信息 - if (errorTimeoutRef.current) { - window.clearTimeout(errorTimeoutRef.current); - } - errorTimeoutRef.current = window.setTimeout(() => { - setErrorMessage(null); - }, 3000); + // fix: 使用全局通知函数 + showErrorMessage(t('mailbox.deleteFailed')); } } catch (error) { console.error('Error deleting mailbox:', error); - // 显示错误信息 - setErrorMessage(t('mailbox.deleteFailed')); - - // 3秒后清除错误信息 - if (errorTimeoutRef.current) { - window.clearTimeout(errorTimeoutRef.current); - } - errorTimeoutRef.current = window.setTimeout(() => { - setErrorMessage(null); - }, 3000); + // fix: 使用全局通知函数 + showErrorMessage(t('mailbox.deleteFailed')); } }; - // 刷新邮件列表 - const refreshEmails = async () => { - if (!mailbox) return; - - // 防止重复请求 - if (isEmailsLoading) return; - + // feat: 增加 isManual 参数,只有手动点击刷新时才显示Toast + const refreshEmails = async (isManual = false) => { + if (!mailbox || isEmailsLoading) return; setIsEmailsLoading(true); try { @@ -231,13 +219,25 @@ export const MailboxProvider: React.FC = ({ children }) => if (result.success) { setEmails(result.emails); + // feat: 手动刷新成功时显示Toast + if (isManual) { + showSuccessMessage(t('email.refreshSuccess')); + } } else if (result.notFound) { // [fix]: 如果邮箱不存在,调用 handleMailboxNotFound 进行平滑处理,而不是强制刷新页面 await handleMailboxNotFound(); + } else { + // feat: 刷新失败时也显示Toast + if (isManual) { + showErrorMessage(t('email.fetchFailed')); + } } } catch (error) { // 错误处理 console.error('Error refreshing emails:', error); + if (isManual) { + showErrorMessage(t('email.fetchFailed')); + } } finally { setIsEmailsLoading(false); } @@ -246,14 +246,10 @@ export const MailboxProvider: React.FC = ({ children }) => // 自动刷新邮件 useEffect(() => { if (!mailbox || isLoading) return; - - // 首次加载邮件(无论autoRefresh是否开启) - refreshEmails(); - - // 如果自动刷新开启,则设置定时器 + refreshEmails(); // 初始加载不显示 a Toast let intervalId: number | undefined; if (autoRefresh) { - intervalId = window.setInterval(refreshEmails, AUTO_REFRESH_INTERVAL); + intervalId = window.setInterval(() => refreshEmails(), AUTO_REFRESH_INTERVAL); // 自动刷新不显示 a Toast } return () => { @@ -265,8 +261,8 @@ export const MailboxProvider: React.FC = ({ children }) => // [fix]: 重构处理邮箱不存在的逻辑,避免页面刷新 const handleMailboxNotFound = async () => { - // 设置提示信息,告知用户正在创建新邮箱 - setSuccessMessage(t('mailbox.creatingNew')); + // fix: 使用全局通知函数 + showSuccessMessage(t('mailbox.creatingNew')); // 清除当前无效的邮箱信息 removeMailboxFromLocalStorage(); @@ -274,14 +270,6 @@ export const MailboxProvider: React.FC = ({ children }) => // 异步创建新邮箱,并更新应用状态 await createNewMailbox(); - - // 3秒后清除提示信息 - if (successTimeoutRef.current) { - window.clearTimeout(successTimeoutRef.current); - } - successTimeoutRef.current = window.setTimeout(() => { - setSuccessMessage(null); - }, 3000); }; // 添加邮件到缓存 @@ -376,10 +364,13 @@ export const MailboxProvider: React.FC = ({ children }) => clearEmailCache, handleMailboxNotFound, errorMessage, - successMessage + successMessage, + // feat: 将函数添加到 context value 中 + showSuccessMessage, + showErrorMessage, }} > - {/* [feat] 将错误和成功提示移至此处,作为全局通知组件 */} + {/* [feat] 全局通知组件 */} {(errorMessage || successMessage) && (
{errorMessage || successMessage} diff --git a/frontend/src/pages/MailboxPage.tsx b/frontend/src/pages/MailboxPage.tsx index 4042697..5c792f2 100644 --- a/frontend/src/pages/MailboxPage.tsx +++ b/frontend/src/pages/MailboxPage.tsx @@ -15,28 +15,15 @@ const MailboxPage: React.FC = () => { emails, isEmailsLoading, autoRefresh, - setAutoRefresh + setAutoRefresh, + // feat: 从 context 中获取全局通知函数 + showSuccessMessage, + showErrorMessage } = useContext(MailboxContext); const [mailbox, setMailbox] = useState(null); const [selectedEmail, setSelectedEmail] = useState(null); const [isLoading, setIsLoading] = useState(true); - const [errorMessage, setErrorMessage] = useState(null); - const [successMessage, setSuccessMessage] = useState(null); - const errorTimeoutRef = useRef(null); - const successTimeoutRef = useRef(null); - - // 清除提示的定时器 - useEffect(() => { - return () => { - if (errorTimeoutRef.current) { - window.clearTimeout(errorTimeoutRef.current); - } - if (successTimeoutRef.current) { - window.clearTimeout(successTimeoutRef.current); - } - }; - }, []); // 获取邮箱信息 useEffect(() => { @@ -49,13 +36,11 @@ const MailboxPage: React.FC = () => { if (!response.ok) { if (response.status === 404) { - setErrorMessage(t('mailbox.invalidAddress')); + // fix: 使用全局通知函数 + showErrorMessage(t('mailbox.invalidAddress')); // 3秒后导航到首页 - if (errorTimeoutRef.current) { - window.clearTimeout(errorTimeoutRef.current); - } - errorTimeoutRef.current = window.setTimeout(() => { + setTimeout(() => { navigate('/'); }, 3000); return; @@ -70,22 +55,15 @@ const MailboxPage: React.FC = () => { throw new Error(data.error || 'Unknown error'); } } catch (error) { - setErrorMessage(String(error)); - - // 3秒后清除错误信息 - if (errorTimeoutRef.current) { - window.clearTimeout(errorTimeoutRef.current); - } - errorTimeoutRef.current = window.setTimeout(() => { - setErrorMessage(null); - }, 3000); + // fix: 使用全局通知函数 + showErrorMessage(String(error)); } finally { setIsLoading(false); } }; fetchMailbox(); - }, [address, navigate, t]); + }, [address, navigate, t, showErrorMessage]); // 处理删除邮箱 const handleDeleteMailbox = async () => { @@ -102,39 +80,25 @@ const MailboxPage: React.FC = () => { const data = await response.json(); if (data.success) { - setSuccessMessage(t('mailbox.deleteSuccess')); + // fix: 使用全局通知函数 + showSuccessMessage(t('mailbox.deleteSuccess')); // 2秒后导航到首页 - if (successTimeoutRef.current) { - window.clearTimeout(successTimeoutRef.current); - } - successTimeoutRef.current = window.setTimeout(() => { + setTimeout(() => { navigate('/'); }, 2000); } else { throw new Error(data.error || 'Unknown error'); } } catch (error) { - setErrorMessage(t('mailbox.deleteFailed')); - - // 3秒后清除错误信息 - if (errorTimeoutRef.current) { - window.clearTimeout(errorTimeoutRef.current); - } - errorTimeoutRef.current = window.setTimeout(() => { - setErrorMessage(null); - }, 3000); + // fix: 使用全局通知函数 + showErrorMessage(t('mailbox.deleteFailed')); } }; return (
- {/* 错误和成功提示 */} - {(errorMessage || successMessage) && ( -
- {errorMessage || successMessage} -
- )} + {/* fix: 移除局部的错误和成功提示 */} {mailbox && (