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

use useImperativeHandle for open notification handler

Yuki Takei 4 лет назад
Родитель
Сommit
72703a46cb

+ 3 - 0
packages/app/src/client/interfaces/in-app-notification-openable.ts

@@ -0,0 +1,3 @@
+export interface IInAppNotificationOpenable {
+  open: () => void,
+}

+ 54 - 15
packages/app/src/components/InAppNotification/InAppNotificationElm.tsx

@@ -1,20 +1,42 @@
-import React from 'react';
+import React, {
+  FC, useRef,
+} from 'react';
+import { DropdownItem } from 'reactstrap';
 
 import { UserPicture } from '@growi/ui';
-import { IInAppNotification } from '~/interfaces/in-app-notification';
+import { IInAppNotification, InAppNotificationStatuses } from '~/interfaces/in-app-notification';
 import { HasObjectId } from '~/interfaces/has-object-id';
 
 // Change the display for each targetmodel
 import PageModelNotification from './PageNotification/PageModelNotification';
+import { IInAppNotificationOpenable } from '~/client/interfaces/in-app-notification-openable';
+import { apiv3Post } from '~/client/util/apiv3-client';
 
 interface Props {
   notification: IInAppNotification & HasObjectId
+  elemClassName?: string,
+  type?: 'button' | 'dropdown-item',
 }
 
-const InAppNotificationElm = (props: Props): JSX.Element => {
+
+const InAppNotificationElm: FC<Props> = (props: Props) => {
 
   const { notification } = props;
 
+  const notificationRef = useRef<IInAppNotificationOpenable>(null);
+
+  const clickHandler = async(notification: IInAppNotification & HasObjectId): Promise<void> => {
+    if (notification.status === InAppNotificationStatuses.STATUS_UNOPENED) {
+      // set notification status "OPEND"
+      await apiv3Post('/in-app-notification/open', { id: notification._id });
+    }
+
+    const currentInstance = notificationRef.current;
+    if (currentInstance != null) {
+      currentInstance.open();
+    }
+  };
+
   const getActionUsers = () => {
     const latestActionUsers = notification.actionUsers.slice(0, 3);
     const latestUsers = latestActionUsers.map((user) => {
@@ -96,19 +118,36 @@ const InAppNotificationElm = (props: Props): JSX.Element => {
       actionIcon = '';
   }
 
+  const isDropdownItem = props.type === 'dropdown-item';
+
+  // determine tag
+  const TagElem = isDropdownItem
+    ? DropdownItem
+    // eslint-disable-next-line react/prop-types
+    : props => <button type="button" {...props}>{props.children}</button>;
+
   return (
-    <div className="d-flex align-items-center">
-      <span className={`${notification.status === 'UNOPENED' ? 'grw-unopend-notification' : 'ml-2'} rounded-circle mr-3`}></span>
-      {renderActionUserPictures()}
-      {notification.targetModel === 'Page' && (
-        <PageModelNotification
-          notification={notification}
-          actionMsg={actionMsg}
-          actionIcon={actionIcon}
-          actionUsers={actionUsers}
-        />
-      )}
-    </div>
+    <TagElem className={props.elemClassName} onClick={() => clickHandler(notification)}>
+      <div className="d-flex align-items-center">
+        <span
+          className={`${notification.status === InAppNotificationStatuses.STATUS_UNOPENED
+            ? 'grw-unopend-notification'
+            : 'ml-2'
+          } rounded-circle mr-3`}
+        >
+        </span>
+        {renderActionUserPictures()}
+        {notification.targetModel === 'Page' && (
+          <PageModelNotification
+            ref={notificationRef}
+            notification={notification}
+            actionMsg={actionMsg}
+            actionIcon={actionIcon}
+            actionUsers={actionUsers}
+          />
+        )}
+      </div>
+    </TagElem>
   );
 };
 

+ 31 - 26
packages/app/src/components/InAppNotification/PageNotification/PageModelNotification.tsx

@@ -1,10 +1,14 @@
-import React, { FC, useCallback } from 'react';
+import React, {
+  forwardRef, ForwardRefRenderFunction, useImperativeHandle,
+} 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 { IInAppNotificationOpenable } from '~/client/interfaces/in-app-notification-openable';
 import { IInAppNotification } from '~/interfaces/in-app-notification';
 import { HasObjectId } from '~/interfaces/has-object-id';
+
 import FormattedDistanceDate from '../../FormattedDistanceDate';
+import { parseSnapshot } from '../../../models/serializers/in-app-notification-snapshot/page';
 
 interface Props {
   notification: IInAppNotification & HasObjectId
@@ -13,7 +17,8 @@ interface Props {
   actionUsers: string
 }
 
-const PageModelNotification: FC<Props> = (props: Props) => {
+const PageModelNotification: ForwardRefRenderFunction<IInAppNotificationOpenable, Props> = (props: Props, ref) => {
+
   const {
     notification, actionMsg, actionIcon, actionUsers,
   } = props;
@@ -21,33 +26,33 @@ const PageModelNotification: FC<Props> = (props: 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;
-    }
-  }, []);
+  // publish open()
+  useImperativeHandle(ref, () => ({
+    open() {
+      if (notification.target != null) {
+        // 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>
+        <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>
   );
 };
 
-export default PageModelNotification;
+export default forwardRef(PageModelNotification);