Skip to content
33 changes: 29 additions & 4 deletions packages/core/src/js/feedback/FeedbackForm.styles.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { ViewStyle } from 'react-native';

import type { FeedbackFormStyles } from './FeedbackForm.types';

const PURPLE = 'rgba(88, 74, 192, 1)';
Expand Down Expand Up @@ -77,10 +79,33 @@ const defaultStyles: FeedbackFormStyles = {
width: 40,
height: 40,
},
modalBackground: {
flex: 1,
justifyContent: 'center',
},
};

export const modalWrapper: ViewStyle = {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
};

export const modalBackground: ViewStyle = {
flex: 1,
justifyContent: 'flex-end',
};

export const modalSheetContainer: ViewStyle = {
backgroundColor: '#ffffff',
borderTopLeftRadius: 16,
borderTopRightRadius: 16,
overflow: 'hidden',
alignSelf: 'stretch',
height: '92%',
shadowColor: '#000',
shadowOffset: { width: 0, height: -3 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 5,
};

export default defaultStyles;
8 changes: 6 additions & 2 deletions packages/core/src/js/feedback/FeedbackForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
Image,
Keyboard,
KeyboardAvoidingView,
Platform,
SafeAreaView,
ScrollView,
Text,
Expand Down Expand Up @@ -130,8 +131,11 @@ export class FeedbackForm extends React.Component<FeedbackFormProps, FeedbackFor

return (
<SafeAreaView style={[styles.container, { padding: 0 }]}>
<KeyboardAvoidingView behavior={'padding'} style={[styles.container, { padding: 0 }]}>
<ScrollView>
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
style={[styles.container, { padding: 0 }]}
>
<ScrollView bounces={false}>
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<View style={styles.container}>
<View style={styles.titleContainer}>
Expand Down
1 change: 0 additions & 1 deletion packages/core/src/js/feedback/FeedbackForm.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,6 @@ export interface FeedbackFormStyles {
screenshotText?: TextStyle;
titleContainer?: ViewStyle;
sentryLogo?: ImageStyle;
modalBackground?: ViewStyle;
}

/**
Expand Down
56 changes: 43 additions & 13 deletions packages/core/src/js/feedback/FeedbackFormManager.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { logger } from '@sentry/core';
import * as React from 'react';
import { Modal, View } from 'react-native';
import { Animated, KeyboardAvoidingView, Modal, Platform, View } from 'react-native';

import { FeedbackForm } from './FeedbackForm';
import defaultStyles from './FeedbackForm.styles';
import { modalBackground, modalSheetContainer, modalWrapper } from './FeedbackForm.styles';
import type { FeedbackFormStyles } from './FeedbackForm.types';
import { getFeedbackOptions } from './integration';
import { isModalSupported } from './utils';
Expand Down Expand Up @@ -40,16 +40,37 @@ interface FeedbackFormProviderProps {
styles?: FeedbackFormStyles;
}

interface FeedbackFormProviderState {
isVisible: boolean;
backgroundOpacity: Animated.Value;
}

class FeedbackFormProvider extends React.Component<FeedbackFormProviderProps> {
public state = {
public state: FeedbackFormProviderState = {
isVisible: false,
backgroundOpacity: new Animated.Value(0),
};

public constructor(props: FeedbackFormProviderProps) {
super(props);
FeedbackFormManager.initialize(this._setVisibilityFunction);
}

/**
* Animates the background opacity when the modal is shown.
*/
public componentDidUpdate(_prevProps: any, prevState: FeedbackFormProviderState): void {
if (!prevState.isVisible && this.state.isVisible) {
Animated.timing(this.state.backgroundOpacity, {
toValue: 1,
duration: 300,
useNativeDriver: true,
}).start();
} else if (prevState.isVisible && !this.state.isVisible) {
this.state.backgroundOpacity.setValue(0);
}
}

/**
* Renders the feedback form modal.
*/
Expand All @@ -59,25 +80,34 @@ class FeedbackFormProvider extends React.Component<FeedbackFormProviderProps> {
return <>{this.props.children}</>;
}

const { isVisible } = this.state;
const styles: FeedbackFormStyles = { ...defaultStyles, ...this.props.styles };
const { isVisible, backgroundOpacity } = this.state;

const backgroundColor = backgroundOpacity.interpolate({
inputRange: [0, 1],
outputRange: ['rgba(0, 0, 0, 0)', 'rgba(0, 0, 0, 0.9)'],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

q: why 0.9?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. I mainly tried to match the darker background of the manually created form and still keep the transparency.

});

// Wrapping the `Modal` component in a `View` component is necessary to avoid
// issues like https://github.com/software-mansion/react-native-reanimated/issues/6035
return (
<>
{this.props.children}
{isVisible && (
<View>
<Animated.View style={[modalWrapper, { backgroundColor }]} >
<Modal visible={isVisible} transparent animationType="slide" onRequestClose={this._handleClose} testID="feedback-form-modal">
<View style={styles.modalBackground}>
<FeedbackForm {...getFeedbackOptions()}
onFormClose={this._handleClose}
onFormSubmitted={this._handleClose}
/>
</View>
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
style={modalBackground}
>
<View style={modalSheetContainer}>
<FeedbackForm {...getFeedbackOptions()}
onFormClose={this._handleClose}
onFormSubmitted={this._handleClose}
/>
</View>
</KeyboardAvoidingView>
</Modal>
</View>
</Animated.View>
)}
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ exports[`FeedbackForm matches the snapshot with custom styles 1`] = `
]
}
>
<RCTScrollView>
<RCTScrollView
bounces={false}
>
<View>
<View
accessibilityState={
Expand Down Expand Up @@ -299,7 +301,9 @@ exports[`FeedbackForm matches the snapshot with custom styles and screenshot but
]
}
>
<RCTScrollView>
<RCTScrollView
bounces={false}
>
<View>
<View
accessibilityState={
Expand Down Expand Up @@ -617,7 +621,9 @@ exports[`FeedbackForm matches the snapshot with custom texts 1`] = `
]
}
>
<RCTScrollView>
<RCTScrollView
bounces={false}
>
<View>
<View
accessibilityState={
Expand Down Expand Up @@ -916,7 +922,9 @@ exports[`FeedbackForm matches the snapshot with custom texts and screenshot butt
]
}
>
<RCTScrollView>
<RCTScrollView
bounces={false}
>
<View>
<View
accessibilityState={
Expand Down Expand Up @@ -1265,7 +1273,9 @@ exports[`FeedbackForm matches the snapshot with default configuration 1`] = `
]
}
>
<RCTScrollView>
<RCTScrollView
bounces={false}
>
<View>
<View
accessibilityState={
Expand Down Expand Up @@ -1564,7 +1574,9 @@ exports[`FeedbackForm matches the snapshot with default configuration and screen
]
}
>
<RCTScrollView>
<RCTScrollView
bounces={false}
>
<View>
<View
accessibilityState={
Expand Down
Loading