second flow
This commit is contained in:
@@ -0,0 +1,217 @@
|
||||
import React, {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { Dimensions, FlatList, View } from "react-native";
|
||||
import { background } from "../../assets";
|
||||
import GradientButton from "../../components/GradientButton";
|
||||
import MusicLandHeader from "../../components/MusicLandHeader";
|
||||
import firebase from "../../config/firebase";
|
||||
import Page from "../../layouts/Page";
|
||||
import { Routes } from "../../navigation";
|
||||
import { goBack, navigate } from "../../navigation/NavigationService";
|
||||
import { useUser } from "../../providers/UserDataProvider";
|
||||
import { gutters } from "../../styles";
|
||||
import ChooseGenre from "./ChooseGenre";
|
||||
import ChooseInstruments from "./ChooseInstruments";
|
||||
import ChooseRhythm from "./ChooseRhythm";
|
||||
import CustomizeVoice from "./CustomizeVoice";
|
||||
|
||||
const { width: windowWidth } = Dimensions.get("window");
|
||||
|
||||
const ComposeSong = () => {
|
||||
const scrollRef = useRef(null);
|
||||
const [selectedIndex, setSelectedIndex] = useState(0);
|
||||
const [progress, setProgress] = useState(18);
|
||||
const [parentLayout, setParentLayout] = useState(null);
|
||||
const containerWidth = parentLayout?.width || windowWidth || 1;
|
||||
const { selectedProjectId, selectedProject, updateProjectData } = useUser();
|
||||
|
||||
const [genres, setGenres] = useState([]);
|
||||
const [voice, setVoice] = useState({});
|
||||
const [instruments, setInstruments] = useState([]);
|
||||
const [rhythm, setRhythm] = useState(null);
|
||||
|
||||
const isStepValid = useMemo(() => {
|
||||
switch (selectedIndex) {
|
||||
case 0:
|
||||
return Array.isArray(genres) && genres.length > 0;
|
||||
case 1:
|
||||
return !!(voice && typeof voice === "object" && voice.base);
|
||||
case 2:
|
||||
return Array.isArray(instruments) && instruments.length > 0;
|
||||
case 3:
|
||||
return !!rhythm;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}, [selectedIndex, genres, voice, instruments, rhythm]);
|
||||
|
||||
const musicConfig = useMemo(() => {
|
||||
let lyricsArr = [];
|
||||
if (Array.isArray(selectedProject?.lyrics)) {
|
||||
lyricsArr = selectedProject.lyrics.map((s) => ({
|
||||
type: (s?.type || "").toLowerCase(),
|
||||
lyrics: s?.lyrics || "",
|
||||
}));
|
||||
} else {
|
||||
const c = selectedProject?.lyrics?.couplet;
|
||||
const r = selectedProject?.lyrics?.refrain;
|
||||
if (c) lyricsArr.push({ type: "couplet", lyrics: c });
|
||||
if (r) lyricsArr.push({ type: "refrain", lyrics: r });
|
||||
}
|
||||
const voiceArray = Object.entries(voice || {})
|
||||
.filter(([, v]) => typeof v === "string" && v.trim())
|
||||
.map(([category, value]) => ({ category, value }));
|
||||
|
||||
return {
|
||||
title: selectedProject?.title || "",
|
||||
lyrics: lyricsArr,
|
||||
genres: Array.isArray(genres) ? genres : [],
|
||||
voice: voiceArray,
|
||||
instruments: Array.isArray(instruments) ? instruments : [],
|
||||
tempo: rhythm || undefined,
|
||||
projectId: selectedProjectId || undefined,
|
||||
};
|
||||
}, [selectedProject, genres, voice, instruments, rhythm, selectedProjectId]);
|
||||
|
||||
const steps = useMemo(
|
||||
() => [
|
||||
{
|
||||
key: "genres",
|
||||
render: () => <ChooseGenre selected={genres} setSelected={setGenres} />,
|
||||
},
|
||||
{
|
||||
key: "voice",
|
||||
render: () => (
|
||||
<CustomizeVoice selected={voice} setSelected={setVoice} />
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "instruments",
|
||||
render: () => (
|
||||
<ChooseInstruments
|
||||
selected={instruments}
|
||||
setSelected={setInstruments}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "rhythm",
|
||||
render: () => (
|
||||
<ChooseRhythm selected={rhythm} setSelected={setRhythm} />
|
||||
),
|
||||
},
|
||||
],
|
||||
[genres, voice, instruments, rhythm]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const nextProgress = 18 + selectedIndex * 9;
|
||||
if (nextProgress !== progress) {
|
||||
setProgress(nextProgress);
|
||||
}
|
||||
try {
|
||||
scrollRef.current?.scrollToIndex?.({
|
||||
index: selectedIndex,
|
||||
animated: true,
|
||||
});
|
||||
} catch (_) {}
|
||||
}, [selectedIndex, progress, containerWidth]);
|
||||
|
||||
const getItemLayout = useCallback(
|
||||
(_data, index) => ({
|
||||
length: containerWidth,
|
||||
offset: containerWidth * index,
|
||||
index,
|
||||
}),
|
||||
[containerWidth]
|
||||
);
|
||||
|
||||
const onPressNext = async () => {
|
||||
if (selectedIndex === steps.length - 1) {
|
||||
try {
|
||||
if (selectedProjectId) {
|
||||
await updateProjectData({
|
||||
musicConfig: {
|
||||
title: musicConfig?.title || "",
|
||||
lyrics: Array.isArray(musicConfig?.lyrics)
|
||||
? musicConfig.lyrics
|
||||
: [],
|
||||
genres: Array.isArray(musicConfig?.genres)
|
||||
? musicConfig.genres
|
||||
: [],
|
||||
voice: Array.isArray(musicConfig?.voice) ? musicConfig.voice : [],
|
||||
instruments: Array.isArray(musicConfig?.instruments)
|
||||
? musicConfig.instruments
|
||||
: [],
|
||||
tempo: musicConfig?.tempo || "",
|
||||
},
|
||||
musicStatus: null,
|
||||
sunoTaskId: firebase.firestore.FieldValue.delete(),
|
||||
musicUrls: firebase.firestore.FieldValue.delete(),
|
||||
});
|
||||
}
|
||||
} catch (e) {}
|
||||
navigate(Routes.GeneratingSong, { config: musicConfig });
|
||||
return;
|
||||
}
|
||||
setSelectedIndex((prev) => Math.min(prev + 1, steps.length - 1));
|
||||
};
|
||||
|
||||
const onPressBack = () => {
|
||||
if (selectedIndex > 0) {
|
||||
setSelectedIndex((prev) => Math.max(prev - 1, 0));
|
||||
} else {
|
||||
goBack();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Page backgroundImg={background.studioBG2} headerType="NONE">
|
||||
<MusicLandHeader onPressBack={onPressBack} progress={progress} />
|
||||
<View style={{ flex: 1, paddingBottom: gutters, gap: 48 }}>
|
||||
<View
|
||||
style={{ flex: 1 }}
|
||||
onLayout={(event) => setParentLayout(event.nativeEvent.layout)}
|
||||
>
|
||||
<FlatList
|
||||
ref={scrollRef}
|
||||
data={steps}
|
||||
keyExtractor={(item) => item.key}
|
||||
horizontal
|
||||
pagingEnabled
|
||||
scrollEnabled={false}
|
||||
showsHorizontalScrollIndicator={false}
|
||||
initialScrollIndex={selectedIndex}
|
||||
getItemLayout={getItemLayout}
|
||||
style={{ width: containerWidth }}
|
||||
renderItem={({ item }) => (
|
||||
<View
|
||||
style={{
|
||||
width: containerWidth,
|
||||
height: parentLayout?.height,
|
||||
paddingHorizontal: gutters,
|
||||
}}
|
||||
>
|
||||
{item.render()}
|
||||
</View>
|
||||
)}
|
||||
/>
|
||||
</View>
|
||||
{selectedIndex !== steps.length && (
|
||||
<GradientButton
|
||||
title={selectedIndex === steps.length - 1 ? "Générer" : "Suivant"}
|
||||
onPress={onPressNext}
|
||||
disabled={!isStepValid}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
|
||||
export default ComposeSong;
|
||||
Reference in New Issue
Block a user