diff --git a/src/navigation/MainStack.js b/src/navigation/MainStack.js index 0c02dfb..c28162d 100644 --- a/src/navigation/MainStack.js +++ b/src/navigation/MainStack.js @@ -52,7 +52,7 @@ import AllMyLikedMusic from "../screens/Library/AllMyLikedMusic"; import Research from "../screens/Library/Research"; import MusicDetails from "../screens/Library/MusicDetails"; import PlaylistDetails from "../screens/Library/PlaylistDetails"; -import SingerProfile from "../screens/Profile/SingerProfile"; +import Profile from "../screens/Profile/Profile"; import EditProfile from "../screens/Profile/EditProfile"; import Settings from "../screens/Profile/Settings"; import ChangeEmailAddress from "../screens/Profile/ChangeEmailAddress"; @@ -252,7 +252,7 @@ const screens = [ }, { name: Routes.SingerProfile, - component: SingerProfile, + component: Profile, }, { name: Routes.EditProfile, diff --git a/src/screens/Profile/Profile.js b/src/screens/Profile/Profile.js index bae65f9..7a6ff07 100644 --- a/src/screens/Profile/Profile.js +++ b/src/screens/Profile/Profile.js @@ -1,6 +1,7 @@ +import { useRoute } from "@react-navigation/native"; import { BlurView } from "expo-blur"; import { Image as ExpoImage } from "expo-image"; -import React, { useState } from "react"; +import React, { useEffect, useMemo, useState } from "react"; import { FlatList, Image, @@ -17,7 +18,8 @@ import { useGlobal } from "reactn"; import { background, icons, img } from "../../assets"; import BorderGradient from "../../components/BorderGradient/BorderGradient"; import MoreMenu from "../../components/MoreMenu"; -import { projectsRef } from "../../config/firebase"; +import { projectsRef, usersRef } from "../../config/firebase"; +import useDataFromRef from "../../hooks/useDataFromRef"; import Page from "../../layouts/Page"; import { Routes } from "../../navigation"; import { navigate, push } from "../../navigation/NavigationService"; @@ -28,10 +30,25 @@ import Style, { gutters, size } from "../../styles/Style"; import MusicCard from "../Library/components/MusicCard"; const Profile = () => { + const { params } = useRoute(); const [selected, setSelected] = useState("Chansons"); - const { userProjects, userPlaybacks, followingCount, currentUserData } = - useUser(); + const { + userProjects: selfProjects, + userPlaybacks: selfPlaybacks, + followingCount, + currentUserData, + getUserByUid, + followUser, + unfollowUser, + } = useUser(); + const [currentUID] = useGlobal("currentUID"); + const targetUserId = params?.userId || currentUID || null; + const isSelf = !!currentUID && targetUserId === currentUID; const [, setTooltip] = useGlobal("_tooltip"); + const [userData, setUserData] = useState(null); + const [isFollowing, setIsFollowing] = useState(false); + const [followers, setFollowers] = useState(0); + const [following, setFollowing] = useState(0); const [menuTop, setMenuTop] = useState(0); const [showMenu, setShowMenu] = useState(false); const [selectedProjectId, setSelectedProjectId] = useState(null); @@ -39,6 +56,61 @@ const Profile = () => { setSelected(item); }; + // Chargement des données utilisateur si on consulte un autre profil + useEffect(() => { + const fetch = async () => { + if (!targetUserId) return; + if (isSelf) { + setUserData(currentUserData || null); + setFollowers( + Array.isArray(currentUserData?.followedBy) + ? currentUserData.followedBy.length + : 0 + ); + return; + } + const data = await getUserByUid(targetUserId); + setUserData(data || null); + const list = Array.isArray(data?.followedBy) ? data.followedBy : []; + setFollowers(list.length); + setIsFollowing(currentUID ? list.includes(currentUID) : false); + }; + fetch(); + }, [targetUserId, isSelf, currentUserData?.followedBy?.length || 0]); + + // Live following count pour l'utilisateur consulté (autre que soi) + const followingQueryRef = useMemo(() => { + try { + return targetUserId + ? usersRef.where("followedBy", "array-contains", targetUserId) + : null; + } catch (e) { + return null; + } + }, [targetUserId]); + + useDataFromRef({ + ref: followingQueryRef, + simpleRef: false, + listener: true, + condition: !!followingQueryRef && !isSelf, + refreshArray: [targetUserId], + onUpdate: (list) => setFollowing(Array.isArray(list) ? list.length : 0), + }); + + const handleFollowUser = async () => { + if (!currentUID || !targetUserId || isSelf) return; + if (isFollowing) { + await unfollowUser(targetUserId); + setIsFollowing(false); + setFollowers((v) => Math.max(0, (v || 0) - 1)); + } else { + await followUser(targetUserId); + setIsFollowing(true); + setFollowers((v) => (v || 0) + 1); + } + }; + // Composant factorisé pour afficher la liste de musiques const MusicListSection = ({ data, emptyText }) => ( @@ -52,7 +124,10 @@ const Profile = () => { renderItem={({ item }) => ( { onClose={() => setShowMenu(false)} inPlaylist={false} projectId={selectedProjectId} - extraItems={[ - { - label: "Supprimer", - onPress: () => - SheetManager.show("Delete", { - payload: { - title: "Supprimer le projet", - message: "Cette action supprimera définitivement ce projet.", - onConfirm: async () => { - try { - if (!selectedProjectId) return; - await projectsRef.doc(selectedProjectId).delete(); - setTooltip({ - type: "success", - text: "Projet supprimé", - }); - } catch (e) { - setTooltip({ - type: "error", - text: e?.message || "Suppression impossible", - }); - } - }, + extraItems={ + isSelf + ? [ + { + label: "Supprimer", + onPress: () => + SheetManager.show("Delete", { + payload: { + title: "Supprimer le projet", + message: + "Cette action supprimera définitivement ce projet.", + onConfirm: async () => { + try { + if (!selectedProjectId) return; + await projectsRef.doc(selectedProjectId).delete(); + setTooltip({ + type: "success", + text: "Projet supprimé", + }); + } catch (e) { + setTooltip({ + type: "error", + text: e?.message || "Suppression impossible", + }); + } + }, + }, + }), }, - }), - }, - ]} + ] + : [] + } /> ); @@ -122,22 +202,47 @@ const Profile = () => { ); + // Données projets/profils selon si c'est soi ou un autre utilisateur + const projectsQueryRef = useMemo(() => { + try { + if (!targetUserId || isSelf) return null; + return projectsRef + .where("userId", "==", targetUserId) + .orderBy("updatedAt", "desc"); + } catch (e) { + return null; + } + }, [targetUserId, isSelf]); + + const { data: otherUserProjects = [] } = useDataFromRef({ + ref: projectsQueryRef, + simpleRef: false, + listener: true, + condition: !!projectsQueryRef, + refreshArray: [targetUserId], + }); + + const displayedProjects = isSelf ? selfProjects : otherUserProjects; + const displayedPlaybacks = isSelf ? selfPlaybacks : []; + return ( ( - SheetManager.show("ProfileSettings")}> - - - )} + rightComponent={() => + isSelf ? ( + SheetManager.show("ProfileSettings")}> + + + ) : null + } > @@ -155,9 +260,15 @@ const Profile = () => { { fontFamily: FONT_FAMILY.InterSemiBold, }} > - {currentUserData?.userName} + {(isSelf ? currentUserData?.userName : userData?.userName) || + "Profil"} - {userProjects?.length || 0} + + {Array.isArray(displayedProjects) + ? displayedProjects.length + : 0} + projets push(Routes.Follows, { selected: "Abonnés" })} + onPress={() => + push(Routes.Follows, { + selected: "Abonnés", + ...(isSelf ? {} : { userId: targetUserId }), + }) + } > - {currentUserData?.followedBy?.length || 0} + {isSelf + ? currentUserData?.followedBy?.length || 0 + : followers} abonnés - push(Routes.Follows, { selected: "Abonnements" }) + push(Routes.Follows, { + selected: "Abonnements", + ...(isSelf ? {} : { userId: targetUserId }), + }) } > - {followingCount} + + {isSelf ? followingCount : following} + abonnements + {!isSelf && ( + + + + {isFollowing ? "Ne plus suivre" : "Suivre"} + + + + )} { {selected === "Playbacks" && ( )} {selected === "Chansons" && ( )} diff --git a/src/screens/Profile/SingerProfile.js b/src/screens/Profile/SingerProfile.js deleted file mode 100644 index fc70484..0000000 --- a/src/screens/Profile/SingerProfile.js +++ /dev/null @@ -1,352 +0,0 @@ -import React, { useEffect, useMemo, useState } from "react"; -import { - FlatList, - Image, - Platform, - Pressable, - StyleSheet, - Text, - View, -} from "react-native"; -import { Image as ExpoImage } from "expo-image"; -import { responsiveHeight } from "react-native-responsive-dimensions"; -import { useGlobal } from "reactn"; -import { background, icons, img } from "../../assets"; -import BorderGradient from "../../components/BorderGradient/BorderGradient"; -import GradientButton from "../../components/GradientButton"; -import MoreMenu from "../../components/MoreMenu"; -import { projectsRef } from "../../config/firebase"; -import Page from "../../layouts/Page"; -import { Routes } from "../../navigation"; -import { navigate, push } from "../../navigation/NavigationService"; -import { useUser } from "../../providers/UserDataProvider"; -import useDataFromRef from "../../hooks/useDataFromRef"; -import { usersRef } from "../../config/firebase"; -import { gutters, Palette } from "../../styles"; -import { FONT_FAMILY } from "../../styles/Fonts"; -import Style, { size } from "../../styles/Style"; -import MusicCard from "../Library/components/MusicCard"; -import { BlurView } from "expo-blur"; - -const SingerProfile = ({ route }) => { - const { userId } = route.params; - const { getUserByUid, followUser, unfollowUser } = useUser(); - const [currentUID] = useGlobal("currentUID"); - const [selected, setSelected] = useState("Chansons"); - const [userData, setUserData] = useState(null); - const [following, setFollowing] = useState(0); - const [followers, setFollowers] = useState(0); - const fetchUserData = async () => { - const data = userId ? await getUserByUid(userId) : {}; - setUserData(data); - const list = Array.isArray(data?.followedBy) ? data.followedBy : []; - setFollowers(list.length); - setIsFollowing(currentUID ? list.includes(currentUID) : false); - }; - useEffect(() => { - fetchUserData(); - }, [userId]); - // Live following count for the viewed user - const followingQueryRef = React.useMemo(() => { - try { - return userId - ? usersRef.where("followedBy", "array-contains", userId) - : null; - } catch (e) { - return null; - } - }, [userId]); - - useDataFromRef({ - ref: followingQueryRef, - simpleRef: false, - listener: true, - condition: !!followingQueryRef, - refreshArray: [userId], - onUpdate: (list) => setFollowing(Array.isArray(list) ? list.length : 0), - }); - const onPressMenu = (item) => { - if (selected === item) { - setSelected(null); - } else { - setSelected(item); - } - }; - const [isFollowing, setIsFollowing] = useState(false); - const [menuTop, setMenuTop] = useState(0); - const [showMenu, setShowMenu] = useState(false); - const [selectedProjectId, setSelectedProjectId] = useState(null); - const userProjectsQueryRef = useMemo(() => { - try { - if (!userId) return null; - return projectsRef - .where("userId", "==", userId) - .orderBy("updatedAt", "desc"); - } catch (e) { - return null; - } - }, [userId]); - - const { data: userProjects = [] } = useDataFromRef({ - ref: userProjectsQueryRef, - simpleRef: false, - listener: true, - condition: !!userProjectsQueryRef, - refreshArray: [userId], - }); - - const EmptyText = ({ text }) => ( - - - {text} - - - ); - const handleFollowUser = async () => { - if (!currentUID || !userId || currentUID === userId) return; - if (isFollowing) { - await unfollowUser(userId); - setIsFollowing(false); - setFollowers((v) => Math.max(0, (v || 0) - 1)); - } else { - await followUser(userId); - setIsFollowing(true); - setFollowers((v) => (v || 0) + 1); - } - }; - return ( - ( - - - - )} - > - - - - - - - {userData?.userName} - - - - - - {Array.isArray(userProjects) ? userProjects.length : 0} - - projets - - - push(Routes.Follows, { selected: "Abonnés", userId }) - } - > - {followers} - abonnés - - - push(Routes.Follows, { selected: "Abonnements", userId }) - } - > - {following} - abonnements - - - {currentUID !== userId && ( - - )} - - - - {["Playbacks", "Clips", "Chansons"].map((item, index) => ( - onPressMenu(item)} - style={{ flex: 1 }} - > - - - - {item} - - - - ))} - - {selected === "Playbacks" && ( - - - - )} - {selected === "Chansons" && ( - - {Array.isArray(userProjects) && userProjects.length > 0 ? ( - item.id} - renderItem={({ item }) => ( - - navigate(Routes.MusicDetails, { projectId: item.id }) - } - onPressMore={(posTop) => { - setSelectedProjectId(item.id); - setMenuTop(posTop); - setShowMenu((prev) => !prev || posTop !== menuTop); - }} - /> - )} - /> - ) : ( - - )} - - )} - - {selected === "Clips" && ( - - - - )} - setShowMenu(false)} - inPlaylist={false} - projectId={selectedProjectId} - /> - - - ); -}; - -export default SingerProfile; - -const styles = StyleSheet.create({ - value: { - fontSize: 16, - color: Palette.white, - fontFamily: FONT_FAMILY.InterRegular, - }, - label: { - fontSize: 12, - color: Palette.white, - fontFamily: FONT_FAMILY.InterRegular, - }, - menu: { - fontSize: 12, - color: Palette.white, - fontFamily: FONT_FAMILY.InterRegular, - }, - borderGradient: { - borderWidth: 1, - borderRadius: 10, - height: 33, - position: "absolute", - zIndex: 1, - width: "100%", - }, - blurContainer: { - height: 33, - zIndex: -1, - borderRadius: 10, - overflow: "hidden", - backgroundColor: Palette.glass, - }, - blurView: { - width: "100%", - height: "100%", - paddingHorizontal: 10, - ...Style.containerCenter, - }, -});