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

Merge pull request #4569 from weseek/feat/80098-appearance-of-in-app-notification-settings

Feat/80097 appearance of in app notification settings
Shun Miyazawa 4 лет назад
Родитель
Сommit
5096297234

+ 7 - 0
packages/app/resource/locales/en_US/translation.json

@@ -256,6 +256,13 @@
       "This tree": "Only children of this tree"
     }
   },
+  "in_app_notification_settings": {
+    "in_app_notification_settings": "In-App Notification Settings",
+    "subscribe_settings": "Settings to automatically subscribe (Receive notifications) to pages",
+    "default_subscribe_rules": {
+      "page_create": "Subscribe to the page when you create it."
+    }
+  },
   "editor_settings": {
     "editor_settings": "Editor Settings",
     "common_settings": {

+ 7 - 0
packages/app/resource/locales/ja_JP/translation.json

@@ -258,6 +258,13 @@
       "This tree": "この階層下の子ページのみ"
     }
   },
+  "in_app_notification_settings": {
+    "in_app_notification_settings": "アプリ内通知設定",
+    "subscribe_settings": "自動でページをサブスクライブする(通知を受け取る)設定",
+    "default_subscribe_rules": {
+      "page_create": "ページを作成した時にそのページをサブスクライブします。"
+    }
+  },
   "editor_settings": {
     "editor_settings": "エディター設定",
     "common_settings": {

+ 7 - 0
packages/app/resource/locales/zh_CN/translation.json

@@ -236,6 +236,13 @@
 			"All pages": "所有页面",
 			"This tree": "当前分支以下内容"
 		}
+	},
+  "in_app_notification_settings": {
+    "in_app_notification_settings": "在应用程序通知设置",
+    "subscribe_settings": "自动订阅(接收通知)页面的设置",
+    "default_subscribe_rules": {
+      "page_create": "创建页面时订阅页面。"
+    }
   },
   "editor_settings": {
     "editor_settings": "编辑器设置",

+ 111 - 0
packages/app/src/components/Me/InAppNotificationSettings.tsx

@@ -0,0 +1,111 @@
+import React, {
+  FC, useState, useEffect, useCallback,
+} from 'react';
+
+import { useTranslation } from 'react-i18next';
+import { pullAllBy } from 'lodash';
+import { apiv3Get, apiv3Put } from '~/client/util/apiv3-client';
+import { toastSuccess, toastError } from '~/client/util/apiNotification';
+import { subscribeRuleNames, SubscribeRuleDescriptions } from '~/interfaces/in-app-notification-settings';
+
+type SubscribeRule = {
+  name: string,
+  isEnabled: boolean,
+}
+
+const subscribeRulesMenuItems = [
+  {
+    name: subscribeRuleNames.PAGE_CREATE,
+    description: SubscribeRuleDescriptions.PAGE_CREATE,
+  },
+];
+
+const isCheckedRule = (ruleName: string, subscribeRules: SubscribeRule[]) => (
+  subscribeRules.find(stateRule => (
+    stateRule.name === ruleName
+  ))?.isEnabled || false
+);
+
+const updateIsEnabled = (subscribeRules: SubscribeRule[], ruleName: string, isChecked: boolean) => {
+  const target = [{ name: ruleName, isEnabled: isChecked }];
+  return pullAllBy(subscribeRules, target, 'name').concat(target);
+};
+
+
+const InAppNotificationSettings: FC = () => {
+  const { t } = useTranslation();
+  const [subscribeRules, setSubscribeRules] = useState<SubscribeRule[]>([]);
+
+  const initializeInAppNotificationSettings = useCallback(async() => {
+    const { data } = await apiv3Get('/personal-setting/in-app-notification-settings');
+    const retrievedRules: SubscribeRule[] | null = data?.subscribeRules;
+
+    if (retrievedRules != null && retrievedRules.length > 0) {
+      setSubscribeRules(retrievedRules);
+    }
+  }, []);
+
+  const ruleCheckboxHandler = useCallback((ruleName: string, isChecked: boolean) => {
+    setSubscribeRules(prevState => updateIsEnabled(prevState, ruleName, isChecked));
+  }, []);
+
+  const updateSettingsHandler = useCallback(async() => {
+    try {
+      const { data } = await apiv3Put('/personal-setting/in-app-notification-settings', { subscribeRules });
+      setSubscribeRules(data.subscribeRules);
+      toastSuccess(t('toaster.update_successed', { target: 'InAppNotification Settings' }));
+    }
+    catch (err) {
+      toastError(err);
+    }
+  }, [subscribeRules, setSubscribeRules, t]);
+
+  useEffect(() => {
+    initializeInAppNotificationSettings();
+  }, [initializeInAppNotificationSettings]);
+
+  return (
+    <>
+      <h2 className="border-bottom my-4">{t('in_app_notification_settings.subscribe_settings')}</h2>
+
+      <div className="form-group row">
+        <div className="offset-md-3 col-md-6 text-left">
+          {subscribeRulesMenuItems.map(rule => (
+            <div
+              key={rule.name}
+              className="custom-control custom-switch custom-checkbox-success"
+            >
+              <input
+                type="checkbox"
+                className="custom-control-input"
+                id={rule.name}
+                checked={isCheckedRule(rule.name, subscribeRules)}
+                onChange={e => ruleCheckboxHandler(rule.name, e.target.checked)}
+              />
+              <label className="custom-control-label" htmlFor={rule.name}>
+                <strong>{rule.name}</strong>
+              </label>
+              <p className="form-text text-muted small">
+                {t(rule.description)}
+              </p>
+            </div>
+          ))}
+        </div>
+      </div>
+
+      <div className="row my-3">
+        <div className="offset-4 col-5">
+          <button
+            type="button"
+            className="btn btn-primary"
+            onClick={updateSettingsHandler}
+          >
+            {t('Update')}
+          </button>
+        </div>
+      </div>
+    </>
+  );
+};
+
+export default InAppNotificationSettings;

+ 7 - 0
packages/app/src/components/Me/PersonalSettings.jsx

@@ -9,6 +9,7 @@ import PasswordSettings from './PasswordSettings';
 import ExternalAccountLinkedMe from './ExternalAccountLinkedMe';
 import ApiSettings from './ApiSettings';
 import { EditorSettings } from './EditorSettings';
+import InAppNotificationSettings from './InAppNotificationSettings';
 
 const PersonalSettings = (props) => {
 
@@ -46,6 +47,12 @@ const PersonalSettings = (props) => {
         i18n: t('editor_settings.editor_settings'),
         index: 4,
       },
+      in_app_notification_settings: {
+        Icon: () => <i className="icon-fw icon-bell"></i>,
+        Content: InAppNotificationSettings,
+        i18n: t('in_app_notification_settings.in_app_notification_settings'),
+        index: 5,
+      },
     };
   }, [t]);
 

