feat: download options

This commit is contained in:
2026-09-03 09:30:31 +02:00
parent f4549b694f
commit 99431f9ae4
20 changed files with 837 additions and 1512 deletions
+16 -4
View File
@@ -46,7 +46,11 @@ const Playback = ({ route, navigation }) => {
const onPressRecord = useCallback(() => {
if (isManualBlocked) return
navigate(Routes.RecordPlayback, { project, returnToAdventureModal })
navigate(Routes.PlaybackImageRightsConsent, {
project,
flow: 'record',
returnToAdventureModal,
})
}, [isManualBlocked, project, returnToAdventureModal])
const onPressPlaybackAi = useCallback(() => {
@@ -54,8 +58,12 @@ const Playback = ({ route, navigation }) => {
navigate(Routes.PlaybackAiStatus, { project })
return
}
navigate(Routes.PlaybackAi, { project })
}, [project])
navigate(Routes.PlaybackImageRightsConsent, {
project,
flow: 'ai',
returnToAdventureModal,
})
}, [project, returnToAdventureModal])
return (
<Page
@@ -86,7 +94,11 @@ const Playback = ({ route, navigation }) => {
onPress={onPressRecord}
disabled={!project || isManualBlocked}
/>
<BorderGradientButton title={aiButtonLabel} onPress={onPressPlaybackAi} disabled={!project} />
<BorderGradientButton
title={aiButtonLabel}
onPress={onPressPlaybackAi}
disabled={!project}
/>
{isManualBlocked ? (
<Text style={styles.blockedText}>
Le playback manuel est bloqué tant que le playback IA est en cours ou en attente de
@@ -0,0 +1,150 @@
import React, { useMemo, useState } from 'react'
import { ScrollView, StyleSheet, Text, View } from 'react-native'
import useMinuit from 'react-native-minuit/src/hooks/useMinuit.js'
import { background } from '../../assets'
import AppCheckbox from '../../components/AppCheckbox'
import GradientButton from '../../components/GradientButton'
import MusicLandHeader from '../../components/MusicLandHeader'
import { projectsRef, serverTimestamp } from '../../config/firebase'
import Page from '../../layouts/Page'
import { Routes } from '../../navigation'
import { goBack, navigate } 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'
const PlaybackImageRightsConsent = ({ route }) => {
const { project: routeProject, flow, returnToAdventureModal = false } = route.params || {}
const { selectedProject } = useUserData() || {}
const { setTooltip } = useMinuit()
const [hasAcceptedImageRights, setHasAcceptedImageRights] = useState(false)
const [isSaving, setIsSaving] = useState(false)
const project = useMemo(() => {
if (routeProject?.id && selectedProject?.id === routeProject.id) {
return selectedProject
}
return routeProject || null
}, [routeProject, selectedProject])
const handleContinue = async () => {
if (!hasAcceptedImageRights || isSaving) return
if (!project?.id || (flow !== 'record' && flow !== 'ai')) {
setTooltip({
type: 'error',
text: "Impossible de poursuivre vers l'espace Playback.",
})
return
}
try {
setIsSaving(true)
await projectsRef.doc(project.id).set(
{
imageRightsAccepted: true,
imageRightsAcceptedAt: serverTimestamp(),
},
{ merge: true }
)
const params = { project, returnToAdventureModal }
navigate(flow === 'record' ? Routes.RecordPlayback : Routes.PlaybackAi, params)
} catch (error) {
setTooltip({
type: 'error',
text: error?.message || "Impossible d'enregistrer la confirmation.",
})
} finally {
setIsSaving(false)
}
}
return (
<Page backgroundImg={background.playbackBG2} backgroundColor="#07111B" headerType="NONE">
<MusicLandHeader progress={28} onPressBack={goBack} />
<ScrollView contentContainerStyle={styles.container} showsVerticalScrollIndicator={false}>
<CreateLyricsHeader
title="Droit à l'image"
subTitle="Cette confirmation est nécessaire pour continuer."
/>
<View style={styles.disclaimerCard}>
<Text style={styles.disclaimerTitle}>Avant de créer ton playback</Text>
<Text style={styles.disclaimerText}>
La photo ou la vidéo utilisée doit représenter une personne qui a donné son accord.
N'utilise pas l'image d'un tiers sans son autorisation.
</Text>
<Text style={styles.disclaimerText}>
Si une autre personne apparaît dans le contenu, tu dois disposer de son autorisation
pour l'enregistrement, le traitement et, si tu le choisis plus tard, la publication sur
MusicLand.
</Text>
</View>
<View style={styles.consentContainer}>
<AppCheckbox
selected={hasAcceptedImageRights}
onPress={() => setHasAcceptedImageRights((current) => !current)}
label="Je confirme disposer des droits et autorisations nécessaires sur toutes les personnes représentées."
/>
</View>
<GradientButton
title="Continuer vers mon playback"
onPress={handleContinue}
disabled={!hasAcceptedImageRights || isSaving}
loading={isSaving}
loadingText="Enregistrement..."
containerStyle={styles.continueButton}
/>
</ScrollView>
</Page>
)
}
export default PlaybackImageRightsConsent
const styles = StyleSheet.create({
container: {
flexGrow: 1,
width: '100%',
maxWidth: 620,
alignSelf: 'center',
paddingHorizontal: gutters,
paddingTop: gutters,
paddingBottom: gutters * 2,
gap: gutters,
},
disclaimerCard: {
padding: gutters,
borderRadius: 18,
borderWidth: 1,
borderColor: 'rgba(255, 255, 255, 0.16)',
backgroundColor: 'rgba(5, 12, 24, 0.82)',
gap: 12,
},
disclaimerTitle: {
color: Palette.white,
fontFamily: FONT_FAMILY.InterSemiBold,
fontSize: 18,
},
disclaimerText: {
color: Palette.white,
fontFamily: FONT_FAMILY.InterRegular,
fontSize: 15,
lineHeight: 22,
opacity: 0.86,
},
consentContainer: {
padding: gutters,
borderRadius: 16,
backgroundColor: 'rgba(255, 255, 255, 0.08)',
},
continueButton: {
width: '100%',
maxWidth: 360,
alignSelf: 'center',
},
})