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

Merge branch 'feat/notification' into feat/77832-get-subscription-on-client-side

Shun Miyazawa 4 лет назад
Родитель
Сommit
9da2d80e6a

+ 1 - 1
packages/app/src/components/Navbar/GrowiSubNavigation.jsx

@@ -110,7 +110,7 @@ const GrowiSubNavigation = (props) => {
 
         <div className="d-flex flex-column align-items-end">
           <div className="d-flex">
-            <SubnavButtons isCompactMode={isCompactMode} pageId={pageId} />
+            <SubnavButtons isCompactMode={isCompactMode} />
           </div>
           <div className="mt-2">
             {pageContainer.isAbleToShowPageEditorModeManager && (

+ 2 - 3
packages/app/src/components/Navbar/SubNavButtons.jsx

@@ -12,7 +12,7 @@ import PageManagement from '../Page/PageManagement';
 
 const SubnavButtons = (props) => {
   const {
-    appContainer, navigationContainer, pageContainer, isCompactMode, pageId,
+    appContainer, navigationContainer, pageContainer, isCompactMode,
   } = props;
 
   /* eslint-enable react/prop-types */
@@ -23,7 +23,7 @@ const SubnavButtons = (props) => {
     return (
       <>
         <span>
-          <SubscribeButton pageId={pageId} />
+          <SubscribeButton pageId={pageContainer.state.pageId} />
         </span>
         {pageContainer.isAbleToShowLikeButton && (
           <span>
@@ -66,7 +66,6 @@ SubnavButtons.propTypes = {
   pageContainer: PropTypes.instanceOf(PageContainer).isRequired,
 
   isCompactMode: PropTypes.bool,
-  pageId: PropTypes.string,
 };
 
 export default SubnavButtonsWrapper;

+ 1 - 106
packages/app/src/server/models/activity.ts

@@ -1,4 +1,3 @@
-import { DeleteWriteOpResultObject } from 'mongodb';
 import {
   Types, Document, Model, Schema,
 } from 'mongoose';
@@ -9,11 +8,9 @@ import loggerFactory from '../../utils/logger';
 import ActivityDefine from '../util/activityDefine';
 
 import Subscription from './subscription';
-// import { InAppNotification } from './in-app-notification';
 
 const logger = loggerFactory('growi:models:activity');
 
-const mongoose = require('mongoose');
 
 export interface ActivityDocument extends Document {
   _id: Types.ObjectId
@@ -28,16 +25,7 @@ export interface ActivityDocument extends Document {
   getNotificationTargetUsers(): Promise<any[]>
 }
 
-export interface ActivityModel extends Model<ActivityDocument> {
-  createByParameters(parameters: any): Promise<ActivityDocument>
-  removeByParameters(parameters: any): any
-  createByPageComment(comment: any): Promise<ActivityDocument>
-  createByPageLike(page: any, user: any): Promise<ActivityDocument>
-  removeByPageUnlike(page: any, user: any): Promise<DeleteWriteOpResultObject['result']>
-  removeByPage(page: any): Promise<DeleteWriteOpResultObject['result']>
-  findByUser(user: any): Promise<ActivityDocument[]>
-  getActionUsersFromActivities(activities: ActivityDocument[]): any[]
-}
+export type ActivityModel = Model<ActivityDocument>
 
 module.exports = function(crowi: Crowi) {
   const activityEvent = crowi.event('activity');
@@ -83,99 +71,6 @@ module.exports = function(crowi: Crowi) {
     user: 1, target: 1, action: 1, createdAt: 1,
   }, { unique: true });
 
-  /**
-     * @param {object} parameters
-     * @return {Promise}
-     */
-  activitySchema.statics.createByParameters = function(parameters) {
-    return this.create(parameters);
-  };
-
-  /**
-     * @param {object} parameters
-     */
-  activitySchema.statics.removeByParameters = async function(parameters) {
-    const activity = await this.findOne(parameters);
-    activityEvent.emit('remove', activity);
-
-    return this.deleteMany(parameters).exec();
-  };
-
-  /**
-     * @param {Comment} comment
-     * @return {Promise}
-     */
-  activitySchema.statics.createByPageComment = function(comment) {
-    const parameters = {
-      user: comment.creator,
-      targetModel: ActivityDefine.MODEL_PAGE,
-      target: comment.page,
-      eventModel: ActivityDefine.MODEL_COMMENT,
-      event: comment._id,
-      action: ActivityDefine.ACTION_COMMENT,
-    };
-
-    return this.createByParameters(parameters);
-  };
-
-  /**
-     * @param {Page} page
-     * @param {User} user
-     * @return {Promise}
-     */
-  activitySchema.statics.createByPageLike = function(page, user) {
-    const parameters = {
-      user: user._id,
-      targetModel: ActivityDefine.MODEL_PAGE,
-      target: page,
-      action: ActivityDefine.ACTION_LIKE,
-    };
-
-    return this.createByParameters(parameters);
-  };
-
-  /**
-     * @param {Page} page
-     * @param {User} user
-     * @return {Promise}
-     */
-  activitySchema.statics.removeByPageUnlike = function(page, user) {
-    const parameters = {
-      user,
-      targetModel: ActivityDefine.MODEL_PAGE,
-      target: page,
-      action: ActivityDefine.ACTION_LIKE,
-    };
-
-    return this.removeByParameters(parameters);
-  };
-
-  /**
-     * @param {Page} page
-     *
-     * @return {Promise}
-     */
-  activitySchema.statics.removeByPage = async function(page) {
-    // const activityEvent = new ActivityEvent();
-    const activities = await this.find({ target: page });
-    for (const activity of activities) {
-      // TODO: implement removeActivity when page deleted by GW-7481
-      // activityEvent.emit('remove', activity);
-    }
-    return this.deleteMany({ target: page }).exec();
-  };
-
-  /**
-     * @param {User} user
-     * @return {Promise}
-     */
-  activitySchema.statics.findByUser = function(user) {
-    return this.find({ user }).sort({ createdAt: -1 }).exec();
-  };
-
-  activitySchema.statics.getActionUsersFromActivities = function(activities) {
-    return activities.map(({ user }) => user).filter((user, i, self) => self.indexOf(user) === i);
-  };
 
   activitySchema.methods.getNotificationTargetUsers = async function() {
     const User = getModelSafely('User') || require('~/server/models/user')();

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

@@ -1,14 +1,11 @@
 import {
   Types, Document, Model, Schema /* , Query */,
 } from 'mongoose';
-import { subDays } from 'date-fns';
 import ActivityDefine from '../util/activityDefine';
-import { getOrCreateModel, getModelSafely } from '../util/mongoose-utils';
+import { getOrCreateModel } from '../util/mongoose-utils';
 import loggerFactory from '../../utils/logger';
-import { ActivityDocument } from './activity';
 
 const logger = loggerFactory('growi:models:inAppNotification');
-const mongoose = require('mongoose');
 
 export const STATUS_UNREAD = 'UNREAD';
 export const STATUS_UNOPENED = 'UNOPENED';
@@ -28,13 +25,9 @@ export interface InAppNotificationDocument extends Document {
 
 export interface InAppNotificationModel extends Model<InAppNotificationDocument> {
   findLatestInAppNotificationsByUser(user: Types.ObjectId, skip: number, offset: number): Promise<InAppNotificationDocument[]>
-
-  // commented out type 'Query' temporary to avoid ts error
-  removeEmpty()/* : Query<any> */
-  read(user) /* : Promise<Query<any>> */
-
-  open(user, id: Types.ObjectId): Promise<InAppNotificationDocument | null>
   getUnreadCountByUser(user: Types.ObjectId): Promise<number | undefined>
+  open(user, id: Types.ObjectId): Promise<InAppNotificationDocument | null>
+  read(user) /* : Promise<Query<any>> */
 
   STATUS_UNREAD: string
   STATUS_UNOPENED: string
@@ -82,12 +75,6 @@ const inAppNotificationSchema = new Schema<InAppNotificationDocument, InAppNotif
   },
 });
 
-// TODO: move this virtual property getter to the service layer if necessary 77893
-// inAppNotificationSchema.virtual('actionUsers').get(function(this: InAppNotificationDocument) {
-//   const Activity = getModelSafely('Activity') || require('../models/activity')(this.crowi);
-//   return Activity.getActionUsersFromActivities((this.activities as any) as ActivityDocument[]);
-// });
-
 const transform = (doc, ret) => {
   // delete ret.activities
 };
@@ -110,31 +97,6 @@ inAppNotificationSchema.statics.findLatestInAppNotificationsByUser = function(us
     .exec();
 };
 
-inAppNotificationSchema.statics.removeEmpty = function() {
-  return InAppNotification.deleteMany({ activities: { $size: 0 } });
-};
-
-inAppNotificationSchema.statics.read = async function(user) {
-  const query = { user, status: STATUS_UNREAD };
-  const parameters = { status: STATUS_UNOPENED };
-
-  return InAppNotification.updateMany(query, parameters);
-};
-
-inAppNotificationSchema.statics.getUnreadCountByUser = async function(user) {
-  const query = { user, status: STATUS_UNREAD };
-
-  try {
-    const count = await InAppNotification.countDocuments(query);
-
-    return count;
-  }
-  catch (err) {
-    logger.error('Error on getUnreadCountByUser', err);
-    throw err;
-  }
-};
-
 inAppNotificationSchema.statics.STATUS_UNOPENED = function() {
   return STATUS_UNOPENED;
 };

+ 2 - 13
packages/app/src/server/models/subscription.ts

@@ -5,8 +5,8 @@ import {
 import ActivityDefine from '../util/activityDefine';
 import { getOrCreateModel } from '../util/mongoose-utils';
 
-const STATUS_SUBSCRIBE = 'SUBSCRIBE';
-const STATUS_UNSUBSCRIBE = 'UNSUBSCRIBE';
+export const STATUS_SUBSCRIBE = 'SUBSCRIBE';
+export const STATUS_UNSUBSCRIBE = 'UNSUBSCRIBE';
 const STATUSES = [STATUS_SUBSCRIBE, STATUS_UNSUBSCRIBE];
 
 export interface ISubscription {
@@ -28,9 +28,6 @@ export interface SubscriptionModel extends Model<SubscriptionDocument> {
   subscribeByPageId(user: Types.ObjectId, pageId: Types.ObjectId, status: string): any
   getSubscription(target: Types.ObjectId): Promise<Types.ObjectId[]>
   getUnsubscription(target: Types.ObjectId): Promise<Types.ObjectId[]>
-
-  STATUS_SUBSCRIBE: string
-  STATUS_UNSUBSCRIB: string
 }
 
 const subscriptionSchema = new Schema<SubscriptionDocument, SubscriptionModel>({
@@ -91,12 +88,4 @@ subscriptionSchema.statics.getUnsubscription = async function(target) {
   return this.find({ target, status: STATUS_UNSUBSCRIBE }).distinct('user');
 };
 
-subscriptionSchema.statics.STATUS_SUBSCRIBE = function() {
-  return STATUS_SUBSCRIBE;
-};
-
-subscriptionSchema.statics.STATUS_UNSUBSCRIBE = function() {
-  return STATUS_UNSUBSCRIBE;
-};
-
 export default getOrCreateModel<SubscriptionDocument, SubscriptionModel>('Subscription', subscriptionSchema);

+ 2 - 2
packages/app/src/server/routes/apiv3/page.js

@@ -1,7 +1,7 @@
 import { pagePathUtils } from '@growi/core';
 import loggerFactory from '~/utils/logger';
 
-import Subscription from '~/server/models/subscription';
+import Subscription, { STATUS_SUBSCRIBE, STATUS_UNSUBSCRIBE } from '~/server/models/subscription';
 
 const logger = loggerFactory('growi:routes:apiv3:page'); // eslint-disable-line no-unused-vars
 
@@ -517,7 +517,7 @@ module.exports = (crowi) => {
   router.put('/subscribe', accessTokenParser, loginRequiredStrictly, csrf, validator.subscribe, apiV3FormValidator, async(req, res) => {
     const { pageId } = req.body;
     const userId = req.user._id;
-    const status = req.body.status ? Subscription.STATUS_SUBSCRIBE() : Subscription.STATUS_UNSUBSCRIBE();
+    const status = req.body.status ? STATUS_SUBSCRIBE : STATUS_UNSUBSCRIBE;
     try {
       const subscription = await Subscription.subscribeByPageId(userId, pageId, status);
       return res.apiv3({ subscription });

+ 15 - 20
packages/app/src/server/service/activity.ts

@@ -1,15 +1,8 @@
-import { Types } from 'mongoose';
 import Crowi from '../crowi';
-import loggerFactory from '../../utils/logger';
 
-import { ActivityDocument } from '../models/activity';
-
-import ActivityDefine from '../util/activityDefine';
 import { getModelSafely } from '../util/mongoose-utils';
 
 
-const logger = loggerFactory('growi:service:ActivityService');
-
 class ActivityService {
 
   crowi!: Crowi;
@@ -18,8 +11,6 @@ class ActivityService {
 
   activityEvent!: any;
 
-  // commentEvent!: any;
-
   constructor(crowi: Crowi) {
     this.crowi = crowi;
     this.inAppNotificationService = crowi.inAppNotificationService;
@@ -28,22 +19,26 @@ class ActivityService {
 
 
   /**
-   * @param {Page} page
+     * @param {object} parameters
+     * @return {Promise}
+     */
+  createByParameters = function(parameters) {
+    const Activity = getModelSafely('Activity') || require('../models/activity')(this.crowi);
+    return Activity.create(parameters);
+  };
+
+
+  /**
    * @param {User} user
    * @return {Promise}
    */
-  createByPageUpdate = async function(page, user) {
-    const parameters = {
-      user: user._id,
-      targetModel: ActivityDefine.MODEL_PAGE,
-      target: page,
-      action: ActivityDefine.ACTION_UPDATE,
-    };
-    const Activity = getModelSafely('Activity') || require('../models/activity')(this.crowi);
-    const savedActivity = await Activity.createByParameters(parameters);
-    return savedActivity;
+  findByUser = function(user) {
+    return this.find({ user }).sort({ createdAt: -1 }).exec();
   };
 
+  getActionUsersFromActivities = function(activities) {
+    return activities.map(({ user }) => user).filter((user, i, self) => self.indexOf(user) === i);
+  };
 
 }
 

+ 23 - 3
packages/app/src/server/service/comment.ts

@@ -1,7 +1,7 @@
 import { Types } from 'mongoose';
 import loggerFactory from '../../utils/logger';
 import { getModelSafely } from '../util/mongoose-utils';
-import { ActivityDocument } from '../models/activity';
+import ActivityDefine from '../util/activityDefine';
 import Crowi from '../crowi';
 
 const logger = loggerFactory('growi:service:CommentService');
@@ -37,8 +37,7 @@ class CommentService {
         const Page = getModelSafely('Page') || require('../models/page')(this.crowi);
         await Page.updateCommentCount(savedComment.page);
 
-        const Activity = getModelSafely('Activity') || require('../models/activity')(this.crowi);
-        const savedActivity = await Activity.createByPageComment(savedComment);
+        const savedActivity = await this.createByPageComment(savedComment);
 
         let targetUsers: Types.ObjectId[] = [];
         targetUsers = await savedActivity.getNotificationTargetUsers();
@@ -73,6 +72,27 @@ class CommentService {
     });
   }
 
+  /**
+   * @param {Comment} comment
+   * @return {Promise}
+   */
+  createByPageComment = function(comment) {
+    const { activityService } = this.crowi;
+
+
+    const parameters = {
+      user: comment.creator,
+      targetModel: ActivityDefine.MODEL_PAGE,
+      target: comment.page,
+      eventModel: ActivityDefine.MODEL_COMMENT,
+      event: comment._id,
+      action: ActivityDefine.ACTION_COMMENT,
+    };
+
+    return activityService.createByParameters(parameters);
+  };
+
+
 }
 
 module.exports = CommentService;

+ 31 - 1
packages/app/src/server/service/in-app-notification.ts

@@ -1,7 +1,9 @@
 import { Types } from 'mongoose';
 import { subDays } from 'date-fns';
 import Crowi from '../crowi';
-import { InAppNotification, InAppNotificationDocument, STATUS_UNREAD } from '~/server/models/in-app-notification';
+import {
+  InAppNotification, InAppNotificationDocument, STATUS_UNREAD, STATUS_UNOPENED,
+} from '~/server/models/in-app-notification';
 import { ActivityDocument } from '~/server/models/activity';
 
 import loggerFactory from '~/utils/logger';
@@ -68,6 +70,34 @@ export default class InAppNotificationService {
     return;
   }
 
+  // inAppNotificationSchema.virtual('actionUsers').get(function(this: InAppNotificationDocument) {
+  //   const Activity = getModelSafely('Activity') || require('../models/activity')(this.crowi);
+  //   return Activity.getActionUsersFromActivities((this.activities as any) as ActivityDocument[]);
+  // });
+
+  read = async function(user: Types.ObjectId): Promise<void> {
+    const query = { user, status: STATUS_UNREAD };
+    const parameters = { status: STATUS_UNOPENED };
+    await InAppNotification.updateMany(query, parameters);
+
+    return;
+  };
+
+  getUnreadCountByUser = async function(user: Types.ObjectId): Promise<number| undefined> {
+    const query = { user, status: STATUS_UNREAD };
+
+    try {
+      const count = await InAppNotification.countDocuments(query);
+
+      return count;
+    }
+    catch (err) {
+      logger.error('Error on getUnreadCountByUser', err);
+      throw err;
+    }
+  };
+
+
 }
 
 module.exports = InAppNotificationService;

+ 24 - 2
packages/app/src/server/service/page.js

@@ -1,5 +1,7 @@
 import { pagePathUtils } from '@growi/core';
+import isThisHour from 'date-fns/isThisHour/index.js';
 import loggerFactory from '~/utils/logger';
+import ActivityDefine from '../util/activityDefine';
 
 const mongoose = require('mongoose');
 const escapeStringRegexp = require('escape-string-regexp');
@@ -31,12 +33,12 @@ class PageService {
 
     // update
     this.pageEvent.on('update', async(page, user) => {
-      const { activityService, inAppNotificationService } = this.crowi;
+      const { inAppNotificationService } = this.crowi;
 
       this.pageEvent.onUpdate();
 
       try {
-        const savedActivity = await activityService.createByPageUpdate(page, user);
+        const savedActivity = await this.createByPageUpdate(page, user);
         let targetUsers = [];
         targetUsers = await savedActivity.getNotificationTargetUsers();
 
@@ -764,6 +766,26 @@ class PageService {
     }
   }
 
+  /**
+   * @param {Page} page
+   * @param {User} user
+   * @return {Promise}
+   */
+  createByPageUpdate = async function(page, user) {
+    const { activityService } = this.crowi;
+
+    const parameters = {
+      user: user._id,
+      targetModel: ActivityDefine.MODEL_PAGE,
+      target: page,
+      action: ActivityDefine.ACTION_UPDATE,
+    };
+
+    const savedActivity = await activityService.createByParameters(parameters);
+    return savedActivity;
+  };
+
+
 }
 
 module.exports = PageService;