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/HeaderMailbox.tsx b/frontend/src/components/HeaderMailbox.tsx index 212b791..37233e0 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'; // feat: 导入 useContext import { useTranslation } from 'react-i18next'; import { createRandomMailbox, createCustomMailbox } from '../utils/api'; import MailboxSwitcher from './MailboxSwitcher'; +import { MailboxContext } from '../contexts/MailboxContext'; // feat: 导入 MailboxContext interface HeaderMailboxProps { mailbox: Mailbox | null; @@ -19,20 +20,18 @@ const HeaderMailbox: React.FC = ({ isLoading }) => { const { t } = useTranslation(); + // feat: 从 context 中获取全局通知函数 + 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); + // fix: 移除独立的错误状态,因为将使用全局通知 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(() => { @@ -48,12 +47,6 @@ const HeaderMailbox: React.FC = ({ if (refreshSuccessTimeoutRef.current) { window.clearTimeout(refreshSuccessTimeoutRef.current); } - if (successMessageTimeoutRef.current) { - window.clearTimeout(successMessageTimeoutRef.current); - } - if (errorMessageTimeoutRef.current) { - window.clearTimeout(errorMessageTimeoutRef.current); - } }; }, []); @@ -61,13 +54,10 @@ const HeaderMailbox: React.FC = ({ // 复制邮箱地址到剪贴板 const copyToClipboard = () => { - // 清除之前的错误信息 - setCopyError(null); - const fullAddress = mailbox.address.includes('@') ? mailbox.address : `${mailbox.address}@${selectedDomain}`; navigator.clipboard.writeText(fullAddress) .then(() => { - // 显示复制成功提示 + // 显示复制成功提示 (Tooltip 形式,保留局部状态) setShowCopyTooltip(true); // 2秒后隐藏提示 @@ -79,24 +69,13 @@ const HeaderMailbox: React.FC = ({ }, 2000); }) .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); @@ -104,7 +83,7 @@ const HeaderMailbox: React.FC = ({ if (result.success && result.mailbox) { onMailboxChange(result.mailbox); - // 显示更新成功提示 + // 显示更新成功提示 (Tooltip 形式,保留局部状态) setShowRefreshSuccess(true); // 3秒后隐藏提示 @@ -115,16 +94,8 @@ const HeaderMailbox: React.FC = ({ setShowRefreshSuccess(false); }, 3000); } 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 +103,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,18 +117,13 @@ const HeaderMailbox: React.FC = ({ if (result.success && result.mailbox) { onMailboxChange(result.mailbox); - - // 显示成功消息在表单下方 - setCustomAddressSuccess(t('mailbox.createSuccess')); + // fix: 使用全局通知函数显示成功 + showSuccessMessage(t('mailbox.createSuccess')); // 3秒后自动关闭表单 - if (successMessageTimeoutRef.current) { - window.clearTimeout(successMessageTimeoutRef.current); - } - successMessageTimeoutRef.current = window.setTimeout(() => { + setTimeout(() => { setIsCustomMode(false); setCustomAddress(''); - setCustomAddressSuccess(null); }, 3000); } else { // 检查错误信息是否包含"邮箱地址已存在" @@ -167,9 +132,11 @@ const HeaderMailbox: React.FC = ({ String(result.error).includes('已存在'); if (isAddressExistsError) { + // fix: 对于表单内校验错误,保留局部状态提示 setCustomAddressError(t('mailbox.addressExists')); } else { - setCustomAddressError(t('mailbox.createFailed')); + // fix: 对于通用创建失败,使用全局通知 + showErrorMessage(t('mailbox.createFailed')); } } }; @@ -179,7 +146,6 @@ const HeaderMailbox: React.FC = ({ setIsCustomMode(false); setCustomAddress(''); setCustomAddressError(null); - setCustomAddressSuccess(null); }; // 移动设备上的邮箱地址显示 @@ -222,7 +188,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 +239,6 @@ const HeaderMailbox: React.FC = ({
)} - {/* 成功信息显示 */} - {customAddressSuccess && ( -
- {customAddressSuccess} -
- )} ) : ( <> @@ -360,12 +319,6 @@ const HeaderMailbox: React.FC = ({
- {/* 错误信息显示 */} - {(copyError || refreshError) && ( -
- {copyError || refreshError} -
- )} {/* 移动版显示 */} @@ -433,13 +386,6 @@ const HeaderMailbox: React.FC = ({ - - {/* 错误信息显示 */} - {(copyError || refreshError) && ( -
- {copyError || refreshError} -
- )} )} diff --git a/frontend/src/contexts/MailboxContext.tsx b/frontend/src/contexts/MailboxContext.tsx index 8f39da1..00a65c8 100644 --- a/frontend/src/contexts/MailboxContext.tsx +++ b/frontend/src/contexts/MailboxContext.tsx @@ -40,6 +40,9 @@ interface MailboxContextType { 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 () => { @@ -137,15 +166,8 @@ export const MailboxProvider: React.FC = ({ children }) => clearEmailCache(); } 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 +191,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,41 +201,17 @@ 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')); } }; @@ -265,8 +263,8 @@ export const MailboxProvider: React.FC = ({ children }) => // [fix]: 重构处理邮箱不存在的逻辑,避免页面刷新 const handleMailboxNotFound = async () => { - // 设置提示信息,告知用户正在创建新邮箱 - setSuccessMessage(t('mailbox.creatingNew')); + // fix: 使用全局通知函数 + showSuccessMessage(t('mailbox.creatingNew')); // 清除当前无效的邮箱信息 removeMailboxFromLocalStorage(); @@ -274,14 +272,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 +366,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 && (