import { View, Text, 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 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 useMinuit from "react-native-minuit/src/hooks/useMinuit.js"; const Lyrics = ({ navigation }) => { const [containerLayout, setContainerLayout] = useState(null); const route = useRoute(); const lyricsData = route?.params?.lyricsData; const config = route?.params?.config; const selections = route?.params?.selections; const { setIsLoading } = useMinuit(); const initial = useMemo(() => { const title = lyricsData?.title || ""; 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 || "" })) }; } // Sinon, créer à partir de la structure if (targetStructure && targetStructure.length) { return { title, sections: targetStructure.map((t) => ({ type: t, lyrics: "" })), }; } // Fallback vide return { title, sections: [] }; }, [lyricsData, config]); const [titleValue, setTitleValue] = useState(initial.title || ""); const [sections, setSections] = useState(initial.sections || []); const setSectionAt = (index, value) => { setSections((prev) => { const next = [...prev]; if (next[index]) next[index] = { ...next[index], lyrics: value }; return next; }); }; // Plus d'appel direct à la cloud function ici: on retourne à l'étape précédente const regenerate = useCallback(() => { navigate(Routes.CreateLyricsWithAi, { regenerateKey: Date.now() }); }, []); const sanitize = (obj) => { if (obj === undefined) return null; if (obj === null) return null; if (Array.isArray(obj)) return obj.map((v) => sanitize(v)); if (typeof obj === "object") { const out = {}; Object.keys(obj).forEach((k) => { const v = obj[k]; if (v === undefined) return; // omit undefined out[k] = sanitize(v); }); return out; } return obj; }; const onValidate = useCallback(async () => { try { await setIsLoading(true); const user = firebase.auth().currentUser; const payload = { title: titleValue?.trim() || "", lyrics: (sections || []).map((s) => ({ type: (s?.type || "").toLowerCase(), lyrics: s?.lyrics || "", })), config: sanitize(config), selections: sanitize(selections), userId: user ? user.uid : null, createdAt: firebase.firestore.FieldValue.serverTimestamp(), updatedAt: firebase.firestore.FieldValue.serverTimestamp(), }; await firebase.firestore().collection("projects").add(payload); navigate(Routes.Studio); } catch (e) { console.log(e); Alert.alert("Erreur", "Échec de l'enregistrement dans le projet."); } finally { await setIsLoading(false); } }, [titleValue, sections, config, selections, setIsLoading]); return ( setContainerLayout(e.nativeEvent.layout)} > {sections.map((s, idx) => { // Calculer l'index humain par type const type = (s?.type || "").toLowerCase(); const countBefore = sections .slice(0, idx) .filter((x) => (x?.type || "").toLowerCase() === type).length; const labelBase = type === "refrain" ? "Refrain" : "Couplet"; const label = `${labelBase} ${countBefore + 1}`; return ( setSectionAt(idx, val)} /> ); })} ); }; export default Lyrics;