Files
musicland/src/screens/Library/PlaylistDetails.js
T
2026-03-12 10:58:43 +01:00

217 lines
7.0 KiB
JavaScript

import { BlurView } from 'expo-blur'
import { useRoute } from '@react-navigation/native'
import React, { useCallback, useMemo, useState } from 'react'
import { Image, Pressable, Text, View } from 'react-native'
import { SheetManager } from 'react-native-actions-sheet'
import { useDataFromRef } from 'react-native-minuit/src/hooks'
import useDataFromArrayId from 'react-native-minuit/src/hooks/useDataFromArrayId'
import { useGlobal } from 'reactn'
import { background, icons } from '../../assets'
import MoreMenu from '../../components/MoreMenu'
import { playlistsRef, projectsRef } from '../../config/firebase'
import useNavigateToMusicDetails from '../../hooks/useNavigateToMusicDetails'
import usePlayer from '../../hooks/usePlayer'
import Page from '../../layouts/Page'
import { goBack } from '../../navigation/NavigationService'
import { gutters, Palette } from '../../styles'
import { FONT_FAMILY } from '../../styles/Fonts'
import { getProjectLikes, LIKE_TARGET } from '../../utils/likes'
import MusicCard from './components/MusicCard'
const PlaylistDetails = () => {
const [, setTooltip] = useGlobal('_tooltip')
const route = useRoute()
const playlistId = route?.params?.playlistId
// Fetch the playlist document
const { data: playlist } = useDataFromRef({
ref: playlistId ? playlistsRef.doc(playlistId) : null,
simpleRef: true,
listener: true,
condition: !!playlistId,
refreshArray: [playlistId],
})
// Fetch projects by ids from playlist.musics
const { data: musics } = useDataFromArrayId({
ref: projectsRef,
arrayId: Array.isArray(playlist?.musics) ? playlist.musics : [],
condition: Array.isArray(playlist?.musics) && playlist.musics.length > 0,
refreshArray: [playlistId, playlist?.musics?.length || 0],
})
const [menuPosition, setMenuPosition] = useState(null)
const [showMenu, setShowMenu] = useState(false)
const [selectedProjectId, setSelectedProjectId] = useState(null)
const navigateToMusicDetails = useNavigateToMusicDetails()
const { setQueue } = usePlayer() || {}
const playlistQueueItems = useMemo(() => {
if (!Array.isArray(musics)) return []
return musics
.map((project) => {
const projectId =
typeof project?.id === 'string' ? project.id : (project?.projectId ?? null)
const songUrl =
typeof project?.songUrl === 'string' && project.songUrl.length > 0
? project.songUrl
: null
if (!songUrl) return null
const descriptorId = projectId ? `project-${projectId}` : songUrl
return {
id: descriptorId,
uri: songUrl,
songUrl,
title: project?.title || 'Sans titre',
artist: project?.userName || '',
artwork: project?.coverUrl ?? null,
coverUrl: project?.coverUrl ?? null,
metadata: {
projectId,
playlistId: playlistId ?? null,
playlistName: playlist?.name ?? null,
},
context: {
projectId,
playlistId: playlistId ?? null,
playlistName: playlist?.name ?? null,
},
}
})
.filter(Boolean)
}, [musics, playlist?.name, playlistId])
const handlePlayFromPlaylist = useCallback(
(project) => {
if (!project?.id) return
if (Array.isArray(playlistQueueItems) && playlistQueueItems.length > 0) {
const queueIndex = playlistQueueItems.findIndex(
(item) => item?.metadata?.projectId === project.id
)
if (queueIndex >= 0 && typeof setQueue === 'function') {
setQueue({
items: playlistQueueItems,
id: playlistId,
type: 'playlist',
name: playlist?.name ?? null,
index: queueIndex,
})
}
}
navigateToMusicDetails({
projectId: project.id,
songUrl: project?.songUrl,
project,
})
},
[navigateToMusicDetails, playlist?.name, playlistId, playlistQueueItems, setQueue]
)
const confirmDelete = () => {
SheetManager.show('Delete', {
payload: {
title: 'Supprimer la playlist',
message: 'Es-tu sûr de vouloir supprimer cette playlist ?',
onConfirm: async () => {
try {
if (playlistId) {
await playlistsRef.doc(playlistId).delete()
setTooltip({ type: 'success', text: 'Playlist supprimée' })
}
} catch (e) {
console.log('Delete playlist error', e?.message)
setTooltip({
type: 'error',
text: e?.message || 'Suppression impossible',
})
} finally {
goBack()
}
},
},
})
}
const openAddTracks = useCallback(() => {
if (!playlistId) return
SheetManager.show('PlaylistAddTracks', {
payload: {
playlistId,
existingMusicIds: Array.isArray(playlist?.musics) ? playlist.musics : [],
},
})
}, [playlist?.musics, playlistId])
return (
<Page
headerType="NAVIGATION"
backgroundImg={background.libraryBG}
title={playlist?.name || 'Playlist'}
rightComponent={() => (
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 10 }}>
<Pressable onPress={openAddTracks}>
<BlurView intensity={20} tint="dark" style={styles.addTracksButton}>
<Text numberOfLines={1} style={styles.addTracksText}>
Ajouter des morceaux
</Text>
</BlurView>
</Pressable>
<Pressable onPress={confirmDelete}>
<Image source={icons.trash} style={{ width: 24, height: 24 }} />
</Pressable>
</View>
)}
contentContainerStyle={{ paddingBottom: gutters * 2 }}
>
<View style={{ flex: 1, gap: 10 }}>
{Array.isArray(musics) &&
musics.map((p) => (
<MusicCard
key={p.id}
title={p?.title}
subtitle={p?.userName}
imageUri={p?.coverUrl || null}
projectId={p?.id}
likedBy={getProjectLikes(p, LIKE_TARGET.SONG)}
likeTarget={LIKE_TARGET.SONG}
onPress={() => handlePlayFromPlaylist(p)}
onPressMore={(posTop) => {
setSelectedProjectId(p.id)
setMenuPosition(posTop)
setShowMenu((prev) => !prev || posTop?.top !== (menuPosition?.top ?? null))
}}
/>
))}
<MoreMenu
visible={showMenu}
top={menuPosition?.top ?? 0}
position={menuPosition}
onClose={() => setShowMenu(false)}
inPlaylist
playlistId={playlistId}
projectId={selectedProjectId}
/>
</View>
</Page>
)
}
export default PlaylistDetails
const styles = {
addTracksButton: {
paddingHorizontal: 12,
paddingVertical: 8,
borderRadius: 999,
overflow: 'hidden',
backgroundColor: Palette.glass,
},
addTracksText: {
color: Palette.white,
fontSize: 12,
fontFamily: FONT_FAMILY.InterSemiBold,
},
}