返回 AiToEarn
1 /**
2 * DouyinLaunchModal - 移动端抖音唤起引导弹窗
3 * 引导用户点击按钮打开抖音 App 完成发布
4 * 使用多策略唤起(iframe + location.href + 超时降级)
5 */
6 'use client'
7
8 import { Loader2 } from 'lucide-react'
9 import { useCallback, useState } from 'react'
10 import { PlatType } from '@/app/config/platConfig'
11 import { useTransClient } from '@/app/i18n/client'
12 import { PlatformIcon } from '@/components/common/PlatformIcon'
13 import { Button } from '@/components/ui/button'
14 import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'
15 import { openApp } from '@/utils/appLaunch'
16
17 type LaunchState = 'idle' | 'launching' | 'failed'
18
19 interface DouyinLaunchModalProps {
20 open: boolean
21 permalink: string
22 onClose: () => void
23 }
24
25 export function DouyinLaunchModal({ open, permalink, onClose }: DouyinLaunchModalProps) {
26 const { t } = useTransClient('publish')
27 const [launchState, setLaunchState] = useState<LaunchState>('idle')
28
29 const handleLaunch = useCallback(() => {
30 setLaunchState('launching')
31 openApp(permalink, () => setLaunchState('failed'))
32 }, [permalink])
33
34 const handleOpenChange = useCallback((isOpen: boolean) => {
35 if (!isOpen) {
36 setLaunchState('idle')
37 onClose()
38 }
39 }, [onClose])
40
41 const isLaunching = launchState === 'launching'
42 const isFailed = launchState === 'failed'
43
44 return (
45 <Dialog open={open} onOpenChange={handleOpenChange}>
46 <DialogContent className="w-[90vw] max-w-[400px]">
47 <DialogHeader>
48 <DialogTitle className="text-center">
49 {t('douyin.mobileLaunchTitle')}
50 </DialogTitle>
51 </DialogHeader>
52
53 <div className="flex flex-col items-center gap-5 py-4">
54 <PlatformIcon platform={PlatType.Douyin} width={64} height={64} className="rounded-xl" />
55
56 <p className="text-sm text-muted-foreground text-center px-2">
57 {isFailed ? t('douyin.launchFailed') : t('douyin.mobileLaunchDesc')}
58 </p>
59
60 <Button
61 className="w-full cursor-pointer"
62 size="lg"
63 disabled={isLaunching}
64 onClick={isFailed ? handleLaunch : handleLaunch}
65 >
66 {isLaunching && <Loader2 className="animate-spin" />}
67 {isFailed ? t('douyin.retryOpen') : t('douyin.openApp')}
68 </Button>
69 </div>
70 </DialogContent>
71 </Dialog>
72 )
73 }
74
74 lines Plain Text