diff --git a/src/components/ProfilePicture.js b/src/components/ProfilePicture.js
index 583315a..300e1f4 100644
--- a/src/components/ProfilePicture.js
+++ b/src/components/ProfilePicture.js
@@ -56,7 +56,7 @@ const styles = StyleSheet.create({
backgroundColor: "transparent",
},
fallback: {
- backgroundColor: Palette.glass,
+ backgroundColor: "#7373736e",
borderColor: Palette.ultraLightWhite,
borderWidth: 2,
},
diff --git a/src/components/modal/ProfileSettingsModal.js b/src/components/modal/ProfileSettingsModal.js
index 763c81b..7416b08 100644
--- a/src/components/modal/ProfileSettingsModal.js
+++ b/src/components/modal/ProfileSettingsModal.js
@@ -1,12 +1,12 @@
-import { View, Text } from "react-native";
import React from "react";
-import AppActionSheet from "../AppActionSheet";
+import { Text, View } from "react-native";
+import { SheetManager } from "react-native-actions-sheet";
+import { Routes } from "../../navigation";
+import { navigate } from "../../navigation/NavigationService";
import { Palette } from "../../styles";
import { FONT_FAMILY } from "../../styles/Fonts";
+import AppActionSheet from "../AppActionSheet";
import BlurItemButton from "../BlurItemButton";
-import { SheetManager } from "react-native-actions-sheet";
-import { navigate } from "../../navigation/NavigationService";
-import { Routes } from "../../navigation";
const ProfileSettingsModal = () => {
const onClose = async () => {
@@ -27,13 +27,13 @@ const ProfileSettingsModal = () => {
Paramètres
- {
onClose();
navigate(Routes.EditProfile);
}}
- />
+ /> */}
{
diff --git a/src/providers/PlayerProvider.js b/src/providers/PlayerProvider.js
index 401f568..b12482d 100644
--- a/src/providers/PlayerProvider.js
+++ b/src/providers/PlayerProvider.js
@@ -79,6 +79,9 @@ const DEFAULT_CONTEXT = {
seekBy: noopAsync,
stop: noopAsync,
setQueue: noop,
+ isLooping: false,
+ setLooping: noop,
+ toggleLooping: noop,
};
export const PlayerContext = createContext(DEFAULT_CONTEXT);
@@ -215,6 +218,7 @@ const PlayerProvider = ({ children }) => {
durationMs: 0,
});
const [error, setError] = useState(null);
+ const [isLooping, setIsLooping] = useState(false);
const autoPlayRef = useRef(false);
const pendingSeekValueRef = useRef(null);
@@ -295,6 +299,13 @@ const PlayerProvider = ({ children }) => {
run();
}, [player, currentTrack?.id]);
+ useEffect(() => {
+ if (!player) return;
+ try {
+ player.loop = !!isLooping;
+ } catch (_err) {}
+ }, [player, isLooping]);
+
const play = useCallback(
async (trackInput, options = {}) => {
const normalized = normalizeTrack(trackInput, options);
@@ -455,6 +466,14 @@ const PlayerProvider = ({ children }) => {
}
}, [player]);
+ const setLooping = useCallback((value) => {
+ setIsLooping(!!value);
+ }, []);
+
+ const toggleLooping = useCallback(() => {
+ setIsLooping((prev) => !prev);
+ }, []);
+
const contextValue = useMemo(
() => ({
currentTrack,
@@ -474,6 +493,9 @@ const PlayerProvider = ({ children }) => {
seekBy,
stop,
setQueue,
+ isLooping,
+ setLooping,
+ toggleLooping,
}),
[
currentTrack,
@@ -491,6 +513,9 @@ const PlayerProvider = ({ children }) => {
seekTo,
shouldHideOnRoute,
stop,
+ isLooping,
+ setLooping,
+ toggleLooping,
]
);
diff --git a/src/screens/LandingPage.js b/src/screens/LandingPage.js
index ad89378..d22abba 100644
--- a/src/screens/LandingPage.js
+++ b/src/screens/LandingPage.js
@@ -35,7 +35,7 @@ export default function LandingPage() {
}, []);
const handleLaunchAdventure = useCallback(() => {
- navigation.navigate(Routes.Register);
+ navigation.navigate(Routes.Login);
}, [navigation]);
const { data: video } = useDataFromRef({
@@ -103,9 +103,7 @@ export default function LandingPage() {
title="Présentation de Musicland"
onPress={() =>
setVideoUrl(
- Platform.OS === "web"
- ? video?.landingWeb
- : video?.landing,
+ Platform.OS === "web" ? video?.landingWeb : video?.landing
)
}
containerStyle={styles.actionButton}
diff --git a/src/screens/Library/MusicDetails.js b/src/screens/Library/MusicDetails.js
index 6b5bf8c..2b026a6 100644
--- a/src/screens/Library/MusicDetails.js
+++ b/src/screens/Library/MusicDetails.js
@@ -1,4 +1,6 @@
+import { Feather } from "@expo/vector-icons";
import useTrackController from "../../hooks/useTrackController";
+import usePlayer from "../../hooks/usePlayer";
import { Image as ExpoImage } from "expo-image";
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
import {
@@ -58,6 +60,7 @@ const MusicDetails = ({ route }) => {
const incrementDoneRef = useRef(false);
const timerRef = useRef(null);
const hasAutoPlayedRef = useRef(false);
+ const { isLooping, setLooping } = usePlayer() || {};
const { data: project } = useDataFromRef({
ref: projectId ? projectsRef.doc(projectId) : null,
@@ -193,6 +196,7 @@ const MusicDetails = ({ route }) => {
seekTo: seekTrackTo,
seekBy: seekTrackBy,
} = useTrackController(trackDescriptor);
+ const loopEnabled = isCurrentTrack && !!isLooping;
// Karaoke aligned words (from timestamps)
const alignedWords = useMemo(() => {
@@ -325,6 +329,31 @@ const MusicDetails = ({ route }) => {
[durationMs, trackDescriptor, isCurrentTrack, ensureLoaded, seekTrackTo]
);
+ const handleToggleLoop = useCallback(async () => {
+ if (!isCurrentTrack) return;
+ const next = !loopEnabled;
+ if (typeof setLooping === "function") {
+ setLooping(next);
+ }
+ if (next) {
+ try {
+ await seekTrackTo(0);
+ if (!isTrackPlaying) {
+ await resumeTrack();
+ }
+ } catch (e) {
+ console.log("MusicDetails loop toggle error", e?.message);
+ }
+ }
+ }, [
+ isCurrentTrack,
+ loopEnabled,
+ setLooping,
+ seekTrackTo,
+ isTrackPlaying,
+ resumeTrack,
+ ]);
+
useEffect(() => {
if (!autoPlayRequested || hasAutoPlayedRef.current) return;
if (!trackDescriptor || !songUrl) return;
@@ -746,6 +775,24 @@ const MusicDetails = ({ route }) => {
{artist}
+
+
+
{
if (!projectId || !currentUID) return;
diff --git a/src/screens/Library/MusicDetails.web.js b/src/screens/Library/MusicDetails.web.js
index 4cb8b4a..8bebb08 100644
--- a/src/screens/Library/MusicDetails.web.js
+++ b/src/screens/Library/MusicDetails.web.js
@@ -1,5 +1,6 @@
import { BlurView } from "expo-blur";
import { Image as ExpoImage } from "expo-image";
+import { Feather } from "@expo/vector-icons";
import React, {
useCallback,
useEffect,
@@ -33,6 +34,7 @@ import {
} from "../../config/firebase";
import useDataFromRef from "../../hooks/useDataFromRef";
import useTrackController from "../../hooks/useTrackController";
+import usePlayer from "../../hooks/usePlayer";
import Page from "../../layouts/Page";
import { Palette } from "../../styles";
import { FONT_FAMILY } from "../../styles/Fonts";
@@ -67,6 +69,7 @@ const MusicDetails = ({ route }) => {
const incrementDoneRef = React.useRef(false);
const timerRef = React.useRef(null);
const hasAutoPlayedRef = React.useRef(false);
+ const { isLooping, setLooping } = usePlayer() || {};
const { data: project } = useDataFromRef({
ref: projectId ? projectsRef.doc(projectId) : null,
@@ -275,6 +278,7 @@ const MusicDetails = ({ route }) => {
} = useTrackController(trackDescriptor);
const isPlaying = isTrackPlaying;
+ const loopEnabled = isCurrentTrack && !!isLooping;
// Karaoke aligned words (from timestamps)
const alignedWords = useMemo(() => {
@@ -429,6 +433,31 @@ const MusicDetails = ({ route }) => {
[trackDescriptor, durationMs, isCurrentTrack, ensureLoaded, seekTrackTo]
);
+ const handleToggleLoop = useCallback(async () => {
+ if (!isCurrentTrack) return;
+ const next = !loopEnabled;
+ if (typeof setLooping === "function") {
+ setLooping(next);
+ }
+ if (next) {
+ try {
+ await seekTrackTo(0);
+ if (!isPlaying) {
+ await resumeTrack();
+ }
+ } catch (e) {
+ console.log("MusicDetails.web loop toggle error", e?.message);
+ }
+ }
+ }, [
+ isCurrentTrack,
+ loopEnabled,
+ setLooping,
+ seekTrackTo,
+ isPlaying,
+ resumeTrack,
+ ]);
+
useEffect(() => {
if (!autoPlayRequested || hasAutoPlayedRef.current) {
return;
@@ -926,6 +955,27 @@ const MusicDetails = ({ route }) => {
{artist}
+
+
+
{
if (!projectId || !currentUID) return;
@@ -972,9 +1022,7 @@ const MusicDetails = ({ route }) => {
handleSeekBySeconds(-10)}>
{/* Play / Pause */}
@@ -987,7 +1035,7 @@ const MusicDetails = ({ route }) => {
onPress={togglePlay}
>
diff --git a/src/screens/Profile/Profile.js b/src/screens/Profile/Profile.js
index d06cfc2..8a83053 100644
--- a/src/screens/Profile/Profile.js
+++ b/src/screens/Profile/Profile.js
@@ -34,11 +34,11 @@ import Page from "../../layouts/Page";
import { Routes } from "../../navigation";
import { push } from "../../navigation/NavigationService";
import { useUser } from "../../providers/UserDataProvider";
-import { getArtistDisplayName } from "../../utils/artistName";
-import { getProjectLikes, LIKE_TARGET } from "../../utils/likes";
import { Palette } from "../../styles";
import { FONT_FAMILY } from "../../styles/Fonts";
import Style, { gutters, size } from "../../styles/Style";
+import { getArtistDisplayName } from "../../utils/artistName";
+import { getProjectLikes, LIKE_TARGET } from "../../utils/likes";
import MusicCard from "../Library/components/MusicCard";
const Profile = () => {
const { params } = useRoute();
@@ -321,9 +321,7 @@ const Profile = () => {
});
const displayedProjects = isSelf ? selfProjects : otherUserProjects;
- const displayedPlaybacksSource = isSelf
- ? selfPlaybacks
- : otherUserProjects;
+ const displayedPlaybacksSource = isSelf ? selfPlaybacks : otherUserProjects;
const displayedPlaybacks = Array.isArray(displayedPlaybacksSource)
? displayedPlaybacksSource.filter((project) => project?.playbackUrl)
: [];
@@ -419,12 +417,12 @@ const Profile = () => {
{webUploadMessage}
) : null}
+ style={{
+ fontSize: 22,
+ color: Palette.white,
+ fontFamily: FONT_FAMILY.InterSemiBold,
+ }}
+ >
{profileTitle}