EditorNavbarBottom.tsx 5.6 KB

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