+ 8 - 4
packages/app/src/interfaces/in-app-notification-settings.ts

@@ -1,13 +1,17 @@
 import { Schema } from 'mongoose';
 
-export enum subscribeRules {
+export enum subscribeRuleNames {
   PAGE_CREATE = 'PAGE_CREATE'
 }
-export interface IDefaultSubscribeRule {
-  name: subscribeRules;
+
+export enum SubscribeRuleDescriptions {
+  PAGE_CREATE = 'in_app_notification_settings.default_subscribe_rules.page_create',
+}
+export interface ISubscribeRule {
+  name: subscribeRuleNames;
   isEnabled: boolean;
 }
 export interface IInAppNotificationSettings {
   userId: Schema.Types.ObjectId;
-  defaultSubscribeRules: IDefaultSubscribeRule[];
+  subscribeRules: ISubscribeRule[];
 }

+ 3 - 3
packages/app/src/server/models/in-app-notification-settings.ts

@@ -1,16 +1,16 @@
 import { Schema, Model, Document } from 'mongoose';
 import { getOrCreateModel } from '@growi/core';
 
-import { IInAppNotificationSettings, subscribeRules } from '../../interfaces/in-app-notification-settings';
+import { IInAppNotificationSettings, subscribeRuleNames } from '../../interfaces/in-app-notification-settings';
 
 export interface InAppNotificationSettingsDocument extends IInAppNotificationSettings, Document {}
 export type InAppNotificationSettingsModel = Model<InAppNotificationSettingsDocument>
 
 const inAppNotificationSettingsSchema = new Schema<IInAppNotificationSettings>({
   userId: { type: String },
-  defaultSubscribeRules: [
+  subscribeRules: [
     {
-      name: { type: String, require: true, enum: subscribeRules },
+      name: { type: String, require: true, enum: subscribeRuleNames },
       isEnabled: { type: Boolean },
     },
   ],

+ 3 - 3
packages/app/src/server/routes/apiv3/personal-setting.js

@@ -577,15 +577,15 @@ module.exports = (crowi) => {
   // eslint-disable-next-line max-len
   router.put('/in-app-notification-settings', accessTokenParser, loginRequiredStrictly, csrf, validator.inAppNotificationSettings, apiV3FormValidator, async(req, res) => {
     const query = { userId: req.user.id };
-    const defaultSubscribeRules = req.body.defaultSubscribeRules;
+    const subscribeRules = req.body.subscribeRules;
 
-    if (defaultSubscribeRules == null) {
+    if (subscribeRules == null) {
       return res.apiv3Err('no-rules-found');
     }
 
     const options = { upsert: true, new: true, runValidators: true };
     try {
-      const response = await InAppNotificationSettings.findOneAndUpdate(query, { defaultSubscribeRules }, options);
+      const response = await InAppNotificationSettings.findOneAndUpdate(query, { subscribeRules }, options);
       return res.apiv3(response);
     }
     catch (err) {