Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions frontend/js/src/common/brainzplayer/BrainzPlayerContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as React from "react";
import { faRepeat } from "@fortawesome/free-solid-svg-icons";
import { isEqual, isNil } from "lodash";
import { faRepeatOnce } from "../../utils/icons";
import { listenOrJSPFTrackToQueueItem } from "./utils";
import { listenOrJSPFTrackToQueueItem, shuffleQueue } from "./utils";

export const QueueRepeatModes = {
off: {
Expand Down Expand Up @@ -85,7 +85,8 @@ export type BrainzPlayerActionType = Partial<BrainzPlayerContextT> & {
| "ADD_LISTEN_TO_TOP_OF_QUEUE"
| "ADD_LISTEN_TO_BOTTOM_OF_QUEUE"
| "ADD_LISTEN_TO_BOTTOM_OF_AMBIENT_QUEUE"
| "ADD_MULTIPLE_LISTEN_TO_BOTTOM_OF_AMBIENT_QUEUE";
| "ADD_MULTIPLE_LISTEN_TO_BOTTOM_OF_AMBIENT_QUEUE"
| "SHUFFLE_QUEUE";
data?: any;
};

Expand Down Expand Up @@ -324,6 +325,14 @@ function valueReducer(
ambientQueue: [...ambientQueue, ...tracksToAdd],
};
}
case "SHUFFLE_QUEUE": {
const { queue, currentListenIndex } = state;
const newQueue = shuffleQueue(queue, currentListenIndex);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I appreciate your solution, I think this is not what users will expect from a music player. Normally, in all cases I remember using a shuffle option, instead of instantly shuffling the queue, activating "shuffle mode" will instead change the selection process for the next track, making it random, without visually affecting the queue.

So I think this action should be called SHUFFLE_MODE and should store a boolean state for the same shuffleMode.

Then the random picking would need to happen in the playNextTrack method in BrainzPlayer component:

const playNextTrack = (invert: boolean = false): void => {

This method should access the value of shuffle mode in the brainzPlayerContext (brainzPlayerContextRef.current.shuffleMode in this case) and decide what to do depending on that boolean state.

return {
...state,
queue: newQueue,
};
}
default: {
throw Error(`Unknown action: ${action.type}`);
}
Expand Down
8 changes: 8 additions & 0 deletions frontend/js/src/common/brainzplayer/BrainzPlayerUI.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
faMusic,
faPauseCircle,
faPlayCircle,
faShuffle,
faSlash,
faVolumeUp,
faMaximize,
Expand Down Expand Up @@ -454,6 +455,13 @@ function BrainzPlayerUI(props: React.PropsWithChildren<BrainzPlayerUIProps>) {
onClick={toggleRepeatMode}
/>
)}
{!isMobile && (
<FontAwesomeIcon
icon={faShuffle}
title="Shuffle queue"
onClick={() => dispatch({ type: "SHUFFLE_QUEUE" })}
/>
)}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This !isMobile part tells you this is only displayed on large screen sizes.

You'll also need to add a button to the mobile UI, that is the MusicPlayer component, between the toggleQueue and toggleRepeatMode buttons, using the PlaybackControlButton component, with a color based on the boolean state currently missing (shuffle on or off):

<div className="player-buttons secondary">
<FeedbackButtons
currentListenFeedback={currentListenFeedback}
isPlayingATrack={isPlayingATrack}
submitLikeFeedback={submitLikeFeedback}
submitDislikeFeedback={submitDislikeFeedback}
/>
<PlaybackControlButton
className="toggle-queue-button"
action={toggleQueue}
title="Queue"
icon={faBarsStaggered}
size="xl"
/>
<PlaybackControlButton
className={queueRepeatMode.title}
action={toggleRepeatMode}
title={queueRepeatMode.title}
icon={queueRepeatMode.icon}
color={queueRepeatMode.color}
size="xl"
/>
<PlaybackControlButton
action={toggleShowVolume}
title="Volume"
icon={faVolumeUp}
size="xl"
/>
</div>

{showFeedback && !isMobile && (
<>
<FontAwesomeIcon
Expand Down
26 changes: 25 additions & 1 deletion frontend/js/src/common/brainzplayer/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { cloneDeep, has } from "lodash";
import { cloneDeep, has, shuffle } from "lodash";
import { JSPFTrackToListen } from "../../playlists/utils";
import {
getArtistMBIDs,
Expand Down Expand Up @@ -41,6 +41,30 @@ export function listenOrJSPFTrackToQueueItem(
return queueItem;
}

// eslint-disable-next-line import/prefer-default-export
export function shuffleQueue(
queue: BrainzPlayerQueue,
currentListenIndex: number
): BrainzPlayerQueue {
if (!queue || queue.length === 0) {
return [];
}
if (currentListenIndex >= queue.length - 1) {
return [...queue];
}

const newQueue = [...queue]; // Create a shallow copy to not modify the original
const shuffleStartIndex = currentListenIndex + 1;
if (shuffleStartIndex >= newQueue.length) {
return newQueue;
}

const partToKeep = newQueue.slice(0, shuffleStartIndex);
const partToShuffle = newQueue.slice(shuffleStartIndex);
const shuffledPart = shuffle(partToShuffle); // Lodash shuffle returns a new shuffled array
return [...partToKeep, ...shuffledPart];
}

export enum FeedbackValue {
LIKE = 1,
DISLIKE = -1,
Expand Down
Loading