diff --git a/App.js b/App.js index 2626af6..d661ab6 100644 --- a/App.js +++ b/App.js @@ -56,6 +56,26 @@ const App = () => { const [isInitializing, setInitializing] = useState(true); + const routeNameRef = React.useRef(null); + + const syncActiveRoute = useCallback(() => { + const currentRoute = navigationRef.current?.getCurrentRoute(); + const currentName = currentRoute?.name ?? null; + + if (routeNameRef.current !== currentName) { + routeNameRef.current = currentName; + setGlobal({ activeRouteName: currentName }); + } + }, [setGlobal]); + + const handleNavigationReady = useCallback(() => { + syncActiveRoute(); + }, [syncActiveRoute]); + + const handleNavigationStateChange = useCallback(() => { + syncActiveRoute(); + }, [syncActiveRoute]); + const [loaded] = useFonts({ NewYorkSemibold: require("./src/assets/fonts/NewYork-Semibold.ttf"), OpenSansRegular: require("./src/assets/fonts/OpenSans-Regular.ttf"), @@ -164,6 +184,8 @@ const App = () => { }, }} ref={navigationRef} + onReady={handleNavigationReady} + onStateChange={handleNavigationStateChange} documentTitle={{ formatter: (options) => options?.title diff --git a/functions/config/suno.js b/functions/config/suno.js index fb9c938..5cb402c 100644 --- a/functions/config/suno.js +++ b/functions/config/suno.js @@ -3,6 +3,6 @@ exports.SUNO_API_PATH = "/api/v1/generate"; exports.SUNO_STATUS_PATH = "/api/v1/generate/record-info"; exports.SUNO_TIMESTAMPED_LYRICS_PATH = "/api/v1/generate/get-timestamped-lyrics"; -exports.SUNO_MODEL = "V4_5"; +exports.SUNO_MODEL = "V5"; exports.SUNO_CALLBACK_URL = "https://us-central1-musicland-d33f9.cloudfunctions.net/music-sunoCallback"; diff --git a/src/config/initialGlobalState.js b/src/config/initialGlobalState.js index 68b9b25..0290d28 100644 --- a/src/config/initialGlobalState.js +++ b/src/config/initialGlobalState.js @@ -5,6 +5,8 @@ export default { currentUserData: null, currentUserRoles: [], + activeRouteName: null, + webBackgroundImg: null, webLayoutMode: "default", diff --git a/src/providers/PlayerProvider.js b/src/providers/PlayerProvider.js index 28b1c46..e7bd1ce 100644 --- a/src/providers/PlayerProvider.js +++ b/src/providers/PlayerProvider.js @@ -8,8 +8,46 @@ import React, { } from "react"; import { Platform } from "react-native"; import { useAudioPlayer, useAudioPlayerStatus } from "expo-audio"; +import { useGlobal } from "reactn"; import GlobalAudioPlayer from "../components/player/GlobalAudioPlayer"; +import { Routes } from "../navigation/Routes"; + +const HIDDEN_ROUTE_NAMES = new Set([ + Routes.Writing, + Routes.WritingLyrics, + Routes.CreateLyricsWithAi, + Routes.Lyrics, + Routes.FinishedWriting, + Routes.Studio, + Routes.Compose, + Routes.ComposeSong, + Routes.CustomizeVoice, + Routes.SongReady, + Routes.Regenerate, + Routes.PouchReady, + Routes.PhotoCover, + Routes.ValidateCover, + Routes.FinishCompose, + Routes.GeneratingSong, + Routes.ProductionOnboarding, + Routes.Production, + Routes.DownloadSongs, + Routes.DownloadPrices, + Routes.SongDownloaded, + Routes.StreamSong, + Routes.SongRelease, + Routes.PlaybackExample, + Routes.PlaybackOnboarding, + Routes.Playback, + Routes.RecordPlayback, + Routes.RecordedPlayback, + Routes.ChooseDecor, + Routes.CreatingDecor, + Routes.VideoFinalize, + Routes.FlowSelection, + Routes.ChooseCoverType, +]); const noopAsync = async () => {}; const noop = () => {}; @@ -23,6 +61,7 @@ const DEFAULT_CONTEXT = { positionMs: 0, durationMs: 0, error: null, + isPlayerVisible: true, play: noopAsync, togglePlay: noopAsync, pause: noopAsync, @@ -146,6 +185,7 @@ const normalizeTrack = (trackInput = {}, options = {}) => { }; const PlayerProvider = ({ children }) => { + const [activeRouteName] = useGlobal("activeRouteName"); const [currentTrack, setCurrentTrack] = useState(null); const [queue, setQueue] = useState([]); const [playback, setPlayback] = useState({ @@ -167,6 +207,9 @@ const PlayerProvider = ({ children }) => { const player = useAudioPlayer(source || null, 200); const status = useAudioPlayerStatus(player); + const shouldHideOnRoute = activeRouteName + ? HIDDEN_ROUTE_NAMES.has(activeRouteName) + : false; useEffect(() => { if (!status) return; @@ -392,6 +435,7 @@ const PlayerProvider = ({ children }) => { positionMs: playback.positionMs, durationMs: playback.durationMs, error, + isPlayerVisible: !shouldHideOnRoute, play, togglePlay, pause, @@ -415,6 +459,7 @@ const PlayerProvider = ({ children }) => { resume, seekBy, seekTo, + shouldHideOnRoute, stop, ] ); @@ -422,7 +467,7 @@ const PlayerProvider = ({ children }) => { return ( {children} - + {!shouldHideOnRoute && } ); }; diff --git a/src/providers/UserDataProvider.js b/src/providers/UserDataProvider.js index 1fac189..3f8d135 100644 --- a/src/providers/UserDataProvider.js +++ b/src/providers/UserDataProvider.js @@ -19,7 +19,7 @@ export default ({ children }) => { const { setIsLoading, setTooltip } = useMinuit(); const [pendingUserData, setPendingUserData] = useState({ - creationTimestamp: new Date(), + createdAt: new Date(), }); const [currentUID] = useGlobal("currentUID"); @@ -292,8 +292,8 @@ export default ({ children }) => { userID: dynamicUID, }; - if (!cleanData?.creationTimestamp) { - cleanData.creationTimestamp = new Date(); + if (!cleanData?.createdAt) { + cleanData.createdAt = firebase.firestore.FieldValue.serverTimestamp(); } await usersRef.doc(dynamicUID).set(cleanData, { merge: true }); diff --git a/src/screens/Playback/Playback.js b/src/screens/Playback/Playback.js index 0b7854a..2d59880 100644 --- a/src/screens/Playback/Playback.js +++ b/src/screens/Playback/Playback.js @@ -2,21 +2,19 @@ import React, { useCallback, useState } from "react"; import { Image, StyleSheet, View } from "react-native"; import { ai, background } from "../../assets"; import BorderGradientButton from "../../components/BorderGradientButton"; +import FullscreenIntroVideo from "../../components/FullscreenIntroVideo"; import GradientButton from "../../components/GradientButton"; import MusicLandHeader from "../../components/MusicLandHeader"; import Page from "../../layouts/Page"; import { Routes } from "../../navigation"; import { goBack, navigate } from "../../navigation/NavigationService"; import { gutters } from "../../styles"; -import FullscreenIntroVideo from "../../components/FullscreenIntroVideo"; const Playback = ({ route }) => { const { project } = route.params; const [showIntro, setShowIntro] = useState(true); const onPressRecord = useCallback(() => { - // Les timestamps sont désormais générés automatiquement côté serveur - // lors de la mise à jour du document (si songUrl existe). navigate(Routes.RecordPlayback, { project }); }, [project]); diff --git a/src/screens/Playback/RecordPlayback.js b/src/screens/Playback/RecordPlayback.js index 4ab2b22..98d7716 100644 --- a/src/screens/Playback/RecordPlayback.js +++ b/src/screens/Playback/RecordPlayback.js @@ -1,5 +1,4 @@ import { useFocusEffect } from "@react-navigation/native"; -import useSharedAudioPlayer from "../../hooks/useSharedAudioPlayer"; import { CameraView, useCameraPermissions } from "expo-camera"; import React, { useCallback, @@ -11,11 +10,12 @@ import React, { import { Text, View } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import Svg, { Circle } from "react-native-svg"; +import alert from "../../components/Alert"; import GradientButton from "../../components/GradientButton"; import KaraokeLyrics from "../../components/KaraokeLyrics"; import MusicLandHeader from "../../components/MusicLandHeader"; -import alert from "../../components/Alert"; import { increment, projectsRef } from "../../config/firebase"; +import useSharedAudioPlayer from "../../hooks/useSharedAudioPlayer"; import { Routes } from "../../navigation"; import { goBack, navigate } from "../../navigation/NavigationService"; import { gutters, Palette } from "../../styles"; @@ -27,6 +27,7 @@ const TIME_BEFORE_INCREMENT_MS = 20000; // 20s const RecordPlayback = ({ route }) => { const { top, bottom } = useSafeAreaInsets(); const { project } = route.params || {}; + console.log("project is : ", JSON.stringify(project, null, 2)); const projectId = project?.id; const songIndex = project?.songIndex; // Permissions @@ -55,9 +56,14 @@ const RecordPlayback = ({ route }) => { // Musique const songUrl = project?.songUrl || null; const player = useSharedAudioPlayer(songUrl ? { uri: songUrl } : undefined, { - id: projectId ? `record-${projectId}` : songUrl ? `record-${songUrl}` : undefined, + id: projectId + ? `record-${projectId}` + : songUrl + ? `record-${songUrl}` + : undefined, title: typeof project?.title === "string" ? project.title : "Sans titre", - artist: typeof project?.userName === "string" ? project.userName : "MusicLand", + artist: + typeof project?.userName === "string" ? project.userName : "MusicLand", artwork: project?.coverUrl || null, coverUrl: project?.coverUrl || null, metadata: { projectId, screen: "RecordPlayback" }, diff --git a/src/screens/Playback/RecordPlayback.web.js b/src/screens/Playback/RecordPlayback.web.js index a28f0ca..08c8bc7 100644 --- a/src/screens/Playback/RecordPlayback.web.js +++ b/src/screens/Playback/RecordPlayback.web.js @@ -1,5 +1,4 @@ import { useFocusEffect } from "@react-navigation/native"; -import useSharedAudioPlayer from "../../hooks/useSharedAudioPlayer"; import { useCameraPermissions } from "expo-camera"; import React, { useCallback, @@ -11,12 +10,13 @@ import React, { import { Text, View } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import Svg, { Circle } from "react-native-svg"; +import alert from "../../components/Alert"; import GradientButton from "../../components/GradientButton"; import KaraokeLyrics from "../../components/KaraokeLyrics"; import MusicLandHeader from "../../components/MusicLandHeader"; -import alert from "../../components/Alert"; import { increment, projectsRef } from "../../config/firebase"; import { isWeb } from "../../hooks/useLayoutType"; +import useSharedAudioPlayer from "../../hooks/useSharedAudioPlayer"; import Page from "../../layouts/Page"; import { Routes } from "../../navigation"; import { goBack, navigate } from "../../navigation/NavigationService"; @@ -62,12 +62,13 @@ const RecordPlayback = ({ route }) => { // Player const songUrl = project?.songUrl || null; const player = useSharedAudioPlayer(songUrl ? { uri: songUrl } : undefined, { - id: projectId ? `record-${projectId}` : songUrl ? `record-${songUrl}` : undefined, + id: project?.id, title: typeof project?.title === "string" ? project.title : "Sans titre", - artist: typeof project?.userName === "string" ? project.userName : "MusicLand", + artist: + typeof project?.userName === "string" ? project.userName : "MusicLand", artwork: project?.coverUrl || null, coverUrl: project?.coverUrl || null, - metadata: { projectId, screen: "RecordPlayback" }, + metadata: { projectId: project?.id, screen: "RecordPlayback" }, }); // MediaStream / Recorder (web only)