InAppNotificationElm.tsx 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. import React, { useCallback } from 'react';
  2. import { UserPicture, PagePathLabel } from '@growi/ui';
  3. import { IInAppNotification } from '~/interfaces/in-app-notification';
  4. import { HasObjectId } from '~/interfaces/has-object-id';
  5. import { apiv3Post } from '~/client/util/apiv3-client';
  6. import FormattedDistanceDate from '../FormattedDistanceDate';
  7. interface Props {
  8. notification: IInAppNotification & HasObjectId
  9. }
  10. const InAppNotificationElm = (props: Props): JSX.Element => {
  11. const { notification } = props;
  12. const getActionUsers = () => {
  13. const latestActionUsers = notification.actionUsers.slice(0, 3);
  14. const latestUsers = latestActionUsers.map((user) => {
  15. return `@${user.name}`;
  16. });
  17. let actionedUsers = '';
  18. const latestUsersCount = latestUsers.length;
  19. if (latestUsersCount === 1) {
  20. actionedUsers = latestUsers[0];
  21. }
  22. else if (notification.actionUsers.length >= 4) {
  23. actionedUsers = `${latestUsers.slice(0, 2).join(', ')} and ${notification.actionUsers.length - 2} others`;
  24. }
  25. else {
  26. actionedUsers = latestUsers.join(', ');
  27. }
  28. return actionedUsers;
  29. };
  30. const renderActionUserPictures = (): JSX.Element => {
  31. const actionUsers = notification.actionUsers;
  32. if (actionUsers.length < 1) {
  33. return <></>;
  34. }
  35. if (actionUsers.length === 1) {
  36. return <UserPicture user={actionUsers[0]} size="md" noTooltip />;
  37. }
  38. return (
  39. <div className="position-relative">
  40. <UserPicture user={actionUsers[0]} size="md" noTooltip />
  41. <div className="position-absolute" style={{ top: 10, left: 10 }}>
  42. <UserPicture user={actionUsers[1]} size="md" noTooltip />
  43. </div>
  44. </div>
  45. );
  46. };
  47. const renderNotificationDate = (): JSX.Element => {
  48. return (
  49. <FormattedDistanceDate
  50. id={notification._id}
  51. date={notification.createdAt}
  52. isShowTooltip={false}
  53. differenceForAvoidingFormat={Number.POSITIVE_INFINITY}
  54. />
  55. );
  56. };
  57. const actionUsers = getActionUsers();
  58. const actionType: string = notification.action;
  59. let actionMsg: string;
  60. let actionIcon: string;
  61. switch (actionType) {
  62. case 'PAGE_LIKE':
  63. actionMsg = 'liked';
  64. actionIcon = 'icon-like';
  65. break;
  66. case 'PAGE_BOOKMARK':
  67. actionMsg = 'bookmarked on';
  68. actionIcon = 'icon-star';
  69. break;
  70. case 'PAGE_UPDATE':
  71. actionMsg = 'updated on';
  72. actionIcon = 'ti-agenda';
  73. break;
  74. case 'PAGE_RENAME':
  75. actionMsg = 'renamed';
  76. actionIcon = 'icon-action-redo';
  77. break;
  78. case 'PAGE_DELETE':
  79. actionMsg = 'deleted';
  80. actionIcon = 'icon-trash';
  81. break;
  82. case 'PAGE_DELETE_COMPLETELY':
  83. actionMsg = 'completely deleted';
  84. actionIcon = 'icon-fire';
  85. break;
  86. case 'COMMENT_CREATE':
  87. actionMsg = 'commented on';
  88. actionIcon = 'icon-bubble';
  89. break;
  90. default:
  91. actionMsg = '';
  92. actionIcon = '';
  93. }
  94. const RenderPageModelNotification = (): JSX.Element => {
  95. const snapshot = JSON.parse(notification.snapshot);
  96. const pagePath = { path: snapshot.path };
  97. const notificationClickHandler = useCallback(() => {
  98. // set notification status "OPEND"
  99. apiv3Post('/in-app-notification/open', { id: notification._id });
  100. // jump to target page
  101. const targetPagePath = notification.target?.path;
  102. if (targetPagePath != null) {
  103. window.location.href = targetPagePath;
  104. }
  105. }, []);
  106. return (
  107. <div className="p-2">
  108. <div onClick={notificationClickHandler}>
  109. <div>
  110. <b>{actionUsers}</b> {actionMsg} <PagePathLabel page={pagePath} />
  111. </div>
  112. <i className={`${actionIcon} mr-2`} />
  113. {renderNotificationDate()}
  114. </div>
  115. </div>
  116. );
  117. };
  118. return (
  119. <div className="dropdown-item d-flex flex-row mb-3">
  120. <div className="p-2 mr-2 d-flex align-items-center">
  121. <span className={`${notification.status === 'UNOPENED' ? 'grw-unopend-notification' : 'ml-2'} rounded-circle mr-3`}></span>
  122. {renderActionUserPictures()}
  123. </div>
  124. {notification.targetModel === 'Page' && (
  125. RenderPageModelNotification()
  126. )}
  127. </div>
  128. );
  129. };
  130. export default InAppNotificationElm;