import React from "react"
import * as antd from "antd"
import classnames from "classnames"
import ReactMarkdown from "react-markdown"
import remarkGfm from "remark-gfm"
import fuse from "fuse.js"
import { WithPlayerContext } from "@contexts/WithPlayerContext"
import { Context as PlaylistContext } from "@contexts/WithPlaylistContext"
import useWsEvents from "@hooks/useWsEvents"
import checkUserIdIsSelf from "@utils/checkUserIdIsSelf"
import LoadMore from "@components/LoadMore"
import { Icons } from "@components/Icons"
import MusicTrack from "@components/Music/Track"
import SearchButton from "@components/SearchButton"
import ImageViewer from "@components/ImageViewer"
import MusicModel from "@models/music"
import "./index.less"
const PlaylistTypeDecorators = {
single: () => (
Single
),
album: () => (
Album
),
ep: () => (
EP
),
mix: () => (
Mix
),
}
const PlaylistInfo = (props) => {
return (
)
}
const MoreMenuHandlers = {
edit: async (playlist) => {},
delete: async (playlist) => {
return antd.Modal.confirm({
title: "Are you sure you want to delete this playlist?",
onOk: async () => {
const result = await MusicModel.deletePlaylist(
playlist._id,
).catch((err) => {
console.log(err)
app.message.error("Failed to delete playlist")
return null
})
if (result) {
app.navigation.goToMusic()
}
},
})
},
}
const PlaylistView = (props) => {
const [playlist, setPlaylist] = React.useState(props.playlist)
const [searchResults, setSearchResults] = React.useState(null)
const [owningPlaylist, setOwningPlaylist] = React.useState(
checkUserIdIsSelf(props.playlist?.user_id),
)
const moreMenuItems = React.useMemo(() => {
const items = [
{
key: "edit",
label: "Edit",
},
]
if (!playlist.type || playlist.type === "playlist") {
if (checkUserIdIsSelf(playlist.user_id)) {
items.push({
key: "delete",
label: "Delete",
})
}
}
return items
})
const contextValues = {
playlist_data: playlist,
owning_playlist: owningPlaylist,
add_track: (track) => {},
remove_track: (track) => {},
}
let debounceSearch = null
const makeSearch = (value) => {
//TODO: Implement me using API
return app.message.info("Not implemented yet...")
}
const handleOnSearchChange = (value) => {
debounceSearch = setTimeout(() => {
makeSearch(value)
}, 500)
}
const handleOnSearchEmpty = () => {
if (debounceSearch) {
clearTimeout(debounceSearch)
}
setSearchResults(null)
}
const handleOnClickPlaylistPlay = () => {
app.cores.player.start(playlist.items)
}
const handleOnClickViewDetails = () => {
app.layout.modal.open("playlist_info", PlaylistInfo, {
props: {
data: playlist,
},
})
}
const handleOnClickTrack = (track) => {
// search index of track
const index = playlist.items.findIndex((item) => {
return item._id === track._id
})
if (index === -1) {
return
}
// check if clicked track is currently playing
if (app.cores.player.state.track_manifest?._id === track._id) {
app.cores.player.playback.toggle()
} else {
app.cores.player.start(playlist.items, {
startIndex: index,
})
}
}
const handleUpdateTrackLike = (track_id, liked) => {
setPlaylist((prev) => {
const index = prev.list.findIndex((item) => {
return item._id === track_id
})
if (index !== -1) {
const newState = {
...prev,
}
newState.list[index].liked = liked
return newState
}
return prev
})
}
const handleTrackChangeState = (track_id, update) => {
setPlaylist((prev) => {
const index = prev.list.findIndex((item) => {
return item._id === track_id
})
if (index !== -1) {
const newState = {
...prev,
}
newState.list[index] = {
...newState.list[index],
...update,
}
return newState
}
return prev
})
}
const handleMoreMenuClick = async (e) => {
const handler = MoreMenuHandlers[e.key]
if (typeof handler !== "function") {
throw new Error(`Invalid menu handler [${e.key}]`)
}
return await handler(playlist)
}
useWsEvents(
{
"music:track:toggle:like": (data) => {
handleUpdateTrackLike(data.track_id, data.action === "liked")
},
},
{
socketName: "music",
},
)
React.useEffect(() => {
setPlaylist(props.playlist)
setOwningPlaylist(checkUserIdIsSelf(props.playlist?.user_id))
}, [props.playlist])
if (!playlist) {
return
}
const playlistType = playlist.type?.toLowerCase() ?? "playlist"
return (
{!props.noHeader && (
{playlist.service === "tidal" && (
)}
{typeof playlist.title ===
"function" ? (
playlist.title
) : (
{playlist.title}
)}
{playlistType &&
PlaylistTypeDecorators[
playlistType
] && (
{PlaylistTypeDecorators[
playlistType
]()}
)}
{" "}
{props.length ??
playlist.total_length ??
playlist.items.length}{" "}
Items
{playlist.publisher && (
)}
Play
{playlist.description && (
}
onClick={
handleOnClickViewDetails
}
/>
)}
{owningPlaylist && (
}
/>
)}
)}
{!props.noHeader && playlist.items.length > 0 && (
Tracks
)}
{playlist.items.length === 0 && (
This playlist
its empty!
>
}
/>
)}
{searchResults &&
searchResults.map((item) => {
return (
handleOnClickTrack(item)}
changeState={(update) =>
handleTrackChangeState(
item._id,
update,
)
}
/>
)
})}
{!searchResults && playlist.items.length > 0 && (
}
onBottom={props.onLoadMore}
hasMore={props.hasMore}
>
{playlist.items.map((item, index) => {
return (
handleOnClickTrack(item)
}
changeState={(update) =>
handleTrackChangeState(
item._id,
update,
)
}
/>
)
})}
)}
)
}
export default PlaylistView