InAppNotificationDropdown.tsx 3.3 KB

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