page.tsx 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. import useSWR, { SWRResponse } from 'swr';
  2. import useSWRImmutable from 'swr/immutable';
  3. import useSWRInfinite, { SWRInfiniteResponse } from 'swr/infinite';
  4. import { apiv3Get } from '~/client/util/apiv3-client';
  5. import { HasObjectId } from '~/interfaces/has-object-id';
  6. import {
  7. IPageInfo, IPageHasId, IPageInfoForOperation, IPageInfoForListing, IDataWithMeta,
  8. } from '~/interfaces/page';
  9. import { IRecordApplicableGrant, IResIsGrantNormalized } from '~/interfaces/page-grant';
  10. import { IPagingResult } from '~/interfaces/paging-result';
  11. import { IRevisionsForPagination } from '~/interfaces/revision';
  12. import { apiGet } from '../client/util/apiv1-client';
  13. import { Nullable } from '../interfaces/common';
  14. import { IPageTagsInfo } from '../interfaces/tag';
  15. import {
  16. useCurrentPageId, useCurrentPagePath,
  17. } from './context';
  18. import { ITermNumberManagerUtil, useTermNumberManager } from './use-static-swr';
  19. export const useSWRxPage = (pageId?: string, shareLinkId?: string): SWRResponse<IPageHasId, Error> => {
  20. return useSWR(
  21. pageId != null ? ['/page', pageId, shareLinkId] : null,
  22. (endpoint, pageId, shareLinkId) => apiv3Get(endpoint, { pageId, shareLinkId }).then(result => result.data.page),
  23. );
  24. };
  25. export const useSWRxPageByPath = (path?: string): SWRResponse<IPageHasId, Error> => {
  26. return useSWR(
  27. path != null ? ['/page', path] : null,
  28. (endpoint, path) => apiv3Get(endpoint, { path }).then(result => result.data.page),
  29. );
  30. };
  31. export const useSWRxCurrentPage = (shareLinkId?: string): SWRResponse<IPageHasId, Error> => {
  32. const { data: currentPageId } = useCurrentPageId();
  33. return useSWRxPage(currentPageId ?? undefined, shareLinkId);
  34. };
  35. // eslint-disable-next-line @typescript-eslint/no-unused-vars
  36. export const useSWRxRecentlyUpdated = (): SWRResponse<(IPageHasId)[], Error> => {
  37. return useSWR(
  38. '/pages/recent',
  39. endpoint => apiv3Get<{ pages:(IPageHasId)[] }>(endpoint).then(response => response.data?.pages),
  40. );
  41. };
  42. export const useSWRInifinitexRecentlyUpdated = () : SWRInfiniteResponse<(IPageHasId)[], Error> => {
  43. const getKey = (page: number) => {
  44. return `/pages/recent?offset=${page + 1}`;
  45. };
  46. return useSWRInfinite(
  47. getKey,
  48. (endpoint: string) => apiv3Get<{ pages:(IPageHasId)[] }>(endpoint).then(response => response.data?.pages),
  49. {
  50. revalidateFirstPage: false,
  51. revalidateAll: false,
  52. },
  53. );
  54. };
  55. // eslint-disable-next-line @typescript-eslint/no-unused-vars
  56. export const useSWRxPageList = (path: string | null, pageNumber?: number, termNumber?: number): SWRResponse<IPagingResult<IPageHasId>, Error> => {
  57. const key = path != null
  58. ? [`/pages/list?path=${path}&page=${pageNumber ?? 1}`, termNumber]
  59. : null;
  60. return useSWR(
  61. key,
  62. (endpoint: string) => apiv3Get<{pages: IPageHasId[], totalCount: number, limit: number}>(endpoint).then((response) => {
  63. return {
  64. items: response.data.pages,
  65. totalCount: response.data.totalCount,
  66. limit: response.data.limit,
  67. };
  68. }),
  69. );
  70. };
  71. export const useDescendantsPageListForCurrentPathTermManager = (isDisabled?: boolean) : SWRResponse<number, Error> & ITermNumberManagerUtil => {
  72. return useTermNumberManager(isDisabled === true ? null : 'descendantsPageListForCurrentPathTermNumber');
  73. };
  74. export const useSWRxDescendantsPageListForCurrrentPath = (pageNumber?: number): SWRResponse<IPagingResult<IPageHasId>, Error> => {
  75. const { data: currentPagePath } = useCurrentPagePath();
  76. const { data: termNumber } = useDescendantsPageListForCurrentPathTermManager();
  77. const path = currentPagePath == null || termNumber == null
  78. ? null
  79. : currentPagePath;
  80. return useSWRxPageList(path, pageNumber, termNumber);
  81. };
  82. export const useSWRxTagsInfo = (pageId: Nullable<string>): SWRResponse<IPageTagsInfo | undefined, Error> => {
  83. const endpoint = `/pages.getPageTag?pageId=${pageId}`;
  84. const key = [endpoint, pageId];
  85. const fetcher = async(endpoint: string, pageId: Nullable<string>) => {
  86. let tags: string[] = [];
  87. // when the page exists
  88. if (pageId != null) {
  89. const res = await apiGet<IPageTagsInfo>(endpoint, { pageId });
  90. tags = res?.tags;
  91. }
  92. return { tags };
  93. };
  94. return useSWRImmutable(key, fetcher);
  95. };
  96. export const useSWRxPageInfo = (
  97. pageId: string | null | undefined,
  98. shareLinkId?: string | null,
  99. ): SWRResponse<IPageInfo | IPageInfoForOperation, Error> => {
  100. // assign null if shareLinkId is undefined in order to identify SWR key only by pageId
  101. const fixedShareLinkId = shareLinkId ?? null;
  102. return useSWRImmutable(
  103. pageId != null ? ['/page/info', pageId, fixedShareLinkId] : null,
  104. (endpoint, pageId, shareLinkId) => apiv3Get(endpoint, { pageId, shareLinkId }).then(response => response.data),
  105. );
  106. };
  107. type PageInfoInjector = {
  108. injectTo: <D extends HasObjectId>(pages: (D | IDataWithMeta<D>)[]) => IDataWithMeta<D, IPageInfoForOperation>[],
  109. }
  110. const isIDataWithMeta = (item: HasObjectId | IDataWithMeta): item is IDataWithMeta => {
  111. return 'data' in item;
  112. };
  113. export const useSWRxPageInfoForList = (
  114. pageIds: string[] | null | undefined,
  115. attachBookmarkCount = false,
  116. attachShortBody = false,
  117. ): SWRResponse<Record<string, IPageInfoForListing>, Error> & PageInfoInjector => {
  118. const shouldFetch = pageIds != null && pageIds.length > 0;
  119. const swrResult = useSWRImmutable<Record<string, IPageInfoForListing>>(
  120. shouldFetch ? ['/page-listing/info', pageIds, attachBookmarkCount, attachShortBody] : null,
  121. (endpoint, pageIds, attachBookmarkCount, attachShortBody) => {
  122. return apiv3Get(endpoint, { pageIds, attachBookmarkCount, attachShortBody }).then(response => response.data);
  123. },
  124. );
  125. return {
  126. ...swrResult,
  127. injectTo: <D extends HasObjectId>(pages: (D | IDataWithMeta<D>)[]) => {
  128. return pages.map((item) => {
  129. const page = isIDataWithMeta(item) ? item.data : item;
  130. const orgPageMeta = isIDataWithMeta(item) ? item.meta : undefined;
  131. // get an applicable IPageInfo
  132. const applicablePageInfo = (swrResult.data ?? {})[page._id];
  133. return {
  134. data: page,
  135. meta: applicablePageInfo ?? orgPageMeta,
  136. };
  137. });
  138. },
  139. };
  140. };
  141. export const useSWRxPageRevisions = (
  142. pageId: string,
  143. page: number, // page number of pagination
  144. limit: number, // max number of pages in one paginate
  145. ): SWRResponse<IRevisionsForPagination, Error> => {
  146. return useSWRImmutable<IRevisionsForPagination, Error>(
  147. ['/revisions/list', pageId, page, limit],
  148. (endpoint, pageId, page, limit) => {
  149. return apiv3Get(endpoint, { pageId, page, limit }).then((response) => {
  150. const revisions = {
  151. revisions: response.data.docs,
  152. totalCounts: response.data.totalDocs,
  153. };
  154. return revisions;
  155. });
  156. },
  157. );
  158. };
  159. /*
  160. * Grant normalization fetching hooks
  161. */
  162. export const useSWRxIsGrantNormalized = (
  163. pageId: string | null | undefined,
  164. ): SWRResponse<IResIsGrantNormalized, Error> => {
  165. return useSWRImmutable(
  166. pageId != null ? ['/page/is-grant-normalized', pageId] : null,
  167. (endpoint, pageId) => apiv3Get(endpoint, { pageId }).then(response => response.data),
  168. );
  169. };
  170. export const useSWRxApplicableGrant = (
  171. pageId: string | null | undefined,
  172. ): SWRResponse<IRecordApplicableGrant, Error> => {
  173. return useSWRImmutable(
  174. pageId != null ? ['/page/applicable-grant', pageId] : null,
  175. (endpoint, pageId) => apiv3Get(endpoint, { pageId }).then(response => response.data),
  176. );
  177. };