Refactor Track menu logic into separate handlers and items files

This commit is contained in:
srgooglo 2025-07-04 14:10:31 +02:00
parent d1b58d19fc
commit 843405dd15
4 changed files with 164 additions and 159 deletions

View File

@ -1,9 +1,10 @@
import React from "react"
import * as antd from "antd"
import classnames from "classnames"
import { WithPlayerContext } from "@contexts/WithPlayerContext"
import { Context as PlaylistContext } from "@contexts/WithPlaylistContext"
import {
WithPlayerContext,
usePlayerStateContext,
} from "@contexts/WithPlayerContext"
import LoadMore from "@components/LoadMore"
import { Icons } from "@components/Icons"
@ -24,6 +25,8 @@ const TrackList = ({
hasMore,
noHeader = false,
}) => {
const [{ track_manifest, playback_status }] = usePlayerStateContext()
const showListHeader = !noHeader && (tracks.length > 0 || searchResults)
if (!searchResults && tracks.length === 0) {
@ -62,7 +65,7 @@ const TrackList = ({
key={item._id}
order={item._id} // Consider using index if order matters
track={item}
onPlay={() => onTrackClick(item)}
onPlay={onTrackClick}
changeState={(update) =>
onTrackStateChange(item._id, update)
}
@ -76,19 +79,19 @@ const TrackList = ({
onBottom={onLoadMore}
hasMore={hasMore}
>
<WithPlayerContext>
{tracks.map((item, index) => (
<MusicTrack
key={item._id} // Use unique ID for key
order={index + 1}
track={item}
onPlay={() => onTrackClick(item)}
changeState={(update) =>
onTrackStateChange(item._id, update)
}
/>
))}
</WithPlayerContext>
{tracks.map((item, index) => (
<MusicTrack
key={item._id}
order={index + 1}
track={item}
onPlay={onTrackClick}
isCurrent={item._id === track_manifest?._id}
isPlaying={
item._id === track_manifest?._id &&
playback_status === "playing"
}
/>
))}
</LoadMore>
)}
</div>

View File

@ -2,14 +2,13 @@ import React from "react"
import * as antd from "antd"
import classnames from "classnames"
import RGBStringToValues from "@utils/rgbToValues"
import ImageViewer from "@components/ImageViewer"
import { Icons } from "@components/Icons"
import MusicModel from "@models/music"
import MenuItemsBase from "./menuItems"
import MenuHandlers from "./menuHandlers"
import { usePlayerStateContext } from "@contexts/WithPlayerContext"
import RGBStringToValues from "@utils/rgbToValues"
import { Context as PlaylistContext } from "@contexts/WithPlaylistContext"
import "./index.less"
@ -22,128 +21,18 @@ function secondsToIsoTime(seconds) {
.padStart(2, "0")}`
}
const handlers = {
like: async (ctx, track) => {
await MusicModel.toggleItemFavourite("track", track._id, true)
ctx.changeState({
liked: true,
})
ctx.closeMenu()
},
unlike: async (ctx, track) => {
await MusicModel.toggleItemFavourite("track", track._id, false)
ctx.changeState({
liked: false,
})
ctx.closeMenu()
},
add_to_playlist: async (ctx, track) => {},
add_to_queue: async (ctx, track) => {
await app.cores.player.queue.add(track)
},
play_next: async (ctx, track) => {
await app.cores.player.queue.add(track, { next: true })
},
}
const Track = (props) => {
const [{ loading, track_manifest, playback_status }] =
usePlayerStateContext()
const Track = React.memo((props) => {
const playlist_ctx = React.useContext(PlaylistContext)
const [moreMenuOpened, setMoreMenuOpened] = React.useState(false)
const [liked, setLiked] = React.useState(props.track.liked)
const isCurrent = track_manifest?._id === props.track._id
const isPlaying = isCurrent && playback_status === "playing"
const trackDuration = React.useMemo(() => {
return props.track?.metadata?.duration ?? props.track?.duration
}, [props.track])
const handleClickPlayBtn = () => {
if (typeof props.onPlay === "function") {
return props.onPlay(props.track)
}
if (typeof props.onClickPlayBtn === "function") {
props.onClickPlayBtn(props.track)
}
if (!isCurrent) {
app.cores.player.start(props.track)
} else {
app.cores.player.playback.toggle()
}
}
const handleOnClickItem = React.useCallback(() => {
if (props.onClick) {
props.onClick(props.track)
}
if (app.isMobile) {
handleClickPlayBtn()
}
}, [])
const handleMoreMenuOpen = React.useCallback(() => {
if (app.isMobile) {
return
}
return setMoreMenuOpened((prev) => {
return !prev
})
}, [])
const handleMoreMenuItemClick = React.useCallback((e) => {
const { key } = e
if (typeof handlers[key] === "function") {
return handlers[key](
{
closeMenu: () => {
setMoreMenuOpened(false)
},
changeState: props.changeState,
},
props.track,
)
}
}, [])
const moreMenuItems = React.useMemo(() => {
const items = [
{
key: "like",
icon: <Icons.MdFavorite />,
label: "Like",
},
{
key: "share",
icon: <Icons.MdShare />,
label: "Share",
disabled: true,
},
{
key: "add_to_playlist",
icon: <Icons.MdPlaylistAdd />,
label: "Add to playlist",
disabled: true,
},
{
type: "divider",
},
{
key: "add_to_queue",
icon: <Icons.MdQueueMusic />,
label: "Add to queue",
},
{
key: "play_next",
icon: <Icons.MdSkipNext />,
label: "Play next",
},
]
const menuItems = React.useMemo(() => {
const items = [...MenuItemsBase]
if (props.track.liked) {
items[0] = {
@ -170,22 +59,68 @@ const Track = (props) => {
return items
}, [props.track])
const trackDuration =
props.track?.metadata?.duration ?? props.track?.duration
const handleClickPlayBtn = React.useCallback(() => {
if (typeof props.onPlay === "function") {
return props.onPlay(props.track)
}
if (typeof props.onClickPlayBtn === "function") {
props.onClickPlayBtn(props.track)
}
if (!props.isCurrent) {
app.cores.player.start(props.track)
} else {
app.cores.player.playback.toggle()
}
}, [props.isCurrent])
const handleOnClickItem = React.useCallback(() => {
if (props.onClick) {
props.onClick(props.track)
}
if (app.isMobile) {
handleClickPlayBtn()
}
}, [props.track])
const handleMoreMenuOpen = React.useCallback(() => {
if (app.isMobile) {
return
}
return setMoreMenuOpened((prev) => {
return !prev
})
}, [])
const handleMoreMenuItemClick = React.useCallback(
(e) => {
const { key } = e
if (typeof MenuHandlers[key] === "function") {
return MenuHandlers[key](
{
close: () => {
setMoreMenuOpened(false)
},
setLiked: setLiked,
},
props.track,
)
}
},
[props.track],
)
return (
<div
id={props.track._id}
className={classnames("music-track", {
["current"]: isCurrent,
["playing"]: isPlaying,
["loading"]: isCurrent && loading,
["current"]: props.isCurrent,
["playing"]: props.isPlaying,
})}
style={{
"--cover_average-color": RGBStringToValues(
track_manifest?.cover_analysis?.rgb,
),
}}
>
<div className="music-track_background" />
@ -204,7 +139,7 @@ const Track = (props) => {
type="primary"
shape="circle"
icon={
isPlaying ? (
props.isPlaying ? (
<Icons.MdPause />
) : (
<Icons.MdPlayArrow />
@ -212,14 +147,6 @@ const Track = (props) => {
}
onClick={handleClickPlayBtn}
/>
{/* {props.track?.metadata?.duration && (
<div className="music-track_play_duration">
{secondsToIsoTime(
props.track.metadata.duration,
)}
</div>
)} */}
</div>
)}
@ -267,7 +194,7 @@ const Track = (props) => {
<antd.Dropdown
menu={{
items: moreMenuItems,
items: menuItems,
onClick: handleMoreMenuItemClick,
}}
onOpenChange={handleMoreMenuOpen}
@ -281,6 +208,8 @@ const Track = (props) => {
</div>
</div>
)
}
})
Track.displayName = "Track"
export default Track

View File

@ -0,0 +1,31 @@
import MusicModel from "@models/music"
export default {
like: async (ctx, track) => {
await MusicModel.toggleItemFavourite("track", track._id, true)
ctx.changeState({
liked: true,
})
ctx.close()
},
unlike: async (ctx, track) => {
await MusicModel.toggleItemFavourite("track", track._id, false)
ctx.changeState({
liked: false,
})
ctx.close()
},
add_to_playlist: async (ctx, track) => {},
add_to_queue: async (ctx, track) => {
await app.cores.player.queue.add(track)
},
play_next: async (ctx, track) => {
await app.cores.player.queue.add(track, { next: true })
},
copy_id: (ctx, track) => {
console.log("copy_id", track)
navigator.clipboard.writeText(track._id)
},
}

View File

@ -0,0 +1,42 @@
import { Icons } from "@components/Icons"
export default [
{
key: "like",
icon: <Icons.MdFavorite />,
label: "Like",
},
{
key: "share",
icon: <Icons.MdShare />,
label: "Share",
disabled: true,
},
{
key: "add_to_playlist",
icon: <Icons.MdPlaylistAdd />,
label: "Add to playlist",
disabled: true,
},
{
type: "divider",
},
{
key: "add_to_queue",
icon: <Icons.MdQueueMusic />,
label: "Add to queue",
},
{
key: "play_next",
icon: <Icons.MdSkipNext />,
label: "Play next",
},
{
type: "divider",
},
{
key: "copy_id",
icon: <Icons.MdLink />,
label: "Copy ID",
},
]