mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-09 10:34:17 +00:00
improve player data display
This commit is contained in:
parent
ac93563a5e
commit
13ab074840
@ -30,7 +30,13 @@ const EventsHandlers = {
|
||||
return app.cores.player.controls.mute("toggle")
|
||||
},
|
||||
"like": async (ctx) => {
|
||||
await app.cores.player.toggleCurrentTrackLike(!ctx.track_manifest?.liked)
|
||||
if (!ctx.track_manifest) {
|
||||
return false
|
||||
}
|
||||
|
||||
const track = app.cores.player.track()
|
||||
|
||||
return await track.manifest.serviceOperations.toggleItemFavourite("track", ctx.track_manifest._id)
|
||||
},
|
||||
}
|
||||
|
||||
@ -84,12 +90,6 @@ const Controls = (props) => {
|
||||
onClick={() => handleAction("next")}
|
||||
disabled={playerState.control_locked}
|
||||
/>
|
||||
{
|
||||
app.isMobile && <LikeButton
|
||||
onClick={() => handleAction("like")}
|
||||
liked={playerState.track_manifest?.liked}
|
||||
/>
|
||||
}
|
||||
{
|
||||
!app.isMobile && <antd.Popover
|
||||
content={React.createElement(
|
||||
@ -113,6 +113,13 @@ const Controls = (props) => {
|
||||
</button>
|
||||
</antd.Popover>
|
||||
}
|
||||
|
||||
{
|
||||
app.isMobile && <LikeButton
|
||||
liked={playerState.track_manifest?.serviceOperations.fetchLikeStatus}
|
||||
onClick={() => handleAction("like")}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
|
@ -6,12 +6,18 @@ import LikeButton from "@components/LikeButton"
|
||||
|
||||
import { usePlayerStateContext } from "@contexts/WithPlayerContext"
|
||||
|
||||
import MusicModel from "@models/music"
|
||||
|
||||
const ExtraActions = (props) => {
|
||||
const [playerState] = usePlayerStateContext()
|
||||
|
||||
const handleClickLike = async () => {
|
||||
await MusicModel.toggleItemFavourite("track", playerState.track_manifest._id)
|
||||
if (!playerState.track_manifest) {
|
||||
console.error("Cannot like a track if nothing is playing")
|
||||
return false
|
||||
}
|
||||
|
||||
const track = app.cores.player.track()
|
||||
|
||||
await track.manifest.serviceOperations.toggleItemFavourite("track", playerState.track_manifest._id)
|
||||
}
|
||||
|
||||
return <div className="extra_actions">
|
||||
@ -24,8 +30,8 @@ const ExtraActions = (props) => {
|
||||
}
|
||||
|
||||
{
|
||||
!app.isMobile && <LikeButton
|
||||
liked={playerState.track_manifest?.fetchLikeStatus}
|
||||
!app.isMobile && playerState.track_manifest?._id && <LikeButton
|
||||
liked={playerState.track_manifest?.serviceOperations.fetchLikeStatus}
|
||||
onClick={handleClickLike}
|
||||
/>
|
||||
}
|
||||
|
@ -118,6 +118,9 @@ export default class SeekBar extends React.Component {
|
||||
this.calculateTime()
|
||||
this.updateAll()
|
||||
},
|
||||
"player.durationchange": () => {
|
||||
this.calculateDuration()
|
||||
},
|
||||
}
|
||||
|
||||
tick = () => {
|
||||
|
@ -15,195 +15,215 @@ import ExtraActions from "../ExtraActions"
|
||||
import "./index.less"
|
||||
|
||||
function isOverflown(parent, element) {
|
||||
if (!parent || !element) {
|
||||
return false
|
||||
}
|
||||
if (!parent || !element) {
|
||||
return false
|
||||
}
|
||||
|
||||
const parentRect = parent.getBoundingClientRect()
|
||||
const elementRect = element.getBoundingClientRect()
|
||||
const parentRect = parent.getBoundingClientRect()
|
||||
const elementRect = element.getBoundingClientRect()
|
||||
|
||||
return elementRect.width > parentRect.width
|
||||
return elementRect.width > parentRect.width
|
||||
}
|
||||
|
||||
const Indicators = (props) => {
|
||||
const { track } = props
|
||||
|
||||
if (!track) {
|
||||
return null
|
||||
}
|
||||
|
||||
const indicators = []
|
||||
|
||||
if (track.metadata) {
|
||||
if (track.metadata.lossless) {
|
||||
indicators.push(<Icons.Lossless />)
|
||||
}
|
||||
}
|
||||
|
||||
if (indicators.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="toolbar_player_indicators_wrapper">
|
||||
<div className="toolbar_player_indicators">{indicators}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const ServiceIndicator = (props) => {
|
||||
if (!props.service) {
|
||||
return null
|
||||
}
|
||||
if (!props.service) {
|
||||
return null
|
||||
}
|
||||
|
||||
switch (props.service) {
|
||||
case "tidal": {
|
||||
return <div className="service_indicator">
|
||||
<Icons.SiTidal />
|
||||
</div>
|
||||
}
|
||||
default: {
|
||||
return null
|
||||
}
|
||||
}
|
||||
switch (props.service) {
|
||||
case "tidal": {
|
||||
return (
|
||||
<div className="service_indicator">
|
||||
<Icons.SiTidal />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
default: {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Player = (props) => {
|
||||
const [playerState] = usePlayerStateContext()
|
||||
const [playerState] = usePlayerStateContext()
|
||||
|
||||
const contentRef = React.useRef()
|
||||
const titleRef = React.useRef()
|
||||
const subtitleRef = React.useRef()
|
||||
const contentRef = React.useRef()
|
||||
const titleRef = React.useRef()
|
||||
|
||||
const [topActionsVisible, setTopActionsVisible] = React.useState(false)
|
||||
const [titleOverflown, setTitleOverflown] = React.useState(false)
|
||||
const [subtitleOverflown, setSubtitleOverflown] = React.useState(false)
|
||||
const [topActionsVisible, setTopActionsVisible] = React.useState(false)
|
||||
const [titleOverflown, setTitleOverflown] = React.useState(false)
|
||||
const [coverAnalysis, setCoverAnalysis] = React.useState(null)
|
||||
|
||||
const handleOnMouseInteraction = (e) => {
|
||||
if (e.type === "mouseenter") {
|
||||
setTopActionsVisible(true)
|
||||
} else {
|
||||
setTopActionsVisible(false)
|
||||
}
|
||||
}
|
||||
const handleOnMouseInteraction = (e) => {
|
||||
if (e.type === "mouseenter") {
|
||||
setTopActionsVisible(true)
|
||||
} else {
|
||||
setTopActionsVisible(false)
|
||||
}
|
||||
}
|
||||
|
||||
const {
|
||||
title,
|
||||
album,
|
||||
artistStr,
|
||||
liked,
|
||||
service,
|
||||
lyrics_enabled,
|
||||
cover_analysis,
|
||||
cover,
|
||||
} = playerState.track_manifest ?? {}
|
||||
const { title, artistStr, service, cover_analysis, cover } =
|
||||
playerState.track_manifest ?? {}
|
||||
|
||||
const playing = playerState.playback_status === "playing"
|
||||
const stopped = playerState.playback_status === "stopped"
|
||||
const playing = playerState.playback_status === "playing"
|
||||
const stopped = playerState.playback_status === "stopped"
|
||||
|
||||
const titleText = (!playing && stopped) ? "Stopped" : (title ?? "Untitled")
|
||||
const subtitleText = ""
|
||||
const titleText = !playing && stopped ? "Stopped" : (title ?? "Untitled")
|
||||
const subtitleText = ""
|
||||
|
||||
React.useEffect(() => {
|
||||
const titleIsOverflown = isOverflown(contentRef.current, titleRef.current)
|
||||
React.useEffect(() => {
|
||||
const titleIsOverflown = isOverflown(
|
||||
contentRef.current,
|
||||
titleRef.current,
|
||||
)
|
||||
|
||||
setTitleOverflown(titleIsOverflown)
|
||||
}, [title])
|
||||
setTitleOverflown(titleIsOverflown)
|
||||
}, [title])
|
||||
|
||||
return <div
|
||||
className={classnames(
|
||||
"toolbar_player_wrapper",
|
||||
{
|
||||
"hover": topActionsVisible,
|
||||
"minimized": playerState.minimized,
|
||||
"cover_light": cover_analysis?.isLight,
|
||||
}
|
||||
)}
|
||||
style={{
|
||||
"--cover_averageValues": RGBStringToValues(cover_analysis?.rgb),
|
||||
"--cover_isLight": cover_analysis?.isLight,
|
||||
}}
|
||||
onMouseEnter={handleOnMouseInteraction}
|
||||
onMouseLeave={handleOnMouseInteraction}
|
||||
>
|
||||
<div
|
||||
className={classnames(
|
||||
"toolbar_player_top_actions",
|
||||
)}
|
||||
>
|
||||
{
|
||||
!playerState.control_locked && <antd.Button
|
||||
icon={<Icons.MdCast />}
|
||||
shape="circle"
|
||||
React.useEffect(() => {
|
||||
const trackInstance = app.cores.player.track()
|
||||
|
||||
/>
|
||||
}
|
||||
if (playerState.track_manifest && trackInstance) {
|
||||
if (
|
||||
typeof trackInstance.manifest.analyzeCoverColor === "function"
|
||||
) {
|
||||
trackInstance.manifest
|
||||
.analyzeCoverColor()
|
||||
.then((analysis) => {
|
||||
setCoverAnalysis(analysis)
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("Failed to get cover analysis", err)
|
||||
})
|
||||
}
|
||||
}
|
||||
}, [playerState.track_manifest])
|
||||
|
||||
{
|
||||
lyrics_enabled && <antd.Button
|
||||
icon={<Icons.MdLyrics />}
|
||||
shape="circle"
|
||||
onClick={() => app.location.push("/lyrics")}
|
||||
/>
|
||||
}
|
||||
return (
|
||||
<div
|
||||
className={classnames("toolbar_player_wrapper", {
|
||||
hover: topActionsVisible,
|
||||
minimized: playerState.minimized,
|
||||
cover_light: coverAnalysis?.isLight,
|
||||
})}
|
||||
style={{
|
||||
"--cover_averageValues": RGBStringToValues(
|
||||
coverAnalysis?.rgb ?? "0,0,0",
|
||||
),
|
||||
"--cover_isLight": coverAnalysis?.isLight ?? false,
|
||||
}}
|
||||
onMouseEnter={handleOnMouseInteraction}
|
||||
onMouseLeave={handleOnMouseInteraction}
|
||||
>
|
||||
<div className={classnames("toolbar_player_top_actions")}>
|
||||
{!playerState.control_locked && (
|
||||
<antd.Button icon={<Icons.MdCast />} shape="circle" />
|
||||
)}
|
||||
|
||||
{/* <antd.Button
|
||||
<antd.Button
|
||||
icon={<Icons.MdFullscreen />}
|
||||
shape="circle"
|
||||
onClick={() => app.location.push("/lyrics")}
|
||||
/>
|
||||
|
||||
{/* <antd.Button
|
||||
icon={<Icons.MdOfflineBolt />}
|
||||
>
|
||||
HyperDrive
|
||||
</antd.Button> */}
|
||||
|
||||
<antd.Button
|
||||
icon={<Icons.FiX />}
|
||||
shape="circle"
|
||||
onClick={() => app.cores.player.close()}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className={classnames(
|
||||
"toolbar_player"
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className="toolbar_cover_background"
|
||||
style={{
|
||||
backgroundImage: `url(${cover})`
|
||||
}}
|
||||
/>
|
||||
<antd.Button
|
||||
icon={<Icons.FiX />}
|
||||
shape="circle"
|
||||
onClick={() => app.cores.player.close()}
|
||||
/>
|
||||
</div>
|
||||
<div className={classnames("toolbar_player")}>
|
||||
<div
|
||||
className="toolbar_cover_background"
|
||||
style={{
|
||||
backgroundImage: `url(${cover})`,
|
||||
}}
|
||||
/>
|
||||
|
||||
<div
|
||||
className="toolbar_player_content"
|
||||
ref={contentRef}
|
||||
>
|
||||
<div className="toolbar_player_info">
|
||||
<h1
|
||||
ref={titleRef}
|
||||
className={classnames(
|
||||
"toolbar_player_info_title",
|
||||
{
|
||||
["overflown"]: titleOverflown
|
||||
}
|
||||
)}
|
||||
>
|
||||
<ServiceIndicator
|
||||
service={service}
|
||||
/>
|
||||
<div className="toolbar_player_content" ref={contentRef}>
|
||||
<div className="toolbar_player_info">
|
||||
<h1
|
||||
ref={titleRef}
|
||||
className={classnames("toolbar_player_info_title", {
|
||||
["overflown"]: titleOverflown,
|
||||
})}
|
||||
>
|
||||
<ServiceIndicator service={service} />
|
||||
|
||||
{titleText}
|
||||
</h1>
|
||||
{titleText}
|
||||
</h1>
|
||||
|
||||
{
|
||||
titleOverflown && <Marquee
|
||||
gradientColor={RGBStringToValues(cover_analysis?.rgb)}
|
||||
gradientWidth={20}
|
||||
play={playerState.playback_status !== "stopped"}
|
||||
>
|
||||
<h1
|
||||
className="toolbar_player_info_title"
|
||||
>
|
||||
<ServiceIndicator
|
||||
service={service}
|
||||
/>
|
||||
{titleOverflown && (
|
||||
<Marquee
|
||||
gradientColor={RGBStringToValues(
|
||||
coverAnalysis?.rgb ?? "0,0,0",
|
||||
)}
|
||||
gradientWidth={20}
|
||||
play={playerState.playback_status !== "stopped"}
|
||||
>
|
||||
<h1 className="toolbar_player_info_title">
|
||||
<ServiceIndicator service={service} />
|
||||
|
||||
{titleText}
|
||||
</h1>
|
||||
</Marquee>
|
||||
}
|
||||
{titleText}
|
||||
</h1>
|
||||
</Marquee>
|
||||
)}
|
||||
|
||||
<p className="toolbar_player_info_subtitle">
|
||||
{artistStr ?? ""}
|
||||
</p>
|
||||
</div>
|
||||
<p className="toolbar_player_info_subtitle">
|
||||
{artistStr ?? ""}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="toolbar_player_actions">
|
||||
<Controls />
|
||||
<div className="toolbar_player_actions">
|
||||
<Controls />
|
||||
|
||||
<SeekBar
|
||||
stopped={playerState.playback_status === "stopped"}
|
||||
playing={playerState.playback_status === "playing"}
|
||||
streamMode={playerState.livestream_mode}
|
||||
disabled={playerState.control_locked}
|
||||
/>
|
||||
<SeekBar
|
||||
stopped={playerState.playback_status === "stopped"}
|
||||
playing={playerState.playback_status === "playing"}
|
||||
streamMode={playerState.livestream_mode}
|
||||
disabled={playerState.control_locked}
|
||||
/>
|
||||
|
||||
<ExtraActions />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ExtraActions />
|
||||
</div>
|
||||
|
||||
<Indicators track={playerState.track_manifest} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Player
|
||||
export default Player
|
||||
|
@ -266,4 +266,40 @@
|
||||
.ant-btn {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.toolbar_player_indicators_wrapper {
|
||||
position: absolute;
|
||||
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
|
||||
padding: 10px;
|
||||
|
||||
|
||||
.toolbar_player_indicators {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
width: fit-content;
|
||||
|
||||
padding: 7px 10px;
|
||||
|
||||
border-radius: 12px;
|
||||
|
||||
background-color: rgba(var(--layoutBackgroundColor), 0.7);
|
||||
-webkit-backdrop-filter: blur(5px);
|
||||
backdrop-filter: blur(5px);
|
||||
|
||||
font-size: 1rem;
|
||||
|
||||
svg {
|
||||
margin: 0 !important;
|
||||
|
||||
color: white
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user