filter on lyrics for bloking unwanted content, tickets fix
This commit is contained in:
@@ -31,13 +31,12 @@ const ChooseRhythm = ({ selected, setSelected }) => {
|
||||
<CreateLyricsHeader
|
||||
onPress={() => onPressSelect(item)}
|
||||
tint={selectedItem ? "default" : "dark"}
|
||||
colors={
|
||||
selectedItem
|
||||
? ["#FFFFFF00", "#FFFFFF"]
|
||||
: [Palette.tran, Palette.tran]
|
||||
}
|
||||
// Keep transparent colors; highlight selection with border only
|
||||
colors={[Palette.tran, Palette.tran]}
|
||||
containerStyle={{
|
||||
...styles.itemContainer,
|
||||
borderWidth: selectedItem ? 2 : 0,
|
||||
borderColor: selectedItem ? "#F94697" : "transparent",
|
||||
}}
|
||||
>
|
||||
<View
|
||||
|
||||
@@ -11,8 +11,8 @@ import ChooseGenre from "./ChooseGenre";
|
||||
import CustomizeVoice from "./CustomizeVoice";
|
||||
import ChooseInstruments from "./ChooseInstruments";
|
||||
import ChooseRhythm from "./ChooseRhythm";
|
||||
import { projectsRef } from "../../config/firebase";
|
||||
import { useUser } from "../../providers/UserDataProvider";
|
||||
import firebase from "../../config/firebase";
|
||||
import { Routes } from "../../navigation";
|
||||
|
||||
const { width } = Dimensions.get("window");
|
||||
@@ -26,7 +26,8 @@ const ComposeSong = () => {
|
||||
|
||||
// Selections state
|
||||
const [genres, setGenres] = useState([]);
|
||||
const [voice, setVoice] = useState(null);
|
||||
// voice: object keyed by category (e.g., { base: string|null, Sensibilité: string|null, Technique: string|null })
|
||||
const [voice, setVoice] = useState({});
|
||||
const [instruments, setInstruments] = useState([]);
|
||||
const [rhythm, setRhythm] = useState(null);
|
||||
|
||||
@@ -35,7 +36,8 @@ const ComposeSong = () => {
|
||||
case 0:
|
||||
return Array.isArray(genres) && genres.length > 0;
|
||||
case 1:
|
||||
return !!voice;
|
||||
// Must select at least one in base category
|
||||
return !!(voice && typeof voice === "object" && voice.base);
|
||||
case 2:
|
||||
return Array.isArray(instruments) && instruments.length > 0;
|
||||
case 3:
|
||||
@@ -58,11 +60,15 @@ const ComposeSong = () => {
|
||||
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: voice || undefined,
|
||||
voice: voiceArray,
|
||||
instruments: Array.isArray(instruments) ? instruments : [],
|
||||
tempo: rhythm || undefined,
|
||||
projectId: selectedProjectId || undefined,
|
||||
@@ -83,13 +89,15 @@ const ComposeSong = () => {
|
||||
genres: Array.isArray(musicConfig?.genres)
|
||||
? musicConfig.genres
|
||||
: [],
|
||||
voice: musicConfig?.voice || "",
|
||||
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) {}
|
||||
|
||||
@@ -7,13 +7,18 @@ import { FONT_FAMILY } from "../../styles/Fonts";
|
||||
import { BlurView } from "expo-blur";
|
||||
import { VOICE } from "../../data/data";
|
||||
|
||||
const CustomizeVoice = ({ selected, setSelected }) => {
|
||||
const CustomizeVoice = ({ selected = {}, setSelected }) => {
|
||||
const [containerLayout, setContainerLayout] = useState(null);
|
||||
|
||||
const onPressSelect = (item) => {
|
||||
// Toggle select: only 1 per category (section.title)
|
||||
const onPressSelect = (category, item) => {
|
||||
if (!setSelected) return;
|
||||
if (selected === item) setSelected(null);
|
||||
else setSelected(item);
|
||||
const current = selected && typeof selected === "object" ? selected : {};
|
||||
// If tapping the same item, deselect; otherwise, set new one for the category
|
||||
const next = { ...current };
|
||||
if (current[category] === item) next[category] = null;
|
||||
else next[category] = item;
|
||||
setSelected(next);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -32,25 +37,26 @@ const CustomizeVoice = ({ selected, setSelected }) => {
|
||||
sections={VOICE}
|
||||
showsVerticalScrollIndicator={false}
|
||||
contentContainerStyle={styles.contentContainer}
|
||||
renderItem={({ item }) => {
|
||||
const selectedItem = selected === item;
|
||||
renderItem={({ item, section }) => {
|
||||
const cat = section?.title;
|
||||
const selectedItem = selected?.[cat] === item;
|
||||
|
||||
return (
|
||||
<CreateLyricsHeader
|
||||
onPress={() => onPressSelect(item)}
|
||||
onPress={() => onPressSelect(cat, item)}
|
||||
tint={selectedItem ? "default" : "dark"}
|
||||
colors={
|
||||
selectedItem
|
||||
? ["#FFFFFF00", "#FFFFFF"]
|
||||
: [Palette.tran, Palette.tran]
|
||||
}
|
||||
// Ne pas changer le blur; garder fond transparent comme ChooseInstruments
|
||||
colors={[Palette.tran, Palette.tran]}
|
||||
containerStyle={{
|
||||
...styles.itemContainer,
|
||||
borderWidth: selectedItem ? 2 : 0,
|
||||
borderColor: selectedItem ? "#F94697" : "transparent",
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
paddingVertical: 6,
|
||||
height: 40,
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<Text style={styles.itemText}>{item}</Text>
|
||||
@@ -89,6 +95,7 @@ const styles = StyleSheet.create({
|
||||
paddingBottom: 50,
|
||||
},
|
||||
itemContainer: {
|
||||
height: 54,
|
||||
backgroundColor: Palette.glass,
|
||||
borderRadius: 14,
|
||||
overflow: "hidden",
|
||||
|
||||
@@ -25,8 +25,9 @@ const GeneratingSong = () => {
|
||||
const progressTimerRef = useRef(null);
|
||||
const navigatedRef = useRef(false);
|
||||
const isFocused = useIsFocused();
|
||||
const lastConfigKeyRef = useRef(null);
|
||||
const askedRef = useRef(false);
|
||||
const callingRef = useRef(false);
|
||||
const localStartRef = useRef(null);
|
||||
|
||||
// project loaded from provider
|
||||
|
||||
@@ -39,26 +40,49 @@ const GeneratingSong = () => {
|
||||
progressTimerRef.current = null;
|
||||
}
|
||||
};
|
||||
if (selectedProject?.musicStatus === "GENERATED") {
|
||||
|
||||
const status = selectedProject?.musicStatus;
|
||||
|
||||
if (status !== "GENERATING") {
|
||||
// Reset local start when leaving generating state
|
||||
localStartRef.current = null;
|
||||
}
|
||||
|
||||
if (status === "GENERATED") {
|
||||
setProgress(100);
|
||||
clearTimer();
|
||||
return () => clearTimer();
|
||||
}
|
||||
|
||||
const resolveStartDate = () => {
|
||||
const gs = selectedProject?.generationStartAt;
|
||||
if (gs?.toDate) return gs.toDate();
|
||||
if (gs) return new Date(gs);
|
||||
if (status === "GENERATING") {
|
||||
if (!localStartRef.current) localStartRef.current = new Date();
|
||||
return localStartRef.current;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const update = () => {
|
||||
const startDate = selectedProject?.generationStartAt?.toDate
|
||||
? selectedProject.generationStartAt.toDate()
|
||||
: new Date(selectedProject?.generationStartAt || Date.now());
|
||||
const startDate = resolveStartDate();
|
||||
if (!startDate) {
|
||||
setProgress(0);
|
||||
return;
|
||||
}
|
||||
const elapsed = moment().diff(moment(startDate));
|
||||
const raw = Math.floor((elapsed / totalMs) * 100);
|
||||
// While status is GENERATING, block visual progress at 99%
|
||||
const pct = Math.max(0, Math.min(99, raw));
|
||||
setProgress(pct);
|
||||
};
|
||||
|
||||
update();
|
||||
clearTimer();
|
||||
progressTimerRef.current = setInterval(update, 1000);
|
||||
return () => clearTimer();
|
||||
}, [selectedProject?.musicStatus]);
|
||||
}, [selectedProject?.musicStatus, selectedProject?.generationStartAt]);
|
||||
|
||||
// Auto navigate to SongReady when generation completed
|
||||
useEffect(() => {
|
||||
@@ -76,7 +100,7 @@ const GeneratingSong = () => {
|
||||
title: cfg?.title || "",
|
||||
lyrics: Array.isArray(cfg?.lyrics) ? cfg.lyrics : [],
|
||||
genres: Array.isArray(cfg?.genres) ? cfg.genres : [],
|
||||
voice: cfg?.voice || "",
|
||||
voice: Array.isArray(cfg?.voice) ? cfg.voice : [],
|
||||
instruments: Array.isArray(cfg?.instruments) ? cfg.instruments : [],
|
||||
tempo: cfg?.tempo || "",
|
||||
};
|
||||
@@ -131,37 +155,49 @@ const GeneratingSong = () => {
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger generation only when focused; ask once per config
|
||||
// Ensure we never call generation more than once concurrently
|
||||
const startMusicGenerationOnce = React.useCallback(() => {
|
||||
if (callingRef.current) return;
|
||||
callingRef.current = true;
|
||||
startMusicGeneration()
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
// Keep locked while status transitions to GENERATING; will be prevented by guards
|
||||
setTimeout(() => {
|
||||
callingRef.current = false;
|
||||
}, 500);
|
||||
});
|
||||
}, []);
|
||||
|
||||
// Trigger generation only when focused; ask once while idle
|
||||
useEffect(() => {
|
||||
if (!isFocused) return;
|
||||
const status = selectedProject?.musicStatus;
|
||||
const titleOk = !!selectedProject?.title;
|
||||
// Idle if not actively generating or generated (ignore stale taskId)
|
||||
const isIdle =
|
||||
status == null ||
|
||||
status === "" ||
|
||||
status === "PENDING" ||
|
||||
status === "READY";
|
||||
|
||||
const key = JSON.stringify(effectiveConfig || {});
|
||||
if (lastConfigKeyRef.current !== key) {
|
||||
lastConfigKeyRef.current = key;
|
||||
askedRef.current = false;
|
||||
}
|
||||
|
||||
const canAsk =
|
||||
!!selectedProject?.title && selectedProject?.musicStatus !== "GENERATING";
|
||||
|
||||
if (canAsk && !askedRef.current) {
|
||||
if (titleOk && isIdle && !askedRef.current) {
|
||||
askedRef.current = true;
|
||||
Alert.alert("Attention", "Une génération va être lancée. Continuer ?", [
|
||||
{
|
||||
text: "Non",
|
||||
style: "cancel",
|
||||
onPress: () => {
|
||||
askedRef.current = false;
|
||||
},
|
||||
},
|
||||
{
|
||||
text: "Oui",
|
||||
onPress: () => startMusicGeneration(),
|
||||
},
|
||||
{ text: "Oui", onPress: () => startMusicGenerationOnce() },
|
||||
]);
|
||||
}
|
||||
}, [
|
||||
isFocused,
|
||||
selectedProject?.title,
|
||||
selectedProject?.musicStatus,
|
||||
effectiveConfig,
|
||||
]);
|
||||
|
||||
return (
|
||||
|
||||
@@ -8,7 +8,7 @@ import { goBack } from "../../navigation/NavigationService";
|
||||
import { gutters, Palette } from "../../styles";
|
||||
import { FONT_FAMILY } from "../../styles/Fonts";
|
||||
import GradientButton from "../../components/GradientButton";
|
||||
import { useRoute } from "@react-navigation/core";
|
||||
import { useRoute } from "@react-navigation/native";
|
||||
|
||||
const Regenerate = () => {
|
||||
const params = useRoute().params;
|
||||
|
||||
Reference in New Issue
Block a user