1
1
import { logger } from '@sentry/core' ;
2
2
import * as React from 'react' ;
3
- import { Animated , KeyboardAvoidingView , Modal , PanResponder , Platform } from 'react-native' ;
3
+ import { Animated , Dimensions , Easing , KeyboardAvoidingView , Modal , PanResponder , Platform } from 'react-native' ;
4
4
5
5
import { FeedbackWidget } from './FeedbackWidget' ;
6
6
import { modalBackground , modalSheetContainer , modalWrapper } from './FeedbackWidget.styles' ;
@@ -10,6 +10,8 @@ import { isModalSupported } from './utils';
10
10
11
11
const PULL_DOWN_CLOSE_THREESHOLD = 200 ;
12
12
const PULL_DOWN_ANDROID_ACTIVATION_HEIGHT = 150 ;
13
+ const SLIDE_ANIMATION_DURATION = 200 ;
14
+ const BACKGROUND_ANIMATION_DURATION = 200 ;
13
15
14
16
class FeedbackWidgetManager {
15
17
private static _isVisible = false ;
@@ -53,7 +55,7 @@ class FeedbackWidgetProvider extends React.Component<FeedbackWidgetProviderProps
53
55
public state : FeedbackWidgetProviderState = {
54
56
isVisible : false ,
55
57
backgroundOpacity : new Animated . Value ( 0 ) ,
56
- panY : new Animated . Value ( 0 ) ,
58
+ panY : new Animated . Value ( Dimensions . get ( 'screen' ) . height ) ,
57
59
} ;
58
60
59
61
private _panResponder = PanResponder . create ( {
@@ -72,8 +74,8 @@ class FeedbackWidgetProvider extends React.Component<FeedbackWidgetProviderProps
72
74
onPanResponderRelease : ( _ , gestureState ) => {
73
75
if ( gestureState . dy > PULL_DOWN_CLOSE_THREESHOLD ) { // Close on swipe below a certain threshold
74
76
Animated . timing ( this . state . panY , {
75
- toValue : 600 ,
76
- duration : 200 ,
77
+ toValue : Dimensions . get ( 'screen' ) . height ,
78
+ duration : SLIDE_ANIMATION_DURATION ,
77
79
useNativeDriver : true ,
78
80
} ) . start ( ( ) => {
79
81
this . _handleClose ( ) ;
@@ -97,11 +99,22 @@ class FeedbackWidgetProvider extends React.Component<FeedbackWidgetProviderProps
97
99
*/
98
100
public componentDidUpdate ( _prevProps : any , prevState : FeedbackWidgetProviderState ) : void {
99
101
if ( ! prevState . isVisible && this . state . isVisible ) {
100
- Animated . timing ( this . state . backgroundOpacity , {
101
- toValue : 1 ,
102
- duration : 300 ,
103
- useNativeDriver : true ,
104
- } ) . start ( ) ;
102
+ Animated . parallel ( [
103
+ Animated . timing ( this . state . backgroundOpacity , {
104
+ toValue : 1 ,
105
+ duration : BACKGROUND_ANIMATION_DURATION ,
106
+ useNativeDriver : true ,
107
+ easing : Easing . in ( Easing . quad ) ,
108
+ } ) ,
109
+ Animated . timing ( this . state . panY , {
110
+ toValue : 0 ,
111
+ duration : SLIDE_ANIMATION_DURATION ,
112
+ useNativeDriver : true ,
113
+ easing : Easing . in ( Easing . quad ) ,
114
+ } )
115
+ ] ) . start ( ( ) => {
116
+ logger . info ( 'FeedbackWidgetProvider componentDidUpdate' ) ;
117
+ } ) ;
105
118
} else if ( prevState . isVisible && ! this . state . isVisible ) {
106
119
this . state . backgroundOpacity . setValue ( 0 ) ;
107
120
}
@@ -130,7 +143,7 @@ class FeedbackWidgetProvider extends React.Component<FeedbackWidgetProviderProps
130
143
{ this . props . children }
131
144
{ isVisible && (
132
145
< Animated . View style = { [ modalWrapper , { backgroundColor } ] } >
133
- < Modal visible = { isVisible } transparent animationType = "slide " onRequestClose = { this . _handleClose } testID = "feedback-form-modal" >
146
+ < Modal visible = { isVisible } transparent animationType = "none " onRequestClose = { this . _handleClose } testID = "feedback-form-modal" >
134
147
< KeyboardAvoidingView
135
148
behavior = { Platform . OS === 'ios' ? 'padding' : 'height' }
136
149
style = { modalBackground }
@@ -153,15 +166,35 @@ class FeedbackWidgetProvider extends React.Component<FeedbackWidgetProviderProps
153
166
}
154
167
155
168
private _setVisibilityFunction = ( visible : boolean ) : void => {
156
- this . setState ( { isVisible : visible } ) ;
157
- if ( visible ) {
158
- this . state . panY . setValue ( 0 ) ;
169
+ const updateState = ( ) : void => {
170
+ this . setState ( { isVisible : visible } ) ;
171
+ } ;
172
+ if ( ! visible ) {
173
+ Animated . parallel ( [
174
+ Animated . timing ( this . state . panY , {
175
+ toValue : Dimensions . get ( 'screen' ) . height ,
176
+ duration : SLIDE_ANIMATION_DURATION ,
177
+ useNativeDriver : true ,
178
+ easing : Easing . out ( Easing . quad ) ,
179
+ } ) ,
180
+ Animated . timing ( this . state . backgroundOpacity , {
181
+ toValue : 0 ,
182
+ duration : BACKGROUND_ANIMATION_DURATION ,
183
+ useNativeDriver : true ,
184
+ easing : Easing . out ( Easing . quad ) ,
185
+ } )
186
+ ] ) . start ( ( ) => {
187
+ // Change of the state unmount the component
188
+ // which would cancel the animation
189
+ updateState ( ) ;
190
+ } ) ;
191
+ } else {
192
+ updateState ( ) ;
159
193
}
160
194
} ;
161
195
162
196
private _handleClose = ( ) : void => {
163
197
FeedbackWidgetManager . hide ( ) ;
164
- this . setState ( { isVisible : false } ) ;
165
198
} ;
166
199
}
167
200
0 commit comments