From 1763f666b5cd6d2af48a985ca088d79b17aef3e6 Mon Sep 17 00:00:00 2001 From: jeffvli Date: Sun, 23 Nov 2025 15:58:23 -0800 Subject: [PATCH] add more shuffle play modes --- src/i18n/locales/en.json | 4 +- .../context-menu/actions/play-action.tsx | 15 +++ src/renderer/store/player.store.ts | 117 ++++++++++++++++++ src/shared/hooks/use-long-press.ts | 3 + src/shared/types/types.ts | 2 + 5 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 src/shared/hooks/use-long-press.ts diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 29f08482e..68bff6568 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -485,6 +485,8 @@ "player": { "addLast": "add last", "addNext": "add next", + "addLastShuffled": "add last (shuffled)", + "addNextShuffled": "add next (shuffled)", "favorite": "favorite", "mute": "mute", "muted": "muted", @@ -509,7 +511,7 @@ "repeat_off": "repeat disabled", "repeat_one": "repeat one", "repeat_other": "", - "shuffle": "play shuffled", + "shuffle": "play (shuffled)", "shuffle_off": "shuffle disabled", "skip": "skip", "skip_back": "skip backwards", diff --git a/src/renderer/features/context-menu/actions/play-action.tsx b/src/renderer/features/context-menu/actions/play-action.tsx index 6d38c5682..830e64a9d 100644 --- a/src/renderer/features/context-menu/actions/play-action.tsx +++ b/src/renderer/features/context-menu/actions/play-action.tsx @@ -47,6 +47,14 @@ export const PlayAction = ({ ids, itemType, songs }: PlayActionProps) => { handlePlay(Play.SHUFFLE); }, [handlePlay]); + const handlePlayNextShuffled = useCallback(() => { + handlePlay(Play.NEXT_SHUFFLE); + }, [handlePlay]); + + const handlePlayLastShuffled = useCallback(() => { + handlePlay(Play.LAST_SHUFFLE); + }, [handlePlay]); + const playButtonBehavior = usePlayButtonBehavior(); const defaultPlayAction = useCallback(() => { @@ -76,9 +84,16 @@ export const PlayAction = ({ ids, itemType, songs }: PlayActionProps) => { {t('player.addLast', { postProcess: 'sentenceCase' })} + {t('player.shuffle', { postProcess: 'sentenceCase' })} + + {t('player.addNextShuffled', { postProcess: 'sentenceCase' })} + + + {t('player.addLastShuffled', { postProcess: 'sentenceCase' })} + ); diff --git a/src/renderer/store/player.store.ts b/src/renderer/store/player.store.ts index 8411758fb..7b7fc8c3b 100644 --- a/src/renderer/store/player.store.ts +++ b/src/renderer/store/player.store.ts @@ -160,6 +160,34 @@ export const usePlayerStoreBase = create()( }); break; } + case Play.LAST_SHUFFLE: { + set((state) => { + const currentIndex = state.player.index; + newItems.forEach((item) => { + state.queue.songs[item._uniqueId] = item; + }); + + // Shuffle the new items before appending + const shuffledIds = shuffleInPlace([...newUniqueIds]); + + state.queue.default = [ + ...state.queue.default, + ...shuffledIds, + ]; + + if (state.player.shuffle === PlayerShuffle.TRACK) { + state.queue.shuffled = [ + ...state.queue.shuffled.slice(0, currentIndex), + state.queue.shuffled[currentIndex], + ...shuffleInPlace([ + ...state.queue.shuffled.slice(currentIndex + 1), + ...shuffledIds, + ]), + ]; + } + }); + break; + } case Play.NEXT: { set((state) => { const currentIndex = state.player.index; @@ -186,6 +214,35 @@ export const usePlayerStoreBase = create()( }); break; } + case Play.NEXT_SHUFFLE: { + set((state) => { + const currentIndex = state.player.index; + newItems.forEach((item) => { + state.queue.songs[item._uniqueId] = item; + }); + + // Shuffle the new items before inserting + const shuffledIds = shuffleInPlace([...newUniqueIds]); + + state.queue.default = [ + ...state.queue.default.slice(0, currentIndex + 1), + ...shuffledIds, + ...state.queue.default.slice(currentIndex + 1), + ]; + + if (state.player.shuffle === PlayerShuffle.TRACK) { + state.queue.shuffled = [ + ...state.queue.shuffled.slice(0, currentIndex), + state.queue.shuffled[currentIndex], + ...shuffleInPlace([ + ...state.queue.shuffled.slice(currentIndex + 1), + ...shuffledIds, + ]), + ]; + } + }); + break; + } case Play.NOW: { set((state) => { newItems.forEach((item) => { @@ -252,6 +309,28 @@ export const usePlayerStoreBase = create()( }); break; } + case Play.LAST_SHUFFLE: { + set((state) => { + // Add new songs to songs object + newItems.forEach((item) => { + state.queue.songs[item._uniqueId] = item; + }); + + // Shuffle the new items before appending + const shuffledIds = shuffleInPlace([...newUniqueIds]); + + state.queue.priority = [ + ...state.queue.priority, + ...shuffledIds, + ]; + + state.queue.shuffled = [ + ...state.queue.shuffled, + ...shuffledIds, + ]; + }); + break; + } case Play.NEXT: { set((state) => { const currentIndex = state.player.index; @@ -287,6 +366,44 @@ export const usePlayerStoreBase = create()( }); break; } + case Play.NEXT_SHUFFLE: { + set((state) => { + const currentIndex = state.player.index; + const isInPriority = + currentIndex < state.queue.priority.length; + + // Add new songs to songs object + newItems.forEach((item) => { + state.queue.songs[item._uniqueId] = item; + }); + + // Shuffle the new items before inserting + const shuffledIds = shuffleInPlace([...newUniqueIds]); + + if (isInPriority) { + state.queue.priority = [ + ...state.queue.priority.slice(0, currentIndex + 1), + ...shuffledIds, + ...state.queue.priority.slice(currentIndex + 1), + ]; + } else { + state.queue.priority = [ + ...state.queue.priority, + ...shuffledIds, + ]; + } + + state.queue.shuffled = [ + ...state.queue.shuffled.slice(0, currentIndex), + state.queue.shuffled[currentIndex], + ...shuffleInPlace([ + ...state.queue.shuffled.slice(currentIndex + 1), + ...shuffledIds, + ]), + ]; + }); + break; + } case Play.NOW: { set((state) => { // Add new songs to songs object diff --git a/src/shared/hooks/use-long-press.ts b/src/shared/hooks/use-long-press.ts new file mode 100644 index 000000000..01afac4ab --- /dev/null +++ b/src/shared/hooks/use-long-press.ts @@ -0,0 +1,3 @@ +import { useLongPress as useMantineLongPress } from '@mantine/hooks'; + +export const useLongPress = useMantineLongPress; diff --git a/src/shared/types/types.ts b/src/shared/types/types.ts index cf2fa158f..95fd99f3b 100644 --- a/src/shared/types/types.ts +++ b/src/shared/types/types.ts @@ -110,7 +110,9 @@ export enum FontType { export enum Play { INDEX = 'index', LAST = 'last', + LAST_SHUFFLE = 'lastShuffle', NEXT = 'next', + NEXT_SHUFFLE = 'nextShuffle', NOW = 'now', SHUFFLE = 'shuffle', }