page.tsx 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  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, useShareLinkId,
  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 { data: shareLinkId } = useShareLinkId();
  84. const endpoint = `/pages.getPageTag?pageId=${pageId}`;
  85. const key = [endpoint, pageId, shareLinkId];
  86. const fetcher = async(endpoint: string, pageId: Nullable<string>, shareLinkId: Nullable<string>) => {
  87. if (shareLinkId != null) {
  88. return;
  89. }
  90. let tags: string[] = [];
  91. // when the page exists or is a shared page
  92. if (pageId != null && shareLinkId == null) {
  93. const res = await apiGet<IPageTagsInfo>(endpoint, { pageId });
  94. tags = res?.tags;
  95. }
  96. return { tags };
  97. };
  98. return useSWRImmutable(key, fetcher);
  99. };
  100. export const useSWRxPageInfo = (
  101. pageId: string | null | undefined,
  102. shareLinkId?: string | null,
  103. ): SWRResponse<IPageInfo | IPageInfoForOperation, Error> => {
  104. // assign null if shareLinkId is undefined in order to identify SWR key only by pageId
  105. const fixedShareLinkId = shareLinkId ?? null;
  106. return useSWRImmutable(
  107. pageId != null ? ['/page/info', pageId, fixedShareLinkId] : null,
  108. (endpoint, pageId, shareLinkId) => apiv3Get(endpoint, { pageId, shareLinkId }).then(response => response.data),
  109. );
  110. };
  111. type PageInfoInjector = {
  112. injectTo: <D extends HasObjectId>(pages: (D | IDataWithMeta<D>)[]) => IDataWithMeta<D, IPageInfoForOperation>[],
  113. }
  114. const isIDataWithMeta = (item: HasObjectId | IDataWithMeta): item is IDataWithMeta => {
  115. return 'data' in item;
  116. };
  117. export const useSWRxPageInfoForList = (
  118. pageIds: string[] | null | undefined,
  119. attachBookmarkCount = false,
  120. attachShortBody = false,
  121. ): SWRResponse<Record<string, IPageInfoForListing>, Error> & PageInfoInjector => {
  122. const shouldFetch = pageIds != null && pageIds.length > 0;
  123. const swrResult = useSWRImmutable<Record<string, IPageInfoForListing>>(
  124. shouldFetch ? ['/page-listing/info', pageIds, attachBookmarkCount, attachShortBody] : null,
  125. (endpoint, pageIds, attachBookmarkCount, attachShortBody) => {
  126. return apiv3Get(endpoint, { pageIds, attachBookmarkCount, attachShortBody }).then(response => response.data);
  127. },
  128. );
  129. return {
  130. ...swrResult,
  131. injectTo: <D extends HasObjectId>(pages: (D | IDataWithMeta<D>)[]) => {
  132. return pages.map((item) => {
  133. const page = isIDataWithMeta(item) ? item.data : item;
  134. const orgPageMeta = isIDataWithMeta(item) ? item.meta : undefined;
  135. // get an applicable IPageInfo
  136. const applicablePageInfo = (swrResult.data ?? {})[page._id];
  137. return {
  138. data: page,
  139. meta: applicablePageInfo ?? orgPageMeta,
  140. };
  141. });
  142. },
  143. };
  144. };
  145. export const useSWRxPageRevisions = (
  146. pageId: string,
  147. page: number, // page number of pagination
  148. limit: number, // max number of pages in one paginate
  149. ): SWRResponse<IRevisionsForPagination, Error> => {
  150. return useSWRImmutable<IRevisionsForPagination, Error>(
  151. ['/revisions/list', pageId, page, limit],
  152. (endpoint, pageId, page, limit) => {
  153. return apiv3Get(endpoint, { pageId, page, limit }).then((response) => {
  154. const revisions = {
  155. revisions: response.data.docs,
  156. totalCounts: response.data.totalDocs,
  157. };
  158. return revisions;
  159. });
  160. },
  161. );
  162. };
  163. /*
  164. * Grant normalization fetching hooks
  165. */
  166. export const useSWRxIsGrantNormalized = (
  167. pageId: string | null | undefined,
  168. ): SWRResponse<IResIsGrantNormalized, Error> => {
  169. return useSWRImmutable(
  170. pageId != null ? ['/page/is-grant-normalized', pageId] : null,
  171. (endpoint, pageId) => apiv3Get(endpoint, { pageId }).then(response => response.data),
  172. );
  173. };
  174. export const useSWRxApplicableGrant = (
  175. pageId: string | null | undefined,
  176. ): SWRResponse<IRecordApplicableGrant, Error> => {
  177. return useSWRImmutable(
  178. pageId != null ? ['/page/applicable-grant', pageId] : null,
  179. (endpoint, pageId) => apiv3Get(endpoint, { pageId }).then(response => response.data),
  180. );
  181. };