InAppNotificationDropdown.tsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. import React, {
  2. useState, useEffect, FC, useCallback,
  3. } from 'react';
  4. import { useTranslation } from 'react-i18next';
  5. import {
  6. Dropdown, DropdownToggle, DropdownMenu, DropdownItem,
  7. } from 'reactstrap';
  8. import SocketIoContainer from '~/client/services/SocketIoContainer';
  9. import { toastError } from '~/client/util/apiNotification';
  10. import { apiv3Post } from '~/client/util/apiv3-client';
  11. import { useSWRxInAppNotifications, useSWRxInAppNotificationStatus } from '~/stores/in-app-notification';
  12. import loggerFactory from '~/utils/logger';
  13. import { withUnstatedContainers } from '../UnstatedUtils';
  14. import InAppNotificationList from './InAppNotificationList';
  15. const logger = loggerFactory('growi:InAppNotificationDropdown');
  16. type Props = {
  17. socketIoContainer: SocketIoContainer,
  18. };
  19. const InAppNotificationDropdown: FC<Props> = (props: Props) => {
  20. const { t } = useTranslation();
  21. const [isOpen, setIsOpen] = useState(false);
  22. const limit = 6;
  23. const { data: inAppNotificationData, mutate: mutateInAppNotificationData } = useSWRxInAppNotifications(limit);
  24. const { data: inAppNotificationUnreadStatusCount, mutate: mutateInAppNotificationUnreadStatusCount } = useSWRxInAppNotificationStatus();
  25. const initializeSocket = useCallback((props) => {
  26. const socket = props.socketIoContainer.getSocket();
  27. socket.on('notificationUpdated', () => {
  28. mutateInAppNotificationUnreadStatusCount();
  29. });
  30. }, [mutateInAppNotificationUnreadStatusCount]);
  31. const updateNotificationStatus = async() => {
  32. try {
  33. await apiv3Post('/in-app-notification/read');
  34. }
  35. catch (err) {
  36. toastError(err);
  37. logger.error(err);
  38. }
  39. };
  40. useEffect(() => {
  41. initializeSocket(props);
  42. }, [initializeSocket, props]);
  43. const toggleDropdownHandler = async() => {
  44. if (!isOpen && inAppNotificationUnreadStatusCount != null && inAppNotificationUnreadStatusCount > 0) {
  45. await updateNotificationStatus();
  46. mutateInAppNotificationUnreadStatusCount();
  47. }
  48. const newIsOpenState = !isOpen;
  49. if (newIsOpenState) {
  50. mutateInAppNotificationData();
  51. }
  52. setIsOpen(newIsOpenState);
  53. };
  54. let badge;
  55. if (inAppNotificationUnreadStatusCount != null && inAppNotificationUnreadStatusCount > 0) {
  56. badge = <span className="badge badge-pill badge-danger grw-notification-badge">{inAppNotificationUnreadStatusCount}</span>;
  57. }
  58. else {
  59. badge = '';
  60. }
  61. return (
  62. <Dropdown className="notification-wrapper grw-notification-dropdown" isOpen={isOpen} toggle={toggleDropdownHandler}>
  63. <DropdownToggle tag="a" className="px-3 nav-link border-0 bg-transparent waves-effect waves-light">
  64. <i className="icon-bell" /> {badge}
  65. </DropdownToggle>
  66. <DropdownMenu right>
  67. { inAppNotificationData != null && inAppNotificationData.docs.length === 0
  68. // no items
  69. ? <DropdownItem disabled>{t('in_app_notification.mark_all_as_read')}</DropdownItem>
  70. // render DropdownItem
  71. : <InAppNotificationList type="dropdown-item" inAppNotificationData={inAppNotificationData} />
  72. }
  73. <DropdownItem divider />
  74. <DropdownItem tag="a" href="/me/all-in-app-notifications">
  75. { t('in_app_notification.see_all') }
  76. </DropdownItem>
  77. </DropdownMenu>
  78. </Dropdown>
  79. );
  80. };
  81. /**
  82. * Wrapper component for using unstated
  83. */
  84. const InAppNotificationDropdownWrapper = withUnstatedContainers(InAppNotificationDropdown, [SocketIoContainer]);
  85. export default InAppNotificationDropdownWrapper;