refactor(audio): Phase 4 — unify song tracking, remove compat aliases
playerStore: remove currentPlayingSongId/currentPlayingBandId/setCurrentPlayingSong. Single pair (currentSongId/currentBandId) now set exclusively when play() is called, not when the page opens. This means MiniPlayer and sidebar links only appear after audio has been started — correct UX for a "now playing" widget. audioService: play() calls setCurrentSong instead of setCurrentPlayingSong; cleanup() clears it. Remove isReadyForPlayback() and canAttemptPlayback() aliases — all callers now use isWaveformReady() directly. useWaveform: remove setCurrentSong call from init (store updated by play() now); restore-playback snapshot reads currentSongId/currentBandId. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -41,17 +41,12 @@ export function useWaveform(
|
|||||||
try {
|
try {
|
||||||
await audioService.initialize(containerRef.current!, options.url!);
|
await audioService.initialize(containerRef.current!, options.url!);
|
||||||
|
|
||||||
// Update global song context
|
|
||||||
if (options.songId && options.bandId) {
|
|
||||||
usePlayerStore.getState().setCurrentSong(options.songId, options.bandId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore playback if this song was already playing when the page loaded.
|
// Restore playback if this song was already playing when the page loaded.
|
||||||
// Read as a one-time snapshot — these values must NOT be reactive deps or
|
// Read as a one-time snapshot — these values must NOT be reactive deps or
|
||||||
// the effect would re-run on every time update (re-initializing WaveSurfer).
|
// the effect would re-run on every time update (re-initializing WaveSurfer).
|
||||||
const {
|
const {
|
||||||
currentPlayingSongId,
|
currentSongId,
|
||||||
currentPlayingBandId,
|
currentBandId,
|
||||||
isPlaying: wasPlaying,
|
isPlaying: wasPlaying,
|
||||||
currentTime: savedTime,
|
currentTime: savedTime,
|
||||||
} = usePlayerStore.getState();
|
} = usePlayerStore.getState();
|
||||||
@@ -59,8 +54,8 @@ export function useWaveform(
|
|||||||
if (
|
if (
|
||||||
options.songId &&
|
options.songId &&
|
||||||
options.bandId &&
|
options.bandId &&
|
||||||
currentPlayingSongId === options.songId &&
|
currentSongId === options.songId &&
|
||||||
currentPlayingBandId === options.bandId &&
|
currentBandId === options.bandId &&
|
||||||
wasPlaying &&
|
wasPlaying &&
|
||||||
audioService.isWaveformReady()
|
audioService.isWaveformReady()
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ class AudioService {
|
|||||||
}
|
}
|
||||||
await this.wavesurfer.play();
|
await this.wavesurfer.play();
|
||||||
if (songId && bandId) {
|
if (songId && bandId) {
|
||||||
usePlayerStore.getState().setCurrentPlayingSong(songId, bandId);
|
usePlayerStore.getState().setCurrentSong(songId, bandId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,19 +116,10 @@ class AudioService {
|
|||||||
return this.isReady && !!this.wavesurfer;
|
return this.isReady && !!this.wavesurfer;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aliases kept for callers until Phase 3 cleans them up
|
|
||||||
public isReadyForPlayback(): boolean {
|
|
||||||
return this.isWaveformReady();
|
|
||||||
}
|
|
||||||
|
|
||||||
public canAttemptPlayback(): boolean {
|
|
||||||
return this.isWaveformReady();
|
|
||||||
}
|
|
||||||
|
|
||||||
public cleanup(): void {
|
public cleanup(): void {
|
||||||
this.destroyWaveSurfer();
|
this.destroyWaveSurfer();
|
||||||
const store = usePlayerStore.getState();
|
const store = usePlayerStore.getState();
|
||||||
store.setCurrentPlayingSong(null, null);
|
store.setCurrentSong(null, null);
|
||||||
store.batchUpdate({ isPlaying: false, currentTime: 0 });
|
store.batchUpdate({ isPlaying: false, currentTime: 0 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,17 +4,16 @@ interface PlayerState {
|
|||||||
isPlaying: boolean;
|
isPlaying: boolean;
|
||||||
currentTime: number;
|
currentTime: number;
|
||||||
duration: number;
|
duration: number;
|
||||||
|
// Set when audio starts playing; cleared on cleanup.
|
||||||
|
// Drives MiniPlayer visibility and sidebar "go to now playing" links.
|
||||||
currentSongId: string | null;
|
currentSongId: string | null;
|
||||||
currentBandId: string | null;
|
currentBandId: string | null;
|
||||||
currentPlayingSongId: string | null; // Track which song is actively playing
|
|
||||||
currentPlayingBandId: string | null; // Track which band's song is actively playing
|
|
||||||
setPlaying: (isPlaying: boolean) => void;
|
setPlaying: (isPlaying: boolean) => void;
|
||||||
setCurrentTime: (currentTime: number) => void;
|
setCurrentTime: (currentTime: number) => void;
|
||||||
setDuration: (duration: number) => void;
|
setDuration: (duration: number) => void;
|
||||||
setCurrentSong: (songId: string | null, bandId: string | null) => void;
|
setCurrentSong: (songId: string | null, bandId: string | null) => void;
|
||||||
setCurrentPlayingSong: (songId: string | null, bandId: string | null) => void;
|
|
||||||
reset: () => void;
|
reset: () => void;
|
||||||
batchUpdate: (updates: Partial<Omit<PlayerState, 'setPlaying' | 'setCurrentTime' | 'setDuration' | 'setCurrentSong' | 'setCurrentPlayingSong' | 'reset' | 'batchUpdate'>>) => void;
|
batchUpdate: (updates: Partial<Omit<PlayerState, 'setPlaying' | 'setCurrentTime' | 'setDuration' | 'setCurrentSong' | 'reset' | 'batchUpdate'>>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const usePlayerStore = create<PlayerState>()((set) => ({
|
export const usePlayerStore = create<PlayerState>()((set) => ({
|
||||||
@@ -23,13 +22,10 @@ export const usePlayerStore = create<PlayerState>()((set) => ({
|
|||||||
duration: 0,
|
duration: 0,
|
||||||
currentSongId: null,
|
currentSongId: null,
|
||||||
currentBandId: null,
|
currentBandId: null,
|
||||||
currentPlayingSongId: null,
|
|
||||||
currentPlayingBandId: null,
|
|
||||||
setPlaying: (isPlaying) => set({ isPlaying }),
|
setPlaying: (isPlaying) => set({ isPlaying }),
|
||||||
setCurrentTime: (currentTime) => set({ currentTime }),
|
setCurrentTime: (currentTime) => set({ currentTime }),
|
||||||
setDuration: (duration) => set({ duration }),
|
setDuration: (duration) => set({ duration }),
|
||||||
setCurrentSong: (songId, bandId) => set({ currentSongId: songId, currentBandId: bandId }),
|
setCurrentSong: (songId, bandId) => set({ currentSongId: songId, currentBandId: bandId }),
|
||||||
setCurrentPlayingSong: (songId, bandId) => set({ currentPlayingSongId: songId, currentPlayingBandId: bandId }),
|
|
||||||
batchUpdate: (updates) => set(updates),
|
batchUpdate: (updates) => set(updates),
|
||||||
reset: () => set({
|
reset: () => set({
|
||||||
isPlaying: false,
|
isPlaying: false,
|
||||||
@@ -37,7 +33,5 @@ export const usePlayerStore = create<PlayerState>()((set) => ({
|
|||||||
duration: 0,
|
duration: 0,
|
||||||
currentSongId: null,
|
currentSongId: null,
|
||||||
currentBandId: null,
|
currentBandId: null,
|
||||||
currentPlayingSongId: null,
|
|
||||||
currentPlayingBandId: null
|
|
||||||
})
|
})
|
||||||
}));
|
}));
|
||||||
@@ -164,19 +164,19 @@ describe('AudioService', () => {
|
|||||||
expect(mockWs.play).toHaveBeenCalled();
|
expect(mockWs.play).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('updates currentPlayingSongId/BandId in the store', async () => {
|
it('updates currentSongId/BandId in the store', async () => {
|
||||||
await initService(service);
|
await initService(service);
|
||||||
await service.play('song-1', 'band-1');
|
await service.play('song-1', 'band-1');
|
||||||
const state = usePlayerStore.getState();
|
const state = usePlayerStore.getState();
|
||||||
expect(state.currentPlayingSongId).toBe('song-1');
|
expect(state.currentSongId).toBe('song-1');
|
||||||
expect(state.currentPlayingBandId).toBe('band-1');
|
expect(state.currentBandId).toBe('band-1');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not update store ids when called without ids', async () => {
|
it('does not update store ids when called without ids', async () => {
|
||||||
await initService(service);
|
await initService(service);
|
||||||
await service.play();
|
await service.play();
|
||||||
const state = usePlayerStore.getState();
|
const state = usePlayerStore.getState();
|
||||||
expect(state.currentPlayingSongId).toBeNull();
|
expect(state.currentSongId).toBeNull();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user