Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(replay): add mobile platforms to onboarding sidebar #76709

Merged
merged 14 commits into from
Sep 3, 2024
Merged
2 changes: 2 additions & 0 deletions static/app/components/onboarding/gettingStartedDoc/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ export interface Docs<PlatformOptions extends BasePlatformOptions = BasePlatform
feedbackOnboardingNpm?: OnboardingConfig<PlatformOptions>;
platformOptions?: PlatformOptions;
replayOnboardingJsLoader?: OnboardingConfig<PlatformOptions>;
replayOnboardingMobile?: OnboardingConfig<PlatformOptions>;
replayOnboardingNpm?: OnboardingConfig<PlatformOptions>;
michellewzhang marked this conversation as resolved.
Show resolved Hide resolved
}

Expand All @@ -97,4 +98,5 @@ export type ConfigType =
| 'crashReportOnboarding'
| 'replayOnboardingNpm'
| 'replayOnboardingJsLoader'
| 'replayOnboardingMobile'
| 'customMetricsOnboarding';
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export function MobileBetaBanner({link}: {link: string}) {
return (
<Alert type="info" showIcon>
{tct(
`Currently, Mobile Replay is in beta. You can [link:read our docs] to learn how to set it up for your project.`,
`Currently, Mobile Replay is in beta. To learn more, you can [link:read our docs].`,
{
link: <ExternalLink href={link} />,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@ import type {DocsParams} from 'sentry/components/onboarding/gettingStartedDoc/ty
import {tct} from 'sentry/locale';

export const getReplayConfigureDescription = ({link}: {link: string}) =>
tct(
'The SDK is aggressively redacting all text and images. We plan to add fine controls for redacting, but currently, we just allow either on or off. Learn more about configuring Session Replay by reading the [link:configuration docs].',
michellewzhang marked this conversation as resolved.
Show resolved Hide resolved
{
code: <code />,
link: <ExternalLink href={link} />,
}
);

export const getReplayMobileConfigureDescription = ({link}: {link: string}) =>
michellewzhang marked this conversation as resolved.
Show resolved Hide resolved
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].',
{
Expand Down
27 changes: 18 additions & 9 deletions static/app/components/replaysOnboarding/replayOnboardingLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type {DocsParams} from 'sentry/components/onboarding/gettingStartedDoc/ty
import {useSourcePackageRegistries} from 'sentry/components/onboarding/gettingStartedDoc/useSourcePackageRegistries';
import {useUrlPlatformOptions} from 'sentry/components/onboarding/platformOptionsControl';
import ReplayConfigToggle from 'sentry/components/replaysOnboarding/replayConfigToggle';
import {space} from 'sentry/styles/space';
import useOrganization from 'sentry/utils/useOrganization';

export function ReplayOnboardingLayout({
Expand All @@ -25,7 +26,7 @@ export function ReplayOnboardingLayout({
const selectedOptions = useUrlPlatformOptions(docsConfig.platformOptions);
const [mask, setMask] = useState(true);
const [block, setBlock] = useState(true);
const {steps} = useMemo(() => {
const {introduction, steps} = useMemo(() => {
const doc = docsConfig[configType] ?? docsConfig.onboarding;

const docParams: DocsParams<any> = {
Expand Down Expand Up @@ -78,21 +79,23 @@ export function ReplayOnboardingLayout({
return (
<AuthTokenGeneratorProvider projectSlug={projectSlug}>
<Wrapper>
{introduction && <Introduction>{introduction}</Introduction>}
<Steps>
{steps.map(step =>
step.type === StepType.CONFIGURE ? (
<Step
key={step.title ?? step.type}
{...{
...step,
codeHeader: (
<ReplayConfigToggle
blockToggle={block}
maskToggle={mask}
onBlockToggle={() => setBlock(!block)}
onMaskToggle={() => setMask(!mask)}
/>
),
codeHeader:
configType === 'replayOnboardingMobile' ? null : (
<ReplayConfigToggle
blockToggle={block}
maskToggle={mask}
onBlockToggle={() => setBlock(!block)}
onMaskToggle={() => setMask(!mask)}
/>
),
}}
/>
) : (
Expand Down Expand Up @@ -124,3 +127,9 @@ const Wrapper = styled('div')`
}
}
`;

const Introduction = styled('div')`
display: flex;
flex-direction: column;
margin: 0 0 ${space(2)} 0;
`;
69 changes: 25 additions & 44 deletions static/app/components/replaysOnboarding/sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {CompactSelect} from 'sentry/components/compactSelect';
import RadioGroup from 'sentry/components/forms/controls/radioGroup';
import IdBadge from 'sentry/components/idBadge';
import LoadingIndicator from 'sentry/components/loadingIndicator';
import {MobileBetaBanner} from 'sentry/components/onboarding/gettingStartedDoc/utils';
import useCurrentProjectState from 'sentry/components/onboarding/gettingStartedDoc/utils/useCurrentProjectState';
import {useLoadGettingStarted} from 'sentry/components/onboarding/gettingStartedDoc/utils/useLoadGettingStarted';
import {PlatformOptionDropdown} from 'sentry/components/replaysOnboarding/platformOptionDropdown';
Expand All @@ -21,10 +20,10 @@ import type {CommonSidebarProps} from 'sentry/components/sidebar/types';
import {SidebarPanelKey} from 'sentry/components/sidebar/types';
import TextOverflow from 'sentry/components/textOverflow';
import {
backend,
replayBackendPlatforms,
replayFrontendPlatforms,
replayJsLoaderInstructionsPlatformList,
replayMobilePlatforms,
replayOnboardingPlatforms,
replayPlatforms,
} from 'sentry/data/platformCategories';
Expand Down Expand Up @@ -162,6 +161,8 @@ function OnboardingContent({
currentProject: Project;
hasDocs: boolean;
}) {
const organization = useOrganization();

const jsFrameworkSelectOptions = replayJsFrameworkOptions().map(platform => {
return {
value: platform.id,
Expand All @@ -175,40 +176,33 @@ function OnboardingContent({
};
});

const organization = useOrganization();
const [jsFramework, setJsFramework] = useState<{
value: PlatformKey;
label?: ReactNode;
textValue?: string;
}>(jsFrameworkSelectOptions[0]);

const defaultTab =
currentProject.platform && backend.includes(currentProject.platform)
? 'jsLoader'
: 'npm';

const {getParamValue: setupMode, setParamValue: setSetupMode} = useUrlParams(
'mode',
defaultTab
);

const showJsFrameworkInstructions =
currentProject.platform &&
replayBackendPlatforms.includes(currentProject.platform) &&
setupMode() === 'npm';

const backendPlatform =
currentProject.platform && replayBackendPlatforms.includes(currentProject.platform);
const mobilePlatform =
currentProject.platform && replayMobilePlatforms.includes(currentProject.platform);
const npmOnlyFramework =
currentProject.platform &&
replayFrontendPlatforms
.filter((p): p is PlatformKey => p !== 'javascript')
.includes(currentProject.platform);

const showRadioButtons =
const jsLoaderPlatform =
currentProject.platform &&
replayJsLoaderInstructionsPlatformList.includes(currentProject.platform);
michellewzhang marked this conversation as resolved.
Show resolved Hide resolved

const backendPlatforms =
currentProject.platform && replayBackendPlatforms.includes(currentProject.platform);
const defaultTab = backendPlatform ? 'jsLoader' : mobilePlatform ? 'mobile' : 'npm';
const {getParamValue: setupMode, setParamValue: setSetupMode} = useUrlParams(
'mode',
defaultTab
);

const showJsFrameworkInstructions = backendPlatform && setupMode() === 'npm';
const showRadioButtons = jsLoaderPlatform;

const currentPlatform = currentProject.platform
? platforms.find(p => p.id === currentProject.platform) ?? otherPlatform
Expand Down Expand Up @@ -248,7 +242,7 @@ function OnboardingContent({
choices={[
[
'npm',
backendPlatforms ? (
backendPlatform ? (
<PlatformSelect key="platform-select">
{tct('I use [platformSelect]', {
platformSelect: (
Expand Down Expand Up @@ -283,6 +277,7 @@ function OnboardingContent({
onChange={setSetupMode}
/>
) : (
!mobilePlatform &&
docs?.platformOptions &&
!isProjKeysLoading && (
<PlatformSelect>
Expand All @@ -306,22 +301,6 @@ function OnboardingContent({
);
}

// TODO: remove once we have mobile replay onboarding
if (['android', 'react-native'].includes(currentPlatform.language)) {
return (
<MobileBetaBanner
link={`https://docs.sentry.io/platforms/${currentPlatform.language}/session-replay/`}
/>
);
}
if (currentPlatform.language === 'apple') {
return (
<MobileBetaBanner
link={`https://docs.sentry.io/platforms/apple/guides/ios/session-replay/`}
/>
);
}

const doesNotSupportReplay = currentProject.platform
? !replayPlatforms.includes(currentProject.platform)
: true;
Expand Down Expand Up @@ -382,11 +361,13 @@ function OnboardingContent({
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
? 'replayOnboardingNpm'
: 'replayOnboardingJsLoader'
mobilePlatform
? 'replayOnboardingMobile'
: 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
? 'replayOnboardingNpm'
: 'replayOnboardingJsLoader'
}
/>
</Fragment>
Expand Down
1 change: 1 addition & 0 deletions static/app/data/platformCategories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,7 @@ export const replayPlatforms: readonly PlatformKey[] = [
export const replayOnboardingPlatforms: readonly PlatformKey[] = [
...replayFrontendPlatforms.filter(p => !['javascript-backbone'].includes(p)),
...replayBackendPlatforms,
...replayMobilePlatforms,
];

// These are the supported replay platforms that can also be set up using the JS loader.
Expand Down
79 changes: 79 additions & 0 deletions static/app/gettingStartedDocs/android/android.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import type {
DocsParams,
OnboardingConfig,
} from 'sentry/components/onboarding/gettingStartedDoc/types';
import {MobileBetaBanner} from 'sentry/components/onboarding/gettingStartedDoc/utils';
import {getAndroidMetricsOnboarding} from 'sentry/components/onboarding/gettingStartedDoc/utils/metricsOnboarding';
import {getReplayMobileConfigureDescription} from 'sentry/components/onboarding/gettingStartedDoc/utils/replayOnboarding';
import {feedbackOnboardingCrashApiJava} from 'sentry/gettingStartedDocs/java/java';
import {t, tct} from 'sentry/locale';
import {getPackageVersion} from 'sentry/utils/gettingStartedDocs/getPackageVersion';
Expand Down Expand Up @@ -93,6 +95,24 @@ val breakWorld = Button(this).apply {
addContentView(breakWorld, ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT))`;

const getReplaySetupSnippetKotlin = (params: Params) => `
SentryAndroid.init(context) { options ->
options.dsn = "${params.dsn.public}"
options.isDebug = true

// Currently under experimental options:
options.experimental.sessionReplay.errorSampleRate = 1.0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI: i've already merged the PR that changes the name to onErrorSampleRate - do we wanna wait for the new SDK release and change it now, or rather ship this and follow-up later?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm @romtsn let's follow up perhaps, since we need to update the docs anyway - can do both at the same time. feel free to ping me to update when the SDK release is out

options.experimental.sessionReplay.sessionSampleRate = 1.0
}`;

const getReplaySetupSnippetXml = () => `
<meta-data android:name="io.sentry.session-replay.error-sample-rate" android:value="1.0" />
<meta-data android:name="io.sentry.session-replay.session-sample-rate" android:value="1.0" />`;

const getReplayConfigurationSnippet = () => `
options.experimental.sessionReplay.redactAllText = true
options.experimental.sessionReplay.redactAllImages = true`;

const onboarding: OnboardingConfig<PlatformOptions> = {
install: params =>
isAutoInstall(params)
Expand Down Expand Up @@ -283,12 +303,71 @@ const onboarding: OnboardingConfig<PlatformOptions> = {
],
};

const replayOnboarding: OnboardingConfig<PlatformOptions> = {
introduction: () => (
<MobileBetaBanner link="https://docs.sentry.io/platforms/android/session-replay/" />
),
install: (params: Params) => [
{
type: StepType.INSTALL,
description: t(
'Make sure your Sentry Android SDK version is at least 7.12.0. To set up the integration, add the following to your Sentry initialization.'
),
configurations: [
{
code: [
{
label: 'Kotlin',
value: 'kotlin',
language: 'kotlin',
code: getReplaySetupSnippetKotlin(params),
},
{
label: 'Xml',
michellewzhang marked this conversation as resolved.
Show resolved Hide resolved
value: 'xml',
language: 'xml',
filename: 'AndroidManifest.xml',
code: getReplaySetupSnippetXml(),
},
],
},
],
},
],
configure: () => [
{
type: StepType.CONFIGURE,
description: getReplayMobileConfigureDescription({
link: 'https://docs.sentry.io/platforms/android/session-replay/#privacy',
}),
configurations: [
{
description: t(
'The following code is the default configuration, which masks and blocks everything.'
),
code: [
{
label: 'Kotlin',
value: 'kotlin',
language: 'kotlin',
code: getReplayConfigurationSnippet(),
},
],
},
],
},
],
verify: () => [],
nextSteps: () => [],
};

const docs: Docs<PlatformOptions> = {
onboarding,
feedbackOnboardingCrashApi: feedbackOnboardingCrashApiJava,
crashReportOnboarding: feedbackOnboardingCrashApiJava,
customMetricsOnboarding: getAndroidMetricsOnboarding(),
platformOptions,
replayOnboardingMobile: replayOnboarding,
};

export default docs;
Loading
Loading