From 87d89c1240b6179ddc27e3dbba9dea7bd26ffb2a Mon Sep 17 00:00:00 2001 From: leon-morival Date: Fri, 29 Aug 2025 14:31:03 +0200 Subject: [PATCH] edit profile --- src/config/firebase.js | 2 +- src/providers/UserDataProvider.js | 7 +- src/screens/AccountSettings.js | 8 +- src/screens/Library/Research.js | 56 ++++++++------ src/screens/Profile/EditProfile.js | 109 ++++++++++++++++++++++----- src/screens/Profile/Profile.js | 31 +++++--- src/screens/Profile/SingerProfile.js | 4 +- src/services/follows.js | 2 +- 8 files changed, 153 insertions(+), 66 deletions(-) diff --git a/src/config/firebase.js b/src/config/firebase.js index 6ec7adf..1054af1 100644 --- a/src/config/firebase.js +++ b/src/config/firebase.js @@ -54,7 +54,7 @@ export const notificationsRef = firestore.collection("notifications"); export const documentsRef = firestore.collection("documents"); export const chatsRef = firestore.collection("chats"); -export const { arrayUnion, arrayRemove, increment } = +export const { arrayUnion, arrayRemove, increment, serverTimestamp } = firebase.firestore.FieldValue; export default firebase; diff --git a/src/providers/UserDataProvider.js b/src/providers/UserDataProvider.js index b7e8a0b..7abe7dd 100644 --- a/src/providers/UserDataProvider.js +++ b/src/providers/UserDataProvider.js @@ -93,7 +93,7 @@ export default ({ children }) => { { followerId: currentUID, followeeId: userId, - createdAt: firebase.firestore.FieldValue.serverTimestamp(), + createdAt: new Date(), }, { merge: true } ); @@ -109,7 +109,10 @@ export default ({ children }) => { try { if (currentUID && userId) { await followsRef.doc(`${currentUID}_${userId}`).delete(); - setTooltip({ type: "success", text: "Vous ne suivez plus cet utilisateur" }); + setTooltip({ + type: "success", + text: "Vous ne suivez plus cet utilisateur", + }); } } catch (e) { console.log(e); diff --git a/src/screens/AccountSettings.js b/src/screens/AccountSettings.js index dc06b47..e804399 100644 --- a/src/screens/AccountSettings.js +++ b/src/screens/AccountSettings.js @@ -1,21 +1,21 @@ -import React, { useGlobal } from "reactn"; -import { Text, Pressable } from "react-native"; import * as ImagePicker from "expo-image-picker"; +import { Pressable, Text } from "react-native"; import useMinuit from "react-native-minuit/src/hooks/useMinuit.js"; +import React, { useGlobal } from "reactn"; import { responsiveWidth } from "../actions/responsiveSizes.js"; import Page from "../layouts/Page"; +import alert from "../components/Alert.js"; import Avatar from "../components/Avatar"; import InputRow from "../components/InputRow"; import ItemRowList from "../components/ItemRowList.js"; -import alert from "../components/Alert.js"; import firebase, { usersRef } from "../config/firebase"; -import { Fonts, gutters, Palette, Style } from "../styles"; import { uploadFileToFirebase } from "../helpers/uploadToFirebase"; import { Routes } from "../navigation/Routes.js"; +import { Fonts, gutters, Palette, Style } from "../styles"; import { useUserData } from "../providers/UserDataProvider.js"; diff --git a/src/screens/Library/Research.js b/src/screens/Library/Research.js index 664ac05..99b5780 100644 --- a/src/screens/Library/Research.js +++ b/src/screens/Library/Research.js @@ -1,9 +1,10 @@ -import React, { useMemo, useState } from "react"; +import React, { useEffect, useMemo, useState } from "react"; import { Image, Pressable, ScrollView, Text, View } from "react-native"; import { background, img } from "../../assets"; import MoreMenu from "../../components/MoreMenu"; import { usersRef } from "../../config/firebase"; import useDataFromRef from "../../hooks/useDataFromRef"; +import { useGlobal } from "reactn"; import Page from "../../layouts/Page"; import { Routes } from "../../navigation"; import { navigate } from "../../navigation/NavigationService"; @@ -18,14 +19,18 @@ const Research = () => { const [selected, setSelected] = useState(null); const [search, setSearch] = useState(""); const queryText = useMemo(() => search.trim(), [search]); + const [currentUID] = useGlobal("currentUID"); + const [autoLoads, setAutoLoads] = useState(0); const usersQueryRef = useMemo(() => { - if (!queryText) return null; try { - return usersRef - .orderBy("userName") - .startAt(queryText) - .endAt(queryText + "\uf8ff"); + if (queryText) { + return usersRef + .orderBy("userName") + .startAt(queryText) + .endAt(queryText + "\uf8ff"); + } + return usersRef.orderBy("userName"); } catch (e) { return null; } @@ -39,11 +44,28 @@ const Research = () => { ref: usersQueryRef, simpleRef: false, listener: false, - condition: !!usersQueryRef && queryText.length > 0, + condition: !!usersQueryRef, refreshArray: [queryText], usePagination: true, batchSize: 5, }); + const filteredUsers = useMemo(() => { + const arr = Array.isArray(userResults) ? userResults : []; + return arr.filter((u) => u?.id !== currentUID && u?.userID !== currentUID); + }, [JSON.stringify(userResults), currentUID]); + + useEffect(() => { + setAutoLoads(0); + }, [queryText]); + + useEffect(() => { + if (filteredUsers.length < 5 && usersQueryRef && !usersLoading) { + if (autoLoads < 3) { + setAutoLoads((n) => n + 1); + loadMoreUsers(); + } + } + }, [filteredUsers.length, usersLoading, usersQueryRef, autoLoads, loadMoreUsers]); const [menuTop, setMenuTop] = useState(0); const [showMenu, setShowMenu] = useState(false); const [selectedProjectId, setSelectedProjectId] = useState(null); @@ -138,21 +160,8 @@ const Research = () => { gradientProps={{ start: { x: 0, y: 0 }, end: { x: 0, y: 1 } }} > - {queryText.length === 0 && ( - - Commence à taper pour rechercher des profils… - - )} - - {queryText.length > 0 && - Array.isArray(userResults) && - userResults.map((u) => ( + {Array.isArray(filteredUsers) && + filteredUsers.slice(0, 5).map((u) => ( { ))} - - {queryText.length > 0 && userResults.length > 0 && ( + {(filteredUsers.length >= 5 || usersLoading) && ( { const [currentUserData] = useGlobal("currentUserData"); const [, setTooltip] = useGlobal("_tooltip"); - + const { currentUID } = useUser(); const [pseudo, setPseudo] = useState(""); const [loading, setLoading] = useState(false); + const [updatingPhoto, setUpdatingPhoto] = useState(false); useEffect(() => { if (currentUserData?.userName && !pseudo) { @@ -53,18 +57,67 @@ const EditProfile = () => { userNameLower: lower, updatedAt: firebase.firestore.FieldValue.serverTimestamp(), }, - { merge: true }, + { merge: true } ); setTooltip({ text: "Pseudo mis à jour", type: "success" }); goBack(); } catch (e) { console.log("EditProfile onSave error", e?.message); - setTooltip({ text: e?.message || "Mise à jour impossible", type: "error" }); + setTooltip({ + text: e?.message || "Mise à jour impossible", + type: "error", + }); } finally { setLoading(false); } }; + + const onChangePicture = async () => { + try { + setUpdatingPhoto(true); + const uid = currentUID; + if (!uid) return; + + const result = await ImagePicker.launchImageLibraryAsync({ + mediaTypes: ImagePicker.MediaTypeOptions.Images, + allowsEditing: true, + aspect: [4, 4], + quality: 1, + }); + + const pickedUri = result?.assets?.[0]?.uri || null; + if (!pickedUri) return; + + const { resultURI = null } = await uploadFileToFirebase({ + uri: pickedUri, + path: `users/${uid}/profilePicture.png`, + }); + + if (!resultURI) throw new Error("Téléversement de l'image impossible"); + + await usersRef.doc(uid).set( + { + profilePictureURL: resultURI, + updatedAt: serverTimestamp(), + }, + { merge: true } + ); + + await firebase.auth().currentUser.updateProfile({ + photoURL: resultURI, + }); + + setTooltip({ text: "Photo de profil mise à jour", type: "success" }); + } catch (e) { + setTooltip({ + text: e?.message || "Erreur changement photo de profil", + type: "error", + }); + } finally { + setUpdatingPhoto(false); + } + }; return ( { Platform.OS !== "ios" ? "dimezisBlurView" : "none" } > - + { fontFamily: FONT_FAMILY.InterRegular, }} > - Modifier la photo + {updatingPhoto ? "Chargement..." : "Modifier la photo"} - - + + { alignSelf: "center", }} onPress={onSave} - disabled={loading || !pseudo?.trim() || pseudo?.trim() === (currentUserData?.userName || "")} + disabled={ + loading || + updatingPhoto || + !pseudo?.trim() || + pseudo?.trim() === (currentUserData?.userName || "") + } /> diff --git a/src/screens/Profile/Profile.js b/src/screens/Profile/Profile.js index 4b3ca1a..ab0666c 100644 --- a/src/screens/Profile/Profile.js +++ b/src/screens/Profile/Profile.js @@ -1,5 +1,5 @@ import { BlurView } from "expo-blur"; -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import { FlatList, Image, @@ -22,24 +22,29 @@ import Page from "../../layouts/Page"; import { Routes } from "../../navigation"; import { navigate } from "../../navigation/NavigationService"; import { useUser } from "../../providers/UserDataProvider"; +import { getFollowersCount, getFollowingCount } from "../../services/follows"; import { Palette } from "../../styles"; import { FONT_FAMILY } from "../../styles/Fonts"; import Style, { gutters, size } from "../../styles/Style"; import MusicCard from "../Library/components/MusicCard"; - const Profile = () => { const [selected, setSelected] = useState("Chansons"); const [currentUserData] = useGlobal("currentUserData"); - const { userProjects } = useUser(); + const { userProjects, currentUID } = useUser(); const [, setTooltip] = useGlobal("_tooltip"); const [menuTop, setMenuTop] = useState(0); const [showMenu, setShowMenu] = useState(false); const [selectedProjectId, setSelectedProjectId] = useState(null); - + const [following, setFollowing] = useState(0); + const [followers, setFollowers] = useState(0); const onPressMenu = (item) => { setSelected(item); }; - + useEffect(() => { + if (!currentUID) return; + getFollowersCount(currentUID).then(setFollowers); + getFollowingCount(currentUID).then(setFollowing); + }, [currentUID]); return ( { > { playbacks - - {currentUserData?.followBy?.length || 0} - + {followers} abonnés - - {currentUserData?.following?.length || 0} - + {following} abonnements diff --git a/src/screens/Profile/SingerProfile.js b/src/screens/Profile/SingerProfile.js index 6f66b12..1878e2c 100644 --- a/src/screens/Profile/SingerProfile.js +++ b/src/screens/Profile/SingerProfile.js @@ -10,14 +10,14 @@ import { View, } from "react-native"; 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 { followsRef } from "../../config/firebase"; import Page from "../../layouts/Page"; import { useUser } from "../../providers/UserDataProvider"; import { getFollowersCount, getFollowingCount } from "../../services/follows"; -import { useGlobal } from "reactn"; -import { followsRef } from "../../config/firebase"; import { gutters, Palette } from "../../styles"; import { FONT_FAMILY } from "../../styles/Fonts"; import Style, { size } from "../../styles/Style"; diff --git a/src/services/follows.js b/src/services/follows.js index 3218f06..7989ff5 100644 --- a/src/services/follows.js +++ b/src/services/follows.js @@ -13,7 +13,7 @@ export const getFollowingCount = async (userId) => { }; export const unfollowUser = async ({ followerId, followeeId }) => { - if (!followerId || !followeeId) throw new Error("Missing followerId/followeeId"); + if (!followerId || !followeeId) throw new Error("Erreur lors du unfollow"); const id = `${followerId}_${followeeId}`; await followsRef.doc(id).delete(); return id;