Skip to content

Commit d9099fd

Browse files
authored
Merge 8ad3494 into 7660f80
2 parents 7660f80 + 8ad3494 commit d9099fd

File tree

7 files changed

+252
-237
lines changed

7 files changed

+252
-237
lines changed

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

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,10 @@ import { defaultButtonConfiguration } from './defaults';
66
import { defaultButtonStyles } from './FeedbackWidget.styles';
77
import { getTheme } from './FeedbackWidget.theme';
88
import type { FeedbackButtonProps, FeedbackButtonStyles, FeedbackButtonTextConfiguration } from './FeedbackWidget.types';
9+
import { showFeedbackWidget } from './FeedbackWidgetManager';
910
import { feedbackIcon } from './icons';
1011
import { lazyLoadFeedbackIntegration } from './lazy';
1112

12-
const showFeedbackWidget = (): void => {
13-
// eslint-disable-next-line @typescript-eslint/no-var-requires
14-
const { showFeedbackWidget } = require('./FeedbackWidgetManager');
15-
showFeedbackWidget();
16-
};
17-
1813
/**
1914
* @beta
2015
* Implements a feedback button that opens the FeedbackForm.

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { defaultConfiguration } from './defaults';
2323
import defaultStyles from './FeedbackWidget.styles';
2424
import { getTheme } from './FeedbackWidget.theme';
2525
import type { FeedbackGeneralConfiguration, FeedbackTextConfiguration, FeedbackWidgetProps, FeedbackWidgetState, FeedbackWidgetStyles, ImagePickerConfiguration } from './FeedbackWidget.types';
26+
import { hideFeedbackButton, showScreenshotButton } from './FeedbackWidgetManager';
2627
import { lazyLoadFeedbackIntegration } from './lazy';
2728
import { getCapturedScreenshot } from './ScreenshotButton';
2829
import { base64ToUint8Array, feedbackAlertDialog, isValidEmail } from './utils';
@@ -320,8 +321,6 @@ export class FeedbackWidget extends React.Component<FeedbackWidgetProps, Feedbac
320321
)}
321322
{config.enableScreenshot && !this.state.attachmentUri && (
322323
<TouchableOpacity style={styles.takeScreenshotButton} onPress={() => {
323-
// eslint-disable-next-line @typescript-eslint/no-var-requires
324-
const { hideFeedbackButton, showScreenshotButton } = require('./FeedbackWidgetManager');
325324
hideFeedbackButton();
326325
onCancel();
327326
showScreenshotButton();
Lines changed: 25 additions & 225 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,8 @@
1-
import { logger } from '@sentry/core';
2-
import * as React from 'react';
3-
import type { NativeEventSubscription, NativeScrollEvent, NativeSyntheticEvent} from 'react-native';
4-
import { Animated, Appearance, Dimensions, Easing, Modal, PanResponder, Platform, ScrollView, View } from 'react-native';
5-
6-
import { notWeb } from '../utils/environment';
7-
import { FeedbackButton } from './FeedbackButton';
8-
import { FeedbackWidget } from './FeedbackWidget';
9-
import { modalSheetContainer, modalWrapper, topSpacer } from './FeedbackWidget.styles';
10-
import { getTheme } from './FeedbackWidget.theme';
11-
import type { FeedbackWidgetStyles } from './FeedbackWidget.types';
12-
import { getFeedbackButtonOptions, getFeedbackOptions, getScreenshotkButtonOptions } from './integration';
131
import { lazyLoadAutoInjectFeedbackButtonIntegration,lazyLoadAutoInjectFeedbackIntegration, lazyLoadAutoInjectScreenshotButtonIntegration } from './lazy';
14-
import { ScreenshotButton } from './ScreenshotButton';
15-
import { isModalSupported } from './utils';
162

17-
const PULL_DOWN_CLOSE_THRESHOLD = 200;
18-
const SLIDE_ANIMATION_DURATION = 200;
19-
const BACKGROUND_ANIMATION_DURATION = 200;
3+
export const PULL_DOWN_CLOSE_THRESHOLD = 200;
4+
export const SLIDE_ANIMATION_DURATION = 200;
5+
export const BACKGROUND_ANIMATION_DURATION = 200;
206

217
abstract class FeedbackManager {
228
protected static _isVisible = false;
@@ -65,226 +51,40 @@ abstract class FeedbackManager {
6551
}
6652
}
6753

68-
class FeedbackWidgetManager extends FeedbackManager {
54+
/**
55+
* Provides functionality to show and hide the feedback widget.
56+
*/
57+
export class FeedbackWidgetManager extends FeedbackManager {
58+
/**
59+
* Returns the name of the feedback component.
60+
*/
6961
protected static get _feedbackComponentName(): string {
7062
return 'FeedbackWidget';
7163
}
7264
}
7365

