continue generating flow, add backend connection on main tabs
This commit is contained in:
@@ -69,7 +69,7 @@ const CreatingSong = ({ active, config }) => {
|
||||
const elapsed = moment().diff(moment(startDate));
|
||||
const pct = Math.max(
|
||||
0,
|
||||
Math.min(100, Math.floor((elapsed / totalMs) * 100))
|
||||
Math.min(100, Math.floor((elapsed / totalMs) * 100)),
|
||||
);
|
||||
setProgress(pct);
|
||||
if (pct >= 100) {
|
||||
@@ -127,10 +127,11 @@ const CreatingSong = ({ active, config }) => {
|
||||
instruments: config?.instruments || [],
|
||||
tempo: config?.tempo || "",
|
||||
},
|
||||
generationStartAt: firebase.firestore.FieldValue.serverTimestamp(),
|
||||
generationStartAt:
|
||||
firebase.firestore.FieldValue.serverTimestamp(),
|
||||
updatedAt: firebase.firestore.FieldValue.serverTimestamp(),
|
||||
},
|
||||
{ merge: true }
|
||||
{ merge: true },
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { View, Text, Image, StyleSheet } from "react-native";
|
||||
import { View, Image, StyleSheet } from "react-native";
|
||||
import React from "react";
|
||||
import { ai, background } from "../../assets";
|
||||
import Page from "../../layouts/Page";
|
||||
@@ -27,11 +27,11 @@ const FinishCompose = () => {
|
||||
>
|
||||
<BorderGradientButton
|
||||
title="Continuer plus tard"
|
||||
onPress={() => navigate(Routes.Onboarding)}
|
||||
onPress={() => navigate(Routes.Home)}
|
||||
/>
|
||||
<GradientButton
|
||||
title="Continuer la création"
|
||||
onPress={() => navigate(Routes.Onboarding)}
|
||||
onPress={() => navigate(Routes.Home)}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { View, Text, Image } from "react-native";
|
||||
import React from "react";
|
||||
import { View, Text, Image, ActivityIndicator } from "react-native";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Page from "../../layouts/Page";
|
||||
import { background, icons, img } from "../../assets";
|
||||
import MusicLandHeader from "../../components/MusicLandHeader";
|
||||
@@ -10,27 +10,103 @@ import BorderGradientButton from "../../components/BorderGradientButton";
|
||||
import GradientButton from "../../components/GradientButton";
|
||||
import { Routes } from "../../navigation";
|
||||
import { FONT_FAMILY } from "../../styles/Fonts";
|
||||
import { useRoute } from "@react-navigation/native";
|
||||
import firebase from "../../config/firebase";
|
||||
|
||||
const PouchReady = () => {
|
||||
const route = useRoute();
|
||||
const projectId = route?.params?.projectId;
|
||||
const [coverUrl, setCoverUrl] = useState(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [title, setTitle] = useState("");
|
||||
const [coverStatus, setCoverStatus] = useState(null);
|
||||
const isGenerating = coverStatus === "GENERATING";
|
||||
|
||||
useEffect(() => {
|
||||
if (!projectId) return;
|
||||
const unsub = firebase
|
||||
.firestore()
|
||||
.collection("projects")
|
||||
.doc(projectId)
|
||||
.onSnapshot((doc) => {
|
||||
const d = doc.data() || {};
|
||||
setTitle(d?.title || "");
|
||||
setCoverUrl(d?.coverUrl || null);
|
||||
setCoverStatus(d?.coverStatus || null);
|
||||
});
|
||||
return () => unsub?.();
|
||||
}, [projectId]);
|
||||
|
||||
const generateCover = async () => {
|
||||
if (!projectId) return;
|
||||
try {
|
||||
setLoading(true);
|
||||
// Marquer le projet en génération
|
||||
await firebase
|
||||
.firestore()
|
||||
.collection("projects")
|
||||
.doc(projectId)
|
||||
.set({ coverStatus: "GENERATING" }, { merge: true });
|
||||
|
||||
// Créer une tâche pour déclencher la Cloud Function onCreate
|
||||
await firebase.firestore().collection("tasks").add({
|
||||
type: "cover",
|
||||
projectId,
|
||||
status: "PENDING",
|
||||
createdAt: firebase.firestore.FieldValue.serverTimestamp(),
|
||||
});
|
||||
} catch (e) {
|
||||
console.log("Cover task error", e?.message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!projectId) return;
|
||||
// Si pas de cover et pas déjà en génération, lancer une tâche
|
||||
if (!coverUrl && !isGenerating && !loading) {
|
||||
generateCover();
|
||||
}
|
||||
}, [projectId, coverUrl, isGenerating]);
|
||||
|
||||
return (
|
||||
<Page backgroundImg={background.studioBG2} headerType="NONE">
|
||||
<MusicLandHeader onPressBack={goBack} progress={72} />
|
||||
<View style={{ flex: 1, marginTop: 16 }}>
|
||||
<CreateLyricsHeader
|
||||
title="Ta pochette est prête!"
|
||||
title={
|
||||
coverUrl ? "Ta pochette est prête!" : "Génération de la pochette"
|
||||
}
|
||||
subTitle="Qu’en penses-tu ?"
|
||||
/>
|
||||
<View style={{ flex: 1, ...Style.containerCenter }}>
|
||||
<View style={{ width: "80%", position: "relative" }}>
|
||||
<Image
|
||||
source={img.placeholder}
|
||||
style={{
|
||||
width: "100%",
|
||||
height: 300,
|
||||
borderRadius: 20,
|
||||
transform: [{ rotateY: "180deg" }],
|
||||
}}
|
||||
/>
|
||||
{coverUrl ? (
|
||||
<Image
|
||||
source={{ uri: coverUrl }}
|
||||
style={{ width: "100%", height: 300, borderRadius: 20 }}
|
||||
/>
|
||||
) : (
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
height: 300,
|
||||
borderRadius: 20,
|
||||
backgroundColor: "#00000040",
|
||||
...Style.containerCenter,
|
||||
}}
|
||||
>
|
||||
{isGenerating || loading ? (
|
||||
<ActivityIndicator color={Palette.white} />
|
||||
) : (
|
||||
<Image
|
||||
source={img.placeholder}
|
||||
style={{ width: 120, height: 120, opacity: 0.5 }}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
<View
|
||||
style={{
|
||||
position: "absolute",
|
||||
@@ -46,7 +122,7 @@ const PouchReady = () => {
|
||||
fontFamily: FONT_FAMILY.InterSemiBold,
|
||||
}}
|
||||
>
|
||||
Lust for Life
|
||||
{title || "Titre"}
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
@@ -55,7 +131,7 @@ const PouchReady = () => {
|
||||
fontFamily: FONT_FAMILY.InterRegular,
|
||||
}}
|
||||
>
|
||||
Lana del Rey
|
||||
MusicLand
|
||||
</Text>
|
||||
</View>
|
||||
<View
|
||||
@@ -84,16 +160,14 @@ const PouchReady = () => {
|
||||
}}
|
||||
>
|
||||
<BorderGradientButton
|
||||
title="Regénérer"
|
||||
title={isGenerating ? "Génération en cours..." : "Regénérer"}
|
||||
icon={icons.stars}
|
||||
onPress={() =>
|
||||
navigate(Routes.Regenerate, {
|
||||
progress: 72,
|
||||
})
|
||||
}
|
||||
onPress={generateCover}
|
||||
disabled={isGenerating}
|
||||
/>
|
||||
<GradientButton
|
||||
title="Valider"
|
||||
title={isGenerating ? "Veuillez patienter..." : "Valider"}
|
||||
disabled={isGenerating || !coverUrl}
|
||||
onPress={() => navigate(Routes.PhotoCover)}
|
||||
/>
|
||||
</View>
|
||||
|
||||
@@ -129,7 +129,7 @@ const SongReady = () => {
|
||||
},
|
||||
{ merge: true },
|
||||
);
|
||||
navigate(Routes.PouchReady);
|
||||
navigate(Routes.PouchReady, { projectId });
|
||||
} catch (e) {
|
||||
console.log("Validate error", e?.message);
|
||||
}
|
||||
|
||||
+22
-104
@@ -8,106 +8,13 @@ import { Routes } from "../../navigation";
|
||||
import { gutters } from "../../styles";
|
||||
import firebase from "../../config/firebase";
|
||||
import useMinuit from "react-native-minuit/src/hooks/useMinuit.js";
|
||||
import useDataFromRef from "../../hooks/useDataFromRef";
|
||||
import { useUser } from "../../providers/UserDataProvider";
|
||||
import { responsiveHeight } from "react-native-responsive-dimensions";
|
||||
|
||||
const Studio = () => {
|
||||
const { setIsLoading } = useMinuit();
|
||||
const [selected, setSelected] = useState(null);
|
||||
|
||||
const user = firebase.auth().currentUser;
|
||||
const { data: projects } = useDataFromRef({
|
||||
ref: user
|
||||
? firebase
|
||||
.firestore()
|
||||
.collection("projects")
|
||||
.where("userId", "==", user.uid)
|
||||
.orderBy("createdAt", "desc")
|
||||
: firebase
|
||||
.firestore()
|
||||
.collection("projects")
|
||||
.orderBy("createdAt", "desc"),
|
||||
simpleRef: false,
|
||||
listener: true,
|
||||
condition: true,
|
||||
});
|
||||
|
||||
async function generateMusic() {
|
||||
try {
|
||||
await setIsLoading(true);
|
||||
const { data } = await firebase
|
||||
.functions()
|
||||
.httpsCallable("music-generateMusic")({
|
||||
lyrics: [
|
||||
{
|
||||
lyrics:
|
||||
"Sous le ciel de minuit, une idée prend son envol\nUn éclat dans le noir, comme un nouveau symbole\nDes startups qui rêvent, des projets plein d'ardeur\nMinuit est là pour elles, avec cœur et saveur\nDes écrans qui s'allument, des lignes de code en fête\nL'équipe est au taquet, pas de place pour la défaite\nLe design est notre guide, l'humain notre priorité\nPour bâtir le futur, avec créativité",
|
||||
type: "couplet",
|
||||
},
|
||||
{
|
||||
lyrics:
|
||||
"Minuit, oh Minuit, on avance ensemble vers l'infini\nNotre passion digitale, c'est notre plus beau défi\nDes idées qui fusent, l'esprit d'équipe au rendez-vous\nPour que chaque projet, brille de mille feux, c'est tout\nLa joie dans nos cœurs, l'énergie à revendre\nMinuit, c'est la flamme qui nous fait bien comprendre\nQue l'innovation, c'est la clé de demain\nAvec vous, à vos côtés, main dans la main !",
|
||||
type: "refrain",
|
||||
},
|
||||
{
|
||||
lyrics:
|
||||
"Du prototype agile, au financement final\nOn vous accompagne, c'est notre idéal\nApplications mobiles, sur mesure et parfaites\nL'optimisation constante, pour des routes bien nettes\nL'accompagnement précis, le sourire sur les visages\nC'est ça notre ADN, notre plus beau message\nLa nuit ne fait que commencer, le travail est un plaisir\nEnsemble, nous allons loin, pour un grand avenir",
|
||||
type: "couplet",
|
||||
},
|
||||
{
|
||||
lyrics:
|
||||
"Minuit, oh Minuit, on avance ensemble vers l'infini\nNotre passion digitale, c'est notre plus beau défi\nDes idées qui fusent, l'esprit d'équipe au rendez-vous\nPour que chaque projet, brille de mille feux, c'est tout\nLa joie dans nos cœurs, l'énergie à revendre\nMinuit, c'est la flamme qui nous fait bien comprendre\nQue l'innovation, c'est la clé de demain\nAvec vous, à vos côtés, main dans la main !",
|
||||
type: "refrain",
|
||||
},
|
||||
],
|
||||
title: "Viens avec nous chez Minuit",
|
||||
genres: [
|
||||
"Pop : Musique commerciale destinée au grand public, accrocheuse et mélodique. Domine les charts internationaux avec des artistes ultra-médiatisés.",
|
||||
"Soul : Voix puissantes, émotion, style afro-américain. Influence pop et le R&B. Mélodieuse, émotionnelle et expressive dérivée du gospel et du R&B. authenticité émotionnelle.",
|
||||
],
|
||||
voice:
|
||||
"Un chœur gospel pour donner une dimension spirituelle et émotionnelle à la chanson.",
|
||||
instruments: [
|
||||
"Piano classique",
|
||||
"Synthétiseur",
|
||||
"Guitare acoustique",
|
||||
"Violon",
|
||||
],
|
||||
tempo: "Normal",
|
||||
});
|
||||
console.log("data", data);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
} finally {
|
||||
await setIsLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function testSunoStatus() {
|
||||
try {
|
||||
await setIsLoading(true);
|
||||
const { data } = await firebase
|
||||
.functions()
|
||||
.httpsCallable("music-getSunoStatus")({
|
||||
taskId: "1b04b24a9b95a46125a73f25144b74c2",
|
||||
});
|
||||
console.log("🎵 Status data:", data);
|
||||
|
||||
if (data.success) {
|
||||
console.log("✅ Fonction getSunoStatus fonctionne correctement");
|
||||
if (data.data.isTestId) {
|
||||
console.log("ℹ️ TaskId de test détecté - réponse simulée");
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("❌ Erreur getSunoStatus:", e.message || e);
|
||||
if (e.message && e.message.includes("404")) {
|
||||
console.log("ℹ️ Erreur 404 normale pour un taskId de test");
|
||||
}
|
||||
} finally {
|
||||
await setIsLoading(false);
|
||||
}
|
||||
}
|
||||
const { userProjects: projects } = useUser();
|
||||
|
||||
return (
|
||||
<Page
|
||||
@@ -207,15 +114,26 @@ const Studio = () => {
|
||||
}}
|
||||
/>
|
||||
{selected?.musicUrls?.length > 0 && (
|
||||
<GradientButton
|
||||
title="Ecouter les audios"
|
||||
containerStyle={{
|
||||
marginTop: responsiveHeight(2),
|
||||
}}
|
||||
onPress={() =>
|
||||
navigate(Routes.SongReady, { projectId: selected.id })
|
||||
}
|
||||
/>
|
||||
<>
|
||||
<GradientButton
|
||||
title="Ecouter les audios"
|
||||
containerStyle={{
|
||||
marginTop: responsiveHeight(2),
|
||||
}}
|
||||
onPress={() =>
|
||||
navigate(Routes.SongReady, { projectId: selected.id })
|
||||
}
|
||||
/>
|
||||
<GradientButton
|
||||
title="Générer une pochette"
|
||||
containerStyle={{
|
||||
marginTop: responsiveHeight(2),
|
||||
}}
|
||||
onPress={() =>
|
||||
navigate(Routes.PouchReady, { projectId: selected.id })
|
||||
}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
</Page>
|
||||
|
||||
Reference in New Issue
Block a user