InAppNotificationDropdown.tsx 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. import React, { useState, useEffect, FC } from 'react';
  2. import {
  3. Dropdown, DropdownToggle, DropdownMenu, DropdownItem,
  4. } from 'reactstrap';
  5. import loggerFactory from '~/utils/logger';
  6. import AppContainer from '../../client/services/AppContainer';
  7. import { withUnstatedContainers } from '../UnstatedUtils';
  8. import { InAppNotification as IInAppNotification } from '../../interfaces/in-app-notification';
  9. // import DropdownMenu from './InAppNotificationDropdown/DropdownMenu';
  10. // import Crowi from 'client/util/Crowi'
  11. // import { Notification } from 'client/types/crowi'
  12. // import { InAppNotification } from './InAppNotification';
  13. import SocketIoContainer from '../../client/services/SocketIoContainer';
  14. const logger = loggerFactory('growi:InAppNotificationDropdown');
  15. type Props = {
  16. appContainer: AppContainer,
  17. socketIoContainer: SocketIoContainer,
  18. };
  19. const InAppNotificationDropdown: FC<Props> = (props: Props) => {
  20. const { appContainer } = props;
  21. const [count, setCount] = useState(0);
  22. const [isLoaded, setIsLoaded] = useState(false);
  23. const [notifications, setNotifications] = useState([]);
  24. const [isOpen, setIsOpen] = useState(false);
  25. useEffect(() => {
  26. initializeSocket(props);
  27. fetchNotificationStatus();
  28. }, []);
  29. const initializeSocket = (props) => {
  30. const socket = props.socketIoContainer.getSocket();
  31. socket.on('notificationUpdated', (data: { userId: string, count: number }) => {
  32. setCount(data.count);
  33. // eslint-disable-next-line no-console
  34. console.log('socketData', data);
  35. });
  36. };
  37. const updateNotificationStatus = async() => {
  38. try {
  39. await appContainer.apiv3Post('/in-app-notification/read');
  40. setCount(0);
  41. }
  42. catch (err) {
  43. logger.error(err);
  44. }
  45. };
  46. const fetchNotificationStatus = async() => {
  47. try {
  48. const res = await appContainer.apiv3Get('/in-app-notification/status');
  49. const { count } = res.data;
  50. setCount(count);
  51. }
  52. catch (err) {
  53. logger.error(err);
  54. }
  55. };
  56. /**
  57. * TODO: Fetch notification list by GW-7473
  58. */
  59. const fetchNotificationList = async() => {
  60. const limit = 6;
  61. try {
  62. const paginationResult = await appContainer.apiv3Get('/in-app-notification/list', { limit });
  63. console.log('paginationResult', paginationResult);
  64. setNotifications(paginationResult.data.docs);
  65. setIsLoaded(true);
  66. }
  67. catch (err) {
  68. logger.error(err);
  69. }
  70. };
  71. const toggleDropdownHandler = () => {
  72. if (isOpen === false && count > 0) {
  73. updateNotificationStatus();
  74. }
  75. const newIsOpenState = !isOpen;
  76. setIsOpen(newIsOpenState);
  77. if (newIsOpenState === true) {
  78. fetchNotificationList();
  79. }
  80. };
  81. /**
  82. * TODO: Jump to the page by clicking on the notification by GW-7472
  83. */
  84. const notificationClickHandler = async(notification: Notification) => {
  85. try {
  86. // await this.props.crowi.apiPost('/notification.open', { id: notification._id });
  87. // jump to target page
  88. // window.location.href = notification.target.path;
  89. }
  90. catch (err) {
  91. logger.error(err);
  92. }
  93. };
  94. const RenderUnLoadedInAppNotification = (): JSX.Element => {
  95. return (
  96. <i className="fa fa-spinner"></i>
  97. );
  98. };
  99. const RenderEmptyInAppNotification = (): JSX.Element => {
  100. return (
  101. // TODO: apply i18n by #78569
  102. <>You had no notifications, yet.</>
  103. );
  104. };
  105. // TODO: improve renderInAppNotificationList by GW-7535
  106. // refer to https://github.com/crowi/crowi/blob/eecf2bc821098d2516b58104fe88fae81497d3ea/client/components/Notification/Notification.tsx
  107. const RenderInAppNotificationList = () => {
  108. console.log('notificationsHoge', notifications);
  109. if (notifications.length === 0) {
  110. return <RenderEmptyInAppNotification />;
  111. }
  112. const notificationList = notifications.map((notification: IInAppNotification) => {
  113. return (
  114. // temporaly notification list. need to delete by #79077
  115. <div key={notification._id}>action: {notification.action} </div>
  116. // use this component to show notification list
  117. // <InAppNotification key={notification._id} notification={notification} onClick={notificationClickHandler} />
  118. );
  119. });
  120. return <>{notificationList}</>;
  121. };
  122. const InAppNotificationContents = (): JSX.Element => {
  123. // if (isLoaded === false) {
  124. // return <RenderUnLoadedInAppNotification />;
  125. // }
  126. return <RenderInAppNotificationList />;
  127. };
  128. const badge = count > 0 ? <span className="badge badge-pill badge-danger grw-notification-badge">{count}</span> : '';
  129. return (
  130. <Dropdown className="notification-wrapper" isOpen={isOpen} toggle={toggleDropdownHandler}>
  131. <DropdownToggle tag="a" className="nav-link">
  132. <i className="icon-bell mr-2" /> {badge}
  133. </DropdownToggle>
  134. <DropdownMenu right>
  135. <InAppNotificationContents />
  136. <DropdownItem divider />
  137. {/* TODO: Able to show all notifications by GW-7534 */}
  138. <a>See All</a>
  139. </DropdownMenu>
  140. </Dropdown>
  141. );
  142. };
  143. /**
  144. * Wrapper component for using unstated
  145. */
  146. const InAppNotificationDropdownWrapper = withUnstatedContainers(InAppNotificationDropdown, [AppContainer, SocketIoContainer]);
  147. export default InAppNotificationDropdownWrapper;