EditorNavbarBottom.tsx 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. import React, { useCallback, useState, useEffect } from 'react';
  2. import dynamic from 'next/dynamic';
  3. import { Collapse, Button } from 'reactstrap';
  4. import { SavePageControlsProps } from '~/components/SavePageControls';
  5. import { useIsSlackConfigured } from '~/stores/context';
  6. import { useSWRxSlackChannels, useIsSlackEnabled } from '~/stores/editor';
  7. import { useCurrentPagePath } from '~/stores/page';
  8. import {
  9. EditorMode, useDrawerOpened, useEditorMode, useIsDeviceSmallerThanMd,
  10. } from '~/stores/ui';
  11. const SavePageControls = dynamic<SavePageControlsProps>(() => import('~/components/SavePageControls').then(mod => mod.SavePageControls), { ssr: false });
  12. const SlackLogo = dynamic(() => import('~/components/SlackLogo').then(mod => mod.SlackLogo), { ssr: false });
  13. const SlackNotification = dynamic(() => import('~/components/SlackNotification').then(mod => mod.SlackNotification), { ssr: false });
  14. const OptionsSelector = dynamic(() => import('~/components/PageEditor/OptionsSelector').then(mod => mod.OptionsSelector), { ssr: false });
  15. const EditorNavbarBottom = (): JSX.Element => {
  16. const [isExpanded, setExpanded] = useState(false);
  17. const [isSlackExpanded, setSlackExpanded] = useState(false);
  18. const { data: editorMode } = useEditorMode();
  19. const { data: isSlackConfigured } = useIsSlackConfigured();
  20. const { mutate: mutateDrawerOpened } = useDrawerOpened();
  21. const { data: isDeviceSmallerThanMd } = useIsDeviceSmallerThanMd();
  22. const { data: currentPagePath } = useCurrentPagePath();
  23. const { data: slackChannelsData } = useSWRxSlackChannels(currentPagePath);
  24. const { data: isSlackEnabled, mutate: mutateIsSlackEnabled } = useIsSlackEnabled();
  25. const additionalClasses = ['grw-editor-navbar-bottom'];
  26. const [slackChannelsStr, setSlackChannelsStr] = useState<string>('');
  27. // DO NOT dependent on slackChannelsData directly: https://github.com/weseek/growi/pull/7332
  28. const slackChannelsDataString = slackChannelsData?.toString();
  29. useEffect(() => {
  30. if (editorMode === 'editor') {
  31. setSlackChannelsStr(slackChannelsDataString ?? '');
  32. mutateIsSlackEnabled(false);
  33. }
  34. }, [editorMode, mutateIsSlackEnabled, slackChannelsDataString]);
  35. const isSlackEnabledToggleHandler = (bool: boolean) => {
  36. mutateIsSlackEnabled(bool, false);
  37. };
  38. const slackChannelsChangedHandler = useCallback((slackChannels: string) => {
  39. setSlackChannelsStr(slackChannels);
  40. }, []);
  41. const renderDrawerButton = () => (
  42. <button
  43. type="button"
  44. className="btn btn-outline-secondary border-0"
  45. onClick={() => mutateDrawerOpened(true)}
  46. >
  47. <i className="icon-menu"></i>
  48. </button>
  49. );
  50. const renderExpandButton = () => (
  51. <div className="d-md-none ml-2">
  52. <button
  53. type="button"
  54. className={`btn btn-outline-secondary btn-expand border-0 ${isExpanded ? 'expand' : ''}`}
  55. onClick={() => setExpanded(!isExpanded)}
  56. >
  57. <i className="icon-arrow-up"></i>
  58. </button>
  59. </div>
  60. );
  61. const isOptionsSelectorEnabled = editorMode !== EditorMode.HackMD;
  62. const isCollapsedOptionsSelectorEnabled = isOptionsSelectorEnabled && isDeviceSmallerThanMd;
  63. return (
  64. <div className={`${isCollapsedOptionsSelectorEnabled ? 'fixed-bottom' : ''} `}>
  65. {/* Collapsed SlackNotification */}
  66. {isSlackConfigured && (
  67. <Collapse isOpen={isSlackExpanded && isDeviceSmallerThanMd === true}>
  68. <nav className={`navbar navbar-expand-lg border-top ${additionalClasses.join(' ')}`}>
  69. {isSlackEnabled != null
  70. && (
  71. <SlackNotification
  72. isSlackEnabled={isSlackEnabled}
  73. slackChannels={slackChannelsStr}
  74. onEnabledFlagChange={isSlackEnabledToggleHandler}
  75. onChannelChange={slackChannelsChangedHandler}
  76. id="idForEditorNavbarBottomForMobile"
  77. />
  78. )
  79. }
  80. </nav>
  81. </Collapse>
  82. )
  83. }
  84. <div className={`flex-expand-horiz align-items-center border-top px-2 px-md-3 ${additionalClasses.join(' ')}`}>
  85. <form className="form-inline">
  86. { isDeviceSmallerThanMd && renderDrawerButton() }
  87. { isOptionsSelectorEnabled && !isDeviceSmallerThanMd && <OptionsSelector /> }
  88. </form>
  89. <form className="form-inline flex-nowrap ml-auto">
  90. {/* Responsive Design for the SlackNotification */}
  91. {/* Button or the normal Slack banner */}
  92. {isSlackConfigured && (isDeviceSmallerThanMd ? (
  93. <Button
  94. className="grw-btn-slack border mr-2"
  95. onClick={() => (setSlackExpanded(!isSlackExpanded))}
  96. >
  97. <div className="grw-slack-logo">
  98. <SlackLogo />
  99. <span className="grw-btn-slack-triangle fa fa-caret-up ml-2"></span>
  100. </div>
  101. </Button>
  102. ) : (
  103. <div className="mr-2">
  104. {isSlackEnabled != null
  105. && (
  106. <SlackNotification
  107. isSlackEnabled={isSlackEnabled}
  108. slackChannels={slackChannelsStr}
  109. onEnabledFlagChange={isSlackEnabledToggleHandler}
  110. onChannelChange={slackChannelsChangedHandler}
  111. id="idForEditorNavbarBottom"
  112. />
  113. )}
  114. </div>
  115. ))}
  116. <SavePageControls slackChannels={slackChannelsStr} />
  117. { isCollapsedOptionsSelectorEnabled && renderExpandButton() }
  118. </form>
  119. </div>
  120. {/* Collapsed OptionsSelector */}
  121. { isCollapsedOptionsSelectorEnabled && (
  122. <Collapse isOpen={isExpanded}>
  123. <div className="px-2"> {/* set padding for border-top */}
  124. <div className={`navbar navbar-expand border-top px-0 ${additionalClasses.join(' ')}`}>
  125. <form className="form-inline ml-auto">
  126. <OptionsSelector />
  127. </form>
  128. </div>
  129. </div>
  130. </Collapse>
  131. ) }
  132. </div>
  133. );
  134. };
  135. export default EditorNavbarBottom;