EditorNavbarBottom.tsx 5.7 KB

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