You've already forked Nano-Banana-AI-Image-Editor
修复(ui): 解决Toast通知不正常关闭的问题
- 修复Toast组件中鼠标悬停状态的延迟通知逻辑 - 重构ToastContext中的定时器管理机制,防止内存泄漏 - 优化Toast的hover状态同步,确保定时器正确暂停和恢复 - 增强cleanup机制,清理已删除Toast的残留定时器 - 解决Toast在指定时间后不自动关闭的问题 修复内容: - Toast.tsx: 移除鼠标离开后的1秒延迟,立即通知状态变化 - ToastContext.tsx: 改进定时器生命周期管理和清理逻辑 验证: - 构建成功通过 - 所有测试套件通过(34个测试) - Toast现在能正确按时关闭,悬停暂停功能正常
This commit is contained in:
@@ -42,10 +42,8 @@ export const Toast: React.FC<ToastProps> = ({ id, message, type, details, onClos
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleMouseLeave = () => {
|
const handleMouseLeave = () => {
|
||||||
// Set a timeout to mark as not hovered after 1 second
|
// Immediately mark as not hovered
|
||||||
hoverTimeoutRef.current = setTimeout(() => {
|
onHoverChange?.(false);
|
||||||
onHoverChange?.(false);
|
|
||||||
}, 1000);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
|
|||||||
@@ -50,6 +50,11 @@ export const ToastProvider: React.FC<{ children: React.ReactNode }> = ({ childre
|
|||||||
};
|
};
|
||||||
|
|
||||||
const removeToast = (id: string) => {
|
const removeToast = (id: string) => {
|
||||||
|
// Clear any existing timeout for this toast
|
||||||
|
if (hoverTimeouts.current[id]) {
|
||||||
|
clearTimeout(hoverTimeouts.current[id]);
|
||||||
|
delete hoverTimeouts.current[id];
|
||||||
|
}
|
||||||
dispatch({ type: 'REMOVE_TOAST', payload: id });
|
dispatch({ type: 'REMOVE_TOAST', payload: id });
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -59,36 +64,48 @@ export const ToastProvider: React.FC<{ children: React.ReactNode }> = ({ childre
|
|||||||
|
|
||||||
// Auto remove toasts after duration, but respect hover state
|
// Auto remove toasts after duration, but respect hover state
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const timers = toasts.map(toast => {
|
// Create a copy of current timeouts to track which ones we need to clear
|
||||||
|
const currentTimeouts = { ...hoverTimeouts.current };
|
||||||
|
|
||||||
|
toasts.forEach(toast => {
|
||||||
// Clear any existing timeout for this toast
|
// Clear any existing timeout for this toast
|
||||||
if (hoverTimeouts.current[toast.id]) {
|
if (currentTimeouts[toast.id]) {
|
||||||
clearTimeout(hoverTimeouts.current[toast.id]);
|
clearTimeout(currentTimeouts[toast.id]);
|
||||||
delete hoverTimeouts.current[toast.id];
|
delete currentTimeouts[toast.id];
|
||||||
}
|
}
|
||||||
|
|
||||||
// If toast is hovered, don't set a timer
|
// If toast is hovered, don't set a timer
|
||||||
if (toast.hovered) {
|
if (toast.hovered) {
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If duration is 0, it's persistent
|
// If duration is 0, it's persistent
|
||||||
if (toast.duration === 0) {
|
if (toast.duration === 0) {
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set timeout to remove toast
|
// Set timeout to remove toast
|
||||||
const timeout = setTimeout(() => {
|
const timeout = setTimeout(() => {
|
||||||
removeToast(toast.id);
|
removeToast(toast.id);
|
||||||
}, toast.duration);
|
}, toast.duration);
|
||||||
|
|
||||||
return { id: toast.id, timeout };
|
hoverTimeouts.current[toast.id] = timeout;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Cleanup function
|
// Clear any remaining timeouts for toasts that no longer exist
|
||||||
|
Object.entries(currentTimeouts).forEach(([id, timeout]) => {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
delete hoverTimeouts.current[id];
|
||||||
|
});
|
||||||
|
|
||||||
|
// Cleanup function for when component unmounts or toasts change
|
||||||
return () => {
|
return () => {
|
||||||
timers.forEach(timer => {
|
// Clear all active timeouts
|
||||||
if (timer) clearTimeout(timer.timeout);
|
Object.values(hoverTimeouts.current).forEach(timeout => {
|
||||||
|
clearTimeout(timeout);
|
||||||
});
|
});
|
||||||
|
// Reset the timeouts object
|
||||||
|
hoverTimeouts.current = {};
|
||||||
};
|
};
|
||||||
}, [toasts]);
|
}, [toasts]);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user