reducer.ts 3.4 KB

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