Merge pull request #25 from gujiangjiang/main

feat: 集中实现全局Toast通知功能
This commit is contained in:
阿杰鲁 authored and GitHub committed 2025-10-10 10:47:19 +08:00
commit ccfa621805
8 files changed
+151 -339

No files matched your search

+6 -1
View File
@@ -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",
+6 -1
View File
@@ -57,14 +57,19 @@
"notFound": "邮箱不存在或已过期",
"creatingNew": "正在创建新邮箱...",
"switch": "切换邮箱",
"switchSuccess": "邮箱切换成功",
"savedMailboxes": "已保存的邮箱",
"confirmDeleteMailbox": "您确定要删除这个已保存的邮箱吗?",
"deleteSavedSuccess": "已保存的邮箱删除成功",
"confirmClearAllMailboxes": "您确定要清空所有已保存的邮箱吗?(当前使用的邮箱不会被删除)",
"clearAll": "全部清除"
"clearAll": "全部清除",
"clearAllSuccess": "已清空所有保存的邮箱",
"clearAllFailed": "部分邮箱清空失败 ({{count}}个)"
},
"email": {
"inbox": "收件箱",
"refresh": "刷新邮件",
"refreshSuccess": "邮件列表已刷新",
"autoRefresh": "自动刷新",
"autoRefreshOn": "自动刷新: 开",
"autoRefreshOff": "自动刷新: 关",
+11 -56
View File
@@ -21,35 +21,16 @@ interface Attachment {
const EmailDetail: React.FC<EmailDetailProps> = ({ 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<Email | null>(null);
const [attachments, setAttachments] = useState<Attachment[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [isLoadingAttachments, setIsLoadingAttachments] = useState(false);
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const [successMessage, setSuccessMessage] = useState<string | null>(null);
const errorTimeoutRef = useRef<number | null>(null);
const successTimeoutRef = useRef<number | null>(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<EmailDetailProps> = ({ 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<EmailDetailProps> = ({ 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<EmailDetailProps> = ({ 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<EmailDetailProps> = ({ emailId, onClose }) => {
return (
<div className="border rounded-lg p-6">
{/* 错误和成功提示 */}
{(errorMessage || successMessage) && (
<div className={`p-3 mb-4 rounded-md ${errorMessage ? 'bg-red-100 text-red-800' : 'bg-green-100 text-green-800'}`}>
{errorMessage || successMessage}
</div>
)}
{/* fix: 移除局部的错误和成功提示,使用全局通知 */}
{isLoading ? (
<div className="flex items-center justify-center h-64">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary"></div>
+2 -1
View File
@@ -62,7 +62,8 @@ const EmailList: React.FC<EmailListProps> = ({
};
const handleRefresh = () => {
refreshEmails();
// feat: 调用 context 中的 refreshEmails,并传入 true 表示是手动刷新
refreshEmails(true);
};
const toggleAutoRefresh = () => {
+23 -147
View File
@@ -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<HeaderMailboxProps> = ({
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<string | null>(null);
const [refreshError, setRefreshError] = useState<string | null>(null);
const [customAddressError, setCustomAddressError] = useState<string | null>(null);
const [customAddressSuccess, setCustomAddressSuccess] = useState<string | null>(null);
const [showRefreshSuccess, setShowRefreshSuccess] = useState(false);
const copyTooltipTimeoutRef = useRef<number | null>(null);
const refreshSuccessTimeoutRef = useRef<number | null>(null);
const successMessageTimeoutRef = useRef<number | null>(null);
const errorMessageTimeoutRef = useRef<number | null>(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<HeaderMailboxProps> = ({
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<HeaderMailboxProps> = ({
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);
} else {
// 检查错误信息是否包含"邮箱地址已存在"
const isAddressExistsError =
result.error === 'Address already exists' ||
String(result.error).includes('已存在');
}, 1500);
} else {
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<HeaderMailboxProps> = ({
setIsCustomMode(false);
setCustomAddress('');
setCustomAddressError(null);
setCustomAddressSuccess(null);
};
// 移动设备上的邮箱地址显示
@@ -222,7 +150,6 @@ const HeaderMailbox: React.FC<HeaderMailboxProps> = ({
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<HeaderMailboxProps> = ({
</div>
)}
{/* 成功信息显示 */}
{customAddressSuccess && (
<div className="text-green-500 text-xs px-1">
{customAddressSuccess}
</div>
)}
</form>
) : (
<>
@@ -321,14 +242,6 @@ const HeaderMailbox: React.FC<HeaderMailboxProps> = ({
>
<i className="fas fa-copy text-sm"></i>
</button>
{/* 复制成功提示 */}
{showCopyTooltip && (
<div className="absolute top-9 left-1/2 transform -translate-x-1/2 bg-primary text-primary-foreground text-xs py-1 px-2 rounded shadow-lg whitespace-nowrap z-10">
{t('mailbox.copySuccess')}
<div className="absolute -top-1 left-1/2 transform -translate-x-1/2 w-2 h-2 bg-primary rotate-45"></div>
</div>
)}
</div>
<div className="relative">
@@ -340,14 +253,6 @@ const HeaderMailbox: React.FC<HeaderMailboxProps> = ({
>
<i className="fas fa-sync-alt text-sm"></i>
</button>
{/* 更新成功提示 */}
{showRefreshSuccess && (
<div className="absolute top-9 left-1/2 transform -translate-x-1/2 bg-primary text-primary-foreground text-xs py-1 px-2 rounded shadow-lg whitespace-nowrap z-10">
{t('mailbox.refreshSuccess')}
<div className="absolute -top-1 left-1/2 transform -translate-x-1/2 w-2 h-2 bg-primary rotate-45"></div>
</div>
)}
</div>
<button
@@ -360,12 +265,6 @@ const HeaderMailbox: React.FC<HeaderMailboxProps> = ({
</button>
</div>
{/* 错误信息显示 */}
{(copyError || refreshError) && (
<div className="text-red-500 text-xs mt-1">
{copyError || refreshError}
</div>
)}
</div>
{/* 移动版显示 */}
@@ -392,14 +291,6 @@ const HeaderMailbox: React.FC<HeaderMailboxProps> = ({
>
<i className="fas fa-copy text-xs"></i>
</button>
{/* 复制成功提示 */}
{showCopyTooltip && (
<div className="absolute top-7 left-1/2 transform -translate-x-1/2 bg-primary text-primary-foreground text-xs py-1 px-2 rounded shadow-lg whitespace-nowrap z-10">
{t('mailbox.copySuccess')}
<div className="absolute -top-1 left-1/2 transform -translate-x-1/2 w-2 h-2 bg-primary rotate-45"></div>
</div>
)}
</div>
</div>
@@ -413,14 +304,6 @@ const HeaderMailbox: React.FC<HeaderMailboxProps> = ({
>
<i className="fas fa-sync-alt text-xs"></i>
</button>
{/* 更新成功提示 */}
{showRefreshSuccess && (
<div className="absolute top-7 left-1/2 transform -translate-x-1/2 bg-primary text-primary-foreground text-xs py-1 px-2 rounded shadow-lg whitespace-nowrap z-10">
{t('mailbox.refreshSuccess')}
<div className="absolute -top-1 left-1/2 transform -translate-x-1/2 w-2 h-2 bg-primary rotate-45"></div>
</div>
)}
</div>
<button
@@ -433,13 +316,6 @@ const HeaderMailbox: React.FC<HeaderMailboxProps> = ({
</button>
</div>
</div>
{/* 错误信息显示 */}
{(copyError || refreshError) && (
<div className="text-red-500 text-xs mt-1">
{copyError || refreshError}
</div>
)}
</>
)}
</div>
+22 -7
View File
@@ -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<MailboxSwitcherProps> = ({
domain
}) => {
const { t } = useTranslation();
// feat: 从 context 中获取全局通知函数
const { showSuccessMessage, showErrorMessage } = useContext(MailboxContext);
const [savedMailboxes, setSavedMailboxes] = useState<Mailbox[]>([]);
const [showDropdown, setShowDropdown] = useState(false);
const dropdownRef = useRef<HTMLDivElement>(null);
@@ -103,9 +105,11 @@ const MailboxSwitcher: React.FC<MailboxSwitcherProps> = ({
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<MailboxSwitcherProps> = ({
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<MailboxSwitcherProps> = ({
// 并行执行所有后端删除请求
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<MailboxSwitcherProps> = ({
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'));
}
}
};
+65 -74
View File
@@ -33,13 +33,16 @@ interface MailboxContextType {
setAutoRefresh: (autoRefresh: boolean) => void;
createNewMailbox: () => Promise<void>;
deleteMailbox: () => Promise<void>;
refreshEmails: () => Promise<void>;
refreshEmails: (isManual?: boolean) => Promise<void>; // feat: 添加一个参数以区分手动刷新
emailCache: EmailCache;
addToEmailCache: (emailId: string, email: Email, attachments: any[]) => void;
clearEmailCache: () => void;
handleMailboxNotFound: () => Promise<void>;
errorMessage: string | null;
successMessage: string | null;
// feat: 添加用于显示全局通知的函数
showSuccessMessage: (message: string) => void;
showErrorMessage: (message: string) => void;
}
export const MailboxContext = createContext<MailboxContextType>({
@@ -62,7 +65,10 @@ export const MailboxContext = createContext<MailboxContextType>({
clearEmailCache: () => {},
handleMailboxNotFound: async () => {},
errorMessage: null,
successMessage: null
successMessage: null,
// feat: 提供默认空函数
showSuccessMessage: () => {},
showErrorMessage: () => {},
});
interface MailboxProviderProps {
@@ -83,6 +89,29 @@ export const MailboxProvider: React.FC<MailboxProviderProps> = ({ children }) =>
const errorTimeoutRef = useRef<number | null>(null);
const successTimeoutRef = useRef<number | null>(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<MailboxProviderProps> = ({ 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<MailboxProviderProps> = ({ 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<MailboxProviderProps> = ({ 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<MailboxProviderProps> = ({ 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<MailboxProviderProps> = ({ 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<MailboxProviderProps> = ({ children }) =>
// [fix]: 重构处理邮箱不存在的逻辑,避免页面刷新
const handleMailboxNotFound = async () => {
// 设置提示信息,告知用户正在创建新邮箱
setSuccessMessage(t('mailbox.creatingNew'));
// fix: 使用全局通知函数
showSuccessMessage(t('mailbox.creatingNew'));
// 清除当前无效的邮箱信息
removeMailboxFromLocalStorage();
@@ -274,14 +270,6 @@ export const MailboxProvider: React.FC<MailboxProviderProps> = ({ 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<MailboxProviderProps> = ({ children }) =>
clearEmailCache,
handleMailboxNotFound,
errorMessage,
successMessage
successMessage,
// feat: 将函数添加到 context value 中
showSuccessMessage,
showErrorMessage,
}}
>
{/* [feat] 将错误和成功提示移至此处,作为全局通知组件 */}
{/* [feat] 全局通知组件 */}
{(errorMessage || successMessage) && (
<div className="fixed top-4 right-4 z-50 p-3 rounded-md shadow-lg max-w-md" style={{ backgroundColor: errorMessage ? '#FEE2E2' : '#ECFDF5', color: errorMessage ? '#991B1B' : '#065F46' }}>
{errorMessage || successMessage}
+16 -52
View File
@@ -15,28 +15,15 @@ const MailboxPage: React.FC = () => {
emails,
isEmailsLoading,
autoRefresh,
setAutoRefresh
setAutoRefresh,
// feat: 从 context 中获取全局通知函数
showSuccessMessage,
showErrorMessage
} = useContext(MailboxContext);
const [mailbox, setMailbox] = useState<Mailbox | null>(null);
const [selectedEmail, setSelectedEmail] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const [successMessage, setSuccessMessage] = useState<string | null>(null);
const errorTimeoutRef = useRef<number | null>(null);
const successTimeoutRef = useRef<number | null>(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 (
<div className="flex flex-col space-y-6">
{/* 错误和成功提示 */}
{(errorMessage || successMessage) && (
<div className={`p-3 rounded-md ${errorMessage ? 'bg-red-100 text-red-800' : 'bg-green-100 text-green-800'}`}>
{errorMessage || successMessage}
</div>
)}
{/* fix: 移除局部的错误和成功提示 */}
{mailbox && (
<MailboxInfo