useOnboardingDocs.tsx 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. import {useEffect, useRef, useState} from 'react';
  2. import * as Sentry from '@sentry/react';
  3. import {loadDocs} from 'sentry/actionCreators/projects';
  4. import platforms from 'sentry/data/platforms';
  5. import {Project} from 'sentry/types';
  6. import useApi from 'sentry/utils/useApi';
  7. import useOrganization from 'sentry/utils/useOrganization';
  8. const INITIAL_LOADING_DOCS = {};
  9. const INITIAL_DOC_CONTENTS = {};
  10. type Options = {
  11. docKeys: string[];
  12. isPlatformSupported: boolean;
  13. project: Project;
  14. };
  15. function useOnboardingDocs({docKeys, isPlatformSupported, project}: Options) {
  16. const api = useApi();
  17. const organization = useOrganization();
  18. const loadingDocsRef = useRef<Record<string, boolean>>(INITIAL_LOADING_DOCS);
  19. const docContentsRef = useRef<Record<string, string>>(INITIAL_DOC_CONTENTS);
  20. const [docContents, setDocContents] =
  21. useState<Record<string, string>>(INITIAL_DOC_CONTENTS);
  22. docContentsRef.current = docContents;
  23. useEffect(() => {
  24. if (!isPlatformSupported) {
  25. if (loadingDocsRef.current !== INITIAL_LOADING_DOCS) {
  26. loadingDocsRef.current = INITIAL_LOADING_DOCS;
  27. }
  28. if (docContentsRef.current !== INITIAL_DOC_CONTENTS) {
  29. setDocContents(INITIAL_DOC_CONTENTS);
  30. }
  31. return undefined;
  32. }
  33. let cancelRequest = false;
  34. docKeys.forEach(docKey => {
  35. if (docKey in loadingDocsRef.current) {
  36. // If a documentation content is loading, we should not attempt to fetch it again.
  37. // otherwise, if it's not loading, we should only fetch at most once.
  38. // Any errors that occurred will be captured via Sentry.
  39. return;
  40. }
  41. const setLoadingDoc = (loadingState: boolean) => {
  42. loadingDocsRef.current = {
  43. ...loadingDocsRef.current,
  44. [docKey]: loadingState,
  45. };
  46. };
  47. const setDocContent = (docContent: string | undefined) =>
  48. setDocContents(prevState => {
  49. if (docContent === undefined) {
  50. const newState = {
  51. ...prevState,
  52. };
  53. delete newState[docKey];
  54. return newState;
  55. }
  56. return {
  57. ...prevState,
  58. [docKey]: docContent,
  59. };
  60. });
  61. setLoadingDoc(true);
  62. loadDocs({
  63. api,
  64. orgSlug: organization.slug,
  65. projectSlug: project.slug,
  66. platform: docKey as any,
  67. })
  68. .then(({html}) => {
  69. if (cancelRequest) {
  70. return;
  71. }
  72. setLoadingDoc(false);
  73. setDocContent(html as string);
  74. })
  75. .catch(error => {
  76. if (cancelRequest) {
  77. return;
  78. }
  79. Sentry.captureException(error);
  80. setLoadingDoc(false);
  81. setDocContent(undefined);
  82. });
  83. });
  84. return () => {
  85. cancelRequest = true;
  86. for (const key of docKeys) {
  87. delete loadingDocsRef.current[key];
  88. }
  89. };
  90. }, [docKeys, isPlatformSupported, api, organization.slug, project]);
  91. const currentPlatform = project.platform
  92. ? platforms.find(p => p.id === project.platform)
  93. : undefined;
  94. if (!currentPlatform || !isPlatformSupported) {
  95. return {
  96. isLoading: false,
  97. hasOnboardingContents: false,
  98. docContents: {},
  99. };
  100. }
  101. const isLoading =
  102. docKeys &&
  103. docKeys.some(key => {
  104. if (key in loadingDocsRef.current) {
  105. return !!loadingDocsRef.current[key];
  106. }
  107. return true;
  108. });
  109. return {
  110. docKeys,
  111. isLoading,
  112. hasOnboardingContents:
  113. docKeys && docKeys.every(key => typeof docContents[key] === 'string'),
  114. docContents,
  115. };
  116. }
  117. export default useOnboardingDocs;