返回 AiToEarn
1 /**
2 * VideoCreateDraftAction - 视频素材生成草稿操作
3 * 在视频素材卡片上提供从视频内容一键生成草稿的入口
4 */
5
6 'use client'
7
8 import type { MediaItem } from '@/api/materials/material.types'
9 import type { PlatType } from '@/app/config/platConfig'
10 import { Loader2, Sparkles } from 'lucide-react'
11 import { memo, useCallback, useMemo, useState } from 'react'
12 import { useShallow } from 'zustand/react/shallow'
13 import { PubType } from '@/app/config/publishConfig'
14 import { useTransClient } from '@/app/i18n/client'
15 import InlinePlatformSelector from '@/components/draft-box/components/CreateMaterialModal/InlinePlatformSelector'
16 import { Button } from '@/components/ui/button'
17 import {
18 Dialog,
19 DialogContent,
20 DialogDescription,
21 DialogFooter,
22 DialogHeader,
23 DialogTitle,
24 } from '@/components/ui/dialog'
25 import { useTaskPlatforms } from '@/hooks/usePlatformMetadata'
26 import { cn } from '@/utils/className'
27 import { toast } from '@/utils/ui/toast'
28 import { useMediaTabStore } from '../ContentTabs/mediaTabStore'
29
30 interface VideoCreateDraftActionProps {
31 media: MediaItem
32 groupId: string
33 }
34
35 export const VideoCreateDraftAction = memo(({ media, groupId }: VideoCreateDraftActionProps) => {
36 const { t } = useTransClient('material')
37 const { t: tCommon } = useTransClient('common')
38 const taskPlatforms = useTaskPlatforms()
39 const [dialogOpen, setDialogOpen] = useState(false)
40
41 const availablePlatforms = useMemo(() => {
42 return taskPlatforms
43 .filter(([_, info]) => info.pubTypes.has(PubType.VIDEO))
44 .map(([plat]) => plat)
45 }, [taskPlatforms])
46
47 const [selectedPlatforms, setSelectedPlatforms] = useState<PlatType[]>(availablePlatforms)
48
49 const { createDraftFromVideo, isCreating } = useMediaTabStore(
50 useShallow(state => ({
51 createDraftFromVideo: state.createDraftFromVideo,
52 isCreating: !!state.creatingDraftMap[media._id],
53 })),
54 )
55
56 const handleOpenDialog = useCallback(() => {
57 if (media.type !== 'video' || isCreating) {
58 return
59 }
60
61 setSelectedPlatforms(availablePlatforms)
62 setDialogOpen(true)
63 }, [availablePlatforms, isCreating, media.type])
64
65 const handleCreateDraft = useCallback(async () => {
66 if (media.type !== 'video' || isCreating) {
67 return
68 }
69
70 if (selectedPlatforms.length === 0) {
71 toast.warning(t('mediaManagement.createDraftPlatformRequired'))
72 return
73 }
74
75 setDialogOpen(false)
76 toast.info(t('mediaManagement.createDraftTaskStartedToast'))
77
78 const result = await createDraftFromVideo({
79 mediaId: media._id,
80 mediaTitle: media.title || media.desc || '',
81 videoUrl: media.url,
82 groupId,
83 platforms: selectedPlatforms,
84 })
85
86 if (result.success) {
87 toast.success(t('mediaManagement.createDraftSuccess'))
88 return
89 }
90
91 toast.error(result.message || t('mediaManagement.createDraftFailed'))
92 }, [createDraftFromVideo, groupId, isCreating, media._id, media.desc, media.title, media.type, media.url, selectedPlatforms, t])
93
94 if (media.type !== 'video') {
95 return null
96 }
97
98 return (
99 <>
100 <Button
101 type="button"
102 disabled={isCreating}
103 onClick={handleOpenDialog}
104 className={cn(
105 'h-9 rounded-full border border-primary/25 bg-background/95 px-3 text-xs font-semibold text-foreground shadow-lg shadow-primary/15 backdrop-blur-md',
106 'cursor-pointer gap-1.5 transition-all duration-200 hover:border-primary/40 hover:bg-background hover:shadow-xl hover:shadow-primary/20',
107 )}
108 >
109 {isCreating
110 ? <Loader2 className="h-4 w-4 animate-spin" />
111 : <Sparkles className="h-4 w-4 text-primary" />}
112 <span>{t('mediaManagement.createDraft')}</span>
113 </Button>
114
115 {dialogOpen && (
116 <Dialog open={dialogOpen} onOpenChange={setDialogOpen}>
117 <DialogContent className="max-w-[560px]">
118 <DialogHeader>
119 <DialogTitle>{t('mediaManagement.createDraftConfirmTitle')}</DialogTitle>
120 <DialogDescription>{t('mediaManagement.createDraftConfirmDesc')}</DialogDescription>
121 </DialogHeader>
122
123 <div className="space-y-4 py-2">
124 <InlinePlatformSelector
125 selectedPlatforms={selectedPlatforms}
126 onPlatformsChange={setSelectedPlatforms}
127 availablePlatforms={availablePlatforms}
128 />
129 <p className="text-xs leading-5 text-muted-foreground">
130 {t('mediaManagement.createDraftPlatformHint')}
131 </p>
132 </div>
133
134 <DialogFooter>
135 <Button
136 variant="ghost"
137 onClick={() => setDialogOpen(false)}
138 disabled={isCreating}
139 className="cursor-pointer"
140 >
141 {tCommon('cancel')}
142 </Button>
143 <Button
144 onClick={handleCreateDraft}
145 disabled={isCreating}
146 className="cursor-pointer gap-1.5"
147 >
148 {isCreating && <Loader2 className="h-4 w-4 animate-spin" />}
149 {t('mediaManagement.createDraft')}
150 </Button>
151 </DialogFooter>
152 </DialogContent>
153 </Dialog>
154 )}
155 </>
156 )
157 })
158
159 VideoCreateDraftAction.displayName = 'VideoCreateDraftAction'
160
160 lines Plain Text