فهرست منبع

feat: replace song info when skipping to next / previous song

Fela Maslen 5 سال پیش
والد
کامیت
f163fb50f8
4فایلهای تغییر یافته به همراه40 افزوده شده و 6 حذف شده
  1. 6 3
      gmus-web/src/actions/actions.ts
  2. 2 2
      gmus-web/src/hooks/queue.ts
  3. 14 0
      gmus-web/src/reducer/reducer.spec.ts
  4. 18 1
      gmus-web/src/reducer/reducer.ts

+ 6 - 3
gmus-web/src/actions/actions.ts

@@ -52,11 +52,14 @@ export const playPaused = (): ActionPlayPaused => ({
   payload: undefined,
 });
 
-export type ActionSongInfoFetched = ActionLocal<ActionTypeLocal.SongInfoFetched, Song | null>;
+export type ActionSongInfoFetched = ActionLocal<
+  ActionTypeLocal.SongInfoFetched,
+  { song: Song | null; replace: boolean }
+>;
 
-export const songInfoFetched = (song: Song | null): ActionSongInfoFetched => ({
+export const songInfoFetched = (song: Song | null, replace = false): ActionSongInfoFetched => ({
   type: ActionTypeLocal.SongInfoFetched,
-  payload: song,
+  payload: { song, replace },
 });
 
 export type LocalAction =

+ 2 - 2
gmus-web/src/hooks/queue.ts

@@ -1,7 +1,7 @@
 import { useThrottleCallback } from '@react-hook/throttle';
 import { AxiosInstance, AxiosResponse } from 'axios';
 import { Dispatch, useCallback, useContext, useEffect } from 'react';
-import { LocalAction, stateSet } from '../actions';
+import { LocalAction, songInfoFetched, stateSet } from '../actions';
 import { DispatchContext } from '../context/state';
 import { NullSong, Song, songExists } from '../types';
 import { getApiUrl } from '../utils/url';
@@ -25,7 +25,7 @@ function useNextOrPrevSong(
   useEffect(() => {
     if (response) {
       if (songExists(response)) {
-        dispatch(stateSet({ songId: response.id, seekTime: 0, playing: true }));
+        dispatch(songInfoFetched(response, true));
       } else {
         dispatch(stateSet({ songId: null, playing: false, seekTime: -1 }));
       }

+ 14 - 0
gmus-web/src/reducer/reducer.spec.ts

@@ -516,5 +516,19 @@ describe(globalReducer.name, () => {
       const result = globalReducer(initialState, action);
       expect(result.songInfo).toStrictEqual<Song>(song);
     });
+
+    describe('when set to replace the current song', () => {
+      const actionReplace = songInfoFetched(song, true);
+
+      it('should play the given song from the start', () => {
+        expect.assertions(4);
+        const result = globalReducer(initialState, actionReplace);
+
+        expect(result.songInfo).toStrictEqual<Song>(song);
+        expect(result.player.playing).toBe(true);
+        expect(result.player.songId).toBe(song.id);
+        expect(result.player.seekTime).toBe(0);
+      });
+    });
   });
 });

+ 18 - 1
gmus-web/src/reducer/reducer.ts

@@ -1,4 +1,5 @@
 import {
+  ActionSongInfoFetched,
   ActionStateSetLocal,
   ActionStateSetRemote,
   ActionTypeLocal,
@@ -58,6 +59,22 @@ function onLocalStateSet(state: GlobalState, action: ActionStateSetLocal): Globa
   return state;
 }
 
+function onSongFetched(state: GlobalState, action: ActionSongInfoFetched): GlobalState {
+  const nextState: GlobalState = { ...state, songInfo: action.payload.song };
+  if (!action.payload.replace) {
+    return nextState;
+  }
+  return {
+    ...nextState,
+    player: {
+      ...state.player,
+      playing: !!action.payload.song,
+      songId: action.payload.song?.id ?? null,
+      seekTime: 0,
+    },
+  };
+}
+
 export function globalReducer(state: GlobalState, action: AnyAction): GlobalState {
   switch (action.type) {
     case ActionTypeRemote.StateSet:
@@ -107,7 +124,7 @@ export function globalReducer(state: GlobalState, action: AnyAction): GlobalStat
       return { ...state, player: { ...state.player, playing: !state.player.playing } };
 
     case ActionTypeLocal.SongInfoFetched:
-      return { ...state, songInfo: action.payload };
+      return onSongFetched(state, action);
 
     default:
       return state;