From ae2e123a1427725538902d91be1362b2371bc12f Mon Sep 17 00:00:00 2001
From: ImAsra <34755744+ImAsra@users.noreply.github.com>
Date: Thu, 28 May 2026 22:59:49 +0200
Subject: [PATCH] feat: add long press to shuffle with a wave animation (#888)
* feat: add long press to shuffle with a wave animation to singnify how long to press
* refactor: long-press shuffle cleanup
Follow-up on the long-press shuffle PR: shared hook/overlay, playback parity,
pointer events, broader surface coverage, locales, and tests.
* docs: credit ImAsra for long-press album shuffle (PR #888)
Add CHANGELOG entry and Settings credits for the hold-to-shuffle play
interaction shipped in Psychotoxical/psysonic#888.
* fix: restore playAlbumShuffled and long-press hook wiring
The follow-up merge dropped playAlbumShuffled and reverted the shared
long-press hook in album play buttons, breaking tsc and vitest on PR #888.
---------
Co-authored-by: cucadmuh <49571317+cucadmuh@users.noreply.github.com>
---
CHANGELOG.md | 9 +
src/components/AlbumCard.tsx | 29 +--
src/components/BecauseYouLikeRail.tsx | 23 ++-
src/components/Hero.tsx | 40 +++--
src/components/LongPressWaveOverlay.tsx | 20 +++
src/config/settingsCredits.ts | 7 +
src/hooks/useLongPressAction.test.ts | 108 ++++++++++++
src/hooks/useLongPressAction.ts | 91 ++++++++++
src/locales/de/hero.ts | 1 +
src/locales/en/hero.ts | 1 +
src/locales/es/hero.ts | 1 +
src/locales/fr/hero.ts | 1 +
src/locales/nb/hero.ts | 1 +
src/locales/nl/hero.ts | 1 +
src/locales/ro/hero.ts | 1 +
src/locales/ru/hero.ts | 1 +
src/locales/zh/hero.ts | 1 +
src/pages/MostPlayed.tsx | 38 ++--
src/styles/components/album-row-container.css | 73 ++++++++
src/utils/playback/playAlbum.test.ts | 165 ++++++++++++++++++
src/utils/playback/playAlbum.ts | 17 +-
21 files changed, 585 insertions(+), 44 deletions(-)
create mode 100644 src/components/LongPressWaveOverlay.tsx
create mode 100644 src/hooks/useLongPressAction.test.ts
create mode 100644 src/hooks/useLongPressAction.ts
create mode 100644 src/utils/playback/playAlbum.test.ts
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3059d0a2..9e0871e9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -142,6 +142,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
+### Album play — hold to shuffle
+
+**By [@ImAsra](https://github.com/ImAsra), PR [#888](https://github.com/Psychotoxical/psysonic/pull/888)**
+
+* Hold an album **Play** button (~1 s) for a filling wave animation, then the album starts in shuffled order; a short click still plays in track order.
+* Album cards, hero, Because-you-like rail, and Most Played; tooltip in all locales.
+
+
+
## Changed
### Linux — session GDK, WebKitGTK mitigations, and Wayland text
diff --git a/src/components/AlbumCard.tsx b/src/components/AlbumCard.tsx
index 5ddc2a97..1141b35f 100644
--- a/src/components/AlbumCard.tsx
+++ b/src/components/AlbumCard.tsx
@@ -16,7 +16,9 @@ import { COVER_DENSE_GRID_MIN_CELL_CSS_PX } from '../cover/layoutSizes';
import { resolveCoverDisplayTier } from '../cover/tiers';
import { acquireUrl } from '../utils/imageCache/urlPool';
import { OpenArtistRefInline } from './OpenArtistRefInline';
-import { playAlbum } from '../utils/playback/playAlbum';
+import { playAlbum, playAlbumShuffled } from '../utils/playback/playAlbum';
+import { useLongPressAction } from '../hooks/useLongPressAction';
+import { LongPressWaveOverlay } from './LongPressWaveOverlay';
import { useDragDrop } from '../contexts/DragDropContext';
import { isAlbumRecentlyAdded } from '../utils/albumRecency';
import { deriveAlbumArtistRefs } from '../utils/album/deriveAlbumHeaderArtistRefs';
@@ -59,6 +61,10 @@ function AlbumCard({
libraryResolve = false,
}: AlbumCardProps) {
const { t } = useTranslation();
+ const { isHolding, pressBind } = useLongPressAction({
+ onShortPress: () => playAlbum(album.id),
+ onLongPress: () => playAlbumShuffled(album.id),
+ });
const navigate = useNavigate();
const openContextMenu = usePlayerStore(s => s.openContextMenu);
const enqueue = usePlayerStore(s => s.enqueue);
@@ -157,15 +163,18 @@ function AlbumCard({
)}
{!selectionMode && (
-
+