8 files changed
+151
-339
No files matched your search
@@ -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",
|
||||
|
||||
@@ -57,14 +57,19 @@
|
||||
"notFound": "邮箱不存在或已过期",
|
||||
"creatingNew": "正在创建新邮箱...",
|
||||
"switch": "切换邮箱",
|
||||
"switchSuccess": "邮箱切换成功",
|
||||
"savedMailboxes": "已保存的邮箱",
|
||||
"confirmDeleteMailbox": "您确定要删除这个已保存的邮箱吗?",
|
||||
"deleteSavedSuccess": "已保存的邮箱删除成功",
|
||||
"confirmClearAllMailboxes": "您确定要清空所有已保存的邮箱吗?(当前使用的邮箱不会被删除)",
|
||||
"clearAll": "全部清除"
|
||||
"clearAll": "全部清除",
|
||||
"clearAllSuccess": "已清空所有保存的邮箱",
|
||||
"clearAllFailed": "部分邮箱清空失败 ({{count}}个)"
|
||||
},
|
||||
"email": {
|
||||
"inbox": "收件箱",
|
||||
"refresh": "刷新邮件",
|
||||
"refreshSuccess": "邮件列表已刷新",
|
||||
"autoRefresh": "自动刷新",
|
||||
"autoRefreshOn": "自动刷新: 开",
|
||||
"autoRefreshOff": "自动刷新: 关",
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -62,7 +62,8 @@ const EmailList: React.FC<EmailListProps> = ({
|
||||
};
|
||||
|
||||
const handleRefresh = () => {
|
||||
refreshEmails();
|
||||
// feat: 调用 context 中的 refreshEmails,并传入 true 表示是手动刷新
|
||||
refreshEmails(true);
|
||||
};
|
||||
|
||||
const toggleAutoRefresh = () => {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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'));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user