-
-
Notifications
You must be signed in to change notification settings - Fork 337
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
(1) feat: Add Feedback From Component #4328
Open
antonis
wants to merge
71
commits into
main
Choose a base branch
from
antonis/3859-newCaptureFeedbackAPI-Form
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+622
−0
Open
Changes from all commits
Commits
Show all changes
71 commits
Select commit
Hold shift + click to select a range
817eac8
Update the client implementation to use the new capture feedback js api
antonis 5370a99
Updates SDK API
antonis 42e2fa1
Adds new feedback button in the sample
antonis 514b102
Adds changelog
antonis 0935bbd
Removes unused mock
antonis 9ea5496
Update CHANGELOG.md
antonis da0d4ac
Directly use captureFeedback from sentry/core
antonis 3e36c6d
Use import from core
antonis 5f3df64
Fixes imports order lint issue
antonis 71b28e8
Fixes build issue
antonis f9d2b59
Adds captureFeedback tests from sentry-javascript
antonis d05d531
Update CHANGELOG.md
krystofwoldrich 2bb104b
Only deprecate client captureUserFeedback
antonis 4339274
Add simple form UI
antonis 6ce799b
Adds basic form functionality
antonis f2cefc6
Update imports
antonis 694ee33
Update imports
antonis 18b1c33
Merge branch 'main' into antonis/3859-newCaptureFeedbackAPI-Form
antonis 034efde
Remove useState hook to avoid multiple react instances issues
antonis 67a492d
Move types and styles in different files
antonis 8eaa61d
Removes attachment button to be added back separately along with the …
antonis 0b88cc5
Add basic field validation
antonis ae11b8d
Adds changelog
antonis 7f2ca06
Updates changelog
antonis 064b6c4
Updates changelog
antonis e2add4a
Merge branch 'main' into antonis/3859-newCaptureFeedbackAPI-Form
antonis e21718a
Trim whitespaces from the submitted feedback
antonis 407f179
Adds tests
antonis 14ac005
Merge branch 'main' into antonis/3859-newCaptureFeedbackAPI-Form
antonis ddade00
Renames FeedbackFormScreen to FeedbackForm
antonis 1bc1e4c
Add beta label
antonis 7934756
Extract default text to constants
antonis 95e1e0f
Merge branch 'main' into antonis/3859-newCaptureFeedbackAPI-Form
antonis a169362
Moves constant to a separate file and aligns naming with JS
antonis 4b5df7a
Adds input text labels
antonis 4fa81ce
Close screen before sending the feedback to minimise wait time
antonis 4fff82f
Rename file for consistency
antonis 0258bf2
Flatten configuration hierarchy and clean up
antonis 458ebc2
Align required values with JS
antonis f0e1bef
Use Sentry user email and name when set
antonis b9235f2
Simplifies email validation
antonis 6717a84
Merge branch 'main' into antonis/3859-newCaptureFeedbackAPI-Form
antonis 39a67bd
Show success alert message
antonis 501a134
Aligns naming with JS and unmounts the form by default
antonis 4b290a2
Use the minimum config without props in the changelog
antonis 7109deb
Adds development not for unimplemented function
antonis c80c5cb
Show email and name conditionally
antonis 8c56753
Adds sentry branding (png logo)
antonis d6e9229
Adds sentry logo resource
antonis 5292475
Add assets in module exports
antonis efd809f
Revert "Add assets in module exports"
antonis bc7ae65
Revert "Adds sentry logo resource"
antonis 79ee5ba
Revert "Adds sentry branding (png logo)"
antonis ba13320
Add last event id
antonis 1f5fb56
Mock lastEventId
antonis 439367a
Merge branch 'main' into antonis/3859-newCaptureFeedbackAPI-Form
antonis 9831482
Adds beta note in the changelog
antonis 4097347
Updates changelog
antonis f8a82fd
Merge branch 'main' into antonis/3859-newCaptureFeedbackAPI-Form
antonis 30a7b10
Align colors with JS
antonis 3eccf25
Update CHANGELOG.md
antonis bc96fce
Update CHANGELOG.md
antonis 995a9ca
Update CHANGELOG.md
antonis 3aacaf7
Use regular fonts for both buttons
antonis 20e3a6c
Merge branch 'antonis/3859-newCaptureFeedbackAPI-Form' of https://git…
antonis 78e412c
Handle keyboard properly
antonis c45a5e6
Adds an option on whether the email should be validated
antonis 6e39119
Merge properties only once
antonis 57d99e9
Loads current user data on form construction
antonis 6fb8ab4
Remove unneeded extra padding
antonis fd2e317
Fix background color issue
antonis File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import type { FeedbackFormStyles } from './FeedbackForm.types'; | ||
|
||
const PURPLE = 'rgba(88, 74, 192, 1)'; | ||
const FORGROUND_COLOR = '#2b2233'; | ||
const BACKROUND_COLOR = '#fff'; | ||
const BORDER_COLOR = 'rgba(41, 35, 47, 0.13)'; | ||
|
||
const defaultStyles: FeedbackFormStyles = { | ||
container: { | ||
flex: 1, | ||
padding: 20, | ||
backgroundColor: BACKROUND_COLOR, | ||
}, | ||
title: { | ||
fontSize: 24, | ||
fontWeight: 'bold', | ||
marginBottom: 20, | ||
textAlign: 'center', | ||
color: FORGROUND_COLOR, | ||
}, | ||
label: { | ||
marginBottom: 4, | ||
fontSize: 16, | ||
color: FORGROUND_COLOR, | ||
}, | ||
input: { | ||
height: 50, | ||
borderColor: BORDER_COLOR, | ||
borderWidth: 1, | ||
borderRadius: 5, | ||
paddingHorizontal: 10, | ||
marginBottom: 15, | ||
fontSize: 16, | ||
color: FORGROUND_COLOR, | ||
}, | ||
textArea: { | ||
height: 100, | ||
textAlignVertical: 'top', | ||
color: FORGROUND_COLOR, | ||
}, | ||
submitButton: { | ||
backgroundColor: PURPLE, | ||
paddingVertical: 15, | ||
borderRadius: 5, | ||
alignItems: 'center', | ||
marginBottom: 10, | ||
}, | ||
submitText: { | ||
color: BACKROUND_COLOR, | ||
fontSize: 18, | ||
}, | ||
cancelButton: { | ||
paddingVertical: 15, | ||
alignItems: 'center', | ||
}, | ||
cancelText: { | ||
color: FORGROUND_COLOR, | ||
fontSize: 16, | ||
}, | ||
}; | ||
|
||
export default defaultStyles; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
import { captureFeedback, getCurrentScope, lastEventId } from '@sentry/core'; | ||
import type { SendFeedbackParams } from '@sentry/types'; | ||
import * as React from 'react'; | ||
import type { KeyboardTypeOptions } from 'react-native'; | ||
import { | ||
Alert, | ||
Keyboard, | ||
KeyboardAvoidingView, | ||
SafeAreaView, | ||
ScrollView, | ||
Text, | ||
TextInput, | ||
TouchableOpacity, | ||
TouchableWithoutFeedback, | ||
View | ||
} from 'react-native'; | ||
|
||
import { defaultConfiguration } from './defaults'; | ||
import defaultStyles from './FeedbackForm.styles'; | ||
import type { FeedbackFormProps, FeedbackFormState, FeedbackFormStyles,FeedbackGeneralConfiguration, FeedbackTextConfiguration } from './FeedbackForm.types'; | ||
|
||
/** | ||
* @beta | ||
* Implements a feedback form screen that sends feedback to Sentry using Sentry.captureFeedback. | ||
*/ | ||
export class FeedbackForm extends React.Component<FeedbackFormProps, FeedbackFormState> { | ||
private _config: FeedbackFormProps; | ||
|
||
public constructor(props: FeedbackFormProps) { | ||
super(props); | ||
|
||
const currentUser = { | ||
useSentryUser: { | ||
email: getCurrentScope().getUser().email || '', | ||
name: getCurrentScope().getUser().name || '', | ||
} | ||
} | ||
|
||
this._config = { ...defaultConfiguration, ...currentUser, ...props }; | ||
this.state = { | ||
isVisible: true, | ||
name: this._config.useSentryUser.name, | ||
email: this._config.useSentryUser.email, | ||
description: '', | ||
}; | ||
} | ||
|
||
public handleFeedbackSubmit: () => void = () => { | ||
const { name, email, description } = this.state; | ||
const { onFormClose } = this._config; | ||
const text: FeedbackTextConfiguration = this._config; | ||
|
||
const trimmedName = name?.trim(); | ||
const trimmedEmail = email?.trim(); | ||
const trimmedDescription = description?.trim(); | ||
|
||
if ((this._config.isNameRequired && !trimmedName) || (this._config.isEmailRequired && !trimmedEmail) || !trimmedDescription) { | ||
Alert.alert(text.errorTitle, text.formError); | ||
return; | ||
} | ||
|
||
if (this._config.shouldValidateEmail && (this._config.isEmailRequired || trimmedEmail.length > 0) && !this._isValidEmail(trimmedEmail)) { | ||
Alert.alert(text.errorTitle, text.emailError); | ||
return; | ||
} | ||
|
||
const eventId = lastEventId(); | ||
const userFeedback: SendFeedbackParams = { | ||
message: trimmedDescription, | ||
name: trimmedName, | ||
email: trimmedEmail, | ||
associatedEventId: eventId, | ||
}; | ||
|
||
onFormClose(); | ||
this.setState({ isVisible: false }); | ||
|
||
captureFeedback(userFeedback); | ||
Alert.alert(text.successMessageText); | ||
}; | ||
|
||
/** | ||
* Renders the feedback form screen. | ||
*/ | ||
public render(): React.ReactNode { | ||
const { name, email, description } = this.state; | ||
const { onFormClose } = this._config; | ||
const config: FeedbackGeneralConfiguration = this._config; | ||
const text: FeedbackTextConfiguration = this._config; | ||
const styles: FeedbackFormStyles = { ...defaultStyles, ...this.props.styles }; | ||
const onCancel = (): void => { | ||
onFormClose(); | ||
this.setState({ isVisible: false }); | ||
} | ||
|
||
if (!this.state.isVisible) { | ||
return null; | ||
} | ||
|
||
return ( | ||
<SafeAreaView style={[styles.container, { padding: 0 }]}> | ||
<KeyboardAvoidingView behavior={'padding'} style={[styles.container, { padding: 0 }]}> | ||
<ScrollView> | ||
<TouchableWithoutFeedback onPress={Keyboard.dismiss}> | ||
<View style={styles.container}> | ||
<Text style={styles.title}>{text.formTitle}</Text> | ||
|
||
{config.showName && ( | ||
<> | ||
<Text style={styles.label}> | ||
{text.nameLabel} | ||
{config.isNameRequired && ` ${text.isRequiredLabel}`} | ||
</Text> | ||
<TextInput | ||
style={styles.input} | ||
placeholder={text.namePlaceholder} | ||
value={name} | ||
onChangeText={(value) => this.setState({ name: value })} | ||
/> | ||
</> | ||
)} | ||
|
||
{config.showEmail && ( | ||
<> | ||
<Text style={styles.label}> | ||
{text.emailLabel} | ||
{config.isEmailRequired && ` ${text.isRequiredLabel}`} | ||
</Text> | ||
<TextInput | ||
style={styles.input} | ||
placeholder={text.emailPlaceholder} | ||
keyboardType={'email-address' as KeyboardTypeOptions} | ||
value={email} | ||
onChangeText={(value) => this.setState({ email: value })} | ||
/> | ||
</> | ||
)} | ||
|
||
<Text style={styles.label}> | ||
{text.messageLabel} | ||
{` ${text.isRequiredLabel}`} | ||
</Text> | ||
<TextInput | ||
style={[styles.input, styles.textArea]} | ||
placeholder={text.messagePlaceholder} | ||
value={description} | ||
onChangeText={(value) => this.setState({ description: value })} | ||
multiline | ||
/> | ||
|
||
<TouchableOpacity style={styles.submitButton} onPress={this.handleFeedbackSubmit}> | ||
<Text style={styles.submitText}>{text.submitButtonLabel}</Text> | ||
</TouchableOpacity> | ||
|
||
<TouchableOpacity style={styles.cancelButton} onPress={onCancel}> | ||
<Text style={styles.cancelText}>{text.cancelButtonLabel}</Text> | ||
</TouchableOpacity> | ||
</View> | ||
</TouchableWithoutFeedback> | ||
</ScrollView> | ||
</KeyboardAvoidingView> | ||
</SafeAreaView> | ||
); | ||
} | ||
|
||
private _isValidEmail = (email: string): boolean => { | ||
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/ | ||
return emailRegex.test(email); | ||
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Show resolved
Hide resolved
|
||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
import type { TextStyle, ViewStyle } from 'react-native'; | ||
|
||
export interface FeedbackFormProps extends FeedbackGeneralConfiguration, FeedbackTextConfiguration, FeedbackCallbacks { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the configuration parameters used are copied from the JS APi https://github.com/getsentry/sentry-javascript/blob/develop/packages/core/src/types-hoist/feedback/config.ts |
||
styles?: FeedbackFormStyles; | ||
} | ||
|
||
/** | ||
* General feedback configuration | ||
*/ | ||
export interface FeedbackGeneralConfiguration { | ||
/** | ||
* Should the email field be required? | ||
*/ | ||
isEmailRequired?: boolean; | ||
|
||
/** | ||
* Should the email field be validated? | ||
*/ | ||
shouldValidateEmail?: boolean; | ||
|
||
/** | ||
* Should the name field be required? | ||
*/ | ||
isNameRequired?: boolean; | ||
|
||
/** | ||
* Should the email input field be visible? Note: email will still be collected if set via `Sentry.setUser()` | ||
*/ | ||
showEmail?: boolean; | ||
|
||
/** | ||
* Should the name input field be visible? Note: name will still be collected if set via `Sentry.setUser()` | ||
*/ | ||
showName?: boolean; | ||
|
||
/** | ||
* Fill in email/name input fields with Sentry user context if it exists. | ||
* The value of the email/name keys represent the properties of your user context. | ||
*/ | ||
useSentryUser?: { | ||
email: string; | ||
name: string; | ||
}; | ||
} | ||
|
||
/** | ||
* All of the different text labels that can be customized | ||
*/ | ||
export interface FeedbackTextConfiguration { | ||
/** | ||
* The label for the Feedback form cancel button that closes dialog | ||
*/ | ||
cancelButtonLabel?: string; | ||
|
||
/** | ||
* The label for the Feedback form submit button that sends feedback | ||
*/ | ||
submitButtonLabel?: string; | ||
|
||
/** | ||
* The title of the Feedback form | ||
*/ | ||
formTitle?: string; | ||
|
||
/** | ||
* Label for the email input | ||
*/ | ||
emailLabel?: string; | ||
|
||
/** | ||
* Placeholder text for Feedback email input | ||
*/ | ||
emailPlaceholder?: string; | ||
|
||
/** | ||
* Label for the message input | ||
*/ | ||
messageLabel?: string; | ||
|
||
/** | ||
* Placeholder text for Feedback message input | ||
*/ | ||
messagePlaceholder?: string; | ||
|
||
/** | ||
* Label for the name input | ||
*/ | ||
nameLabel?: string; | ||
|
||
/** | ||
* Message after feedback was sent successfully | ||
*/ | ||
successMessageText?: string; | ||
|
||
/** | ||
* Placeholder text for Feedback name input | ||
*/ | ||
namePlaceholder?: string; | ||
|
||
/** | ||
* Text which indicates that a field is required | ||
*/ | ||
isRequiredLabel?: string; | ||
|
||
/** | ||
* The title of the error dialog | ||
*/ | ||
errorTitle?: string; | ||
|
||
/** | ||
* The error message when the form is invalid | ||
*/ | ||
formError?: string; | ||
|
||
/** | ||
* The error message when the email is invalid | ||
*/ | ||
emailError?: string; | ||
} | ||
|
||
/** | ||
* The public callbacks available for the feedback integration | ||
*/ | ||
export interface FeedbackCallbacks { | ||
/** | ||
* Callback when form is closed and not submitted | ||
*/ | ||
onFormClose?: () => void; | ||
krystofwoldrich marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
export interface FeedbackFormStyles { | ||
container?: ViewStyle; | ||
title?: TextStyle; | ||
label?: TextStyle; | ||
input?: TextStyle; | ||
textArea?: TextStyle; | ||
submitButton?: ViewStyle; | ||
submitText?: TextStyle; | ||
cancelButton?: ViewStyle; | ||
cancelText?: TextStyle; | ||
} | ||
|
||
export interface FeedbackFormState { | ||
isVisible: boolean; | ||
name: string; | ||
email: string; | ||
description: string; | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's match the default with JS.
Mainly I've noticed submitButton backgroundColor (JS has
rgba(88, 74, 192, 1)
) and test color#2b2233
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tried to align with the light theme of JS with 30a7b10
Screenshot