171 lines
5.9 KiB
JavaScript
171 lines
5.9 KiB
JavaScript
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 (
|
|
<Page headerType="NONE">
|
|
<MusicLandHeader onPressBack={goBack} progress={95} />
|
|
<View
|
|
style={{
|
|
flex: 1,
|
|
marginTop: 16,
|
|
gap: 48,
|
|
}}
|
|
onLayout={(e) => setContainerLayout(e.nativeEvent.layout)}
|
|
>
|
|
<ItemContainer height={containerLayout?.height}>
|
|
<ScrollView
|
|
contentContainerStyle={{
|
|
paddingHorizontal: 14,
|
|
paddingVertical: 10,
|
|
gap: 10,
|
|
flexGrow: 1,
|
|
}}
|
|
>
|
|
<CustomInput
|
|
label="Titre"
|
|
placeholder="Titre"
|
|
height={45}
|
|
value={titleValue}
|
|
setValue={setTitleValue}
|
|
/>
|
|
{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 (
|
|
<CustomInput
|
|
key={idx}
|
|
label={label}
|
|
placeholder={labelBase}
|
|
height={type === "refrain" ? 170 : 225}
|
|
value={s?.lyrics || ""}
|
|
setValue={(val) => setSectionAt(idx, val)}
|
|
/>
|
|
);
|
|
})}
|
|
</ScrollView>
|
|
</ItemContainer>
|
|
</View>
|
|
<View
|
|
style={{
|
|
paddingTop: gutters,
|
|
paddingBottom: gutters * 2,
|
|
paddingHorizontal: gutters,
|
|
gap: 12,
|
|
}}
|
|
>
|
|
<BorderGradientButton
|
|
title="Générer d'autre paroles"
|
|
onPress={regenerate}
|
|
/>
|
|
<GradientButton title="Valider" onPress={onValidate} />
|
|
</View>
|
|
</Page>
|
|
);
|
|
};
|
|
|
|
export default Lyrics;
|