InAppNotificationPage.tsx 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. import type { FC } from 'react';
  2. import React, { useState } from 'react';
  3. import { LoadingSpinner } from '@growi/ui/dist/components';
  4. import { useAtomValue } from 'jotai';
  5. import { useTranslation } from 'next-i18next';
  6. import { apiv3Put } from '~/client/util/apiv3-client';
  7. import { InAppNotificationStatuses } from '~/interfaces/in-app-notification';
  8. import { showPageLimitationXLAtom } from '~/states/server-configurations';
  9. import {
  10. useSWRxInAppNotificationStatus,
  11. useSWRxInAppNotifications,
  12. } from '~/stores/in-app-notification';
  13. import CustomNavAndContents from '../CustomNavigation/CustomNavAndContents';
  14. import PaginationWrapper from '../PaginationWrapper';
  15. import InAppNotificationList from './InAppNotificationList';
  16. type InAppNotificationCategoryByStatusProps = {
  17. status?: InAppNotificationStatuses;
  18. };
  19. const EmptyIcon: FC = () => {
  20. return null;
  21. };
  22. const InAppNotificationCategoryByStatus: FC<
  23. InAppNotificationCategoryByStatusProps
  24. > = ({ status }) => {
  25. const { t } = useTranslation('commons');
  26. const showPageLimitationXL = useAtomValue(showPageLimitationXLAtom);
  27. const limit = showPageLimitationXL != null ? showPageLimitationXL : 20;
  28. const [activePage, setActivePage] = useState(1);
  29. const offset = (activePage - 1) * limit;
  30. const categoryStatus =
  31. status === InAppNotificationStatuses.STATUS_UNOPENED
  32. ? InAppNotificationStatuses.STATUS_UNOPENED
  33. : undefined;
  34. const { data: notificationData, mutate: mutateNotificationData } =
  35. useSWRxInAppNotifications(limit, offset, categoryStatus);
  36. const { mutate: mutateAllNotificationData } = useSWRxInAppNotifications(
  37. limit,
  38. offset,
  39. undefined,
  40. );
  41. const { mutate: mutateNotificationCount } = useSWRxInAppNotificationStatus();
  42. const setAllNotificationPageNumber = (selectedPageNumber: number): void => {
  43. setActivePage(selectedPageNumber);
  44. };
  45. if (notificationData == null) {
  46. return (
  47. <div className="wiki" data-testid="grw-in-app-notification-page-spinner">
  48. <div className="text-muted text-center">
  49. <LoadingSpinner className="me-1 fs-3" />
  50. </div>
  51. </div>
  52. );
  53. }
  54. const updateUnopendNotificationStatusesToOpened = async () => {
  55. await apiv3Put('/in-app-notification/all-statuses-open');
  56. // mutate notification statuses in 'UNREAD' Category
  57. mutateNotificationData();
  58. // mutate notification statuses in 'ALL' Category
  59. mutateAllNotificationData();
  60. mutateNotificationCount();
  61. };
  62. return (
  63. <>
  64. {status === InAppNotificationStatuses.STATUS_UNOPENED &&
  65. notificationData.totalDocs > 0 && (
  66. <div className="mb-2 d-flex justify-content-end">
  67. <button
  68. type="button"
  69. className="btn btn-outline-primary"
  70. onClick={updateUnopendNotificationStatusesToOpened}
  71. >
  72. {t('in_app_notification.mark_all_as_read')}
  73. </button>
  74. </div>
  75. )}
  76. {notificationData != null && notificationData.docs.length === 0 ? (
  77. // no items
  78. t('in_app_notification.no_unread_messages')
  79. ) : (
  80. // render list-group
  81. <InAppNotificationList inAppNotificationData={notificationData} />
  82. )}
  83. {notificationData.totalDocs > 0 && (
  84. <div className="mt-4">
  85. <PaginationWrapper
  86. activePage={activePage}
  87. changePage={setAllNotificationPageNumber}
  88. totalItemsCount={notificationData.totalDocs}
  89. pagingLimit={notificationData.limit}
  90. align="center"
  91. size="sm"
  92. />
  93. </div>
  94. )}
  95. </>
  96. );
  97. };
  98. const InAppNotificationAllTabContent: FC = () => {
  99. return <InAppNotificationCategoryByStatus />;
  100. };
  101. const InAppNotificationUnreadTabContent: FC = () => {
  102. return (
  103. <InAppNotificationCategoryByStatus
  104. status={InAppNotificationStatuses.STATUS_UNOPENED}
  105. />
  106. );
  107. };
  108. export const InAppNotificationPage: FC = () => {
  109. const { t } = useTranslation('commons');
  110. const navTabMapping = {
  111. user_infomation: {
  112. Icon: EmptyIcon,
  113. Content: InAppNotificationAllTabContent,
  114. i18n: t('in_app_notification.all'),
  115. },
  116. external_accounts: {
  117. Icon: EmptyIcon,
  118. Content: InAppNotificationUnreadTabContent,
  119. i18n: t('in_app_notification.unopend'),
  120. },
  121. };
  122. return (
  123. <div data-testid="grw-in-app-notification-page">
  124. <CustomNavAndContents
  125. navTabMapping={navTabMapping}
  126. tabContentClasses={['mt-4']}
  127. />
  128. </div>
  129. );
  130. };
  131. InAppNotificationPage.displayName = 'InAppNotificationPage';