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 { normalizeStructureType } from "../../utils/songStructure"; 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: normalizeStructureType(s?.type), 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: () => , }, { key: "voice", render: () => ( ), }, { key: "instruments", render: () => ( ), }, { key: "rhythm", render: () => ( ), }, ], [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 ( setParentLayout(event.nativeEvent.layout)} > item.key} horizontal pagingEnabled scrollEnabled={false} showsHorizontalScrollIndicator={false} initialScrollIndex={selectedIndex} getItemLayout={getItemLayout} style={{ width: containerWidth }} renderItem={({ item }) => ( {item.render()} )} /> {selectedIndex !== steps.length && ( )} ); }; export default ComposeSong;