Files
musicland/src/providers/UserDataProvider.js
T
Thomas Demirdjian 167e62fcfc end of home
2025-10-01 10:49:03 +02:00

418 lines
11 KiB
JavaScript

/* eslint-disable react/display-name */
import { useDataFromRef } from "react-native-minuit/src/hooks";
import useMinuit from "react-native-minuit/src/hooks/useMinuit";
import { createContext, useContext, useGlobal } from "reactn";
import { useCallback, useEffect, useState } from "react";
import { checkIfEmailIsValid } from "../actions/signupActions";
import firebase, {
arrayRemove,
arrayUnion,
playlistsRef,
projectsRef,
usersRef,
} from "../config/firebase";
export const UserDataContext = createContext();
export default ({ children }) => {
const { setIsLoading, setTooltip } = useMinuit();
const [pendingUserData, setPendingUserData] = useState({
creationTimestamp: new Date(),
});
const [currentUID] = useGlobal("currentUID");
const [currentUserData, setCurrentUserData] = useGlobal("currentUserData");
const [currentUserRoles] = useGlobal("currentUserRoles");
// Current user document (live)
const { data: currentUserDoc = null } = useDataFromRef({
ref: currentUID ? usersRef.doc(currentUID) : null,
updateGlobalState: "currentUserData",
simpleRef: true,
listener: true,
condition: !!currentUID,
refreshArray: [currentUID],
});
// Subscribe to user's projects (musics)
const { data: userProjects = [] } = useDataFromRef({
ref: currentUID
? projectsRef
.where("userId", "==", currentUID)
.orderBy("updatedAt", "desc")
: null,
simpleRef: false,
listener: true,
condition: !!currentUID,
refreshArray: [currentUID],
});
const userPlaybacks = Array.isArray(userProjects)
? userProjects.filter((project) => project.playbackUrl)
: [];
// Subscribe to user's liked projects
const { data: userLikedProjects = [] } = useDataFromRef({
ref: currentUID
? projectsRef
.where("likedBy", "array-contains", currentUID)
.orderBy("updatedAt", "desc")
: null,
simpleRef: false,
listener: true,
condition: !!currentUID,
refreshArray: [currentUID],
});
const userLikedPlaybacks = Array.isArray(userLikedProjects)
? userLikedProjects.filter((project) => project.playbackUrl)
: [];
// Subscribe to user's playlists
const { data: userPlaylists = [] } = useDataFromRef({
ref: currentUID ? playlistsRef.where("createdBy", "==", currentUID) : null,
simpleRef: false,
listener: true,
condition: !!currentUID,
refreshArray: [currentUID],
});
// Live list of users that the current user is following ("abonnements")
const { data: userFollowing = [] } = useDataFromRef({
ref: currentUID
? usersRef.where("followedBy", "array-contains", currentUID)
: null,
simpleRef: false,
listener: true,
condition: !!currentUID,
refreshArray: [currentUID],
});
const updatePendingUserData = (newData) => {
setPendingUserData((prevData) => ({
...prevData,
...newData,
}));
};
// Selected project state and helpers
const [selectedProjectId, setSelectedProjectId] = useState(null);
const { data: selectedProject = null, setData: setSelectedProject } =
useDataFromRef({
ref: selectedProjectId ? projectsRef.doc(selectedProjectId) : null,
simpleRef: true,
listener: true,
condition: !!selectedProjectId,
refreshArray: [selectedProjectId],
});
const persistSelectedProjectId = useCallback(
async (projectId) => {
if (!currentUID) return;
try {
await usersRef.doc(currentUID).set(
{
selectedProjectId: projectId || null,
},
{ merge: true },
);
console.log("update selected project");
} catch (error) {
console.log(
"UserDataProvider: unable to persist selectedProjectId",
error?.message || error,
);
}
},
[currentUID],
);
const selectProject = useCallback(
(projectId) => {
const safeId = projectId || null;
setSelectedProjectId(safeId);
if (!safeId) {
setSelectedProject(null);
}
if (currentUID) {
persistSelectedProjectId(safeId).catch((error) => {
console.log(
"UserDataProvider: persist selectedProjectId failed",
error?.message || error,
);
});
}
},
[currentUID, persistSelectedProjectId, setSelectedProject],
);
const resetSelectedProject = useCallback(() => {
selectProject(null);
}, [selectProject]);
// Ancienne variante de création de projet supprimée pour éviter les doublons.
const updateProjectData = async (partial = {}, options = { merge: true }) => {
if (!selectedProjectId) return;
try {
await projectsRef.doc(selectedProjectId).set(
{
...partial,
updatedAt: firebase.firestore.FieldValue.serverTimestamp(),
},
options,
);
} catch (e) {
console.log("updateProjectData error", e?.message);
throw e;
}
};
const createNewProject = async ({ hasLyrics = false } = {}) => {
try {
await setIsLoading(true);
const user = firebase.auth().currentUser;
const payload = {
userId: user ? user.uid : currentUID || null,
hasLyrics: !!hasLyrics,
createdAt: firebase.firestore.FieldValue.serverTimestamp(),
updatedAt: firebase.firestore.FieldValue.serverTimestamp(),
};
const { id } = await projectsRef.add(payload);
selectProject(id);
return id;
} catch (e) {
console.log("createNewProject error", e?.message);
throw e;
} finally {
await setIsLoading(false);
}
};
const followUser = async (userId) => {
try {
if (currentUID && userId) {
await usersRef.doc(userId).set(
{
followedBy: arrayUnion(currentUID),
lastFollowersUpdateAt: new Date(),
},
{ merge: true },
);
setTooltip({ type: "success", text: "Abonnement mis à jour" });
}
} catch (e) {
console.log(e);
setTooltip({ type: "error", text: e?.message || "Action impossible" });
}
};
const unfollowUser = async (userId) => {
try {
if (currentUID && userId) {
await usersRef.doc(userId).set(
{
followedBy: arrayRemove(currentUID),
lastFollowersUpdateAt: new Date(),
},
{ merge: true },
);
setTooltip({
type: "success",
text: "Vous ne suivez plus cet utilisateur",
});
}
} catch (e) {
console.log(e);
setTooltip({ type: "error", text: e?.message || "Action impossible" });
}
};
const getUserByUid = async (uid) => {
try {
const userDoc = await usersRef.doc(uid).get();
return userDoc.exists
? {
id: userDoc.id,
...userDoc.data(),
}
: null;
} catch (e) {
console.log(e);
return null;
}
};
const updateUserData = async ({ data, shouldSetTooltip = true }) => {
try {
await setIsLoading(true);
let dynamicUID = currentUID || null;
let cleanData = {
...currentUserData,
...data,
lastUpdateTimestamp: new Date(),
};
// if (cleanData?.firstName?.length > 2 && cleanData?.lastName?.length > 2) {
// cleanData = {
// ...cleanData,
// firstName: capitalize(cleanData?.firstName?.toLowerCase()),
// lastName: capitalize(cleanData?.lastName?.toLowerCase()),
// };
// } else {
// throw new Error("Veuillez renseigner un prénom et un nom");
// }
if (!checkIfEmailIsValid({ email: cleanData?.email })) {
throw new Error("Veuillez renseigner un email valide");
}
// let cleanPhoneNumber = formatPhoneNumber({
// phoneNumber: cleanData.phoneNumber,
// });
// if (cleanPhoneNumber) {
// cleanData.phoneNumber = cleanPhoneNumber;
// } else {
// throw new Error("Veuillez renseigner un numéro de téléphone valide");
// }
if (!dynamicUID) {
const { email, password } = cleanData;
const { user = null } = await firebase
.auth()
.createUserWithEmailAndPassword(email, password);
dynamicUID = user.uid;
}
delete cleanData.password;
cleanData = {
...cleanData,
userID: dynamicUID,
};
if (!cleanData?.creationTimestamp) {
cleanData.creationTimestamp = new Date();
}
await usersRef.doc(dynamicUID).set(cleanData, { merge: true });
if (shouldSetTooltip) {
setTooltip({
type: "success",
text: "Données enregistrées avec succès",
});
}
} catch (e) {
console.log(e);
setTooltip({
type: "error",
text: "Erreur lors de la mise à jour des données utilisateur",
});
throw e;
} finally {
await setIsLoading(false);
}
};
const onSignOut = async () => {
try {
setIsLoading(true);
await firebase.auth().signOut();
} catch (e) {
console.log(e);
} finally {
setIsLoading(false);
}
};
const isSuperAdmin = currentUserRoles.some((role) => role === "SUPERADMIN");
useEffect(() => {
const remoteSelectedId = currentUserDoc?.selectedProjectId;
if (!remoteSelectedId) {
if (remoteSelectedId === null && selectedProjectId !== null) {
setSelectedProjectId(null);
setSelectedProject(null);
}
return;
}
if (remoteSelectedId !== selectedProjectId) {
setSelectedProjectId(remoteSelectedId);
}
}, [
currentUserDoc?.selectedProjectId,
selectedProjectId,
setSelectedProject,
]);
useEffect(() => {
if (!Array.isArray(userProjects) || userProjects.length === 0) return;
if (selectedProjectId) return;
if (currentUserDoc?.selectedProjectId) return;
const fallbackProjectId = userProjects[0]?.id;
if (!fallbackProjectId) return;
selectProject(fallbackProjectId);
}, [
currentUserDoc?.selectedProjectId,
selectProject,
selectedProjectId,
userProjects,
]);
return (
<UserDataContext.Provider
value={{
isSuperAdmin,
currentUserRoles,
currentUID,
currentUserData: currentUserDoc,
userProjects,
userPlaybacks,
userLikedProjects,
userLikedPlaybacks,
userPlaylists,
selectedProjectId,
selectedProject,
followingCount: userFollowing?.length || 0,
setCurrentUserData,
getUserByUid,
onSignOut,
pendingUserData,
updatePendingUserData,
updateUserData,
followUser,
unfollowUser,
resetSelectedProject,
selectProject,
setSelectedProjectId: selectProject,
updateProjectData,
createNewProject,
}}
>
{children}
</UserDataContext.Provider>
);
};
export const useUserData = () => {
return useContext(UserDataContext);
};
// Convenience alias returning the same context, as requested
export const useUser = () => {
return useContext(UserDataContext);
};