diff --git a/.env.production b/.env.production index 24e6f58..1720734 100644 --- a/.env.production +++ b/.env.production @@ -1,16 +1,16 @@ # 测试 -# NEXT_PUBLIC_JAVA_URL = https://auth.test.movieflow.ai -# NEXT_PUBLIC_BASE_URL = https://77.smartvideo.py.qikongjian.com -# NEXT_PUBLIC_CUT_URL = https://77.smartcut.py.qikongjian.com -# NEXT_PUBLIC_GOOGLE_REDIRECT_URI=https://www.movieflow.net/api/auth/google/callback -# NEXT_PUBLIC_CUT_URL_TO = https://smartcut.huiying.video +NEXT_PUBLIC_JAVA_URL = https://auth.test.movieflow.ai +NEXT_PUBLIC_BASE_URL = https://77.smartvideo.py.qikongjian.com +NEXT_PUBLIC_CUT_URL = https://77.smartcut.py.qikongjian.com +NEXT_PUBLIC_GOOGLE_REDIRECT_URI=https://www.movieflow.net/api/auth/google/callback +NEXT_PUBLIC_CUT_URL_TO = https://smartcut.huiying.video # 生产 -NEXT_PUBLIC_JAVA_URL = https://auth.movieflow.ai -NEXT_PUBLIC_BASE_URL = https://api.video.movieflow.ai -NEXT_PUBLIC_CUT_URL = https://smartcut.api.movieflow.ai -NEXT_PUBLIC_GOOGLE_REDIRECT_URI=https://www.movieflow.ai/api/auth/google/callback -NEXT_PUBLIC_CUT_URL_TO = https://smartcut.movieflow.ai +# NEXT_PUBLIC_JAVA_URL = https://auth.movieflow.ai +# NEXT_PUBLIC_BASE_URL = https://api.video.movieflow.ai +# NEXT_PUBLIC_CUT_URL = https://smartcut.api.movieflow.ai +# NEXT_PUBLIC_GOOGLE_REDIRECT_URI=https://www.movieflow.ai/api/auth/google/callback +# NEXT_PUBLIC_CUT_URL_TO = https://smartcut.movieflow.ai # 通用 # 当前域名配置 NEXT_PUBLIC_FRONTEND_URL = https://www.movieflow.ai diff --git a/api/movie_queue.ts b/api/movie_queue.ts index 273da29..a20d442 100644 --- a/api/movie_queue.ts +++ b/api/movie_queue.ts @@ -1,5 +1,5 @@ import { ApiResponse } from './common'; -import { showQueueNotification } from '../components/QueueBox/QueueNotification2'; +import { showH5QueueNotification } from '../components/QueueBox/H5QueueNotication'; import { notification } from 'antd'; /** 队列状态枚举 */ @@ -66,6 +66,7 @@ export async function withQueuePolling( let attempts = 0; let lastNotificationPosition: number | undefined; let isCancelled = false; + let closeModal: (() => void) | null = null; // 生成唯一的轮询ID const pollId = Math.random().toString(36).substring(7); @@ -73,7 +74,8 @@ export async function withQueuePolling( // 创建取消函数 const cancel = () => { isCancelled = true; - notification.destroy(); // 关闭通知 + try { closeModal?.(); } catch {} + notification.destroy(); // 兼容旧弹层 onCancel?.(); cancelTokens.delete(pollId); }; @@ -100,7 +102,19 @@ export async function withQueuePolling( // 如果位置发生变化,更新通知 if ((position !== lastNotificationPosition || status === QueueStatus.PROCESS) && position !== undefined && waiting !== undefined) { - showQueueNotification(position, waiting, status, cancel); + // 打开或更新 H5 弹窗(仅允许 Cancel 关闭,Refresh 触发刷新) + try { closeModal?.(); } catch {} + closeModal = showH5QueueNotification( + position, + waiting, + status, + cancel, + async () => { + // 触发一次立刻刷新:重置 attempts 的等待,直接递归调用 poll() + // 不关闭弹窗,由 showH5QueueNotification 保持打开 + attempts = Math.max(0, attempts - 1); + } + ); lastNotificationPosition = position; } @@ -120,15 +134,18 @@ export async function withQueuePolling( // 如果状态为ready,结束轮询 if (response.code !== 202 && response.data) { - notification.destroy(); // 关闭通知 + try { closeModal?.(); } catch {} + notification.destroy(); // 兼容旧弹层 onSuccess?.(response.data); return response; } - notification.destroy(); // 关闭通知 + try { closeModal?.(); } catch {} + notification.destroy(); // 兼容旧弹层 return response; } catch (error) { - notification.destroy(); // 关闭通知 + try { closeModal?.(); } catch {} + notification.destroy(); // 兼容旧弹层 if (error instanceof Error) { onError?.(error); } diff --git a/app/globals.css b/app/globals.css index 0e918bf..32576a1 100644 --- a/app/globals.css +++ b/app/globals.css @@ -307,6 +307,6 @@ body { } } -textarea, input { +.mobile-textarea, .mobile-input { font-size: 16px !important; } \ No newline at end of file diff --git a/app/signup/page.tsx b/app/signup/page.tsx index 33808c6..157a35d 100644 --- a/app/signup/page.tsx +++ b/app/signup/page.tsx @@ -9,6 +9,7 @@ import { GoogleLoginButton } from "@/components/ui/google-login-button"; import { Eye, EyeOff, Mail, PartyPopper } from "lucide-react"; import { isGoogleLoginEnabled } from "@/lib/server-config"; import { fetchSettingByCode } from "@/api/serversetting"; +import { useDeviceType } from "@/hooks/useDeviceType"; export default function SignupPage() { const [name, setName] = useState(""); @@ -31,7 +32,7 @@ export default function SignupPage() { const [showGoogleLogin, setShowGoogleLogin] = useState(false); const [showRedirectModal, setShowRedirectModal] = useState(false); const router = useRouter(); - + const { isMobile } = useDeviceType(); // Handle scroll indicator for small screens and load SSO config React.useEffect(() => { try { @@ -448,7 +449,7 @@ export default function SignupPage() { onFocus={() => setEmailFocused(true)} onBlur={() => setEmailFocused(false)} required - className="w-full px-4 py-3 rounded-lg bg-black/30 border border-white/20 text-white placeholder-gray-400 focus:outline-none focus:ring-1 focus:border-custom-blue/80" + className={`w-full px-4 py-3 rounded-lg bg-black/30 border border-white/20 text-white placeholder-gray-400 focus:outline-none focus:ring-1 focus:border-custom-blue/80 ${isMobile ? 'mobile-input' : ''}`} /> @@ -473,7 +474,7 @@ export default function SignupPage() { required className={`w-full px-4 py-3 pr-12 rounded-lg bg-black/30 border border-white/20 text-white placeholder-gray-400 focus:outline-none focus:ring-1 focus:border-custom-blue/80 ${ passwordError ? "border-red-500/50" : "border-white/20" - }`} + } ${isMobile ? 'mobile-input' : ''}`} /> + {status !== 'process' && ( + + )} + + + + ); +} + +/** + * 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 showH5QueueNotification( + 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', 'h5-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( + + ); + + return close; +} + + diff --git a/components/SmartChatBox/InputBar.tsx b/components/SmartChatBox/InputBar.tsx index ecea67d..bdc3a3a 100644 --- a/components/SmartChatBox/InputBar.tsx +++ b/components/SmartChatBox/InputBar.tsx @@ -286,7 +286,7 @@ const { isMobile, isTablet, isDesktop } = useDeviceType();