ContextExtractor.tsx 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. import React, { FC, useEffect, useState } from 'react';
  2. import { pagePathUtils } from '@growi/core';
  3. import { IUserUISettings } from '~/interfaces/user-ui-settings';
  4. import {
  5. useIsDeviceSmallerThanMd, useIsDeviceSmallerThanLg,
  6. usePreferDrawerModeByUser, usePreferDrawerModeOnEditByUser, useSidebarCollapsed, useCurrentSidebarContents, useCurrentProductNavWidth,
  7. useSelectedGrant, useSelectedGrantGroupId, useSelectedGrantGroupName,
  8. } from '~/stores/ui';
  9. import { useSetupGlobalSocket, useSetupGlobalAdminSocket } from '~/stores/websocket';
  10. import {
  11. useSiteUrl,
  12. useCurrentCreatedAt, useDeleteUsername, useDeletedAt, useHasChildren, useHasDraftOnHackmd,
  13. useIsNotCreatable, useIsTrashPage, useIsUserPage, useLastUpdateUsername,
  14. useCurrentPageId, usePageIdOnHackmd, usePageUser, useCurrentPagePath, useRevisionCreatedAt, useRevisionId, useRevisionIdHackmdSynced,
  15. useShareLinkId, useShareLinksNumber, useTemplateTagData, useCurrentUpdatedAt, useCreator, useRevisionAuthor, useCurrentUser, useTargetAndAncestors,
  16. useNotFoundTargetPathOrId, useIsSearchPage, useIsForbidden, useIsIdenticalPath, useHasParent,
  17. useIsAclEnabled, useIsSearchServiceConfigured, useIsSearchServiceReachable, useIsEnabledAttachTitleHeader,
  18. useDefaultIndentSize, useIsIndentSizeForced, useCsrfToken, useIsEmptyPage, useEmptyPageId, useGrowiVersion, useAuditLogEnabled,
  19. useActivityExpirationSeconds, useAuditLogAvailableActions,
  20. } from '../../stores/context';
  21. const { isTrashPage: _isTrashPage } = pagePathUtils;
  22. const jsonNull = 'null';
  23. const ContextExtractorOnce: FC = () => {
  24. const mainContent = document.querySelector('#content-main');
  25. const notFoundContentForPt = document.getElementById('growi-pagetree-not-found-context');
  26. const notFoundContext = document.getElementById('growi-not-found-context');
  27. const forbiddenContent = document.getElementById('forbidden-page');
  28. // get csrf token from body element
  29. // DO NOT REMOVE: uploading attachment data requires appContainer.csrfToken
  30. const body = document.querySelector('body');
  31. const csrfToken = body?.dataset.csrftoken;
  32. /*
  33. * App Context from DOM
  34. */
  35. const currentUser = JSON.parse(document.getElementById('growi-current-user')?.textContent || jsonNull);
  36. /*
  37. * Settings from context-hydrate DOM
  38. */
  39. const configByContextHydrate = JSON.parse(document.getElementById('growi-context-hydrate')?.textContent || jsonNull);
  40. /*
  41. * UserUISettings from DOM
  42. */
  43. const userUISettings: Partial<IUserUISettings> = JSON.parse(document.getElementById('growi-user-ui-settings')?.textContent || jsonNull);
  44. /*
  45. * Page Context from DOM
  46. */
  47. const revisionId = mainContent?.getAttribute('data-page-revision-id');
  48. const path = decodeURI(mainContent?.getAttribute('data-path') || '');
  49. // assign `null` to avoid returning empty string
  50. const pageId = mainContent?.getAttribute('data-page-id') || null;
  51. const emptyPageId = notFoundContext?.getAttribute('data-page-id') || null;
  52. const revisionCreatedAt = +(mainContent?.getAttribute('data-page-revision-created') || '');
  53. // createdAt
  54. const createdAtAttribute = mainContent?.getAttribute('data-page-created-at');
  55. const createdAt: Date | null = (createdAtAttribute != null) ? new Date(createdAtAttribute) : null;
  56. // updatedAt
  57. const updatedAtAttribute = mainContent?.getAttribute('data-page-updated-at');
  58. const updatedAt: Date | null = (updatedAtAttribute != null) ? new Date(updatedAtAttribute) : null;
  59. const deletedAt = mainContent?.getAttribute('data-page-deleted-at') || null;
  60. const isIdenticalPath = JSON.parse(mainContent?.getAttribute('data-identical-path') || jsonNull) ?? false;
  61. const isUserPage = JSON.parse(mainContent?.getAttribute('data-page-user') || jsonNull) != null;
  62. const isTrashPage = _isTrashPage(path);
  63. const isNotCreatable = JSON.parse(mainContent?.getAttribute('data-page-is-not-creatable') || jsonNull) ?? false;
  64. const isForbidden = forbiddenContent != null;
  65. const pageUser = JSON.parse(mainContent?.getAttribute('data-page-user') || jsonNull);
  66. const hasChildren = JSON.parse(mainContent?.getAttribute('data-page-has-children') || jsonNull);
  67. const hasParent = JSON.parse(mainContent?.getAttribute('data-has-parent') || jsonNull);
  68. const templateTagData = mainContent?.getAttribute('data-template-tags') || null;
  69. const shareLinksNumber = mainContent?.getAttribute('data-share-links-number');
  70. const shareLinkId = JSON.parse(mainContent?.getAttribute('data-share-link-id') || jsonNull);
  71. const revisionIdHackmdSynced = mainContent?.getAttribute('data-page-revision-id-hackmd-synced') || null;
  72. const lastUpdateUsername = mainContent?.getAttribute('data-page-last-update-username') || null;
  73. const deleteUsername = mainContent?.getAttribute('data-page-delete-username') || null;
  74. const pageIdOnHackmd = mainContent?.getAttribute('data-page-id-on-hackmd') || null;
  75. const hasDraftOnHackmd = !!mainContent?.getAttribute('data-page-has-draft-on-hackmd');
  76. const creator = JSON.parse(mainContent?.getAttribute('data-page-creator') || jsonNull);
  77. const revisionAuthor = JSON.parse(mainContent?.getAttribute('data-page-revision-author') || jsonNull);
  78. const targetAndAncestors = JSON.parse(document.getElementById('growi-pagetree-target-and-ancestors')?.textContent || jsonNull);
  79. const notFoundTargetPathOrId = JSON.parse(notFoundContentForPt?.getAttribute('data-not-found-target-path-or-id') || jsonNull);
  80. const isSearchPage = document.getElementById('search-page') != null;
  81. const isEmptyPage = JSON.parse(mainContent?.getAttribute('data-page-is-empty') || jsonNull) ?? false;
  82. const grant = +(mainContent?.getAttribute('data-page-grant') || 1);
  83. const grantGroupId = mainContent?.getAttribute('data-page-grant-group') || null;
  84. const grantGroupName = mainContent?.getAttribute('data-page-grant-group-name') || null;
  85. /*
  86. * use static swr
  87. */
  88. useCsrfToken(csrfToken);
  89. // App
  90. useCurrentUser(currentUser);
  91. // UserUISettings
  92. usePreferDrawerModeByUser(userUISettings?.preferDrawerModeByUser ?? configByContextHydrate.isSidebarDrawerMode);
  93. usePreferDrawerModeOnEditByUser(userUISettings?.preferDrawerModeOnEditByUser);
  94. useSidebarCollapsed(userUISettings?.isSidebarCollapsed ?? configByContextHydrate.isSidebarClosedAtDockMode);
  95. useCurrentSidebarContents(userUISettings?.currentSidebarContents);
  96. useCurrentProductNavWidth(userUISettings?.currentProductNavWidth);
  97. // hydrated config
  98. useSiteUrl(configByContextHydrate.crowi.url);
  99. useIsAclEnabled(configByContextHydrate.isAclEnabled);
  100. useIsSearchServiceConfigured(configByContextHydrate.isSearchServiceConfigured);
  101. useIsSearchServiceReachable(configByContextHydrate.isSearchServiceReachable);
  102. useIsEnabledAttachTitleHeader(configByContextHydrate.isEnabledAttachTitleHeader);
  103. useIsIndentSizeForced(configByContextHydrate.isIndentSizeForced);
  104. useDefaultIndentSize(configByContextHydrate.adminPreferredIndentSize);
  105. useAuditLogEnabled(configByContextHydrate.auditLogEnabled);
  106. useActivityExpirationSeconds(configByContextHydrate.activityExpirationSeconds);
  107. useAuditLogAvailableActions(configByContextHydrate.auditLogAvailableActions);
  108. useGrowiVersion(configByContextHydrate.crowi.version);
  109. // Page
  110. useCurrentCreatedAt(createdAt);
  111. useDeleteUsername(deleteUsername);
  112. useDeletedAt(deletedAt);
  113. useHasChildren(hasChildren);
  114. useHasDraftOnHackmd(hasDraftOnHackmd);
  115. useIsIdenticalPath(isIdenticalPath);
  116. useIsNotCreatable(isNotCreatable);
  117. useIsForbidden(isForbidden);
  118. useIsTrashPage(isTrashPage);
  119. useIsUserPage(isUserPage);
  120. useLastUpdateUsername(lastUpdateUsername);
  121. useCurrentPageId(pageId);
  122. useEmptyPageId(emptyPageId);
  123. usePageIdOnHackmd(pageIdOnHackmd);
  124. usePageUser(pageUser);
  125. useCurrentPagePath(path);
  126. useRevisionCreatedAt(revisionCreatedAt);
  127. useRevisionId(revisionId);
  128. useRevisionIdHackmdSynced(revisionIdHackmdSynced);
  129. useShareLinkId(shareLinkId);
  130. useShareLinksNumber(shareLinksNumber);
  131. useTemplateTagData(templateTagData);
  132. useCurrentUpdatedAt(updatedAt);
  133. useCreator(creator);
  134. useRevisionAuthor(revisionAuthor);
  135. useTargetAndAncestors(targetAndAncestors);
  136. useNotFoundTargetPathOrId(notFoundTargetPathOrId);
  137. useIsSearchPage(isSearchPage);
  138. useIsEmptyPage(isEmptyPage);
  139. useHasParent(hasParent);
  140. // Navigation
  141. usePreferDrawerModeByUser();
  142. usePreferDrawerModeOnEditByUser();
  143. useIsDeviceSmallerThanMd();
  144. // Navigation
  145. usePreferDrawerModeByUser();
  146. usePreferDrawerModeOnEditByUser();
  147. useIsDeviceSmallerThanMd();
  148. // Editor
  149. useSelectedGrant(grant);
  150. useSelectedGrantGroupId(grantGroupId);
  151. useSelectedGrantGroupName(grantGroupName);
  152. // SearchResult
  153. useIsDeviceSmallerThanLg();
  154. // Global Socket
  155. useSetupGlobalSocket();
  156. const shouldInitAdminSock = !!currentUser?.isAdmin;
  157. useSetupGlobalAdminSocket(shouldInitAdminSock);
  158. return null;
  159. };
  160. const ContextExtractor: FC = React.memo(() => {
  161. const [isRunOnce, setRunOnce] = useState(false);
  162. useEffect(() => {
  163. setRunOnce(true);
  164. }, []);
  165. return isRunOnce ? null : <ContextExtractorOnce></ContextExtractorOnce>;
  166. });
  167. export default ContextExtractor;