UNPKG

@sentry/react-native

Version:
249 lines 14.1 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import { captureFeedback, getCurrentScope, lastEventId, logger } from '@sentry/core'; import * as React from 'react'; import { Image, Keyboard, Text, TextInput, TouchableOpacity, TouchableWithoutFeedback, View } from 'react-native'; import { isWeb, notWeb } from '../utils/environment'; import { NATIVE } from '../wrapper'; import { sentryLogo } from './branding'; import { defaultConfiguration } from './defaults'; import defaultStyles from './FeedbackWidget.styles'; import { lazyLoadFeedbackIntegration } from './lazy'; import { base64ToUint8Array, feedbackAlertDialog, isValidEmail } from './utils'; /** * @beta * Implements a feedback form screen that sends feedback to Sentry using Sentry.captureFeedback. */ export class FeedbackWidget extends React.Component { constructor(props) { var _a, _b, _c, _d, _e, _f, _g, _h; super(props); this._didSubmitForm = false; this.handleFeedbackSubmit = () => { const { name, email, description } = this.state; const { onSubmitSuccess, onSubmitError, onFormSubmitted } = this.props; const text = this.props; const trimmedName = name === null || name === void 0 ? void 0 : name.trim(); const trimmedEmail = email === null || email === void 0 ? void 0 : email.trim(); const trimmedDescription = description === null || description === void 0 ? void 0 : description.trim(); if ((this.props.isNameRequired && !trimmedName) || (this.props.isEmailRequired && !trimmedEmail) || !trimmedDescription) { feedbackAlertDialog(text.errorTitle, text.formError); return; } if (this.props.shouldValidateEmail && (this.props.isEmailRequired || trimmedEmail.length > 0) && !isValidEmail(trimmedEmail)) { feedbackAlertDialog(text.errorTitle, text.emailError); return; } const attachments = this.state.filename && this.state.attachment ? [ { filename: this.state.filename, data: this.state.attachment, }, ] : undefined; const eventId = lastEventId(); const userFeedback = { message: trimmedDescription, name: trimmedName, email: trimmedEmail, associatedEventId: eventId, }; try { if (!onFormSubmitted) { this.setState({ isVisible: false }); } captureFeedback(userFeedback, attachments ? { attachments } : undefined); onSubmitSuccess({ name: trimmedName, email: trimmedEmail, message: trimmedDescription, attachments: attachments }); feedbackAlertDialog(text.successMessageText, ''); onFormSubmitted(); this._didSubmitForm = true; } catch (error) { const errorString = `Feedback form submission failed: ${error}`; onSubmitError(new Error(errorString)); feedbackAlertDialog(text.errorTitle, text.genericError); logger.error(`Feedback form submission failed: ${error}`); } }; this.onScreenshotButtonPress = () => __awaiter(this, void 0, void 0, function* () { if (!this.state.filename && !this.state.attachment) { const imagePickerConfiguration = this.props; if (imagePickerConfiguration.imagePicker) { const launchImageLibrary = imagePickerConfiguration.imagePicker.launchImageLibraryAsync // expo-image-picker library is available ? () => imagePickerConfiguration.imagePicker.launchImageLibraryAsync({ mediaTypes: ['images'], base64: isWeb() }) // react-native-image-picker library is available : imagePickerConfiguration.imagePicker.launchImageLibrary ? () => imagePickerConfiguration.imagePicker.launchImageLibrary({ mediaType: 'photo', includeBase64: isWeb() }) : null; if (!launchImageLibrary) { logger.warn('No compatible image picker library found. Please provide a valid image picker library.'); if (__DEV__) { feedbackAlertDialog('Development note', 'No compatible image picker library found. Please provide a compatible version of `expo-image-picker` or `react-native-image-picker`.'); } return; } const result = yield launchImageLibrary(); if (result.assets && result.assets.length > 0) { if (isWeb()) { const filename = result.assets[0].fileName; const imageUri = result.assets[0].uri; const base64 = result.assets[0].base64; const data = base64ToUint8Array(base64); if (data != null) { this.setState({ filename, attachment: data, attachmentUri: imageUri }); } else { logger.error('Failed to read image data on the web'); } } else { const filename = result.assets[0].fileName; const imageUri = result.assets[0].uri; NATIVE.getDataFromUri(imageUri).then((data) => { if (data != null) { this.setState({ filename, attachment: data, attachmentUri: imageUri }); } else { logger.error('Failed to read image data from uri:', imageUri); } }).catch((error) => { logger.error('Failed to read image data from uri:', imageUri, 'error: ', error); }); } } } else { // Defaulting to the onAddScreenshot callback const { onAddScreenshot } = Object.assign(Object.assign({}, defaultConfiguration), this.props); onAddScreenshot((uri) => { NATIVE.getDataFromUri(uri).then((data) => { if (data != null) { this.setState({ filename: 'feedback_screenshot', attachment: data, attachmentUri: uri }); } else { logger.error('Failed to read image data from uri:', uri); } }) .catch((error) => { logger.error('Failed to read image data from uri:', uri, 'error: ', error); }); }); } } else { this.setState({ filename: undefined, attachment: undefined, attachmentUri: undefined }); } }); this._saveFormState = () => { FeedbackWidget._savedState = Object.assign({}, this.state); }; this._clearFormState = () => { FeedbackWidget._savedState = { name: '', email: '', description: '', filename: undefined, attachment: undefined, attachmentUri: undefined, }; }; const currentUser = { useSentryUser: { email: ((_b = (_a = this.props) === null || _a === void 0 ? void 0 : _a.useSentryUser) === null || _b === void 0 ? void 0 : _b.email) || ((_d = (_c = getCurrentScope()) === null || _c === void 0 ? void 0 : _c.getUser()) === null || _d === void 0 ? void 0 : _d.email) || '', name: ((_f = (_e = this.props) === null || _e === void 0 ? void 0 : _e.useSentryUser) === null || _f === void 0 ? void 0 : _f.name) || ((_h = (_g = getCurrentScope()) === null || _g === void 0 ? void 0 : _g.getUser()) === null || _h === void 0 ? void 0 : _h.name) || '', } }; this.state = { isVisible: true, name: FeedbackWidget._savedState.name || currentUser.useSentryUser.name, email: FeedbackWidget._savedState.email || currentUser.useSentryUser.email, description: FeedbackWidget._savedState.description || '', filename: FeedbackWidget._savedState.filename || undefined, attachment: FeedbackWidget._savedState.attachment || undefined, attachmentUri: FeedbackWidget._savedState.attachmentUri || undefined, }; lazyLoadFeedbackIntegration(); } /** * Save the state before unmounting the component. */ componentWillUnmount() { if (this._didSubmitForm) { this._clearFormState(); this._didSubmitForm = false; } else { this._saveFormState(); } } /** * Renders the feedback form screen. */ render() { const { name, email, description } = this.state; const { onFormClose } = this.props; const config = this.props; const imagePickerConfiguration = this.props; const text = this.props; const styles = Object.assign(Object.assign({}, defaultStyles), this.props.styles); const onCancel = () => { if (onFormClose) { onFormClose(); } else { this.setState({ isVisible: false }); } }; if (!this.state.isVisible) { return null; } return (React.createElement(TouchableWithoutFeedback, { onPress: notWeb() ? Keyboard.dismiss : undefined }, React.createElement(View, { style: styles.container }, React.createElement(View, { style: styles.titleContainer }, React.createElement(Text, { style: styles.title }, text.formTitle), config.showBranding && (React.createElement(Image, { source: { uri: sentryLogo }, style: styles.sentryLogo, testID: 'sentry-logo' }))), config.showName && (React.createElement(React.Fragment, null, React.createElement(Text, { style: styles.label }, text.nameLabel, config.isNameRequired && ` ${text.isRequiredLabel}`), React.createElement(TextInput, { style: styles.input, placeholder: text.namePlaceholder, value: name, onChangeText: (value) => this.setState({ name: value }) }))), config.showEmail && (React.createElement(React.Fragment, null, React.createElement(Text, { style: styles.label }, text.emailLabel, config.isEmailRequired && ` ${text.isRequiredLabel}`), React.createElement(TextInput, { style: styles.input, placeholder: text.emailPlaceholder, keyboardType: 'email-address', value: email, onChangeText: (value) => this.setState({ email: value }) }))), React.createElement(Text, { style: styles.label }, text.messageLabel, ` ${text.isRequiredLabel}`), React.createElement(TextInput, { style: [styles.input, styles.textArea], placeholder: text.messagePlaceholder, value: description, onChangeText: (value) => this.setState({ description: value }), multiline: true }), (config.enableScreenshot || imagePickerConfiguration.imagePicker) && (React.createElement(View, { style: styles.screenshotContainer }, this.state.attachmentUri && (React.createElement(Image, { source: { uri: this.state.attachmentUri }, style: styles.screenshotThumbnail })), React.createElement(TouchableOpacity, { style: styles.screenshotButton, onPress: this.onScreenshotButtonPress }, React.createElement(Text, { style: styles.screenshotText }, !this.state.filename && !this.state.attachment ? text.addScreenshotButtonLabel : text.removeScreenshotButtonLabel)))), React.createElement(TouchableOpacity, { style: styles.submitButton, onPress: this.handleFeedbackSubmit }, React.createElement(Text, { style: styles.submitText }, text.submitButtonLabel)), React.createElement(TouchableOpacity, { style: styles.cancelButton, onPress: onCancel }, React.createElement(Text, { style: styles.cancelText }, text.cancelButtonLabel))))); } } FeedbackWidget.defaultProps = Object.assign({}, defaultConfiguration); FeedbackWidget._savedState = { name: '', email: '', description: '', filename: undefined, attachment: undefined, attachmentUri: undefined, }; //# sourceMappingURL=FeedbackWidget.js.map