playlists

This commit is contained in:
2025-08-29 11:38:17 +02:00
parent d857be1424
commit e0146321b8
9 changed files with 248 additions and 108 deletions
+67 -46
View File
@@ -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 (
<AppActionSheet id="Playlist">
@@ -33,6 +36,7 @@ const PlaylistModal = () => {
style={{ width, left: -14 }}
ref={scrollRef}
disableGesture
index={startAtCreate ? 1 : 0}
>
<View style={{ width, paddingHorizontal: 14 }}>
<View style={{ gap: 20 }}>
@@ -47,7 +51,7 @@ const PlaylistModal = () => {
Ajouter ce titre à une playlist
</Text>
<FlatList
data={PLAYLIST}
data={Array.isArray(userPlaylists) ? userPlaylists : []}
contentContainerStyle={{ gap: 20 }}
ListHeaderComponent={
<Pressable
@@ -76,9 +80,9 @@ const PlaylistModal = () => {
}
renderItem={({ item }) => (
<AppCheckbox
label={item}
onPress={() => setSelected(item)}
selected={selected === item}
label={item?.name || "Sans nom"}
onPress={() => setSelectedId(item?.id)}
selected={selectedId === item?.id}
/>
)}
/>
@@ -94,25 +98,27 @@ const PlaylistModal = () => {
</View>
<View style={{ width, paddingHorizontal: 14 }}>
<View style={{ gap: 20 }}>
<Pressable
onPress={() => {
scrollRef.current.scrollToIndex({
index: 0,
animated: true,
});
setPlaylist("");
}}
>
<Text
style={{
fontSize: 12,
color: Palette.gray,
fontFamily: FONT_FAMILY.HelveticaNeueRegular,
{!startAtCreate && (
<Pressable
onPress={() => {
scrollRef.current.scrollToIndex({
index: 0,
animated: true,
});
setPlaylist("");
}}
>
Annuler
</Text>
</Pressable>
<Text
style={{
fontSize: 12,
color: Palette.gray,
fontFamily: FONT_FAMILY.HelveticaNeueRegular,
}}
>
Annuler
</Text>
</Pressable>
)}
<View style={{ ...Style.containerCenter, gap: 14 }}>
<Text
style={{
@@ -140,17 +146,32 @@ const PlaylistModal = () => {
/>
</View>
<GradientButton
title="Créer la playlist"
title={isCreating ? "Création..." : "Créer la playlist"}
containerStyle={{
width: "80%",
alignSelf: "center",
opacity: playlist?.trim()?.length ? 1 : 0.5,
}}
onPress={() => {
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);
}
}}
/>
</View>
+8 -7
View File
@@ -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");
+77
View File
@@ -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 };
}
+13 -19
View File
@@ -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,
+27 -14
View File
@@ -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 (
<Page
headerType="NAVIGATION"
backgroundImg={background.libraryBG}
title="Mes Playlists"
rightComponent={() => (
<Pressable
onPress={() =>
SheetManager.show("Playlist", { payload: { startAtCreate: true } })
}
>
<Image
source={icons.add}
style={size({ size: 18 })}
resizeMode="contain"
/>
</Pressable>
)}
contentContainerStyle={{
paddingBottom: gutters * 2,
}}
>
<View style={{ flex: 1 }}>
<View style={{ gap: 8 }}>
{[
"Musiques de lover",
"Musiques triste",
"Happy new year",
"Happy songs",
].map((item, index) => (
<View style={{ borderRadius: 12, overflow: "hidden" }} key={index}>
{(Array.isArray(userPlaylists) ? userPlaylists : []).map((item) => (
<View
style={{ borderRadius: 12, overflow: "hidden" }}
key={item?.id}
>
<BlurView
intensity={20}
style={{
@@ -44,7 +57,7 @@ const AllMyPlaylist = () => {
fontFamily: FONT_FAMILY.InterRegular,
}}
>
{item}
{item?.name || "Sans nom"}
</Text>
<Pressable>
<Image
+23
View File
@@ -0,0 +1,23 @@
import { playlistsRef } from "../../../config/firebase";
export const createPlaylist = async (payload = {}) => {
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;
}
};
+19 -11
View File
@@ -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);
+6 -8
View File
@@ -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 (
<CardContainer
label="Mes musiques"
@@ -28,6 +25,7 @@ const MyMusic = () => {
projectId={p?.id}
likedBy={p?.likedBy || []}
onPress={() => navigate(Routes.MusicDetails, { projectId: p.id })}
onPressMore={() => console.log("More options for", p.id)}
/>
))}
</View>
+8 -3
View File
@@ -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 (
<CardContainer
label="Mes Playlists"
onPress={() => navigate(Routes.AllMyPlaylist)}
>
<View style={{ gap: 8 }}>
{["Musiques de lover", "Musiques triste"].map((item, index) => (
<View style={{ borderRadius: 12, overflow: "hidden" }} key={index}>
{playlists.map((item) => (
<View style={{ borderRadius: 12, overflow: "hidden" }} key={item.id}>
<BlurView
intensity={20}
style={{
@@ -36,7 +41,7 @@ const MyPlaylist = () => {
fontFamily: FONT_FAMILY.InterRegular,
}}
>
{item}
{item?.name || "Sans nom"}
</Text>
<Pressable>
<Image