feat: fixes and formatter
This commit is contained in:
@@ -1,19 +1,19 @@
|
||||
import { View, Text } from "react-native";
|
||||
import React, { useState } from "react";
|
||||
import Page from "../../layouts/Page";
|
||||
import { background } from "../../assets";
|
||||
import MusicLandHeader from "../../components/MusicLandHeader";
|
||||
import { goBack, navigate } from "../../navigation/NavigationService";
|
||||
import CreateLyricsHeader from "../Writing/components/CreateLyricsHeader";
|
||||
import ItemContainer from "../../components/ItemContainer/ItemContainer";
|
||||
import GradientButton from "../../components/GradientButton";
|
||||
import { CHOOSE_DECOR } from "../../data/data";
|
||||
import { Palette } from "../../styles";
|
||||
import { FONT_FAMILY } from "../../styles/Fonts";
|
||||
import { Routes } from "../../navigation";
|
||||
import { View, Text } from 'react-native'
|
||||
import React, { useState } from 'react'
|
||||
import Page from '../../layouts/Page'
|
||||
import { background } from '../../assets'
|
||||
import MusicLandHeader from '../../components/MusicLandHeader'
|
||||
import { goBack, navigate } from '../../navigation/NavigationService'
|
||||
import CreateLyricsHeader from '../Writing/components/CreateLyricsHeader'
|
||||
import ItemContainer from '../../components/ItemContainer/ItemContainer'
|
||||
import GradientButton from '../../components/GradientButton'
|
||||
import { CHOOSE_DECOR } from '../../data/data'
|
||||
import { Palette } from '../../styles'
|
||||
import { FONT_FAMILY } from '../../styles/Fonts'
|
||||
import { Routes } from '../../navigation'
|
||||
|
||||
const ChooseDecor = () => {
|
||||
const [selected, setSelected] = useState("");
|
||||
const [selected, setSelected] = useState('')
|
||||
|
||||
return (
|
||||
<Page headerType="NONE" backgroundImg={background.playbackBG2}>
|
||||
@@ -27,7 +27,7 @@ const ChooseDecor = () => {
|
||||
<ItemContainer height={402}>
|
||||
<View style={{ gap: 10, marginTop: 5, paddingHorizontal: 5 }}>
|
||||
{CHOOSE_DECOR.map((item, index) => {
|
||||
const selectedItem = selected === item;
|
||||
const selectedItem = selected === item
|
||||
|
||||
return (
|
||||
<CreateLyricsHeader
|
||||
@@ -39,12 +39,8 @@ const ChooseDecor = () => {
|
||||
borderRadius: 14,
|
||||
}}
|
||||
onPress={() => setSelected(item)}
|
||||
colors={
|
||||
selectedItem
|
||||
? ["#99999900", "#FFFFFF"]
|
||||
: [Palette.tran, Palette.tran]
|
||||
}
|
||||
tint={selectedItem ? "default" : "dark"}
|
||||
colors={selectedItem ? ['#99999900', '#FFFFFF'] : [Palette.tran, Palette.tran]}
|
||||
tint={selectedItem ? 'default' : 'dark'}
|
||||
>
|
||||
<View style={{ paddingVertical: 6 }}>
|
||||
<Text
|
||||
@@ -58,18 +54,15 @@ const ChooseDecor = () => {
|
||||
</Text>
|
||||
</View>
|
||||
</CreateLyricsHeader>
|
||||
);
|
||||
)
|
||||
})}
|
||||
</View>
|
||||
</ItemContainer>
|
||||
</View>
|
||||
<GradientButton
|
||||
title="Suivant"
|
||||
onPress={() => navigate(Routes.CreatingDecor)}
|
||||
/>
|
||||
<GradientButton title="Suivant" onPress={() => navigate(Routes.CreatingDecor)} />
|
||||
</View>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default ChooseDecor;
|
||||
export default ChooseDecor
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
import { View, Text, Image, Platform } from "react-native";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Page from "../../layouts/Page";
|
||||
import { ai, background } from "../../assets";
|
||||
import MusicLandHeader from "../../components/MusicLandHeader";
|
||||
import { goBack, navigate } from "../../navigation/NavigationService";
|
||||
import { Palette, Style } from "../../styles";
|
||||
import { BlurView } from "expo-blur";
|
||||
import { FONT_FAMILY } from "../../styles/Fonts";
|
||||
import ProgressBar from "../../components/ProgressBar";
|
||||
import { Routes } from "../../navigation";
|
||||
import GradientButton from "../../components/GradientButton";
|
||||
import { View, Text, Image, Platform } from 'react-native'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import Page from '../../layouts/Page'
|
||||
import { ai, background } from '../../assets'
|
||||
import MusicLandHeader from '../../components/MusicLandHeader'
|
||||
import { goBack, navigate } from '../../navigation/NavigationService'
|
||||
import { Palette, Style } from '../../styles'
|
||||
import { BlurView } from 'expo-blur'
|
||||
import { FONT_FAMILY } from '../../styles/Fonts'
|
||||
import ProgressBar from '../../components/ProgressBar'
|
||||
import { Routes } from '../../navigation'
|
||||
import GradientButton from '../../components/GradientButton'
|
||||
|
||||
const CreatingDecor = () => {
|
||||
const [progress, setProgress] = useState(0);
|
||||
const [progress, setProgress] = useState(0)
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setProgress((prevProgress) => {
|
||||
if (prevProgress >= 100) {
|
||||
clearInterval(interval);
|
||||
clearInterval(interval)
|
||||
|
||||
return 100;
|
||||
return 100
|
||||
}
|
||||
return prevProgress + 1;
|
||||
});
|
||||
}, 100);
|
||||
return prevProgress + 1
|
||||
})
|
||||
}, 100)
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
return () => clearInterval(interval)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Page backgroundImg={background.playbackBG2} headerType="NONE">
|
||||
@@ -41,23 +41,23 @@ const CreatingDecor = () => {
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "50%",
|
||||
width: '100%',
|
||||
height: '50%',
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
zIndex: 1,
|
||||
position: "absolute",
|
||||
position: 'absolute',
|
||||
top: -150,
|
||||
width: "50%",
|
||||
height: "80%",
|
||||
alignSelf: "center",
|
||||
width: '50%',
|
||||
height: '80%',
|
||||
alignSelf: 'center',
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
source={ai.john}
|
||||
style={{ width: "100%", height: "100%", right: -10 }}
|
||||
style={{ width: '100%', height: '100%', right: -10 }}
|
||||
resizeMode="contain"
|
||||
/>
|
||||
</View>
|
||||
@@ -65,42 +65,42 @@ const CreatingDecor = () => {
|
||||
style={{
|
||||
flex: 1,
|
||||
borderRadius: 20,
|
||||
overflow: "hidden",
|
||||
backgroundColor: "#0F0C1933",
|
||||
overflow: 'hidden',
|
||||
backgroundColor: '#0F0C1933',
|
||||
}}
|
||||
>
|
||||
<BlurView
|
||||
intensity={Platform.OS !== "ios" ? 10 : 40}
|
||||
intensity={Platform.OS !== 'ios' ? 10 : 40}
|
||||
tint="dark"
|
||||
style={{ flex: 1, padding: 10, paddingBottom: 20 }}
|
||||
// experimentalBlurMethod={
|
||||
// Platform.OS !== "ios" ? "dimezisBlurView" : "none"
|
||||
// }
|
||||
>
|
||||
<View style={{ flex: 1, justifyContent: "flex-end", gap: 20 }}>
|
||||
<View style={{ flex: 1, justifyContent: 'flex-end', gap: 20 }}>
|
||||
<View>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 22,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.InterSemiBold,
|
||||
textAlign: "center",
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
Ton décor est en cours de{"\n"}création
|
||||
Ton décor est en cours de{'\n'}création
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 14,
|
||||
color: Palette.gray,
|
||||
fontFamily: FONT_FAMILY.InterRegular,
|
||||
textAlign: "center",
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
Veuillez patienter
|
||||
</Text>
|
||||
</View>
|
||||
<View style={{ alignItems: "center", gap: 16 }}>
|
||||
<View style={{ alignItems: 'center', gap: 16 }}>
|
||||
<ProgressBar gradient progress={progress} />
|
||||
<Text
|
||||
style={{
|
||||
@@ -115,8 +115,8 @@ const CreatingDecor = () => {
|
||||
<GradientButton
|
||||
title="Découvrir mon Playback"
|
||||
containerStyle={{
|
||||
width: "80%",
|
||||
alignSelf: "center",
|
||||
width: '80%',
|
||||
alignSelf: 'center',
|
||||
}}
|
||||
onPress={() => navigate(Routes.VideoFinalize)}
|
||||
/>
|
||||
@@ -126,7 +126,7 @@ const CreatingDecor = () => {
|
||||
</View>
|
||||
</View>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default CreatingDecor;
|
||||
export default CreatingDecor
|
||||
|
||||
@@ -1,51 +1,48 @@
|
||||
import React, { useCallback, useState } from "react";
|
||||
import { Image, StyleSheet, View } from "react-native";
|
||||
import { ai, background } from "../../assets";
|
||||
import BorderGradientButton from "../../components/BorderGradientButton";
|
||||
import FullscreenIntroVideo from "../../components/FullscreenIntroVideo";
|
||||
import GradientButton from "../../components/GradientButton";
|
||||
import MusicLandHeader from "../../components/MusicLandHeader";
|
||||
import Page from "../../layouts/Page";
|
||||
import { isWeb } from "../../hooks/useLayoutType";
|
||||
import { Routes } from "../../navigation";
|
||||
import { navigate } from "../../navigation/NavigationService";
|
||||
import { useUser } from "../../providers/UserDataProvider";
|
||||
import { gutters } from "../../styles";
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import { Image, StyleSheet, View } from 'react-native'
|
||||
import { ai, background } from '../../assets'
|
||||
import BorderGradientButton from '../../components/BorderGradientButton'
|
||||
import FullscreenIntroVideo from '../../components/FullscreenIntroVideo'
|
||||
import GradientButton from '../../components/GradientButton'
|
||||
import MusicLandHeader from '../../components/MusicLandHeader'
|
||||
import Page from '../../layouts/Page'
|
||||
import { isWeb } from '../../hooks/useLayoutType'
|
||||
import { Routes } from '../../navigation'
|
||||
import { navigate } from '../../navigation/NavigationService'
|
||||
import { useUser } from '../../providers/UserDataProvider'
|
||||
import { gutters } from '../../styles'
|
||||
|
||||
const Playback = ({ route, navigation }) => {
|
||||
const { project } = route.params || {};
|
||||
const { videos } = useUser();
|
||||
const { project } = route.params || {}
|
||||
const { videos } = useUser()
|
||||
// const benhaiUrl = isWeb ? videos?.benhaiWeb || null : videos?.benhai;
|
||||
const theoUrl = isWeb ? videos?.theoWeb : videos?.theo;
|
||||
const [showIntro, setShowIntro] = useState(theoUrl);
|
||||
const theoUrl = isWeb ? videos?.theoWeb : videos?.theo
|
||||
const [showIntro, setShowIntro] = useState(theoUrl)
|
||||
|
||||
const handleCloseIntro = () => {
|
||||
setShowIntro(null);
|
||||
};
|
||||
setShowIntro(null)
|
||||
}
|
||||
|
||||
const onPressRecord = useCallback(() => {
|
||||
navigate(Routes.RecordPlayback, { project });
|
||||
}, [project]);
|
||||
navigate(Routes.RecordPlayback, { project })
|
||||
}, [project])
|
||||
|
||||
return (
|
||||
<Page headerType="NONE" backgroundImg={isWeb ? background.playbackWeb : background.playbackMobile}>
|
||||
<Page
|
||||
headerType="NONE"
|
||||
backgroundImg={isWeb ? background.playbackWeb : background.playbackMobile}
|
||||
>
|
||||
{/* <Image source={ai.theo} style={styles.img} resizeMode="contain" /> */}
|
||||
<MusicLandHeader
|
||||
onPressBack={() => navigation.navigate(Routes.Home)}
|
||||
progress={25}
|
||||
/>
|
||||
<MusicLandHeader onPressBack={() => navigation.navigate(Routes.Home)} progress={25} />
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
paddingBottom: gutters * 2,
|
||||
justifyContent: "flex-end",
|
||||
justifyContent: 'flex-end',
|
||||
}}
|
||||
>
|
||||
<View style={{ width: "80%", alignSelf: "center", gap: 12 }}>
|
||||
<GradientButton
|
||||
title="Enregistrer mon Playback"
|
||||
onPress={onPressRecord}
|
||||
/>
|
||||
<View style={{ width: '80%', alignSelf: 'center', gap: 12 }}>
|
||||
<GradientButton title="Enregistrer mon Playback" onPress={onPressRecord} />
|
||||
<BorderGradientButton
|
||||
title="Guide du Playbacker"
|
||||
onPress={() => navigate(Routes.PlaybackGuide)}
|
||||
@@ -53,22 +50,18 @@ const Playback = ({ route, navigation }) => {
|
||||
{/* <BorderGradientButton title="Importer une vidéo" /> */}
|
||||
</View>
|
||||
</View>
|
||||
<FullscreenIntroVideo
|
||||
url={showIntro}
|
||||
visible={showIntro}
|
||||
onClose={handleCloseIntro}
|
||||
/>
|
||||
<FullscreenIntroVideo url={showIntro} visible={showIntro} onClose={handleCloseIntro} />
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default Playback;
|
||||
export default Playback
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
img: {
|
||||
width: "100%",
|
||||
height: "70%",
|
||||
position: "absolute",
|
||||
width: '100%',
|
||||
height: '70%',
|
||||
position: 'absolute',
|
||||
bottom: -40,
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
@@ -1,341 +1,359 @@
|
||||
import React from "react";
|
||||
import { ScrollView, StyleSheet, Text, View, Platform } from "react-native";
|
||||
import { BlurView } from "expo-blur";
|
||||
import { background } from "../../assets";
|
||||
import MusicLandHeader from "../../components/MusicLandHeader";
|
||||
import Page from "../../layouts/Page";
|
||||
import { Routes } from "../../navigation";
|
||||
import { gutters, Palette } from "../../styles";
|
||||
import { FONT_FAMILY } from "../../styles/Fonts";
|
||||
import { isWeb } from "../../hooks/useLayoutType";
|
||||
import React from 'react'
|
||||
import { ScrollView, StyleSheet, Text, View, Platform } from 'react-native'
|
||||
import { BlurView } from 'expo-blur'
|
||||
import { background } from '../../assets'
|
||||
import MusicLandHeader from '../../components/MusicLandHeader'
|
||||
import Page from '../../layouts/Page'
|
||||
import { Routes } from '../../navigation'
|
||||
import { gutters, Palette } from '../../styles'
|
||||
import { FONT_FAMILY } from '../../styles/Fonts'
|
||||
import { isWeb } from '../../hooks/useLayoutType'
|
||||
|
||||
const PlaybackGuide = ({ navigation }) => {
|
||||
return (
|
||||
<Page headerType="NONE" backgroundImg={isWeb ? background.playbackWeb : background.playbackMobile}>
|
||||
<MusicLandHeader onPressBack={() => navigation.goBack()} />
|
||||
<View style={styles.container}>
|
||||
<BlurView intensity={Platform.OS === "ios" ? 80 : 40} style={styles.blurContainer} experimentalBlurMethod="dimezisBlurView">
|
||||
<ScrollView
|
||||
contentContainerStyle={styles.scrollContent}
|
||||
showsVerticalScrollIndicator={false}
|
||||
>
|
||||
<Text style={styles.title}>Le Guide Ultime du Playback de TA Chanson : L'Expérience Créative Totale</Text>
|
||||
return (
|
||||
<Page
|
||||
headerType="NONE"
|
||||
backgroundImg={isWeb ? background.playbackWeb : background.playbackMobile}
|
||||
>
|
||||
<MusicLandHeader onPressBack={() => navigation.goBack()} />
|
||||
<View style={styles.container}>
|
||||
<BlurView
|
||||
intensity={Platform.OS === 'ios' ? 80 : 40}
|
||||
style={styles.blurContainer}
|
||||
experimentalBlurMethod="dimezisBlurView"
|
||||
>
|
||||
<ScrollView
|
||||
contentContainerStyle={styles.scrollContent}
|
||||
showsVerticalScrollIndicator={false}
|
||||
>
|
||||
<Text style={styles.title}>
|
||||
Le Guide Ultime du Playback de TA Chanson : L'Expérience Créative Totale
|
||||
</Text>
|
||||
|
||||
<Text style={styles.sectionTitle}>Introduction : Tu Es l'Artiste ET le Performeur !</Text>
|
||||
<Text style={styles.text}>
|
||||
Imagine ça : tu as créé Ta chanson, TES paroles, TA mélodie, TON univers.
|
||||
Et maintenant, tu vas lui donner vie en playback. C'est une expérience UNIQUE au monde !
|
||||
Tu n'imites personne - tu deviens la version scénique de toi-même. C'est comme avoir un
|
||||
jumeau qui chante pendant que toi, tu performes. Double dose de créativité, double dose de
|
||||
fun !
|
||||
</Text>
|
||||
<Text style={styles.sectionTitle}>
|
||||
Introduction : Tu Es l'Artiste ET le Performeur !
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
Imagine ça : tu as créé Ta chanson, TES paroles, TA mélodie, TON univers. Et
|
||||
maintenant, tu vas lui donner vie en playback. C'est une expérience UNIQUE au monde !
|
||||
Tu n'imites personne - tu deviens la version scénique de toi-même. C'est comme avoir
|
||||
un jumeau qui chante pendant que toi, tu performes. Double dose de créativité, double
|
||||
dose de fun !
|
||||
</Text>
|
||||
|
||||
<Text style={styles.chapterTitle}>Chapitre 1 : Le Lip-Sync de Ta Propre Création - L'Avantage Ultime</Text>
|
||||
<Text style={styles.chapterTitle}>
|
||||
Chapitre 1 : Le Lip-Sync de Ta Propre Création - L'Avantage Ultime
|
||||
</Text>
|
||||
|
||||
<Text style={styles.subTitle}>Le Pouvoir d'Être le Créateur</Text>
|
||||
<Text style={styles.text}>
|
||||
Tu as un super-pouvoir que personne d'autre n'a : tu connais CHAQUE intention derrière
|
||||
chaque mot, chaque respiration, chaque silence. Tu n'as pas à deviner ce que l'artiste
|
||||
voulait dire - c'est TOI l'artiste ! Cette connexion intime avec ta chanson va transparaître
|
||||
dans chaque mouvement de tes lèvres.
|
||||
</Text>
|
||||
<Text style={styles.subTitle}>Le Pouvoir d'Être le Créateur</Text>
|
||||
<Text style={styles.text}>
|
||||
Tu as un super-pouvoir que personne d'autre n'a : tu connais CHAQUE intention derrière
|
||||
chaque mot, chaque respiration, chaque silence. Tu n'as pas à deviner ce que l'artiste
|
||||
voulait dire - c'est TOI l'artiste ! Cette connexion intime avec ta chanson va
|
||||
transparaître dans chaque mouvement de tes lèvres.
|
||||
</Text>
|
||||
|
||||
<Text style={styles.subTitle}>La Règle des 15 Écoutes - Version Créateur</Text>
|
||||
<Text style={styles.text}>
|
||||
Oui, même pour TA chanson, les 15 écoutes sont cruciales ! Mais avec une twist magique :
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
<Text style={styles.bold}>Écoutes 1-5 : Redécouvre Ta Création</Text> Écoute ta chanson comme si c'était la première
|
||||
fois. Tu vas être surpris de détails que tu avais oubliés !
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
<Text style={styles.bold}>Écoutes 6-10 : L'Analyse du Créateur</Text>
|
||||
{"\n"}• Où as-tu placé les émotions fortes ?
|
||||
{"\n"}• Quels mots méritent d'être sur-articulés ?
|
||||
{"\n"}• Où sont tes respirations naturelles dans l'enregistrement ?
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
<Text style={styles.bold}>Écoutes 11-15 : La Transformation</Text> Maintenant, détache-toi du "toi qui a enregistré" pour
|
||||
devenir le "toi qui performe". C'est là que la magie opère !
|
||||
</Text>
|
||||
<Text style={styles.subTitle}>La Règle des 15 Écoutes - Version Créateur</Text>
|
||||
<Text style={styles.text}>
|
||||
Oui, même pour TA chanson, les 15 écoutes sont cruciales ! Mais avec une twist magique
|
||||
:
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
<Text style={styles.bold}>Écoutes 1-5 : Redécouvre Ta Création</Text> Écoute ta
|
||||
chanson comme si c'était la première fois. Tu vas être surpris de détails que tu avais
|
||||
oubliés !
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
<Text style={styles.bold}>Écoutes 6-10 : L'Analyse du Créateur</Text>
|
||||
{'\n'}• Où as-tu placé les émotions fortes ?{'\n'}• Quels mots méritent d'être
|
||||
sur-articulés ?{'\n'}• Où sont tes respirations naturelles dans l'enregistrement ?
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
<Text style={styles.bold}>Écoutes 11-15 : La Transformation</Text> Maintenant,
|
||||
détache-toi du "toi qui a enregistré" pour devenir le "toi qui performe". C'est là que
|
||||
la magie opère !
|
||||
</Text>
|
||||
|
||||
<Text style={styles.subTitle}>Les Secrets du Créateur-Performeur</Text>
|
||||
<Text style={styles.text}>
|
||||
<Text style={styles.bold}>Le Phénomène de Dédoublement</Text> C'est normal de se sentir bizarre au début - tu entends
|
||||
ta voix mais tu ne chantes pas ! Embrasse cette sensation unique. C'est comme être ton
|
||||
propre fan numéro un.
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
<Text style={styles.bold}>La Liberté de Réinterpréter</Text> Tu as le droit de changer l'émotion ! Si tu as enregistré la
|
||||
chanson en étant mélancolique mais qu'aujourd'hui tu la sens joyeuse - VAS-Y ! C'est TA
|
||||
chanson, tu peux la réinventer à chaque performance.
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
<Text style={styles.bold}>L'Anticipation Parfaite</Text> Tu connais chaque micro-pause, chaque inflexion. Utilise cet
|
||||
avantage ! Anticipe les moments forts avec ton expression faciale une fraction de seconde
|
||||
avant qu'ils arrivent.
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
<Text style={styles.bold}>Le Truc du "Nouveau Moi"</Text> Crée un personnage scénique de toi-même. Peut-être que le
|
||||
"toi studio" est introverti, mais le "toi playback" est extraverti. Cette dualité est fascinante et
|
||||
totalement unique à ton expérience !
|
||||
</Text>
|
||||
<Text style={styles.subTitle}>Les Secrets du Créateur-Performeur</Text>
|
||||
<Text style={styles.text}>
|
||||
<Text style={styles.bold}>Le Phénomène de Dédoublement</Text> C'est normal de se
|
||||
sentir bizarre au début - tu entends ta voix mais tu ne chantes pas ! Embrasse cette
|
||||
sensation unique. C'est comme être ton propre fan numéro un.
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
<Text style={styles.bold}>La Liberté de Réinterpréter</Text> Tu as le droit de changer
|
||||
l'émotion ! Si tu as enregistré la chanson en étant mélancolique mais qu'aujourd'hui
|
||||
tu la sens joyeuse - VAS-Y ! C'est TA chanson, tu peux la réinventer à chaque
|
||||
performance.
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
<Text style={styles.bold}>L'Anticipation Parfaite</Text> Tu connais chaque
|
||||
micro-pause, chaque inflexion. Utilise cet avantage ! Anticipe les moments forts avec
|
||||
ton expression faciale une fraction de seconde avant qu'ils arrivent.
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
<Text style={styles.bold}>Le Truc du "Nouveau Moi"</Text> Crée un personnage scénique
|
||||
de toi-même. Peut-être que le "toi studio" est introverti, mais le "toi playback" est
|
||||
extraverti. Cette dualité est fascinante et totalement unique à ton expérience !
|
||||
</Text>
|
||||
|
||||
<Text style={styles.chapterTitle}>Chapitre 2 : Capturer l'Essence de Ta Création</Text>
|
||||
<Text style={styles.chapterTitle}>Chapitre 2 : Capturer l'Essence de Ta Création</Text>
|
||||
|
||||
<Text style={styles.subTitle}>Ton Smartphone, Ton Studio Visuel</Text>
|
||||
<Text style={styles.text}>
|
||||
Quand tu filmes le playback de TA chanson, tu complètes l'œuvre. La musique était l'audio,
|
||||
maintenant tu ajoutes le visuel !
|
||||
</Text>
|
||||
<Text style={styles.subTitle}>Ton Smartphone, Ton Studio Visuel</Text>
|
||||
<Text style={styles.text}>
|
||||
Quand tu filmes le playback de TA chanson, tu complètes l'œuvre. La musique était
|
||||
l'audio, maintenant tu ajoutes le visuel !
|
||||
</Text>
|
||||
|
||||
<Text style={styles.subTitle}>Les Angles Qui Racontent Ton Histoire</Text>
|
||||
<Text style={styles.text}>
|
||||
<Text style={styles.bold}>L'Angle "Confession" (Très Proche)</Text> Pour les parties intimes de ta chanson. Le spectateur
|
||||
doit sentir que tu lui confies un secret.
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
<Text style={styles.bold}>L'Angle "Concert" (Plan Large)</Text> Montre l'artiste complet en toi ! Parfait pour les refrains
|
||||
énergiques où tu veux bouger.
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
<Text style={styles.bold}>L'Angle "Clip Musical" (Variété)</Text> Change d'angle selon les parties de ta chanson. Verse 1
|
||||
proche, refrain large, pont en profil... Raconte visuellement l'histoire de ta musique.
|
||||
</Text>
|
||||
<Text style={styles.subTitle}>Les Angles Qui Racontent Ton Histoire</Text>
|
||||
<Text style={styles.text}>
|
||||
<Text style={styles.bold}>L'Angle "Confession" (Très Proche)</Text> Pour les parties
|
||||
intimes de ta chanson. Le spectateur doit sentir que tu lui confies un secret.
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
<Text style={styles.bold}>L'Angle "Concert" (Plan Large)</Text> Montre l'artiste
|
||||
complet en toi ! Parfait pour les refrains énergiques où tu veux bouger.
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
<Text style={styles.bold}>L'Angle "Clip Musical" (Variété)</Text> Change d'angle selon
|
||||
les parties de ta chanson. Verse 1 proche, refrain large, pont en profil... Raconte
|
||||
visuellement l'histoire de ta musique.
|
||||
</Text>
|
||||
|
||||
<Text style={styles.subTitle}>L'Accessoire Signature : L'Instrument de Ta Création</Text>
|
||||
<Text style={styles.text}>
|
||||
C'est encore plus puissant quand c'est TA chanson !
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
Si tu as composé avec un instrument :
|
||||
{"\n"}• Mime-le même si tu ne l'as pas sous la main
|
||||
{"\n"}• Montre les accords que TU as choisis
|
||||
{"\n"}• Tes mains connaissent les mouvements par cœur
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
Si c'est une création digitale :
|
||||
{"\n"}• Un casque audio autour du cou = le créateur
|
||||
{"\n"}• Un smartphone en main = le compositeur moderne
|
||||
{"\n"}• Les mains qui "mixent" dans l'air = le producteur
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
L'accessoire émotionnel :
|
||||
{"\n"}• Un carnet (tes paroles originales !)
|
||||
{"\n"}• Un objet qui t'a inspiré la chanson
|
||||
{"\n"}• Un élément qui représente le thème de ta chanson
|
||||
</Text>
|
||||
<Text style={styles.subTitle}>
|
||||
L'Accessoire Signature : L'Instrument de Ta Création
|
||||
</Text>
|
||||
<Text style={styles.text}>C'est encore plus puissant quand c'est TA chanson !</Text>
|
||||
<Text style={styles.text}>
|
||||
Si tu as composé avec un instrument :{'\n'}• Mime-le même si tu ne l'as pas sous la
|
||||
main
|
||||
{'\n'}• Montre les accords que TU as choisis
|
||||
{'\n'}• Tes mains connaissent les mouvements par cœur
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
Si c'est une création digitale :{'\n'}• Un casque audio autour du cou = le créateur
|
||||
{'\n'}• Un smartphone en main = le compositeur moderne
|
||||
{'\n'}• Les mains qui "mixent" dans l'air = le producteur
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
L'accessoire émotionnel :{'\n'}• Un carnet (tes paroles originales !)
|
||||
{'\n'}• Un objet qui t'a inspiré la chanson
|
||||
{'\n'}• Un élément qui représente le thème de ta chanson
|
||||
</Text>
|
||||
|
||||
<Text style={styles.chapterTitle}>Chapitre 3 : La Scène de Ta Première</Text>
|
||||
<Text style={styles.chapterTitle}>Chapitre 3 : La Scène de Ta Première</Text>
|
||||
|
||||
<Text style={styles.subTitle}>Chaque Lieu Devient TON Lieu</Text>
|
||||
<Text style={styles.text}>
|
||||
Quand tu performes ta propre chanson, chaque endroit prend une signification spéciale. Ce
|
||||
n'est pas juste un décor, c'est le lieu de naissance visuelle de ta création !
|
||||
</Text>
|
||||
<Text style={styles.subTitle}>Chaque Lieu Devient TON Lieu</Text>
|
||||
<Text style={styles.text}>
|
||||
Quand tu performes ta propre chanson, chaque endroit prend une signification spéciale.
|
||||
Ce n'est pas juste un décor, c'est le lieu de naissance visuelle de ta création !
|
||||
</Text>
|
||||
|
||||
<Text style={styles.subTitle}>Les Scènes Qui Amplifient Ta Création</Text>
|
||||
<Text style={styles.text}>
|
||||
<Text style={styles.bold}>L'Endroit Où Tu l'as Écrite - Le Retour aux Sources</Text> Si tu as écrit ta chanson dans ta
|
||||
chambre, filme-là ! C'est puissant de montrer l'artiste là où la magie a commencé.
|
||||
L'authenticité sera palpable.
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
<Text style={styles.bold}>Le Studio Maison - Là Où Tu l'as Enregistrée</Text> Montre ton setup, même basique ! Un
|
||||
micro, un ordinateur, des câbles... C'est TON univers créatif. Les gens adorent voir les
|
||||
coulisses.
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
<Text style={styles.bold}>Le Lieu Qui t'a Inspiré - La Boucle Créative</Text> Tu as écrit sur la plage ? Sur l'amour au café
|
||||
? Sur la solitude au parc ? Retourne-y ! La cohérence entre ton inspiration et ta performance
|
||||
est magique.
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
<Text style={styles.bold}>La Scène Imaginaire - Ce Que Tu Visualisais</Text> Quand tu as créé cette chanson, où
|
||||
t'imaginais-tu la performer ? Un grand stade ? Un petit club intime ? Recrée cette ambiance
|
||||
avec ce que tu as !
|
||||
</Text>
|
||||
<Text style={styles.subTitle}>Les Scènes Qui Amplifient Ta Création</Text>
|
||||
<Text style={styles.text}>
|
||||
<Text style={styles.bold}>L'Endroit Où Tu l'as Écrite - Le Retour aux Sources</Text>{' '}
|
||||
Si tu as écrit ta chanson dans ta chambre, filme-là ! C'est puissant de montrer
|
||||
l'artiste là où la magie a commencé. L'authenticité sera palpable.
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
<Text style={styles.bold}>Le Studio Maison - Là Où Tu l'as Enregistrée</Text> Montre
|
||||
ton setup, même basique ! Un micro, un ordinateur, des câbles... C'est TON univers
|
||||
créatif. Les gens adorent voir les coulisses.
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
<Text style={styles.bold}>Le Lieu Qui t'a Inspiré - La Boucle Créative</Text> Tu as
|
||||
écrit sur la plage ? Sur l'amour au café ? Sur la solitude au parc ? Retourne-y ! La
|
||||
cohérence entre ton inspiration et ta performance est magique.
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
<Text style={styles.bold}>La Scène Imaginaire - Ce Que Tu Visualisais</Text> Quand tu
|
||||
as créé cette chanson, où t'imaginais-tu la performer ? Un grand stade ? Un petit club
|
||||
intime ? Recrée cette ambiance avec ce que tu as !
|
||||
</Text>
|
||||
|
||||
<Text style={styles.subTitle}>Suggestions Créatives Uniques :</Text>
|
||||
<Text style={styles.text}>
|
||||
• La Voiture : Si c'est là que tu as eu l'idée de la mélodie
|
||||
{"\n"}• La Douche : Avec le téléphone protégé, si c'est là que tu chantes toujours
|
||||
{"\n"}• Le Toit/Balcon : Pour une sensation de "crier au monde" ta création
|
||||
{"\n"}• Devant le Miroir : Le dialogue entre toi créateur et toi performeur
|
||||
{"\n"}• Dans le Noir avec une Seule Lumière : Pour l'aspect dramatique et intime
|
||||
</Text>
|
||||
<Text style={styles.subTitle}>Suggestions Créatives Uniques :</Text>
|
||||
<Text style={styles.text}>
|
||||
• La Voiture : Si c'est là que tu as eu l'idée de la mélodie
|
||||
{'\n'}• La Douche : Avec le téléphone protégé, si c'est là que tu chantes toujours
|
||||
{'\n'}• Le Toit/Balcon : Pour une sensation de "crier au monde" ta création
|
||||
{'\n'}• Devant le Miroir : Le dialogue entre toi créateur et toi performeur
|
||||
{'\n'}• Dans le Noir avec une Seule Lumière : Pour l'aspect dramatique et intime
|
||||
</Text>
|
||||
|
||||
<Text style={styles.chapterTitle}>Chapitre 4 : Le Mindset du Créateur-Performeur</Text>
|
||||
<Text style={styles.chapterTitle}>Chapitre 4 : Le Mindset du Créateur-Performeur</Text>
|
||||
|
||||
<Text style={styles.subTitle}>L'Expérience Unique au Monde</Text>
|
||||
<Text style={styles.text}>
|
||||
Ce que tu fais est EXTRAORDINAIRE. Tu es à la fois Mozart et le pianiste, Beyoncé et la
|
||||
performeuse, le compositeur ET l'interprète. Cette dualité créative est rare et précieuse !
|
||||
</Text>
|
||||
<Text style={styles.subTitle}>L'Expérience Unique au Monde</Text>
|
||||
<Text style={styles.text}>
|
||||
Ce que tu fais est EXTRAORDINAIRE. Tu es à la fois Mozart et le pianiste, Beyoncé et
|
||||
la performeuse, le compositeur ET l'interprète. Cette dualité créative est rare et
|
||||
précieuse !
|
||||
</Text>
|
||||
|
||||
<Text style={styles.subTitle}>Les Mantras du Créateur en Playback</Text>
|
||||
<Text style={styles.text}>
|
||||
<Text style={styles.bold}>"C'est MON Bébé Musical"</Text> Cette chanson est née de toi. Chaque note, chaque mot est ton
|
||||
enfant créatif. Quand tu la performes en playback, tu lui donnes une nouvelle dimension.
|
||||
Sois fier !
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
<Text style={styles.bold}>"Je Me Réinvente à Chaque Prise"</Text> L'avantage de faire le playback de TA chanson ? Tu
|
||||
peux explorer 1000 façons de l'interpréter ! Mélancolique le matin, énergique le soir. C'est ta
|
||||
playground créative.
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
<Text style={styles.bold}>"Mes Deux Moi Collaborent"</Text> Le toi qui a chanté et le toi qui performe sont une équipe.
|
||||
C'est une collaboration unique avec toi-même à travers le temps !
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
<Text style={styles.bold}>"C'est Mon Histoire, Mes Règles"</Text> Personne ne peut te dire "ce n'est pas comme ça que
|
||||
l'artiste le fait" - parce que l'artiste, c'est TOI !
|
||||
</Text>
|
||||
<Text style={styles.subTitle}>Les Mantras du Créateur en Playback</Text>
|
||||
<Text style={styles.text}>
|
||||
<Text style={styles.bold}>"C'est MON Bébé Musical"</Text> Cette chanson est née de
|
||||
toi. Chaque note, chaque mot est ton enfant créatif. Quand tu la performes en
|
||||
playback, tu lui donnes une nouvelle dimension. Sois fier !
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
<Text style={styles.bold}>"Je Me Réinvente à Chaque Prise"</Text> L'avantage de faire
|
||||
le playback de TA chanson ? Tu peux explorer 1000 façons de l'interpréter !
|
||||
Mélancolique le matin, énergique le soir. C'est ta playground créative.
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
<Text style={styles.bold}>"Mes Deux Moi Collaborent"</Text> Le toi qui a chanté et le
|
||||
toi qui performe sont une équipe. C'est une collaboration unique avec toi-même à
|
||||
travers le temps !
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
<Text style={styles.bold}>"C'est Mon Histoire, Mes Règles"</Text> Personne ne peut te
|
||||
dire "ce n'est pas comme ça que l'artiste le fait" - parce que l'artiste, c'est TOI !
|
||||
</Text>
|
||||
|
||||
<Text style={styles.subTitle}>La Célébration de Ta Création</Text>
|
||||
<Text style={styles.text}>
|
||||
<Text style={styles.bold}>Le Rituel du Créateur :</Text>
|
||||
{"\n"}1. Écoute ta chanson une fois les yeux fermés - Reconnecte avec ton intention originale
|
||||
{"\n"}2. Dis à voix haute : "J'ai créé quelque chose qui n'existait pas avant"
|
||||
{"\n"}3. Danse 30 secondes sur TA création - Célèbre-toi !
|
||||
{"\n"}4. Filme avec fierté - Ce moment est historique dans ton parcours
|
||||
</Text>
|
||||
<Text style={styles.subTitle}>La Célébration de Ta Création</Text>
|
||||
<Text style={styles.text}>
|
||||
<Text style={styles.bold}>Le Rituel du Créateur :</Text>
|
||||
{'\n'}1. Écoute ta chanson une fois les yeux fermés - Reconnecte avec ton intention
|
||||
originale
|
||||
{'\n'}2. Dis à voix haute : "J'ai créé quelque chose qui n'existait pas avant"
|
||||
{'\n'}3. Danse 30 secondes sur TA création - Célèbre-toi !{'\n'}4. Filme avec fierté -
|
||||
Ce moment est historique dans ton parcours
|
||||
</Text>
|
||||
|
||||
<Text style={styles.subTitle}>Le Partage de Ton Univers</Text>
|
||||
<Text style={styles.text}>
|
||||
Quand tu partages le playback de ta propre chanson, tu offres une expérience complète :
|
||||
{"\n"}• Les gens découvrent ta musique
|
||||
{"\n"}• Ils voient ta personnalité
|
||||
{"\n"}• Ils connectent avec l'artiste complet que tu es
|
||||
{"\n"}• Ils witness la naissance visuelle de ton art
|
||||
</Text>
|
||||
<Text style={styles.subTitle}>Le Partage de Ton Univers</Text>
|
||||
<Text style={styles.text}>
|
||||
Quand tu partages le playback de ta propre chanson, tu offres une expérience complète
|
||||
:{'\n'}• Les gens découvrent ta musique
|
||||
{'\n'}• Ils voient ta personnalité
|
||||
{'\n'}• Ils connectent avec l'artiste complet que tu es
|
||||
{'\n'}• Ils witness la naissance visuelle de ton art
|
||||
</Text>
|
||||
|
||||
<Text style={styles.chapterTitle}>Résumé : Ton Parcours Créatif Unique</Text>
|
||||
<Text style={styles.chapterTitle}>Résumé : Ton Parcours Créatif Unique</Text>
|
||||
|
||||
<Text style={styles.subTitle}>🎵 Chapitre 1 - Ton Lip-Sync Signature</Text>
|
||||
<Text style={styles.text}>
|
||||
• Tu connais chaque intention : Avantage ultime du créateur
|
||||
{"\n"}• 15 écoutes pour redécouvrir : Même ta création mérite cette attention
|
||||
{"\n"}• Le dédoublement créatif : Embrasse d'être ton propre interprète
|
||||
{"\n"}• Liberté totale : Réinterprète selon ton humeur du jour
|
||||
</Text>
|
||||
<Text style={styles.subTitle}>🎵 Chapitre 1 - Ton Lip-Sync Signature</Text>
|
||||
<Text style={styles.text}>
|
||||
• Tu connais chaque intention : Avantage ultime du créateur
|
||||
{'\n'}• 15 écoutes pour redécouvrir : Même ta création mérite cette attention
|
||||
{'\n'}• Le dédoublement créatif : Embrasse d'être ton propre interprète
|
||||
{'\n'}• Liberté totale : Réinterprète selon ton humeur du jour
|
||||
</Text>
|
||||
|
||||
<Text style={styles.subTitle}>🎬 Chapitre 2 - Ton Setup Créatif</Text>
|
||||
<Text style={styles.text}>
|
||||
• Les angles qui racontent : Confession, concert, clip
|
||||
{"\n"}• L'instrument de création : Montre ton processus créatif
|
||||
{"\n"}• Accessoires significatifs : Objets liés à ton inspiration
|
||||
{"\n"}• L'authenticité avant tout : Ton setup, tes règles
|
||||
</Text>
|
||||
<Text style={styles.subTitle}>🎬 Chapitre 2 - Ton Setup Créatif</Text>
|
||||
<Text style={styles.text}>
|
||||
• Les angles qui racontent : Confession, concert, clip
|
||||
{'\n'}• L'instrument de création : Montre ton processus créatif
|
||||
{'\n'}• Accessoires significatifs : Objets liés à ton inspiration
|
||||
{'\n'}• L'authenticité avant tout : Ton setup, tes règles
|
||||
</Text>
|
||||
|
||||
<Text style={styles.subTitle}>🎭 Chapitre 3 - Tes Lieux de Création</Text>
|
||||
<Text style={styles.text}>
|
||||
• Retour aux sources : Là où tu as créé
|
||||
{"\n"}• Le lieu d'inspiration : Boucle ta vision créative
|
||||
{"\n"}• La scène rêvée : Matérialise ta vision
|
||||
{"\n"}• Chaque lieu = ton histoire : Personnalise à fond
|
||||
</Text>
|
||||
<Text style={styles.subTitle}>🎭 Chapitre 3 - Tes Lieux de Création</Text>
|
||||
<Text style={styles.text}>
|
||||
• Retour aux sources : Là où tu as créé
|
||||
{'\n'}• Le lieu d'inspiration : Boucle ta vision créative
|
||||
{'\n'}• La scène rêvée : Matérialise ta vision
|
||||
{'\n'}• Chaque lieu = ton histoire : Personnalise à fond
|
||||
</Text>
|
||||
|
||||
<Text style={styles.subTitle}>🌟 Chapitre 4 - Ton Mindset Unique</Text>
|
||||
<Text style={styles.text}>
|
||||
• Double créativité : Compositeur ET performeur
|
||||
{"\n"}• Fierté créative : Tu as créé quelque chose de rien
|
||||
{"\n"}• Réinvention constante : Explore toutes les facettes
|
||||
{"\n"}• Histoire complète : Tu offres l'expérience totale
|
||||
</Text>
|
||||
<Text style={styles.subTitle}>🌟 Chapitre 4 - Ton Mindset Unique</Text>
|
||||
<Text style={styles.text}>
|
||||
• Double créativité : Compositeur ET performeur
|
||||
{'\n'}• Fierté créative : Tu as créé quelque chose de rien
|
||||
{'\n'}• Réinvention constante : Explore toutes les facettes
|
||||
{'\n'}• Histoire complète : Tu offres l'expérience totale
|
||||
</Text>
|
||||
|
||||
<Text style={styles.chapterTitle}>Ton Défi Ultime : La Première Mondiale !</Text>
|
||||
<Text style={styles.text}>
|
||||
Tu es sur le point de faire quelque chose qu'AUCUN autre artiste ne peut faire : créer la
|
||||
performance visuelle parfaite de TA chanson. C'est ta première mondiale, ton moment
|
||||
unique !
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
Dans les 24 prochaines heures, donne vie visuellement à ta création. Choisis ta chanson
|
||||
préférée (parmi les tiennes !), trouve ton spot créatif, et MONTRE au monde non seulement
|
||||
ta musique, mais TOI en train de la vivre.
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
Rappelle-toi : Tu n'es pas en train d'imiter. Tu n'es pas en train de copier. Tu es en train de
|
||||
COMPLÉTER ton œuvre. La chanson était le tableau, le playback est le cadre qui le
|
||||
sublime.
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
C'est ton moment. Ta chanson. Ta performance. Ton histoire.
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
Le monde attend de découvrir l'artiste complet que tu es ! 🎤🎵✨
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
#MonPlaybackMaChanson #CréateurPerformeur #DoubleArtiste
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
P.S. : Garde chaque prise, même les "ratées". Dans 10 ans, tu regarderas ces vidéos et tu
|
||||
verras le début de ton parcours d'artiste. C'est de l'or pur !
|
||||
</Text>
|
||||
</ScrollView>
|
||||
</BlurView>
|
||||
</View>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
<Text style={styles.chapterTitle}>Ton Défi Ultime : La Première Mondiale !</Text>
|
||||
<Text style={styles.text}>
|
||||
Tu es sur le point de faire quelque chose qu'AUCUN autre artiste ne peut faire : créer
|
||||
la performance visuelle parfaite de TA chanson. C'est ta première mondiale, ton moment
|
||||
unique !
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
Dans les 24 prochaines heures, donne vie visuellement à ta création. Choisis ta
|
||||
chanson préférée (parmi les tiennes !), trouve ton spot créatif, et MONTRE au monde
|
||||
non seulement ta musique, mais TOI en train de la vivre.
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
Rappelle-toi : Tu n'es pas en train d'imiter. Tu n'es pas en train de copier. Tu es en
|
||||
train de COMPLÉTER ton œuvre. La chanson était le tableau, le playback est le cadre
|
||||
qui le sublime.
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
C'est ton moment. Ta chanson. Ta performance. Ton histoire.
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
Le monde attend de découvrir l'artiste complet que tu es ! 🎤🎵✨
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
#MonPlaybackMaChanson #CréateurPerformeur #DoubleArtiste
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
P.S. : Garde chaque prise, même les "ratées". Dans 10 ans, tu regarderas ces vidéos et
|
||||
tu verras le début de ton parcours d'artiste. C'est de l'or pur !
|
||||
</Text>
|
||||
</ScrollView>
|
||||
</BlurView>
|
||||
</View>
|
||||
</Page>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
marginHorizontal: Platform.OS === "web" ? gutters : 0,
|
||||
marginBottom: gutters,
|
||||
borderRadius: 20,
|
||||
overflow: "hidden",
|
||||
},
|
||||
blurContainer: {
|
||||
flex: 1,
|
||||
backgroundColor: Palette.glass,
|
||||
},
|
||||
scrollContent: {
|
||||
padding: gutters,
|
||||
paddingBottom: gutters * 3,
|
||||
},
|
||||
title: {
|
||||
fontFamily: FONT_FAMILY.InterBold,
|
||||
fontSize: 24,
|
||||
color: Palette.white,
|
||||
textAlign: "center",
|
||||
marginBottom: 20,
|
||||
},
|
||||
sectionTitle: {
|
||||
fontFamily: FONT_FAMILY.InterBold,
|
||||
fontSize: 20,
|
||||
color: Palette.primary,
|
||||
marginBottom: 10,
|
||||
marginTop: 10,
|
||||
},
|
||||
chapterTitle: {
|
||||
fontFamily: FONT_FAMILY.InterBold,
|
||||
fontSize: 22,
|
||||
color: Palette.primary,
|
||||
marginBottom: 15,
|
||||
marginTop: 25,
|
||||
textAlign: "center",
|
||||
},
|
||||
subTitle: {
|
||||
fontFamily: FONT_FAMILY.InterSemiBold,
|
||||
fontSize: 18,
|
||||
color: Palette.white,
|
||||
marginBottom: 8,
|
||||
marginTop: 15,
|
||||
},
|
||||
text: {
|
||||
fontFamily: FONT_FAMILY.InterRegular,
|
||||
fontSize: 16,
|
||||
color: Palette.white,
|
||||
marginBottom: 10,
|
||||
lineHeight: 24,
|
||||
},
|
||||
bold: {
|
||||
fontFamily: FONT_FAMILY.InterBold,
|
||||
},
|
||||
});
|
||||
container: {
|
||||
flex: 1,
|
||||
marginHorizontal: Platform.OS === 'web' ? gutters : 0,
|
||||
marginBottom: gutters,
|
||||
borderRadius: 20,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
blurContainer: {
|
||||
flex: 1,
|
||||
backgroundColor: Palette.glass,
|
||||
},
|
||||
scrollContent: {
|
||||
padding: gutters,
|
||||
paddingBottom: gutters * 3,
|
||||
},
|
||||
title: {
|
||||
fontFamily: FONT_FAMILY.InterBold,
|
||||
fontSize: 24,
|
||||
color: Palette.white,
|
||||
textAlign: 'center',
|
||||
marginBottom: 20,
|
||||
},
|
||||
sectionTitle: {
|
||||
fontFamily: FONT_FAMILY.InterBold,
|
||||
fontSize: 20,
|
||||
color: Palette.primary,
|
||||
marginBottom: 10,
|
||||
marginTop: 10,
|
||||
},
|
||||
chapterTitle: {
|
||||
fontFamily: FONT_FAMILY.InterBold,
|
||||
fontSize: 22,
|
||||
color: Palette.primary,
|
||||
marginBottom: 15,
|
||||
marginTop: 25,
|
||||
textAlign: 'center',
|
||||
},
|
||||
subTitle: {
|
||||
fontFamily: FONT_FAMILY.InterSemiBold,
|
||||
fontSize: 18,
|
||||
color: Palette.white,
|
||||
marginBottom: 8,
|
||||
marginTop: 15,
|
||||
},
|
||||
text: {
|
||||
fontFamily: FONT_FAMILY.InterRegular,
|
||||
fontSize: 16,
|
||||
color: Palette.white,
|
||||
marginBottom: 10,
|
||||
lineHeight: 24,
|
||||
},
|
||||
bold: {
|
||||
fontFamily: FONT_FAMILY.InterBold,
|
||||
},
|
||||
})
|
||||
|
||||
export default PlaybackGuide;
|
||||
export default PlaybackGuide
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { View, Text } from "react-native";
|
||||
import React from "react";
|
||||
import Page from "../../layouts/Page";
|
||||
import { background } from "../../assets";
|
||||
import { gutters } from "../../styles";
|
||||
import GradientButton from "../../components/GradientButton";
|
||||
import { navigate } from "../../navigation/NavigationService";
|
||||
import { Routes } from "../../navigation";
|
||||
import { View, Text } from 'react-native'
|
||||
import React from 'react'
|
||||
import Page from '../../layouts/Page'
|
||||
import { background } from '../../assets'
|
||||
import { gutters } from '../../styles'
|
||||
import GradientButton from '../../components/GradientButton'
|
||||
import { navigate } from '../../navigation/NavigationService'
|
||||
import { Routes } from '../../navigation'
|
||||
|
||||
const PlaybackOnboarding = () => {
|
||||
return (
|
||||
@@ -14,16 +14,13 @@ const PlaybackOnboarding = () => {
|
||||
style={{
|
||||
flex: 1,
|
||||
paddingBottom: gutters * 2,
|
||||
justifyContent: "flex-end",
|
||||
justifyContent: 'flex-end',
|
||||
}}
|
||||
>
|
||||
<GradientButton
|
||||
title="Commence"
|
||||
onPress={() => navigate(Routes.Playback)}
|
||||
/>
|
||||
<GradientButton title="Commence" onPress={() => navigate(Routes.Playback)} />
|
||||
</View>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default PlaybackOnboarding;
|
||||
export default PlaybackOnboarding
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,173 +1,167 @@
|
||||
import useSharedAudioPlayer from "../../hooks/useSharedAudioPlayer";
|
||||
import * as FileSystem from "expo-file-system";
|
||||
import { VideoView, useVideoPlayer } from "expo-video";
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { Image, Pressable, View } from "react-native";
|
||||
import { background, icons } from "../../assets";
|
||||
import BorderGradientButton from "../../components/BorderGradientButton";
|
||||
import GradientButton from "../../components/GradientButton";
|
||||
import MusicLandHeader from "../../components/MusicLandHeader";
|
||||
import ProgressSlider from "../../components/player/ProgressSlider";
|
||||
import Page from "../../layouts/Page";
|
||||
import { Routes } from "../../navigation";
|
||||
import { goBack, navigate } from "../../navigation/NavigationService";
|
||||
import { gutters, Palette } from "../../styles";
|
||||
import useSharedAudioPlayer from '../../hooks/useSharedAudioPlayer'
|
||||
import * as FileSystem from 'expo-file-system'
|
||||
import { VideoView, useVideoPlayer } from 'expo-video'
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { Image, Pressable, View } from 'react-native'
|
||||
import { background, icons } from '../../assets'
|
||||
import BorderGradientButton from '../../components/BorderGradientButton'
|
||||
import GradientButton from '../../components/GradientButton'
|
||||
import MusicLandHeader from '../../components/MusicLandHeader'
|
||||
import ProgressSlider from '../../components/player/ProgressSlider'
|
||||
import Page from '../../layouts/Page'
|
||||
import { Routes } from '../../navigation'
|
||||
import { goBack, navigate } from '../../navigation/NavigationService'
|
||||
import { gutters, Palette } from '../../styles'
|
||||
const RecordedPlayback = ({ route }) => {
|
||||
const { videoUri, project } = route.params || {};
|
||||
const songUrl = project?.songUrl || null;
|
||||
const { videoUri, project } = route.params || {}
|
||||
const songUrl = project?.songUrl || null
|
||||
const audioPlayer = useSharedAudioPlayer(songUrl ? { uri: songUrl } : undefined, {
|
||||
id: project?.id ? `recorded-${project.id}` : songUrl ? `recorded-${songUrl}` : undefined,
|
||||
title: typeof project?.title === "string" ? project.title : "Sans titre",
|
||||
artist: typeof project?.userName === "string" ? project.userName : "MusicLand",
|
||||
title: typeof project?.title === 'string' ? project.title : 'Sans titre',
|
||||
artist: typeof project?.userName === 'string' ? project.userName : 'MusicLand',
|
||||
artwork: project?.coverUrl || null,
|
||||
coverUrl: project?.coverUrl || null,
|
||||
metadata: { projectId: project?.id, screen: "RecordedPlayback" },
|
||||
});
|
||||
metadata: { projectId: project?.id, screen: 'RecordedPlayback' },
|
||||
})
|
||||
const videoPlayer = useVideoPlayer(videoUri || null, (p) => {
|
||||
p.loop = false;
|
||||
p.muted = true; // recorded video has no audio; keep muted anyway
|
||||
p.timeUpdateEventInterval = 0.2;
|
||||
});
|
||||
p.loop = false
|
||||
p.muted = true // recorded video has no audio; keep muted anyway
|
||||
p.timeUpdateEventInterval = 0.2
|
||||
})
|
||||
|
||||
const [progressInfo, setProgressInfo] = useState({
|
||||
pos: 0,
|
||||
dur: 0,
|
||||
isPlaying: false,
|
||||
});
|
||||
const playbackEndedRef = useRef(false);
|
||||
})
|
||||
const playbackEndedRef = useRef(false)
|
||||
|
||||
// Start both players on mount
|
||||
const stopPlayback = useCallback(async () => {
|
||||
try {
|
||||
if (audioPlayer?.playing) await audioPlayer.pause?.();
|
||||
} catch (e) { }
|
||||
if (audioPlayer?.playing) await audioPlayer.pause?.()
|
||||
} catch (e) {}
|
||||
try {
|
||||
if (videoPlayer?.playing) videoPlayer.pause();
|
||||
} catch (e) { }
|
||||
}, [audioPlayer, videoPlayer]);
|
||||
if (videoPlayer?.playing) videoPlayer.pause()
|
||||
} catch (e) {}
|
||||
}, [audioPlayer, videoPlayer])
|
||||
|
||||
useEffect(() => {
|
||||
playbackEndedRef.current = false;
|
||||
playbackEndedRef.current = false
|
||||
const start = async () => {
|
||||
try {
|
||||
if (audioPlayer && songUrl) await audioPlayer.play?.();
|
||||
if (videoPlayer) videoPlayer.play();
|
||||
} catch (e) { }
|
||||
};
|
||||
start();
|
||||
if (audioPlayer && songUrl) await audioPlayer.play?.()
|
||||
if (videoPlayer) videoPlayer.play()
|
||||
} catch (e) {}
|
||||
}
|
||||
start()
|
||||
return () => {
|
||||
playbackEndedRef.current = false;
|
||||
void stopPlayback();
|
||||
};
|
||||
}, [audioPlayer, songUrl, stopPlayback, videoPlayer]);
|
||||
playbackEndedRef.current = false
|
||||
void stopPlayback()
|
||||
}
|
||||
}, [audioPlayer, songUrl, stopPlayback, videoPlayer])
|
||||
|
||||
// Poll from audio player for progress display; keep video in sync if drifting
|
||||
useEffect(() => {
|
||||
const id = global.setInterval(() => {
|
||||
try {
|
||||
const dur = (audioPlayer?.duration || 0) * 1000;
|
||||
const pos = (audioPlayer?.currentTime || 0) * 1000;
|
||||
const playing = !!audioPlayer?.playing || !!videoPlayer?.playing;
|
||||
setProgressInfo({ pos, dur, isPlaying: playing });
|
||||
const dur = (audioPlayer?.duration || 0) * 1000
|
||||
const pos = (audioPlayer?.currentTime || 0) * 1000
|
||||
const playing = !!audioPlayer?.playing || !!videoPlayer?.playing
|
||||
setProgressInfo({ pos, dur, isPlaying: playing })
|
||||
|
||||
// basic drift correction: if desync > 300ms, align video
|
||||
if (videoPlayer && !Number.isNaN(videoPlayer.currentTime)) {
|
||||
const v = (videoPlayer.currentTime || 0) * 1000;
|
||||
const drift = Math.abs(v - pos);
|
||||
const v = (videoPlayer.currentTime || 0) * 1000
|
||||
const drift = Math.abs(v - pos)
|
||||
if (drift > 350) {
|
||||
videoPlayer.currentTime = Math.max(0, (pos || 0) / 1000);
|
||||
videoPlayer.currentTime = Math.max(0, (pos || 0) / 1000)
|
||||
}
|
||||
}
|
||||
} catch (e) { }
|
||||
}, 250);
|
||||
return () => global.clearInterval(id);
|
||||
}, [audioPlayer, videoPlayer]);
|
||||
} catch (e) {}
|
||||
}, 250)
|
||||
return () => global.clearInterval(id)
|
||||
}, [audioPlayer, videoPlayer])
|
||||
|
||||
const onSeek = async (targetMs) => {
|
||||
try {
|
||||
const dur = progressInfo.dur || 0;
|
||||
const pos = Math.max(0, Math.min(dur, Math.floor(targetMs)));
|
||||
if (audioPlayer && dur > 0)
|
||||
await audioPlayer.seekTo?.(Math.floor(pos / 1000));
|
||||
if (videoPlayer) videoPlayer.currentTime = Math.max(0, pos / 1000);
|
||||
} catch (e) { }
|
||||
};
|
||||
const dur = progressInfo.dur || 0
|
||||
const pos = Math.max(0, Math.min(dur, Math.floor(targetMs)))
|
||||
if (audioPlayer && dur > 0) await audioPlayer.seekTo?.(Math.floor(pos / 1000))
|
||||
if (videoPlayer) videoPlayer.currentTime = Math.max(0, pos / 1000)
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
const onSeekStart = useCallback(() => {
|
||||
playbackEndedRef.current = false;
|
||||
}, []);
|
||||
playbackEndedRef.current = false
|
||||
}, [])
|
||||
|
||||
const pauseDuringSeek = useCallback(async () => {
|
||||
try {
|
||||
if (audioPlayer?.playing) await audioPlayer.pause?.();
|
||||
} catch (e) { }
|
||||
if (audioPlayer?.playing) await audioPlayer.pause?.()
|
||||
} catch (e) {}
|
||||
try {
|
||||
if (videoPlayer?.playing) videoPlayer.pause();
|
||||
} catch (e) { }
|
||||
}, [audioPlayer, videoPlayer]);
|
||||
if (videoPlayer?.playing) videoPlayer.pause()
|
||||
} catch (e) {}
|
||||
}, [audioPlayer, videoPlayer])
|
||||
|
||||
const resumeAfterSeek = useCallback(async () => {
|
||||
try {
|
||||
if (audioPlayer) await audioPlayer.play?.();
|
||||
} catch (e) { }
|
||||
if (audioPlayer) await audioPlayer.play?.()
|
||||
} catch (e) {}
|
||||
try {
|
||||
if (videoPlayer) videoPlayer.play();
|
||||
} catch (e) { }
|
||||
}, [audioPlayer, videoPlayer]);
|
||||
if (videoPlayer) videoPlayer.play()
|
||||
} catch (e) {}
|
||||
}, [audioPlayer, videoPlayer])
|
||||
|
||||
const sliderProgress = useMemo(() => {
|
||||
return progressInfo.dur
|
||||
? Math.min(1, (progressInfo.pos || 0) / progressInfo.dur)
|
||||
: 0;
|
||||
}, [progressInfo]);
|
||||
return progressInfo.dur ? Math.min(1, (progressInfo.pos || 0) / progressInfo.dur) : 0
|
||||
}, [progressInfo])
|
||||
|
||||
useEffect(() => {
|
||||
const duration = progressInfo?.dur || 0;
|
||||
if (!duration) return;
|
||||
const position = progressInfo?.pos || 0;
|
||||
const duration = progressInfo?.dur || 0
|
||||
if (!duration) return
|
||||
const position = progressInfo?.pos || 0
|
||||
if (playbackEndedRef.current && duration - position > 1000) {
|
||||
playbackEndedRef.current = false;
|
||||
return;
|
||||
playbackEndedRef.current = false
|
||||
return
|
||||
}
|
||||
const remaining = Math.max(0, duration - position);
|
||||
const remaining = Math.max(0, duration - position)
|
||||
if (remaining <= 400 && !playbackEndedRef.current) {
|
||||
playbackEndedRef.current = true;
|
||||
void stopPlayback();
|
||||
playbackEndedRef.current = true
|
||||
void stopPlayback()
|
||||
}
|
||||
}, [progressInfo, stopPlayback]);
|
||||
}, [progressInfo, stopPlayback])
|
||||
|
||||
const handleTogglePlayback = async () => {
|
||||
try {
|
||||
const duration = progressInfo?.dur || 0;
|
||||
const position = progressInfo?.pos || 0;
|
||||
const isAtEnd = duration > 0 && duration - position < 1000;
|
||||
const isCurrentlyPlaying =
|
||||
!!audioPlayer?.playing || !!videoPlayer?.playing;
|
||||
const duration = progressInfo?.dur || 0
|
||||
const position = progressInfo?.pos || 0
|
||||
const isAtEnd = duration > 0 && duration - position < 1000
|
||||
const isCurrentlyPlaying = !!audioPlayer?.playing || !!videoPlayer?.playing
|
||||
|
||||
if (isCurrentlyPlaying) {
|
||||
playbackEndedRef.current = false;
|
||||
if (audioPlayer?.playing) await audioPlayer.pause?.();
|
||||
if (videoPlayer?.playing) videoPlayer.pause();
|
||||
return;
|
||||
playbackEndedRef.current = false
|
||||
if (audioPlayer?.playing) await audioPlayer.pause?.()
|
||||
if (videoPlayer?.playing) videoPlayer.pause()
|
||||
return
|
||||
}
|
||||
|
||||
if (isAtEnd) {
|
||||
if (audioPlayer) await audioPlayer.seekTo?.(0);
|
||||
if (videoPlayer) videoPlayer.currentTime = 0;
|
||||
if (audioPlayer) await audioPlayer.seekTo?.(0)
|
||||
if (videoPlayer) videoPlayer.currentTime = 0
|
||||
}
|
||||
|
||||
playbackEndedRef.current = false;
|
||||
if (songUrl && audioPlayer) await audioPlayer.play?.();
|
||||
if (videoPlayer) videoPlayer.play();
|
||||
} catch (e) { }
|
||||
};
|
||||
playbackEndedRef.current = false
|
||||
if (songUrl && audioPlayer) await audioPlayer.play?.()
|
||||
if (videoPlayer) videoPlayer.play()
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
return (
|
||||
<Page backgroundColor={Palette.grayMid} headerType="NONE">
|
||||
<MusicLandHeader progress={19} onPressBack={goBack} />
|
||||
<View
|
||||
style={{ flex: 1, paddingTop: 12, gap: 14, paddingBottom: gutters * 2 }}
|
||||
>
|
||||
<View style={{ flex: 1, paddingTop: 12, gap: 14, paddingBottom: gutters * 2 }}>
|
||||
<View style={{ flex: 1, gap: 22 }}>
|
||||
{!!videoUri && (
|
||||
<VideoView
|
||||
@@ -175,12 +169,12 @@ const RecordedPlayback = ({ route }) => {
|
||||
nativeControls={false}
|
||||
contentFit="contain"
|
||||
style={{
|
||||
width: "80%",
|
||||
width: '80%',
|
||||
flex: 1,
|
||||
alignSelf: "center",
|
||||
alignSelf: 'center',
|
||||
borderRadius: 16,
|
||||
backgroundColor: "#00000066",
|
||||
overflow: "hidden",
|
||||
backgroundColor: '#00000066',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
@@ -200,13 +194,13 @@ const RecordedPlayback = ({ route }) => {
|
||||
width: 72,
|
||||
height: 72,
|
||||
borderRadius: 36,
|
||||
alignSelf: "center",
|
||||
alignSelf: 'center',
|
||||
marginTop: 4,
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
backgroundColor: "rgba(10, 5, 24, 0.65)",
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: 'rgba(10, 5, 24, 0.65)',
|
||||
borderWidth: 1,
|
||||
borderColor: "#F94697",
|
||||
borderColor: '#F94697',
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
@@ -215,41 +209,39 @@ const RecordedPlayback = ({ route }) => {
|
||||
/>
|
||||
</Pressable>
|
||||
</View>
|
||||
<View
|
||||
style={{ width: "80%", alignSelf: "center", marginTop: 4, gap: 12 }}
|
||||
>
|
||||
<View style={{ width: '80%', alignSelf: 'center', marginTop: 4, gap: 12 }}>
|
||||
<GradientButton
|
||||
title="Je valide"
|
||||
onPress={() => {
|
||||
navigate(Routes.PlaybackDownload, {
|
||||
action: "playback",
|
||||
action: 'playback',
|
||||
uri: videoUri,
|
||||
project,
|
||||
});
|
||||
})
|
||||
}}
|
||||
/>
|
||||
<BorderGradientButton
|
||||
title="Recommencer"
|
||||
onPress={async () => {
|
||||
try {
|
||||
await stopPlayback();
|
||||
} catch (e) { }
|
||||
await stopPlayback()
|
||||
} catch (e) {}
|
||||
try {
|
||||
if (videoUri) {
|
||||
const info = await FileSystem.getInfoAsync(videoUri);
|
||||
const info = await FileSystem.getInfoAsync(videoUri)
|
||||
if (info?.exists)
|
||||
await FileSystem.deleteAsync(videoUri, {
|
||||
idempotent: true,
|
||||
});
|
||||
})
|
||||
}
|
||||
} catch (e) { }
|
||||
navigate(Routes.RecordPlayback, { project });
|
||||
} catch (e) {}
|
||||
navigate(Routes.RecordPlayback, { project })
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default RecordedPlayback;
|
||||
export default RecordedPlayback
|
||||
|
||||
@@ -1,266 +1,247 @@
|
||||
import React, {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { Image, Pressable, Text, View } from "react-native";
|
||||
import { background, icons } from "../../assets";
|
||||
import BorderGradientButton from "../../components/BorderGradientButton";
|
||||
import GradientButton from "../../components/GradientButton";
|
||||
import MusicLandHeader from "../../components/MusicLandHeader";
|
||||
import Slider from "../../components/Slider";
|
||||
import Page from "../../layouts/Page";
|
||||
import { Routes } from "../../navigation";
|
||||
import { goBack, navigate } from "../../navigation/NavigationService";
|
||||
import { gutters, Palette } from "../../styles";
|
||||
import { isWeb } from "../../hooks/useLayoutType";
|
||||
import useSharedAudioPlayer from "../../hooks/useSharedAudioPlayer";
|
||||
import { releaseBlobUrl } from "../../utils/blobUrlCache";
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { Image, Pressable, Text, View } from 'react-native'
|
||||
import { background, icons } from '../../assets'
|
||||
import BorderGradientButton from '../../components/BorderGradientButton'
|
||||
import GradientButton from '../../components/GradientButton'
|
||||
import MusicLandHeader from '../../components/MusicLandHeader'
|
||||
import Slider from '../../components/Slider'
|
||||
import Page from '../../layouts/Page'
|
||||
import { Routes } from '../../navigation'
|
||||
import { goBack, navigate } from '../../navigation/NavigationService'
|
||||
import { gutters, Palette } from '../../styles'
|
||||
import { isWeb } from '../../hooks/useLayoutType'
|
||||
import useSharedAudioPlayer from '../../hooks/useSharedAudioPlayer'
|
||||
import { releaseBlobUrl } from '../../utils/blobUrlCache'
|
||||
|
||||
const fmtSeconds = (s) => {
|
||||
const total = Math.max(0, Math.floor(Number(s || 0)));
|
||||
const m = Math.floor(total / 60).toString();
|
||||
const sec = (total % 60).toString().padStart(2, "0");
|
||||
return `${m}:${sec}`;
|
||||
};
|
||||
const total = Math.max(0, Math.floor(Number(s || 0)))
|
||||
const m = Math.floor(total / 60).toString()
|
||||
const sec = (total % 60).toString().padStart(2, '0')
|
||||
return `${m}:${sec}`
|
||||
}
|
||||
|
||||
const toSeconds = (value) => {
|
||||
const n = Number(value ?? 0);
|
||||
if (!Number.isFinite(n) || n < 0) return 0;
|
||||
return n > 10000 ? n / 1000 : n; // heuristique ms → s
|
||||
};
|
||||
const n = Number(value ?? 0)
|
||||
if (!Number.isFinite(n) || n < 0) return 0
|
||||
return n > 10000 ? n / 1000 : n // heuristique ms → s
|
||||
}
|
||||
|
||||
const WEB_PREVIEW_WIDTH = 360;
|
||||
const WEB_PREVIEW_WIDTH = 360
|
||||
|
||||
const RecordedPlayback = ({ route }) => {
|
||||
const { videoUri, project } = route.params || {};
|
||||
const songUrl = project?.songUrl || null;
|
||||
const { videoUri, project } = route.params || {}
|
||||
const songUrl = project?.songUrl || null
|
||||
// AUDIO PLAYER (expo-audio → seconds)
|
||||
const audioPlayer = useSharedAudioPlayer(
|
||||
songUrl ? { uri: songUrl } : undefined,
|
||||
{
|
||||
id: project?.id
|
||||
? `recorded-${project.id}`
|
||||
: songUrl
|
||||
? `recorded-${songUrl}`
|
||||
: undefined,
|
||||
title: typeof project?.title === "string" ? project.title : "Sans titre",
|
||||
artist:
|
||||
typeof project?.userName === "string" ? project.userName : "MusicLand",
|
||||
artwork: project?.coverUrl || null,
|
||||
coverUrl: project?.coverUrl || null,
|
||||
metadata: { projectId: project?.id, screen: "RecordedPlayback" },
|
||||
},
|
||||
);
|
||||
const audioPlayer = useSharedAudioPlayer(songUrl ? { uri: songUrl } : undefined, {
|
||||
id: project?.id ? `recorded-${project.id}` : songUrl ? `recorded-${songUrl}` : undefined,
|
||||
title: typeof project?.title === 'string' ? project.title : 'Sans titre',
|
||||
artist: typeof project?.userName === 'string' ? project.userName : 'MusicLand',
|
||||
artwork: project?.coverUrl || null,
|
||||
coverUrl: project?.coverUrl || null,
|
||||
metadata: { projectId: project?.id, screen: 'RecordedPlayback' },
|
||||
})
|
||||
|
||||
// Optionnel: si tu veux quand même un rendu vidéo sur web si tu as une source
|
||||
const videoElRef = useRef(null);
|
||||
const playbackEndedRef = useRef(false);
|
||||
const shouldPreserveBlobRef = useRef(false);
|
||||
const videoElRef = useRef(null)
|
||||
const playbackEndedRef = useRef(false)
|
||||
const shouldPreserveBlobRef = useRef(false)
|
||||
|
||||
const [progress, setProgress] = useState({
|
||||
posS: 0, // secondes
|
||||
durS: 0, // secondes
|
||||
playing: false,
|
||||
});
|
||||
})
|
||||
|
||||
const stopPlayback = useCallback(async () => {
|
||||
try {
|
||||
if (audioPlayer?.playing) await audioPlayer.pause?.();
|
||||
} catch { }
|
||||
if (audioPlayer?.playing) await audioPlayer.pause?.()
|
||||
} catch {}
|
||||
try {
|
||||
if (videoElRef.current && !videoElRef.current.paused) {
|
||||
videoElRef.current.pause();
|
||||
videoElRef.current.pause()
|
||||
}
|
||||
} catch { }
|
||||
}, [audioPlayer]);
|
||||
} catch {}
|
||||
}, [audioPlayer])
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (!shouldPreserveBlobRef.current) {
|
||||
releaseBlobUrl(videoUri || null);
|
||||
releaseBlobUrl(videoUri || null)
|
||||
}
|
||||
};
|
||||
}, [videoUri]);
|
||||
}
|
||||
}, [videoUri])
|
||||
|
||||
// Démarrage / arrêt
|
||||
useEffect(() => {
|
||||
playbackEndedRef.current = false;
|
||||
playbackEndedRef.current = false
|
||||
const start = async () => {
|
||||
try {
|
||||
if (audioPlayer && songUrl) {
|
||||
await audioPlayer.play?.();
|
||||
await audioPlayer.play?.()
|
||||
}
|
||||
if (videoElRef.current && videoUri) {
|
||||
// Lecture vidéo HTML5 (muet pour éviter les policies)
|
||||
videoElRef.current.muted = true;
|
||||
videoElRef.current.play().catch(() => { });
|
||||
videoElRef.current.muted = true
|
||||
videoElRef.current.play().catch(() => {})
|
||||
}
|
||||
} catch { }
|
||||
};
|
||||
start();
|
||||
} catch {}
|
||||
}
|
||||
start()
|
||||
|
||||
return () => {
|
||||
playbackEndedRef.current = false;
|
||||
void stopPlayback();
|
||||
};
|
||||
}, [audioPlayer, songUrl, stopPlayback, videoUri]);
|
||||
playbackEndedRef.current = false
|
||||
void stopPlayback()
|
||||
}
|
||||
}, [audioPlayer, songUrl, stopPlayback, videoUri])
|
||||
|
||||
// Boucle de progression + éventuelle sync de la vidéo si fournie
|
||||
useEffect(() => {
|
||||
const id = setInterval(() => {
|
||||
try {
|
||||
const durS = toSeconds(audioPlayer?.duration); // secondes
|
||||
const posS = toSeconds(audioPlayer?.currentTime); // secondes
|
||||
const durS = toSeconds(audioPlayer?.duration) // secondes
|
||||
const posS = toSeconds(audioPlayer?.currentTime) // secondes
|
||||
|
||||
setProgress({
|
||||
posS,
|
||||
durS,
|
||||
playing: !!audioPlayer?.playing,
|
||||
});
|
||||
})
|
||||
|
||||
// Sync vidéo si on a une source vidéo
|
||||
if (
|
||||
videoElRef.current &&
|
||||
videoUri &&
|
||||
!Number.isNaN(videoElRef.current.currentTime)
|
||||
) {
|
||||
const v = Number(videoElRef.current.currentTime || 0);
|
||||
const drift = Math.abs(v - posS);
|
||||
if (videoElRef.current && videoUri && !Number.isNaN(videoElRef.current.currentTime)) {
|
||||
const v = Number(videoElRef.current.currentTime || 0)
|
||||
const drift = Math.abs(v - posS)
|
||||
if (drift > 0.35) {
|
||||
videoElRef.current.currentTime = Math.max(0, posS);
|
||||
videoElRef.current.currentTime = Math.max(0, posS)
|
||||
}
|
||||
}
|
||||
} catch { }
|
||||
}, 250);
|
||||
return () => clearInterval(id);
|
||||
}, [audioPlayer, videoUri]);
|
||||
} catch {}
|
||||
}, 250)
|
||||
return () => clearInterval(id)
|
||||
}, [audioPlayer, videoUri])
|
||||
|
||||
// Slider: ratio 0..1
|
||||
const sliderProgress = useMemo(() => {
|
||||
return progress.durS > 0 ? Math.min(1, progress.posS / progress.durS) : 0;
|
||||
}, [progress]);
|
||||
const pendingSeekRef = useRef(null);
|
||||
return progress.durS > 0 ? Math.min(1, progress.posS / progress.durS) : 0
|
||||
}, [progress])
|
||||
const pendingSeekRef = useRef(null)
|
||||
|
||||
useEffect(() => {
|
||||
const duration = progress?.durS || 0;
|
||||
if (!duration) return;
|
||||
const position = progress?.posS || 0;
|
||||
const duration = progress?.durS || 0
|
||||
if (!duration) return
|
||||
const position = progress?.posS || 0
|
||||
if (playbackEndedRef.current && duration - position > 1) {
|
||||
playbackEndedRef.current = false;
|
||||
return;
|
||||
playbackEndedRef.current = false
|
||||
return
|
||||
}
|
||||
const remaining = Math.max(0, duration - position);
|
||||
const remaining = Math.max(0, duration - position)
|
||||
if (remaining <= 0.35 && !playbackEndedRef.current) {
|
||||
playbackEndedRef.current = true;
|
||||
playbackEndedRef.current = true
|
||||
void (async () => {
|
||||
await stopPlayback();
|
||||
await stopPlayback()
|
||||
try {
|
||||
if (audioPlayer) await audioPlayer.seekTo?.(0);
|
||||
} catch { }
|
||||
if (audioPlayer) await audioPlayer.seekTo?.(0)
|
||||
} catch {}
|
||||
try {
|
||||
if (videoElRef.current) {
|
||||
videoElRef.current.currentTime = 0;
|
||||
videoElRef.current.currentTime = 0
|
||||
}
|
||||
} catch { }
|
||||
})();
|
||||
} catch {}
|
||||
})()
|
||||
}
|
||||
}, [progress, stopPlayback, audioPlayer]);
|
||||
}, [progress, stopPlayback, audioPlayer])
|
||||
|
||||
const onSeek = useCallback(
|
||||
async (ratio) => {
|
||||
const durS = Number(progress.durS || 0);
|
||||
const target = durS > 0 ? durS * ratio : 0; // secondes
|
||||
const durS = Number(progress.durS || 0)
|
||||
const target = durS > 0 ? durS * ratio : 0 // secondes
|
||||
const promise = (async () => {
|
||||
try {
|
||||
if (audioPlayer && durS > 0) {
|
||||
await audioPlayer.seekTo?.(Math.max(0, target));
|
||||
await audioPlayer.seekTo?.(Math.max(0, target))
|
||||
}
|
||||
if (videoElRef.current && videoUri) {
|
||||
videoElRef.current.currentTime = Math.max(0, target);
|
||||
videoElRef.current.currentTime = Math.max(0, target)
|
||||
}
|
||||
} catch { }
|
||||
})();
|
||||
pendingSeekRef.current = promise;
|
||||
await promise;
|
||||
} catch {}
|
||||
})()
|
||||
pendingSeekRef.current = promise
|
||||
await promise
|
||||
},
|
||||
[audioPlayer, progress.durS, videoUri],
|
||||
);
|
||||
[audioPlayer, progress.durS, videoUri]
|
||||
)
|
||||
|
||||
const waitForPendingSeek = useCallback(async () => {
|
||||
const promise = pendingSeekRef.current;
|
||||
pendingSeekRef.current = null;
|
||||
if (!promise) return;
|
||||
const promise = pendingSeekRef.current
|
||||
pendingSeekRef.current = null
|
||||
if (!promise) return
|
||||
try {
|
||||
await promise;
|
||||
} catch { }
|
||||
}, []);
|
||||
await promise
|
||||
} catch {}
|
||||
}, [])
|
||||
|
||||
const wasPlayingRef = useRef(false);
|
||||
const isSeekingRef = useRef(false);
|
||||
const wasPlayingRef = useRef(false)
|
||||
const isSeekingRef = useRef(false)
|
||||
const onSeekStart = useCallback(async () => {
|
||||
try {
|
||||
if (isSeekingRef.current) return;
|
||||
isSeekingRef.current = true;
|
||||
wasPlayingRef.current = !!audioPlayer?.playing;
|
||||
pendingSeekRef.current = null;
|
||||
playbackEndedRef.current = false;
|
||||
if (audioPlayer?.playing) await audioPlayer.pause?.();
|
||||
if (isSeekingRef.current) return
|
||||
isSeekingRef.current = true
|
||||
wasPlayingRef.current = !!audioPlayer?.playing
|
||||
pendingSeekRef.current = null
|
||||
playbackEndedRef.current = false
|
||||
if (audioPlayer?.playing) await audioPlayer.pause?.()
|
||||
if (videoElRef.current && !videoElRef.current.paused) {
|
||||
videoElRef.current.pause();
|
||||
videoElRef.current.pause()
|
||||
}
|
||||
} catch { }
|
||||
}, [audioPlayer]);
|
||||
} catch {}
|
||||
}, [audioPlayer])
|
||||
const onSeekEnd = useCallback(async () => {
|
||||
try {
|
||||
if (!isSeekingRef.current) return;
|
||||
await waitForPendingSeek();
|
||||
isSeekingRef.current = false;
|
||||
if (!isSeekingRef.current) return
|
||||
await waitForPendingSeek()
|
||||
isSeekingRef.current = false
|
||||
if (wasPlayingRef.current) {
|
||||
if (audioPlayer) await audioPlayer.resume?.();
|
||||
if (videoElRef.current && videoUri)
|
||||
videoElRef.current.play().catch(() => { });
|
||||
if (audioPlayer) await audioPlayer.resume?.()
|
||||
if (videoElRef.current && videoUri) videoElRef.current.play().catch(() => {})
|
||||
}
|
||||
} catch { }
|
||||
}, [audioPlayer, videoUri, waitForPendingSeek]);
|
||||
} catch {}
|
||||
}, [audioPlayer, videoUri, waitForPendingSeek])
|
||||
|
||||
const handleTogglePlayback = useCallback(async () => {
|
||||
try {
|
||||
const duration = Number(progress?.durS || 0);
|
||||
const position = Number(progress?.posS || 0);
|
||||
const isAtEnd = duration > 0 && duration - position < 0.35;
|
||||
const isCurrentlyPlaying = !!audioPlayer?.playing;
|
||||
const duration = Number(progress?.durS || 0)
|
||||
const position = Number(progress?.posS || 0)
|
||||
const isAtEnd = duration > 0 && duration - position < 0.35
|
||||
const isCurrentlyPlaying = !!audioPlayer?.playing
|
||||
|
||||
if (isCurrentlyPlaying) {
|
||||
playbackEndedRef.current = false;
|
||||
await stopPlayback();
|
||||
return;
|
||||
playbackEndedRef.current = false
|
||||
await stopPlayback()
|
||||
return
|
||||
}
|
||||
|
||||
if (isAtEnd) {
|
||||
if (audioPlayer) await audioPlayer.seekTo?.(0);
|
||||
if (audioPlayer) await audioPlayer.seekTo?.(0)
|
||||
if (videoElRef.current && videoUri) {
|
||||
videoElRef.current.currentTime = 0;
|
||||
videoElRef.current.currentTime = 0
|
||||
}
|
||||
}
|
||||
|
||||
playbackEndedRef.current = false;
|
||||
playbackEndedRef.current = false
|
||||
if (songUrl && audioPlayer) {
|
||||
await audioPlayer.play?.();
|
||||
await audioPlayer.play?.()
|
||||
}
|
||||
if (videoElRef.current && videoUri) {
|
||||
videoElRef.current.muted = true;
|
||||
videoElRef.current.play().catch(() => { });
|
||||
videoElRef.current.muted = true
|
||||
videoElRef.current.play().catch(() => {})
|
||||
}
|
||||
} catch { }
|
||||
}, [audioPlayer, songUrl, stopPlayback, progress, videoUri]);
|
||||
} catch {}
|
||||
}, [audioPlayer, songUrl, stopPlayback, progress, videoUri])
|
||||
|
||||
return (
|
||||
<Page
|
||||
containerStyle={{
|
||||
alignItems: "none",
|
||||
alignItems: 'none',
|
||||
}}
|
||||
backgroundColor={Palette.grayMid}
|
||||
headerType="NONE"
|
||||
@@ -274,7 +255,7 @@ const RecordedPlayback = ({ route }) => {
|
||||
/>
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
{/* Bloc vidéo optionnel si jamais tu as un videoUri sur web */}
|
||||
@@ -282,14 +263,14 @@ const RecordedPlayback = ({ route }) => {
|
||||
<Pressable
|
||||
onPress={handleTogglePlayback}
|
||||
style={{
|
||||
width: isWeb ? WEB_PREVIEW_WIDTH : "80%",
|
||||
maxWidth: "100%",
|
||||
width: isWeb ? WEB_PREVIEW_WIDTH : '80%',
|
||||
maxWidth: '100%',
|
||||
aspectRatio: 9 / 16,
|
||||
borderRadius: 12,
|
||||
backgroundColor: "#00000066",
|
||||
overflow: "hidden",
|
||||
alignSelf: "center",
|
||||
position: "relative",
|
||||
backgroundColor: '#00000066',
|
||||
overflow: 'hidden',
|
||||
alignSelf: 'center',
|
||||
position: 'relative',
|
||||
}}
|
||||
>
|
||||
<video
|
||||
@@ -298,10 +279,10 @@ const RecordedPlayback = ({ route }) => {
|
||||
muted
|
||||
playsInline
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
objectFit: "cover",
|
||||
backgroundColor: "black",
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
objectFit: 'cover',
|
||||
backgroundColor: 'black',
|
||||
}}
|
||||
controls={false}
|
||||
/>
|
||||
@@ -309,18 +290,18 @@ const RecordedPlayback = ({ route }) => {
|
||||
<View
|
||||
pointerEvents="none"
|
||||
style={{
|
||||
position: "absolute",
|
||||
left: "50%",
|
||||
top: "50%",
|
||||
position: 'absolute',
|
||||
left: '50%',
|
||||
top: '50%',
|
||||
transform: [{ translateX: -36 }, { translateY: -36 }],
|
||||
width: 72,
|
||||
height: 72,
|
||||
borderRadius: 36,
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
backgroundColor: "rgba(10, 5, 24, 0.75)",
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: 'rgba(10, 5, 24, 0.75)',
|
||||
borderWidth: 1,
|
||||
borderColor: "#F94697",
|
||||
borderColor: '#F94697',
|
||||
}}
|
||||
>
|
||||
<Image source={icons.play} style={{ width: 26, height: 26 }} />
|
||||
@@ -331,15 +312,15 @@ const RecordedPlayback = ({ route }) => {
|
||||
// Placeholder quand pas de vidéo sur web
|
||||
<View
|
||||
style={{
|
||||
width: isWeb ? WEB_PREVIEW_WIDTH : "80%",
|
||||
maxWidth: "100%",
|
||||
width: isWeb ? WEB_PREVIEW_WIDTH : '80%',
|
||||
maxWidth: '100%',
|
||||
aspectRatio: 9 / 16,
|
||||
borderRadius: 12,
|
||||
backgroundColor: "#00000066",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
overflow: "hidden",
|
||||
alignSelf: "center",
|
||||
backgroundColor: '#00000066',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
overflow: 'hidden',
|
||||
alignSelf: 'center',
|
||||
}}
|
||||
>
|
||||
<Text style={{ color: Palette.white, opacity: 0.8 }}>
|
||||
@@ -350,14 +331,14 @@ const RecordedPlayback = ({ route }) => {
|
||||
|
||||
<View
|
||||
style={{
|
||||
width: isWeb ? WEB_PREVIEW_WIDTH : "80%",
|
||||
maxWidth: "100%",
|
||||
alignSelf: "center",
|
||||
width: isWeb ? WEB_PREVIEW_WIDTH : '80%',
|
||||
maxWidth: '100%',
|
||||
alignSelf: 'center',
|
||||
marginTop: 6,
|
||||
alignItems: "center",
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<View style={{ width: "100%" }}>
|
||||
<View style={{ width: '100%' }}>
|
||||
<Slider
|
||||
value={fmtSeconds(progress.posS)} // mm:ss (seconds)
|
||||
maxValue={fmtSeconds(progress.durS)} // mm:ss (seconds)
|
||||
@@ -373,9 +354,9 @@ const RecordedPlayback = ({ route }) => {
|
||||
|
||||
<View
|
||||
style={{
|
||||
width: isWeb ? WEB_PREVIEW_WIDTH : "80%",
|
||||
maxWidth: "100%",
|
||||
alignSelf: "center",
|
||||
width: isWeb ? WEB_PREVIEW_WIDTH : '80%',
|
||||
maxWidth: '100%',
|
||||
alignSelf: 'center',
|
||||
gap: 12,
|
||||
marginTop: 50,
|
||||
}}
|
||||
@@ -383,26 +364,26 @@ const RecordedPlayback = ({ route }) => {
|
||||
<GradientButton
|
||||
title="Je valide"
|
||||
onPress={() => {
|
||||
shouldPreserveBlobRef.current = true;
|
||||
shouldPreserveBlobRef.current = true
|
||||
navigate(Routes.PlaybackDownload, {
|
||||
action: "playback",
|
||||
action: 'playback',
|
||||
uri: videoUri || null, // peut être null sur web
|
||||
project,
|
||||
});
|
||||
})
|
||||
}}
|
||||
/>
|
||||
<BorderGradientButton
|
||||
title="Recommencer"
|
||||
onPress={async () => {
|
||||
try {
|
||||
await stopPlayback();
|
||||
} catch { }
|
||||
navigate(Routes.RecordPlayback, { project });
|
||||
await stopPlayback()
|
||||
} catch {}
|
||||
navigate(Routes.RecordPlayback, { project })
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default RecordedPlayback;
|
||||
export default RecordedPlayback
|
||||
|
||||
@@ -1,54 +1,46 @@
|
||||
import { View, Text, Image } from "react-native";
|
||||
import React from "react";
|
||||
import MusicLandHeader from "../../components/MusicLandHeader";
|
||||
import Page from "../../layouts/Page";
|
||||
import { background, icons, img } from "../../assets";
|
||||
import { goBack, navigate } from "../../navigation/NavigationService";
|
||||
import Slider from "../../components/Slider";
|
||||
import GradientButton from "../../components/GradientButton";
|
||||
import BorderGradientButton from "../../components/BorderGradientButton";
|
||||
import { gutters } from "../../styles";
|
||||
import { Routes } from "../../navigation";
|
||||
import { View, Text, Image } from 'react-native'
|
||||
import React from 'react'
|
||||
import MusicLandHeader from '../../components/MusicLandHeader'
|
||||
import Page from '../../layouts/Page'
|
||||
import { background, icons, img } from '../../assets'
|
||||
import { goBack, navigate } from '../../navigation/NavigationService'
|
||||
import Slider from '../../components/Slider'
|
||||
import GradientButton from '../../components/GradientButton'
|
||||
import BorderGradientButton from '../../components/BorderGradientButton'
|
||||
import { gutters } from '../../styles'
|
||||
import { Routes } from '../../navigation'
|
||||
|
||||
const VideoFinalize = () => {
|
||||
return (
|
||||
<Page backgroundImg={background.playbackBG2} headerType="NONE">
|
||||
<MusicLandHeader progress={19} onPressBack={goBack} />
|
||||
<View
|
||||
style={{ flex: 1, paddingTop: 12, gap: 14, paddingBottom: gutters * 2 }}
|
||||
>
|
||||
<View style={{ flex: 1, paddingTop: 12, gap: 14, paddingBottom: gutters * 2 }}>
|
||||
<View style={{ flex: 1, gap: 22 }}>
|
||||
<Image
|
||||
source={img.placeholder3}
|
||||
style={{
|
||||
width: "80%",
|
||||
width: '80%',
|
||||
flex: 1,
|
||||
alignSelf: "center",
|
||||
alignSelf: 'center',
|
||||
borderRadius: 16,
|
||||
}}
|
||||
/>
|
||||
<Slider value="0:00" maxValue="2:00" />
|
||||
</View>
|
||||
<View
|
||||
style={{ width: "80%", alignSelf: "center", marginTop: 4, gap: 12 }}
|
||||
>
|
||||
<BorderGradientButton
|
||||
icon={icons.stars}
|
||||
title="Regénérer"
|
||||
onPress={goBack}
|
||||
/>
|
||||
<View style={{ width: '80%', alignSelf: 'center', marginTop: 4, gap: 12 }}>
|
||||
<BorderGradientButton icon={icons.stars} title="Regénérer" onPress={goBack} />
|
||||
<GradientButton
|
||||
title="Valider"
|
||||
onPress={() =>
|
||||
navigate(Routes.PlaybackDownload, {
|
||||
action: "playback",
|
||||
action: 'playback',
|
||||
})
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default VideoFinalize;
|
||||
export default VideoFinalize
|
||||
|
||||
Reference in New Issue
Block a user