feat: fixes and formatter
This commit is contained in:
@@ -1,213 +1,167 @@
|
||||
import React, {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { useRoute } from "@react-navigation/native";
|
||||
import {
|
||||
FlatList,
|
||||
Modal,
|
||||
Text,
|
||||
View,
|
||||
useWindowDimensions,
|
||||
} from "react-native";
|
||||
import { background } from "../../assets";
|
||||
import BorderGradientButton from "../../components/BorderGradientButton";
|
||||
import GradientButton from "../../components/GradientButton";
|
||||
import MusicLandHeader from "../../components/MusicLandHeader";
|
||||
import AppAlert from "../../components/Alert";
|
||||
import CreditAmount from "../../components/CreditAmount";
|
||||
import firebase, { getFunctionsClient } 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, Palette } from "../../styles";
|
||||
import { FONT_FAMILY } from "../../styles/Fonts";
|
||||
import { normalizeStructureType } from "../../utils/songStructure";
|
||||
import { openCoinPackModal } from "../../utils/coinPackModal";
|
||||
import ChooseGenre from "./ChooseGenre";
|
||||
import ChooseInstruments from "./ChooseInstruments";
|
||||
import ChooseRhythm from "./ChooseRhythm";
|
||||
import CustomizeVoice from "./CustomizeVoice";
|
||||
import CreateLyricsHeader from "../Writing/components/CreateLyricsHeader";
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useRoute } from '@react-navigation/native'
|
||||
import { FlatList, Modal, Text, View, useWindowDimensions } from 'react-native'
|
||||
import { background } from '../../assets'
|
||||
import BorderGradientButton from '../../components/BorderGradientButton'
|
||||
import GradientButton from '../../components/GradientButton'
|
||||
import MusicLandHeader from '../../components/MusicLandHeader'
|
||||
import AppAlert from '../../components/Alert'
|
||||
import CreditAmount from '../../components/CreditAmount'
|
||||
import firebase, { getFunctionsClient } 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, Palette } from '../../styles'
|
||||
import { FONT_FAMILY } from '../../styles/Fonts'
|
||||
import { normalizeStructureType } from '../../utils/songStructure'
|
||||
import { openCoinPackModal } from '../../utils/coinPackModal'
|
||||
import ChooseGenre from './ChooseGenre'
|
||||
import ChooseInstruments from './ChooseInstruments'
|
||||
import ChooseRhythm from './ChooseRhythm'
|
||||
import CustomizeVoice from './CustomizeVoice'
|
||||
import CreateLyricsHeader from '../Writing/components/CreateLyricsHeader'
|
||||
|
||||
const MUSIC_GENERATION_COIN_COST = 8;
|
||||
const CONFIRM_MODAL_MAX_WIDTH = 540;
|
||||
const FUNCTIONS_REGION = "europe-west1";
|
||||
const VOICE_SECTION_TITLES = ["BASE", "SENSIBILITÉ", "TECHNIQUE"];
|
||||
const FIRST_VOICE_STEP_INDEX = 1;
|
||||
const OPTIONAL_VOICE_CATEGORIES = new Set(["SENSIBILITÉ", "TECHNIQUE"]);
|
||||
const PAGE_WIDTH_RATIO_WEB = 0.6; // keep in sync with Page default width on web
|
||||
const PAGE_MAX_WIDTH = 1200;
|
||||
const PAGE_HORIZONTAL_PADDING = gutters * 2;
|
||||
const MUSIC_GENERATION_COIN_COST = 8
|
||||
const CONFIRM_MODAL_MAX_WIDTH = 540
|
||||
const FUNCTIONS_REGION = 'europe-west1'
|
||||
const VOICE_SECTION_TITLES = ['BASE', 'SENSIBILITÉ', 'TECHNIQUE']
|
||||
const FIRST_VOICE_STEP_INDEX = 1
|
||||
const OPTIONAL_VOICE_CATEGORIES = new Set(['SENSIBILITÉ', 'TECHNIQUE'])
|
||||
const PAGE_WIDTH_RATIO_WEB = 0.6 // keep in sync with Page default width on web
|
||||
const PAGE_MAX_WIDTH = 1200
|
||||
const PAGE_HORIZONTAL_PADDING = gutters * 2
|
||||
|
||||
const ComposeSong = () => {
|
||||
const scrollRef = useRef(null);
|
||||
const { width: windowWidth } = useWindowDimensions();
|
||||
const [selectedIndex, setSelectedIndex] = useState(0);
|
||||
const [progress, setProgress] = useState(18);
|
||||
const [parentLayout, setParentLayout] = useState(null);
|
||||
const scrollRef = useRef(null)
|
||||
const { width: windowWidth } = useWindowDimensions()
|
||||
const [selectedIndex, setSelectedIndex] = useState(0)
|
||||
const [progress, setProgress] = useState(18)
|
||||
const [parentLayout, setParentLayout] = useState(null)
|
||||
const estimatedContainerWidth = useMemo(() => {
|
||||
const safeWindowWidth =
|
||||
typeof windowWidth === "number" && Number.isFinite(windowWidth)
|
||||
? windowWidth
|
||||
: 0;
|
||||
const estimatedPageWidth = Math.min(
|
||||
safeWindowWidth * PAGE_WIDTH_RATIO_WEB,
|
||||
PAGE_MAX_WIDTH
|
||||
);
|
||||
const paddedWidth = estimatedPageWidth - PAGE_HORIZONTAL_PADDING;
|
||||
return Math.max(1, paddedWidth);
|
||||
}, [windowWidth]);
|
||||
const containerWidth = parentLayout?.width || estimatedContainerWidth;
|
||||
const route = useRoute();
|
||||
const {
|
||||
selectedProjectId,
|
||||
selectedProject,
|
||||
updateProjectData,
|
||||
currentUserData,
|
||||
currentUID,
|
||||
} = useUser();
|
||||
typeof windowWidth === 'number' && Number.isFinite(windowWidth) ? windowWidth : 0
|
||||
const estimatedPageWidth = Math.min(safeWindowWidth * PAGE_WIDTH_RATIO_WEB, PAGE_MAX_WIDTH)
|
||||
const paddedWidth = estimatedPageWidth - PAGE_HORIZONTAL_PADDING
|
||||
return Math.max(1, paddedWidth)
|
||||
}, [windowWidth])
|
||||
const containerWidth = parentLayout?.width || estimatedContainerWidth
|
||||
const route = useRoute()
|
||||
const { selectedProjectId, selectedProject, updateProjectData, currentUserData, currentUID } =
|
||||
useUser()
|
||||
|
||||
const [genres, setGenres] = useState([]);
|
||||
const [voice, setVoice] = useState({});
|
||||
const [instruments, setInstruments] = useState([]);
|
||||
const [rhythm, setRhythm] = useState(null);
|
||||
const [isConfirmVisible, setIsConfirmVisible] = useState(false);
|
||||
const [isProcessingConfirmation, setIsProcessingConfirmation] =
|
||||
useState(false);
|
||||
const isRegenerationFlow = route?.params?.isRegeneration === true;
|
||||
const [genres, setGenres] = useState([])
|
||||
const [voice, setVoice] = useState({})
|
||||
const [instruments, setInstruments] = useState([])
|
||||
const [rhythm, setRhythm] = useState(null)
|
||||
const [isConfirmVisible, setIsConfirmVisible] = useState(false)
|
||||
const [isProcessingConfirmation, setIsProcessingConfirmation] = useState(false)
|
||||
const isRegenerationFlow = route?.params?.isRegeneration === true
|
||||
|
||||
const coinBalance = useMemo(() => {
|
||||
const value = currentUserData?.coins;
|
||||
if (typeof value === "number" && Number.isFinite(value)) {
|
||||
return value;
|
||||
const value = currentUserData?.coins
|
||||
if (typeof value === 'number' && Number.isFinite(value)) {
|
||||
return value
|
||||
}
|
||||
if (typeof value === "string") {
|
||||
const parsed = Number(value);
|
||||
if (typeof value === 'string') {
|
||||
const parsed = Number(value)
|
||||
if (Number.isFinite(parsed)) {
|
||||
return parsed;
|
||||
return parsed
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}, [currentUserData?.coins]);
|
||||
return 0
|
||||
}, [currentUserData?.coins])
|
||||
|
||||
const steps = useMemo(
|
||||
() => [
|
||||
{
|
||||
key: "genres",
|
||||
key: 'genres',
|
||||
render: () => <ChooseGenre selected={genres} setSelected={setGenres} />,
|
||||
},
|
||||
...VOICE_SECTION_TITLES.map((section) => ({
|
||||
key: `voice-${section}`,
|
||||
render: () => (
|
||||
<CustomizeVoice
|
||||
category={section}
|
||||
selected={voice}
|
||||
setSelected={setVoice}
|
||||
/>
|
||||
),
|
||||
render: () => <CustomizeVoice category={section} selected={voice} setSelected={setVoice} />,
|
||||
})),
|
||||
{
|
||||
key: "instruments",
|
||||
render: () => (
|
||||
<ChooseInstruments
|
||||
selected={instruments}
|
||||
setSelected={setInstruments}
|
||||
/>
|
||||
),
|
||||
key: 'instruments',
|
||||
render: () => <ChooseInstruments selected={instruments} setSelected={setInstruments} />,
|
||||
},
|
||||
{
|
||||
key: "rhythm",
|
||||
render: () => (
|
||||
<ChooseRhythm selected={rhythm} setSelected={setRhythm} />
|
||||
),
|
||||
key: 'rhythm',
|
||||
render: () => <ChooseRhythm selected={rhythm} setSelected={setRhythm} />,
|
||||
},
|
||||
],
|
||||
[genres, voice, instruments, rhythm]
|
||||
);
|
||||
)
|
||||
|
||||
const totalSteps = steps.length;
|
||||
const lastStepIndex = totalSteps - 1;
|
||||
const instrumentStepIndex = Math.max(lastStepIndex - 1, 0);
|
||||
const totalSteps = steps.length
|
||||
const lastStepIndex = totalSteps - 1
|
||||
const instrumentStepIndex = Math.max(lastStepIndex - 1, 0)
|
||||
|
||||
const isStepValid = useMemo(() => {
|
||||
if (selectedIndex === 0) {
|
||||
return Array.isArray(genres) && genres.length > 0;
|
||||
return Array.isArray(genres) && genres.length > 0
|
||||
}
|
||||
|
||||
const voiceStepIndex = selectedIndex - FIRST_VOICE_STEP_INDEX;
|
||||
const isVoiceStep =
|
||||
voiceStepIndex >= 0 && voiceStepIndex < VOICE_SECTION_TITLES.length;
|
||||
const voiceStepIndex = selectedIndex - FIRST_VOICE_STEP_INDEX
|
||||
const isVoiceStep = voiceStepIndex >= 0 && voiceStepIndex < VOICE_SECTION_TITLES.length
|
||||
if (isVoiceStep) {
|
||||
const category = VOICE_SECTION_TITLES[voiceStepIndex];
|
||||
const category = VOICE_SECTION_TITLES[voiceStepIndex]
|
||||
if (OPTIONAL_VOICE_CATEGORIES.has(category)) {
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
return !!(voice && typeof voice === "object" && voice[category]);
|
||||
return !!(voice && typeof voice === 'object' && voice[category])
|
||||
}
|
||||
|
||||
if (selectedIndex === instrumentStepIndex) {
|
||||
return Array.isArray(instruments) && instruments.length > 0;
|
||||
return Array.isArray(instruments) && instruments.length > 0
|
||||
}
|
||||
if (selectedIndex === lastStepIndex) {
|
||||
return !!rhythm;
|
||||
return !!rhythm
|
||||
}
|
||||
return true;
|
||||
}, [
|
||||
selectedIndex,
|
||||
genres,
|
||||
voice,
|
||||
instruments,
|
||||
rhythm,
|
||||
instrumentStepIndex,
|
||||
lastStepIndex,
|
||||
]);
|
||||
return true
|
||||
}, [selectedIndex, genres, voice, instruments, rhythm, instrumentStepIndex, lastStepIndex])
|
||||
|
||||
const musicConfig = useMemo(() => {
|
||||
let lyricsArr = [];
|
||||
let lyricsArr = []
|
||||
if (Array.isArray(selectedProject?.lyrics)) {
|
||||
lyricsArr = selectedProject.lyrics.map((s) => ({
|
||||
type: normalizeStructureType(s?.type),
|
||||
lyrics: s?.lyrics || "",
|
||||
}));
|
||||
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 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 }));
|
||||
.filter(([, v]) => typeof v === 'string' && v.trim())
|
||||
.map(([category, value]) => ({ category, value }))
|
||||
|
||||
return {
|
||||
title: selectedProject?.title || "",
|
||||
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]);
|
||||
|
||||
}
|
||||
}, [selectedProject, genres, voice, instruments, rhythm, selectedProjectId])
|
||||
|
||||
useEffect(() => {
|
||||
const nextProgress = 18 + selectedIndex * 9;
|
||||
const nextProgress = 18 + selectedIndex * 9
|
||||
if (nextProgress !== progress) {
|
||||
setProgress(nextProgress);
|
||||
setProgress(nextProgress)
|
||||
}
|
||||
try {
|
||||
scrollRef.current?.scrollToIndex?.({
|
||||
index: selectedIndex,
|
||||
animated: true,
|
||||
});
|
||||
})
|
||||
} catch (_) {}
|
||||
}, [selectedIndex, progress, containerWidth]);
|
||||
}, [selectedIndex, progress, containerWidth])
|
||||
|
||||
const getItemLayout = useCallback(
|
||||
(_data, index) => ({
|
||||
@@ -216,101 +170,88 @@ const ComposeSong = () => {
|
||||
index,
|
||||
}),
|
||||
[containerWidth]
|
||||
);
|
||||
)
|
||||
|
||||
const handleCancelGeneration = () => {
|
||||
setIsConfirmVisible(false);
|
||||
navigate(Routes.SongReady);
|
||||
};
|
||||
setIsConfirmVisible(false)
|
||||
navigate(Routes.SongReady)
|
||||
}
|
||||
|
||||
const persistMusicConfig = useCallback(async () => {
|
||||
try {
|
||||
if (!selectedProjectId) return;
|
||||
if (!selectedProjectId) return
|
||||
const payload = {
|
||||
musicConfig: {
|
||||
title: musicConfig?.title || "",
|
||||
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 || "",
|
||||
instruments: Array.isArray(musicConfig?.instruments) ? musicConfig.instruments : [],
|
||||
tempo: musicConfig?.tempo || '',
|
||||
},
|
||||
musicStatus: null,
|
||||
sunoTaskId: firebase.firestore.FieldValue.delete(),
|
||||
};
|
||||
|
||||
if (!isRegenerationFlow) {
|
||||
payload.musicUrls = firebase.firestore.FieldValue.delete();
|
||||
}
|
||||
|
||||
await updateProjectData(payload);
|
||||
if (!isRegenerationFlow) {
|
||||
payload.musicUrls = firebase.firestore.FieldValue.delete()
|
||||
}
|
||||
|
||||
await updateProjectData(payload)
|
||||
} catch (_error) {}
|
||||
}, [
|
||||
isRegenerationFlow,
|
||||
musicConfig,
|
||||
selectedProjectId,
|
||||
updateProjectData,
|
||||
]);
|
||||
}, [isRegenerationFlow, musicConfig, selectedProjectId, updateProjectData])
|
||||
|
||||
const spendCoinsForGeneration = useCallback(async () => {
|
||||
if (!currentUID) {
|
||||
throw new Error("Utilisateur introuvable. Merci de réessayer.");
|
||||
throw new Error('Utilisateur introuvable. Merci de réessayer.')
|
||||
}
|
||||
|
||||
try {
|
||||
const functionsClient = getFunctionsClient(FUNCTIONS_REGION);
|
||||
const createSongOrder =
|
||||
functionsClient.httpsCallable("orders-createSongOrder");
|
||||
const functionsClient = getFunctionsClient(FUNCTIONS_REGION)
|
||||
const createSongOrder = functionsClient.httpsCallable('orders-createSongOrder')
|
||||
|
||||
await createSongOrder({
|
||||
amount: -MUSIC_GENERATION_COIN_COST,
|
||||
songId: selectedProjectId || null,
|
||||
source: "music_generation",
|
||||
});
|
||||
source: 'music_generation',
|
||||
})
|
||||
} catch (error) {
|
||||
const message =
|
||||
typeof error?.message === "string"
|
||||
? error.message.replace(
|
||||
/^functions\.https\.HttpsError:\s*/iu,
|
||||
"",
|
||||
)
|
||||
: null;
|
||||
typeof error?.message === 'string'
|
||||
? error.message.replace(/^functions\.https\.HttpsError:\s*/iu, '')
|
||||
: null
|
||||
throw new Error(
|
||||
message ||
|
||||
"Une erreur est survenue lors de la création de la commande de crédits.",
|
||||
);
|
||||
message || 'Une erreur est survenue lors de la création de la commande de crédits.'
|
||||
)
|
||||
}
|
||||
}, [currentUID, selectedProjectId]);
|
||||
}, [currentUID, selectedProjectId])
|
||||
|
||||
const handleConfirmGeneration = useCallback(async () => {
|
||||
if (isProcessingConfirmation) return;
|
||||
setIsProcessingConfirmation(true);
|
||||
if (isProcessingConfirmation) return
|
||||
setIsProcessingConfirmation(true)
|
||||
try {
|
||||
const availableCoins = Number.isFinite(coinBalance) ? coinBalance : 0;
|
||||
const availableCoins = Number.isFinite(coinBalance) ? coinBalance : 0
|
||||
if (availableCoins < MUSIC_GENERATION_COIN_COST) {
|
||||
setIsConfirmVisible(false);
|
||||
setIsConfirmVisible(false)
|
||||
AppAlert(
|
||||
"Crédits insuffisants",
|
||||
"Tu n'as pas assez de pièces pour générer une musique. Recharge ton compte pour continuer.",
|
||||
);
|
||||
openCoinPackModal();
|
||||
return;
|
||||
'Crédits insuffisants',
|
||||
"Tu n'as pas assez de pièces pour générer une musique. Recharge ton compte pour continuer."
|
||||
)
|
||||
openCoinPackModal()
|
||||
return
|
||||
}
|
||||
|
||||
await spendCoinsForGeneration();
|
||||
await persistMusicConfig();
|
||||
setIsConfirmVisible(false);
|
||||
navigate(Routes.GeneratingSong, { config: musicConfig });
|
||||
await spendCoinsForGeneration()
|
||||
await persistMusicConfig()
|
||||
setIsConfirmVisible(false)
|
||||
navigate(Routes.GeneratingSong, { config: musicConfig })
|
||||
} catch (error) {
|
||||
setIsConfirmVisible(false);
|
||||
setIsConfirmVisible(false)
|
||||
const message =
|
||||
error?.message ||
|
||||
"Une erreur est survenue lors du lancement de la génération.";
|
||||
AppAlert("Impossible de lancer la génération", message);
|
||||
error?.message || 'Une erreur est survenue lors du lancement de la génération.'
|
||||
AppAlert('Impossible de lancer la génération', message)
|
||||
} finally {
|
||||
setIsProcessingConfirmation(false);
|
||||
setIsProcessingConfirmation(false)
|
||||
}
|
||||
}, [
|
||||
coinBalance,
|
||||
@@ -318,23 +259,23 @@ const ComposeSong = () => {
|
||||
musicConfig,
|
||||
persistMusicConfig,
|
||||
spendCoinsForGeneration,
|
||||
]);
|
||||
])
|
||||
|
||||
const onPressNext = () => {
|
||||
if (selectedIndex === steps.length - 1) {
|
||||
setIsConfirmVisible(true);
|
||||
return;
|
||||
setIsConfirmVisible(true)
|
||||
return
|
||||
}
|
||||
setSelectedIndex((prev) => Math.min(prev + 1, steps.length - 1));
|
||||
};
|
||||
setSelectedIndex((prev) => Math.min(prev + 1, steps.length - 1))
|
||||
}
|
||||
|
||||
const onPressBack = () => {
|
||||
if (selectedIndex > 0) {
|
||||
setSelectedIndex((prev) => Math.max(prev - 1, 0));
|
||||
setSelectedIndex((prev) => Math.max(prev - 1, 0))
|
||||
} else {
|
||||
goBack();
|
||||
goBack()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<Page backgroundImg={background.studioBG2} headerType="NONE">
|
||||
@@ -344,10 +285,7 @@ const ComposeSong = () => {
|
||||
// logo={icons.musicLandStudio}
|
||||
/>
|
||||
<View style={{ flex: 1, paddingBottom: gutters, gap: 48 }}>
|
||||
<View
|
||||
style={{ flex: 1 }}
|
||||
onLayout={(event) => setParentLayout(event.nativeEvent.layout)}
|
||||
>
|
||||
<View style={{ flex: 1 }} onLayout={(event) => setParentLayout(event.nativeEvent.layout)}>
|
||||
<FlatList
|
||||
ref={scrollRef}
|
||||
data={steps}
|
||||
@@ -381,7 +319,7 @@ const ComposeSong = () => {
|
||||
/>
|
||||
)}
|
||||
<GradientButton
|
||||
title={selectedIndex === steps.length - 1 ? "Générer" : "Suivant"}
|
||||
title={selectedIndex === steps.length - 1 ? 'Générer' : 'Suivant'}
|
||||
onPress={onPressNext}
|
||||
disabled={!isStepValid}
|
||||
/>
|
||||
@@ -393,36 +331,36 @@ const ComposeSong = () => {
|
||||
transparent
|
||||
visible={isConfirmVisible}
|
||||
onRequestClose={() => {
|
||||
if (isProcessingConfirmation) return;
|
||||
setIsConfirmVisible(false);
|
||||
if (isProcessingConfirmation) return
|
||||
setIsConfirmVisible(false)
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
paddingHorizontal: gutters,
|
||||
paddingVertical: gutters * 1.5,
|
||||
backgroundColor: "rgba(0, 0, 0, 0.6)",
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.6)',
|
||||
}}
|
||||
>
|
||||
<CreateLyricsHeader
|
||||
containerStyle={{
|
||||
width: "100%",
|
||||
width: '100%',
|
||||
maxWidth: CONFIRM_MODAL_MAX_WIDTH,
|
||||
alignSelf: "center",
|
||||
alignSelf: 'center',
|
||||
paddingVertical: 24,
|
||||
paddingHorizontal: 24,
|
||||
gap: 24,
|
||||
}}
|
||||
>
|
||||
<View style={{ gap: 30, alignItems: "center" }}>
|
||||
<View style={{ gap: 30, alignItems: 'center' }}>
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: 15,
|
||||
gap: 12,
|
||||
alignItems: "center",
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
@@ -430,18 +368,18 @@ const ComposeSong = () => {
|
||||
fontSize: 22,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.InterSemiBold,
|
||||
textAlign: "center",
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
Générer la musique ?
|
||||
</Text>
|
||||
<View style={{ alignItems: "center", gap: 8 }}>
|
||||
<View style={{ alignItems: 'center', gap: 8 }}>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
flexWrap: "wrap",
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
flexWrap: 'wrap',
|
||||
gap: 6,
|
||||
}}
|
||||
>
|
||||
@@ -450,7 +388,7 @@ const ComposeSong = () => {
|
||||
fontSize: 16,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.InterRegular,
|
||||
textAlign: "center",
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
Cette action coûte
|
||||
@@ -461,7 +399,7 @@ const ComposeSong = () => {
|
||||
fontSize: 16,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.InterSemiBold,
|
||||
textAlign: "center",
|
||||
textAlign: 'center',
|
||||
}}
|
||||
iconSize={18}
|
||||
/>
|
||||
@@ -471,7 +409,7 @@ const ComposeSong = () => {
|
||||
fontSize: 16,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.InterRegular,
|
||||
textAlign: "center",
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
Souhaites-tu les utiliser pour lancer la génération ?
|
||||
@@ -479,9 +417,9 @@ const ComposeSong = () => {
|
||||
</View>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: 6,
|
||||
}}
|
||||
>
|
||||
@@ -490,7 +428,7 @@ const ComposeSong = () => {
|
||||
fontSize: 14,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.InterMedium,
|
||||
textAlign: "center",
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
Solde disponible :
|
||||
@@ -501,13 +439,13 @@ const ComposeSong = () => {
|
||||
fontSize: 14,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.InterMedium,
|
||||
textAlign: "center",
|
||||
textAlign: 'center',
|
||||
}}
|
||||
iconSize={16}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
<View style={{ width: "85%", alignSelf: "center", gap: 15 }}>
|
||||
<View style={{ width: '85%', alignSelf: 'center', gap: 15 }}>
|
||||
<GradientButton
|
||||
title="Confirmer"
|
||||
onPress={handleConfirmGeneration}
|
||||
@@ -516,8 +454,8 @@ const ComposeSong = () => {
|
||||
<BorderGradientButton
|
||||
title="Annuler"
|
||||
onPress={() => {
|
||||
if (isProcessingConfirmation) return;
|
||||
setIsConfirmVisible(false);
|
||||
if (isProcessingConfirmation) return
|
||||
setIsConfirmVisible(false)
|
||||
}}
|
||||
disabled={isProcessingConfirmation}
|
||||
/>
|
||||
@@ -527,7 +465,7 @@ const ComposeSong = () => {
|
||||
</View>
|
||||
</Modal>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default ComposeSong;
|
||||
export default ComposeSong
|
||||
|
||||
Reference in New Issue
Block a user