index.tsx 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. import { useThrottleCallback } from '@react-hook/throttle';
  2. import React, { Dispatch, useCallback, useEffect, useRef, useState } from 'react';
  3. import { AnyAction, stateSet } from '../../actions';
  4. import { masterStateUpdateTimeout } from '../../constants/system';
  5. import { useDispatchEffects, useKeepalive } from '../../hooks/socket';
  6. import { GlobalState } from '../../reducer/types';
  7. import { ClientList } from '../client-list';
  8. import { Player } from '../player';
  9. export type Props = {
  10. socket: WebSocket;
  11. state: GlobalState;
  12. dispatch: Dispatch<AnyAction>;
  13. };
  14. function useMaster(dispatch: Dispatch<AnyAction>, isMaster: boolean): void {
  15. const masterUpdateTimer = useRef<number>(0);
  16. useEffect(() => {
  17. if (isMaster) {
  18. masterUpdateTimer.current = window.setInterval(() => {
  19. dispatch(stateSet());
  20. }, masterStateUpdateTimeout);
  21. }
  22. return (): void => {
  23. window.clearInterval(masterUpdateTimer.current);
  24. };
  25. }, [dispatch, isMaster]);
  26. }
  27. export const Gmus: React.FC<Props> = ({ socket, state, dispatch }) => {
  28. useKeepalive(socket);
  29. useDispatchEffects(socket, state);
  30. const { clientList, player, myClientName } = state;
  31. const isMaster = player.master === myClientName;
  32. useMaster(dispatch, isMaster);
  33. const [tempSongId, setTempSongId] = useState<number>(0);
  34. const playSong = useCallback((): void => {
  35. if (!tempSongId) {
  36. return;
  37. }
  38. dispatch(
  39. stateSet({
  40. songId: tempSongId,
  41. currentTime: 0,
  42. playing: true,
  43. master: myClientName,
  44. }),
  45. );
  46. }, [dispatch, tempSongId, myClientName]);
  47. const playPause = useCallback(() => {
  48. dispatch(stateSet({ playing: !player.playing }));
  49. }, [dispatch, player.playing]);
  50. const onTimeUpdate = useCallback(
  51. (currentTime: number): void => {
  52. if (isMaster) {
  53. dispatch(stateSet({ currentTime }));
  54. }
  55. },
  56. [dispatch, isMaster],
  57. );
  58. const onTimeUpdateThrottled = useThrottleCallback(onTimeUpdate, 1000);
  59. return (
  60. <div>
  61. <div>
  62. <button onClick={playPause}>{player.playing ? 'Pause' : 'Play'}</button>
  63. </div>
  64. <div>
  65. <input
  66. onChange={({ target: { value } }): void => setTempSongId(Number(value))}
  67. type="number"
  68. min={0}
  69. step={1}
  70. />
  71. <button onClick={playSong}>Change track</button>
  72. </div>
  73. <ClientList myClientName={myClientName} clients={clientList} />
  74. <div>
  75. <h6>Player State</h6>
  76. <pre>{JSON.stringify(player, null, 2)}</pre>
  77. </div>
  78. {isMaster && !!player.songId && (
  79. <Player
  80. playing={player.playing}
  81. onTimeUpdate={onTimeUpdateThrottled}
  82. songId={player.songId}
  83. />
  84. )}
  85. </div>
  86. );
  87. };