feat: filter by lang
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
|
"configVersion": 0,
|
||||||
"workspaces": {
|
"workspaces": {
|
||||||
"": {
|
"": {
|
||||||
"name": "MusicLand",
|
"name": "MusicLand",
|
||||||
@@ -110,6 +111,7 @@
|
|||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"firebase": "~9.20.0",
|
"firebase": "~9.20.0",
|
||||||
|
"react-error-overlay": "6.0.9",
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"@0no-co/graphql.web": ["@0no-co/graphql.web@1.2.0", "", { "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" }, "optionalPeers": ["graphql"] }, "sha512-/1iHy9TTr63gE1YcR5idjx8UREz1s0kFhydf3bBLCXyqjhkIc6igAzTOx3zPifCwFR87tsh/4Pa9cNts6d2otw=="],
|
"@0no-co/graphql.web": ["@0no-co/graphql.web@1.2.0", "", { "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" }, "optionalPeers": ["graphql"] }, "sha512-/1iHy9TTr63gE1YcR5idjx8UREz1s0kFhydf3bBLCXyqjhkIc6igAzTOx3zPifCwFR87tsh/4Pa9cNts6d2otw=="],
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ const { setTimeout } = require('timers/promises')
|
|||||||
|
|
||||||
// --- CONFIGURATION ---
|
// --- CONFIGURATION ---
|
||||||
// Plus intelligente que le Flash original, ultra rapide, et stable sur l'API.
|
// Plus intelligente que le Flash original, ultra rapide, et stable sur l'API.
|
||||||
const TEXT_MODEL_NAME = 'gemini-3.5-flash'
|
const TEXT_MODEL_NAME = 'gemini-flash-latest'
|
||||||
const IMAGE_MODEL_NAME = 'gemini-3-pro-image'
|
const IMAGE_MODEL_NAME = 'gemini-3-pro-image'
|
||||||
|
|
||||||
// --- SINGLETON PATTERN (WARM START) ---
|
// --- SINGLETON PATTERN (WARM START) ---
|
||||||
|
|||||||
@@ -140,6 +140,7 @@ const buildModerationBrief = ({
|
|||||||
exports.generateLyrics = onCall({ secrets: [GEMINI_API_KEY] }, async ({ auth = {}, data = {} }) => {
|
exports.generateLyrics = onCall({ secrets: [GEMINI_API_KEY] }, async ({ auth = {}, data = {} }) => {
|
||||||
try {
|
try {
|
||||||
const {
|
const {
|
||||||
|
lang: rawLang,
|
||||||
objective = '',
|
objective = '',
|
||||||
context = '',
|
context = '',
|
||||||
style = '',
|
style = '',
|
||||||
@@ -148,6 +149,15 @@ exports.generateLyrics = onCall({ secrets: [GEMINI_API_KEY] }, async ({ auth = {
|
|||||||
structure: rawStructure = ['couplet', 'refrain', 'couplet', 'refrain'],
|
structure: rawStructure = ['couplet', 'refrain', 'couplet', 'refrain'],
|
||||||
rhymes = '',
|
rhymes = '',
|
||||||
} = data
|
} = data
|
||||||
|
if (!['fr', 'en'].includes(rawLang)) {
|
||||||
|
throw new HttpsError('invalid-argument', 'La langue doit être "fr" ou "en".')
|
||||||
|
}
|
||||||
|
const lang = rawLang
|
||||||
|
const languageLabel = lang === 'en' ? 'English' : 'français'
|
||||||
|
const languageInstruction =
|
||||||
|
lang === 'en'
|
||||||
|
? 'Write the title, lyrics and description in English.'
|
||||||
|
: 'Rédige le titre, les paroles et la description en français.'
|
||||||
const rhymesInstruction = resolveRhymesInstruction(rhymes)
|
const rhymesInstruction = resolveRhymesInstruction(rhymes)
|
||||||
|
|
||||||
// 1. Préparation Structure
|
// 1. Préparation Structure
|
||||||
@@ -201,6 +211,7 @@ Tu es le meilleur parolier musical actuel, expert en "Songwriting" pour les IA g
|
|||||||
Ta spécialité est la **Prosodie** (le rythme naturel des mots) et l'impact émotionnel.
|
Ta spécialité est la **Prosodie** (le rythme naturel des mots) et l'impact émotionnel.
|
||||||
|
|
||||||
TES RÈGLES D'OR :
|
TES RÈGLES D'OR :
|
||||||
|
0. **LANGUE** : ${languageInstruction}
|
||||||
1. **MÉTRIQUE & RYTHME** : Tes vers doivent être "chantables". Compte les syllabes pour qu'elles collent au style musical demandé. Évite les phrases trop longues ou imprononçables rapidement.
|
1. **MÉTRIQUE & RYTHME** : Tes vers doivent être "chantables". Compte les syllabes pour qu'elles collent au style musical demandé. Évite les phrases trop longues ou imprononçables rapidement.
|
||||||
2. **RIMES** : Suis strictement <TYPE_DE_RIMES> du brief. Si "Sans rimes" ou "Mélange des deux" est demandé, respecte l'absence/alternance de rimes. Si rien n'est indiqué, fais des rimes riches et évite les rimes faciles (amour/toujours).
|
2. **RIMES** : Suis strictement <TYPE_DE_RIMES> du brief. Si "Sans rimes" ou "Mélange des deux" est demandé, respecte l'absence/alternance de rimes. Si rien n'est indiqué, fais des rimes riches et évite les rimes faciles (amour/toujours).
|
||||||
3. **SHOW, DON'T TELL** : N'écris pas "Je suis triste", écris "La pluie brouille mes carreaux". Utilise des détails sensoriels.
|
3. **SHOW, DON'T TELL** : N'écris pas "Je suis triste", écris "La pluie brouille mes carreaux". Utilise des détails sensoriels.
|
||||||
@@ -224,6 +235,7 @@ Retourne UNIQUEMENT un JSON valide.
|
|||||||
|
|
||||||
const prompt = `
|
const prompt = `
|
||||||
<BRIEF_CREATIF>
|
<BRIEF_CREATIF>
|
||||||
|
<LANGUE>${languageLabel}</LANGUE>
|
||||||
<OBJECTIF>${objective || 'Créer une chanson mémorable'}</OBJECTIF>
|
<OBJECTIF>${objective || 'Créer une chanson mémorable'}</OBJECTIF>
|
||||||
<CONTEXTE>${context || 'Libre interprétation'}</CONTEXTE>
|
<CONTEXTE>${context || 'Libre interprétation'}</CONTEXTE>
|
||||||
<EMOTION_DOMINANTE>${emotion || 'Intense'}</EMOTION_DOMINANTE>
|
<EMOTION_DOMINANTE>${emotion || 'Intense'}</EMOTION_DOMINANTE>
|
||||||
@@ -237,6 +249,7 @@ ${structureTags || fallbackStructureTags}
|
|||||||
</STRUCTURE_IMPOSEE>
|
</STRUCTURE_IMPOSEE>
|
||||||
|
|
||||||
<CONSIGNES_GENERATION>
|
<CONSIGNES_GENERATION>
|
||||||
|
0. ${languageInstruction}
|
||||||
1. Génère un **Titre** percutant (moins de 6 mots).
|
1. Génère un **Titre** percutant (moins de 6 mots).
|
||||||
2. Pour chaque section <SECTION>, écris le contenu adapté :
|
2. Pour chaque section <SECTION>, écris le contenu adapté :
|
||||||
- Si c'est "Instrumental" ou "Solo" : laisse le champ 'lyrics' vide ou mets une brève indication d'ambiance entre parenthèses ex: "(Solo de guitare déchirant)".
|
- Si c'est "Instrumental" ou "Solo" : laisse le champ 'lyrics' vide ou mets une brève indication d'ambiance entre parenthèses ex: "(Solo de guitare déchirant)".
|
||||||
|
|||||||
@@ -101,6 +101,9 @@ const ensureUserDocument = async (user, overrides = {}, isNewUser = false) => {
|
|||||||
typeof overrides?.lastName === 'string' ? overrides.lastName.trim() : null
|
typeof overrides?.lastName === 'string' ? overrides.lastName.trim() : null
|
||||||
const overrideDisplayName =
|
const overrideDisplayName =
|
||||||
typeof overrides?.displayName === 'string' ? overrides.displayName.trim() : null
|
typeof overrides?.displayName === 'string' ? overrides.displayName.trim() : null
|
||||||
|
const overridePreferredLanguage = ['fr', 'en'].includes(overrides?.preferredLanguage)
|
||||||
|
? overrides.preferredLanguage
|
||||||
|
: null
|
||||||
|
|
||||||
const { firstName: nameFromDisplay, lastName: lastFromDisplay } = splitDisplayName(
|
const { firstName: nameFromDisplay, lastName: lastFromDisplay } = splitDisplayName(
|
||||||
user.displayName
|
user.displayName
|
||||||
@@ -124,6 +127,10 @@ const ensureUserDocument = async (user, overrides = {}, isNewUser = false) => {
|
|||||||
payload.lastName = lastFromDisplay
|
payload.lastName = lastFromDisplay
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (overridePreferredLanguage) {
|
||||||
|
payload.preferredLanguage = overridePreferredLanguage
|
||||||
|
}
|
||||||
|
|
||||||
await docRef.set(payload, { merge: true })
|
await docRef.set(payload, { merge: true })
|
||||||
return {
|
return {
|
||||||
isNewUser,
|
isNewUser,
|
||||||
@@ -138,7 +145,7 @@ const ensureUserDocument = async (user, overrides = {}, isNewUser = false) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const useSocialAuth = ({ onSuccess } = {}) => {
|
const useSocialAuth = ({ onSuccess, preferredLanguage = null } = {}) => {
|
||||||
const [, setTooltip] = useGlobal('_tooltip')
|
const [, setTooltip] = useGlobal('_tooltip')
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
const [isAppleSignInAvailable, setIsAppleSignInAvailable] = useState(false)
|
const [isAppleSignInAvailable, setIsAppleSignInAvailable] = useState(false)
|
||||||
@@ -235,7 +242,7 @@ const useSocialAuth = ({ onSuccess } = {}) => {
|
|||||||
const userCredential = await firebase.auth().signInWithCredential(credential)
|
const userCredential = await firebase.auth().signInWithCredential(credential)
|
||||||
const authResult = await ensureUserDocument(
|
const authResult = await ensureUserDocument(
|
||||||
userCredential?.user,
|
userCredential?.user,
|
||||||
{},
|
{ preferredLanguage },
|
||||||
userCredential?.additionalUserInfo?.isNewUser === true
|
userCredential?.additionalUserInfo?.isNewUser === true
|
||||||
)
|
)
|
||||||
if (typeof onSuccess === 'function') {
|
if (typeof onSuccess === 'function') {
|
||||||
@@ -253,7 +260,7 @@ const useSocialAuth = ({ onSuccess } = {}) => {
|
|||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[onSuccess, redirectUri, request, setTooltip]
|
[onSuccess, preferredLanguage, redirectUri, request, setTooltip]
|
||||||
)
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -316,7 +323,7 @@ const useSocialAuth = ({ onSuccess } = {}) => {
|
|||||||
const userCredential = await firebase.auth().signInWithPopup(provider)
|
const userCredential = await firebase.auth().signInWithPopup(provider)
|
||||||
const authResult = await ensureUserDocument(
|
const authResult = await ensureUserDocument(
|
||||||
userCredential?.user,
|
userCredential?.user,
|
||||||
{},
|
{ preferredLanguage },
|
||||||
userCredential?.additionalUserInfo?.isNewUser === true
|
userCredential?.additionalUserInfo?.isNewUser === true
|
||||||
)
|
)
|
||||||
if (typeof onSuccess === 'function') {
|
if (typeof onSuccess === 'function') {
|
||||||
@@ -370,6 +377,7 @@ const useSocialAuth = ({ onSuccess } = {}) => {
|
|||||||
isLoading,
|
isLoading,
|
||||||
isWeb,
|
isWeb,
|
||||||
onSuccess,
|
onSuccess,
|
||||||
|
preferredLanguage,
|
||||||
promptAsync,
|
promptAsync,
|
||||||
setTooltip,
|
setTooltip,
|
||||||
])
|
])
|
||||||
@@ -422,6 +430,7 @@ const useSocialAuth = ({ onSuccess } = {}) => {
|
|||||||
{
|
{
|
||||||
firstName,
|
firstName,
|
||||||
lastName,
|
lastName,
|
||||||
|
preferredLanguage,
|
||||||
displayName:
|
displayName:
|
||||||
userCredential?.user?.displayName ||
|
userCredential?.user?.displayName ||
|
||||||
[firstName, lastName].filter(Boolean).join(' ') ||
|
[firstName, lastName].filter(Boolean).join(' ') ||
|
||||||
@@ -447,7 +456,7 @@ const useSocialAuth = ({ onSuccess } = {}) => {
|
|||||||
setTooltip({ text: message, type: 'error' })
|
setTooltip({ text: message, type: 'error' })
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
}
|
}
|
||||||
}, [isAppleSignInAvailable, isLoading, onSuccess, setTooltip])
|
}, [isAppleSignInAvailable, isLoading, onSuccess, preferredLanguage, setTooltip])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
signInWithGoogle,
|
signInWithGoogle,
|
||||||
|
|||||||
@@ -222,9 +222,11 @@ export default ({ children }) => {
|
|||||||
try {
|
try {
|
||||||
await setIsLoading(true)
|
await setIsLoading(true)
|
||||||
const artistDisplayName = getUserPreferredArtistName(currentUserDoc || currentUserData || {})
|
const artistDisplayName = getUserPreferredArtistName(currentUserDoc || currentUserData || {})
|
||||||
|
const lang = currentUserDoc?.preferredLanguage === 'en' ? 'en' : 'fr'
|
||||||
const payload = {
|
const payload = {
|
||||||
userId: currentUID || null,
|
userId: currentUID || null,
|
||||||
userName: artistDisplayName,
|
userName: artistDisplayName,
|
||||||
|
lang,
|
||||||
createdAt: firebase.firestore.FieldValue.serverTimestamp(),
|
createdAt: firebase.firestore.FieldValue.serverTimestamp(),
|
||||||
updatedAt: firebase.firestore.FieldValue.serverTimestamp(),
|
updatedAt: firebase.firestore.FieldValue.serverTimestamp(),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,5 @@
|
|||||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import {
|
import { FlatList, Image, Platform, ScrollView, StyleSheet, Text, View } from 'react-native'
|
||||||
FlatList,
|
|
||||||
Image,
|
|
||||||
Platform,
|
|
||||||
ScrollView,
|
|
||||||
StyleSheet,
|
|
||||||
Text,
|
|
||||||
View,
|
|
||||||
} from 'react-native'
|
|
||||||
import { BlurView } from 'expo-blur'
|
import { BlurView } from 'expo-blur'
|
||||||
import { responsiveHeight } from 'react-native-responsive-dimensions'
|
import { responsiveHeight } from 'react-native-responsive-dimensions'
|
||||||
import { background, icons } from '../../assets'
|
import { background, icons } from '../../assets'
|
||||||
@@ -29,6 +21,30 @@ import SongCard from './components/SongCard'
|
|||||||
import ClubCard from '../Home/components/ClubCard'
|
import ClubCard from '../Home/components/ClubCard'
|
||||||
|
|
||||||
const allowedPremiumLevels = new Set(['starter', 'pro', 'premium'])
|
const allowedPremiumLevels = new Set(['starter', 'pro', 'premium'])
|
||||||
|
const languageOptions = [
|
||||||
|
{ value: 'all', label: 'Toutes' },
|
||||||
|
{ value: 'fr', label: 'Français' },
|
||||||
|
{ value: 'en', label: 'English' },
|
||||||
|
]
|
||||||
|
|
||||||
|
const StreamingLanguageFilter = ({ selectedLanguage, onSelectLanguage }) => (
|
||||||
|
<View style={styles.languageFilter}>
|
||||||
|
<Text style={styles.languageFilterLabel}>Langue des musiques</Text>
|
||||||
|
<View style={styles.languageFilterButtons}>
|
||||||
|
{languageOptions.map((option) => (
|
||||||
|
<BorderGradientButton
|
||||||
|
key={option.value}
|
||||||
|
title={option.label}
|
||||||
|
onPress={() => onSelectLanguage(option.value)}
|
||||||
|
tint={selectedLanguage === option.value ? 'light' : 'dark'}
|
||||||
|
containerStyle={styles.languageFilterButton}
|
||||||
|
titleStyle={styles.languageFilterButtonText}
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
|
||||||
const normalizePremiumLevel = (value) => {
|
const normalizePremiumLevel = (value) => {
|
||||||
if (typeof value !== 'string') {
|
if (typeof value !== 'string') {
|
||||||
@@ -48,13 +64,19 @@ const chunkArray = (items = [], size = 10) => {
|
|||||||
|
|
||||||
const HitParade = () => {
|
const HitParade = () => {
|
||||||
const [selectedCategory, setSelectedCategory] = useState('Chansons')
|
const [selectedCategory, setSelectedCategory] = useState('Chansons')
|
||||||
|
const [selectedLanguage, setSelectedLanguage] = useState('all')
|
||||||
const navigateToMusicDetails = useNavigateToMusicDetails()
|
const navigateToMusicDetails = useNavigateToMusicDetails()
|
||||||
|
const allSongsRef = useMemo(() => {
|
||||||
|
const songsRef = projectsRef.where('hasSong', '==', true)
|
||||||
|
return selectedLanguage === 'all' ? songsRef : songsRef.where('lang', '==', selectedLanguage)
|
||||||
|
}, [selectedLanguage])
|
||||||
const { data: allSongs } = useDataFromRef({
|
const { data: allSongs } = useDataFromRef({
|
||||||
ref: projectsRef.where('hasSong', '==', true),
|
ref: allSongsRef,
|
||||||
format: (docs) => docs.filter((item) => item?.hasPlayback !== true),
|
format: (docs) => docs.filter((item) => item?.hasPlayback !== true),
|
||||||
simpleRef: false,
|
simpleRef: false,
|
||||||
listener: true,
|
listener: true,
|
||||||
condition: true,
|
condition: true,
|
||||||
|
refreshArray: [selectedLanguage],
|
||||||
})
|
})
|
||||||
|
|
||||||
const { data: topMonthSongs } = useDataFromRef({
|
const { data: topMonthSongs } = useDataFromRef({
|
||||||
@@ -62,7 +84,8 @@ const HitParade = () => {
|
|||||||
format: (docs) => docs.filter((item) => item?.hasPlayback !== true),
|
format: (docs) => docs.filter((item) => item?.hasPlayback !== true),
|
||||||
simpleRef: false,
|
simpleRef: false,
|
||||||
listener: false,
|
listener: false,
|
||||||
condition: true,
|
condition: selectedLanguage === 'all',
|
||||||
|
refreshArray: [selectedLanguage],
|
||||||
})
|
})
|
||||||
|
|
||||||
const { data: allTimeSongs } = useDataFromRef({
|
const { data: allTimeSongs } = useDataFromRef({
|
||||||
@@ -70,20 +93,31 @@ const HitParade = () => {
|
|||||||
format: (docs) => docs.filter((item) => item?.hasPlayback !== true),
|
format: (docs) => docs.filter((item) => item?.hasPlayback !== true),
|
||||||
simpleRef: false,
|
simpleRef: false,
|
||||||
listener: false,
|
listener: false,
|
||||||
condition: true,
|
condition: selectedLanguage === 'all',
|
||||||
|
refreshArray: [selectedLanguage],
|
||||||
})
|
})
|
||||||
|
|
||||||
const songsList = useMemo(() => (Array.isArray(allSongs) ? allSongs : []), [allSongs])
|
const songsList = useMemo(() => (Array.isArray(allSongs) ? allSongs : []), [allSongs])
|
||||||
|
|
||||||
const topMonthList = useMemo(
|
const topMonthList = useMemo(() => {
|
||||||
() => (Array.isArray(topMonthSongs) ? topMonthSongs : []),
|
if (selectedLanguage === 'all') {
|
||||||
[topMonthSongs]
|
return Array.isArray(topMonthSongs) ? topMonthSongs : []
|
||||||
)
|
}
|
||||||
|
return [...songsList]
|
||||||
|
.filter((item) => typeof item?.monthViews === 'number' && item.monthViews > 0)
|
||||||
|
.sort((a, b) => b.monthViews - a.monthViews)
|
||||||
|
.slice(0, 20)
|
||||||
|
}, [selectedLanguage, songsList, topMonthSongs])
|
||||||
|
|
||||||
const allTimeList = useMemo(
|
const allTimeList = useMemo(() => {
|
||||||
() => (Array.isArray(allTimeSongs) ? allTimeSongs : []),
|
if (selectedLanguage === 'all') {
|
||||||
[allTimeSongs]
|
return Array.isArray(allTimeSongs) ? allTimeSongs : []
|
||||||
)
|
}
|
||||||
|
return [...songsList]
|
||||||
|
.filter((item) => typeof item?.views === 'number' && item.views > 0)
|
||||||
|
.sort((a, b) => b.views - a.views)
|
||||||
|
.slice(0, 20)
|
||||||
|
}, [allTimeSongs, selectedLanguage, songsList])
|
||||||
|
|
||||||
// === RENDUS PAR PLATEFORME ===
|
// === RENDUS PAR PLATEFORME ===
|
||||||
const renderSongsMobile = () => (
|
const renderSongsMobile = () => (
|
||||||
@@ -254,7 +288,12 @@ const HitParade = () => {
|
|||||||
[setSearchSelected]
|
[setSearchSelected]
|
||||||
)
|
)
|
||||||
|
|
||||||
const musicResults = useMemo(() => (Array.isArray(musics) ? musics : []), [musics])
|
const musicResults = useMemo(() => {
|
||||||
|
const results = Array.isArray(musics) ? musics : []
|
||||||
|
return selectedLanguage === 'all'
|
||||||
|
? results
|
||||||
|
: results.filter((project) => project?.lang === selectedLanguage)
|
||||||
|
}, [musics, selectedLanguage])
|
||||||
|
|
||||||
const [creatorsById, setCreatorsById] = useState({})
|
const [creatorsById, setCreatorsById] = useState({})
|
||||||
|
|
||||||
@@ -632,6 +671,10 @@ const HitParade = () => {
|
|||||||
style={{ alignSelf: 'center', height: 150, resizeMode: 'contain' }}
|
style={{ alignSelf: 'center', height: 150, resizeMode: 'contain' }}
|
||||||
/> */}
|
/> */}
|
||||||
<ClubCard />
|
<ClubCard />
|
||||||
|
<StreamingLanguageFilter
|
||||||
|
selectedLanguage={selectedLanguage}
|
||||||
|
onSelectLanguage={setSelectedLanguage}
|
||||||
|
/>
|
||||||
|
|
||||||
{isWeb && (
|
{isWeb && (
|
||||||
<View
|
<View
|
||||||
@@ -724,3 +767,28 @@ const HitParade = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default HitParade
|
export default HitParade
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
languageFilter: {
|
||||||
|
width: '100%',
|
||||||
|
maxWidth: 620,
|
||||||
|
alignSelf: 'center',
|
||||||
|
gap: 8,
|
||||||
|
},
|
||||||
|
languageFilterLabel: {
|
||||||
|
color: Palette.white,
|
||||||
|
fontSize: 13,
|
||||||
|
fontFamily: FONT_FAMILY.InterSemiBold,
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
languageFilterButtons: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
gap: 10,
|
||||||
|
},
|
||||||
|
languageFilterButton: {
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
languageFilterButtonText: {
|
||||||
|
fontSize: 13,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ const Register = () => {
|
|||||||
isLoading: socialLoading,
|
isLoading: socialLoading,
|
||||||
} = useSocialAuth({
|
} = useSocialAuth({
|
||||||
onSuccess: afterSocialAuth,
|
onSuccess: afterSocialAuth,
|
||||||
|
preferredLanguage,
|
||||||
})
|
})
|
||||||
|
|
||||||
const isBusy = socialLoading
|
const isBusy = socialLoading
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ const CreatingLyrics = ({ active, config, selections, onErrorRedirect }) => {
|
|||||||
const [error, setError] = useState(null)
|
const [error, setError] = useState(null)
|
||||||
const hasShownErrorAlert = useRef(false)
|
const hasShownErrorAlert = useRef(false)
|
||||||
const { setIsLoading } = useMinuit()
|
const { setIsLoading } = useMinuit()
|
||||||
const { updateProjectData } = useUser()
|
const { selectedProject, updateProjectData } = useUser()
|
||||||
|
const projectLang = selectedProject?.lang === 'en' ? 'en' : 'fr'
|
||||||
const progressValue = Math.max(0, Math.min(100, Math.round(progress)))
|
const progressValue = Math.max(0, Math.min(100, Math.round(progress)))
|
||||||
|
|
||||||
// Reset when becomes active
|
// Reset when becomes active
|
||||||
@@ -74,6 +75,7 @@ const CreatingLyrics = ({ active, config, selections, onErrorRedirect }) => {
|
|||||||
const sanitizedStructure = sanitizeStructureList(config?.structure, CUSTOM_SANITIZE_OPTIONS)
|
const sanitizedStructure = sanitizeStructureList(config?.structure, CUSTOM_SANITIZE_OPTIONS)
|
||||||
const callable = firebase.functions().httpsCallable('lyrics-generateLyrics')
|
const callable = firebase.functions().httpsCallable('lyrics-generateLyrics')
|
||||||
const { data } = await callable({
|
const { data } = await callable({
|
||||||
|
lang: projectLang,
|
||||||
objective: config?.objective,
|
objective: config?.objective,
|
||||||
context: config?.context,
|
context: config?.context,
|
||||||
emotion: config?.emotion,
|
emotion: config?.emotion,
|
||||||
@@ -188,6 +190,7 @@ const CreatingLyrics = ({ active, config, selections, onErrorRedirect }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await updateProjectData({
|
await updateProjectData({
|
||||||
|
lang: projectLang,
|
||||||
title: result?.title || '',
|
title: result?.title || '',
|
||||||
lyrics: Array.isArray(result?.lyrics) ? result.lyrics : [],
|
lyrics: Array.isArray(result?.lyrics) ? result.lyrics : [],
|
||||||
description: result?.lyricsDescription,
|
description: result?.lyricsDescription,
|
||||||
@@ -206,7 +209,18 @@ const CreatingLyrics = ({ active, config, selections, onErrorRedirect }) => {
|
|||||||
if (active && result && progress >= 100 && !saved && !error) {
|
if (active && result && progress >= 100 && !saved && !error) {
|
||||||
autoSaveAndGo()
|
autoSaveAndGo()
|
||||||
}
|
}
|
||||||
}, [active, result, progress, saved, error, setIsLoading, updateProjectData, config, selections])
|
}, [
|
||||||
|
active,
|
||||||
|
result,
|
||||||
|
progress,
|
||||||
|
saved,
|
||||||
|
error,
|
||||||
|
setIsLoading,
|
||||||
|
updateProjectData,
|
||||||
|
config,
|
||||||
|
selections,
|
||||||
|
projectLang,
|
||||||
|
])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (error && !hasShownErrorAlert.current) {
|
if (error && !hasShownErrorAlert.current) {
|
||||||
@@ -303,6 +317,7 @@ const CreatingLyrics = ({ active, config, selections, onErrorRedirect }) => {
|
|||||||
await setIsLoading(true)
|
await setIsLoading(true)
|
||||||
console.log('📄 [CreatingLyrics] Consultation manuelle du texte')
|
console.log('📄 [CreatingLyrics] Consultation manuelle du texte')
|
||||||
await updateProjectData({
|
await updateProjectData({
|
||||||
|
lang: projectLang,
|
||||||
title: result?.title || '',
|
title: result?.title || '',
|
||||||
lyrics: Array.isArray(result?.lyrics) ? result.lyrics : [],
|
lyrics: Array.isArray(result?.lyrics) ? result.lyrics : [],
|
||||||
config: config || null,
|
config: config || null,
|
||||||
|
|||||||
Reference in New Issue
Block a user