Просмотр исходного кода

Merge pull request #1498 from weseek/reactify-admin/slack-app-configuration-component

Reactify admin/slack app configuration component
Yuki Takei 6 лет назад
Родитель
Сommit
60cf3dd88d

+ 2 - 1
resource/locales/en-US/translation.json

@@ -648,7 +648,8 @@
     "event_comment": "When someone \"COMMENTS\" on page",
     "event_comment": "When someone \"COMMENTS\" on page",
     "email": {
     "email": {
       "ifttt_link": "Create a new IFTTT applet with Email trigger"
       "ifttt_link": "Create a new IFTTT applet with Email trigger"
-    }
+    },
+    "updated_slackApp": "Succeeded to update Slack App Configuration setting"
   },
   },
 
 
   "customize_page": {
   "customize_page": {

+ 2 - 1
resource/locales/ja/translation.json

@@ -632,7 +632,8 @@
     "event_comment": "コメントが投稿されたとき",
     "event_comment": "コメントが投稿されたとき",
     "email": {
     "email": {
       "ifttt_link": "IFTTT でメールトリガの新しいアプレットを作る"
       "ifttt_link": "IFTTT でメールトリガの新しいアプレットを作る"
-    }
+    },
+    "updated_slackApp": "SlackApp設定を更新しました"
   },
   },
 
 
   "customize_page": {
   "customize_page": {

+ 2 - 1
src/client/js/components/Admin/Notification/NotificationSetting.jsx

@@ -5,6 +5,7 @@ import { withTranslation } from 'react-i18next';
 import { createSubscribedElement } from '../../UnstatedUtils';
 import { createSubscribedElement } from '../../UnstatedUtils';
 
 
 import AppContainer from '../../../services/AppContainer';
 import AppContainer from '../../../services/AppContainer';
+import SlackAppConfiguration from './SlackAppConfiguration';
 
 
 class NotificationSetting extends React.Component {
 class NotificationSetting extends React.Component {
 
 
@@ -26,7 +27,7 @@ class NotificationSetting extends React.Component {
 
 
         <div className="tab-content m-t-15">
         <div className="tab-content m-t-15">
           <div id="slack-configuration" className="tab-pane active" role="tabpanel">
           <div id="slack-configuration" className="tab-pane active" role="tabpanel">
-            {/* TODO GW-773 create slak config component */}
+            <SlackAppConfiguration />
           </div>
           </div>
           <div id="user-trigger-notification" className="tab-pane" role="tabpanel">
           <div id="user-trigger-notification" className="tab-pane" role="tabpanel">
             {/* TODO GW-775 user trigger notification component */}
             {/* TODO GW-775 user trigger notification component */}

+ 189 - 0
src/client/js/components/Admin/Notification/SlackAppConfiguration.jsx

@@ -0,0 +1,189 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withTranslation } from 'react-i18next';
+
+import loggerFactory from '@alias/logger';
+
+import { createSubscribedElement } from '../../UnstatedUtils';
+import { toastSuccess, toastError } from '../../../util/apiNotification';
+
+import AppContainer from '../../../services/AppContainer';
+import AdminNotificationContainer from '../../../services/AdminNotificationContainer';
+import AdminUpdateButtonRow from '../Common/AdminUpdateButtonRow';
+
+const logger = loggerFactory('growi:slackAppConfiguration');
+
+class SlackAppConfiguration extends React.Component {
+
+  constructor(props) {
+    super(props);
+
+    this.state = {
+      retrieveError: null,
+    };
+
+    this.onClickSubmit = this.onClickSubmit.bind(this);
+  }
+
+  async onClickSubmit() {
+    const { t, adminNotificationContainer } = this.props;
+
+    try {
+      await adminNotificationContainer.updateSlackAppConfiguration();
+      toastSuccess(t('notification_setting.updated_slackApp'));
+    }
+    catch (err) {
+      toastError(err);
+      logger.error(err);
+    }
+  }
+
+  // TODO GW-788 i18n
+  render() {
+    const { adminNotificationContainer } = this.props;
+
+    return (
+      <React.Fragment>
+        <div className="row mb-5">
+          <div className="col-xs-6 text-left">
+            <div className="my-0 btn-group">
+              <div className="dropdown">
+                <button className="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+                  <span className="pull-left">Slack {adminNotificationContainer.state.selectSlackOption} </span>
+                  <span className="bs-caret pull-right">
+                    <span className="caret" />
+                  </span>
+                </button>
+                {/* TODO adjust dropdown after BS4 */}
+                <ul className="dropdown-menu" role="menu">
+                  <li type="button" onClick={() => adminNotificationContainer.switchSlackOption('Incoming Webhooks')}>
+                    <a role="menuitem">Slack Incoming Webhooks</a>
+                  </li>
+                  <li type="button" onClick={() => adminNotificationContainer.switchSlackOption('App')}>
+                    <a role="menuitem">Slack App</a>
+                  </li>
+                </ul>
+              </div>
+            </div>
+          </div>
+        </div>
+        {adminNotificationContainer.state.selectSlackOption === 'Incoming Webhooks' ? (
+          <React.Fragment>
+            <h2 className="border-bottom mb-5">Slack Incoming Webhooks Configuration</h2>
+
+            <div className="row mb-5">
+              <label className="col-xs-3 text-right">Webhook URL</label>
+              <div className="col-xs-6">
+                <input
+                  className="form-control"
+                  type="text"
+                  defaultValue={adminNotificationContainer.state.webhookUrl}
+                  onChange={e => adminNotificationContainer.changeWebhookUrl(e.target.value)}
+                />
+              </div>
+            </div>
+
+            <div className="row mb-5">
+              <div className="col-xs-offset-3 col-xs-6 text-left">
+                <div className="checkbox checkbox-success">
+                  <input
+                    id="cbPrioritizeIWH"
+                    type="checkbox"
+                    checked={adminNotificationContainer.state.isIncomingWebhookPrioritized}
+                    onChange={() => { adminNotificationContainer.switchIsIncomingWebhookPrioritized() }}
+                  />
+                  <label htmlFor="cbPrioritizeIWH">
+                    Prioritize Incoming Webhook than Slack App
+                  </label>
+                </div>
+                <p className="help-block">
+                  Check this option and GROWI use Incoming Webhooks even if Slack App settings are enabled.
+                </p>
+              </div>
+            </div>
+          </React.Fragment>
+        )
+          : (
+            <React.Fragment>
+              <h2 className="border-bottom mb-5">Slack App Configuration</h2>
+
+              <div className="well">
+                <i className="icon-fw icon-exclamation text-danger"></i><span className="text-danger">NOT RECOMMENDED</span>
+                <br /><br />
+                This is the way that compatible with Crowi,<br />
+                but not recommended in GROWI because it is <strong>too complex</strong>.
+                <br /><br />
+                Please use
+                <a
+                  href="#slack-incoming-webhooks"
+                  data-toggle="tab"
+                  onClick={() => adminNotificationContainer.switchSlackOption('Incoming Webhooks')}
+                >
+                  Slack incomming webhooks Configuration
+                </a>
+                instead.
+              </div>
+
+              <div className="row mb-5">
+                <label className="col-xs-3 text-right">OAuth Access Token</label>
+                <div className="col-xs-6">
+                  <input
+                    className="form-control"
+                    type="text"
+                    defaultValue={adminNotificationContainer.state.slackToken}
+                    onChange={e => adminNotificationContainer.changeSlackToken(e.target.value)}
+                  />
+                </div>
+              </div>
+
+            </React.Fragment>
+          )
+        }
+
+        <AdminUpdateButtonRow
+          onClick={this.onClickSubmit}
+          disabled={this.state.retrieveError != null}
+        />
+
+        <hr />
+
+        <h3>
+          <i className="icon-question" aria-hidden="true"></i>
+          <a href="#collapseHelpForIwh" data-toggle="collapse"> How to configure Incoming Webhooks?</a>
+        </h3>
+
+        <ol id="collapseHelpForIwh" className="collapse">
+          <li>
+            (At Workspace) Add a hook
+            <ol>
+              <li>Go to <a href="https://slack.com/services/new/incoming-webhook">Incoming Webhooks Configuration page</a>.</li>
+              <li>Choose the default channel to post.</li>
+              <li>Add.</li>
+            </ol>
+          </li>
+          <li>
+            (At GROWI admin page) Set Webhook URL
+            <ol>
+              <li>Input &rdquo;Webhook URL&rdquo; and submit on this page.</li>
+            </ol>
+          </li>
+        </ol>
+
+      </React.Fragment>
+    );
+  }
+
+}
+
+const SlackAppConfigurationWrapper = (props) => {
+  return createSubscribedElement(SlackAppConfiguration, props, [AppContainer, AdminNotificationContainer]);
+};
+
+SlackAppConfiguration.propTypes = {
+  t: PropTypes.func.isRequired, // i18next
+  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
+  adminNotificationContainer: PropTypes.instanceOf(AdminNotificationContainer).isRequired,
+
+};
+
+export default withTranslation()(SlackAppConfigurationWrapper);

+ 41 - 1
src/client/js/services/AdminNotificationContainer.js

@@ -12,7 +12,10 @@ export default class AdminNotificationContainer extends Container {
     this.appContainer = appContainer;
     this.appContainer = appContainer;
 
 
     this.state = {
     this.state = {
-
+      selectSlackOption: 'Incoming Webhooks',
+      webhookUrl: '',
+      isIncomingWebhookPrioritized: false,
+      slackToken: '',
     };
     };
 
 
   }
   }
@@ -24,4 +27,41 @@ export default class AdminNotificationContainer extends Container {
     return 'AdminNotificationContainer';
     return 'AdminNotificationContainer';
   }
   }
 
 
+  /**
+   * Switch slackOption
+   */
+  switchSlackOption(slackOption) {
+    this.setState({ selectSlackOption: slackOption });
+  }
+
+  /**
+   * Change webhookUrl
+   */
+  changeWebhookUrl(webhookUrl) {
+    this.setState({ webhookUrl });
+  }
+
+  /**
+   * Switch incomingWebhookPrioritized
+   */
+  switchIsIncomingWebhookPrioritized() {
+    this.setState({ isIncomingWebhookPrioritized: !this.state.isIncomingWebhookPrioritized });
+  }
+
+  /**
+   * Change slackToken
+   */
+  changeSlackToken(slackToken) {
+    this.setState({ slackToken });
+  }
+
+  /**
+   * Update slackAppConfiguration
+   * @memberOf SlackAppConfiguration
+   */
+  async updateSlackAppConfiguration() {
+    // TODO GW-794 create apiV3 updateSlackAppConfiguration
+
+  }
+
 }
 }