Explorar o código

feat: action and effect to toggle client active state

Fela Maslen %!s(int64=4) %!d(string=hai) anos
pai
achega
fe3cd26185

+ 8 - 0
gmus-web/src/actions/actions.ts

@@ -45,6 +45,13 @@ export const masterSet = (name?: string): ActionMasterSet => ({
   payload: name,
 });
 
+export type ActionActiveClientToggled = ActionLocal<ActionTypeLocal.ActiveClientToggled, string>;
+
+export const activeClientToggled = (client: string): ActionActiveClientToggled => ({
+  type: ActionTypeLocal.ActiveClientToggled,
+  payload: client,
+});
+
 export type ActionPlayPaused = ActionLocal<ActionTypeLocal.PlayPaused, void>;
 
 export const playPaused = (): ActionPlayPaused => ({
@@ -97,6 +104,7 @@ export type LocalAction =
   | ActionSeeked
   | ActionPlayPaused
   | ActionMasterSet
+  | ActionActiveClientToggled
   | ActionSongInfoFetched
   | ActionQueuePushed
   | ActionQueueShifted

+ 1 - 0
gmus-web/src/actions/types.ts

@@ -12,6 +12,7 @@ export enum ActionTypeLocal {
   StateSet = '@@local/STATE_SET',
   Seeked = '@@local/SEEKED',
   MasterSet = '@@local/MASTER_SET',
+  ActiveClientToggled = '@@local/ACTIVE_CLIENT_TOGGLED',
   PlayPaused = '@@local/PLAY_PAUSED',
   SongInfoFetched = '@@local/SONG_INFO_FETCHED',
   QueuePushed = '@@local/QUEUE_PUSHED',

+ 58 - 0
gmus-web/src/effects/effects.spec.ts

@@ -2,6 +2,7 @@ import {
   ActionStateSetRemote,
   ActionTypeLocal,
   ActionTypeRemote,
+  activeClientToggled,
   masterSet,
   playPaused,
   queueOrdered,
@@ -28,6 +29,7 @@ describe(globalEffects.name, () => {
         currentTime: 83,
         seekTime: 87,
         master: 'my-client',
+        activeClients: [],
         queue: [],
       };
 
@@ -56,6 +58,7 @@ describe(globalEffects.name, () => {
         currentTime: 83,
         seekTime: 87,
         master: 'my-client-name',
+        activeClients: [],
         queue: [],
       },
       myClientName: 'my-client-name',
@@ -97,6 +100,7 @@ describe(globalEffects.name, () => {
         currentTime: 83,
         seekTime: 5,
         master: 'some-master-went-away',
+        activeClients: [],
         queue: [],
       },
       myClientName: 'my-client-name',
@@ -116,6 +120,7 @@ describe(globalEffects.name, () => {
           currentTime: 83,
           seekTime: -1,
           master: 'my-client-name',
+          activeClients: [],
           queue: [],
         },
       });
@@ -134,6 +139,7 @@ describe(globalEffects.name, () => {
             currentTime: 83,
             seekTime: 83,
             master: 'other-client',
+            activeClients: [],
             queue: [],
           },
         });
@@ -141,6 +147,54 @@ describe(globalEffects.name, () => {
     });
   });
 
+  describe(ActionTypeLocal.ActiveClientToggled, () => {
+    const action = activeClientToggled('some-client');
+
+    describe('when the given client is active', () => {
+      const stateWithGivenClientActive: GlobalState = {
+        ...initialState,
+        player: {
+          ...initialState.player,
+          activeClients: ['some-client', 'other-client'],
+        },
+      };
+
+      it('should remove the given client from the active clients list', () => {
+        expect.assertions(1);
+        const result = globalEffects(stateWithGivenClientActive, action);
+
+        expect(result).toStrictEqual<ActionStateSetRemote>({
+          type: ActionTypeRemote.StateSet,
+          payload: expect.objectContaining({
+            activeClients: ['other-client'],
+          }),
+        });
+      });
+    });
+
+    describe('when the given client is not active', () => {
+      const stateWithGivenClientInactive: GlobalState = {
+        ...initialState,
+        player: {
+          ...initialState.player,
+          activeClients: ['other-client'],
+        },
+      };
+
+      it('should add the given client to the active clients list', () => {
+        expect.assertions(1);
+        const result = globalEffects(stateWithGivenClientInactive, action);
+
+        expect(result).toStrictEqual<ActionStateSetRemote>({
+          type: ActionTypeRemote.StateSet,
+          payload: expect.objectContaining({
+            activeClients: expect.arrayContaining(['some-client', 'other-client']),
+          }),
+        });
+      });
+    });
+  });
+
   describe(ActionTypeLocal.PlayPaused, () => {
     const statePriorMaster: GlobalState = {
       ...initialState,
@@ -150,6 +204,7 @@ describe(globalEffects.name, () => {
         currentTime: 83,
         seekTime: 5,
         master: 'some-master-client',
+        activeClients: [],
         queue: [],
       },
       myClientName: 'some-master-client',
@@ -182,6 +237,7 @@ describe(globalEffects.name, () => {
             currentTime: 83,
             seekTime: 5,
             master: 'some-master-client',
+            activeClients: [],
             queue: [],
           },
         });
@@ -198,6 +254,7 @@ describe(globalEffects.name, () => {
         currentTime: 83,
         seekTime: 5,
         master: 'some-master-client',
+        activeClients: [],
         queue: [],
       },
       myClientName: 'some-master-client',
@@ -230,6 +287,7 @@ describe(globalEffects.name, () => {
             currentTime: 0,
             seekTime: 0,
             master: 'some-master-client',
+            activeClients: [],
             queue: [],
           },
         });

+ 11 - 0
gmus-web/src/effects/effects.ts

@@ -81,6 +81,17 @@ export function globalEffects(prevState: GlobalState, action: LocalAction): Remo
         },
       };
 
+    case ActionTypeLocal.ActiveClientToggled:
+      return {
+        type: ActionTypeRemote.StateSet,
+        payload: {
+          ...prevState.player,
+          activeClients: prevState.player.activeClients.includes(action.payload)
+            ? prevState.player.activeClients.filter((client) => client !== action.payload)
+            : [...prevState.player.activeClients, action.payload],
+        },
+      };
+
     case ActionTypeLocal.PlayPaused:
       if (isMaster(prevState)) {
         return null;