wrapper.tsx 3.1 KB

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