use only profile
This commit is contained in:
+202
-47
@@ -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 }) => (
|
||||
<ScrollView style={{ flex: 1 }}>
|
||||
@@ -52,7 +124,10 @@ const Profile = () => {
|
||||
renderItem={({ item }) => (
|
||||
<MusicCard
|
||||
title={item?.title || "Sans titre"}
|
||||
subtitle={currentUserData?.userName || "MusicLand"}
|
||||
subtitle={
|
||||
(isSelf ? currentUserData?.userName : userData?.userName) ||
|
||||
"MusicLand"
|
||||
}
|
||||
imageUri={item?.coverUrl || null}
|
||||
projectId={item?.id}
|
||||
likedBy={item?.likedBy || []}
|
||||
@@ -76,33 +151,38 @@ const Profile = () => {
|
||||
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",
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
}),
|
||||
},
|
||||
]}
|
||||
]
|
||||
: []
|
||||
}
|
||||
/>
|
||||
</ScrollView>
|
||||
);
|
||||
@@ -122,22 +202,47 @@ const Profile = () => {
|
||||
</View>
|
||||
);
|
||||
|
||||
// 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 (
|
||||
<Page
|
||||
backgroundImg={background.profileBG}
|
||||
headerType="NAVIGATE"
|
||||
hideBackButton
|
||||
hideBackButton={isSelf}
|
||||
contentContainerStyle={{
|
||||
paddingBottom: gutters * 2,
|
||||
}}
|
||||
containerStyle={{
|
||||
backgroundColor: "#0000004D",
|
||||
}}
|
||||
rightComponent={() => (
|
||||
<Pressable onPress={() => SheetManager.show("ProfileSettings")}>
|
||||
<Image source={icons.more} style={{ ...size({ size: 24 }) }} />
|
||||
</Pressable>
|
||||
)}
|
||||
rightComponent={() =>
|
||||
isSelf ? (
|
||||
<Pressable onPress={() => SheetManager.show("ProfileSettings")}>
|
||||
<Image source={icons.more} style={{ ...size({ size: 24 }) }} />
|
||||
</Pressable>
|
||||
) : null
|
||||
}
|
||||
>
|
||||
<View style={{ flex: 1, marginTop: responsiveHeight(2), gap: 20 }}>
|
||||
<View style={{ borderRadius: 20, overflow: "hidden" }}>
|
||||
@@ -155,9 +260,15 @@ const Profile = () => {
|
||||
<View style={{ alignItems: "center" }}>
|
||||
<ExpoImage
|
||||
source={
|
||||
currentUserData?.profilePictureURL
|
||||
(
|
||||
isSelf
|
||||
? currentUserData?.profilePictureURL
|
||||
: userData?.profilePictureURL
|
||||
)
|
||||
? {
|
||||
uri: currentUserData?.profilePictureURL,
|
||||
uri: isSelf
|
||||
? currentUserData?.profilePictureURL
|
||||
: userData?.profilePictureURL,
|
||||
}
|
||||
: img.profile
|
||||
}
|
||||
@@ -177,33 +288,77 @@ const Profile = () => {
|
||||
fontFamily: FONT_FAMILY.InterSemiBold,
|
||||
}}
|
||||
>
|
||||
{currentUserData?.userName}
|
||||
{(isSelf ? currentUserData?.userName : userData?.userName) ||
|
||||
"Profil"}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={{ ...Style.containerRow, gap: 10, marginTop: 10 }}>
|
||||
<View style={{ flex: 1, alignItems: "center" }}>
|
||||
<Text style={styles.value}>{userProjects?.length || 0}</Text>
|
||||
<Text style={styles.value}>
|
||||
{Array.isArray(displayedProjects)
|
||||
? displayedProjects.length
|
||||
: 0}
|
||||
</Text>
|
||||
<Text style={styles.label}>projets</Text>
|
||||
</View>
|
||||
<Pressable
|
||||
style={{ flex: 1, alignItems: "center" }}
|
||||
onPress={() => push(Routes.Follows, { selected: "Abonnés" })}
|
||||
onPress={() =>
|
||||
push(Routes.Follows, {
|
||||
selected: "Abonnés",
|
||||
...(isSelf ? {} : { userId: targetUserId }),
|
||||
})
|
||||
}
|
||||
>
|
||||
<Text style={styles.value}>
|
||||
{currentUserData?.followedBy?.length || 0}
|
||||
{isSelf
|
||||
? currentUserData?.followedBy?.length || 0
|
||||
: followers}
|
||||
</Text>
|
||||
<Text style={styles.label}>abonnés</Text>
|
||||
</Pressable>
|
||||
<Pressable
|
||||
style={{ flex: 1, alignItems: "center" }}
|
||||
onPress={() =>
|
||||
push(Routes.Follows, { selected: "Abonnements" })
|
||||
push(Routes.Follows, {
|
||||
selected: "Abonnements",
|
||||
...(isSelf ? {} : { userId: targetUserId }),
|
||||
})
|
||||
}
|
||||
>
|
||||
<Text style={styles.value}>{followingCount}</Text>
|
||||
<Text style={styles.value}>
|
||||
{isSelf ? followingCount : following}
|
||||
</Text>
|
||||
<Text style={styles.label}>abonnements</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
{!isSelf && (
|
||||
<Pressable onPress={handleFollowUser}>
|
||||
<View
|
||||
style={{
|
||||
height: 36,
|
||||
borderRadius: 100,
|
||||
overflow: "hidden",
|
||||
backgroundColor: "#FFFFFF22",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
width: "80%",
|
||||
alignSelf: "center",
|
||||
marginTop: 18,
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 12,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.InterSemiBold,
|
||||
}}
|
||||
>
|
||||
{isFollowing ? "Ne plus suivre" : "Suivre"}
|
||||
</Text>
|
||||
</View>
|
||||
</Pressable>
|
||||
)}
|
||||
</BlurView>
|
||||
</View>
|
||||
<View
|
||||
@@ -246,13 +401,13 @@ const Profile = () => {
|
||||
</View>
|
||||
{selected === "Playbacks" && (
|
||||
<MusicListSection
|
||||
data={userPlaybacks}
|
||||
data={displayedPlaybacks}
|
||||
emptyText="Aucun playback pour le moment"
|
||||
/>
|
||||
)}
|
||||
{selected === "Chansons" && (
|
||||
<MusicListSection
|
||||
data={userProjects}
|
||||
data={displayedProjects}
|
||||
emptyText="Aucune chanson pour le moment"
|
||||
/>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user