Skip to content

Commit

Permalink
Merge pull request #13900 from Expensify/neil-test-tool-gesture
Browse files Browse the repository at this point in the history
Open a test tools modal with 5 taps on dev
  • Loading branch information
techievivek authored Feb 22, 2023
2 parents e08b4fc + eca2c73 commit a7ec150
Show file tree
Hide file tree
Showing 10 changed files with 157 additions and 25 deletions.
2 changes: 2 additions & 0 deletions src/CONST.js
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@ const CONST = {
CONFIRM: 'confirm',
CENTERED: 'centered',
CENTERED_UNSWIPEABLE: 'centered_unswipeable',
CENTERED_SMALL: 'centered_small',
BOTTOM_DOCKED: 'bottom_docked',
POPOVER: 'popover',
RIGHT_DOCKED: 'right_docked',
Expand All @@ -400,6 +401,7 @@ const CONST = {
WARM: 'warm',
REPORT_ACTION_ITEM_LAYOUT_DEBOUNCE_TIME: 1500,
SHOW_LOADING_SPINNER_DEBOUNCE_TIME: 250,
TEST_TOOLS_MODAL_THROTTLE_TIME: 800,
TOOLTIP_SENSE: 1000,
TRIE_INITIALIZATION: 'trie_initialization',
},
Expand Down
3 changes: 3 additions & 0 deletions src/ONYXKEYS.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,9 @@ export default {
// Is Keyboard shortcuts modal open?
IS_SHORTCUTS_MODAL_OPEN: 'isShortcutsModalOpen',

// Is the test tools modal open?
IS_TEST_TOOLS_MODAL_OPEN: 'isTestToolsModalOpen',

// Stores information about active wallet transfer amount, selectedAccountID, status, etc
WALLET_TRANSFER: 'walletTransfer',

Expand Down
70 changes: 47 additions & 23 deletions src/components/ScreenWrapper/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {View} from 'react-native';
import React from 'react';
import {GestureDetector, Gesture} from 'react-native-gesture-handler';
import _ from 'underscore';
import {withOnyx} from 'react-native-onyx';
import lodashGet from 'lodash/get';
Expand All @@ -13,10 +14,13 @@ import OfflineIndicator from '../OfflineIndicator';
import compose from '../../libs/compose';
import withNavigation from '../withNavigation';
import withWindowDimensions from '../withWindowDimensions';
import withEnvironment from '../withEnvironment';
import ONYXKEYS from '../../ONYXKEYS';
import {withNetwork} from '../OnyxProvider';
import {propTypes, defaultProps} from './propTypes';
import * as App from '../../libs/actions/App';
import SafeAreaConsumer from '../SafeAreaConsumer';
import TestToolsModal from '../TestToolsModal';

class ScreenWrapper extends React.Component {
constructor(props) {
Expand Down Expand Up @@ -78,6 +82,19 @@ class ScreenWrapper extends React.Component {
}

render() {
// Open the test tools menu on 5 taps in dev only
const isDevEnvironment = this.props.environment === CONST.ENVIRONMENT.DEV;
const quintupleTap = Gesture.Tap()
.numberOfTaps(5)

// Run the callbacks on the JS thread otherwise there's an error on iOS
.runOnJS(true)
.onEnd(() => {
if (!isDevEnvironment) {
return;
}
App.toggleTestToolsModal();
});
return (
<SafeAreaConsumer>
{({
Expand All @@ -95,29 +112,32 @@ class ScreenWrapper extends React.Component {
}

return (
<View
style={[
...this.props.style,
styles.flex1,
paddingStyle,
]}
>
<KeyboardAvoidingView style={[styles.w100, styles.h100, {maxHeight: this.props.windowHeight}]} behavior={this.props.keyboardAvoidingViewBehavior}>
<HeaderGap />
{// If props.children is a function, call it to provide the insets to the children.
_.isFunction(this.props.children)
? this.props.children({
insets,
safeAreaPaddingBottomStyle,
didScreenTransitionEnd: this.state.didScreenTransitionEnd,
})
: this.props.children
}
{this.props.isSmallScreenWidth && (
<OfflineIndicator />
)}
</KeyboardAvoidingView>
</View>
<GestureDetector gesture={quintupleTap}>
<View
style={[
...this.props.style,
styles.flex1,
paddingStyle,
]}
>
<KeyboardAvoidingView style={[styles.w100, styles.h100, {maxHeight: this.props.windowHeight}]} behavior={this.props.keyboardAvoidingViewBehavior}>
<HeaderGap />
{(this.props.environment === CONST.ENVIRONMENT.DEV) && <TestToolsModal />}
{// If props.children is a function, call it to provide the insets to the children.
_.isFunction(this.props.children)
? this.props.children({
insets,
safeAreaPaddingBottomStyle,
didScreenTransitionEnd: this.state.didScreenTransitionEnd,
})
: this.props.children
}
{this.props.isSmallScreenWidth && (
<OfflineIndicator />
)}
</KeyboardAvoidingView>
</View>
</GestureDetector>
);
}}
</SafeAreaConsumer>
Expand All @@ -135,6 +155,10 @@ export default compose(
modal: {
key: ONYXKEYS.MODAL,
},
isTestToolsModalOpen: {
key: ONYXKEYS.IS_TEST_TOOLS_MODAL_OPEN,
},
}),
withNetwork(),
withEnvironment,
)(ScreenWrapper);
7 changes: 7 additions & 0 deletions src/components/ScreenWrapper/propTypes.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import PropTypes from 'prop-types';
import {environmentPropTypes} from '../withEnvironment';

const propTypes = {
/** Array of additional styles to add */
Expand All @@ -19,6 +20,9 @@ const propTypes = {
// Called when navigated Screen's transition is finished. It does not fire when user exit the page.
onEntryTransitionEnd: PropTypes.func,

// Is the test tools modal open?
isTestToolsModalOpen: PropTypes.bool,

/** The behavior to pass to the KeyboardAvoidingView, requires some trial and error depending on the layout/devices used.
* Search 'switch(behavior)' in ./node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js for more context */
keyboardAvoidingViewBehavior: PropTypes.oneOf(['padding', 'height', 'position']),
Expand All @@ -28,6 +32,8 @@ const propTypes = {
/** Indicates when an Alert modal is about to be visible */
willAlertModalBecomeVisible: PropTypes.bool,
}),

...environmentPropTypes,
};

const defaultProps = {
Expand All @@ -36,6 +42,7 @@ const defaultProps = {
includePaddingTop: true,
onEntryTransitionEnd: () => {},
modal: {},
isTestToolsModalOpen: false,
keyboardAvoidingViewBehavior: 'padding',
};

Expand Down
2 changes: 1 addition & 1 deletion src/components/TestToolMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const defaultProps = {

const TestToolMenu = props => (
<>
<Text style={[styles.textLabelSupporting, styles.mb2, styles.mt6]} numberOfLines={1}>
<Text style={[styles.textLabelSupporting, styles.mb2]} numberOfLines={1}>
Test Preferences
</Text>

Expand Down
2 changes: 1 addition & 1 deletion src/components/TestToolRow.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const propTypes = {
};

const TestToolRow = props => (
<View style={[styles.flexRow, styles.mb6, styles.justifyContentBetween, styles.alignItemsCenter]}>
<View style={[styles.flexRow, styles.mb6, styles.justifyContentBetween, styles.alignItemsCenter, styles.mnw120]}>
<View style={styles.flex2}>
<Text>
{props.title}
Expand Down
44 changes: 44 additions & 0 deletions src/components/TestToolsModal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React from 'react';
import PropTypes from 'prop-types';
import {withOnyx} from 'react-native-onyx';
import {View} from 'react-native';
import ONYXKEYS from '../ONYXKEYS';
import Modal from './Modal';
import CONST from '../CONST';
import * as App from '../libs/actions/App';
import TestToolMenu from './TestToolMenu';
import styles from '../styles/styles';

const propTypes = {
/** Whether the test tools modal is open */
isTestToolsModalOpen: PropTypes.bool,
};

const defaultProps = {
isTestToolsModalOpen: false,
};

const TestToolsModal = props => (
<Modal
isVisible={props.isTestToolsModalOpen}
type={CONST.MODAL.MODAL_TYPE.CENTERED_SMALL}
onClose={App.toggleTestToolsModal}
>
<View style={[styles.settingsPageBody, styles.p5]}>
<TestToolMenu />
</View>
</Modal>
);

TestToolsModal.propTypes = propTypes;
TestToolsModal.defaultProps = defaultProps;
TestToolsModal.displayName = 'TestToolsModal';

export default withOnyx({
modal: {
key: ONYXKEYS.MODAL,
},
isTestToolsModalOpen: {
key: ONYXKEYS.IS_TEST_TOOLS_MODAL_OPEN,
},
})(TestToolsModal);
17 changes: 17 additions & 0 deletions src/libs/actions/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,22 @@ function openProfile() {
Navigation.navigate(ROUTES.SETTINGS_PROFILE);
}

let isTestToolsModalOpen = false;
Onyx.connect({
key: ONYXKEYS.IS_TEST_TOOLS_MODAL_OPEN,
callback: val => isTestToolsModalOpen = val || false,
});

/**
* Toggle the test tools modal open or closed. Throttle the toggle to fix Android Chrome where there seems to be an extra tap which closes the modal.
* Throttling also makes the modal stay open if you accidentally tap an extra time, which is easy to do.
*/
function toggleTestToolsModal() {
const toggle = () => Onyx.set(ONYXKEYS.IS_TEST_TOOLS_MODAL_OPEN, !isTestToolsModalOpen);
const throttledToggle = _.throttle(toggle, CONST.TIMING.TEST_TOOLS_MODAL_THROTTLE_TIME);
throttledToggle();
}

export {
setLocale,
setLocaleAndNavigate,
Expand All @@ -294,4 +310,5 @@ export {
openProfile,
openApp,
reconnectApp,
toggleTestToolsModal,
};
31 changes: 31 additions & 0 deletions src/styles/getModalStyles/getBaseModalStyles.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,37 @@ export default (type, windowDimensions, popoverAnchorPosition = {}, innerContain
shouldAddTopSafeAreaPadding = isSmallScreenWidth;
shouldAddBottomSafeAreaPadding = false;
break;
case CONST.MODAL.MODAL_TYPE.CENTERED_SMALL:
// A centered modal that takes up the minimum possible screen space on all devices
modalStyle = {
...modalStyle,
...{
alignItems: 'center',
},
};
modalContainerStyle = {
// Shadow Styles
shadowColor: themeColors.shadow,
shadowOffset: {
width: 0,
height: 0,
},
shadowOpacity: 0.1,
shadowRadius: 5,

borderRadius: 12,
borderWidth: 0,
};

// Allow this modal to be dismissed with a swipe down or swipe right
swipeDirection = ['down', 'right'];
animationIn = 'fadeIn';
animationOut = 'fadeOut';
shouldAddTopSafeAreaMargin = false;
shouldAddBottomSafeAreaMargin = false;
shouldAddTopSafeAreaPadding = false;
shouldAddBottomSafeAreaPadding = false;
break;
case CONST.MODAL.MODAL_TYPE.BOTTOM_DOCKED:
modalStyle = {
...modalStyle,
Expand Down
4 changes: 4 additions & 0 deletions src/styles/utilities/sizing.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ export default {
minWidth: '25%',
},

mnw120: {
minWidth: 120,
},

w50: {
width: '50%',
},
Expand Down

0 comments on commit a7ec150

Please sign in to comment.