import useMinuit from 'react-native-minuit/src/hooks/useMinuit.js' import React, { useMemo, useState } from 'react' import { Image, Text, View } from 'react-native' import { background } from '../../assets' import useDataFromRef from '../../hooks/useDataFromRef' import ActivityLoader from '../../components/ActivityLoader' import BorderGradientButton from '../../components/BorderGradientButton' import GradientButton from '../../components/GradientButton' import MusicLandHeader from '../../components/MusicLandHeader' import { getFunctionsClient, projectsRef } from '../../config/firebase' import Page from '../../layouts/Page' import { Routes } from '../../navigation' import { goBack, navigate, push } from '../../navigation/NavigationService' import { useUserData } from '../../providers/UserDataProvider' import { gutters, Palette } from '../../styles' import { FONT_FAMILY } from '../../styles/Fonts' import CreateLyricsHeader from '../Writing/components/CreateLyricsHeader' import { PLAYBACK_AI_STATUS } from '../../utils/playbackAi' const FUNCTIONS_REGION = 'europe-west1' const resolveProject = ({ routeProject, selectedProject, liveProject }) => { if (liveProject?.id) return liveProject if (routeProject?.id && selectedProject?.id === routeProject.id) { return selectedProject } return routeProject || null } const callValidatePlaybackDraft = (payload) => { const callable = getFunctionsClient(FUNCTIONS_REGION).httpsCallable('heygen-validatePlaybackDraft') return callable(payload) } const PlaybackAiStatus = ({ route }) => { const { project: routeProject } = route.params || {} const { selectedProject } = useUserData() || {} const { setTooltip } = useMinuit() const [isValidating, setIsValidating] = useState(false) const projectId = routeProject?.id || selectedProject?.id || null const { data: liveProject = null } = useDataFromRef({ ref: projectId ? projectsRef.doc(projectId) : null, simpleRef: true, listener: true, condition: !!projectId, refreshArray: [projectId], }) const project = useMemo( () => resolveProject({ routeProject, selectedProject, liveProject }), [liveProject, routeProject, selectedProject] ) const status = project?.playbackStatus || (routeProject?.playbackGenerating ? PLAYBACK_AI_STATUS.GENERATING : null) const draftUrl = project?.playbackDraftUrl || routeProject?.playbackDraftUrl || null const handleValidate = async () => { try { if (!project?.id) { throw new Error('Projet introuvable') } setIsValidating(true) await callValidatePlaybackDraft({ projectId: project.id }) const latestProjectSnap = await projectsRef.doc(project.id).get() const latestProject = latestProjectSnap.exists ? { ...latestProjectSnap.data(), id: latestProjectSnap.id } : project navigate(Routes.PlaybackDownload, { action: 'playback', project: latestProject, }) } catch (error) { setTooltip({ type: 'error', text: error?.message || 'Impossible de valider le playback IA', }) } finally { setIsValidating(false) } } const handleRetry = () => { push(Routes.PlaybackAi, { project, initialPhotoUrl: project?.playbackPhotoUrl || null, }) } return ( {project?.coverUrl ? ( ) : null} {project?.title || 'Sans titre'} Format vertical 9:16 généré avec HeyGen {status === PLAYBACK_AI_STATUS.GENERATING ? ( Tu peux fermer cet écran et revenir plus tard. Une notification te sera envoyée dès que ta vidéo sera prête. ) : null} {status === PLAYBACK_AI_STATUS.FAILED ? ( La génération a échoué {project?.playbackError || 'HeyGen n’a pas réussi à générer la vidéo.'} ) : null} {status === PLAYBACK_AI_STATUS.DRAFT_READY ? ( <> {draftUrl ? ( {isValidating ? ( ) : null} ) : null} {status === PLAYBACK_AI_STATUS.READY && project?.playbackUrl ? ( Le playback IA est déjà validé navigate(Routes.PlaybackDownload, { action: 'playback', project, }) } /> ) : null} ) } const styles = { content: { flex: 1, width: '92%', alignSelf: 'center', gap: 16, }, headerCard: { flexDirection: 'row', alignItems: 'center', gap: 12, padding: 16, borderRadius: 18, borderWidth: 1, borderColor: 'rgba(255,255,255,0.14)', backgroundColor: 'rgba(5,12,24,0.76)', }, cover: { width: 68, height: 68, borderRadius: 14, }, title: { color: Palette.white, fontSize: 18, fontFamily: FONT_FAMILY.InterSemiBold, }, subtitle: { color: Palette.white, opacity: 0.7, fontFamily: FONT_FAMILY.InterRegular, }, stateCard: { flex: 1, minHeight: 240, borderRadius: 20, borderWidth: 1, borderColor: 'rgba(255,255,255,0.14)', backgroundColor: 'rgba(5,12,24,0.8)', alignItems: 'center', justifyContent: 'center', gap: 14, paddingHorizontal: 20, }, videoCard: { width: '60%', maxWidth: 360, aspectRatio: 9 / 16, alignSelf: 'center', borderRadius: 22, overflow: 'hidden', borderWidth: 1, borderColor: 'rgba(255,255,255,0.14)', backgroundColor: 'rgba(5,12,24,0.8)', padding: 12, }, video: { width: '100%', height: '100%', borderRadius: 18, backgroundColor: '#0A0A0A', objectFit: 'contain', }, videoFallback: { width: '100%', height: '100%', borderRadius: 18, backgroundColor: '#0A0A0A', }, emptyVideo: { alignItems: 'center', justifyContent: 'center', }, helperText: { color: Palette.white, opacity: 0.72, fontFamily: FONT_FAMILY.InterRegular, lineHeight: 20, textAlign: 'center', }, errorTitle: { color: Palette.white, fontSize: 18, fontFamily: FONT_FAMILY.InterSemiBold, }, successTitle: { color: Palette.white, fontSize: 18, fontFamily: FONT_FAMILY.InterSemiBold, }, actions: { gap: 12, }, } export default PlaybackAiStatus