InAppNotificationElm.tsx 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. import React, {
  2. FC, useRef,
  3. } from 'react';
  4. import { HasObjectId } from '@growi/core';
  5. import { UserPicture } from '@growi/ui/dist/components/User/UserPicture';
  6. import { DropdownItem } from 'reactstrap';
  7. import { IInAppNotificationOpenable } from '~/client/interfaces/in-app-notification-openable';
  8. import { apiv3Post } from '~/client/util/apiv3-client';
  9. import { IInAppNotification, InAppNotificationStatuses } from '~/interfaces/in-app-notification';
  10. // Change the display for each targetmodel
  11. import PageModelNotification from './PageNotification/PageModelNotification';
  12. import UserModelNotification from './PageNotification/UserModelNotification';
  13. interface Props {
  14. notification: IInAppNotification & HasObjectId
  15. elemClassName?: string,
  16. type?: 'button' | 'dropdown-item',
  17. }
  18. const InAppNotificationElm: FC<Props> = (props: Props) => {
  19. const { notification } = props;
  20. const notificationRef = useRef<IInAppNotificationOpenable>(null);
  21. const clickHandler = async(notification: IInAppNotification & HasObjectId): Promise<void> => {
  22. if (notification.status === InAppNotificationStatuses.STATUS_UNOPENED) {
  23. // set notification status "OPEND"
  24. await apiv3Post('/in-app-notification/open', { id: notification._id });
  25. }
  26. const currentInstance = notificationRef.current;
  27. if (currentInstance != null) {
  28. currentInstance.open();
  29. }
  30. };
  31. const getActionUsers = () => {
  32. if (notification.targetModel === 'User') {
  33. return notification.target.username;
  34. }
  35. const latestActionUsers = notification.actionUsers.slice(0, 3);
  36. const latestUsers = latestActionUsers.map((user) => {
  37. return `@${user.name}`;
  38. });
  39. let actionedUsers = '';
  40. const latestUsersCount = latestUsers.length;
  41. if (latestUsersCount === 1) {
  42. actionedUsers = latestUsers[0];
  43. }
  44. else if (notification.actionUsers.length >= 4) {
  45. actionedUsers = `${latestUsers.slice(0, 2).join(', ')} and ${notification.actionUsers.length - 2} others`;
  46. }
  47. else {
  48. actionedUsers = latestUsers.join(', ');
  49. }
  50. return actionedUsers;
  51. };
  52. const renderActionUserPictures = (): JSX.Element => {
  53. const actionUsers = notification.actionUsers;
  54. if (actionUsers.length < 1) {
  55. return <></>;
  56. }
  57. if (actionUsers.length === 1) {
  58. return <UserPicture user={actionUsers[0]} size="md" noTooltip />;
  59. }
  60. return (
  61. <div className="position-relative">
  62. <UserPicture user={actionUsers[0]} size="md" noTooltip />
  63. <div className="position-absolute" style={{ top: 10, left: 10 }}>
  64. <UserPicture user={actionUsers[1]} size="md" noTooltip />
  65. </div>
  66. </div>
  67. );
  68. };
  69. const actionUsers = getActionUsers();
  70. const actionType: string = notification.action;
  71. let actionMsg: string;
  72. let actionIcon: string;
  73. switch (actionType) {
  74. case 'PAGE_LIKE':
  75. actionMsg = 'liked';
  76. actionIcon = 'icon-like';
  77. break;
  78. case 'PAGE_BOOKMARK':
  79. actionMsg = 'bookmarked on';
  80. actionIcon = 'icon-star';
  81. break;
  82. case 'PAGE_UPDATE':
  83. actionMsg = 'updated on';
  84. actionIcon = 'ti ti-agenda';
  85. break;
  86. case 'PAGE_RENAME':
  87. actionMsg = 'renamed';
  88. actionIcon = 'icon-action-redo';
  89. break;
  90. case 'PAGE_DUPLICATE':
  91. actionMsg = 'duplicated';
  92. actionIcon = 'icon-docs';
  93. break;
  94. case 'PAGE_DELETE':
  95. actionMsg = 'deleted';
  96. actionIcon = 'icon-trash';
  97. break;
  98. case 'PAGE_DELETE_COMPLETELY':
  99. actionMsg = 'completely deleted';
  100. actionIcon = 'icon-fire';
  101. break;
  102. case 'PAGE_REVERT':
  103. actionMsg = 'reverted';
  104. actionIcon = 'icon-action-undo';
  105. break;
  106. case 'PAGE_RECURSIVELY_RENAME':
  107. actionMsg = 'renamed under';
  108. actionIcon = 'icon-action-redo';
  109. break;
  110. case 'PAGE_RECURSIVELY_DELETE':
  111. actionMsg = 'deleted under';
  112. actionIcon = 'icon-trash';
  113. break;
  114. case 'PAGE_RECURSIVELY_DELETE_COMPLETELY':
  115. actionMsg = 'deleted completely under';
  116. actionIcon = 'icon-fire';
  117. break;
  118. case 'PAGE_RECURSIVELY_REVERT':
  119. actionMsg = 'reverted under';
  120. actionIcon = 'icon-action-undo';
  121. break;
  122. case 'COMMENT_CREATE':
  123. actionMsg = 'commented on';
  124. actionIcon = 'icon-bubble';
  125. break;
  126. case 'USER_REGISTRATION_APPROVAL_REQUEST':
  127. actionMsg = 'requested registration approval';
  128. actionIcon = 'icon-bubble';
  129. break;
  130. default:
  131. actionMsg = '';
  132. actionIcon = '';
  133. }
  134. const isDropdownItem = props.type === 'dropdown-item';
  135. // determine tag
  136. const TagElem = isDropdownItem
  137. ? DropdownItem
  138. // eslint-disable-next-line react/prop-types
  139. : props => <button type="button" {...props}>{props.children}</button>;
  140. return (
  141. <TagElem className={props.elemClassName} onClick={() => clickHandler(notification)}>
  142. <div className="d-flex align-items-center">
  143. <span
  144. className={`${notification.status === InAppNotificationStatuses.STATUS_UNOPENED
  145. ? 'grw-unopend-notification'
  146. : 'ml-2'
  147. } rounded-circle mr-3`}
  148. >
  149. </span>
  150. {renderActionUserPictures()}
  151. {notification.targetModel === 'Page' && (
  152. <PageModelNotification
  153. ref={notificationRef}
  154. notification={notification}
  155. actionMsg={actionMsg}
  156. actionIcon={actionIcon}
  157. actionUsers={actionUsers}
  158. />
  159. )}
  160. {notification.targetModel === 'User' && (
  161. <UserModelNotification
  162. ref={notificationRef}
  163. notification={notification}
  164. actionMsg={actionMsg}
  165. actionIcon={actionIcon}
  166. actionUsers={actionUsers}
  167. />
  168. )}
  169. </div>
  170. </TagElem>
  171. );
  172. };
  173. export default InAppNotificationElm;