diff --git a/src/components/modal/PlaylistModal.js b/src/components/modal/PlaylistModal.js index b3f18e5..2a9b1e2 100644 --- a/src/components/modal/PlaylistModal.js +++ b/src/components/modal/PlaylistModal.js @@ -1,31 +1,34 @@ -import { - View, - Text, - FlatList, - Pressable, - Image, - Dimensions, - TextInput, -} from "react-native"; import React, { useRef, useState } from "react"; -import AppActionSheet from "../AppActionSheet"; -import AppCheckbox from "../AppCheckbox"; +import { + Dimensions, + FlatList, + Image, + Pressable, + Text, + TextInput, + View, +} from "react-native"; +import { SheetManager } from "react-native-actions-sheet"; +import SwiperFlatList from "react-native-swiper-flatlist"; +import { icons } from "../../assets"; +import { useUserData } from "../../providers/UserDataProvider"; +import { createPlaylist } from "../../screens/Library/Playlists/playlist"; import { Palette } from "../../styles"; import { FONT_FAMILY } from "../../styles/Fonts"; -import { icons } from "../../assets"; import Style, { size } from "../../styles/Style"; -import SwiperFlatList from "react-native-swiper-flatlist"; +import AppActionSheet from "../AppActionSheet"; +import AppCheckbox from "../AppCheckbox"; import GradientButton from "../GradientButton"; -import { SheetManager } from "react-native-actions-sheet"; - -const PLAYLIST = ["Ménage", "Voiture", "Roadtrip"]; const { width } = Dimensions.get("window"); -const PlaylistModal = () => { +const PlaylistModal = (props) => { const scrollRef = useRef(null); - const [selected, setSelected] = useState(""); + const [selectedId, setSelectedId] = useState(""); const [playlist, setPlaylist] = useState(""); + const [isCreating, setIsCreating] = useState(false); + const { currentUID, userPlaylists = [] } = useUserData(); + const startAtCreate = props?.payload?.startAtCreate || false; return ( @@ -33,6 +36,7 @@ const PlaylistModal = () => { style={{ width, left: -14 }} ref={scrollRef} disableGesture + index={startAtCreate ? 1 : 0} > @@ -47,7 +51,7 @@ const PlaylistModal = () => { Ajouter ce titre à une playlist { } renderItem={({ item }) => ( setSelected(item)} - selected={selected === item} + label={item?.name || "Sans nom"} + onPress={() => setSelectedId(item?.id)} + selected={selectedId === item?.id} /> )} /> @@ -94,25 +98,27 @@ const PlaylistModal = () => { - { - scrollRef.current.scrollToIndex({ - index: 0, - animated: true, - }); - setPlaylist(""); - }} - > - { + scrollRef.current.scrollToIndex({ + index: 0, + animated: true, + }); + setPlaylist(""); }} > - Annuler - - + + Annuler + + + )} { /> { - scrollRef.current.scrollToIndex({ - index: 0, - animated: true, - }); - setPlaylist(""); + disabled={!playlist?.trim()?.length || isCreating} + onPress={async () => { + if (!playlist?.trim()?.length || !currentUID) return; + try { + setIsCreating(true); + await createPlaylist({ + createdBy: currentUID, + name: playlist.trim(), + musics: [], + createdAt: new Date(), + updatedAt: new Date(), + }); + setPlaylist(""); + SheetManager.hide("Playlist"); + scrollRef.current.scrollToIndex({ index: 0, animated: true }); + } catch (e) { + console.log(e); + } finally { + setIsCreating(false); + } }} /> diff --git a/src/config/firebase.js b/src/config/firebase.js index d00d90d..08e018d 100644 --- a/src/config/firebase.js +++ b/src/config/firebase.js @@ -1,14 +1,14 @@ -import firebase from "firebase/compat/app"; -import { - initializeAuth, - getReactNativePersistence, -} from "firebase/auth/react-native"; import AsyncStorage from "@react-native-async-storage/async-storage"; +import { + getReactNativePersistence, + initializeAuth, +} from "firebase/auth/react-native"; +import firebase from "firebase/compat/app"; import "firebase/compat/auth"; -import "firebase/compat/storage"; -import "firebase/compat/functions"; import "firebase/compat/firestore"; +import "firebase/compat/functions"; +import "firebase/compat/storage"; export const firebaseConfig = { apiKey: "AIzaSyCuHJHdwVN_F-VmUG4Hd7bGiMRqj6rPlLo", @@ -48,6 +48,7 @@ try { export const usersRef = firestore.collection("users"); export const projectsRef = firestore.collection("projects"); +export const playlistsRef = firestore.collection("playlists"); export const notificationsRef = firestore.collection("notifications"); export const documentsRef = firestore.collection("documents"); export const chatsRef = firestore.collection("chats"); diff --git a/src/hooks/useDataFromArrayId.js b/src/hooks/useDataFromArrayId.js new file mode 100644 index 0000000..59dea18 --- /dev/null +++ b/src/hooks/useDataFromArrayId.js @@ -0,0 +1,77 @@ +import { useEffect, useState } from "react"; + +export default function useDataFromArrayDocId({ + ref, + format = null, + arrayId = [], + refreshArray = [], + condition = true, + pagination = false, + batchSize = 20, +}) { + const [data, setData] = useState([]); + const [loading, setLoading] = useState(false); + const [currentPage, setCurrentPage] = useState(0); + + async function getAllDocs(arrayToGet) { + try { + setLoading(true); + const promises = arrayToGet.map(async (id) => { + return new Promise(async (resolve) => { + const doc = await ref.doc(id).get(); + if (doc.exists) { + resolve({ ...doc.data(), id: doc.id }); + } else { + resolve(null); + } + }); + }); + await Promise.all(promises).then(async (values) => { + const validData = values.filter((value) => value !== null); + const newData = format ? await format(validData) : validData; + if (currentPage > 0) { + setData((prev) => [...prev, ...newData]); + } else { + setData(newData); + } + if (pagination && newData.length > 0) { + setCurrentPage((prev) => prev + 1); + } + }); + } catch (e) { + console.log(e); + } finally { + setLoading(false); + } + } + + function getNewArrayToGet(currPage = 0) { + const start = currPage * batchSize; + const end = (currPage + 1) * batchSize; + const newArray = arrayId.slice(start, end); + return newArray.filter(Boolean); + } + + useEffect(() => { + if (ref && arrayId?.length > 0 && !!condition && !loading) { + if (pagination) { + setCurrentPage(0); + getAllDocs(getNewArrayToGet(0)); + } else { + getAllDocs(arrayId); + } + } else { + if (data.length > 0) { + setData([]); + } + } + }, [ref, arrayId?.length, condition, ...refreshArray]); + + async function loadMore() { + if (ref && arrayId?.length > 0 && !!condition && !loading) { + getAllDocs(getNewArrayToGet(currentPage)); + } + } + + return { data, setData, loading, loadMore }; +} diff --git a/src/providers/UserDataProvider.js b/src/providers/UserDataProvider.js index 4ab560b..ad3ced0 100644 --- a/src/providers/UserDataProvider.js +++ b/src/providers/UserDataProvider.js @@ -5,7 +5,11 @@ import { createContext, useContext, useGlobal } from "reactn"; import { useState } from "react"; import { checkIfEmailIsValid } from "../actions/signupActions"; -import firebase, { usersRef } from "../config/firebase"; +import firebase, { + playlistsRef, + projectsRef, + usersRef, +} from "../config/firebase"; export const UserDataContext = createContext(); @@ -21,7 +25,8 @@ export default ({ children }) => { const [currentUserRoles] = useGlobal("currentUserRoles"); const [userProjects, setUserProjects] = useState([]); const [userLikedProjects, setUserLikedProjects] = useState([]); - const [userLikedMusic, setUserLikedMusic] = useState([]); + const [userPlaylists, setUserPlaylists] = useState([]); + useDataFromRef({ ref: currentUID ? usersRef.doc(currentUID) : null, simpleRef: true, @@ -40,9 +45,7 @@ export default ({ children }) => { // Subscribe to user's projects (musics) useDataFromRef({ ref: currentUID - ? firebase - .firestore() - .collection("projects") + ? projectsRef .where("userId", "==", currentUID) .orderBy("updatedAt", "desc") : null, @@ -56,9 +59,7 @@ export default ({ children }) => { // Subscribe to user's liked projects useDataFromRef({ ref: currentUID - ? firebase - .firestore() - .collection("projects") + ? projectsRef .where("likedBy", "array-contains", currentUID) .orderBy("updatedAt", "desc") : null, @@ -69,20 +70,14 @@ export default ({ children }) => { onUpdate: (list) => setUserLikedProjects(Array.isArray(list) ? list : []), }); - // userLiked music + // Subscribe to user's playlists useDataFromRef({ - ref: currentUID - ? firebase - .firestore() - .collection("projects") - .where("likedBy", "array-contains", currentUID) - .orderBy("updatedAt", "desc") - : null, + ref: currentUID ? playlistsRef.where("createdBy", "==", currentUID) : null, simpleRef: false, listener: true, condition: !!currentUID, refreshArray: [currentUID], - onUpdate: (list) => setUserLikedMusic(Array.isArray(list) ? list : []), + onUpdate: (list) => setUserPlaylists(Array.isArray(list) ? list : []), }); const updatePendingUserData = (newData) => { @@ -149,8 +144,6 @@ export default ({ children }) => { cleanData.creationTimestamp = new Date(); } - console.log("cleanData", cleanData); - await usersRef.doc(dynamicUID).set(cleanData, { merge: true }); if (shouldSetTooltip) { @@ -197,6 +190,7 @@ export default ({ children }) => { currentUserData, userProjects, userLikedProjects, + userPlaylists, setCurrentUserData, diff --git a/src/screens/Library/AllMyPlaylist.js b/src/screens/Library/AllMyPlaylist.js index 1ff19f3..108da33 100644 --- a/src/screens/Library/AllMyPlaylist.js +++ b/src/screens/Library/AllMyPlaylist.js @@ -1,31 +1,44 @@ -import { View, Text, Image, Pressable, Platform } from "react-native"; -import React from "react"; -import Page from "../../layouts/Page"; -import { background, icons } from "../../assets"; -import { gutters, Palette, Style } from "../../styles"; import { BlurView } from "expo-blur"; +import React from "react"; +import { Image, Platform, Pressable, Text, View } from "react-native"; +import { SheetManager } from "react-native-actions-sheet"; +import { background, icons } from "../../assets"; +import Page from "../../layouts/Page"; +import { gutters, Palette, Style } from "../../styles"; import { FONT_FAMILY } from "../../styles/Fonts"; import { size } from "../../styles/Style"; - +import { useUser } from "../../providers/UserDataProvider"; const AllMyPlaylist = () => { + const { userPlaylists = [] } = useUser(); return ( ( + + SheetManager.show("Playlist", { payload: { startAtCreate: true } }) + } + > + + + )} contentContainerStyle={{ paddingBottom: gutters * 2, }} > - {[ - "Musiques de lover", - "Musiques triste", - "Happy new year", - "Happy songs", - ].map((item, index) => ( - + {(Array.isArray(userPlaylists) ? userPlaylists : []).map((item) => ( + { fontFamily: FONT_FAMILY.InterRegular, }} > - {item} + {item?.name || "Sans nom"} { + try { + const createdBy = payload?.createdBy; + const name = payload?.name?.trim?.(); + const musics = Array.isArray(payload?.musics) ? payload.musics : []; + + const doc = { + createdBy, + name, + musics, + createdAt: new Date(), + updatedAt: new Date(), + }; + + const docRef = await playlistsRef.add(doc); + return docRef.id; + } catch (error) { + console.error("Error creating playlist:", error); + throw error; + } +}; diff --git a/src/screens/Library/components/MusicCard.js b/src/screens/Library/components/MusicCard.js index acc3693..be6f85b 100644 --- a/src/screens/Library/components/MusicCard.js +++ b/src/screens/Library/components/MusicCard.js @@ -1,21 +1,29 @@ +import { BlurView } from "expo-blur"; +import React, { useEffect, useState } from "react"; import { - View, - Text, Image, + Platform, Pressable, StyleSheet, - Platform, + Text, + View, } from "react-native"; -import React, { useEffect, useState } from "react"; -import { Palette, Style } from "../../../styles"; -import { icons, img } from "../../../assets"; -import { size } from "../../../styles/Style"; -import { BlurView } from "expo-blur"; -import { FONT_FAMILY } from "../../../styles/Fonts"; import { useGlobal } from "reactn"; -import { projectsRef, arrayUnion, arrayRemove } from "../../../config/firebase"; +import { icons, img } from "../../../assets"; +import { arrayRemove, arrayUnion, projectsRef } from "../../../config/firebase"; +import { Palette, Style } from "../../../styles"; +import { FONT_FAMILY } from "../../../styles/Fonts"; +import { size } from "../../../styles/Style"; -const MusicCard = ({ onPress, onPressMore, title = "Sans titre", subtitle = "MusicLand", imageUri = null, projectId = null, likedBy = [] }) => { +const MusicCard = ({ + onPress, + onPressMore, + title = "Sans titre", + subtitle = "MusicLand", + imageUri = null, + projectId = null, + likedBy = [], +}) => { const [currentUID] = useGlobal("currentUID"); const [selected, setSelected] = useState(false); const [open, setOpen] = useState(false); diff --git a/src/screens/Library/components/MyMusic.js b/src/screens/Library/components/MyMusic.js index d368156..9ab04d8 100644 --- a/src/screens/Library/components/MyMusic.js +++ b/src/screens/Library/components/MyMusic.js @@ -1,17 +1,14 @@ -import { View } from "react-native"; import React from "react"; +import { View } from "react-native"; +import { Routes } from "../../../navigation"; +import { navigate } from "../../../navigation/NavigationService"; +import { useUser } from "../../../providers/UserDataProvider"; import CardContainer from "./CardContainer"; import MusicCard from "./MusicCard"; -import { navigate } from "../../../navigation/NavigationService"; -import { Routes } from "../../../navigation"; -import { useUser } from "../../../providers/UserDataProvider"; const MyMusic = () => { const { userProjects = [] } = useUser(); - const projects = Array.isArray(userProjects) - ? userProjects.slice(0, 3) - : []; - + const projects = Array.isArray(userProjects) ? userProjects.slice(0, 3) : []; return ( { projectId={p?.id} likedBy={p?.likedBy || []} onPress={() => navigate(Routes.MusicDetails, { projectId: p.id })} + onPressMore={() => console.log("More options for", p.id)} /> ))} diff --git a/src/screens/Library/components/MyPlaylist.js b/src/screens/Library/components/MyPlaylist.js index c1bc8df..e2df378 100644 --- a/src/screens/Library/components/MyPlaylist.js +++ b/src/screens/Library/components/MyPlaylist.js @@ -8,16 +8,21 @@ import { Palette } from "../../../styles"; import { FONT_FAMILY } from "../../../styles/Fonts"; import { navigate } from "../../../navigation/NavigationService"; import { Routes } from "../../../navigation"; +import { useUser } from "../../../providers/UserDataProvider"; const MyPlaylist = () => { + const { userPlaylists = [] } = useUser(); + const playlists = Array.isArray(userPlaylists) + ? userPlaylists.slice(0, 2) + : []; return ( navigate(Routes.AllMyPlaylist)} > - {["Musiques de lover", "Musiques triste"].map((item, index) => ( - + {playlists.map((item) => ( + { fontFamily: FONT_FAMILY.InterRegular, }} > - {item} + {item?.name || "Sans nom"}