From f62dcd42f74b7ae761dc9878645720a3361519c4 Mon Sep 17 00:00:00 2001 From: Elias Nahum Date: Wed, 11 May 2022 12:33:21 -0400 Subject: [PATCH] Open Find Channels with keyboard shortcut (#6260) --- .../com/mattermost/rnbeta/MainActivity.java | 17 +++++-- .../post_draft/post_input/post_input.tsx | 18 +++++--- .../subheader/search_field/index.tsx | 24 ++-------- app/screens/home/index.tsx | 22 ++++++++- app/screens/navigation.ts | 21 +++++++++ app/store/ephemeral_store.ts | 9 ++++ ios/Mattermost/AppDelegate.mm | 11 +++++ ...react-native-hw-keyboard-event+0.0.4.patch | 45 +++++++++++++++++-- 8 files changed, 131 insertions(+), 36 deletions(-) diff --git a/android/app/src/main/java/com/mattermost/rnbeta/MainActivity.java b/android/app/src/main/java/com/mattermost/rnbeta/MainActivity.java index 2a0c1b4529..66be3c74dd 100644 --- a/android/app/src/main/java/com/mattermost/rnbeta/MainActivity.java +++ b/android/app/src/main/java/com/mattermost/rnbeta/MainActivity.java @@ -43,10 +43,19 @@ public class MainActivity extends NavigationActivity { */ @Override public boolean dispatchKeyEvent(KeyEvent event) { - if (HWKeyboardConnected && event.getKeyCode() == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_UP) { - String keyPressed = event.isShiftPressed() ? "shift-enter" : "enter"; - HWKeyboardEventModule.getInstance().keyPressed(keyPressed); - return true; + if (HWKeyboardConnected) { + int keyCode = event.getKeyCode(); + int keyAction = event.getAction(); + if (keyAction == KeyEvent.ACTION_UP) { + if (keyCode == KeyEvent.KEYCODE_ENTER) { + String keyPressed = event.isShiftPressed() ? "shift-enter" : "enter"; + HWKeyboardEventModule.getInstance().keyPressed(keyPressed); + return true; + } else if (keyCode == KeyEvent.KEYCODE_K && event.isCtrlPressed()) { + HWKeyboardEventModule.getInstance().keyPressed("find-channels"); + return true; + } + } } return super.dispatchKeyEvent(event); }; diff --git a/app/components/post_draft/post_input/post_input.tsx b/app/components/post_draft/post_input/post_input.tsx index 133fe62066..2e48f4127a 100644 --- a/app/components/post_draft/post_input/post_input.tsx +++ b/app/components/post_draft/post_input/post_input.tsx @@ -19,12 +19,11 @@ import {useTheme} from '@context/theme'; import {useIsTablet} from '@hooks/device'; import useDidUpdate from '@hooks/did_update'; import {t} from '@i18n'; +import EphemeralStore from '@store/ephemeral_store'; import {extractFileInfo} from '@utils/file'; import {switchKeyboardForCodeBlocks} from '@utils/markdown'; import {changeOpacity, makeStyleSheetFromTheme, getKeyboardAppearanceFromTheme} from '@utils/theme'; -const HW_EVENT_IN_SCREEN = ['Channel', 'Thread']; - type Props = { testID?: string; channelDisplayName?: string; @@ -220,7 +219,14 @@ export default function PostInput({ }, [addFiles, intl]); const handleHardwareEnterPress = useCallback((keyEvent: {pressedKey: string}) => { - if (HW_EVENT_IN_SCREEN.includes(rootId ? Screens.THREAD : Screens.CHANNEL)) { + const topScreen = EphemeralStore.getNavigationTopComponentId(); + let sourceScreen = Screens.CHANNEL; + if (rootId) { + sourceScreen = Screens.THREAD; + } else if (isTablet) { + sourceScreen = Screens.HOME; + } + if (topScreen === sourceScreen) { switch (keyEvent.pressedKey) { case 'enter': sendMessage(); @@ -231,7 +237,7 @@ export default function PostInput({ break; } } - }, [sendMessage, updateValue, value, cursorPosition]); + }, [sendMessage, updateValue, value, cursorPosition, isTablet]); const onAppStateChange = useCallback((appState: AppStateStatus) => { if (appState !== 'active' && previousAppState.current === 'active') { @@ -271,9 +277,9 @@ export default function PostInput({ }, [value]); useEffect(() => { - HWKeyboardEvent.onHWKeyPressed(handleHardwareEnterPress); + const listener = HWKeyboardEvent.onHWKeyPressed(handleHardwareEnterPress); return () => { - HWKeyboardEvent.removeOnHWKeyPressed(); + listener.remove(); }; }, [handleHardwareEnterPress]); diff --git a/app/screens/home/channel_list/categories_list/subheader/search_field/index.tsx b/app/screens/home/channel_list/categories_list/subheader/search_field/index.tsx index 4e86bf8419..14fddec86d 100644 --- a/app/screens/home/channel_list/categories_list/subheader/search_field/index.tsx +++ b/app/screens/home/channel_list/categories_list/subheader/search_field/index.tsx @@ -3,14 +3,12 @@ import React, {useCallback} from 'react'; import {useIntl} from 'react-intl'; -import {DeviceEventEmitter, TouchableHighlight} from 'react-native'; -import {Options} from 'react-native-navigation'; +import {TouchableHighlight} from 'react-native'; import CompassIcon from '@components/compass_icon'; import FormattedText from '@components/formatted_text'; -import {Events, Screens} from '@constants'; import {useTheme} from '@context/theme'; -import {showModal} from '@screens/navigation'; +import {findChannels} from '@screens/navigation'; import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; import {typography} from '@utils/typography'; @@ -44,23 +42,9 @@ const SearchField = () => { const styles = getStyleSheet(theme); const onPress = useCallback(() => { - const options: Options = {modal: {swipeToDismiss: false}}; - const closeButtonId = 'close-find-channels'; - const closeButton = CompassIcon.getImageSourceSync('close', 24, theme.sidebarHeaderTextColor); - options.topBar = { - leftButtons: [{ - id: closeButtonId, - icon: closeButton, - testID: closeButtonId, - }], - }; - - DeviceEventEmitter.emit(Events.PAUSE_KEYBOARD_TRACKING_VIEW, true); - showModal( - Screens.FIND_CHANNELS, + findChannels( intl.formatMessage({id: 'find_channels.title', defaultMessage: 'Find Channels'}), - {closeButtonId}, - options, + theme, ); }, [intl.locale, theme]); diff --git a/app/screens/home/index.tsx b/app/screens/home/index.tsx index ec9f9ff9be..b310d766b3 100644 --- a/app/screens/home/index.tsx +++ b/app/screens/home/index.tsx @@ -6,10 +6,13 @@ import {NavigationContainer} from '@react-navigation/native'; import React, {useEffect} from 'react'; import {useIntl} from 'react-intl'; import {DeviceEventEmitter, Platform} from 'react-native'; +import HWKeyboardEvent from 'react-native-hw-keyboard-event'; import {enableFreeze, enableScreens} from 'react-native-screens'; import {Events, Screens} from '@constants'; import {useTheme} from '@context/theme'; +import {findChannels} from '@screens/navigation'; +import EphemeralStore from '@store/ephemeral_store'; import {alertTeamRemove} from '@utils/navigation'; import {notificationError} from '@utils/notification'; @@ -46,7 +49,7 @@ export default function HomeScreen(props: HomeProps) { return () => { listener.remove(); }; - }, []); + }, [intl.locale]); useEffect(() => { const listener = DeviceEventEmitter.addListener(Events.LEAVE_TEAM, (displayName: string) => { @@ -56,7 +59,22 @@ export default function HomeScreen(props: HomeProps) { return () => { listener.remove(); }; - }); + }, [intl.locale]); + + useEffect(() => { + const listener = HWKeyboardEvent.onHWKeyPressed((keyEvent: {pressedKey: string}) => { + const screen = EphemeralStore.getAllNavigationComponents(); + if (!screen.includes(Screens.FIND_CHANNELS) && keyEvent.pressedKey === 'find-channels') { + findChannels( + intl.formatMessage({id: 'find_channels.title', defaultMessage: 'Find Channels'}), + theme, + ); + } + }); + return () => { + listener.remove(); + }; + }, [intl.locale]); return ( { const passProps = {form, call}; showModal(Screens.APP_FORM, form.title || '', passProps); }; + +export async function findChannels(title: string, theme: Theme) { + const options: Options = {modal: {swipeToDismiss: false}}; + const closeButtonId = 'close-find-channels'; + const closeButton = CompassIcon.getImageSourceSync('close', 24, theme.sidebarHeaderTextColor); + options.topBar = { + leftButtons: [{ + id: closeButtonId, + icon: closeButton, + testID: closeButtonId, + }], + }; + + DeviceEventEmitter.emit(Events.PAUSE_KEYBOARD_TRACKING_VIEW, true); + showModal( + Screens.FIND_CHANNELS, + title, + {closeButtonId}, + options, + ); +} diff --git a/app/store/ephemeral_store.ts b/app/store/ephemeral_store.ts index 5faff2c21d..da74e4b97c 100644 --- a/app/store/ephemeral_store.ts +++ b/app/store/ephemeral_store.ts @@ -71,7 +71,15 @@ class EphemeralStore { hasModalsOpened = () => this.navigationModalStack.length > 0; + private removeNavigationComponent = (componentId: string) => { + const index = this.allNavigationComponentIds.indexOf(componentId); + if (index >= 0) { + this.allNavigationComponentIds.splice(index, 1); + } + }; + removeNavigationComponentId = (componentId: string) => { + this.removeNavigationComponent(componentId); const index = this.navigationComponentIdStack.indexOf(componentId); if (index >= 0) { this.navigationComponentIdStack.splice(index, 1); @@ -79,6 +87,7 @@ class EphemeralStore { }; removeNavigationModal = (componentId: string) => { + this.removeNavigationComponent(componentId); const index = this.navigationModalStack.indexOf(componentId); if (index >= 0) { diff --git a/ios/Mattermost/AppDelegate.mm b/ios/Mattermost/AppDelegate.mm index e831fb82bf..3795230228 100644 --- a/ios/Mattermost/AppDelegate.mm +++ b/ios/Mattermost/AppDelegate.mm @@ -242,17 +242,24 @@ RNHWKeyboardEvent *hwKeyEvent = nil; if ([hwKeyEvent isListening]) { UIKeyCommand *enter = [UIKeyCommand keyCommandWithInput:@"\r" modifierFlags:0 action:@selector(sendEnter:)]; UIKeyCommand *shiftEnter = [UIKeyCommand keyCommandWithInput:@"\r" modifierFlags:UIKeyModifierShift action:@selector(sendShiftEnter:)]; + UIKeyCommand *findChannels = [UIKeyCommand keyCommandWithInput:@"k" modifierFlags:UIKeyModifierCommand action:@selector(sendFindChannels:)]; if (@available(iOS 13.0, *)) { [enter setTitle:@"Send message"]; + [enter setDiscoverabilityTitle:@"Send message"]; [shiftEnter setTitle:@"Add new line"]; + [shiftEnter setDiscoverabilityTitle:@"Add new line"]; + [findChannels setTitle:@"Find channels"]; + [findChannels setDiscoverabilityTitle:@"Find channels"]; } if (@available(iOS 15.0, *)) { [enter setWantsPriorityOverSystemBehavior:YES]; [shiftEnter setWantsPriorityOverSystemBehavior:YES]; + [findChannels setWantsPriorityOverSystemBehavior:YES]; } [commands addObject: enter]; [commands addObject: shiftEnter]; + [commands addObject: findChannels]; } return commands; @@ -266,6 +273,10 @@ RNHWKeyboardEvent *hwKeyEvent = nil; NSString *selected = sender.input; [hwKeyEvent sendHWKeyEvent:@"shift-enter"]; } +- (void)sendFindChannels:(UIKeyCommand *)sender { + NSString *selected = sender.input; + [hwKeyEvent sendHWKeyEvent:@"find-channels"]; +} #if RCT_NEW_ARCH_ENABLED #pragma mark - RCTCxxBridgeDelegate diff --git a/patches/react-native-hw-keyboard-event+0.0.4.patch b/patches/react-native-hw-keyboard-event+0.0.4.patch index 4cf9a326ef..f3dd3b5a22 100644 --- a/patches/react-native-hw-keyboard-event+0.0.4.patch +++ b/patches/react-native-hw-keyboard-event+0.0.4.patch @@ -12,12 +12,49 @@ index a70aace..6443899 100644 versionCode 1 versionName "1.0" diff --git a/node_modules/react-native-hw-keyboard-event/index.d.ts b/node_modules/react-native-hw-keyboard-event/index.d.ts -index 91999f1..53c7a42 100644 +index 91999f1..116b725 100644 --- a/node_modules/react-native-hw-keyboard-event/index.d.ts +++ b/node_modules/react-native-hw-keyboard-event/index.d.ts -@@ -1,4 +1,4 @@ +@@ -1,4 +1,5 @@ ++import {EventSubscription} from "react-native"; ++ declare module "react-native-hw-keyboard-event"; -export function onHWKeyPressed(hwKeyEvent: { pressedKey: string }): void; -+export function onHWKeyPressed(callback: (hwKeyEvent: { pressedKey: string }) => void): void; - export function removeOnHWKeyPressed(): void; +-export function removeOnHWKeyPressed(): void; ++export function onHWKeyPressed(callback: (hwKeyEvent: { pressedKey: string }) => void): EventSubscription; +diff --git a/node_modules/react-native-hw-keyboard-event/index.js b/node_modules/react-native-hw-keyboard-event/index.js +index 30d4dd9..3dcff70 100644 +--- a/node_modules/react-native-hw-keyboard-event/index.js ++++ b/node_modules/react-native-hw-keyboard-event/index.js +@@ -7,28 +7,9 @@ import { + + class HWKeyboardEvent { + onHWKeyPressed(cb) { +- this.removeOnHWKeyPressed(true); +- if (!this.cbStack) { +- this.cbStack = []; +- } +- this.cbStack.push(cb); + let keyEvent = new NativeEventEmitter(NativeModules.RNHWKeyboardEvent); +- this.listener = keyEvent.addListener("onHWKeyPressed", cb); +- } +- +- removeOnHWKeyPressed(newCbAdded) { +- if (this.listener) { +- this.listener.remove(); +- this.listener = null; +- } +- if (!this.cbStack) return; +- if (!newCbAdded) { +- this.cbStack.pop(); +- if (this.cbStack.length > 0) { +- // re-add removed listeners in case there where any +- this.onHWKeyPressed(this.cbStack[this.cbStack.length - 1]); +- } +- } ++ const listener = keyEvent.addListener("onHWKeyPressed", cb); ++ return listener; + } + } +