added loading state for playback start
Mwebclient/src/App.tsx
@@ -37,6 +37,7 @@ const App: Component = () => {
volume: 0.5,
buffered: 0,
rate: 1,
waitingForStart: false,
});
const [showOptions, setShowOptions] = createSignal(false);
const [showPlaylistManager, setShowPlaylistManager] = createSignal(false);
@@ -153,12 +154,15 @@ const App: Component = () => {
setPlayerState("buffered", 0);
//clone, otherwise it shares the reference
setPlayerState("currentTrack", { ...track });
setPlayerState("waitingForStart", true);
});
await audioElement.play();
} catch (e) {
setPlayerState("isPlaying", false);
handlePlayerError(e);
} finally {
setPlayerState("waitingForStart", false);
}
}
@@ -205,11 +209,17 @@ const App: Component = () => {
setPlayerState("isPlaying", newIsPlaying);
if (newIsPlaying) {
audioElement.play().catch((e) => {
setPlayerState("isPlaying", false);
setPlayerState("currentTime", 0);
handlePlayerError(e);
});
setPlayerState("waitingForStart", true);
audioElement
.play()
.catch((e) => {
setPlayerState("isPlaying", false);
setPlayerState("currentTime", 0);
handlePlayerError(e);
})
.finally(() => {
setPlayerState("waitingForStart", false);
});
} else {
audioElement.pause();
}
Mwebclient/src/components/PlayerControls.css
@@ -29,9 +29,16 @@
gap: 0.5rem;
}
.track-info {
display: flex;
justify-content: center;
gap: 1rem;
margin-bottom: 0.5rem;
text-align: center;
font-weight: bold;
.loading-spinner {
width: 24px !important;
height: 24px !important;
}
}
.progress-bar {
@@ -63,11 +70,10 @@
padding: 0.25rem 0.5rem;
gap: 0.5rem;
min-width: 0;
svg{
svg {
flex-shrink: 0;
}
input {
min-width: 0;
}
}
Mwebclient/src/components/PlayerControls.tsx
@@ -4,6 +4,7 @@ import { type Accessor, createEffect, createSignal, type Setter } from "solid-js
import { NextTrackSVG, PauseSVG, PlaySVG, PreviousTrackSVG, RepeatSVG, ShuffleSVG, VolumeSVG } from "./SVG";
import { formatTime } from "../utils";
import type { SetStoreFunction } from "solid-js/store";
import { LoadingSpinner } from "./LoadingScreen";
interface PlayerControlsProps {
playerState: PlayerState;
@@ -46,12 +47,21 @@ export default function PlayerControls(props: PlayerControlsProps) {
const duration = playerState.currentTrack.metadata.duration;
props.audioElement.currentTime = Math.min(pos * duration, playerState.buffered - 1);
props.setPlayerState("currentTime", pos * duration)
props.setPlayerState("currentTime", pos * duration);
}
return (
<div class="player-controls">
<div class="track-info">{getItemName(props.playerState.currentTrack) || "No track selected"}</div>
<div class="track-info">
{getItemName(props.playerState.currentTrack) || "No track selected"}
{props.playerState.waitingForStart ? (
<div>
<LoadingSpinner center={true} />
</div>
) : (
""
)}
</div>
<div class="progress-container">
<div class="progress-bar" onClick={seekTo} style={controlsDisabled() ? { cursor: "default" } : {}}>
<div
Mwebclient/src/offline.ts
@@ -26,12 +26,7 @@ export async function loadDataFromDB() {
const db = await openDB();
const files = await db.listFiles(FileStore);
for (const file of files) {
const parts = file.path.split("/");
let current = "";
for (let index = 0; index < parts.length; index++) {
current = joinPath(current, parts[index]);
if (index === parts.length - 1) setDownloadStatusMap(current, "Synced");
}
setDownloadStatusMap(file.path, "Synced");
updateFileTree(file.path, { metadata: file.metadata }, true);
}
} catch (e) {