280 lines
8.4 KiB
JavaScript
280 lines
8.4 KiB
JavaScript
import firebase, { tasksRef } from '../../config/firebase'
|
||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||
import { ActivityIndicator, Alert, StyleSheet, Text, View } from 'react-native'
|
||
import { background } from '../../assets'
|
||
import BorderGradientButton from '../../components/BorderGradientButton'
|
||
import GradientButton from '../../components/GradientButton'
|
||
import MusiclandPlaybackCover from '../../components/MusiclandPlaybackCover'
|
||
import MusicLandHeader from '../../components/MusicLandHeader'
|
||
import { uploadFileToFirebase } from '../../helpers/uploadToFirebase'
|
||
import { isWeb } from '../../hooks/useLayoutType'
|
||
import Page from '../../layouts/Page'
|
||
import { Routes } from '../../navigation'
|
||
import { goBack, navigate } from '../../navigation/NavigationService'
|
||
import { useUser } from '../../providers/UserDataProvider'
|
||
import { gutters, Palette, Style } from '../../styles'
|
||
import { FONT_FAMILY } from '../../styles/Fonts'
|
||
import { createMusiclandPlaybackCover } from '../../utils/createMusiclandPlaybackCover'
|
||
import CreateLyricsHeader from '../Writing/components/CreateLyricsHeader'
|
||
|
||
const COVER_TEMPLATE = 'MUSICLAND_PLAYBACKER'
|
||
const COVER_OPTION_ID = 'musicland-playbacker'
|
||
|
||
const PouchReady = () => {
|
||
const {
|
||
selectedProjectId,
|
||
selectedProject,
|
||
currentUserData,
|
||
updateProjectData,
|
||
} = useUser()
|
||
const requestStartedRef = useRef(false)
|
||
const [isSaving, setIsSaving] = useState(false)
|
||
|
||
const templateCover =
|
||
selectedProject?.cover?.template === COVER_TEMPLATE ? selectedProject.cover : null
|
||
const coverUrl = templateCover?.result || null
|
||
const isPreparing = selectedProject?.coverStatus === 'GENERATING'
|
||
const hasPreparationError = selectedProject?.coverStatus === 'ERROR'
|
||
const artistName =
|
||
typeof selectedProject?.userName === 'string' && selectedProject.userName.trim()
|
||
? selectedProject.userName.trim()
|
||
: currentUserData?.userName || 'MusicLand'
|
||
const profilePictureURL = currentUserData?.profilePictureURL || null
|
||
const centerImageURL = selectedProject?.coverCenterImageURL || profilePictureURL
|
||
|
||
const selectedOption = useMemo(() => {
|
||
if (!coverUrl) {
|
||
return isWeb
|
||
? {
|
||
id: COVER_OPTION_ID,
|
||
finalUrl: null,
|
||
generatedUrl: null,
|
||
}
|
||
: null
|
||
}
|
||
const savedOption = Array.isArray(templateCover?.options) ? templateCover.options[0] : null
|
||
return (
|
||
savedOption || {
|
||
id: COVER_TEMPLATE,
|
||
finalUrl: coverUrl,
|
||
generatedUrl: null,
|
||
}
|
||
)
|
||
}, [coverUrl, templateCover?.options])
|
||
|
||
const requestCoverPreparation = useCallback(async () => {
|
||
if (!selectedProjectId || requestStartedRef.current || isPreparing) {
|
||
return
|
||
}
|
||
requestStartedRef.current = true
|
||
try {
|
||
await updateProjectData(
|
||
{
|
||
coverStatus: 'PENDING',
|
||
cover: {
|
||
template: COVER_TEMPLATE,
|
||
result: null,
|
||
selectedOptionId: COVER_TEMPLATE,
|
||
options: [],
|
||
},
|
||
},
|
||
{ merge: true }
|
||
)
|
||
await tasksRef.add({
|
||
type: 'cover',
|
||
projectId: selectedProjectId,
|
||
status: 'PENDING',
|
||
createdAt: firebase.firestore.FieldValue.serverTimestamp(),
|
||
})
|
||
} catch (error) {
|
||
requestStartedRef.current = false
|
||
console.log('PouchReady: unable to prepare cover', error?.message)
|
||
}
|
||
}, [isPreparing, selectedProjectId, updateProjectData])
|
||
|
||
useEffect(() => {
|
||
if (isWeb || coverUrl || isPreparing || hasPreparationError) {
|
||
return
|
||
}
|
||
requestCoverPreparation()
|
||
}, [coverUrl, hasPreparationError, isPreparing, requestCoverPreparation])
|
||
|
||
const retryPreparation = useCallback(() => {
|
||
requestStartedRef.current = false
|
||
requestCoverPreparation()
|
||
}, [requestCoverPreparation])
|
||
|
||
const onValidatePicture = useCallback(async () => {
|
||
if (!selectedOption) {
|
||
return
|
||
}
|
||
try {
|
||
setIsSaving(true)
|
||
let optionToSave = selectedOption
|
||
let projectToSave = selectedProject
|
||
|
||
if (isWeb && !coverUrl) {
|
||
const userId = selectedProject?.userId
|
||
if (!userId || !selectedProjectId) {
|
||
throw new Error('cover_project_identity_missing')
|
||
}
|
||
const coverBlob = await createMusiclandPlaybackCover({
|
||
artistName,
|
||
profilePictureURL: centerImageURL,
|
||
})
|
||
const storagePath = `users/${userId}/projects/${selectedProjectId}/cover-musicland-playbacker.png`
|
||
const { resultURI } = await uploadFileToFirebase({
|
||
path: storagePath,
|
||
fileType: 'IMAGE',
|
||
blob: coverBlob,
|
||
})
|
||
optionToSave = {
|
||
id: COVER_OPTION_ID,
|
||
finalUrl: resultURI,
|
||
generatedUrl: null,
|
||
}
|
||
const cover = {
|
||
template: COVER_TEMPLATE,
|
||
result: resultURI,
|
||
generatedBackground: null,
|
||
selectedOptionId: COVER_OPTION_ID,
|
||
options: [optionToSave],
|
||
}
|
||
await updateProjectData(
|
||
{
|
||
cover,
|
||
coverStatus: 'GENERATED',
|
||
},
|
||
{ merge: true }
|
||
)
|
||
projectToSave = {
|
||
...selectedProject,
|
||
cover,
|
||
coverStatus: 'GENERATED',
|
||
}
|
||
}
|
||
|
||
navigate(Routes.SongDownload, {
|
||
project: projectToSave,
|
||
selectedOption: optionToSave,
|
||
coverOptions: [optionToSave],
|
||
backRoute: Routes.PouchReady,
|
||
})
|
||
} catch (error) {
|
||
console.log('PouchReady: unable to save cover', error?.message)
|
||
Alert.alert(
|
||
'Enregistrement impossible',
|
||
'La pochette n’a pas pu être enregistrée. Réessaie dans quelques instants.'
|
||
)
|
||
} finally {
|
||
setIsSaving(false)
|
||
}
|
||
}, [
|
||
artistName,
|
||
coverUrl,
|
||
centerImageURL,
|
||
selectedOption,
|
||
selectedProject,
|
||
selectedProjectId,
|
||
updateProjectData,
|
||
])
|
||
|
||
return (
|
||
<Page backgroundImg={background.studioBG2} headerType="NONE">
|
||
<MusicLandHeader onPressBack={goBack} progress={90} />
|
||
<View style={styles.pageContent}>
|
||
<CreateLyricsHeader
|
||
title="Ta pochette MusicLand"
|
||
subTitle={
|
||
selectedProject?.coverCenterImageURL
|
||
? "L’image choisie et ton nom d’artiste apparaissent automatiquement."
|
||
: "Ta photo de profil et ton nom d’artiste apparaissent automatiquement."
|
||
}
|
||
/>
|
||
<View style={[styles.previewArea, Style.containerCenter]}>
|
||
<MusiclandPlaybackCover
|
||
artistName={artistName}
|
||
profilePictureURL={centerImageURL}
|
||
coverUrl={coverUrl}
|
||
style={isWeb ? styles.coverWeb : styles.coverMobile}
|
||
/>
|
||
{!isWeb && !coverUrl && !hasPreparationError ? (
|
||
<View style={styles.preparingRow}>
|
||
<ActivityIndicator color={Palette.white} />
|
||
<Text style={styles.preparingText}>Préparation de ta pochette…</Text>
|
||
</View>
|
||
) : null}
|
||
{hasPreparationError ? (
|
||
<View style={styles.errorPanel}>
|
||
<Text style={styles.errorText}>
|
||
La pochette n’a pas pu être préparée. Tu peux réessayer.
|
||
</Text>
|
||
<BorderGradientButton title="Réessayer" onPress={retryPreparation} />
|
||
</View>
|
||
) : null}
|
||
</View>
|
||
</View>
|
||
<View style={styles.footer}>
|
||
<GradientButton
|
||
title={isSaving ? 'Enregistrement…' : 'Valider la pochette'}
|
||
onPress={onValidatePicture}
|
||
disabled={isSaving || (!isWeb && (!selectedOption || isPreparing))}
|
||
/>
|
||
</View>
|
||
</Page>
|
||
)
|
||
}
|
||
|
||
const styles = StyleSheet.create({
|
||
pageContent: {
|
||
flex: 1,
|
||
marginTop: 8,
|
||
},
|
||
previewArea: {
|
||
flex: 1,
|
||
width: '100%',
|
||
paddingHorizontal: gutters,
|
||
gap: 16,
|
||
},
|
||
coverWeb: {
|
||
width: 360,
|
||
maxWidth: '70%',
|
||
},
|
||
coverMobile: {
|
||
width: '86%',
|
||
maxWidth: 380,
|
||
},
|
||
preparingRow: {
|
||
minHeight: 24,
|
||
flexDirection: 'row',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
gap: 10,
|
||
},
|
||
preparingText: {
|
||
color: Palette.white,
|
||
fontFamily: FONT_FAMILY.InterRegular,
|
||
fontSize: 14,
|
||
},
|
||
errorPanel: {
|
||
width: isWeb ? 360 : '86%',
|
||
maxWidth: 380,
|
||
gap: 12,
|
||
},
|
||
errorText: {
|
||
color: Palette.white,
|
||
fontFamily: FONT_FAMILY.InterRegular,
|
||
fontSize: 14,
|
||
lineHeight: 20,
|
||
textAlign: 'center',
|
||
},
|
||
footer: {
|
||
width: '80%',
|
||
maxWidth: 520,
|
||
alignSelf: 'center',
|
||
paddingBottom: gutters * 2,
|
||
},
|
||
})
|
||
|
||
export default PouchReady
|