Skip to content

Commit

Permalink
Merge pull request #22743 from rayane-djouah/Migrate-KeyboardShortcut…
Browse files Browse the repository at this point in the history
…sModal.js-to-function-component

Migrate KeyboardShortcutsModal.js to function component
  • Loading branch information
iwiznia authored Jul 13, 2023
2 parents 2454ea8 + 8ec0f33 commit 7872a29
Showing 1 changed file with 102 additions and 103 deletions.
205 changes: 102 additions & 103 deletions src/components/KeyboardShortcutsModal.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, {useEffect, useRef} from 'react';
import PropTypes from 'prop-types';
import {View, ScrollView} from 'react-native';
import {withOnyx} from 'react-native-onyx';
Expand Down Expand Up @@ -32,57 +32,26 @@ const defaultProps = {
isShortcutsModalOpen: false,
};

class KeyboardShortcutsModal extends React.Component {
componentDidMount() {
this.subscribedOpenModalShortcuts = [];
const closeShortcutEscapeModalConfig = CONST.KEYBOARD_SHORTCUTS.ESCAPE;
const closeShortcutEnterModalConfig = CONST.KEYBOARD_SHORTCUTS.ENTER;
const arrowUpConfig = CONST.KEYBOARD_SHORTCUTS.ARROW_UP;
const arrowDownConfig = CONST.KEYBOARD_SHORTCUTS.ARROW_DOWN;
const openShortcutModalConfig = CONST.KEYBOARD_SHORTCUTS.SHORTCUT_MODAL;

const openShortcutModalConfig = CONST.KEYBOARD_SHORTCUTS.SHORTCUT_MODAL;
this.unsubscribeShortcutModal = KeyboardShortcut.subscribe(
openShortcutModalConfig.shortcutKey,
() => {
if (this.props.isShortcutsModalOpen) {
return;
}

ModalActions.close();
KeyboardShortcutsActions.showKeyboardShortcutModal();
},
openShortcutModalConfig.descriptionKey,
openShortcutModalConfig.modifiers,
true,
);

if (this.props.isShortcutsModalOpen) {
// The modal started open, which can happen if you reload the page when the modal is open.
this.subscribeOpenModalShortcuts();
}
}

componentDidUpdate(prevProps) {
if (!prevProps.isShortcutsModalOpen && this.props.isShortcutsModalOpen) {
this.subscribeOpenModalShortcuts();
} else if (prevProps.isShortcutsModalOpen && !this.props.isShortcutsModalOpen) {
// Modal is closing, remove keyboard shortcuts
this.unsubscribeOpenModalShortcuts();
}
}

componentWillUnmount() {
if (this.unsubscribeShortcutModal) {
this.unsubscribeShortcutModal();
}
this.unsubscribeOpenModalShortcuts();
}
function KeyboardShortcutsModal({isShortcutsModalOpen = false, isSmallScreenWidth, translate}) {
const subscribedOpenModalShortcuts = useRef([]);
const modalType = isSmallScreenWidth ? CONST.MODAL.MODAL_TYPE.BOTTOM_DOCKED : CONST.MODAL.MODAL_TYPE.CENTERED_UNSWIPEABLE;
const shortcuts = KeyboardShortcut.getDocumentedShortcuts();

/*
* Subscribe shortcuts that only are used when the modal is open
*/
subscribeOpenModalShortcuts() {
const subscribeOpenModalShortcuts = () => {
// Allow closing the modal with the both Enter and Escape keys
// Both callbacks have the lowest priority (0) to ensure that they are called before any other callbacks
// and configured so that event propagation is stopped after the callback is called (only when the modal is open)
const closeShortcutEscapeModalConfig = CONST.KEYBOARD_SHORTCUTS.ESCAPE;
this.subscribedOpenModalShortcuts.push(

subscribedOpenModalShortcuts.current = [
KeyboardShortcut.subscribe(
closeShortcutEscapeModalConfig.shortcutKey,
() => {
Expand All @@ -94,10 +63,7 @@ class KeyboardShortcutsModal extends React.Component {
true,
true,
),
);

const closeShortcutEnterModalConfig = CONST.KEYBOARD_SHORTCUTS.ENTER;
this.subscribedOpenModalShortcuts.push(
KeyboardShortcut.subscribe(
closeShortcutEnterModalConfig.shortcutKey,
() => {
Expand All @@ -108,81 +74,114 @@ class KeyboardShortcutsModal extends React.Component {
closeShortcutEnterModalConfig.modifiers,
true,
),
);

// Intercept arrow up and down keys to prevent scrolling ArrowKeyFocusManager while this modal is open
const arrowUpConfig = CONST.KEYBOARD_SHORTCUTS.ARROW_UP;
this.subscribedOpenModalShortcuts.push(KeyboardShortcut.subscribe(arrowUpConfig.shortcutKey, () => {}, arrowUpConfig.descriptionKey, arrowUpConfig.modifiers, true));

const arrowDownConfig = CONST.KEYBOARD_SHORTCUTS.ARROW_DOWN;
this.subscribedOpenModalShortcuts.push(KeyboardShortcut.subscribe(arrowDownConfig.shortcutKey, () => {}, arrowDownConfig.descriptionKey, arrowDownConfig.modifiers, true));
}
// Intercept arrow up and down keys to prevent scrolling ArrowKeyFocusManager while this modal is open
KeyboardShortcut.subscribe(arrowUpConfig.shortcutKey, () => {}, arrowUpConfig.descriptionKey, arrowUpConfig.modifiers, true),
KeyboardShortcut.subscribe(arrowDownConfig.shortcutKey, () => {}, arrowDownConfig.descriptionKey, arrowDownConfig.modifiers, true),
];
};

/*
* Unsubscribe all shortcuts that were subscribed when the modal opened
*/
unsubscribeOpenModalShortcuts() {
_.each(this.subscribedOpenModalShortcuts, (unsubscribe) => unsubscribe());
this.subscribedOpenModalShortcuts = [];
}
const unsubscribeOpenModalShortcuts = () => {
_.each(subscribedOpenModalShortcuts.current, (unsubscribe) => unsubscribe());
subscribedOpenModalShortcuts.current = [];
};

/**
* Render single row for the Keyboard shortcuts with description
* @param {Object} shortcut
* @param {Boolean} isFirstRow
* @returns {*}
*/
renderRow(shortcut, isFirstRow) {
return (
<View
style={[styles.keyboardShortcutTableRow, isFirstRow && styles.keyboardShortcutTableFirstRow]}
key={shortcut.displayName}
>
<View style={[styles.dFlex, styles.p2, styles.keyboardShortcutTablePrefix]}>
<Text>{shortcut.displayName}</Text>
</View>
<View style={[styles.flex1, styles.p2, styles.alignSelfStretch]}>
<Text>{this.props.translate(`keyboardShortcutModal.shortcuts.${shortcut.descriptionKey}`)}</Text>
</View>
const renderRow = (shortcut, isFirstRow) => (
<View
style={[styles.keyboardShortcutTableRow, isFirstRow && styles.keyboardShortcutTableFirstRow]}
key={shortcut.displayName}
>
<View style={[styles.dFlex, styles.p2, styles.keyboardShortcutTablePrefix]}>
<Text>{shortcut.displayName}</Text>
</View>
<View style={[styles.flex1, styles.p2, styles.alignSelfStretch]}>
<Text>{translate(`keyboardShortcutModal.shortcuts.${shortcut.descriptionKey}`)}</Text>
</View>
</View>
);

useEffect(() => {
const unsubscribeShortcutModal = KeyboardShortcut.subscribe(
openShortcutModalConfig.shortcutKey,
() => {
if (isShortcutsModalOpen) {
return;
}

ModalActions.close();
KeyboardShortcutsActions.showKeyboardShortcutModal();
},
openShortcutModalConfig.descriptionKey,
openShortcutModalConfig.modifiers,
true,
);
}

render() {
const shortcuts = KeyboardShortcut.getDocumentedShortcuts();
const modalType = this.props.isSmallScreenWidth ? CONST.MODAL.MODAL_TYPE.BOTTOM_DOCKED : CONST.MODAL.MODAL_TYPE.CENTERED_UNSWIPEABLE;

return (
<Modal
isVisible={this.props.isShortcutsModalOpen}
type={modalType}
innerContainerStyle={{...styles.keyboardShortcutModalContainer, ...StyleUtils.getKeyboardShortcutsModalWidth(this.props.isSmallScreenWidth)}}
onClose={KeyboardShortcutsActions.hideKeyboardShortcutModal}
>
<HeaderWithBackButton
title={this.props.translate('keyboardShortcutModal.title')}
shouldShowCloseButton
shouldShowBackButton={false}
onCloseButtonPress={KeyboardShortcutsActions.hideKeyboardShortcutModal}
/>
<ScrollView style={[styles.p5, styles.pt0]}>
<Text style={styles.mb5}>{this.props.translate('keyboardShortcutModal.subtitle')}</Text>
<View style={[styles.keyboardShortcutTableWrapper]}>
<View style={[styles.alignItemsCenter, styles.keyboardShortcutTableContainer]}>
{_.map(shortcuts, (shortcut, index) => {
const isFirstRow = index === 0;
return this.renderRow(shortcut, isFirstRow);
})}
</View>

if (isShortcutsModalOpen) {
// The modal started open, which can happen if you reload the page when the modal is open.
subscribeOpenModalShortcuts();
}

return () => {
if (unsubscribeShortcutModal) {
unsubscribeShortcutModal();
}
unsubscribeOpenModalShortcuts();
};
// We only want this to run on mount
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

useEffect(() => {
if (isShortcutsModalOpen) {
subscribeOpenModalShortcuts();
} else {
// Modal is closing, remove keyboard shortcuts
unsubscribeOpenModalShortcuts();
}
// subscribeOpenModalShortcuts and unsubscribeOpenModalShortcuts functions are not added as dependencies since they don't change between renders
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isShortcutsModalOpen]);

return (
<Modal
isVisible={isShortcutsModalOpen}
type={modalType}
innerContainerStyle={{...styles.keyboardShortcutModalContainer, ...StyleUtils.getKeyboardShortcutsModalWidth(isSmallScreenWidth)}}
onClose={KeyboardShortcutsActions.hideKeyboardShortcutModal}
>
<HeaderWithBackButton
title={translate('keyboardShortcutModal.title')}
shouldShowCloseButton
shouldShowBackButton={false}
onCloseButtonPress={KeyboardShortcutsActions.hideKeyboardShortcutModal}
/>
<ScrollView style={[styles.p5, styles.pt0]}>
<Text style={styles.mb5}>{translate('keyboardShortcutModal.subtitle')}</Text>
<View style={[styles.keyboardShortcutTableWrapper]}>
<View style={[styles.alignItemsCenter, styles.keyboardShortcutTableContainer]}>
{_.map(shortcuts, (shortcut, index) => {
const isFirstRow = index === 0;
return renderRow(shortcut, isFirstRow);
})}
</View>
</ScrollView>
</Modal>
);
}
</View>
</ScrollView>
</Modal>
);
}

KeyboardShortcutsModal.propTypes = propTypes;
KeyboardShortcutsModal.defaultProps = defaultProps;
KeyboardShortcutsModal.displayName = 'KeyboardShortcutsModal';

export default compose(
withWindowDimensions,
Expand Down

0 comments on commit 7872a29

Please sign in to comment.