feat: fixes and formatter
This commit is contained in:
+228
-254
@@ -1,7 +1,7 @@
|
||||
/* global setInterval, clearInterval */
|
||||
import { useFocusEffect } from "@react-navigation/native";
|
||||
import { BlurView } from "expo-blur";
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { useFocusEffect } from '@react-navigation/native'
|
||||
import { BlurView } from 'expo-blur'
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import {
|
||||
FlatList,
|
||||
Image,
|
||||
@@ -11,30 +11,30 @@ import {
|
||||
Text,
|
||||
View,
|
||||
useWindowDimensions,
|
||||
} from "react-native";
|
||||
import { responsiveHeight } from "react-native-responsive-dimensions";
|
||||
import { background, icons } from "../../assets";
|
||||
import alert from "../../components/Alert";
|
||||
import BorderGradientButton from "../../components/BorderGradientButton";
|
||||
import CreditAmount from "../../components/CreditAmount";
|
||||
import GradientButton from "../../components/GradientButton";
|
||||
import ValidateModal from "../../components/modal/ValidateModal";
|
||||
import MusicLandHeader from "../../components/MusicLandHeader";
|
||||
import ProgressSlider from "../../components/player/ProgressSlider";
|
||||
import { isWeb } from "../../hooks/useLayoutType";
|
||||
import Page from "../../layouts/Page";
|
||||
import { Routes } from "../../navigation";
|
||||
import { navigate, reset } from "../../navigation/NavigationService";
|
||||
import { useUser } from "../../providers/UserDataProvider";
|
||||
import { Palette, Style } from "../../styles";
|
||||
import { gutters, size } from "../../styles/Style";
|
||||
import { FONT_FAMILY } from "../../styles/Fonts";
|
||||
import useSharedAudioPlayer from "../../hooks/useSharedAudioPlayer";
|
||||
import CreateLyricsHeader from "../Writing/components/CreateLyricsHeader";
|
||||
} from 'react-native'
|
||||
import { responsiveHeight } from 'react-native-responsive-dimensions'
|
||||
import { background, icons } from '../../assets'
|
||||
import alert from '../../components/Alert'
|
||||
import BorderGradientButton from '../../components/BorderGradientButton'
|
||||
import CreditAmount from '../../components/CreditAmount'
|
||||
import GradientButton from '../../components/GradientButton'
|
||||
import ValidateModal from '../../components/modal/ValidateModal'
|
||||
import MusicLandHeader from '../../components/MusicLandHeader'
|
||||
import ProgressSlider from '../../components/player/ProgressSlider'
|
||||
import { isWeb } from '../../hooks/useLayoutType'
|
||||
import Page from '../../layouts/Page'
|
||||
import { Routes } from '../../navigation'
|
||||
import { navigate, reset } from '../../navigation/NavigationService'
|
||||
import { useUser } from '../../providers/UserDataProvider'
|
||||
import { Palette, Style } from '../../styles'
|
||||
import { gutters, size } from '../../styles/Style'
|
||||
import { FONT_FAMILY } from '../../styles/Fonts'
|
||||
import useSharedAudioPlayer from '../../hooks/useSharedAudioPlayer'
|
||||
import CreateLyricsHeader from '../Writing/components/CreateLyricsHeader'
|
||||
|
||||
const MUSIC_GENERATION_COIN_COST = 8;
|
||||
const SONG_OPTIONS_PER_GENERATION = 2;
|
||||
const REGENERATE_MODAL_MAX_WIDTH = 540;
|
||||
const MUSIC_GENERATION_COIN_COST = 8
|
||||
const SONG_OPTIONS_PER_GENERATION = 2
|
||||
const REGENERATE_MODAL_MAX_WIDTH = 540
|
||||
|
||||
const SongReady = () => {
|
||||
const {
|
||||
@@ -42,81 +42,79 @@ const SongReady = () => {
|
||||
selectedProject,
|
||||
updateProjectData,
|
||||
updateUserData,
|
||||
} = useUser();
|
||||
const [showValidateModal, setShowValidateModal] = useState(false);
|
||||
const [showRegenerateModal, setShowRegenerateModal] = useState(false);
|
||||
const [musicUrls, setMusicUrls] = useState([]);
|
||||
const [selectedIndex, setSelectedIndex] = useState(0);
|
||||
const playerRefs = useRef({});
|
||||
} = useUser()
|
||||
const [showValidateModal, setShowValidateModal] = useState(false)
|
||||
const [showRegenerateModal, setShowRegenerateModal] = useState(false)
|
||||
const [musicUrls, setMusicUrls] = useState([])
|
||||
const [selectedIndex, setSelectedIndex] = useState(0)
|
||||
const playerRefs = useRef({})
|
||||
|
||||
const registerPlayer = useCallback((index, player) => {
|
||||
if (player) {
|
||||
playerRefs.current[index] = player;
|
||||
playerRefs.current[index] = player
|
||||
} else {
|
||||
delete playerRefs.current[index];
|
||||
delete playerRefs.current[index]
|
||||
}
|
||||
}, []);
|
||||
}, [])
|
||||
|
||||
const pauseAllExcept = useCallback(async (keepIndex = null) => {
|
||||
const tasks = Object.entries(playerRefs.current).map(
|
||||
async ([key, player]) => {
|
||||
const idx = Number(key);
|
||||
if (!player || idx === keepIndex) return;
|
||||
try {
|
||||
await player.pause?.();
|
||||
} catch (error) {
|
||||
console.log("SongReady pause error", error?.message);
|
||||
}
|
||||
},
|
||||
);
|
||||
await Promise.all(tasks);
|
||||
}, []);
|
||||
const tasks = Object.entries(playerRefs.current).map(async ([key, player]) => {
|
||||
const idx = Number(key)
|
||||
if (!player || idx === keepIndex) return
|
||||
try {
|
||||
await player.pause?.()
|
||||
} catch (error) {
|
||||
console.log('SongReady pause error', error?.message)
|
||||
}
|
||||
})
|
||||
await Promise.all(tasks)
|
||||
}, [])
|
||||
|
||||
const pauseAllPlayers = useCallback(async () => {
|
||||
await pauseAllExcept(null);
|
||||
}, [pauseAllExcept]);
|
||||
await pauseAllExcept(null)
|
||||
}, [pauseAllExcept])
|
||||
|
||||
const handleTogglePlayback = useCallback(
|
||||
async (index, isPlayingNow) => {
|
||||
const player = playerRefs.current[index];
|
||||
if (!player) return;
|
||||
const player = playerRefs.current[index]
|
||||
if (!player) return
|
||||
if (isPlayingNow) {
|
||||
try {
|
||||
await player.pause?.();
|
||||
await player.pause?.()
|
||||
} catch (error) {
|
||||
console.log("SongReady pause toggle", error?.message);
|
||||
console.log('SongReady pause toggle', error?.message)
|
||||
}
|
||||
return;
|
||||
return
|
||||
}
|
||||
await pauseAllExcept(index);
|
||||
await pauseAllExcept(index)
|
||||
try {
|
||||
await player.play?.();
|
||||
await player.play?.()
|
||||
} catch (error) {
|
||||
console.log("SongReady play error", error?.message);
|
||||
console.log('SongReady play error', error?.message)
|
||||
}
|
||||
},
|
||||
[pauseAllExcept],
|
||||
);
|
||||
[pauseAllExcept]
|
||||
)
|
||||
|
||||
// Sync URLs from provider's selectedProject
|
||||
useEffect(() => {
|
||||
const urls = Array.isArray(selectedProject?.musicUrls)
|
||||
? selectedProject.musicUrls
|
||||
.filter((url) => typeof url === "string" && url.trim())
|
||||
.map((url) => url.trim())
|
||||
: [];
|
||||
setMusicUrls(urls);
|
||||
.filter((url) => typeof url === 'string' && url.trim())
|
||||
.map((url) => url.trim())
|
||||
: []
|
||||
setMusicUrls(urls)
|
||||
setSelectedIndex((prev) => {
|
||||
if (!urls.length) return 0;
|
||||
return Math.min(prev, urls.length - 1);
|
||||
});
|
||||
}, [selectedProject?.musicUrls]);
|
||||
if (!urls.length) return 0
|
||||
return Math.min(prev, urls.length - 1)
|
||||
})
|
||||
}, [selectedProject?.musicUrls])
|
||||
|
||||
const validateSelection = async () => {
|
||||
try {
|
||||
const url = musicUrls[selectedIndex];
|
||||
const url = musicUrls[selectedIndex]
|
||||
|
||||
if (!projectId || !url) return;
|
||||
if (!projectId || !url) return
|
||||
await updateProjectData({
|
||||
songIndex: selectedIndex,
|
||||
songUrl: url,
|
||||
@@ -126,74 +124,74 @@ const SongReady = () => {
|
||||
playbackUrl: null,
|
||||
playbackStatus: null,
|
||||
playbackGenerating: null,
|
||||
});
|
||||
})
|
||||
|
||||
// Incrémenter le compteur de chansons générées
|
||||
const currentGeneratedSongs = selectedProject?.user?.generatedSongs || 0;
|
||||
const currentGeneratedSongs = selectedProject?.user?.generatedSongs || 0
|
||||
await updateUserData({
|
||||
data: {
|
||||
generatedSongs: currentGeneratedSongs + 1,
|
||||
},
|
||||
shouldSetTooltip: false,
|
||||
});
|
||||
})
|
||||
|
||||
await pauseAllPlayers();
|
||||
await pauseAllPlayers()
|
||||
reset({
|
||||
index: 1,
|
||||
routes: [{ name: Routes.BottomTab }, { name: Routes.ChooseCoverType }],
|
||||
});
|
||||
})
|
||||
} catch (e) {
|
||||
console.log("Validate error", e?.message);
|
||||
console.log('Validate error', e?.message)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Pause audio when screen loses focus (navigate/reset) and on unmount
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
return () => {
|
||||
pauseAllPlayers();
|
||||
};
|
||||
}, [pauseAllPlayers]),
|
||||
);
|
||||
pauseAllPlayers()
|
||||
}
|
||||
}, [pauseAllPlayers])
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
pauseAllPlayers();
|
||||
};
|
||||
}, [pauseAllPlayers]);
|
||||
pauseAllPlayers()
|
||||
}
|
||||
}, [pauseAllPlayers])
|
||||
|
||||
const handleChooseTrack = () => {
|
||||
if (isWeb) {
|
||||
alert(
|
||||
"Attention !",
|
||||
"Lorsque tu clique sur valider, tu ne pourra plus changer ni le texte ni la mélodie.",
|
||||
'Attention !',
|
||||
'Lorsque tu clique sur valider, tu ne pourra plus changer ni le texte ni la mélodie.',
|
||||
[
|
||||
{
|
||||
text: "Retour",
|
||||
style: "cancel",
|
||||
text: 'Retour',
|
||||
style: 'cancel',
|
||||
},
|
||||
{
|
||||
text: "Valider",
|
||||
style: "confirm",
|
||||
text: 'Valider',
|
||||
style: 'confirm',
|
||||
onPress: () => {
|
||||
validateSelection();
|
||||
validateSelection()
|
||||
},
|
||||
},
|
||||
],
|
||||
{ cancelable: false },
|
||||
);
|
||||
return;
|
||||
{ cancelable: false }
|
||||
)
|
||||
return
|
||||
}
|
||||
setShowValidateModal(true);
|
||||
};
|
||||
setShowValidateModal(true)
|
||||
}
|
||||
|
||||
const handleConfirmRegenerate = async () => {
|
||||
setShowRegenerateModal(false);
|
||||
setShowRegenerateModal(false)
|
||||
try {
|
||||
await pauseAllPlayers();
|
||||
} catch { }
|
||||
navigate(Routes.Lyrics);
|
||||
};
|
||||
await pauseAllPlayers()
|
||||
} catch {}
|
||||
navigate(Routes.Lyrics)
|
||||
}
|
||||
|
||||
const handleRegeneratePress = () => {
|
||||
if (isWeb) {
|
||||
@@ -201,19 +199,19 @@ const SongReady = () => {
|
||||
fontSize: 16,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.InterRegular,
|
||||
textAlign: "center",
|
||||
};
|
||||
textAlign: 'center',
|
||||
}
|
||||
const amountTextStyle = {
|
||||
...descriptionTextStyle,
|
||||
fontFamily: FONT_FAMILY.InterSemiBold,
|
||||
};
|
||||
}
|
||||
|
||||
alert(
|
||||
"Re-générer le morceau",
|
||||
'Re-générer le morceau',
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
alignItems: "center",
|
||||
width: '100%',
|
||||
alignItems: 'center',
|
||||
gap: 12,
|
||||
}}
|
||||
>
|
||||
@@ -222,10 +220,10 @@ const SongReady = () => {
|
||||
</Text>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
flexWrap: "wrap",
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
flexWrap: 'wrap',
|
||||
gap: 6,
|
||||
}}
|
||||
>
|
||||
@@ -242,30 +240,30 @@ const SongReady = () => {
|
||||
</View>,
|
||||
[
|
||||
{
|
||||
text: "Annuler",
|
||||
style: "cancel",
|
||||
text: 'Annuler',
|
||||
style: 'cancel',
|
||||
},
|
||||
{
|
||||
text: "Oui",
|
||||
style: "confirm",
|
||||
text: 'Oui',
|
||||
style: 'confirm',
|
||||
onPress: () => handleConfirmRegenerate(),
|
||||
},
|
||||
],
|
||||
{ cancelable: true },
|
||||
);
|
||||
return;
|
||||
{ cancelable: true }
|
||||
)
|
||||
return
|
||||
}
|
||||
setShowRegenerateModal(true);
|
||||
};
|
||||
setShowRegenerateModal(true)
|
||||
}
|
||||
|
||||
return (
|
||||
<Page headerType="NONE" backgroundImg={background.studioBG2} maxWidth={800}>
|
||||
<MusicLandHeader
|
||||
onPressBack={async () => {
|
||||
try {
|
||||
await pauseAllPlayers();
|
||||
} catch { }
|
||||
navigate(Routes.Home);
|
||||
await pauseAllPlayers()
|
||||
} catch {}
|
||||
navigate(Routes.Home)
|
||||
}}
|
||||
progress={63}
|
||||
/>
|
||||
@@ -280,7 +278,7 @@ const SongReady = () => {
|
||||
<FlatList
|
||||
style={{ flex: 1 }}
|
||||
data={musicUrls}
|
||||
keyExtractor={(item, idx) => `${item || "song"}-${idx}`}
|
||||
keyExtractor={(item, idx) => `${item || 'song'}-${idx}`}
|
||||
renderItem={({ item, index }) => (
|
||||
<SongOptionCard
|
||||
index={index}
|
||||
@@ -301,8 +299,8 @@ const SongReady = () => {
|
||||
<View
|
||||
style={{
|
||||
paddingBottom: gutters * 2,
|
||||
width: "80%",
|
||||
alignSelf: "center",
|
||||
width: '80%',
|
||||
alignSelf: 'center',
|
||||
gap: 12,
|
||||
}}
|
||||
>
|
||||
@@ -311,10 +309,7 @@ const SongReady = () => {
|
||||
onPress={handleChooseTrack}
|
||||
disabled={!musicUrls?.length}
|
||||
/>
|
||||
<BorderGradientButton
|
||||
title="Re-générer le morceau"
|
||||
onPress={handleRegeneratePress}
|
||||
/>
|
||||
<BorderGradientButton title="Re-générer le morceau" onPress={handleRegeneratePress} />
|
||||
</View>
|
||||
{!isWeb && (
|
||||
<>
|
||||
@@ -331,8 +326,8 @@ const SongReady = () => {
|
||||
</>
|
||||
)}
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
const SongOptionCard = ({
|
||||
index,
|
||||
@@ -345,11 +340,9 @@ const SongOptionCard = ({
|
||||
selectedProject,
|
||||
}) => {
|
||||
const trackTitle =
|
||||
(Array.isArray(selectedProject?.musicTitles)
|
||||
? selectedProject.musicTitles?.[index]
|
||||
: null) ||
|
||||
(Array.isArray(selectedProject?.musicTitles) ? selectedProject.musicTitles?.[index] : null) ||
|
||||
selectedProject?.title ||
|
||||
`Morceau ${index + 1}`;
|
||||
`Morceau ${index + 1}`
|
||||
|
||||
const player = useSharedAudioPlayer(url ? { uri: url } : undefined, {
|
||||
id: url ? `songready-${url}` : undefined,
|
||||
@@ -357,130 +350,124 @@ const SongOptionCard = ({
|
||||
artwork: selectedProject?.coverUrl || null,
|
||||
coverUrl: selectedProject?.coverUrl || null,
|
||||
metadata: { index, projectId },
|
||||
});
|
||||
})
|
||||
|
||||
const [progressInfo, setProgressInfo] = useState({ pos: 0, dur: 0 });
|
||||
const [isPlaying, setIsPlaying] = useState(false);
|
||||
const shouldResumeAfterSeekRef = useRef(false);
|
||||
const [progressInfo, setProgressInfo] = useState({ pos: 0, dur: 0 })
|
||||
const [isPlaying, setIsPlaying] = useState(false)
|
||||
const shouldResumeAfterSeekRef = useRef(false)
|
||||
|
||||
useEffect(() => {
|
||||
registerPlayer(index, player);
|
||||
return () => registerPlayer(index, null);
|
||||
}, [index, player, registerPlayer]);
|
||||
registerPlayer(index, player)
|
||||
return () => registerPlayer(index, null)
|
||||
}, [index, player, registerPlayer])
|
||||
|
||||
useEffect(() => {
|
||||
if (!player || !url) return;
|
||||
if (!player || !url) return
|
||||
const preload = async () => {
|
||||
try {
|
||||
await player.load?.({ startPositionMs: 0 });
|
||||
await player.load?.({ startPositionMs: 0 })
|
||||
} catch (error) {
|
||||
console.log("SongReady preload error", error?.message);
|
||||
console.log('SongReady preload error', error?.message)
|
||||
}
|
||||
};
|
||||
preload();
|
||||
}, [player, url]);
|
||||
}
|
||||
preload()
|
||||
}, [player, url])
|
||||
|
||||
useEffect(() => {
|
||||
if (!player) {
|
||||
setIsPlaying(false);
|
||||
setProgressInfo({ pos: 0, dur: 0 });
|
||||
return undefined;
|
||||
setIsPlaying(false)
|
||||
setProgressInfo({ pos: 0, dur: 0 })
|
||||
return undefined
|
||||
}
|
||||
const id = setInterval(() => {
|
||||
const durationMs = Math.max(
|
||||
0,
|
||||
Math.round((Number(player.duration) || 0) * 1000),
|
||||
);
|
||||
const positionMs = Math.max(
|
||||
0,
|
||||
Math.round((Number(player.currentTime) || 0) * 1000),
|
||||
);
|
||||
const durationMs = Math.max(0, Math.round((Number(player.duration) || 0) * 1000))
|
||||
const positionMs = Math.max(0, Math.round((Number(player.currentTime) || 0) * 1000))
|
||||
setProgressInfo((prev) => {
|
||||
if (
|
||||
Math.abs((prev?.dur || 0) - durationMs) < 5 &&
|
||||
Math.abs((prev?.pos || 0) - positionMs) < 5
|
||||
) {
|
||||
return prev;
|
||||
return prev
|
||||
}
|
||||
return { pos: positionMs, dur: durationMs };
|
||||
});
|
||||
return { pos: positionMs, dur: durationMs }
|
||||
})
|
||||
setIsPlaying((prev) => {
|
||||
const next = !!player.playing;
|
||||
const next = !!player.playing
|
||||
if (prev !== next) {
|
||||
return next;
|
||||
return next
|
||||
}
|
||||
return prev;
|
||||
});
|
||||
}, 300);
|
||||
return () => clearInterval(id);
|
||||
}, [player]);
|
||||
return prev
|
||||
})
|
||||
}, 300)
|
||||
return () => clearInterval(id)
|
||||
}, [player])
|
||||
|
||||
const hasCapturedSeekStateRef = useRef(false);
|
||||
const hasCapturedSeekStateRef = useRef(false)
|
||||
|
||||
const resumePlaybackIfNeeded = useCallback(async () => {
|
||||
if (!player) return;
|
||||
const shouldResume = shouldResumeAfterSeekRef.current;
|
||||
shouldResumeAfterSeekRef.current = false;
|
||||
hasCapturedSeekStateRef.current = false; // Reset capture flag
|
||||
if (!player) return
|
||||
const shouldResume = shouldResumeAfterSeekRef.current
|
||||
shouldResumeAfterSeekRef.current = false
|
||||
hasCapturedSeekStateRef.current = false // Reset capture flag
|
||||
|
||||
if (!shouldResume) return;
|
||||
if (!shouldResume) return
|
||||
|
||||
try {
|
||||
if (player.resume) {
|
||||
await player.resume?.();
|
||||
await player.resume?.()
|
||||
} else {
|
||||
await player.play?.();
|
||||
await player.play?.()
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("SongReady resume after seek", error?.message);
|
||||
console.log('SongReady resume after seek', error?.message)
|
||||
}
|
||||
}, [player]);
|
||||
}, [player])
|
||||
|
||||
const handleSeek = useCallback(
|
||||
async (targetMs) => {
|
||||
if (!player || !progressInfo?.dur) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
const dur = progressInfo.dur || 0;
|
||||
const pos = Math.max(0, Math.min(dur, Math.floor(targetMs)));
|
||||
const dur = progressInfo.dur || 0
|
||||
const pos = Math.max(0, Math.min(dur, Math.floor(targetMs)))
|
||||
|
||||
// Mise à jour visuelle immédiate
|
||||
setProgressInfo((prev) => ({ ...prev, pos }));
|
||||
setProgressInfo((prev) => ({ ...prev, pos }))
|
||||
|
||||
try {
|
||||
await player.seekTo?.(Math.floor(pos / 1000));
|
||||
await player.seekTo?.(Math.floor(pos / 1000))
|
||||
} catch (error) {
|
||||
console.log("SongReady seek error", error?.message);
|
||||
console.log('SongReady seek error', error?.message)
|
||||
}
|
||||
},
|
||||
[player, progressInfo?.dur],
|
||||
);
|
||||
[player, progressInfo?.dur]
|
||||
)
|
||||
|
||||
const handleSeekStart = useCallback(async () => {
|
||||
onSelect(index);
|
||||
if (!player) return;
|
||||
onSelect(index)
|
||||
if (!player) return
|
||||
|
||||
const isCurrentlyPlaying = !!player.playing || isPlaying;
|
||||
const isCurrentlyPlaying = !!player.playing || isPlaying
|
||||
|
||||
// Only capture state if we haven't already for this drag interaction
|
||||
if (!hasCapturedSeekStateRef.current) {
|
||||
hasCapturedSeekStateRef.current = true;
|
||||
shouldResumeAfterSeekRef.current = isCurrentlyPlaying;
|
||||
hasCapturedSeekStateRef.current = true
|
||||
shouldResumeAfterSeekRef.current = isCurrentlyPlaying
|
||||
}
|
||||
|
||||
try {
|
||||
if (isCurrentlyPlaying) {
|
||||
await player.pause?.();
|
||||
await player.pause?.()
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("SongReady pause on seek start", error?.message);
|
||||
console.log('SongReady pause on seek start', error?.message)
|
||||
}
|
||||
}, [index, isPlaying, onSelect, player]);
|
||||
}, [index, isPlaying, onSelect, player])
|
||||
|
||||
const handleToggle = () => {
|
||||
onSelect(index);
|
||||
onTogglePlayback?.(index, isPlaying);
|
||||
};
|
||||
onSelect(index)
|
||||
onTogglePlayback?.(index, isPlaying)
|
||||
}
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
@@ -490,7 +477,7 @@ const SongOptionCard = ({
|
||||
borderRadius: 20,
|
||||
borderWidth: isSelected ? 2 : 1,
|
||||
borderColor: isSelected ? Palette.primary : Palette.ultraLightWhite,
|
||||
overflow: "hidden",
|
||||
overflow: 'hidden',
|
||||
},
|
||||
pressed && { opacity: 0.96 },
|
||||
]}
|
||||
@@ -500,15 +487,15 @@ const SongOptionCard = ({
|
||||
tint="dark"
|
||||
style={{
|
||||
borderRadius: 18,
|
||||
overflow: "hidden",
|
||||
overflow: 'hidden',
|
||||
padding: 12,
|
||||
backgroundColor: "#FFFFFF0A",
|
||||
backgroundColor: '#FFFFFF0A',
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: 12,
|
||||
}}
|
||||
>
|
||||
@@ -524,7 +511,7 @@ const SongOptionCard = ({
|
||||
<View style={{ flex: 1 }}>
|
||||
<Text
|
||||
style={{
|
||||
color: "white",
|
||||
color: 'white',
|
||||
marginBottom: responsiveHeight(1),
|
||||
}}
|
||||
>{`Morceau ${index + 1}`}</Text>
|
||||
@@ -551,9 +538,9 @@ const SongOptionCard = ({
|
||||
height: 18,
|
||||
borderRadius: 9,
|
||||
borderWidth: 2,
|
||||
borderColor: isSelected ? Palette.primary : "white",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
borderColor: isSelected ? Palette.primary : 'white',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
{isSelected && (
|
||||
@@ -571,44 +558,34 @@ const SongOptionCard = ({
|
||||
</View>
|
||||
</BlurView>
|
||||
</Pressable>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
const RegenerateModal = ({ visible, onClose, onConfirm }) => {
|
||||
const { width, height } = useWindowDimensions();
|
||||
const isCompactWidth = width < 420;
|
||||
const horizontalPadding = Math.max(isCompactWidth ? 16 : gutters, 12);
|
||||
const verticalPadding = Math.max(
|
||||
isCompactWidth ? gutters : gutters * 1.5,
|
||||
12,
|
||||
);
|
||||
const availableWidth = Math.max(width - horizontalPadding * 2, 0);
|
||||
const { width, height } = useWindowDimensions()
|
||||
const isCompactWidth = width < 420
|
||||
const horizontalPadding = Math.max(isCompactWidth ? 16 : gutters, 12)
|
||||
const verticalPadding = Math.max(isCompactWidth ? gutters : gutters * 1.5, 12)
|
||||
const availableWidth = Math.max(width - horizontalPadding * 2, 0)
|
||||
const contentWidth =
|
||||
availableWidth > 0
|
||||
? Math.min(REGENERATE_MODAL_MAX_WIDTH, availableWidth)
|
||||
: undefined;
|
||||
const contentGap = isCompactWidth ? 18 : 24;
|
||||
const buttonGap = isCompactWidth ? 12 : 15;
|
||||
availableWidth > 0 ? Math.min(REGENERATE_MODAL_MAX_WIDTH, availableWidth) : undefined
|
||||
const contentGap = isCompactWidth ? 18 : 24
|
||||
const buttonGap = isCompactWidth ? 12 : 15
|
||||
|
||||
return (
|
||||
<Modal
|
||||
animationType="fade"
|
||||
transparent
|
||||
visible={visible}
|
||||
onRequestClose={onClose}
|
||||
>
|
||||
<Modal animationType="fade" transparent visible={visible} onRequestClose={onClose}>
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
backgroundColor: "rgba(0, 0, 0, 0.6)",
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.6)',
|
||||
}}
|
||||
>
|
||||
<ScrollView
|
||||
bounces={false}
|
||||
contentContainerStyle={{
|
||||
flexGrow: 1,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
paddingHorizontal: horizontalPadding,
|
||||
paddingVertical: verticalPadding,
|
||||
minHeight: height,
|
||||
@@ -618,20 +595,20 @@ const RegenerateModal = ({ visible, onClose, onConfirm }) => {
|
||||
>
|
||||
<CreateLyricsHeader
|
||||
containerStyle={{
|
||||
width: contentWidth ?? "100%",
|
||||
width: contentWidth ?? '100%',
|
||||
maxWidth: REGENERATE_MODAL_MAX_WIDTH,
|
||||
alignSelf: "center",
|
||||
alignSelf: 'center',
|
||||
paddingVertical: contentGap,
|
||||
paddingHorizontal: contentGap,
|
||||
gap: contentGap,
|
||||
flexShrink: 1,
|
||||
}}
|
||||
>
|
||||
<View style={{ gap: contentGap, alignItems: "center" }}>
|
||||
<View style={{ gap: contentGap, alignItems: 'center' }}>
|
||||
<View
|
||||
style={{
|
||||
gap: isCompactWidth ? 10 : 12,
|
||||
alignItems: "center",
|
||||
alignItems: 'center',
|
||||
paddingHorizontal: isCompactWidth ? 4 : 12,
|
||||
}}
|
||||
>
|
||||
@@ -640,7 +617,7 @@ const RegenerateModal = ({ visible, onClose, onConfirm }) => {
|
||||
fontSize: 22,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.InterSemiBold,
|
||||
textAlign: "center",
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
Re-générer le morceau ?
|
||||
@@ -650,17 +627,17 @@ const RegenerateModal = ({ visible, onClose, onConfirm }) => {
|
||||
fontSize: 16,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.InterRegular,
|
||||
textAlign: "center",
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
{`Tu vas pouvoir modifier tes choix pour re-générer ${SONG_OPTIONS_PER_GENERATION} nouveaux morceaux.`}
|
||||
</Text>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
flexWrap: "wrap",
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
flexWrap: 'wrap',
|
||||
gap: 6,
|
||||
}}
|
||||
>
|
||||
@@ -669,7 +646,7 @@ const RegenerateModal = ({ visible, onClose, onConfirm }) => {
|
||||
fontSize: 16,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.InterRegular,
|
||||
textAlign: "center",
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
Cette action coûte
|
||||
@@ -681,7 +658,7 @@ const RegenerateModal = ({ visible, onClose, onConfirm }) => {
|
||||
fontSize: 16,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.InterSemiBold,
|
||||
textAlign: "center",
|
||||
textAlign: 'center',
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
@@ -690,7 +667,7 @@ const RegenerateModal = ({ visible, onClose, onConfirm }) => {
|
||||
fontSize: 16,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.InterRegular,
|
||||
textAlign: "center",
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
Les crédits seront utilisés lors de l'étape de génération.
|
||||
@@ -698,15 +675,12 @@ const RegenerateModal = ({ visible, onClose, onConfirm }) => {
|
||||
</View>
|
||||
<View
|
||||
style={{
|
||||
width: isCompactWidth ? "100%" : "85%",
|
||||
alignSelf: "center",
|
||||
width: isCompactWidth ? '100%' : '85%',
|
||||
alignSelf: 'center',
|
||||
gap: buttonGap,
|
||||
}}
|
||||
>
|
||||
<GradientButton
|
||||
title="Oui, modifier mes choix"
|
||||
onPress={onConfirm}
|
||||
/>
|
||||
<GradientButton title="Oui, modifier mes choix" onPress={onConfirm} />
|
||||
<BorderGradientButton title="Retour" onPress={onClose} />
|
||||
</View>
|
||||
</View>
|
||||
@@ -714,7 +688,7 @@ const RegenerateModal = ({ visible, onClose, onConfirm }) => {
|
||||
</ScrollView>
|
||||
</View>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default SongReady;
|
||||
export default SongReady
|
||||
|
||||
Reference in New Issue
Block a user