block global audio from screens

This commit is contained in:
2025-10-07 14:14:42 +02:00
parent 6734dea010
commit 2198090838
8 changed files with 91 additions and 17 deletions
+22
View File
@@ -56,6 +56,26 @@ const App = () => {
const [isInitializing, setInitializing] = useState(true); 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({ const [loaded] = useFonts({
NewYorkSemibold: require("./src/assets/fonts/NewYork-Semibold.ttf"), NewYorkSemibold: require("./src/assets/fonts/NewYork-Semibold.ttf"),
OpenSansRegular: require("./src/assets/fonts/OpenSans-Regular.ttf"), OpenSansRegular: require("./src/assets/fonts/OpenSans-Regular.ttf"),
@@ -164,6 +184,8 @@ const App = () => {
}, },
}} }}
ref={navigationRef} ref={navigationRef}
onReady={handleNavigationReady}
onStateChange={handleNavigationStateChange}
documentTitle={{ documentTitle={{
formatter: (options) => formatter: (options) =>
options?.title options?.title
+1 -1
View File
@@ -3,6 +3,6 @@ exports.SUNO_API_PATH = "/api/v1/generate";
exports.SUNO_STATUS_PATH = "/api/v1/generate/record-info"; exports.SUNO_STATUS_PATH = "/api/v1/generate/record-info";
exports.SUNO_TIMESTAMPED_LYRICS_PATH = exports.SUNO_TIMESTAMPED_LYRICS_PATH =
"/api/v1/generate/get-timestamped-lyrics"; "/api/v1/generate/get-timestamped-lyrics";
exports.SUNO_MODEL = "V4_5"; exports.SUNO_MODEL = "V5";
exports.SUNO_CALLBACK_URL = exports.SUNO_CALLBACK_URL =
"https://us-central1-musicland-d33f9.cloudfunctions.net/music-sunoCallback"; "https://us-central1-musicland-d33f9.cloudfunctions.net/music-sunoCallback";
+2
View File
@@ -5,6 +5,8 @@ export default {
currentUserData: null, currentUserData: null,
currentUserRoles: [], currentUserRoles: [],
activeRouteName: null,
webBackgroundImg: null, webBackgroundImg: null,
webLayoutMode: "default", webLayoutMode: "default",
+46 -1
View File
@@ -8,8 +8,46 @@ import React, {
} from "react"; } from "react";
import { Platform } from "react-native"; import { Platform } from "react-native";
import { useAudioPlayer, useAudioPlayerStatus } from "expo-audio"; import { useAudioPlayer, useAudioPlayerStatus } from "expo-audio";
import { useGlobal } from "reactn";
import GlobalAudioPlayer from "../components/player/GlobalAudioPlayer"; 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 noopAsync = async () => {};
const noop = () => {}; const noop = () => {};
@@ -23,6 +61,7 @@ const DEFAULT_CONTEXT = {
positionMs: 0, positionMs: 0,
durationMs: 0, durationMs: 0,
error: null, error: null,
isPlayerVisible: true,
play: noopAsync, play: noopAsync,
togglePlay: noopAsync, togglePlay: noopAsync,
pause: noopAsync, pause: noopAsync,
@@ -146,6 +185,7 @@ const normalizeTrack = (trackInput = {}, options = {}) => {
}; };
const PlayerProvider = ({ children }) => { const PlayerProvider = ({ children }) => {
const [activeRouteName] = useGlobal("activeRouteName");
const [currentTrack, setCurrentTrack] = useState(null); const [currentTrack, setCurrentTrack] = useState(null);
const [queue, setQueue] = useState([]); const [queue, setQueue] = useState([]);
const [playback, setPlayback] = useState({ const [playback, setPlayback] = useState({
@@ -167,6 +207,9 @@ const PlayerProvider = ({ children }) => {
const player = useAudioPlayer(source || null, 200); const player = useAudioPlayer(source || null, 200);
const status = useAudioPlayerStatus(player); const status = useAudioPlayerStatus(player);
const shouldHideOnRoute = activeRouteName
? HIDDEN_ROUTE_NAMES.has(activeRouteName)
: false;
useEffect(() => { useEffect(() => {
if (!status) return; if (!status) return;
@@ -392,6 +435,7 @@ const PlayerProvider = ({ children }) => {
positionMs: playback.positionMs, positionMs: playback.positionMs,
durationMs: playback.durationMs, durationMs: playback.durationMs,
error, error,
isPlayerVisible: !shouldHideOnRoute,
play, play,
togglePlay, togglePlay,
pause, pause,
@@ -415,6 +459,7 @@ const PlayerProvider = ({ children }) => {
resume, resume,
seekBy, seekBy,
seekTo, seekTo,
shouldHideOnRoute,
stop, stop,
] ]
); );
@@ -422,7 +467,7 @@ const PlayerProvider = ({ children }) => {
return ( return (
<PlayerContext.Provider value={contextValue}> <PlayerContext.Provider value={contextValue}>
{children} {children}
<GlobalAudioPlayer /> {!shouldHideOnRoute && <GlobalAudioPlayer />}
</PlayerContext.Provider> </PlayerContext.Provider>
); );
}; };
+3 -3
View File
@@ -19,7 +19,7 @@ export default ({ children }) => {
const { setIsLoading, setTooltip } = useMinuit(); const { setIsLoading, setTooltip } = useMinuit();
const [pendingUserData, setPendingUserData] = useState({ const [pendingUserData, setPendingUserData] = useState({
creationTimestamp: new Date(), createdAt: new Date(),
}); });
const [currentUID] = useGlobal("currentUID"); const [currentUID] = useGlobal("currentUID");
@@ -292,8 +292,8 @@ export default ({ children }) => {
userID: dynamicUID, userID: dynamicUID,
}; };
if (!cleanData?.creationTimestamp) { if (!cleanData?.createdAt) {
cleanData.creationTimestamp = new Date(); cleanData.createdAt = firebase.firestore.FieldValue.serverTimestamp();
} }
await usersRef.doc(dynamicUID).set(cleanData, { merge: true }); await usersRef.doc(dynamicUID).set(cleanData, { merge: true });
+1 -3
View File
@@ -2,21 +2,19 @@ import React, { useCallback, useState } from "react";
import { Image, StyleSheet, View } from "react-native"; import { Image, StyleSheet, View } from "react-native";
import { ai, background } from "../../assets"; import { ai, background } from "../../assets";
import BorderGradientButton from "../../components/BorderGradientButton"; import BorderGradientButton from "../../components/BorderGradientButton";
import FullscreenIntroVideo from "../../components/FullscreenIntroVideo";
import GradientButton from "../../components/GradientButton"; import GradientButton from "../../components/GradientButton";
import MusicLandHeader from "../../components/MusicLandHeader"; import MusicLandHeader from "../../components/MusicLandHeader";
import Page from "../../layouts/Page"; import Page from "../../layouts/Page";
import { Routes } from "../../navigation"; import { Routes } from "../../navigation";
import { goBack, navigate } from "../../navigation/NavigationService"; import { goBack, navigate } from "../../navigation/NavigationService";
import { gutters } from "../../styles"; import { gutters } from "../../styles";
import FullscreenIntroVideo from "../../components/FullscreenIntroVideo";
const Playback = ({ route }) => { const Playback = ({ route }) => {
const { project } = route.params; const { project } = route.params;
const [showIntro, setShowIntro] = useState(true); const [showIntro, setShowIntro] = useState(true);
const onPressRecord = useCallback(() => { 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 }); navigate(Routes.RecordPlayback, { project });
}, [project]); }, [project]);
+10 -4
View File
@@ -1,5 +1,4 @@
import { useFocusEffect } from "@react-navigation/native"; import { useFocusEffect } from "@react-navigation/native";
import useSharedAudioPlayer from "../../hooks/useSharedAudioPlayer";
import { CameraView, useCameraPermissions } from "expo-camera"; import { CameraView, useCameraPermissions } from "expo-camera";
import React, { import React, {
useCallback, useCallback,
@@ -11,11 +10,12 @@ import React, {
import { Text, View } from "react-native"; import { Text, View } from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context"; import { useSafeAreaInsets } from "react-native-safe-area-context";
import Svg, { Circle } from "react-native-svg"; import Svg, { Circle } from "react-native-svg";
import alert from "../../components/Alert";
import GradientButton from "../../components/GradientButton"; import GradientButton from "../../components/GradientButton";
import KaraokeLyrics from "../../components/KaraokeLyrics"; import KaraokeLyrics from "../../components/KaraokeLyrics";
import MusicLandHeader from "../../components/MusicLandHeader"; import MusicLandHeader from "../../components/MusicLandHeader";
import alert from "../../components/Alert";
import { increment, projectsRef } from "../../config/firebase"; import { increment, projectsRef } from "../../config/firebase";
import useSharedAudioPlayer from "../../hooks/useSharedAudioPlayer";
import { Routes } from "../../navigation"; import { Routes } from "../../navigation";
import { goBack, navigate } from "../../navigation/NavigationService"; import { goBack, navigate } from "../../navigation/NavigationService";
import { gutters, Palette } from "../../styles"; import { gutters, Palette } from "../../styles";
@@ -27,6 +27,7 @@ const TIME_BEFORE_INCREMENT_MS = 20000; // 20s
const RecordPlayback = ({ route }) => { const RecordPlayback = ({ route }) => {
const { top, bottom } = useSafeAreaInsets(); const { top, bottom } = useSafeAreaInsets();
const { project } = route.params || {}; const { project } = route.params || {};
console.log("project is : ", JSON.stringify(project, null, 2));
const projectId = project?.id; const projectId = project?.id;
const songIndex = project?.songIndex; const songIndex = project?.songIndex;
// Permissions // Permissions
@@ -55,9 +56,14 @@ const RecordPlayback = ({ route }) => {
// Musique // Musique
const songUrl = project?.songUrl || null; const songUrl = project?.songUrl || null;
const player = useSharedAudioPlayer(songUrl ? { uri: songUrl } : undefined, { 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", 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, artwork: project?.coverUrl || null,
coverUrl: project?.coverUrl || null, coverUrl: project?.coverUrl || null,
metadata: { projectId, screen: "RecordPlayback" }, metadata: { projectId, screen: "RecordPlayback" },
+6 -5
View File
@@ -1,5 +1,4 @@
import { useFocusEffect } from "@react-navigation/native"; import { useFocusEffect } from "@react-navigation/native";
import useSharedAudioPlayer from "../../hooks/useSharedAudioPlayer";
import { useCameraPermissions } from "expo-camera"; import { useCameraPermissions } from "expo-camera";
import React, { import React, {
useCallback, useCallback,
@@ -11,12 +10,13 @@ import React, {
import { Text, View } from "react-native"; import { Text, View } from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context"; import { useSafeAreaInsets } from "react-native-safe-area-context";
import Svg, { Circle } from "react-native-svg"; import Svg, { Circle } from "react-native-svg";
import alert from "../../components/Alert";
import GradientButton from "../../components/GradientButton"; import GradientButton from "../../components/GradientButton";
import KaraokeLyrics from "../../components/KaraokeLyrics"; import KaraokeLyrics from "../../components/KaraokeLyrics";
import MusicLandHeader from "../../components/MusicLandHeader"; import MusicLandHeader from "../../components/MusicLandHeader";
import alert from "../../components/Alert";
import { increment, projectsRef } from "../../config/firebase"; import { increment, projectsRef } from "../../config/firebase";
import { isWeb } from "../../hooks/useLayoutType"; import { isWeb } from "../../hooks/useLayoutType";
import useSharedAudioPlayer from "../../hooks/useSharedAudioPlayer";
import Page from "../../layouts/Page"; import Page from "../../layouts/Page";
import { Routes } from "../../navigation"; import { Routes } from "../../navigation";
import { goBack, navigate } from "../../navigation/NavigationService"; import { goBack, navigate } from "../../navigation/NavigationService";
@@ -62,12 +62,13 @@ const RecordPlayback = ({ route }) => {
// Player // Player
const songUrl = project?.songUrl || null; const songUrl = project?.songUrl || null;
const player = useSharedAudioPlayer(songUrl ? { uri: songUrl } : undefined, { 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", 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, artwork: project?.coverUrl || null,
coverUrl: project?.coverUrl || null, coverUrl: project?.coverUrl || null,
metadata: { projectId, screen: "RecordPlayback" }, metadata: { projectId: project?.id, screen: "RecordPlayback" },
}); });
// MediaStream / Recorder (web only) // MediaStream / Recorder (web only)