web: add error surfacing
This commit is contained in:
@@ -28,6 +28,7 @@ export interface PlaylistItem {
|
||||
id: number;
|
||||
playing: boolean | null;
|
||||
metadata?: Metadata;
|
||||
playbackError?: string;
|
||||
}
|
||||
|
||||
export const getDisplayTitle = (item: PlaylistItem): string => {
|
||||
@@ -62,6 +63,7 @@ export enum ServerEvent {
|
||||
FavoritesUpdate = "favorites_update",
|
||||
MetadataUpdate = "metadata_update",
|
||||
MPDUpdate = "mpd_update",
|
||||
PlaybackError = "playback_error",
|
||||
ScreenShare = "screen_share",
|
||||
}
|
||||
|
||||
|
||||
@@ -199,6 +199,7 @@ const App: React.FC = () => {
|
||||
case ServerEvent.NowPlayingUpdate:
|
||||
case ServerEvent.MetadataUpdate:
|
||||
case ServerEvent.MPDUpdate:
|
||||
case ServerEvent.PlaybackError:
|
||||
fetchPlaylist();
|
||||
fetchNowPlaying();
|
||||
break;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import classNames from 'classnames';
|
||||
import React, { useState, useRef, useEffect, ReactNode } from 'react';
|
||||
import { FaPlay, FaVolumeUp, FaVolumeOff } from 'react-icons/fa';
|
||||
import { FaPlay, FaVolumeUp, FaVolumeOff, FaExclamationTriangle } from 'react-icons/fa';
|
||||
import { getDisplayTitle, PlaylistItem } from '../api/player';
|
||||
|
||||
export enum PlayState {
|
||||
@@ -19,6 +19,7 @@ export interface SongRowProps {
|
||||
|
||||
const SongRow: React.FC<SongRowProps> = ({ song, auxControl, playState, onDelete, onPlay }) => {
|
||||
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
|
||||
const [showErrorDetails, setShowErrorDetails] = useState(false);
|
||||
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -38,6 +39,7 @@ const SongRow: React.FC<SongRowProps> = ({ song, auxControl, playState, onDelete
|
||||
}, [showDeleteConfirm]);
|
||||
|
||||
const displayTitle = getDisplayTitle(song);
|
||||
const hasError = !!song.playbackError;
|
||||
|
||||
return (
|
||||
<div className={classNames(
|
||||
@@ -46,16 +48,46 @@ const SongRow: React.FC<SongRowProps> = ({ song, auxControl, playState, onDelete
|
||||
"bg-black/30": playState === PlayState.NotPlaying,
|
||||
})}>
|
||||
<div className="flex flex-row gap-2">
|
||||
<button
|
||||
className="text-white/40 hover:text-white transition-colors px-3 py-1 rounded"
|
||||
onClick={onPlay}
|
||||
>
|
||||
{
|
||||
playState === PlayState.Playing ? <FaVolumeUp size={12} />
|
||||
: playState === PlayState.Paused ? <FaVolumeOff size={12} />
|
||||
: <FaPlay size={12} />
|
||||
}
|
||||
</button>
|
||||
<div className="relative">
|
||||
<button
|
||||
className={classNames(
|
||||
"transition-colors px-3 py-1 rounded",
|
||||
hasError ? "text-amber-300 hover:text-amber-100" : "text-white/40 hover:text-white"
|
||||
)}
|
||||
onClick={() => {
|
||||
if (hasError) {
|
||||
setShowErrorDetails((prev) => !prev);
|
||||
} else {
|
||||
onPlay();
|
||||
}
|
||||
}}
|
||||
onMouseEnter={() => {
|
||||
if (hasError) {
|
||||
setShowErrorDetails(true);
|
||||
}
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
if (hasError) {
|
||||
setShowErrorDetails(false);
|
||||
}
|
||||
}}
|
||||
title={hasError ? song.playbackError : undefined}
|
||||
>
|
||||
{
|
||||
hasError ? <FaExclamationTriangle size={12} /> :
|
||||
playState === PlayState.Playing ? <FaVolumeUp size={12} />
|
||||
: playState === PlayState.Paused ? <FaVolumeOff size={12} />
|
||||
: <FaPlay size={12} />
|
||||
}
|
||||
</button>
|
||||
|
||||
{hasError && showErrorDetails && (
|
||||
<div className="absolute z-10 top-full left-0 mt-1 w-64 p-2 text-xs text-white bg-red-600/90 rounded shadow-lg">
|
||||
<div className="font-semibold mb-1">Playback error</div>
|
||||
<div className="break-words">{song.playbackError}</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex-grow min-w-0">
|
||||
|
||||
Reference in New Issue
Block a user