74-
class FeedbackButtonManager extends FeedbackManager {
66+
/**
67+
* Provides functionality to show and hide the feedback button.
68+
*/
69+
export class FeedbackButtonManager extends FeedbackManager {
70+
/**
71+
* Returns the name of the feedback component.
72+
*/
7573
protected static get _feedbackComponentName(): string {
7674
return 'FeedbackButton';
7775
}
7876
}
7977

80-
class ScreenshotButtonManager extends FeedbackManager {
81-
protected static get _feedbackComponentName(): string {
82-
return 'ScreenshotButton';
83-
}
84-
}
85-
86-
interface FeedbackWidgetProviderProps {
87-
children: React.ReactNode;
88-
styles?: FeedbackWidgetStyles;
89-
}
90-
91-
interface FeedbackWidgetProviderState {
92-
isButtonVisible: boolean;
93-
isScreenshotButtonVisible: boolean;
94-
isVisible: boolean;
95-
backgroundOpacity: Animated.Value;
96-
panY: Animated.Value;
97-
isScrollAtTop: boolean;
98-
}
99-
100-
class FeedbackWidgetProvider extends React.Component<FeedbackWidgetProviderProps> {
101-
public state: FeedbackWidgetProviderState = {
102-
isButtonVisible: false,
103-
isScreenshotButtonVisible: false,
104-
isVisible: false,
105-
backgroundOpacity: new Animated.Value(0),
106-
panY: new Animated.Value(Dimensions.get('screen').height),
107-
isScrollAtTop: true,
108-
};
109-
110-
private _themeListener: NativeEventSubscription;
111-
112-
private _panResponder = PanResponder.create({
113-
onStartShouldSetPanResponder: (_, gestureState) => {
114-
return notWeb() && this.state.isScrollAtTop && gestureState.dy > 0;
115-
},
116-
onMoveShouldSetPanResponder: (_, gestureState) => {
117-
return notWeb() && this.state.isScrollAtTop && gestureState.dy > 0;
118-
},
119-
onPanResponderMove: (_, gestureState) => {
120-
if (gestureState.dy > 0) {
121-
this.state.panY.setValue(gestureState.dy);
122-
}
123-
},
124-
onPanResponderRelease: (_, gestureState) => {
125-
if (gestureState.dy > PULL_DOWN_CLOSE_THRESHOLD) {
126-
// Close on swipe below a certain threshold
127-
Animated.timing(this.state.panY, {
128-
toValue: Dimensions.get('screen').height,
129-
duration: SLIDE_ANIMATION_DURATION,
130-
useNativeDriver: true,
131-
}).start(() => {
132-
this._handleClose();
133-
});
134-
} else {
135-
// Animate it back to the original position
136-
Animated.spring(this.state.panY, {
137-
toValue: 0,
138-
useNativeDriver: true,
139-
}).start();
140-
}
141-
},
142-
});
143-
144-
public constructor(props: FeedbackWidgetProviderProps) {
145-
super(props);
146-
FeedbackButtonManager.initialize(this._setButtonVisibilityFunction);
147-
ScreenshotButtonManager.initialize(this._setScreenshotButtonVisibilityFunction);
148-
FeedbackWidgetManager.initialize(this._setVisibilityFunction);
149-
}
150-
151-
/**
152-
* Add a listener to the theme change event.
153-
*/
154-
public componentDidMount(): void {
155-
this._themeListener = Appearance.addChangeListener(() => {
156-
this.forceUpdate();
157-
});
158-
}
159-
78+
/**
79+
* Provides functionality to show and hide the screenshot button.
80+
*/
81+
export class ScreenshotButtonManager extends FeedbackManager {
16082
/**
161-
* Clean up the theme listener.
83+
* Returns the name of the feedback component.
16284
*/
163-
public componentWillUnmount(): void {
164-
if (this._themeListener) {
165-
this._themeListener.remove();
166-
}
167-
}
168-
169-
/**
170-
* Animates the background opacity when the modal is shown.
171-
*/
172-
public componentDidUpdate(_prevProps: any, prevState: FeedbackWidgetProviderState): void {
173-
if (!prevState.isVisible && this.state.isVisible) {
174-
Animated.parallel([
175-
Animated.timing(this.state.backgroundOpacity, {
176-
toValue: 1,
177-
duration: BACKGROUND_ANIMATION_DURATION,
178-
useNativeDriver: true,
179-
easing: Easing.in(Easing.quad),
180-
}),
181-
Animated.timing(this.state.panY, {
182-
toValue: 0,
183-
duration: SLIDE_ANIMATION_DURATION,
184-
useNativeDriver: true,
185-
easing: Easing.in(Easing.quad),
186-
})
187-
]).start(() => {
188-
logger.info('FeedbackWidgetProvider componentDidUpdate');
189-
});
190-
} else if (prevState.isVisible && !this.state.isVisible) {
191-
this.state.backgroundOpacity.setValue(0);
192-
}
193-
}
194-
195-
/**
196-
* Renders the feedback form modal.
197-
*/
198-
public render(): React.ReactNode {
199-
if (!isModalSupported()) {
200-
logger.error('FeedbackWidget Modal is not supported in React Native < 0.71 with Fabric renderer.');
201-
return <>{this.props.children}</>;
202-
}
203-
204-
const theme = getTheme();
205-
206-
const { isButtonVisible, isScreenshotButtonVisible, isVisible, backgroundOpacity } = this.state;
207-
208-
const backgroundColor = backgroundOpacity.interpolate({
209-
inputRange: [0, 1],
210-
outputRange: ['rgba(0, 0, 0, 0)', 'rgba(0, 0, 0, 0.9)'],
211-
});
212-
213-
// Wrapping the `Modal` component in a `View` component is necessary to avoid
214-
// issues like https://github.com/software-mansion/react-native-reanimated/issues/6035
215-
return (
216-
<>
217-
{this.props.children}
218-
{isButtonVisible && <FeedbackButton {...getFeedbackButtonOptions()} />}
219-
{isScreenshotButtonVisible && <ScreenshotButton {...getScreenshotkButtonOptions()}/>}
220-
{isVisible &&
221-
<Animated.View style={[modalWrapper, { backgroundColor }]} >
222-
<Modal visible={isVisible} transparent animationType="none" onRequestClose={this._handleClose} testID="feedback-form-modal">
223-
<View style={topSpacer}/>
224-
<Animated.View
225-
style={[modalSheetContainer(theme), { transform: [{ translateY: this.state.panY }] }]}
226-
{...this._panResponder.panHandlers}>
227-
<ScrollView
228-
bounces={false}
229-
keyboardShouldPersistTaps="handled"
230-
automaticallyAdjustKeyboardInsets={Platform.OS === 'ios'}
231-
onScroll={this._handleScroll}>
232-
<FeedbackWidget {...getFeedbackOptions()}
233-
onFormClose={this._handleClose}
234-
onFormSubmitted={this._handleClose}
235-
/>
236-
</ScrollView>
237-
</Animated.View>
238-
</Modal>
239-
</Animated.View>
240-
}
241-
</>
242-
);
85+
protected static get _feedbackComponentName(): string {
86+
return 'ScreenshotButton';
24387
}
244-
245-
private _handleScroll = (event: NativeSyntheticEvent<NativeScrollEvent>): void => {
246-
this.setState({ isScrollAtTop: event.nativeEvent.contentOffset.y <= 0 });
247-
};
248-
249-
private _setVisibilityFunction = (visible: boolean): void => {
250-
const updateState = (): void => {
251-
this.setState({ isVisible: visible });
252-
};
253-
if (!visible) {
254-
Animated.parallel([
255-
Animated.timing(this.state.panY, {
256-
toValue: Dimensions.get('screen').height,
257-
duration: SLIDE_ANIMATION_DURATION,
258-
useNativeDriver: true,
259-
easing: Easing.out(Easing.quad),
260-
}),
261-
Animated.timing(this.state.backgroundOpacity, {
262-
toValue: 0,
263-
duration: BACKGROUND_ANIMATION_DURATION,
264-
useNativeDriver: true,
265-
easing: Easing.out(Easing.quad),
266-
})
267-
]).start(() => {
268-
// Change of the state unmount the component
269-
// which would cancel the animation
270-
updateState();
271-
});
272-
} else {
273-
updateState();
274-
}
275-
};
276-
277-
private _setButtonVisibilityFunction = (visible: boolean): void => {
278-
this.setState({ isButtonVisible: visible });
279-
};
280-
281-
private _setScreenshotButtonVisibilityFunction = (visible: boolean): void => {
282-
this.setState({ isScreenshotButtonVisible: visible });
283-
};
284-
285-
private _handleClose = (): void => {
286-
FeedbackWidgetManager.hide();
287-
};
28888
}
28989

29090
const showFeedbackWidget = (): void => {
@@ -322,4 +122,4 @@ const resetScreenshotButtonManager = (): void => {
322122
ScreenshotButtonManager.reset();
323123
};
324124

325-
export { showFeedbackButton, hideFeedbackButton, showFeedbackWidget, showScreenshotButton, hideScreenshotButton, FeedbackWidgetProvider, resetFeedbackButtonManager, resetFeedbackWidgetManager, resetScreenshotButtonManager };
125+
export { showFeedbackButton, hideFeedbackButton, showFeedbackWidget, showScreenshotButton, hideScreenshotButton, resetFeedbackButtonManager, resetFeedbackWidgetManager, resetScreenshotButtonManager };

0 commit comments

Comments
 (0)