[Keyboard] Keyboard tracking (#6050)

* Pause/Resume tracking keyboard

* fix keyboard tracking view on tablets

* add EDIT_POST screen to pause keyboard tracking
This commit is contained in:
Elias Nahum
2022-03-14 16:21:45 -03:00
committed by GitHub
parent 9e77c419b1
commit acf4cbde8d
11 changed files with 148 additions and 56 deletions

View File

@@ -1,12 +1,12 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {useCallback, useEffect, useRef, useState} from 'react';
import {DeviceEventEmitter, Platform, View} from 'react-native';
import React, {RefObject, useState} from 'react';
import {Platform, useWindowDimensions, View} from 'react-native';
import {KeyboardTrackingView, KeyboardTrackingViewRef} from 'react-native-keyboard-tracking-view';
import Autocomplete from '@components/autocomplete';
import {PostDraft as PostDraftConstants, View as ViewConstants} from '@constants';
import {View as ViewConstants} from '@constants';
import {useIsTablet} from '@hooks/device';
import Archived from './archived';
@@ -26,9 +26,16 @@ type Props = {
message?: string;
rootId?: string;
scrollViewNativeID?: string;
keyboardTracker: RefObject<KeyboardTrackingViewRef>;
}
export default function PostDraft({
const {
KEYBOARD_TRACKING_OFFSET,
KEYBOARD_TRACKING_OFFSET_MODAL_LANDSCAPE,
KEYBOARD_TRACKING_OFFSET_MODAL_PORTRAIT,
} = ViewConstants;
function PostDraft({
testID,
accessoriesContainerID,
canPost,
@@ -41,35 +48,14 @@ export default function PostDraft({
message = '',
rootId = '',
scrollViewNativeID,
keyboardTracker,
}: Props) {
const keyboardTracker = useRef<KeyboardTrackingViewRef>(null);
const resetScrollViewAnimationFrame = useRef<number>();
const [value, setValue] = useState(message);
const [cursorPosition, setCursorPosition] = useState(message.length);
const [postInputTop, setPostInputTop] = useState(0);
const isTablet = useIsTablet();
const updateNativeScrollView = useCallback((scrollViewNativeIDToUpdate: string) => {
if (keyboardTracker?.current && scrollViewNativeID === scrollViewNativeIDToUpdate) {
resetScrollViewAnimationFrame.current = requestAnimationFrame(() => {
keyboardTracker.current?.resetScrollView(scrollViewNativeIDToUpdate);
if (resetScrollViewAnimationFrame.current != null) {
cancelAnimationFrame(resetScrollViewAnimationFrame.current);
}
resetScrollViewAnimationFrame.current = undefined;
});
}
}, [scrollViewNativeID]);
useEffect(() => {
const listener = DeviceEventEmitter.addListener(PostDraftConstants.UPDATE_NATIVE_SCROLLVIEW, updateNativeScrollView);
return () => {
listener.remove();
if (resetScrollViewAnimationFrame.current) {
cancelAnimationFrame(resetScrollViewAnimationFrame.current);
}
};
}, [updateNativeScrollView]);
const dimensions = useWindowDimensions();
const isLandscape = dimensions.width > dimensions.height;
if (channelIsArchived || deactivatedChannel) {
const archivedTestID = `${testID}.archived`;
@@ -128,13 +114,18 @@ export default function PostDraft({
);
}
let viewInitialOffsetY = isTablet ? KEYBOARD_TRACKING_OFFSET : 0;
if (isTablet && rootId) {
viewInitialOffsetY = isLandscape ? KEYBOARD_TRACKING_OFFSET_MODAL_LANDSCAPE : KEYBOARD_TRACKING_OFFSET_MODAL_PORTRAIT;
}
return (
<>
<KeyboardTrackingView
accessoriesContainerID={accessoriesContainerID}
ref={keyboardTracker}
scrollViewNativeID={scrollViewNativeID}
viewInitialOffsetY={isTablet ? ViewConstants.BOTTOM_TAB_HEIGHT : 0}
viewInitialOffsetY={viewInitialOffsetY}
>
{draftHandler}
</KeyboardTrackingView>
@@ -144,3 +135,5 @@ export default function PostDraft({
</>
);
}
export default PostDraft;

View File

@@ -14,8 +14,10 @@ export default keyMirror({
LEAVE_TEAM: null,
LOADING_CHANNEL_POSTS: null,
NOTIFICATION_ERROR: null,
PAUSE_KEYBOARD_TRACKING_VIEW: null,
SERVER_LOGOUT: null,
SERVER_VERSION_CHANGED: null,
TAB_BAR_VISIBLE: null,
TEAM_LOAD_ERROR: null,
USER_TYPING: null,
USER_STOP_TYPING: null,

View File

@@ -4,7 +4,6 @@
export const MAX_MESSAGE_LENGTH_FALLBACK = 4000;
export const DEFAULT_SERVER_MAX_FILE_SIZE = 50 * 1024 * 1024;// 50 Mb
export const ICON_SIZE = 24;
export const UPDATE_NATIVE_SCROLLVIEW = 'onUpdateNativeScrollView';
export const TYPING_HEIGHT = 26;
export const ACCESSORIES_CONTAINER_NATIVE_ID = 'channelAccessoriesContainer';
export const THREAD_ACCESSORIES_CONTAINER_NATIVE_ID = 'threadAccessoriesContainer';
@@ -18,5 +17,4 @@ export default {
MAX_MESSAGE_LENGTH_FALLBACK,
NOTIFY_ALL_MEMBERS,
TYPING_HEIGHT,
UPDATE_NATIVE_SCROLLVIEW,
};

View File

@@ -21,6 +21,9 @@ export const HEADER_WITH_SUBTITLE = 24;
export const IOS_HEADER_SEARCH_INSET = 20;
export const TABLET_HEADER_SEARCH_INSET = 28;
export const ANDROID_HEADER_SEARCH_INSET = 11;
export const KEYBOARD_TRACKING_OFFSET = 72;
export const KEYBOARD_TRACKING_OFFSET_MODAL_LANDSCAPE = 44;
export const KEYBOARD_TRACKING_OFFSET_MODAL_PORTRAIT = 154;
export const INDICATOR_BAR_HEIGHT = 38;
@@ -44,5 +47,8 @@ export default {
TABLET_HEADER_SEARCH_INSET,
ANDROID_HEADER_SEARCH_INSET,
INDICATOR_BAR_HEIGHT,
KEYBOARD_TRACKING_OFFSET,
KEYBOARD_TRACKING_OFFSET_MODAL_LANDSCAPE,
KEYBOARD_TRACKING_OFFSET_MODAL_PORTRAIT,
};

View File

@@ -1,16 +1,17 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {useCallback, useMemo} from 'react';
import React, {useCallback, useEffect, useMemo, useRef} from 'react';
import {useIntl} from 'react-intl';
import {DeviceEventEmitter, Keyboard, Platform, StyleSheet, View} from 'react-native';
import {KeyboardTrackingViewRef} from 'react-native-keyboard-tracking-view';
import {Edge, SafeAreaView, useSafeAreaInsets} from 'react-native-safe-area-context';
import CompassIcon from '@components/compass_icon';
import FreezeScreen from '@components/freeze_screen';
import NavigationHeader from '@components/navigation_header';
import PostDraft from '@components/post_draft';
import {Navigation} from '@constants';
import {Events, Navigation} from '@constants';
import {ACCESSORIES_CONTAINER_NATIVE_ID} from '@constants/post_draft';
import {useTheme} from '@context/theme';
import {useAppState, useIsTablet} from '@hooks/device';
@@ -48,6 +49,7 @@ const Channel = ({channelId, componentId, displayName, isOwnDirectMessage, membe
const insets = useSafeAreaInsets();
const theme = useTheme();
const defaultHeight = useDefaultHeaderHeight();
const postDraftRef = useRef<KeyboardTrackingViewRef>(null);
const rightButtons: HeaderRightButton[] = useMemo(() => ([{
iconName: 'magnify',
onPress: () => {
@@ -88,6 +90,19 @@ const Channel = ({channelId, componentId, displayName, isOwnDirectMessage, membe
console.log('Title Press go to Channel Info', displayName);
}, [channelId]);
useEffect(() => {
const listener = DeviceEventEmitter.addListener(Events.PAUSE_KEYBOARD_TRACKING_VIEW, (pause: boolean) => {
if (pause) {
postDraftRef.current?.pauseTracking(channelId);
return;
}
postDraftRef.current?.resumeTracking(channelId);
});
return () => listener.remove();
}, []);
let title = displayName;
if (isOwnDirectMessage) {
title = formatMessage({id: 'channel_header.directchannel.you', defaultMessage: '{displayName} (you)'}, {displayName});
@@ -125,6 +140,7 @@ const Channel = ({channelId, componentId, displayName, isOwnDirectMessage, membe
</View>
<PostDraft
channelId={channelId}
keyboardTracker={postDraftRef}
scrollViewNativeID={channelId}
accessoriesContainerID={ACCESSORIES_CONTAINER_NATIVE_ID}
/>

View File

@@ -7,7 +7,7 @@ import {Shadow} from 'react-native-neomorph-shadows';
import Animated, {useAnimatedStyle, withTiming} from 'react-native-reanimated';
import {useSafeAreaInsets} from 'react-native-safe-area-context';
import {Navigation as NavigationConstants, Screens, View as ViewConstants} from '@constants';
import {Events, Navigation as NavigationConstants, Screens, View as ViewConstants} from '@constants';
import EphemeralStore from '@store/ephemeral_store';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
@@ -66,7 +66,7 @@ function TabBar({state, descriptors, navigation, theme}: BottomTabBarProps & {th
const safeareaInsets = useSafeAreaInsets();
useEffect(() => {
const event = DeviceEventEmitter.addListener('tabBarVisible', (show) => {
const event = DeviceEventEmitter.addListener(Events.TAB_BAR_VISIBLE, (show) => {
setVisible(show);
});

View File

@@ -307,7 +307,7 @@ export function goToScreen(name: string, title: string, passProps = {}, options
const theme = getThemeFromState();
const isDark = tinyColor(theme.sidebarBg).isDark();
const componentId = EphemeralStore.getNavigationTopComponentId();
DeviceEventEmitter.emit('tabBarVisible', false);
DeviceEventEmitter.emit(Events.TAB_BAR_VISIBLE, false);
const defaultOptions: Options = {
layout: {
componentBackgroundColor: theme.centerChannelBg,

View File

@@ -1,8 +1,9 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {useCallback, useEffect} from 'react';
import React, {useCallback, useEffect, useRef} from 'react';
import {BackHandler, StyleSheet, View} from 'react-native';
import {KeyboardTrackingViewRef} from 'react-native-keyboard-tracking-view';
import {Navigation} from 'react-native-navigation';
import {Edge, SafeAreaView} from 'react-native-safe-area-context';
@@ -32,6 +33,7 @@ const getStyleSheet = StyleSheet.create(() => ({
const Thread = ({closeButtonId, componentId, rootPost}: ThreadProps) => {
const appState = useAppState();
const styles = getStyleSheet();
const postDraftRef = useRef<KeyboardTrackingViewRef>(null);
const close = useCallback(() => {
dismissModal({componentId});
@@ -83,6 +85,7 @@ const Thread = ({closeButtonId, componentId, rootPost}: ThreadProps) => {
scrollViewNativeID={rootPost!.id}
accessoriesContainerID={THREAD_ACCESSORIES_CONTAINER_NATIVE_ID}
rootId={rootPost!.id}
keyboardTracker={postDraftRef}
/>
</>
}