new project modal & seperate playback and music likes

This commit is contained in:
2025-10-24 16:12:07 +02:00
parent 47d5b92a57
commit 2599a25d77
12 changed files with 415 additions and 63 deletions
+2 -1
View File
@@ -13,6 +13,7 @@ import { Routes } from "../../navigation";
import { navigate } 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 MusicCard from "../Library/components/MusicCard";
@@ -86,7 +87,7 @@ const HomeSave = () => {
subtitle={item?.userName || ownerDisplayName}
imageUri={item?.coverUrl || null}
projectId={item?.id}
likedBy={item?.likedBy || []}
likedBy={getProjectLikes(item, LIKE_TARGET.SONG)}
onPress={() => {
selectProject(item.id);
navigate(Routes.Home);
+8 -4
View File
@@ -1,4 +1,4 @@
import React, { useState } from "react";
import React, { useCallback, useState } from "react";
import {
FlatList,
Image,
@@ -7,6 +7,7 @@ import {
View,
useWindowDimensions,
} from "react-native";
import { SheetManager } from "react-native-actions-sheet";
import GradientButton from "../../../components/GradientButton";
import useLayoutType from "../../../hooks/useLayoutType";
import { Routes } from "../../../navigation";
@@ -29,7 +30,10 @@ const BackTracks = () => {
0,
Math.floor((containerWidth - gap * (numColumns - 1)) / numColumns)
);
console.log("BackTracks items:", items);
const openPlaybackPicker = useCallback(() => {
SheetManager.show("PlaybackPicker");
}, []);
return (
<CardContainer
@@ -41,7 +45,7 @@ const BackTracks = () => {
liked: false,
})
}
onPressPlus={() => navigate(Routes.WritingLyrics)}
onPressPlus={openPlaybackPicker}
>
<View>
{items.length > 0 ? (
@@ -85,7 +89,7 @@ const BackTracks = () => {
</Text>
<GradientButton
containerStyle={{ padding: 2 }}
onPress={() => navigate(Routes.WritingLyrics)}
onPress={openPlaybackPicker}
title=" Créer un playback"
></GradientButton>
</View>
+7 -3
View File
@@ -13,7 +13,7 @@ import { responsiveWidth } from "react-native-responsive-dimensions";
import { useGlobal } from "reactn";
import { icons, img } from "../../../assets";
import PressableScale from "../../../components/PressableScale";
import { arrayRemove, arrayUnion, projectsRef } from "../../../config/firebase";
import { LIKE_TARGET, toggleProjectLike } from "../../../utils/likes";
import { Palette, Style } from "../../../styles";
import { FONT_FAMILY } from "../../../styles/Fonts";
import { size } from "../../../styles/Style";
@@ -26,6 +26,7 @@ const MusicCard = ({
imageUri = null,
projectId = null,
likedBy = [],
likeTarget = LIKE_TARGET.SONG,
}) => {
const [currentUID] = useGlobal("currentUID");
const [selected, setSelected] = useState(false);
@@ -58,8 +59,11 @@ const MusicCard = ({
const next = !selected;
setSelected(next);
try {
await projectsRef.doc(projectId).update({
likedBy: next ? arrayUnion(currentUID) : arrayRemove(currentUID),
await toggleProjectLike({
projectId,
target: likeTarget,
currentUID,
next,
});
} catch (e) {
// rollback on failure
+15 -19
View File
@@ -15,12 +15,7 @@ import { icons, img } from "../../assets";
import { openComments } from "../../components/bottomsheets/CommentsBottomSheet";
import KaraokeLyrics from "../../components/KaraokeLyrics";
import ProfilePicture from "../../components/ProfilePicture";
import {
arrayRemove,
arrayUnion,
projectsRef,
usersRef,
} from "../../config/firebase";
import { projectsRef, usersRef } from "../../config/firebase";
import useDataFromRef from "../../hooks/useDataFromRef";
import { Routes } from "../../navigation";
import { navigate } from "../../navigation/NavigationService";
@@ -28,11 +23,16 @@ import { useUser } from "../../providers/UserDataProvider";
import { Palette } from "../../styles";
import { FONT_FAMILY } from "../../styles/Fonts";
import { size } from "../../styles/Style";
import {
getProjectLikes,
LIKE_TARGET,
toggleProjectLike,
} from "../../utils/likes";
const PlaybackItem = ({ item, isActive, userCache, getUserByUid }) => {
const { currentUID, followUser, unfollowUser } = useUser() || {};
const videoUrl = item?.playbackUrl || null;
const initialLikedBy = Array.isArray(item?.likedBy) ? item.likedBy : [];
const initialLikedBy = getProjectLikes(item, LIKE_TARGET.PLAYBACK);
const [isLiked, setIsLiked] = useState(
currentUID ? initialLikedBy.includes(currentUID) : false
);
@@ -100,10 +100,10 @@ const PlaybackItem = ({ item, isActive, userCache, getUserByUid }) => {
}, [owner?.followedBy, currentUID]);
useEffect(() => {
const lb = Array.isArray(item?.likedBy) ? item.likedBy : [];
const lb = getProjectLikes(item, LIKE_TARGET.PLAYBACK);
setLikesCount(lb.length);
setIsLiked(currentUID ? lb.includes(currentUID) : false);
}, [item?.likedBy, currentUID]);
}, [item?.likes?.playback, currentUID]);
const projectTitle = typeof item?.title === "string" ? item.title.trim() : "";
@@ -292,16 +292,12 @@ const PlaybackItem = ({ item, isActive, userCache, getUserByUid }) => {
setIsLiked(nextLiked);
setLikesCount((c) => Math.max(0, c + (nextLiked ? 1 : -1)));
const ref = projectsRef.doc(item.id);
await ref.set(
{
likedBy: nextLiked
? arrayUnion(currentUID)
: arrayRemove(currentUID),
// Optionally: updatedAt could be set if needed
},
{ merge: true }
);
await toggleProjectLike({
projectId: item.id,
currentUID,
target: LIKE_TARGET.PLAYBACK,
next: nextLiked,
});
} catch (e) {
// rollback on failure
setIsLiked((v) => !v);
@@ -17,12 +17,7 @@ import {
} from "react-native";
import { icons, img } from "../../../assets";
import KaraokeLyrics from "../../../components/KaraokeLyrics";
import {
arrayRemove,
arrayUnion,
projectsRef,
usersRef,
} from "../../../config/firebase";
import { usersRef } from "../../../config/firebase";
import { Routes } from "../../../navigation";
import { navigate } from "../../../navigation/NavigationService";
import { useUser } from "../../../providers/UserDataProvider";
@@ -30,6 +25,11 @@ import { Palette } from "../../../styles";
import { FONT_FAMILY } from "../../../styles/Fonts";
import { size } from "../../../styles/Style";
import CommentsPanel from "./CommentsPanel.web";
import {
getProjectLikes,
LIKE_TARGET,
toggleProjectLike,
} from "../../../utils/likes";
// Debug logging toggle for web playback
const DEBUG_PLAYBACK_WEB = true;
@@ -61,7 +61,7 @@ const PlaybackItem = ({
const wasActiveRef = useRef(false);
const startedRef = useRef(false);
const initialLikedBy = Array.isArray(item?.likedBy) ? item.likedBy : [];
const initialLikedBy = getProjectLikes(item, LIKE_TARGET.PLAYBACK);
const [isLiked, setIsLiked] = useState(
currentUID ? initialLikedBy.includes(currentUID) : false
);
@@ -130,10 +130,10 @@ const PlaybackItem = ({
}, [owner?.followedBy, currentUID]);
useEffect(() => {
const lb = Array.isArray(item?.likedBy) ? item.likedBy : [];
const lb = getProjectLikes(item, LIKE_TARGET.PLAYBACK);
setLikesCount(lb.length);
setIsLiked(currentUID ? lb.includes(currentUID) : false);
}, [item?.likedBy, currentUID]);
}, [item?.likes?.playback, currentUID]);
useEffect(() => {
startedRef.current = false;
@@ -428,15 +428,12 @@ const PlaybackItem = ({
setIsLiked(nextLiked);
setLikesCount((c) => Math.max(0, c + (nextLiked ? 1 : -1)));
const ref = projectsRef.doc(item.id);
await ref.set(
{
likedBy: nextLiked
? arrayUnion(currentUID)
: arrayRemove(currentUID),
},
{ merge: true }
);
await toggleProjectLike({
projectId: item.id,
currentUID,
target: LIKE_TARGET.PLAYBACK,
next: nextLiked,
});
} catch (_e) {
setIsLiked((v) => !v);
setLikesCount((c) =>
+17 -2
View File
@@ -35,6 +35,7 @@ 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";
@@ -191,7 +192,11 @@ const Profile = () => {
};
// Composant factorisé pour afficher la liste de musiques
const MusicListSection = ({ data, emptyText }) => (
const MusicListSection = ({
data,
emptyText,
likeTarget = LIKE_TARGET.SONG,
}) => (
<ScrollView style={{ flex: 1, marginBottom: 70 }}>
{Array.isArray(data) && data.length > 0 ? (
<FlatList
@@ -207,12 +212,21 @@ const Profile = () => {
subtitle={isSelf ? selfDisplayName : targetDisplayName}
imageUri={item?.coverUrl || null}
projectId={item?.id}
likedBy={item?.likedBy || []}
likedBy={getProjectLikes(item, likeTarget)}
likeTarget={likeTarget}
onPress={() =>
navigateToMusicDetails({
projectId: item.id,
songUrl: item?.songUrl,
project: item,
queueProjects: data,
queueSource: {
id: isSelf ? "profile-self" : "profile-other",
type: "collection",
name: isSelf
? "Mes musiques"
: targetDisplayName || "Profil",
},
})
}
onPressMore={(posTop) => {
@@ -528,6 +542,7 @@ const Profile = () => {
<MusicListSection
data={displayedPlaybacks}
emptyText="Aucun playback pour le moment"
likeTarget={LIKE_TARGET.PLAYBACK}
/>
)}
{selected === "Chansons" && (