video-flow-b/app/layout.tsx
2025-10-18 21:26:37 +08:00

116 lines
4.3 KiB
TypeScript

'use client'
import './globals.css';
import '@/iconfont/iconfont.css';
import { createContext, useContext, useEffect, useState } from 'react';
import { usePathname } from 'next/navigation';
import { Providers } from '@/components/providers';
import { ConfigProvider, theme } from 'antd';
import CallbackModal from '@/components/common/CallbackModal';
import { useAppStartupAnalytics } from '@/hooks/useAppStartupAnalytics';
import { gaEnabled, gaMeasurementId, baseUrl } from '@/lib/env';
// 创建上下文来传递弹窗控制方法
const CallbackModalContext = createContext<{
setShowCallbackModal: (show: boolean) => void
} | null>(null)
// Hook 来使用弹窗控制方法
export const useCallbackModal = () => {
const context = useContext(CallbackModalContext)
if (!context) {
throw new Error('useCallbackModal must be used within CallbackModalProvider')
}
return context
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
const [showCallbackModal, setShowCallbackModal] = useState(false)
const [paymentType, setPaymentType] = useState<'subscription' | 'token'>('subscription')
const pathname = usePathname()
const canonicalHref = `https://movieflow.ai${pathname || ''}`
// 应用启动时设置用户GA属性
useAppStartupAnalytics();
const openCallback = async function (ev: MessageEvent<any>) {
if (ev.data.type === 'waiting-payment') {
setPaymentType(ev.data.paymentType || 'subscription')
setShowCallbackModal(true)
}
}
useEffect(() => {
window.addEventListener('message', openCallback)
return () => {
window.removeEventListener('message', openCallback)
}
}, [])
return (
<html lang="en" suppressHydrationWarning>
<head>
<title>MovieFlow - AI Movie Studio</title>
<meta name="description" content="Share your story, or just a few words, and our AI turns it into a great film. We remove the barriers to creation. At MovieFlow, everyone is a movie master." />
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
<link rel="icon" type="image/x-icon" sizes="16x16" href="/favicon.ico?v=1" />
<link rel="icon" type="image/x-icon" sizes="32x32" href="/favicon.ico?v=1" />
<link rel="canonical" href={canonicalHref} />
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico?v=1" />
<link rel="apple-touch-icon" sizes="180x180" href="/favicon.ico?v=1" />
{gaEnabled && (
<>
<script async src={`https://www.googletagmanager.com/gtag/js?id=${gaMeasurementId}`}></script>
<script
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){window.dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${gaMeasurementId}', {
page_title: document.title,
page_location: window.location.href,
send_page_view: true
});
`,
}}
/>
</>
)}
</head>
<body className="font-sans antialiased">
<ConfigProvider
theme={{
algorithm: theme.darkAlgorithm,
token: {
// 自定义暗色主题颜色
colorBgContainer: '#1B1B1B',
colorBgElevated: '#1B1B1B',
colorBgMask: 'rgba(0, 0, 0, 0.6)',
borderRadius: 16,
},
components: {
Message: {
colorBgElevated: '#1f1f1f', // 自定义消息背景色
colorText: '#ffffff', // 自定义文字颜色
borderRadius: 8, // 自定义圆角
},
},
}}
>
<CallbackModalContext.Provider value={{ setShowCallbackModal }}>
<Providers>
{/* <ScreenAdapter /> */}
<div id="app" className='h-full w-full'>
{children}
{showCallbackModal && <CallbackModal paymentType={paymentType} onClose={() => setShowCallbackModal(false)} />}
</div>
</Providers>
</CallbackModalContext.Provider>
</ConfigProvider>
</body>
</html>
)
}