Change main page and modify flows
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
import { View, Dimensions } from "react-native";
|
||||
import React, { useMemo, useRef, useState } from "react";
|
||||
import { useRoute } from "@react-navigation/native";
|
||||
import Page from "../../layouts/Page";
|
||||
import MusicLandHeader from "../../components/MusicLandHeader";
|
||||
import GradientButton from "../../components/GradientButton";
|
||||
import { gutters } from "../../styles";
|
||||
import { SwiperFlatList } from "react-native-swiper-flatlist";
|
||||
import Goals from "./Goals";
|
||||
import { goBack } from "../../navigation/NavigationService";
|
||||
import { goBack, navigate } from "../../navigation/NavigationService";
|
||||
import { Routes } from "../../navigation";
|
||||
import SpecificityContext from "./SpecificityContext";
|
||||
import EmotionConvey from "./EmotionConvey";
|
||||
import SongStyle from "./SongStyle";
|
||||
@@ -20,42 +20,24 @@ import { responsiveHeight } from "react-native-responsive-dimensions";
|
||||
|
||||
const { width } = Dimensions.get("window");
|
||||
|
||||
const CreateLyricsWithAi = () => {
|
||||
const CreateLyricsWithAi = ({ route }) => {
|
||||
const { hasLyrics = false, regenerateKey } = route?.params || {};
|
||||
const scrollRef = useRef(null);
|
||||
const route = useRoute();
|
||||
const regenerateKey = route?.params?.regenerateKey;
|
||||
const [selectedIndex, setSelectedIndex] = useState(0);
|
||||
// If user already has lyrics, start at SongStructure (index 5)
|
||||
const [selectedIndex, setSelectedIndex] = useState(hasLyrics ? 5 : 0);
|
||||
const [progress, setProgress] = useState(16);
|
||||
const [parentLayout, setparentLayout] = useState(null);
|
||||
|
||||
// Collected state across steps
|
||||
const [objective, setObjective] = useState(
|
||||
__DEV__ ? "Pour mon entreprise" : null,
|
||||
); // from Goals list
|
||||
const [objective, setObjective] = useState(null); // from Goals list
|
||||
const [otherObjective, setOtherObjective] = useState("");
|
||||
const [context, setContext] = useState(
|
||||
__DEV__
|
||||
? "L'agence minuit est une agence de développement mobile et web qui accompagnes ses client dans la réalisations de projets divers et variés"
|
||||
: "",
|
||||
);
|
||||
const [emotion, setEmotion] = useState(
|
||||
__DEV__
|
||||
? {
|
||||
title: "La Joie",
|
||||
description:
|
||||
"expose un bonheur profond, l'émerveillement, la gratitude, satisfaction intense, énergie positive",
|
||||
}
|
||||
: null,
|
||||
); // { title, description }
|
||||
const [style, setStyle] = useState(__DEV__ ? "Upbeat" : null); // from list
|
||||
const [context, setContext] = useState("");
|
||||
const [emotion, setEmotion] = useState(null);
|
||||
const [style, setStyle] = useState(null); // from list
|
||||
const [otherStyle, setOtherStyle] = useState("");
|
||||
const [audience, setAudience] = useState(
|
||||
__DEV__ ? "Aux clients de l'agence minuit" : "",
|
||||
);
|
||||
const [structure, setStructure] = useState(
|
||||
__DEV__ ? "1 couplet, 1 refrain, 1 couplet, 1 refrain" : null,
|
||||
); // selected structure string
|
||||
const [rhymes, setRhymes] = useState(__DEV__ ? "Avec rimes" : null);
|
||||
const [audience, setAudience] = useState("");
|
||||
const [structure, setStructure] = useState(null); // selected structure string
|
||||
const [rhymes, setRhymes] = useState(null);
|
||||
const [customStructure, setCustomStructure] = useState(null); // array like ['couplet','refrain']
|
||||
|
||||
const parsedStructure = useMemo(() => {
|
||||
@@ -117,27 +99,61 @@ const CreateLyricsWithAi = () => {
|
||||
]);
|
||||
|
||||
const onPressNext = () => {
|
||||
setSelectedIndex(selectedIndex + 1);
|
||||
setProgress(progress + 9);
|
||||
scrollRef.current.scrollToIndex({
|
||||
index: selectedIndex + 1,
|
||||
animated: true,
|
||||
});
|
||||
// If user already has lyrics and just finished CustomizeSongStructure (index 6),
|
||||
// skip AI generation and go straight to Lyrics editor
|
||||
if (hasLyrics && selectedIndex === 6) {
|
||||
const chosenStructure =
|
||||
(customStructure &&
|
||||
parsedStructure &&
|
||||
customStructure.length === parsedStructure.length
|
||||
? customStructure
|
||||
: parsedStructure) || [];
|
||||
navigate(Routes.Lyrics, {
|
||||
config: { structure: chosenStructure },
|
||||
selections: {
|
||||
objective,
|
||||
otherObjective,
|
||||
context,
|
||||
emotion,
|
||||
style,
|
||||
otherStyle,
|
||||
audience,
|
||||
structure,
|
||||
parsedStructure,
|
||||
customStructure,
|
||||
rhymes,
|
||||
},
|
||||
hasLyrics: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
setSelectedIndex((idx) => idx + 1);
|
||||
};
|
||||
|
||||
const onPressBack = () => {
|
||||
if (selectedIndex > 0) {
|
||||
setSelectedIndex(selectedIndex - 1);
|
||||
setProgress(progress - 9);
|
||||
scrollRef.current.scrollToIndex({
|
||||
index: selectedIndex - 1,
|
||||
animated: true,
|
||||
});
|
||||
// If currently on SongStructure, go back to previous screen instead of previous step
|
||||
if (selectedIndex === 5) {
|
||||
goBack();
|
||||
} else if (selectedIndex > 0) {
|
||||
setSelectedIndex((idx) => Math.max(0, idx - 1));
|
||||
} else {
|
||||
goBack();
|
||||
}
|
||||
};
|
||||
|
||||
// Sync scroll position and progress with selectedIndex
|
||||
React.useEffect(() => {
|
||||
if (!parentLayout?.height) return;
|
||||
try {
|
||||
const nextProgress = 16 + selectedIndex * 9;
|
||||
setProgress(nextProgress);
|
||||
scrollRef.current?.scrollToIndex?.({
|
||||
index: selectedIndex,
|
||||
animated: true,
|
||||
});
|
||||
} catch (_) {}
|
||||
}, [selectedIndex, parentLayout?.height]);
|
||||
|
||||
return (
|
||||
<Page headerType="NONE">
|
||||
<MusicLandHeader
|
||||
@@ -160,6 +176,7 @@ const CreateLyricsWithAi = () => {
|
||||
style={{ width, left: -gutters }}
|
||||
ref={scrollRef}
|
||||
disableGesture
|
||||
index={selectedIndex}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
|
||||
@@ -168,6 +168,7 @@ const CreatingLyrics = ({ active, config, regenerateKey, selections }) => {
|
||||
lyricsData: result,
|
||||
config,
|
||||
selections,
|
||||
hasLyrics: false,
|
||||
})
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -1,84 +1,93 @@
|
||||
import { View, Text, StyleSheet, ScrollView } from "react-native";
|
||||
import React, { useMemo, useState } from "react";
|
||||
import CreateLyricsHeader from "./components/CreateLyricsHeader";
|
||||
import { View, StyleSheet, Image, Platform, Text } from "react-native";
|
||||
import React, { useMemo, useState, useCallback, useEffect } from "react";
|
||||
import { BlurView } from "expo-blur";
|
||||
import { Palette } from "../../styles";
|
||||
import CreateLyricsHeader from "./components/CreateLyricsHeader";
|
||||
import { Palette, Style } from "../../styles";
|
||||
import { FONT_FAMILY } from "../../styles/Fonts";
|
||||
import ItemContainer from "../../components/ItemContainer/ItemContainer";
|
||||
import { CUSTOM_SONG_STRUCTURE } from "../../data/data";
|
||||
import SongStructureDragDrop from "../../components/SongStructureDragDrop";
|
||||
import Sortable from "react-native-sortables";
|
||||
import { icons } from "../../assets";
|
||||
|
||||
// baseStructure: array like ['couplet','refrain',...]
|
||||
// onChange: callback that receives array like ['couplet','refrain',...]
|
||||
const CustomizeSongStructure = ({ baseStructure = [], onChange }) => {
|
||||
const [containerLayout, setContainerLayout] = useState(null);
|
||||
|
||||
const sourceItems = useMemo(() => {
|
||||
// create unique ids for duplicates
|
||||
// Build stable keyed data like { 'couplet-1': { label, value }, ... }
|
||||
const data = useMemo(() => {
|
||||
const counters = { couplet: 0, refrain: 0 };
|
||||
return (baseStructure || []).map((type) => {
|
||||
const key = (type || '').toLowerCase();
|
||||
const out = {};
|
||||
(baseStructure || []).forEach((type) => {
|
||||
const key = (type || "").toLowerCase();
|
||||
counters[key] = (counters[key] || 0) + 1;
|
||||
const idx = counters[key];
|
||||
return {
|
||||
id: `${key}-${idx}`,
|
||||
label: `${type} ${idx}`,
|
||||
const id = `${key}-${idx}`;
|
||||
out[id] = {
|
||||
id,
|
||||
label: `${key === "couplet" ? "Couplet" : "Refrain"} ${idx}`,
|
||||
value: key,
|
||||
};
|
||||
});
|
||||
return out;
|
||||
}, [baseStructure]);
|
||||
|
||||
const items = useMemo(() => Object.values(data), [data]);
|
||||
|
||||
// Initialize parent with current order (so it's saved even without drag)
|
||||
useEffect(() => {
|
||||
const initialValues = (items || []).map((it) => it?.value).filter(Boolean);
|
||||
onChange?.(initialValues);
|
||||
// We only want to run when baseStructure-derived items change
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [items.length]);
|
||||
|
||||
const Row = ({ item: rowData }) => (
|
||||
<View style={styles.itemContainer}>
|
||||
<BlurView
|
||||
intensity={30}
|
||||
style={styles.rowBlur}
|
||||
experimentalBlurMethod={Platform.OS !== "ios" ? "dimezisBlurView" : "none"}
|
||||
>
|
||||
<View style={{ ...Style.containerSpaceBetween, alignItems: "center" }}>
|
||||
<View>
|
||||
<View>
|
||||
<View>
|
||||
<View />
|
||||
</View>
|
||||
</View>
|
||||
<View>
|
||||
<View />
|
||||
</View>
|
||||
</View>
|
||||
<View style={{ flex: 1, justifyContent: "center" }}>
|
||||
<View>
|
||||
<Text style={styles.rowText}>{rowData?.label || ""}</Text>
|
||||
</View>
|
||||
</View>
|
||||
<Sortable.Handle>
|
||||
<Image source={icons.dragDots} style={styles.handleIcon} />
|
||||
</Sortable.Handle>
|
||||
</View>
|
||||
</BlurView>
|
||||
</View>
|
||||
);
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1, gap: 10, marginTop: 16 }}>
|
||||
<CreateLyricsHeader
|
||||
title="Personnalise la structure de ta chanson"
|
||||
subTitle="Intègre en Drag and drop"
|
||||
subTitle="Réorganise par glisser-déposer"
|
||||
/>
|
||||
{/* <View style={{ flex: 1, gap: 10 }}>
|
||||
<View
|
||||
style={{ flex: 1 }}
|
||||
onLayout={(e) => {
|
||||
setContainerLayout(e.nativeEvent.layout);
|
||||
<View style={{ flex: 1 }} onLayout={(e) => setContainerLayout(e.nativeEvent.layout)}>
|
||||
<Sortable.Grid
|
||||
columns={1}
|
||||
rowGap={12}
|
||||
data={items}
|
||||
renderItem={({ item }) => <Row item={item} />}
|
||||
customHandle
|
||||
onDragEnd={({ data: newData }) => {
|
||||
const values = (newData || []).map((it) => it?.value).filter(Boolean);
|
||||
onChange?.(values);
|
||||
}}
|
||||
>
|
||||
<ItemContainer height={containerLayout?.height}>
|
||||
<ScrollView contentContainerStyle={{ gap: 10, paddingBottom: 20 }}>
|
||||
{["Couplet", "Refrain", "Couplet", "Refrain"].map(
|
||||
(item, index) => (
|
||||
<View key={index} style={styles.dropzoneContainer}>
|
||||
<Text style={styles.dropzone}>{item}</Text>
|
||||
</View>
|
||||
)
|
||||
)}
|
||||
</ScrollView>
|
||||
</ItemContainer>
|
||||
</View>
|
||||
<View style={{ flex: 1 }}>
|
||||
<ItemContainer height={containerLayout?.height}>
|
||||
<ScrollView contentContainerStyle={{ gap: 10, paddingBottom: 20 }}>
|
||||
{CUSTOM_SONG_STRUCTURE.map((item, index) => (
|
||||
<View key={index} style={styles.itemContainer}>
|
||||
<BlurView
|
||||
intensity={30}
|
||||
style={{
|
||||
flex: 1,
|
||||
backgroundColor: Palette.glass,
|
||||
justifyContent: "center",
|
||||
paddingHorizontal: 12,
|
||||
}}
|
||||
>
|
||||
<Text style={styles.dropzone}>{item}</Text>
|
||||
</BlurView>
|
||||
</View>
|
||||
))}
|
||||
</ScrollView>
|
||||
</ItemContainer>
|
||||
</View>
|
||||
</View> */}
|
||||
<View style={{ flex: 1 }}>
|
||||
<SongStructureDragDrop
|
||||
sourceItems={sourceItems}
|
||||
onChange={(arr) => onChange?.(arr)}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
@@ -88,17 +97,6 @@ const CustomizeSongStructure = ({ baseStructure = [], onChange }) => {
|
||||
export default CustomizeSongStructure;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
dropzoneContainer: {
|
||||
paddingVertical: 14,
|
||||
paddingHorizontal: 12,
|
||||
backgroundColor: "#00000080",
|
||||
borderRadius: 14,
|
||||
},
|
||||
dropzone: {
|
||||
fontSize: 16,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.InterRegular,
|
||||
},
|
||||
itemContainer: {
|
||||
height: 54,
|
||||
backgroundColor: Palette.glass,
|
||||
@@ -113,4 +111,21 @@ const styles = StyleSheet.create({
|
||||
shadowRadius: 3.84,
|
||||
elevation: 5,
|
||||
},
|
||||
rowBlur: {
|
||||
flex: 1,
|
||||
backgroundColor: Palette.glass,
|
||||
justifyContent: "center",
|
||||
paddingHorizontal: 12,
|
||||
height: "100%",
|
||||
},
|
||||
rowText: {
|
||||
fontSize: 16,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.InterRegular,
|
||||
},
|
||||
handleIcon: {
|
||||
width: 22,
|
||||
height: 22,
|
||||
tintColor: Palette.white,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import { View, Text, FlatList } from "react-native";
|
||||
import React, { useState } from "react";
|
||||
import CreateLyricsHeader from "./components/CreateLyricsHeader";
|
||||
import { responsiveHeight } from "react-native-responsive-dimensions";
|
||||
import { Palette } from "../../styles";
|
||||
import { FONT_FAMILY } from "../../styles/Fonts";
|
||||
import { EMOTION_CONVEY } from "../../data/data";
|
||||
import ItemContainer from "../../components/ItemContainer/ItemContainer";
|
||||
|
||||
const EmotionConvey = ({ selected: selectedProp, setSelected: setSelectedProp }) => {
|
||||
const EmotionConvey = ({
|
||||
selected: selectedProp,
|
||||
setSelected: setSelectedProp,
|
||||
}) => {
|
||||
const [containerLayout, setContainerLayout] = useState(null);
|
||||
const [internalSelected, setInternalSelected] = useState(null);
|
||||
const selected = selectedProp ?? internalSelected;
|
||||
@@ -25,8 +27,8 @@ const EmotionConvey = ({ selected: selectedProp, setSelected: setSelectedProp })
|
||||
return (
|
||||
<View style={{ flex: 1, gap: 10, marginTop: 16 }}>
|
||||
<CreateLyricsHeader
|
||||
title={`Quel émotion veux-tu\ntransmettre ?`}
|
||||
subTitle="Sélectionne tes intentions émotionnelles. (2 max)"
|
||||
title={`Quelle émotion veux-tu\ntransmettre ?`}
|
||||
subTitle="Sélectionne une seule intention émotionnelle."
|
||||
/>
|
||||
|
||||
<View
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
import { View, Text, ScrollView, Alert } from "react-native";
|
||||
import { View, ScrollView, Alert } from "react-native";
|
||||
import React, { useMemo, useState, useCallback } from "react";
|
||||
import Page from "../../layouts/Page";
|
||||
import MusicLandHeader from "../../components/MusicLandHeader";
|
||||
import { goBack, navigate } from "../../navigation/NavigationService";
|
||||
import { gutters, Palette } from "../../styles";
|
||||
import { gutters } from "../../styles";
|
||||
import BorderGradientButton from "../../components/BorderGradientButton";
|
||||
import GradientButton from "../../components/GradientButton";
|
||||
import { Routes } from "../../navigation";
|
||||
import CustomInput from "./components/CustomInput";
|
||||
import { FONT_FAMILY } from "../../styles/Fonts";
|
||||
import ItemContainer from "../../components/ItemContainer/ItemContainer";
|
||||
import { useRoute } from "@react-navigation/native";
|
||||
import firebase from "../../config/firebase";
|
||||
import firebase, { projectsRef } from "../../config/firebase";
|
||||
import useMinuit from "react-native-minuit/src/hooks/useMinuit.js";
|
||||
|
||||
const Lyrics = ({ navigation }) => {
|
||||
@@ -20,17 +19,30 @@ const Lyrics = ({ navigation }) => {
|
||||
const lyricsData = route?.params?.lyricsData;
|
||||
const config = route?.params?.config;
|
||||
const selections = route?.params?.selections;
|
||||
const hasLyrics = !!route?.params?.hasLyrics;
|
||||
const { setIsLoading } = useMinuit();
|
||||
|
||||
const initial = useMemo(() => {
|
||||
const title = lyricsData?.title || "";
|
||||
const aiSections = Array.isArray(lyricsData?.lyrics) ? lyricsData.lyrics : [];
|
||||
const aiSections = Array.isArray(lyricsData?.lyrics)
|
||||
? lyricsData.lyrics
|
||||
: [];
|
||||
// Respecter l'ordre de la structure choisie si disponible
|
||||
const targetStructure = Array.isArray(config?.structure)
|
||||
? config.structure.map((t) => (t || "").toLowerCase())
|
||||
: null;
|
||||
if (aiSections.length && targetStructure && aiSections.length === targetStructure.length) {
|
||||
return { title, sections: aiSections.map((s) => ({ type: (s?.type || "").toLowerCase(), lyrics: s?.lyrics || "" })) };
|
||||
if (
|
||||
aiSections.length &&
|
||||
targetStructure &&
|
||||
aiSections.length === targetStructure.length
|
||||
) {
|
||||
return {
|
||||
title,
|
||||
sections: aiSections.map((s) => ({
|
||||
type: (s?.type || "").toLowerCase(),
|
||||
lyrics: s?.lyrics || "",
|
||||
})),
|
||||
};
|
||||
}
|
||||
// Sinon, créer à partir de la structure
|
||||
if (targetStructure && targetStructure.length) {
|
||||
@@ -77,9 +89,23 @@ const Lyrics = ({ navigation }) => {
|
||||
const onValidate = useCallback(async () => {
|
||||
try {
|
||||
await setIsLoading(true);
|
||||
const titleTrimmed = (titleValue || "").trim();
|
||||
if (!titleTrimmed) {
|
||||
Alert.alert("Titre manquant", "Veuillez renseigner un titre.");
|
||||
return;
|
||||
}
|
||||
const invalid = (sections || []).some((s) => !(s?.lyrics || "").trim());
|
||||
if (invalid) {
|
||||
Alert.alert(
|
||||
"Champs incomplets",
|
||||
"Chaque couplet et refrain doit contenir du texte.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
const user = firebase.auth().currentUser;
|
||||
const payload = {
|
||||
title: titleValue?.trim() || "",
|
||||
title: titleTrimmed,
|
||||
titleLower: titleTrimmed.toLowerCase(),
|
||||
lyrics: (sections || []).map((s) => ({
|
||||
type: (s?.type || "").toLowerCase(),
|
||||
lyrics: s?.lyrics || "",
|
||||
@@ -89,9 +115,10 @@ const Lyrics = ({ navigation }) => {
|
||||
userId: user ? user.uid : null,
|
||||
createdAt: firebase.firestore.FieldValue.serverTimestamp(),
|
||||
updatedAt: firebase.firestore.FieldValue.serverTimestamp(),
|
||||
hasLyrics,
|
||||
};
|
||||
await firebase.firestore().collection("projects").add(payload);
|
||||
navigate(Routes.Studio);
|
||||
const { id: projectId } = await projectsRef.add(payload);
|
||||
navigate(Routes.FlowSelection, { projectId });
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
Alert.alert("Erreur", "Échec de l'enregistrement dans le projet.");
|
||||
@@ -157,10 +184,12 @@ const Lyrics = ({ navigation }) => {
|
||||
gap: 12,
|
||||
}}
|
||||
>
|
||||
<BorderGradientButton
|
||||
title="Générer d'autre paroles"
|
||||
onPress={regenerate}
|
||||
/>
|
||||
{!hasLyrics && (
|
||||
<BorderGradientButton
|
||||
title="Générer d'autres paroles"
|
||||
onPress={regenerate}
|
||||
/>
|
||||
)}
|
||||
<GradientButton title="Valider" onPress={onValidate} />
|
||||
</View>
|
||||
</Page>
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import { View, Text, StyleSheet, FlatList, Pressable } from "react-native";
|
||||
import { View, Text, StyleSheet, FlatList } from "react-native";
|
||||
import React, { useState } from "react";
|
||||
import CreateLyricsHeader from "./components/CreateLyricsHeader";
|
||||
import { BlurView } from "expo-blur";
|
||||
import { GOALS, SONG_STRUCTURE } from "../../data/data";
|
||||
import { Palette, Style } from "../../styles";
|
||||
import { SONG_STRUCTURE } from "../../data/data";
|
||||
import { Palette } from "../../styles";
|
||||
import { FONT_FAMILY } from "../../styles/Fonts";
|
||||
import ItemContainer from "../../components/ItemContainer/ItemContainer";
|
||||
|
||||
const SongStructure = ({ selected: selectedProp, setSelected: setSelectedProp }) => {
|
||||
const SongStructure = ({
|
||||
selected: selectedProp,
|
||||
setSelected: setSelectedProp,
|
||||
}) => {
|
||||
const [internalSelected, setInternalSelected] = useState(null);
|
||||
const selected = selectedProp ?? internalSelected;
|
||||
const setSelected = setSelectedProp ?? setInternalSelected;
|
||||
|
||||
@@ -8,7 +8,12 @@ import CustomInput from "./components/CustomInput";
|
||||
import { FONT_FAMILY } from "../../styles/Fonts";
|
||||
import ItemContainer from "../../components/ItemContainer/ItemContainer";
|
||||
|
||||
const SongStyle = ({ selected: selectedProp, setSelected: setSelectedProp, otherStyle, setOtherStyle }) => {
|
||||
const SongStyle = ({
|
||||
selected: selectedProp,
|
||||
setSelected: setSelectedProp,
|
||||
otherStyle,
|
||||
setOtherStyle,
|
||||
}) => {
|
||||
const [internalSelected, setInternalSelected] = useState(null);
|
||||
const selected = selectedProp ?? internalSelected;
|
||||
const setSelected = setSelectedProp ?? setInternalSelected;
|
||||
|
||||
@@ -13,7 +13,7 @@ const WritingLyrics = () => {
|
||||
return (
|
||||
<Page headerType="NONE">
|
||||
<Image source={ai.nathalie} style={styles.img} resizeMode="contain" />
|
||||
<MusicLandHeader showSkip onPressBack={goBack} progress={9} />
|
||||
<MusicLandHeader onPressBack={goBack} progress={9} />
|
||||
<View
|
||||
style={{ flex: 1, position: "relative", justifyContent: "flex-end" }}
|
||||
>
|
||||
@@ -24,7 +24,13 @@ const WritingLyrics = () => {
|
||||
gap: 12,
|
||||
}}
|
||||
>
|
||||
<BorderGradientButton />
|
||||
<BorderGradientButton
|
||||
onPress={() => {
|
||||
navigate(Routes.CreateLyricsWithAi, {
|
||||
hasLyrics: true,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<GradientButton
|
||||
title="Écrire des paroles avec une IA"
|
||||
onPress={() => navigate(Routes.CreateLyricsWithAi)}
|
||||
|
||||
Reference in New Issue
Block a user