From 845b44a3195338c1946b93ac53b95bcd7ea81526 Mon Sep 17 00:00:00 2001
From: Nuwan
Date: Thu, 15 Jan 2026 01:00:03 +0530
Subject: [PATCH] feat(05-04): implement mixdown picker UI with hierarchy
sorting
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Add handleMixdownChange callback to switch between mixdowns
- Stop and restart playback when changing mixdown (if playing)
- Add mixdown dropdown selector with sorted display
- Sort order: master → custom mixes → stems (alphabetical within types)
- Visual indicators: 🎵 master, 🎨 custom, 🎸 stem
- Disable picker during operations and loading
- Update selectedMixdownId local state and activeMixdown Redux state
Co-Authored-By: Claude Sonnet 4.5
---
.../client/JKSessionJamTrackPlayer.js | 71 +++++++++++++++++++
1 file changed, 71 insertions(+)
diff --git a/jam-ui/src/components/client/JKSessionJamTrackPlayer.js b/jam-ui/src/components/client/JKSessionJamTrackPlayer.js
index 0a2c23f99..20edd8a11 100644
--- a/jam-ui/src/components/client/JKSessionJamTrackPlayer.js
+++ b/jam-ui/src/components/client/JKSessionJamTrackPlayer.js
@@ -241,6 +241,46 @@ const JKSessionJamTrackPlayer = ({
}
}, [isOperating, jamClient, jamTrackState.isPaused, dispatch]);
+ // Mixdown change handler
+ const handleMixdownChange = useCallback(async (mixdownId) => {
+ if (isOperating || !jamClient || !fqIdRef.current) return;
+
+ try {
+ setIsOperating(true);
+ setError(null);
+
+ // Find the new mixdown
+ const mixdown = availableMixdowns.find(m => m.id === mixdownId);
+ if (!mixdown) {
+ throw new Error('Mixdown not found');
+ }
+
+ // Update local state
+ setSelectedMixdownId(mixdownId);
+ dispatch(setActiveMixdown(mixdown));
+
+ // If currently playing, stop and restart with new mixdown
+ if (jamTrackState.isPlaying || jamTrackState.isPaused) {
+ await jamClient.JamTrackStop(fqIdRef.current);
+
+ await dispatch(loadJamTrack({
+ jamTrack,
+ mixdownId,
+ autoPlay: true,
+ jamClient
+ })).unwrap();
+ }
+
+ } catch (err) {
+ console.error('[JamTrack] Mixdown change error:', err);
+ setError({ type: 'playback', message: 'Failed to change mixdown' });
+ } finally {
+ if (mountedRef.current) {
+ setIsOperating(false);
+ }
+ }
+ }, [isOperating, jamClient, jamTrack, jamTrackState, availableMixdowns, dispatch]);
+
// Helper: Format milliseconds to MM:SS
const formatTime = (ms) => {
if (!ms || isNaN(ms)) return '00:00';
@@ -354,6 +394,37 @@ const JKSessionJamTrackPlayer = ({
{formatTime(jamTrackState.currentPositionMs)} / {formatTime(jamTrackState.durationMs)}
+
+ {availableMixdowns.length > 0 && (
+
+
+
+
+ )}
);
};