diff --git a/CHANGELOG.md b/CHANGELOG.md index 326f0831ac..0742d2bb65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,19 +22,11 @@ email: "john@doe.com", message: "Hello World!", associatedEventId: eventId, // optional - }, { - captureContext: { - tags: { "tag-key": "tag-value" }, - }, - attachments: [ - { - filename: 'hello.txt', - data: 'Hello, World!', - }, - ], }); ``` +To learn how to attach context data to the feedback visit [the documentation](https://docs.sentry.io/platforms/react-native/user-feedback/). + - User Feedback Form Component Beta ([#4320](https://github.com/getsentry/sentry-react-native/pull/4328)) To collect user feedback from inside your application add the `FeedbackForm` component. @@ -46,6 +38,7 @@ ``` - Export `Span` type from `@sentry/types` ([#4345](https://github.com/getsentry/sentry-react-native/pull/4345)) +- Add RN SDK package to `sdk.packages` on Android ([#4380](https://github.com/getsentry/sentry-react-native/pull/4380)) ### Fixes diff --git a/packages/core/android/src/main/java/io/sentry/react/RNSentryModuleImpl.java b/packages/core/android/src/main/java/io/sentry/react/RNSentryModuleImpl.java index 6fd2247002..b39261aca7 100644 --- a/packages/core/android/src/main/java/io/sentry/react/RNSentryModuleImpl.java +++ b/packages/core/android/src/main/java/io/sentry/react/RNSentryModuleImpl.java @@ -93,8 +93,6 @@ public class RNSentryModuleImpl { public static final String NAME = "RNSentry"; - private static final String NATIVE_SDK_NAME = "sentry.native.android.react-native"; - private static final String ANDROID_SDK_NAME = "sentry.java.android.react-native"; private static final ILogger logger = new AndroidLogger(NAME); private static final BuildInfoProvider buildInfo = new BuildInfoProvider(logger); private static final String modulesPath = "modules.json"; @@ -191,13 +189,16 @@ protected void getSentryAndroidOptions( @NotNull SentryAndroidOptions options, @NotNull ReadableMap rnOptions, ILogger logger) { @Nullable SdkVersion sdkVersion = options.getSdkVersion(); if (sdkVersion == null) { - sdkVersion = new SdkVersion(ANDROID_SDK_NAME, BuildConfig.VERSION_NAME); + sdkVersion = new SdkVersion(RNSentryVersion.ANDROID_SDK_NAME, BuildConfig.VERSION_NAME); } else { - sdkVersion.setName(ANDROID_SDK_NAME); + sdkVersion.setName(RNSentryVersion.ANDROID_SDK_NAME); } + sdkVersion.addPackage( + RNSentryVersion.REACT_NATIVE_SDK_PACKAGE_NAME, + RNSentryVersion.REACT_NATIVE_SDK_PACKAGE_VERSION); options.setSentryClientName(sdkVersion.getName() + "/" + sdkVersion.getVersion()); - options.setNativeSdkName(NATIVE_SDK_NAME); + options.setNativeSdkName(RNSentryVersion.NATIVE_SDK_NAME); options.setSdkVersion(sdkVersion); if (rnOptions.hasKey("debug") && rnOptions.getBoolean("debug")) { @@ -970,10 +971,10 @@ private void setEventOriginTag(SentryEvent event) { SdkVersion sdk = event.getSdk(); if (sdk != null) { switch (sdk.getName()) { - case NATIVE_SDK_NAME: + case RNSentryVersion.NATIVE_SDK_NAME: setEventEnvironmentTag(event, "native"); break; - case ANDROID_SDK_NAME: + case RNSentryVersion.ANDROID_SDK_NAME: setEventEnvironmentTag(event, "java"); break; default: diff --git a/packages/core/android/src/main/java/io/sentry/react/RNSentryVersion.java b/packages/core/android/src/main/java/io/sentry/react/RNSentryVersion.java new file mode 100644 index 0000000000..4660c838e4 --- /dev/null +++ b/packages/core/android/src/main/java/io/sentry/react/RNSentryVersion.java @@ -0,0 +1,8 @@ +package io.sentry.react; + +class RNSentryVersion { + static final String REACT_NATIVE_SDK_PACKAGE_NAME = "npm:@sentry/react-native"; + static final String REACT_NATIVE_SDK_PACKAGE_VERSION = "6.4.0"; + static final String NATIVE_SDK_NAME = "sentry.native.android.react-native"; + static final String ANDROID_SDK_NAME = "sentry.java.android.react-native"; +} diff --git a/packages/core/src/js/feedback/FeedbackForm.styles.ts b/packages/core/src/js/feedback/FeedbackForm.styles.ts index 0ab35584d8..ff92690f28 100644 --- a/packages/core/src/js/feedback/FeedbackForm.styles.ts +++ b/packages/core/src/js/feedback/FeedbackForm.styles.ts @@ -2,7 +2,7 @@ import type { FeedbackFormStyles } from './FeedbackForm.types'; const PURPLE = 'rgba(88, 74, 192, 1)'; const FORGROUND_COLOR = '#2b2233'; -const BACKROUND_COLOR = '#fff'; +const BACKROUND_COLOR = '#ffffff'; const BORDER_COLOR = 'rgba(41, 35, 47, 0.13)'; const defaultStyles: FeedbackFormStyles = { diff --git a/packages/core/src/js/feedback/FeedbackForm.tsx b/packages/core/src/js/feedback/FeedbackForm.tsx index 25dabb0547..b56f762294 100644 --- a/packages/core/src/js/feedback/FeedbackForm.tsx +++ b/packages/core/src/js/feedback/FeedbackForm.tsx @@ -35,42 +35,43 @@ const submitFeedback = async (feedbackParams: SendFeedbackParams, hint: EventHin * Implements a feedback form screen that sends feedback to Sentry using Sentry.captureFeedback. */ export class FeedbackForm extends React.Component { - private _config: FeedbackFormProps; + public static defaultProps: Partial = { + ...defaultConfiguration + } public constructor(props: FeedbackFormProps) { super(props); const currentUser = { useSentryUser: { - email: getCurrentScope().getUser().email || '', - name: getCurrentScope().getUser().name || '', + email: this.props?.useSentryUser?.email || getCurrentScope()?.getUser()?.email || '', + name: this.props?.useSentryUser?.name || getCurrentScope()?.getUser()?.name || '', } } - this._config = { ...defaultConfiguration, ...currentUser, ...props }; this.state = { isVisible: true, - name: this._config.useSentryUser.name, - email: this._config.useSentryUser.email, + name: currentUser.useSentryUser.name, + email: currentUser.useSentryUser.email, description: '', }; } public handleFeedbackSubmit: () => void = () => { const { name, email, description } = this.state; - const { onSubmitSuccess, onSubmitError, onFormSubmitted } = this._config; - const text: FeedbackTextConfiguration = this._config; + const { onSubmitSuccess, onSubmitError, onFormSubmitted } = this.props; + const text: FeedbackTextConfiguration = this.props; const trimmedName = name?.trim(); const trimmedEmail = email?.trim(); const trimmedDescription = description?.trim(); - if ((this._config.isNameRequired && !trimmedName) || (this._config.isEmailRequired && !trimmedEmail) || !trimmedDescription) { + if ((this.props.isNameRequired && !trimmedName) || (this.props.isEmailRequired && !trimmedEmail) || !trimmedDescription) { Alert.alert(text.errorTitle, text.formError); return; } - if (this._config.shouldValidateEmail && (this._config.isEmailRequired || trimmedEmail.length > 0) && !isValidEmail(trimmedEmail)) { + if (this.props.shouldValidateEmail && (this.props.isEmailRequired || trimmedEmail.length > 0) && !isValidEmail(trimmedEmail)) { Alert.alert(text.errorTitle, text.emailError); return; } @@ -111,9 +112,9 @@ export class FeedbackForm extends React.Component { onFormClose(); diff --git a/packages/core/test/feedback/FeedbackForm.test.tsx b/packages/core/test/feedback/FeedbackForm.test.tsx index 07e9ee54fc..46339376f1 100644 --- a/packages/core/test/feedback/FeedbackForm.test.tsx +++ b/packages/core/test/feedback/FeedbackForm.test.tsx @@ -11,16 +11,17 @@ const mockOnFormClose = jest.fn(); const mockOnSubmitSuccess = jest.fn(); const mockOnFormSubmitted = jest.fn(); const mockOnSubmitError = jest.fn(); +const mockGetUser = jest.fn(() => ({ + email: 'test@example.com', + name: 'Test User', +})); jest.spyOn(Alert, 'alert'); jest.mock('@sentry/core', () => ({ ...jest.requireActual('@sentry/core'), getCurrentScope: jest.fn(() => ({ - getUser: jest.fn(() => ({ - email: 'test@example.com', - name: 'Test User', - })), + getUser: mockGetUser, })), lastEventId: jest.fn(), })); @@ -89,6 +90,17 @@ describe('FeedbackForm', () => { expect(emailInput.props.value).toBe('test@example.com'); }); + it('ensure getUser is called only after the component is rendered', () => { + // Ensure getUser is not called before render + expect(mockGetUser).not.toHaveBeenCalled(); + + // Render the component + render(); + + // After rendering, check that getUser was called twice (email and name) + expect(mockGetUser).toHaveBeenCalledTimes(2); + }); + it('shows an error message if required fields are empty', async () => { const { getByText } = render(); diff --git a/scripts/version-bump.js b/scripts/version-bump.js index 3bb74987a4..ff1a68f6b2 100644 --- a/scripts/version-bump.js +++ b/scripts/version-bump.js @@ -4,7 +4,10 @@ const replace = require('replace-in-file'); const pjson = require('../packages/core/package.json'); replace({ - files: ['packages/core/src/js/version.ts'], + files: [ + 'packages/core/src/js/version.ts', + 'packages/core/android/src/main/java/io/sentry/react/RNSentryVersion.java', + ], from: /\d+\.\d+.\d+(?:-\w+(?:\.\w+)?)?/g, to: pjson.version, })