reducer.ts 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. import {
  2. ActionSongInfoFetched,
  3. ActionStateSetLocal,
  4. ActionStateSetRemote,
  5. ActionTypeLocal,
  6. ActionTypeRemote,
  7. AnyAction,
  8. } from '../actions';
  9. import {
  10. getNextPlayerStateFromAction,
  11. isFromOurselves,
  12. isMaster,
  13. willBeMaster,
  14. } from '../selectors';
  15. import { MusicPlayer } from '../types/state';
  16. import { GlobalState } from './types';
  17. export const nullPlayer: MusicPlayer = {
  18. songId: null,
  19. playing: false,
  20. currentTime: 0,
  21. seekTime: -1,
  22. master: '',
  23. activeClients: [],
  24. queue: [],
  25. shuffleMode: false,
  26. };
  27. export const initialState: GlobalState = {
  28. songInfo: null,
  29. player: nullPlayer,
  30. clientList: [],
  31. myClientName: '',
  32. };
  33. function shouldSetSeekTime(state: GlobalState, action: ActionStateSetRemote): boolean {
  34. return willBeMaster(state, action) && !(isMaster(state) && isFromOurselves(state, action));
  35. }
  36. function onRemoteStateSet(state: GlobalState, action: ActionStateSetRemote): GlobalState {
  37. const nextPlayer = action.payload ?? nullPlayer;
  38. const seekTime = shouldSetSeekTime(state, action) ? nextPlayer.seekTime : -1;
  39. const nextPlayerWithSeekTime: MusicPlayer = { ...nextPlayer, seekTime };
  40. return { ...state, player: nextPlayerWithSeekTime };
  41. }
  42. function onLocalStateSet(state: GlobalState, action: ActionStateSetLocal): GlobalState {
  43. const nextPlayer = getNextPlayerStateFromAction(state.player, action.payload);
  44. if (!nextPlayer) {
  45. return state;
  46. }
  47. if (isMaster(state)) {
  48. return { ...state, player: nextPlayer };
  49. }
  50. if (willBeMaster(state, action)) {
  51. return {
  52. ...state,
  53. player: {
  54. ...nextPlayer,
  55. seekTime: nextPlayer.currentTime,
  56. },
  57. };
  58. }
  59. return state;
  60. }
  61. function onSongFetched(state: GlobalState, action: ActionSongInfoFetched): GlobalState {
  62. const nextState: GlobalState = { ...state, songInfo: action.payload.song };
  63. if (!action.payload.replace) {
  64. return nextState;
  65. }
  66. return {
  67. ...nextState,
  68. player: {
  69. ...state.player,
  70. playing: !!action.payload.song,
  71. songId: action.payload.song?.id ?? null,
  72. seekTime: 0,
  73. },
  74. };
  75. }
  76. export function globalReducer(state: GlobalState, action: AnyAction): GlobalState {
  77. switch (action.type) {
  78. case ActionTypeRemote.StateSet:
  79. return onRemoteStateSet(state, action);
  80. case ActionTypeRemote.ClientListUpdated:
  81. return { ...state, clientList: action.payload };
  82. case ActionTypeLocal.NameSet:
  83. return { ...state, myClientName: action.payload };
  84. case ActionTypeLocal.StateSet:
  85. return onLocalStateSet(state, action);
  86. case ActionTypeLocal.Seeked:
  87. return {
  88. ...state,
  89. player: {
  90. ...state.player,
  91. currentTime: action.payload,
  92. seekTime: isMaster(state) ? action.payload : state.player.seekTime,
  93. },
  94. };
  95. case ActionTypeLocal.MasterSet:
  96. if (action.payload) {
  97. return {
  98. ...state,
  99. player: {
  100. ...state.player,
  101. master: action.payload,
  102. seekTime: state.player.currentTime,
  103. },
  104. };
  105. }
  106. return {
  107. ...state,
  108. player: {
  109. ...state.player,
  110. master: state.myClientName,
  111. seekTime: state.player.currentTime,
  112. playing: false,
  113. },
  114. };
  115. case ActionTypeLocal.SongInfoFetched:
  116. return onSongFetched(state, action);
  117. default:
  118. return state;
  119. }
  120. }