Skip to content

Commit a11c735

Browse files
feat(replay): Add Mask and Unmask Wrappers for react-native-web (#4272)
1 parent 127cae6 commit a11c735

File tree

7 files changed

+55
-10
lines changed

7 files changed

+55
-10
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
### Features
1212

13-
- Add Replay Custom Masking for iOS and Android ([#4224](https://github.com/getsentry/sentry-react-native/pull/4224), [#4265](https://github.com/getsentry/sentry-react-native/pull/4265))
13+
- Add Replay Custom Masking for iOS, Android and Web ([#4224](https://github.com/getsentry/sentry-react-native/pull/4224), [#4265](https://github.com/getsentry/sentry-react-native/pull/4265), [#4272](https://github.com/getsentry/sentry-react-native/pull/4272))
1414

1515
```jsx
1616
import * as Sentry from '@sentry/react-native';

packages/core/src/js/integrations/exports.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export { viewHierarchyIntegration } from './viewhierarchy';
1313
export { expoContextIntegration } from './expocontext';
1414
export { spotlightIntegration } from './spotlight';
1515
export { mobileReplayIntegration } from '../replay/mobilereplay';
16+
export { browserReplayIntegration } from '../replay/browserReplay';
1617
export { appStartIntegration } from '../tracing/integrations/appStart';
1718
export { nativeFramesIntegration, createNativeFramesIntegrations } from '../tracing/integrations/nativeFrames';
1819
export { stallTrackingIntegration } from '../tracing/integrations/stalltracking';
@@ -30,5 +31,4 @@ export {
3031
inboundFiltersIntegration,
3132
linkedErrorsIntegration as browserLinkedErrorsIntegration,
3233
rewriteFramesIntegration,
33-
replayIntegration as browserReplayIntegration,
3434
} from '@sentry/react';

packages/core/src/js/replay/CustomMask.web.tsx

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,35 @@ import * as React from 'react';
22
import type { ViewProps } from 'react-native';
33
import { View } from 'react-native';
44

5+
// Wrapping children in a View and div can cause styling issues
6+
// but with the current implementation of react-native-web
7+
// we can't avoid it.
8+
//
9+
// <View unknown-prop /> the prop is dropped by react-native-web
10+
// https://github.com/necolas/react-native-web/blob/a5ba27c6226aa182979a9cff8cc23c0f5caa4d88/packages/react-native-web/src/exports/View/index.js#L47
11+
//
12+
// So we need to wrap the children in a react-dom div.
13+
// We are using className instead of data-attribute to
14+
// allow for easier CSS styling adjustments.
15+
516
const Mask = (props: ViewProps): React.ReactElement => {
6-
// We have to ensure that the warning is visible even if the app is running without debug
7-
// eslint-disable-next-line no-console
8-
console.warn('[SentrySessionReplay] Mask component is not supported on web.');
9-
return <View {...props} />;
17+
return (
18+
<View {...props}>
19+
<div className='sentry-react-native-mask'>
20+
{props.children}
21+
</div>
22+
</View>
23+
);
1024
};
25+
1126
const Unmask = (props: ViewProps): React.ReactElement => {
12-
// We have to ensure that the warning is visible even if the app is running without debug
13-
// eslint-disable-next-line no-console
14-
console.warn('[SentrySessionReplay] Unmask component is not supported on web.');
15-
return <View {...props} />;
27+
return (
28+
<View {...props}>
29+
<div className='sentry-react-native-unmask'>
30+
{props.children}
31+
</div>
32+
</View>
33+
);
1634
};
1735

1836
export { Mask, Unmask };
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { replayIntegration } from '@sentry/react';
2+
3+
const browserReplayIntegration = (
4+
options: Parameters<typeof replayIntegration>[0] = {},
5+
): ReturnType<typeof replayIntegration> => {
6+
return replayIntegration({
7+
...options,
8+
mask: ['.sentry-react-native-mask', ...(options.mask || [])],
9+
unmask: ['.sentry-react-native-unmask:not(.sentry-react-native-mask *) > *', ...(options.unmask || [])],
10+
});
11+
};
12+
13+
export { browserReplayIntegration };

packages/core/tsconfig.build.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
"extends": "./node_modules/@sentry-internal/typescript/tsconfig.json",
33
"include": [
44
"./src/js/*.ts",
5+
"./src/js/*.tsx",
56
"./src/js/**/*.web.ts",
7+
"./src/js/**/*.web.tsx",
68
"./typings/react-native.d.ts"
79
],
810
"exclude": ["node_modules"],

samples/expo/app/(tabs)/two.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { StyleSheet } from 'react-native';
2+
import * as Sentry from '@sentry/react-native';
23

34
import EditScreenInfo from '@/components/EditScreenInfo';
45
import { Text, View } from '@/components/Themed';
@@ -8,6 +9,16 @@ export default function TabTwoScreen() {
89
<View style={styles.container}>
910
<Text style={styles.title}>Tab Two</Text>
1011
<View style={styles.separator} lightColor="#eee" darkColor="rgba(255,255,255,0.1)" />
12+
<Sentry.Unmask>
13+
<Text>This is unmasked because it's direct child of Sentry.Unmask (can be masked if Sentry.Masked is used higher in the hierarchy)</Text>
14+
<Sentry.Mask>
15+
<Text>This is masked always because it's a child of a Sentry.Mask</Text>
16+
<Sentry.Unmask>
17+
{/* Sentry.Unmask does not override the Sentry.Mask from above in the hierarchy */}
18+
<Text>This is masked always because it's a child of Sentry.Mask</Text>
19+
</Sentry.Unmask>
20+
</Sentry.Mask>
21+
</Sentry.Unmask>
1122
<EditScreenInfo path="app/(tabs)/two.tsx" />
1223
</View>
1324
);

samples/expo/app/_layout.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ process.env.EXPO_SKIP_DURING_EXPORT !== 'true' && Sentry.init({
5656
}),
5757
navigationIntegration,
5858
Sentry.reactNativeTracingIntegration(),
59+
Sentry.browserReplayIntegration(),
5960
);
6061
return integrations.filter(i => i.name !== 'Dedupe');
6162
},

0 commit comments

Comments
 (0)