jam-cloud/jam-ui/src/hooks/useMediaActions.js

244 lines
7.0 KiB
JavaScript

import { useCallback, useMemo } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import {
openBackingTrack as openBackingTrackThunk,
loadJamTrack as loadJamTrackThunk,
closeMedia as closeMediaThunk,
clearJamTrackState,
updateJamTrackState
} from '../store/features/mediaSlice';
import {
setMetronome,
setMetronomeSettings,
updateMediaSummary
} from '../store/features/mixersSlice';
import {
setShowMyMixes,
toggleMyMixes as toggleMyMixesAction,
setShowCustomMixes,
toggleCustomMixes as toggleCustomMixesAction,
setEditingMixdownId,
setCreatingMixdown,
setCreateMixdownErrors
} from '../store/features/sessionUISlice';
import { selectSessionId } from '../store/features/activeSessionSlice';
import { useJamServerContext } from '../context/JamServerContext';
import { syncTracksToServer } from '../services/trackSyncService';
/**
* Custom hook that provides Redux-based media actions
* Replaces MediaContext actions with Redux thunks and dispatchers
*/
const useMediaActions = () => {
const dispatch = useDispatch();
const sessionId = useSelector(selectSessionId);
const { jamClient, subscribe, unsubscribe } = useJamServerContext();
// Create jamServer object with subscribe/unsubscribe for thunks
const jamServer = useMemo(() => ({
subscribe,
unsubscribe
}), [subscribe, unsubscribe]);
/**
* Open a backing track file
* @param {string} file - Path to the backing track file
*/
const openBackingTrack = useCallback(async (file) => {
try {
await dispatch(openBackingTrackThunk({ file, jamClient })).unwrap();
// Update media summary
dispatch(updateMediaSummary({
backingTrackOpen: true,
userNeedsMediaControls: true
}));
// Sync tracks to server after opening backing track
if (sessionId && jamClient) {
console.log('[Track Sync] Backing track opened, syncing tracks');
dispatch(syncTracksToServer(sessionId, jamClient));
}
} catch (error) {
console.error('Error opening backing track:', error);
throw error;
}
}, [dispatch, jamClient, sessionId]);
/**
* Close all media (backing tracks, jam tracks, recordings, metronome)
* @param {boolean} force - Force close even if media is playing
*/
const closeMedia = useCallback(async (force = false) => {
try {
await dispatch(closeMediaThunk({ force, jamClient })).unwrap();
// Update media summary
dispatch(updateMediaSummary({
mediaOpen: false,
backingTrackOpen: false,
jamTrackOpen: false,
recordingOpen: false,
metronomeOpen: false,
userNeedsMediaControls: false
}));
} catch (error) {
console.error('Error closing media:', error);
throw error;
}
}, [dispatch, jamClient]);
/**
* Open metronome with specified settings
* @param {number} bpm - Beats per minute (default: 120)
* @param {string} sound - Metronome sound type (default: "Beep")
* @param {number} meter - Time signature meter (default: 1)
* @param {number} mode - Metronome mode (default: 0)
*/
const openMetronome = useCallback(async (bpm = 120, sound = "Beep", meter = 1, mode = 0) => {
try {
const result = await jamClient.SessionOpenMetronome(bpm, sound, meter, mode);
// Update Redux state
dispatch(setMetronome({ bpm, sound, meter, mode }));
dispatch(setMetronomeSettings({ tempo: bpm, sound, cricket: mode === 1 }));
dispatch(updateMediaSummary({
metronomeOpen: true,
userNeedsMediaControls: true
}));
// Sync tracks to server after opening metronome
if (sessionId && jamClient) {
console.log('[Track Sync] Metronome opened, syncing tracks');
dispatch(syncTracksToServer(sessionId, jamClient));
}
return result;
} catch (error) {
console.error('Error opening metronome:', error);
throw error;
}
}, [dispatch, jamClient, sessionId]);
/**
* Close the metronome
*/
const closeMetronome = useCallback(async () => {
try {
await jamClient.SessionCloseMetronome();
// Update Redux state
dispatch(setMetronome(null));
dispatch(updateMediaSummary({
metronomeOpen: false
}));
// Sync tracks to server after closing metronome
if (sessionId && jamClient) {
console.log('[Track Sync] Metronome closed, syncing tracks');
dispatch(syncTracksToServer(sessionId, jamClient));
}
} catch (error) {
console.error('Error closing metronome:', error);
throw error;
}
}, [dispatch, jamClient, sessionId]);
/**
* Load and play a JamTrack
* @param {object} jamTrack - JamTrack object with id and optional jmep data
*/
const loadJamTrack = useCallback(async (jamTrack) => {
try {
await dispatch(loadJamTrackThunk({ jamTrack, jamClient, jamServer })).unwrap();
// Update media summary
dispatch(updateMediaSummary({
jamTrackOpen: true,
userNeedsMediaControls: true
}));
// Sync tracks to server after opening jam track
if (sessionId && jamClient) {
console.log('[Track Sync] Jam track opened, syncing tracks');
dispatch(syncTracksToServer(sessionId, jamClient));
}
} catch (error) {
console.error('Error loading jam track:', error);
throw error;
}
}, [dispatch, jamClient, jamServer, sessionId]);
/**
* Stop and close the currently playing JamTrack
*/
const closeJamTrack = useCallback(async () => {
try {
await jamClient.JamTrackStopPlay();
// Update Redux state
dispatch(clearJamTrackState());
dispatch(updateMediaSummary({
jamTrackOpen: false
}));
} catch (error) {
console.error('Error closing jam track:', error);
throw error;
}
}, [dispatch, jamClient]);
/**
* Update JamTrack playback state (position, playing, etc.)
* @param {object} changes - JamTrack state changes
*/
const updateJamTrackPlayback = useCallback((changes) => {
dispatch(updateJamTrackState(changes));
}, [dispatch]);
// UI Actions
const toggleMyMixes = useCallback(() => {
dispatch(toggleMyMixesAction());
}, [dispatch]);
const toggleCustomMixes = useCallback(() => {
dispatch(toggleCustomMixesAction());
}, [dispatch]);
const editMixdown = useCallback((mixdownId) => {
dispatch(setEditingMixdownId(mixdownId));
}, [dispatch]);
const startCreatingMixdown = useCallback(() => {
dispatch(setCreatingMixdown(true));
}, [dispatch]);
const stopCreatingMixdown = useCallback(() => {
dispatch(setCreatingMixdown(false));
}, [dispatch]);
const setMixdownErrors = useCallback((errors) => {
dispatch(setCreateMixdownErrors(errors));
}, [dispatch]);
return {
// Core media actions
openBackingTrack,
closeMedia,
openMetronome,
closeMetronome,
loadJamTrack,
closeJamTrack,
updateJamTrackPlayback,
// UI actions
toggleMyMixes,
toggleCustomMixes,
editMixdown,
startCreatingMixdown,
stopCreatingMixdown,
setMixdownErrors
};
};
export default useMediaActions;