big refacto change all flows

This commit is contained in:
Thomas Demirdjian
2025-09-03 15:43:22 +02:00
parent 8f1c062b33
commit 60d1794c99
29 changed files with 841 additions and 344 deletions
+111 -39
View File
@@ -1,41 +1,44 @@
import { View, ScrollView, Alert } from "react-native";
import React, { useMemo, useState, useCallback } from "react";
import React, { useMemo, useState, useCallback, useEffect, useRef } from "react";
import Page from "../../layouts/Page";
import MusicLandHeader from "../../components/MusicLandHeader";
import { goBack, navigate } from "../../navigation/NavigationService";
import { navigate } from "../../navigation/NavigationService";
import { gutters } from "../../styles";
import BorderGradientButton from "../../components/BorderGradientButton";
import GradientButton from "../../components/GradientButton";
import { Routes } from "../../navigation";
import CustomInput from "./components/CustomInput";
import ItemContainer from "../../components/ItemContainer/ItemContainer";
import { useRoute } from "@react-navigation/native";
import firebase, { projectsRef } from "../../config/firebase";
import useMinuit from "react-native-minuit/src/hooks/useMinuit.js";
import { useUser } from "../../providers/UserDataProvider";
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 hasLyrics = !!route?.params?.hasLyrics;
const { setIsLoading } = useMinuit();
const { selectedProjectId, selectedProject } = useUser();
// Effective sources from provider only
const projectTitle = selectedProject?.title || "";
const projectLyrics = Array.isArray(selectedProject?.lyrics)
? selectedProject.lyrics
: [];
const projectConfig = selectedProject?.config || null;
const projectSelections = selectedProject?.selections || null;
const projectHasLyrics = !!selectedProject?.hasLyrics;
const initial = useMemo(() => {
const title = lyricsData?.title || "";
const aiSections = Array.isArray(lyricsData?.lyrics)
? lyricsData.lyrics
: [];
const title = projectTitle || "";
const aiSections = Array.isArray(projectLyrics) ? projectLyrics : [];
// Respecter l'ordre de la structure choisie si disponible
const targetStructure = Array.isArray(config?.structure)
? config.structure.map((t) => (t || "").toLowerCase())
const targetStructure = Array.isArray(projectConfig?.structure)
? projectConfig.structure.map((t) => (t || "").toLowerCase())
: null;
if (
aiSections.length &&
targetStructure &&
aiSections.length === targetStructure.length
) {
// 1) Si des paroles existent déjà, les utiliser en priorité
if (aiSections.length) {
// Optionnel: si une structure cible de même longueur existe, garder l'ordre courant
// et harmoniser les types en minuscule.
return {
title,
sections: aiSections.map((s) => ({
@@ -44,18 +47,51 @@ const Lyrics = ({ navigation }) => {
})),
};
}
// Sinon, créer à partir de la structure
// 2) Sinon, créer à partir de la structure si fournie
if (targetStructure && targetStructure.length) {
return {
title,
sections: targetStructure.map((t) => ({ type: t, lyrics: "" })),
};
}
// Fallback vide
// 3) Fallback vide
return { title, sections: [] };
}, [lyricsData, config]);
}, [projectTitle, projectLyrics, projectConfig]);
const [titleValue, setTitleValue] = useState(initial.title || "");
const titleSaveTimer = useRef(null);
// Auto-save du titre lorsqu'il est modifié (si non vide)
useEffect(() => {
const newTitle = (titleValue || "").trim();
// Annuler tout timer précédent
if (titleSaveTimer.current) clearTimeout(titleSaveTimer.current);
// Ne rien faire si inchangé vs projet courant
const currentTitle = (selectedProject?.title || "").trim();
if (!newTitle || newTitle === currentTitle) return;
titleSaveTimer.current = setTimeout(async () => {
try {
await projectsRef.doc(selectedProjectId).set(
{
title: newTitle,
titleLower: newTitle.toLowerCase(),
updatedAt: firebase.firestore.FieldValue.serverTimestamp(),
},
{ merge: true },
);
} catch (e) {
// silencieux: l'utilisateur pourra tjs valider plus tard
console.log("Auto-save titre échoué", e?.message);
}
}, 500);
return () => {
if (titleSaveTimer.current) clearTimeout(titleSaveTimer.current);
};
}, [titleValue, selectedProjectId, selectedProject]);
const [sections, setSections] = useState(initial.sections || []);
const setSectionAt = (index, value) => {
setSections((prev) => {
@@ -67,7 +103,7 @@ const Lyrics = ({ navigation }) => {
// Plus d'appel direct à la cloud function ici: on retourne à l'étape précédente
const regenerate = useCallback(() => {
navigate(Routes.CreateLyricsWithAi, { regenerateKey: Date.now() });
navigate(Routes.CreateLyricsWithAi);
}, []);
const sanitize = (obj) => {
@@ -102,34 +138,68 @@ const Lyrics = ({ navigation }) => {
);
return;
}
const user = firebase.auth().currentUser;
const payload = {
const normalizedNewLyrics = (sections || []).map((s) => ({
type: (s?.type || "").toLowerCase(),
lyrics: (s?.lyrics || "").trim(),
}));
const normalizedOldLyrics = Array.isArray(projectLyrics)
? projectLyrics.map((s) => ({
type: (s?.type || "").toLowerCase(),
lyrics: (s?.lyrics || "").trim(),
}))
: [];
const sameLength =
normalizedOldLyrics.length === normalizedNewLyrics.length;
const isSame =
sameLength &&
normalizedOldLyrics.every(
(s, i) =>
s.type === normalizedNewLyrics[i]?.type &&
s.lyrics === normalizedNewLyrics[i]?.lyrics,
);
const baseData = {
title: titleTrimmed,
titleLower: titleTrimmed.toLowerCase(),
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(),
lyrics: normalizedNewLyrics,
config: sanitize(projectConfig),
selections: sanitize(projectSelections),
updatedAt: firebase.firestore.FieldValue.serverTimestamp(),
hasLyrics,
hasLyrics: projectHasLyrics,
};
const { id: projectId } = await projectsRef.add(payload);
navigate(Routes.FlowSelection, { projectId });
const updateData = { ...baseData };
if (!isSame) {
updateData.musicUrls = firebase.firestore.FieldValue.delete();
updateData.musicStatus = firebase.firestore.FieldValue.delete();
await projectsRef
.doc(selectedProjectId)
.set(updateData, { merge: true });
}
navigate(Routes.FlowSelection);
} catch (e) {
console.log(e);
Alert.alert("Erreur", "Échec de l'enregistrement dans le projet.");
} finally {
await setIsLoading(false);
}
}, [titleValue, sections, config, selections, setIsLoading]);
}, [
titleValue,
sections,
projectConfig,
projectSelections,
setIsLoading,
selectedProjectId,
projectHasLyrics,
projectLyrics,
]);
return (
<Page headerType="NONE">
<MusicLandHeader onPressBack={goBack} progress={95} />
<MusicLandHeader
onPressBack={() => navigate(Routes.FlowSelection)}
progress={95}
/>
<View
style={{
flex: 1,
@@ -153,6 +223,8 @@ const Lyrics = ({ navigation }) => {
height={45}
value={titleValue}
setValue={setTitleValue}
multiline={false}
maxLength={60}
/>
{sections.map((s, idx) => {
// Calculer l'index humain par type
@@ -184,7 +256,7 @@ const Lyrics = ({ navigation }) => {
gap: 12,
}}
>
{!hasLyrics && (
{!projectHasLyrics && (
<BorderGradientButton
title="Générer d'autres paroles"
onPress={regenerate}