wrapper.tsx 3.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. import React, { useContext, useEffect, useRef } from 'react';
  2. import { useReducer } from 'reinspect';
  3. import { DispatchContext } from '../../../context/state';
  4. import { useVimBindings } from '../../../hooks/vim';
  5. import { init } from '../../../utils/state';
  6. import { UIProviderComponent } from '../types';
  7. import {
  8. CmusUIDispatchContext,
  9. cmusUIReducer,
  10. CmusUIStateContext,
  11. initialCmusUIState,
  12. } from './reducer';
  13. import { Overlay, View } from './types';
  14. import { useLibrary } from './utils/library';
  15. import { ViewClientList } from './views/clients';
  16. import { CommandView } from './views/command';
  17. import { HelpDialog } from './views/help';
  18. import { ViewLibrary } from './views/library';
  19. import { ViewQueue } from './views/queue';
  20. import { PlayerStatus } from './views/status';
  21. import * as Styled from './wrapper.styles';
  22. const viewTitles = Object.values(View);
  23. export const CmusUIProvider: UIProviderComponent = ({ currentSong, nextSong, prevSong }) => {
  24. const dispatch = useContext(DispatchContext);
  25. const [stateUI, dispatchUI] = useReducer(cmusUIReducer, initialCmusUIState, init, 'ui');
  26. useEffect(() => {
  27. if (stateUI.globalAction) {
  28. dispatch(stateUI.globalAction);
  29. }
  30. }, [dispatch, stateUI.globalAction, stateUI.globalActionSerialNumber]);
  31. const lastSkipSerialNumber = useRef<number>(0);
  32. useEffect(() => {
  33. if (lastSkipSerialNumber.current !== stateUI.skipSong.serialNumber) {
  34. lastSkipSerialNumber.current = stateUI.skipSong.serialNumber;
  35. if (stateUI.skipSong.delta === 1) {
  36. nextSong();
  37. } else if (stateUI.skipSong.delta === -1) {
  38. prevSong();
  39. }
  40. }
  41. }, [stateUI.skipSong, nextSong, prevSong]);
  42. useVimBindings(dispatchUI, stateUI.commandMode);
  43. useLibrary(stateUI, dispatchUI);
  44. const showOverlay = !!stateUI.overlay || stateUI.view === View.ClientList;
  45. return (
  46. <CmusUIStateContext.Provider value={stateUI}>
  47. <CmusUIDispatchContext.Provider value={dispatchUI}>
  48. <Styled.Wrapper>
  49. <Styled.ViewTitle>
  50. gmus -{' '}
  51. {viewTitles.map((view, index) => (
  52. <Styled.ViewTitleItem key={view} active={view === stateUI.view}>
  53. ({index + 1}) {view}
  54. </Styled.ViewTitleItem>
  55. ))}
  56. </Styled.ViewTitle>
  57. <Styled.View>
  58. {(stateUI.view === View.Library || stateUI.view === View.ClientList) && (
  59. <ViewLibrary currentArtist={currentSong?.artist ?? null} />
  60. )}
  61. {stateUI.view === View.Queue && <ViewQueue currentSong={currentSong} />}
  62. </Styled.View>
  63. {showOverlay && (
  64. <Styled.Overlay>
  65. {!stateUI.overlay && stateUI.view === View.ClientList && <ViewClientList />}
  66. {stateUI.overlay === Overlay.Help && <HelpDialog />}
  67. </Styled.Overlay>
  68. )}
  69. <PlayerStatus song={currentSong} />
  70. <CommandView />
  71. </Styled.Wrapper>
  72. </CmusUIDispatchContext.Provider>
  73. </CmusUIStateContext.Provider>
  74. );
  75. };