Skip to content

Commit 0261e04

Browse files
committed
Merge branch 'antonis/3859-newCaptureFeedbackAPI-Form' into antonis/4358-Feedback-Form-Autoinject
# Conflicts: # packages/core/src/js/feedback/FeedbackForm.tsx
2 parents 8b9f4d5 + fd2e317 commit 0261e04

File tree

5 files changed

+117
-81
lines changed

5 files changed

+117
-81
lines changed

CHANGELOG.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,10 @@
3535
});
3636
```
3737

38-
- Adds feedback form (beta) ([#4320](https://github.com/getsentry/sentry-react-native/pull/4328))
38+
- User Feedback From Component Beta ([#4320](https://github.com/getsentry/sentry-react-native/pull/4328))
39+
40+
To collect user feedback from inside your application add the `FeedbackFrom` component.
3941

40-
You can add the form component in your UI like:
4142
```jsx
4243
import { FeedbackForm } from "@sentry/react-native";
4344
...
@@ -57,7 +58,6 @@
5758

5859
- Export `Span` type from `@sentry/types` ([#4345](https://github.com/getsentry/sentry-react-native/pull/4345))
5960

60-
6161
### Fixes
6262

6363
- Return `lastEventId` export from `@sentry/core` ([#4315](https://github.com/getsentry/sentry-react-native/pull/4315))

packages/core/src/js/feedback/FeedbackForm.styles.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,60 @@
11
import type { FeedbackFormStyles } from './FeedbackForm.types';
22

3+
const PURPLE = 'rgba(88, 74, 192, 1)';
4+
const FORGROUND_COLOR = '#2b2233';
5+
const BACKROUND_COLOR = '#fff';
6+
const BORDER_COLOR = 'rgba(41, 35, 47, 0.13)';
7+
38
const defaultStyles: FeedbackFormStyles = {
49
container: {
510
flex: 1,
611
padding: 20,
7-
backgroundColor: '#fff',
12+
backgroundColor: BACKROUND_COLOR,
813
},
914
title: {
1015
fontSize: 24,
1116
fontWeight: 'bold',
1217
marginBottom: 20,
1318
textAlign: 'center',
19+
color: FORGROUND_COLOR,
1420
},
1521
label: {
1622
marginBottom: 4,
1723
fontSize: 16,
24+
color: FORGROUND_COLOR,
1825
},
1926
input: {
2027
height: 50,
21-
borderColor: '#ccc',
28+
borderColor: BORDER_COLOR,
2229
borderWidth: 1,
2330
borderRadius: 5,
2431
paddingHorizontal: 10,
2532
marginBottom: 15,
2633
fontSize: 16,
34+
color: FORGROUND_COLOR,
2735
},
2836
textArea: {
2937
height: 100,
3038
textAlignVertical: 'top',
39+
color: FORGROUND_COLOR,
3140
},
3241
submitButton: {
33-
backgroundColor: '#6a1b9a',
42+
backgroundColor: PURPLE,
3443
paddingVertical: 15,
3544
borderRadius: 5,
3645
alignItems: 'center',
3746
marginBottom: 10,
3847
},
3948
submitText: {
40-
color: '#fff',
49+
color: BACKROUND_COLOR,
4150
fontSize: 18,
42-
fontWeight: 'bold',
4351
},
4452
cancelButton: {
4553
paddingVertical: 15,
4654
alignItems: 'center',
4755
},
4856
cancelText: {
49-
color: '#6a1b9a',
57+
color: FORGROUND_COLOR,
5058
fontSize: 16,
5159
},
5260
};

packages/core/src/js/feedback/FeedbackForm.tsx

Lines changed: 94 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,19 @@
1-
import { captureFeedback, lastEventId, logger } from '@sentry/core';
1+
import { captureFeedback, getCurrentScope, lastEventId, logger } from '@sentry/core';
22
import type { SendFeedbackParams } from '@sentry/types';
33
import * as React from 'react';
44
import type { KeyboardTypeOptions } from 'react-native';
5-
import { Alert, Text, TextInput, TouchableOpacity, View } from 'react-native';
5+
import {
6+
Alert,
7+
Keyboard,
8+
KeyboardAvoidingView,
9+
SafeAreaView,
10+
ScrollView,
11+
Text,
12+
TextInput,
13+
TouchableOpacity,
14+
TouchableWithoutFeedback,
15+
View
16+
} from 'react-native';
617

718
import { defaultConfiguration } from './defaults';
819
import defaultStyles from './FeedbackForm.styles';
@@ -38,14 +49,23 @@ export const showFeedbackForm = (navigation: Navigation): void => {
3849
* Implements a feedback form screen that sends feedback to Sentry using Sentry.captureFeedback.
3950
*/
4051
export class FeedbackForm extends React.Component<FeedbackFormProps, FeedbackFormState> {
52+
private _config: FeedbackFormProps;
53+
4154
public constructor(props: FeedbackFormProps) {
4255
super(props);
4356

44-
const config: FeedbackGeneralConfiguration = { ...defaultConfiguration, ...props };
57+
const currentUser = {
58+
useSentryUser: {
59+
email: getCurrentScope().getUser().email || '',
60+
name: getCurrentScope().getUser().name || '',
61+
}
62+
}
63+
64+
this._config = { ...defaultConfiguration, ...currentUser, ...props };
4565
this.state = {
4666
isVisible: true,
47-
name: config.useSentryUser.name,
48-
email: config.useSentryUser.email,
67+
name: this._config.useSentryUser.name,
68+
email: this._config.useSentryUser.email,
4969
description: '',
5070
};
5171
}
@@ -59,20 +79,19 @@ export class FeedbackForm extends React.Component<FeedbackFormProps, FeedbackFor
5979

6080
public handleFeedbackSubmit: () => void = () => {
6181
const { name, email, description } = this.state;
62-
const { onFormClose } = { ...defaultConfiguration, ...this.props };
63-
const config: FeedbackGeneralConfiguration = { ...defaultConfiguration, ...this.props };
64-
const text: FeedbackTextConfiguration = { ...defaultConfiguration, ...this.props };
82+
const { onFormClose } = this._config;
83+
const text: FeedbackTextConfiguration = this._config;
6584

6685
const trimmedName = name?.trim();
6786
const trimmedEmail = email?.trim();
6887
const trimmedDescription = description?.trim();
6988

70-
if ((config.isNameRequired && !trimmedName) || (config.isEmailRequired && !trimmedEmail) || !trimmedDescription) {
89+
if ((this._config.isNameRequired && !trimmedName) || (this._config.isEmailRequired && !trimmedEmail) || !trimmedDescription) {
7190
Alert.alert(text.errorTitle, text.formError);
7291
return;
7392
}
7493

75-
if ((config.isEmailRequired || trimmedEmail.length > 0) && !this._isValidEmail(trimmedEmail)) {
94+
if (this._config.shouldValidateEmail && (this._config.isEmailRequired || trimmedEmail.length > 0) && !this._isValidEmail(trimmedEmail)) {
7695
Alert.alert(text.errorTitle, text.emailError);
7796
return;
7897
}
@@ -97,9 +116,9 @@ export class FeedbackForm extends React.Component<FeedbackFormProps, FeedbackFor
97116
*/
98117
public render(): React.ReactNode {
99118
const { name, email, description } = this.state;
100-
const { onFormClose } = { ...defaultConfiguration, ...this.props };
101-
const config: FeedbackGeneralConfiguration = { ...defaultConfiguration, ...this.props };
102-
const text: FeedbackTextConfiguration = { ...defaultConfiguration, ...this.props };
119+
const { onFormClose } = this._config;
120+
const config: FeedbackGeneralConfiguration = this._config;
121+
const text: FeedbackTextConfiguration = this._config;
103122
const styles: FeedbackFormStyles = { ...defaultStyles, ...this.props.styles };
104123
const onCancel = (): void => {
105124
onFormClose();
@@ -111,60 +130,68 @@ export class FeedbackForm extends React.Component<FeedbackFormProps, FeedbackFor
111130
}
112131

113132
return (
114-
<View style={styles.container}>
115-
<Text style={styles.title}>{text.formTitle}</Text>
116-
117-
{config.showName && (
118-
<>
119-
<Text style={styles.label}>
120-
{text.nameLabel}
121-
{config.isNameRequired && ` ${text.isRequiredLabel}`}
122-
</Text>
123-
<TextInput
124-
style={styles.input}
125-
placeholder={text.namePlaceholder}
126-
value={name}
127-
onChangeText={(value) => this.setState({ name: value })}
128-
/>
129-
</>
130-
)}
131-
132-
{config.showEmail && (
133-
<>
134-
<Text style={styles.label}>
135-
{text.emailLabel}
136-
{config.isEmailRequired && ` ${text.isRequiredLabel}`}
137-
</Text>
138-
<TextInput
139-
style={styles.input}
140-
placeholder={text.emailPlaceholder}
141-
keyboardType={'email-address' as KeyboardTypeOptions}
142-
value={email}
143-
onChangeText={(value) => this.setState({ email: value })}
144-
/>
145-
</>
146-
)}
147-
148-
<Text style={styles.label}>
149-
{text.messageLabel}
150-
{` ${text.isRequiredLabel}`}
151-
</Text>
152-
<TextInput
153-
style={[styles.input, styles.textArea]}
154-
placeholder={text.messagePlaceholder}
155-
value={description}
156-
onChangeText={(value) => this.setState({ description: value })}
157-
multiline
158-
/>
159-
160-
<TouchableOpacity style={styles.submitButton} onPress={this.handleFeedbackSubmit}>
161-
<Text style={styles.submitText}>{text.submitButtonLabel}</Text>
162-
</TouchableOpacity>
163-
164-
<TouchableOpacity style={styles.cancelButton} onPress={onCancel}>
165-
<Text style={styles.cancelText}>{text.cancelButtonLabel}</Text>
166-
</TouchableOpacity>
167-
</View>
133+
<SafeAreaView style={[styles.container, { padding: 0 }]}>
134+
<KeyboardAvoidingView behavior={'padding'} style={[styles.container, { padding: 0 }]}>
135+
<ScrollView>
136+
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
137+
<View style={styles.container}>
138+
<Text style={styles.title}>{text.formTitle}</Text>
139+
140+
{config.showName && (
141+
<>
142+
<Text style={styles.label}>
143+
{text.nameLabel}
144+
{config.isNameRequired && ` ${text.isRequiredLabel}`}
145+
</Text>
146+
<TextInput
147+
style={styles.input}
148+
placeholder={text.namePlaceholder}
149+
value={name}
150+
onChangeText={(value) => this.setState({ name: value })}
151+
/>
152+
</>
153+
)}
154+
155+
{config.showEmail && (
156+
<>
157+
<Text style={styles.label}>
158+
{text.emailLabel}
159+
{config.isEmailRequired && ` ${text.isRequiredLabel}`}
160+
</Text>
161+
<TextInput
162+
style={styles.input}
163+
placeholder={text.emailPlaceholder}
164+
keyboardType={'email-address' as KeyboardTypeOptions}
165+
value={email}
166+
onChangeText={(value) => this.setState({ email: value })}
167+
/>
168+
</>
169+
)}
170+
171+
<Text style={styles.label}>
172+
{text.messageLabel}
173+
{` ${text.isRequiredLabel}`}
174+
</Text>
175+
<TextInput
176+
style={[styles.input, styles.textArea]}
177+
placeholder={text.messagePlaceholder}
178+
value={description}
179+
onChangeText={(value) => this.setState({ description: value })}
180+
multiline
181+
/>
182+
183+
<TouchableOpacity style={styles.submitButton} onPress={this.handleFeedbackSubmit}>
184+
<Text style={styles.submitText}>{text.submitButtonLabel}</Text>
185+
</TouchableOpacity>
186+
187+
<TouchableOpacity style={styles.cancelButton} onPress={onCancel}>
188+
<Text style={styles.cancelText}>{text.cancelButtonLabel}</Text>
189+
</TouchableOpacity>
190+
</View>
191+
</TouchableWithoutFeedback>
192+
</ScrollView>
193+
</KeyboardAvoidingView>
194+
</SafeAreaView>
168195
);
169196
}
170197

packages/core/src/js/feedback/FeedbackForm.types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ export interface FeedbackGeneralConfiguration {
1313
*/
1414
isEmailRequired?: boolean;
1515

16+
/**
17+
* Should the email field be validated?
18+
*/
19+
shouldValidateEmail?: boolean;
20+
1621
/**
1722
* Should the name field be required?
1823
*/

packages/core/src/js/feedback/defaults.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { getCurrentScope } from '@sentry/core';
21
import { Alert } from 'react-native';
32

43
import type { FeedbackFormProps } from './FeedbackForm.types';
@@ -31,13 +30,10 @@ export const defaultConfiguration: Partial<FeedbackFormProps> = {
3130

3231
// FeedbackGeneralConfiguration
3332
isEmailRequired: false,
33+
shouldValidateEmail: true,
3434
isNameRequired: false,
3535
showEmail: true,
3636
showName: true,
37-
useSentryUser: {
38-
email: getCurrentScope().getUser().email || '',
39-
name: getCurrentScope().getUser().name || '',
40-
},
4137

4238
// FeedbackTextConfiguration
4339
cancelButtonLabel: CANCEL_BUTTON_LABEL,

0 commit comments

Comments
 (0)