Просмотр исходного кода

feat(feedback): add java onboarding instructions (#65729)

- add java onboarding instructions for user feedback (these are the
crash report API instructions shown here:
https://docs.sentry.io/platforms/java/user-feedback/ -- the rest of the
platforms to come
- moved the`crashReportCallout` component into a new file, since it was
in a replay utils file before
- refactor some utils files into their own folders
- `useLoadFeedbackOnboardingDoc` is a copy of the replay hook but it
allows all platforms to be loaded in
- relates to https://github.com/getsentry/sentry/issues/65728

<img width="472" alt="SCR-20240223-lsof"
src="https://github.com/getsentry/sentry/assets/56095982/d81f32fe-7959-4b78-ab65-e0d549f5e383">
Michelle Zhang 1 год назад
Родитель
Сommit
40ab562c00

+ 9 - 1
static/app/components/feedback/feedbackOnboarding/feedbackOnboardingLayout.tsx

@@ -7,6 +7,7 @@ import {Step} from 'sentry/components/onboarding/gettingStartedDoc/step';
 import type {DocsParams} from 'sentry/components/onboarding/gettingStartedDoc/types';
 import {useSourcePackageRegistries} from 'sentry/components/onboarding/gettingStartedDoc/useSourcePackageRegistries';
 import {useUrlPlatformOptions} from 'sentry/components/onboarding/platformOptionsControl';
+import {space} from 'sentry/styles/space';
 import useOrganization from 'sentry/utils/useOrganization';
 
 export function FeedbackOnboardingLayout({
@@ -23,7 +24,7 @@ export function FeedbackOnboardingLayout({
   const {isLoading: isLoadingRegistry, data: registryData} =
     useSourcePackageRegistries(organization);
   const selectedOptions = useUrlPlatformOptions(docsConfig.platformOptions);
-  const {steps} = useMemo(() => {
+  const {introduction, steps} = useMemo(() => {
     const doc = docsConfig[configType] ?? docsConfig.onboarding;
 
     const docParams: DocsParams<any> = {
@@ -72,6 +73,7 @@ export function FeedbackOnboardingLayout({
   return (
     <AuthTokenGeneratorProvider projectSlug={projectSlug}>
       <Wrapper>
+        {introduction && <Introduction>{introduction}</Introduction>}
         <Steps>
           {steps.map(step => (
             <Step key={step.title ?? step.type} {...step} />
@@ -101,3 +103,9 @@ const Wrapper = styled('div')`
     }
   }
 `;
+
+const Introduction = styled('div')`
+  display: flex;
+  flex-direction: column;
+  margin-bottom: ${space(4)};
+`;

+ 25 - 13
static/app/components/feedback/feedbackOnboarding/sidebar.tsx

@@ -8,21 +8,23 @@ import HighlightTopRightPattern from 'sentry-images/pattern/highlight-top-right.
 import {Button} from 'sentry/components/button';
 import {CompactSelect} from 'sentry/components/compactSelect';
 import {FeedbackOnboardingLayout} from 'sentry/components/feedback/feedbackOnboarding/feedbackOnboardingLayout';
+import useLoadFeedbackOnboardingDoc from 'sentry/components/feedback/feedbackOnboarding/useLoadFeedbackOnboardingDoc';
 import RadioGroup from 'sentry/components/forms/controls/radioGroup';
 import IdBadge from 'sentry/components/idBadge';
 import LoadingIndicator from 'sentry/components/loadingIndicator';
 import {PlatformOptionDropdown} from 'sentry/components/replaysOnboarding/platformOptionDropdown';
 import useCurrentProjectState from 'sentry/components/replaysOnboarding/useCurrentProjectState';
-import useLoadOnboardingDoc from 'sentry/components/replaysOnboarding/useLoadOnboardingDoc';
 import {replayJsFrameworkOptions} from 'sentry/components/replaysOnboarding/utils';
 import SidebarPanel from 'sentry/components/sidebar/sidebarPanel';
 import type {CommonSidebarProps} from 'sentry/components/sidebar/types';
 import {SidebarPanelKey} from 'sentry/components/sidebar/types';
 import TextOverflow from 'sentry/components/textOverflow';
 import {
+  feedbackOnboardingPlatforms,
   replayBackendPlatforms,
   replayFrontendPlatforms,
   replayJsLoaderInstructionsPlatformList,
+  replayPlatforms,
 } from 'sentry/data/platformCategories';
 import platforms, {otherPlatform} from 'sentry/data/platforms';
 import {t, tct} from 'sentry/locale';
@@ -167,9 +169,12 @@ function OnboardingContent({currentProject}: {currentProject: Project}) {
     currentProject.platform &&
     replayJsLoaderInstructionsPlatformList.includes(currentProject.platform);
 
-  const backendPlatforms =
+  const webBackendPlatform =
     currentProject.platform && replayBackendPlatforms.includes(currentProject.platform);
 
+  const backendPlatform =
+    currentProject.platform && !replayPlatforms.includes(currentProject.platform);
+
   const currentPlatform = currentProject.platform
     ? platforms.find(p => p.id === currentProject.platform) ?? otherPlatform
     : otherPlatform;
@@ -179,7 +184,7 @@ function OnboardingContent({currentProject}: {currentProject: Project}) {
     dsn,
     cdn,
     isProjKeysLoading,
-  } = useLoadOnboardingDoc({
+  } = useLoadFeedbackOnboardingDoc({
     platform:
       showJsFrameworkInstructions && setupMode() === 'npm'
         ? replayJsFrameworkOptions.find(p => p.id === jsFramework.value) ??
@@ -190,7 +195,7 @@ function OnboardingContent({currentProject}: {currentProject: Project}) {
   });
 
   // New onboarding docs for initial loading of JS Framework options
-  const {docs: jsFrameworkDocs} = useLoadOnboardingDoc({
+  const {docs: jsFrameworkDocs} = useLoadFeedbackOnboardingDoc({
     platform:
       replayJsFrameworkOptions.find(p => p.id === jsFramework.value) ??
       replayJsFrameworkOptions[0],
@@ -206,7 +211,7 @@ function OnboardingContent({currentProject}: {currentProject: Project}) {
           choices={[
             [
               'npm',
-              backendPlatforms ? (
+              webBackendPlatform ? (
                 <PlatformSelect key="platform-select">
                   {tct('I use [platformSelect]', {
                     platformSelect: (
@@ -241,7 +246,8 @@ function OnboardingContent({currentProject}: {currentProject: Project}) {
           onChange={setSetupMode}
         />
       ) : (
-        newDocs?.platformOptions && (
+        newDocs?.platformOptions &&
+        !backendPlatform && (
           <PlatformSelect>
             {tct("I'm using [platformSelect]", {
               platformSelect: (
@@ -263,8 +269,12 @@ function OnboardingContent({currentProject}: {currentProject: Project}) {
     );
   }
 
-  // No platform or no docs
-  if (!currentPlatform || !newDocs) {
+  // No platform or not supported or no docs
+  if (
+    !currentPlatform ||
+    !feedbackOnboardingPlatforms.includes(currentPlatform.id) ||
+    !newDocs
+  ) {
     return (
       <Fragment>
         <div>
@@ -299,11 +309,13 @@ function OnboardingContent({currentProject}: {currentProject: Project}) {
           projectId={currentProject.id}
           projectSlug={currentProject.slug}
           configType={
-            setupMode() === 'npm' || // switched to NPM option
-            (!setupMode() && defaultTab === 'npm') || // default value for FE frameworks when ?mode={...} in URL is not set yet
-            npmOnlyFramework // even if '?mode=jsLoader', only show npm instructions for FE frameworks
-              ? 'feedbackOnboardingNpm'
-              : 'replayOnboardingJsLoader'
+            backendPlatform
+              ? 'feedbackOnboardingCrashApi'
+              : setupMode() === 'npm' || // switched to NPM option
+                  (!setupMode() && defaultTab === 'npm') || // default value for FE frameworks when ?mode={...} in URL is not set yet
+                  npmOnlyFramework // even if '?mode=jsLoader', only show npm instructions for FE frameworks
+                ? 'feedbackOnboardingNpm'
+                : 'replayOnboardingJsLoader'
           }
         />
       )}

+ 90 - 0
static/app/components/feedback/feedbackOnboarding/useLoadFeedbackOnboardingDoc.tsx

@@ -0,0 +1,90 @@
+import {useEffect, useState} from 'react';
+import * as Sentry from '@sentry/react';
+
+import type {Docs} from 'sentry/components/onboarding/gettingStartedDoc/types';
+import {feedbackOnboardingPlatforms} from 'sentry/data/platformCategories';
+import type {Organization, PlatformIntegration, ProjectKey} from 'sentry/types';
+import {useApiQuery} from 'sentry/utils/queryClient';
+
+function useLoadFeedbackOnboardingDoc({
+  platform,
+  organization,
+  projectSlug,
+}: {
+  organization: Organization;
+  platform: PlatformIntegration;
+  projectSlug: string;
+}) {
+  const [module, setModule] = useState<
+    | null
+    | {
+        default: Docs<any>;
+      }
+    | 'none'
+  >(null);
+
+  const platformPath =
+    platform?.type === 'framework'
+      ? platform?.id === 'capacitor'
+        ? `capacitor/capacitor`
+        : platform?.id.replace(`${platform.language}-`, `${platform.language}/`)
+      : `${platform?.language}/${platform?.id}`;
+
+  const {
+    data: projectKeys,
+    isError: projectKeysIsError,
+    isLoading: projectKeysIsLoading,
+  } = useApiQuery<ProjectKey[]>([`/projects/${organization.slug}/${projectSlug}/keys/`], {
+    staleTime: Infinity,
+  });
+
+  useEffect(() => {
+    async function getGettingStartedDoc() {
+      if (!feedbackOnboardingPlatforms.includes(platform.id)) {
+        setModule('none');
+        return;
+      }
+      try {
+        const mod = await import(
+          /* webpackExclude: /.spec/ */
+          `sentry/gettingStartedDocs/${platformPath}`
+        );
+        setModule(mod);
+      } catch (err) {
+        Sentry.captureException(err);
+      }
+    }
+    getGettingStartedDoc();
+    return () => {
+      setModule(null);
+    };
+  }, [platformPath, platform.id]);
+
+  if (module === 'none') {
+    return {
+      docs: null,
+    };
+  }
+
+  if (!module || projectKeysIsLoading) {
+    return {
+      isProjKeysLoading: true,
+    };
+  }
+
+  if (projectKeysIsError) {
+    return {
+      isProjKeysError: true,
+    };
+  }
+
+  const {default: docs} = module;
+
+  return {
+    docs,
+    dsn: projectKeys[0].dsn.public,
+    cdn: projectKeys[0].dsn.cdn,
+  };
+}
+
+export default useLoadFeedbackOnboardingDoc;

+ 16 - 0
static/app/components/onboarding/gettingStartedDoc/feedback/crashReportCallout.tsx

@@ -0,0 +1,16 @@
+import Alert from 'sentry/components/alert';
+import ExternalLink from 'sentry/components/links/externalLink';
+import {tct} from 'sentry/locale';
+
+export default function crashReportCallout({link}: {link: string}) {
+  return (
+    <Alert type="info" showIcon>
+      {tct(
+        `Interested in receiving feedback only when an error happens? [link:Read the docs] to learn how to set up our crash-report modal.`,
+        {
+          link: <ExternalLink href={link} />,
+        }
+      )}
+    </Alert>
+  );
+}

+ 18 - 0
static/app/components/onboarding/gettingStartedDoc/replay/tracePropagationMessage.tsx

@@ -0,0 +1,18 @@
+import Alert from 'sentry/components/alert';
+import ExternalLink from 'sentry/components/links/externalLink';
+import {tct} from 'sentry/locale';
+
+export default function tracePropagationMessage() {
+  return (
+    <Alert type="info" showIcon>
+      {tct(
+        `To see replays for backend errors, ensure that you have set up trace propagation. To learn more, [link:read the docs].`,
+        {
+          link: (
+            <ExternalLink href="https://docs.sentry.io/product/session-replay/getting-started/#replays-for-backend-errors/" />
+          ),
+        }
+      )}
+    </Alert>
+  );
+}

+ 2 - 0
static/app/components/onboarding/gettingStartedDoc/types.ts

@@ -77,6 +77,7 @@ export interface OnboardingConfig<
 export interface Docs<PlatformOptions extends BasePlatformOptions = BasePlatformOptions> {
   onboarding: OnboardingConfig<PlatformOptions>;
   customMetricsOnboarding?: OnboardingConfig<PlatformOptions>;
+  feedbackOnboardingCrashApi?: OnboardingConfig<PlatformOptions>;
   feedbackOnboardingNpm?: OnboardingConfig<PlatformOptions>;
   platformOptions?: PlatformOptions;
   replayOnboardingJsLoader?: OnboardingConfig<PlatformOptions>;
@@ -86,6 +87,7 @@ export interface Docs<PlatformOptions extends BasePlatformOptions = BasePlatform
 export type ConfigType =
   | 'onboarding'
   | 'feedbackOnboardingNpm'
+  | 'feedbackOnboardingCrashApi'
   | 'replayOnboardingNpm'
   | 'replayOnboardingJsLoader'
   | 'customMetricsOnboarding';

+ 41 - 0
static/app/components/onboarding/gettingStartedDoc/utils/feedbackOnboarding.tsx

@@ -0,0 +1,41 @@
+import ExternalLink from 'sentry/components/links/externalLink';
+import {t, tct} from 'sentry/locale';
+
+export const getFeedbackConfigureDescription = ({link}: {link: string}) =>
+  tct(
+    'To set up the integration, add the following to your Sentry initialization. There are many options you can pass to the [code:integrations] constructor. Learn more about configuring User Feedback by reading the [link:configuration docs].',
+    {
+      code: <code />,
+      link: <ExternalLink href={link} />,
+    }
+  );
+
+export const getFeedbackSDKSetupSnippet = ({
+  importStatement,
+  dsn,
+}: {
+  dsn: string;
+  importStatement: string;
+}) =>
+  `${importStatement}
+
+  Sentry.init({
+    dsn: "${dsn}",
+    integrations: [
+      Sentry.feedbackIntegration({
+// Additional SDK configuration goes in here, for example:
+colorScheme: "light",
+}),
+    ],
+  });`;
+
+export const getCrashReportApiIntroduction = () =>
+  t(
+    'When a user experiences an error, Sentry provides the ability to collect additional feedback. The user feedback API allows you to collect user feedback while utilizing your own UI. You can use the same programming language you have in your app to send user feedback.'
+  );
+
+export const getCrashReportInstallDescription = () =>
+  tct(
+    'Sentry pairs the feedback with the original event, giving you additional insight into issues. Sentry needs the [codeEvent:eventId] to be able to associate the user feedback to the corresponding event. To get the [codeEvent:eventId], you can use [codeBefore:beforeSend] or the return value of the method capturing an event.',
+    {codeEvent: <code />, codeBefore: <code />}
+  );

+ 0 - 105
static/app/components/onboarding/gettingStartedDoc/utils/index.tsx

@@ -1,5 +1,4 @@
 import ExternalLink from 'sentry/components/links/externalLink';
-import type {DocsParams} from 'sentry/components/onboarding/gettingStartedDoc/types';
 import {t, tct} from 'sentry/locale';
 import type {Organization, PlatformKey} from 'sentry/types';
 import {trackAnalytics} from 'sentry/utils/analytics';
@@ -69,107 +68,3 @@ export function getUploadSourceMapsStep({
     ],
   };
 }
-
-export const getFeedbackConfigureDescription = ({link}: {link: string}) =>
-  tct(
-    'To set up the integration, add the following to your Sentry initialization. There are many options you can pass to the [code:integrations] constructor. Learn more about configuring User Feedback by reading the [link:configuration docs].',
-    {
-      code: <code />,
-      link: <ExternalLink href={link} />,
-    }
-  );
-
-export const getReplayConfigureDescription = ({link}: {link: string}) =>
-  tct(
-    'Add the following to your SDK config. There are several privacy and sampling options available, all of which can be set using the [code:integrations] constructor. Learn more about configuring Session Replay by reading the [link:configuration docs].',
-    {
-      code: <code />,
-      link: <ExternalLink href={link} />,
-    }
-  );
-
-export const getReplayJsLoaderSdkSetupSnippet = (params: DocsParams) => `
-<script>
-  Sentry.onLoad(function() {
-    Sentry.init({
-      integrations: [
-        Sentry.replayIntegration(${getReplayConfigOptions(params.replayOptions)}),
-      ],
-      // Session Replay
-      replaysSessionSampleRate: 0.1, // This sets the sample rate at 10%. You may want to change it to 100% while in development and then sample at a lower rate in production.
-      replaysOnErrorSampleRate: 1.0, // If you're not already sampling the entire session, change the sample rate to 100% when sampling sessions where errors occur.
-    });
-  });
-</script>`;
-
-export const getReplaySDKSetupSnippet = ({
-  importStatement,
-  dsn,
-  mask,
-  block,
-}: {
-  dsn: string;
-  importStatement: string;
-  block?: boolean;
-  mask?: boolean;
-}) =>
-  `${importStatement}
-
-  Sentry.init({
-    dsn: "${dsn}",
-
-    integrations: [
-      Sentry.replayIntegration(${getReplayConfigOptions({
-        mask,
-        block,
-      })}),
-    ],
-    // Session Replay
-    replaysSessionSampleRate: 0.1, // This sets the sample rate at 10%. You may want to change it to 100% while in development and then sample at a lower rate in production.
-    replaysOnErrorSampleRate: 1.0, // If you're not already sampling the entire session, change the sample rate to 100% when sampling sessions where errors occur.
-  });`;
-
-export const getReplayConfigOptions = ({
-  mask,
-  block,
-}: {
-  block?: boolean;
-  mask?: boolean;
-} = {}) => {
-  if (mask && block) {
-    return ``;
-  }
-  if (mask) {
-    return `{
-          blockAllMedia: false,
-        }`;
-  }
-  if (block) {
-    return `{
-          maskAllText: false,
-        }`;
-  }
-  return `{
-          maskAllText: false,
-          blockAllMedia: false,
-        }`;
-};
-
-export const getFeedbackSDKSetupSnippet = ({
-  importStatement,
-  dsn,
-}: {
-  dsn: string;
-  importStatement: string;
-}) =>
-  `${importStatement}
-
-  Sentry.init({
-    dsn: "${dsn}",
-    integrations: [
-      Sentry.feedbackIntegration({
-// Additional SDK configuration goes in here, for example:
-colorScheme: "light",
-}),
-    ],
-  });`;

+ 79 - 0
static/app/components/onboarding/gettingStartedDoc/utils/replayOnboarding.tsx

@@ -0,0 +1,79 @@
+import ExternalLink from 'sentry/components/links/externalLink';
+import type {DocsParams} from 'sentry/components/onboarding/gettingStartedDoc/types';
+import {tct} from 'sentry/locale';
+
+export const getReplayConfigureDescription = ({link}: {link: string}) =>
+  tct(
+    'Add the following to your SDK config. There are several privacy and sampling options available, all of which can be set using the [code:integrations] constructor. Learn more about configuring Session Replay by reading the [link:configuration docs].',
+    {
+      code: <code />,
+      link: <ExternalLink href={link} />,
+    }
+  );
+
+export const getReplayJsLoaderSdkSetupSnippet = (params: DocsParams) => `
+<script>
+  Sentry.onLoad(function() {
+    Sentry.init({
+      integrations: [
+        Sentry.replayIntegration(${getReplayConfigOptions(params.replayOptions)}),
+      ],
+      // Session Replay
+      replaysSessionSampleRate: 0.1, // This sets the sample rate at 10%. You may want to change it to 100% while in development and then sample at a lower rate in production.
+      replaysOnErrorSampleRate: 1.0, // If you're not already sampling the entire session, change the sample rate to 100% when sampling sessions where errors occur.
+    });
+  });
+</script>`;
+
+export const getReplaySDKSetupSnippet = ({
+  importStatement,
+  dsn,
+  mask,
+  block,
+}: {
+  dsn: string;
+  importStatement: string;
+  block?: boolean;
+  mask?: boolean;
+}) =>
+  `${importStatement}
+
+  Sentry.init({
+    dsn: "${dsn}",
+
+    integrations: [
+      Sentry.replayIntegration(${getReplayConfigOptions({
+        mask,
+        block,
+      })}),
+    ],
+    // Session Replay
+    replaysSessionSampleRate: 0.1, // This sets the sample rate at 10%. You may want to change it to 100% while in development and then sample at a lower rate in production.
+    replaysOnErrorSampleRate: 1.0, // If you're not already sampling the entire session, change the sample rate to 100% when sampling sessions where errors occur.
+  });`;
+
+export const getReplayConfigOptions = ({
+  mask,
+  block,
+}: {
+  block?: boolean;
+  mask?: boolean;
+} = {}) => {
+  if (mask && block) {
+    return ``;
+  }
+  if (mask) {
+    return `{
+          blockAllMedia: false,
+        }`;
+  }
+  if (block) {
+    return `{
+          maskAllText: false,
+        }`;
+  }
+  return `{
+          maskAllText: false,
+          blockAllMedia: false,
+        }`;
+};

+ 0 - 27
static/app/components/replaysOnboarding/utils.tsx

@@ -1,10 +1,7 @@
 import partition from 'lodash/partition';
 
-import Alert from 'sentry/components/alert';
-import ExternalLink from 'sentry/components/links/externalLink';
 import {replayFrontendPlatforms, replayPlatforms} from 'sentry/data/platformCategories';
 import platforms from 'sentry/data/platforms';
-import {tct} from 'sentry/locale';
 import type {PlatformIntegration, PlatformKey, Project} from 'sentry/types';
 
 export function generateDocKeys(platform: PlatformKey): string[] {
@@ -33,27 +30,3 @@ export function splitProjectsByReplaySupport(projects: Project[]) {
 export const replayJsFrameworkOptions: PlatformIntegration[] = platforms.filter(p =>
   replayFrontendPlatforms.includes(p.id)
 );
-
-export const tracePropagationMessage = (
-  <Alert type="info" showIcon>
-    {tct(
-      `To see replays for backend errors, ensure that you have set up trace propagation. To learn more, [link:read the docs].`,
-      {
-        link: (
-          <ExternalLink href="https://docs.sentry.io/product/session-replay/getting-started/#replays-for-backend-errors/" />
-        ),
-      }
-    )}
-  </Alert>
-);
-
-export const crashReportCallout = ({link}: {link: string}) => (
-  <Alert type="info" showIcon>
-    {tct(
-      `Interested in receiving feedback only when an error happens? [link:Read the docs] to learn how to set up our crash-report modal.`,
-      {
-        link: <ExternalLink href={link} />,
-      }
-    )}
-  </Alert>
-);

Некоторые файлы не были показаны из-за большого количества измененных файлов