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

Merge pull request #4785 from weseek/feat/82461-create-page-snapshot

feat: 82461 create page snapshot
Shun Miyazawa 4 лет назад
Родитель
Сommit
fb9abb59b9

+ 12 - 34
packages/app/src/components/InAppNotification/InAppNotificationElm.tsx

@@ -1,11 +1,11 @@
-import React, { useCallback } from 'react';
+import React from 'react';
 
-import { UserPicture, PagePathLabel } from '@growi/ui';
+import { UserPicture } from '@growi/ui';
 import { IInAppNotification } from '~/interfaces/in-app-notification';
 import { HasObjectId } from '~/interfaces/has-object-id';
-import { apiv3Post } from '~/client/util/apiv3-client';
-import FormattedDistanceDate from '../FormattedDistanceDate';
 
+// Change the display for each targetmodel
+import PageModelNotification from './PageNotification/PageModelNotification';
 
 interface Props {
   notification: IInAppNotification & HasObjectId
@@ -15,7 +15,6 @@ const InAppNotificationElm = (props: Props): JSX.Element => {
 
   const { notification } = props;
 
-
   const getActionUsers = () => {
     const latestActionUsers = notification.actionUsers.slice(0, 3);
     const latestUsers = latestActionUsers.map((user) => {
@@ -57,22 +56,8 @@ const InAppNotificationElm = (props: Props): JSX.Element => {
     );
   };
 
-  const notificationClickHandler = useCallback(() => {
-    // set notification status "OPEND"
-    apiv3Post('/in-app-notification/open', { id: notification._id });
-
-    // jump to target page
-    const targetPagePath = notification.target?.path;
-    if (targetPagePath != null) {
-      window.location.href = targetPagePath;
-    }
-  }, []);
-
   const actionUsers = getActionUsers();
 
-  // TODO: 82528 Swap target.path and snapshot.path
-  const pagePath = { path: 'test-page' };
-
   const actionType: string = notification.action;
   let actionMsg: string;
   let actionIcon: string;
@@ -111,27 +96,20 @@ const InAppNotificationElm = (props: Props): JSX.Element => {
       actionIcon = '';
   }
 
-
   return (
     <div className="dropdown-item d-flex flex-row mb-3">
       <div className="p-2 mr-2 d-flex align-items-center">
         <span className={`${notification.status === 'UNOPENED' ? 'grw-unopend-notification' : 'ml-2'} rounded-circle mr-3`}></span>
         {renderActionUserPictures()}
       </div>
-      <div className="p-2">
-        <div onClick={notificationClickHandler}>
-          <div>
-            <b>{actionUsers}</b> {actionMsg} <PagePathLabel page={pagePath} />
-          </div>
-          <i className={`${actionIcon} mr-2`} />
-          <FormattedDistanceDate
-            id={notification._id}
-            date={notification.createdAt}
-            isShowTooltip={false}
-            differenceForAvoidingFormat={Number.POSITIVE_INFINITY}
-          />
-        </div>
-      </div>
+      {notification.targetModel === 'Page' && (
+        <PageModelNotification
+          notification={notification}
+          actionMsg={actionMsg}
+          actionIcon={actionIcon}
+          actionUsers={actionUsers}
+        />
+      )}
     </div>
   );
 };

+ 53 - 0
packages/app/src/components/InAppNotification/PageNotification/PageModelNotification.tsx

@@ -0,0 +1,53 @@
+import React, { FC, useCallback } from 'react';
+import { PagePathLabel } from '@growi/ui';
+import { apiv3Post } from '~/client/util/apiv3-client';
+import { parseSnapshot } from '../../../models/serializers/in-app-notification-snapshot/page';
+import { IInAppNotification } from '~/interfaces/in-app-notification';
+import { HasObjectId } from '~/interfaces/has-object-id';
+import FormattedDistanceDate from '../../FormattedDistanceDate';
+
+interface Props {
+  notification: IInAppNotification & HasObjectId
+  actionMsg: string
+  actionIcon: string
+  actionUsers: string
+}
+
+const PageModelNotification: FC<Props> = (props: Props) => {
+  const {
+    notification, actionMsg, actionIcon, actionUsers,
+  } = props;
+
+  const snapshot = parseSnapshot(notification.snapshot);
+  const pagePath = { path: snapshot.path };
+
+  const notificationClickHandler = useCallback(() => {
+    // set notification status "OPEND"
+    apiv3Post('/in-app-notification/open', { id: notification._id });
+
+    // jump to target page
+    const targetPagePath = notification.target?.path;
+    if (targetPagePath != null) {
+      window.location.href = targetPagePath;
+    }
+  }, []);
+
+  return (
+    <div className="p-2">
+      <div onClick={notificationClickHandler}>
+        <div>
+          <b>{actionUsers}</b> {actionMsg} <PagePathLabel page={pagePath} />
+        </div>
+        <i className={`${actionIcon} mr-2`} />
+        <FormattedDistanceDate
+          id={notification._id}
+          date={notification.createdAt}
+          isShowTooltip={false}
+          differenceForAvoidingFormat={Number.POSITIVE_INFINITY}
+        />
+      </div>
+    </div>
+  );
+};
+
+export default PageModelNotification;

+ 1 - 0
packages/app/src/interfaces/in-app-notification.ts

@@ -16,6 +16,7 @@ export interface IInAppNotification {
   status: InAppNotificationStatuses
   actionUsers: IUser[]
   createdAt: Date
+  snapshot: string
 }
 
 /*

+ 18 - 0
packages/app/src/models/serializers/in-app-notification-snapshot/page.ts

@@ -0,0 +1,18 @@
+import { IUser } from '~/interfaces/user';
+import { IPage } from '~/interfaces/page';
+
+export interface IPageSnapshot {
+  path: string
+  creator: IUser
+}
+
+export const stringifySnapshot = (page: IPage): string => {
+  return JSON.stringify({
+    path: page.path,
+    creator: page.creator,
+  });
+};
+
+export const parseSnapshot = (snapshot: string): IPageSnapshot => {
+  return JSON.parse(snapshot);
+};

+ 5 - 0
packages/app/src/server/models/in-app-notification.ts

@@ -20,6 +20,7 @@ export interface InAppNotificationDocument extends Document {
   activities: ActivityDocument[]
   status: string
   createdAt: Date
+  snapshot: string
 }
 
 
@@ -73,6 +74,10 @@ const inAppNotificationSchema = new Schema<InAppNotificationDocument, InAppNotif
     type: Date,
     default: Date.now,
   },
+  snapshot: {
+    type: String,
+    require: true,
+  },
 });
 inAppNotificationSchema.plugin(mongoosePaginate);
 

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

@@ -4,6 +4,8 @@ import loggerFactory from '../../utils/logger';
 import ActivityDefine from '../util/activityDefine';
 import Crowi from '../crowi';
 
+import { stringifySnapshot } from '~/models/serializers/in-app-notification-snapshot/page';
+
 const logger = loggerFactory('growi:service:CommentService');
 
 class CommentService {
@@ -35,8 +37,14 @@ class CommentService {
         const Page = getModelSafely('Page') || require('../models/page')(this.crowi);
         await Page.updateCommentCount(savedComment.page);
 
+        const page = await Page.findById(savedComment.page);
+        if (page == null) {
+          logger.error('Page is not found');
+          return;
+        }
+
         const activity = await this.createActivity(savedComment, ActivityDefine.ACTION_COMMENT_CREATE);
-        await this.createAndSendNotifications(activity);
+        await this.createAndSendNotifications(activity, page);
       }
       catch (err) {
         logger.error('Error occurred while handling the comment create event:\n', err);
@@ -82,14 +90,15 @@ class CommentService {
     return activity;
   };
 
-  private createAndSendNotifications = async function(activity) {
+  private createAndSendNotifications = async function(activity, page) {
+    const snapshot = stringifySnapshot(page);
 
     // Get user to be notified
     let targetUsers: Types.ObjectId[] = [];
     targetUsers = await activity.getNotificationTargetUsers();
 
     // Create and send notifications
-    await this.inAppNotificationService.upsertByActivity(targetUsers, activity);
+    await this.inAppNotificationService.upsertByActivity(targetUsers, activity, snapshot);
     await this.inAppNotificationService.emitSocketIo(targetUsers);
   };
 

+ 3 - 2
packages/app/src/server/service/in-app-notification.ts

@@ -53,7 +53,7 @@ export default class InAppNotificationService {
   }
 
   upsertByActivity = async function(
-      users: Types.ObjectId[], activity: ActivityDocument, createdAt?: Date | null,
+      users: Types.ObjectId[], activity: ActivityDocument, snapshot: string, createdAt?: Date | null,
   ): Promise<void> {
     const {
       _id: activityId, targetModel, target, action,
@@ -62,7 +62,7 @@ export default class InAppNotificationService {
     const lastWeek = subDays(now, 7);
     const operations = users.map((user) => {
       const filter = {
-        user, target, action, createdAt: { $gt: lastWeek },
+        user, target, action, createdAt: { $gt: lastWeek }, snapshot,
       };
       const parameters = {
         user,
@@ -71,6 +71,7 @@ export default class InAppNotificationService {
         action,
         status: STATUS_UNREAD,
         createdAt: now,
+        snapshot,
         $addToSet: { activities: activityId },
       };
       return {

+ 5 - 3
packages/app/src/server/service/page.js

@@ -1,8 +1,9 @@
 import { pagePathUtils } from '@growi/core';
-import isThisHour from 'date-fns/isThisHour/index.js';
 import loggerFactory from '~/utils/logger';
 import ActivityDefine from '../util/activityDefine';
 
+import { stringifySnapshot } from '~/models/serializers/in-app-notification-snapshot/page';
+
 const mongoose = require('mongoose');
 const escapeStringRegexp = require('escape-string-regexp');
 const streamToPromise = require('stream-to-promise');
@@ -809,9 +810,10 @@ class PageService {
   }
 
   createAndSendNotifications = async function(page, user, action) {
-
     const { activityService, inAppNotificationService } = this.crowi;
 
+    const snapshot = stringifySnapshot(page);
+
     // Create activity
     const parameters = {
       user: user._id,
@@ -825,7 +827,7 @@ class PageService {
     const targetUsers = await activity.getNotificationTargetUsers();
 
     // Create and send notifications
-    await inAppNotificationService.upsertByActivity(targetUsers, activity);
+    await inAppNotificationService.upsertByActivity(targetUsers, activity, snapshot);
     await inAppNotificationService.emitSocketIo(targetUsers);
   };