video-flow-b/components/QueueBox/QueueNotication.tsx
2025-10-10 17:35:18 +08:00

168 lines
6.1 KiB
TypeScript

'use client';
import React, { useEffect, useRef } from 'react';
import { createRoot, Root } from 'react-dom/client';
type QueueStatus = 'waiting' | 'process' | string;
interface H5QueueNotificationProps {
position: number;
estimatedMinutes: number;
status: QueueStatus;
onCancel?: () => void;
onRefresh?: () => void;
onClose?: () => void;
}
function QueueNotificationModal(props: H5QueueNotificationProps) {
const { position, estimatedMinutes, status, onCancel, onClose } = props;
const containerRef = useRef<HTMLDivElement | null>(null);
// 禁用 ESC 关闭,保留空 effect 结构便于未来拓展
useEffect(() => {
return () => {};
}, []);
useEffect(() => {
// Prevent background scroll on mobile while modal is open
const originalOverflow = document.body.style.overflow;
document.body.style.overflow = 'hidden';
return () => {
document.body.style.overflow = originalOverflow;
};
}, []);
const message = status === 'process'
? 'Your work is being produced. Please wait until it is completed before creating a new work.'
: `Your work is waiting for production at the ${position} position`;
return (
<div
ref={containerRef}
data-alt="queue-overlay"
className="fixed inset-0 z-[1000] flex items-center justify-center bg-black/60"
>
<div
data-alt="queue-modal"
className="relative w-11/12 max-w-sm rounded-2xl bg-white/5 border border-white/10 backdrop-blur-xl shadow-2xl p-6 text-white"
>
{/* 去除右上角关闭按钮,避免除取消以外的关闭路径 */}
<div data-alt="modal-header" className="flex flex-col items-center text-center gap-2">
<div data-alt="modal-icon" className="w-10 h-10 rounded-full bg-white/10 flex items-center justify-center text-lg">
🎬
</div>
<h3 data-alt="modal-title" className="text-base font-semibold">Queue Reminder</h3>
</div>
<div data-alt="modal-body" className="mt-4 space-y-4">
<p data-alt="queue-description" className="text-sm text-white/80">
{message}
</p>
{status !== 'process' && (
<div data-alt="stats-grid" className="grid grid-cols-2 gap-3">
<div data-alt="stat-position" className="rounded-lg bg-white/5 border border-white/10 p-3">
<div className="text-[11px] text-white/60">Position</div>
<div className="text-sm font-medium">#{position}</div>
</div>
<div data-alt="stat-eta" className="rounded-lg bg-white/5 border border-white/10 p-3">
<div className="text-[11px] text-white/60">ETA</div>
<div className="text-sm font-medium">~{estimatedMinutes} min</div>
</div>
</div>
)}
</div>
<div data-alt="modal-actions" className="mt-6 space-y-3">
<button
data-alt="refresh-button"
className="w-full px-4 py-2 rounded-lg bg-[#9144b0] text-white font-medium transition-colors"
onClick={() => {
// 刷新仅触发回调,不关闭弹窗
if (status !== 'process') {
props.onRefresh?.();
} else {
onCancel?.();
onClose?.();
}
}}
>
{status !== 'process' ? 'Refresh' : 'OK'}
</button>
{status !== 'process' && (
<button
data-alt="cancel-queue-button"
className="w-full text-sm text-[#ab50d0] underline underline-offset-2 decoration-[#9144b0]/60"
onClick={() => {
onCancel?.();
onClose?.();
}}
>
Cancel queue
</button>
)}
</div>
</div>
</div>
);
}
/**
* Opens a lightweight H5-styled dark modal queue notification.
* @param {number} position - Current queue position.
* @param {number} estimatedMinutes - Estimated waiting minutes.
* @param {QueueStatus} status - Queue status: 'waiting' | 'process' | string.
* @param {() => void} onCancel - Callback when user confirms cancel.
* @returns {() => void} - Close function to dismiss the modal programmatically.
*/
export function showQueueNotification(
position: number,
estimatedMinutes: number,
status: QueueStatus,
onCancel?: () => void,
onRefresh?: () => void
): () => void {
if (typeof window === 'undefined' || typeof document === 'undefined') {
return () => {};
}
const mount = document.createElement('div');
mount.setAttribute('data-alt', 'queue-root');
document.body.appendChild(mount);
let root: Root | null = null;
try {
root = createRoot(mount);
} catch {
// Fallback cleanup if root creation fails
document.body.removeChild(mount);
return () => {};
}
const close = () => {
try {
root?.unmount();
} finally {
if (mount.parentNode) {
mount.parentNode.removeChild(mount);
}
}
};
root.render(
<QueueNotificationModal
position={position}
estimatedMinutes={estimatedMinutes}
status={status}
onCancel={onCancel}
onRefresh={onRefresh}
onClose={close}
/>
);
return close;
}