Files
musicland/src/screens/cover/ValidateCover.js
T
2026-01-16 15:58:22 +01:00

240 lines
7.8 KiB
JavaScript

import FullscreenIntroVideo from '../../components/FullscreenIntroVideo'
import { Image as ExpoImage } from 'expo-image'
import React, { useCallback, useMemo, useState } from 'react'
import { Pressable, Text, View } from 'react-native'
import useMinuit from 'react-native-minuit/src/hooks/useMinuit.js'
import { background } from '../../assets'
import GradientButton from '../../components/GradientButton'
import MusicLandHeader from '../../components/MusicLandHeader'
import { isWeb } from '../../hooks/useLayoutType'
import Page from '../../layouts/Page'
import { Routes } from '../../navigation'
import { goBack, navigate } from '../../navigation/NavigationService'
import { useUser, useUserData } from '../../providers/UserDataProvider'
import { gutters, Palette, Style } from '../../styles'
import CreateLyricsHeader from '../Writing/components/CreateLyricsHeader'
const ValidateCover = () => {
const { selectedProject, updateProjectData } = useUserData()
const { videos } = useUser()
const benhaiUrl = isWeb ? videos?.benhaiWeb || null : videos?.benhai
const [showIntro, setShowIntro] = useState(null)
const { setIsLoading } = useMinuit()
const coverOptions = useMemo(() => {
if (!Array.isArray(selectedProject?.cover?.options)) {
return []
}
return selectedProject.cover.options.filter((option) => option)
}, [selectedProject])
const selectedOptionId = selectedProject?.cover?.selectedOptionId || null
const selectedOption = useMemo(() => {
if (!coverOptions.length) {
return null
}
const found = coverOptions.find((option) => option?.id === selectedOptionId)
return found || coverOptions[0] || null
}, [coverOptions, selectedOptionId])
const isMultipleOptions = coverOptions.length > 1
const [isSelecting, setIsSelecting] = useState(false)
async function onStartAgain() {
try {
await setIsLoading(true)
await updateProjectData({
cover: null,
coverUrl: null,
})
navigate(Routes.ChooseCoverType)
} catch (e) {
console.log(e)
} finally {
await setIsLoading(false)
}
}
const handleSelectOption = useCallback(
async (option) => {
if (
!option ||
option?.id === selectedOptionId ||
isSelecting ||
selectedOptionId // Prevent re-selection
) {
return
}
const existingCover = selectedProject?.cover || {}
const finalUrl = option.finalUrl || option.generatedUrl || null
setIsSelecting(true)
try {
await updateProjectData({
cover: {
...existingCover,
options: coverOptions,
selectedOptionId: option.id,
result: finalUrl,
generatedBackground: option.generatedUrl || option.finalUrl || null,
},
})
} catch (e) {
console.log('ValidateCover: unable to select cover', e?.message)
} finally {
setIsSelecting(false)
}
},
[coverOptions, isSelecting, selectedOptionId, selectedProject, updateProjectData]
)
async function onValidatePicture() {
if (!selectedOption) {
return
}
setShowIntro(benhaiUrl)
}
const handleCloseIntro = useCallback(() => {
setShowIntro(null)
navigate(Routes.SongDownload, {
project: selectedProject,
selectedOption,
coverOptions,
})
}, [coverOptions, selectedOption, selectedProject])
return (
<Page backgroundImg={background.studioBG2} headerType="NONE">
<MusicLandHeader onPressBack={goBack} progress={90} />
<View style={{ flex: 1, marginTop: 16 }}>
<CreateLyricsHeader
title="Ta pochette est prête!"
subTitle="Choisis ta version préférée."
/>
<View style={{ flex: 1, ...Style.containerCenter }}>
<View
style={{
width: '90%',
gap: 16,
flexDirection: isMultipleOptions ? 'row' : 'column',
flexWrap: isMultipleOptions ? 'wrap' : 'nowrap',
justifyContent: 'center',
alignItems: isMultipleOptions ? 'stretch' : 'center',
}}
>
{coverOptions.map((option, index) => {
const isSelected = option?.id === selectedOption?.id
const imageSource = option?.finalUrl || option?.generatedUrl || null
if (!imageSource) {
return null
}
return (
<Pressable
key={option?.id || index}
onPress={() => handleSelectOption(option)}
disabled={isSelecting}
style={{
borderRadius: 20,
borderWidth: isSelected ? 2 : 1,
borderColor: isSelected ? Palette.primary : Palette.ultraLightWhite,
overflow: 'hidden',
width: isMultipleOptions ? (isWeb ? 280 : '48%') : isWeb ? 300 : '90%',
maxWidth: isWeb ? 320 : '100%',
position: 'relative',
}}
>
<ExpoImage
source={{ uri: imageSource }}
cachePolicy="memory-disk"
contentFit="cover"
transition={120}
style={{ width: '100%', aspectRatio: 1 }}
/>
<View
style={{
position: 'absolute',
top: 12,
right: 12,
backgroundColor: Palette.transparentBlack,
paddingHorizontal: 10,
paddingVertical: 6,
borderRadius: 12,
}}
>
<Text
style={{
color: Palette.white,
fontWeight: '600',
fontSize: 12,
}}
>
{`Option ${index + 1}`}
</Text>
</View>
{isSelected && (
<View
style={{
position: 'absolute',
bottom: 12,
left: 12,
backgroundColor: Palette.primary,
paddingHorizontal: 12,
paddingVertical: 6,
borderRadius: 12,
}}
>
<Text
style={{
color: Palette.white,
fontWeight: '600',
fontSize: 12,
}}
>
Sélectionnée
</Text>
</View>
)}
</Pressable>
)
})}
{!coverOptions.length && (
<Text
style={{
color: Palette.white,
textAlign: 'center',
opacity: 0.8,
}}
>
Les options de pochette seront disponibles à la fin de la génération.
</Text>
)}
</View>
</View>
</View>
<View
style={{
paddingBottom: gutters * 2,
width: '80%',
alignSelf: 'center',
gap: 12,
}}
>
{/* <BorderGradientButton
title="Recommencer la création"
onPress={onStartAgain}
/> */}
<GradientButton
title="Valider la pochette"
onPress={onValidatePicture}
disabled={!selectedOption || isSelecting}
/>
</View>
<FullscreenIntroVideo url={showIntro} visible={!!showIntro} onClose={handleCloseIntro} />
</Page>
)
}
export default ValidateCover