filter on lyrics for bloking unwanted content, tickets fix

This commit is contained in:
Thomas Demirdjian
2025-09-08 15:48:29 +02:00
parent 987388c501
commit a3dc4f89b7
22 changed files with 1386 additions and 126 deletions
+6 -1
View File
@@ -51,8 +51,13 @@ const EmotionConvey = ({
<View style={{ paddingHorizontal: 5 }}>
<CreateLyricsHeader
colors={item.color}
tint={selectedItem ? "default" : "dark"}
tint={"dark"}
onPress={() => onPressSelect(item)}
containerStyle={{
borderWidth: selectedItem ? 2 : 0,
borderColor: selectedItem ? "#F94697" : "transparent",
borderRadius: 14,
}}
>
<Text
style={{
+5 -6
View File
@@ -37,18 +37,16 @@ const Goals = ({ selected: selectedProp, setSelected: setSelectedProp, otherObje
<CreateLyricsHeader
onPress={() => onPressSelect(item)}
tint={selectedItem ? "default" : "dark"}
colors={
selectedItem
? ["#FFFFFF00", "#FFFFFF"]
: [Palette.tran, Palette.tran]
}
colors={[Palette.tran, Palette.tran]}
containerStyle={{
...styles.itemContainer,
borderWidth: selectedItem ? 2 : 0,
borderColor: selectedItem ? "#F94697" : "transparent",
}}
>
<View
style={{
height: 40,
height: 54,
...Style.containerCenter,
}}
>
@@ -81,6 +79,7 @@ const styles = StyleSheet.create({
paddingBottom: 50,
},
itemContainer: {
height: 54,
borderRadius: 14,
shadowColor: "#00000040",
shadowOffset: {
+74 -32
View File
@@ -1,5 +1,5 @@
import { View, ScrollView, Alert } from "react-native";
import React, { useMemo, useState, useCallback, useEffect, useRef } from "react";
import React, { useMemo, useState, useCallback, useEffect } from "react";
import Page from "../../layouts/Page";
import MusicLandHeader from "../../components/MusicLandHeader";
import { navigate } from "../../navigation/NavigationService";
@@ -61,37 +61,6 @@ const Lyrics = ({ navigation }) => {
}, [projectTitle, projectLyrics, projectConfig]);
const [titleValue, setTitleValue] = useState(initial.title || "");
const titleSaveTimer = useRef(null);
// Auto-save du titre lorsqu'il est modifié (si non vide)
useEffect(() => {
const newTitle = (titleValue || "").trim();
// Annuler tout timer précédent
if (titleSaveTimer.current) clearTimeout(titleSaveTimer.current);
// Ne rien faire si inchangé vs projet courant
const currentTitle = (selectedProject?.title || "").trim();
if (!newTitle || newTitle === currentTitle) return;
titleSaveTimer.current = setTimeout(async () => {
try {
await projectsRef.doc(selectedProjectId).set(
{
title: newTitle,
titleLower: newTitle.toLowerCase(),
updatedAt: firebase.firestore.FieldValue.serverTimestamp(),
},
{ merge: true },
);
} catch (e) {
// silencieux: l'utilisateur pourra tjs valider plus tard
console.log("Auto-save titre échoué", e?.message);
}
}, 500);
return () => {
if (titleSaveTimer.current) clearTimeout(titleSaveTimer.current);
};
}, [titleValue, selectedProjectId, selectedProject]);
const [sections, setSections] = useState(initial.sections || []);
const setSectionAt = (index, value) => {
setSections((prev) => {
@@ -158,6 +127,79 @@ const Lyrics = ({ navigation }) => {
s.lyrics === normalizedNewLyrics[i]?.lyrics,
);
// 1) Appel de la Cloud Function de modération avant tout enregistrement
try {
const callable = firebase
.functions()
.httpsCallable("lyrics-analyseLyricsToxicity");
const { data } = await callable({
title: titleTrimmed,
lyrics: normalizedNewLyrics,
});
if (!data?.success) {
// Blocage dur: afficher le message et ne pas sauvegarder
if (data?.errorCode === "TOXIC_CONTENT_BLOCKED") {
const quotes = Array.isArray(data?.result?.excerpts)
? data.result.excerpts
.slice(0, 3)
.map((e) => `${e.quote}`)
.join("\n")
: null;
Alert.alert(
"Contenu interdit",
[data?.message, quotes].filter(Boolean).join("\n\n"),
);
return; // stop here
}
// Erreur d'analyse: informer et arrêter
if (data?.errorCode === "ANALYSE_FAILED") {
Alert.alert(
"Analyse indisponible",
"Impossible de vérifier la toxicité pour le moment. Réessayez plus tard.",
);
return;
}
}
// Cas signalé mais non bloquant: demander confirmation
if (data?.errorCode === "TOXIC_CONTENT_FLAGGED") {
const quotes = Array.isArray(data?.result?.excerpts)
? data.result.excerpts
.slice(0, 3)
.map((e) => `${e.quote}`)
.join("\n")
: null;
const msg = [data?.message, quotes].filter(Boolean).join("\n\n");
const proceed = await new Promise((resolve) => {
Alert.alert("Contenu potentiellement sensible", msg, [
{
text: "Annuler",
style: "cancel",
onPress: () => resolve(false),
},
{
text: "Continuer",
style: "destructive",
onPress: () => resolve(true),
},
]);
});
if (!proceed) return;
}
} catch (moderationError) {
console.log(
"Moderation call failed",
moderationError?.message || moderationError,
);
Alert.alert(
"Analyse indisponible",
"Impossible de vérifier la toxicité pour le moment. Réessayez plus tard.",
);
return;
}
const baseData = {
title: titleTrimmed,
titleLower: titleTrimmed.toLowerCase(),
+4 -6
View File
@@ -33,18 +33,16 @@ const Rhymes = ({ selected: selectedProp, setSelected: setSelectedProp }) => {
key={index}
onPress={() => onPressSelect(item)}
tint={selectedItem ? "default" : "dark"}
colors={
selectedItem
? ["#FFFFFF00", "#FFFFFF"]
: [Palette.tran, Palette.tran]
}
colors={[Palette.tran, Palette.tran]}
containerStyle={{
...styles.itemContainer,
borderWidth: selectedItem ? 2 : 0,
borderColor: selectedItem ? "#F94697" : "transparent",
}}
>
<View
style={{
height: 40,
height: 54,
...Style.containerCenter,
paddingHorizontal: 14,
}}
+3 -5
View File
@@ -40,13 +40,11 @@ const SongStructure = ({
<CreateLyricsHeader
onPress={() => onPressSelect(item)}
tint={selectedItem ? "default" : "dark"}
colors={
selectedItem
? ["#FFFFFF00", "#FFFFFF"]
: [Palette.tran, Palette.tran]
}
colors={[Palette.tran, Palette.tran]}
containerStyle={{
...styles.itemContainer,
borderWidth: selectedItem ? 2 : 0,
borderColor: selectedItem ? "#F94697" : "transparent",
}}
>
<View
+3 -5
View File
@@ -43,15 +43,13 @@ const SongStyle = ({
return (
<CreateLyricsHeader
colors={
selectedItem
? ["#FFFFFF00", "#FFFFFF"]
: [Palette.tran, Palette.tran]
}
colors={[Palette.tran, Palette.tran]}
tint={selectedItem ? "default" : "dark"}
onPress={() => onPressSelect(item.title)}
containerStyle={{
...styles.itemContainer,
borderWidth: selectedItem ? 2 : 0,
borderColor: selectedItem ? "#F94697" : "transparent",
}}
>
<View style={{ paddingVertical: 6 }}>