filter on lyrics for bloking unwanted content, tickets fix
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useRoute } from "@react-navigation/core";
|
||||
import { useRoute } from "@react-navigation/native";
|
||||
import React, { useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import { background } from "../../assets";
|
||||
|
||||
@@ -10,7 +10,7 @@ import Style, { size } from "../../styles/Style";
|
||||
import { Palette } from "../../styles";
|
||||
import { FONT_FAMILY } from "../../styles/Fonts";
|
||||
import { Routes } from "../../navigation";
|
||||
import { useRoute } from "@react-navigation/core";
|
||||
import { useRoute } from "@react-navigation/native";
|
||||
|
||||
const DownloadPrices = () => {
|
||||
const params = useRoute().params;
|
||||
|
||||
@@ -12,7 +12,7 @@ import { responsiveHeight } from "react-native-responsive-dimensions";
|
||||
import { Palette } from "../../styles";
|
||||
import { FONT_FAMILY } from "../../styles/Fonts";
|
||||
import { Routes } from "../../navigation";
|
||||
import { useRoute } from "@react-navigation/core";
|
||||
import { useRoute } from "@react-navigation/native";
|
||||
|
||||
const SongDownloaded = () => {
|
||||
const params = useRoute().params;
|
||||
|
||||
@@ -12,7 +12,7 @@ import { FONT_FAMILY } from "../../styles/Fonts";
|
||||
import BorderGradientButton from "../../components/BorderGradientButton";
|
||||
import GradientButton from "../../components/GradientButton";
|
||||
import { Routes } from "../../navigation";
|
||||
import { useRoute } from "@react-navigation/core";
|
||||
import { useRoute } from "@react-navigation/native";
|
||||
|
||||
const SongRelease = () => {
|
||||
const params = useRoute().params;
|
||||
|
||||
@@ -10,7 +10,7 @@ import Style, { size } from "../../styles/Style";
|
||||
import { Palette } from "../../styles";
|
||||
import { FONT_FAMILY } from "../../styles/Fonts";
|
||||
import { Routes } from "../../navigation";
|
||||
import { useRoute } from "@react-navigation/core";
|
||||
import { useRoute } from "@react-navigation/native";
|
||||
|
||||
const STREAM_PRICES = [
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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={{
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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,
|
||||
}}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 }}>
|
||||
|
||||
Reference in New Issue
Block a user