NotificationSetting.jsx 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. import React, {
  2. useCallback, useEffect, useMemo, useState,
  3. } from 'react';
  4. import PropTypes from 'prop-types';
  5. import {
  6. TabContent, TabPane,
  7. } from 'reactstrap';
  8. import { useTranslation } from 'react-i18next';
  9. import { SlackbotType } from '@growi/slack';
  10. import loggerFactory from '~/utils/logger';
  11. import { withUnstatedContainers } from '../../UnstatedUtils';
  12. import { toastError } from '~/client/util/apiNotification';
  13. import { toArrayIfNot } from '~/utils/array-utils';
  14. import { withLoadingSppiner } from '../../SuspenseUtils';
  15. import AdminNotificationContainer from '~/client/services/AdminNotificationContainer';
  16. import { CustomNavTab } from '../../CustomNavigation/CustomNav';
  17. import UserTriggerNotification from './UserTriggerNotification';
  18. import GlobalNotification from './GlobalNotification';
  19. const logger = loggerFactory('growi:NotificationSetting');
  20. let retrieveErrors = null;
  21. // eslint-disable-next-line react/prop-types
  22. const Badge = ({ isEnabled }) => {
  23. const { t } = useTranslation();
  24. return isEnabled
  25. ? <span className="badge badge-success">{t('admin:external_notification.enabled')}</span>
  26. : <span className="badge badge-secondary">{t('admin:external_notification.disabled')}</span>;
  27. };
  28. const SkeltonListItem = () => (
  29. <li className="list-group-item">
  30. <h4 className="mb-2">
  31. <span className="badge badge-secondary">――</span>
  32. <span className="ml-2">...</span>
  33. </h4>
  34. </li>
  35. );
  36. // eslint-disable-next-line react/prop-types
  37. const SlackIntegrationListItem = ({ isEnabled, currentBotType }) => {
  38. const { t } = useTranslation();
  39. const isCautionVisible = currentBotType === SlackbotType.OFFICIAL || currentBotType === SlackbotType.CUSTOM_WITH_PROXY;
  40. return (
  41. <li className="list-group-item">
  42. <h4>
  43. <Badge isEnabled={isEnabled} />
  44. <a href="/admin/slack-integration" className="ml-2">{t('slack_integration')}</a>
  45. </h4>
  46. { isCautionVisible && (
  47. <ul className="mt-2 pl-4">
  48. {/* eslint-disable-next-line react/no-danger */}
  49. <li dangerouslySetInnerHTML={{ __html: t('admin:external_notification.caution_enabled') }} />
  50. </ul>
  51. ) }
  52. </li>
  53. );
  54. };
  55. // eslint-disable-next-line react/prop-types
  56. const LegacySlackIntegrationListItem = ({ isEnabled }) => {
  57. const { t } = useTranslation();
  58. return (
  59. <li className="list-group-item">
  60. <h4>
  61. <Badge isEnabled={isEnabled} />
  62. <a href="/admin/slack-integration-legacy" className="ml-2">{t('legacy_slack_integration')}</a>
  63. </h4>
  64. { isEnabled && (
  65. <ul className="mt-2 pl-4">
  66. <li>
  67. {/* eslint-disable-next-line react/no-danger */}
  68. <span className="text-danger" dangerouslySetInnerHTML={{ __html: t('admin:slack_integration_legacy.alert_deplicated') }}></span>
  69. </li>
  70. </ul>
  71. ) }
  72. </li>
  73. );
  74. };
  75. function NotificationSetting(props) {
  76. const { adminNotificationContainer } = props;
  77. const { t } = useTranslation();
  78. const [isMounted, setMounted] = useState(false);
  79. const [activeTab, setActiveTab] = useState('user_trigger_notification');
  80. const [activeComponents, setActiveComponents] = useState(new Set(['user_trigger_notification']));
  81. const switchActiveTab = (selectedTab) => {
  82. setActiveTab(selectedTab);
  83. setActiveComponents(activeComponents.add(selectedTab));
  84. };
  85. const fetchData = useCallback(async() => {
  86. try {
  87. await adminNotificationContainer.retrieveNotificationData();
  88. }
  89. catch (err) {
  90. const errs = toArrayIfNot(err);
  91. toastError(errs);
  92. logger.error(errs);
  93. retrieveErrors = errs;
  94. }
  95. finally {
  96. setMounted(true);
  97. }
  98. }, [adminNotificationContainer]);
  99. useEffect(() => {
  100. fetchData();
  101. }, [fetchData]);
  102. const navTabMapping = useMemo(() => {
  103. return {
  104. user_trigger_notification: {
  105. Icon: () => <i className="icon-settings" />,
  106. i18n: 'User trigger notification',
  107. index: 0,
  108. },
  109. global_notification: {
  110. Icon: () => <i className="icon-settings" />,
  111. i18n: 'Global notification',
  112. index: 1,
  113. },
  114. };
  115. }, []);
  116. const { isSlackbotConfigured, isSlackLegacyConfigured, currentBotType } = adminNotificationContainer.state;
  117. const isSlackEnabled = isSlackbotConfigured;
  118. const isSlackLegacyEnabled = !isSlackbotConfigured && isSlackLegacyConfigured;
  119. return (
  120. <>
  121. <h2 className="admin-setting-header">{t('admin:external_notification.header_status')}</h2>
  122. <ul className="list-group">
  123. { !isMounted && <SkeltonListItem />}
  124. { isMounted && (
  125. <>
  126. <SlackIntegrationListItem isEnabled={isSlackEnabled} currentBotType={currentBotType} />
  127. {/* Legacy Slack Integration become visible only when new Slack Integration is disabled */}
  128. { !isSlackEnabled && <LegacySlackIntegrationListItem isEnabled={isSlackLegacyEnabled} /> }
  129. </>
  130. ) }
  131. </ul>
  132. <h2 className="admin-setting-header mt-5">{t('Notification Settings')}</h2>
  133. <CustomNavTab activeTab={activeTab} navTabMapping={navTabMapping} onNavSelected={switchActiveTab} hideBorderBottom />
  134. <TabContent activeTab={activeTab} className="p-5">
  135. <TabPane tabId="user_trigger_notification">
  136. {activeComponents.has('user_trigger_notification') && <UserTriggerNotification />}
  137. </TabPane>
  138. <TabPane tabId="global_notification">
  139. {activeComponents.has('global_notification') && <GlobalNotification />}
  140. </TabPane>
  141. </TabContent>
  142. </>
  143. );
  144. }
  145. const NotificationSettingWithUnstatedContainer = withUnstatedContainers(withLoadingSppiner(NotificationSetting), [AdminNotificationContainer]);
  146. NotificationSetting.propTypes = {
  147. adminNotificationContainer: PropTypes.instanceOf(AdminNotificationContainer).isRequired,
  148. };
  149. export default NotificationSettingWithUnstatedContainer;