Fix android keyboard area when switching apps (#8554)
This commit is contained in:
parent
c8f42ee67e
commit
762bbabfa0
4 changed files with 141 additions and 32 deletions
98
app/hooks/android_adjust_soft_keyboard/index.test.ts
Normal file
98
app/hooks/android_adjust_soft_keyboard/index.test.ts
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import RNUtils from '@mattermost/rnutils';
|
||||
import {renderHook} from '@testing-library/react-hooks';
|
||||
import {Keyboard} from 'react-native';
|
||||
import {Navigation} from 'react-native-navigation';
|
||||
|
||||
import {useAndroidAdjustSoftKeyboard} from '.'; // Update with the correct path
|
||||
|
||||
jest.mock('react-native', () => ({
|
||||
Keyboard: {
|
||||
isVisible: jest.fn(),
|
||||
dismiss: jest.fn(),
|
||||
},
|
||||
Platform: {
|
||||
OS: 'android',
|
||||
select: jest.fn((options) => options.android ?? options.default),
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('react-native-navigation', () => {
|
||||
const registerComponentListenerMock = jest.fn();
|
||||
return {
|
||||
Navigation: {
|
||||
events: jest.fn(() => ({
|
||||
registerComponentListener: registerComponentListenerMock,
|
||||
})),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('@mattermost/rnutils', () => ({
|
||||
setSoftKeyboardToAdjustNothing: jest.fn(),
|
||||
setSoftKeyboardToAdjustResize: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('useAndroidAdjustSoftKeyboard', () => {
|
||||
let registerComponentListenerMock: jest.SpyInstance;
|
||||
const unsubscribeMock = {remove: jest.fn()};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
const events = Navigation.events();
|
||||
registerComponentListenerMock = jest.spyOn(events, 'registerComponentListener');
|
||||
registerComponentListenerMock.mockReturnValue(unsubscribeMock);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('should register component listener and handle lifecycle events', () => {
|
||||
renderHook(() => useAndroidAdjustSoftKeyboard('Channel'));
|
||||
|
||||
expect(registerComponentListenerMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
componentDidAppear: expect.any(Function),
|
||||
componentDidDisappear: expect.any(Function),
|
||||
}),
|
||||
'Channel',
|
||||
);
|
||||
|
||||
const listener = registerComponentListenerMock.mock.calls[0][0];
|
||||
|
||||
jest.mocked(Keyboard.isVisible).mockReturnValue(true);
|
||||
listener.componentDidAppear();
|
||||
|
||||
expect(Keyboard.dismiss).toHaveBeenCalled();
|
||||
|
||||
jest.runAllTimers();
|
||||
expect(RNUtils.setSoftKeyboardToAdjustNothing).toHaveBeenCalled();
|
||||
|
||||
listener.componentDidDisappear();
|
||||
expect(RNUtils.setSoftKeyboardToAdjustResize).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should register component listener and handle lifecycle events when the screen is undefined', () => {
|
||||
renderHook(() => useAndroidAdjustSoftKeyboard(undefined));
|
||||
|
||||
expect(registerComponentListenerMock).not.toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
componentDidAppear: expect.any(Function),
|
||||
componentDidDisappear: expect.any(Function),
|
||||
}),
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
|
||||
it('should clean up on unmount', () => {
|
||||
const {unmount} = renderHook(() => useAndroidAdjustSoftKeyboard('Channel'));
|
||||
|
||||
unmount();
|
||||
|
||||
expect(unsubscribeMock.remove).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
39
app/hooks/android_adjust_soft_keyboard/index.ts
Normal file
39
app/hooks/android_adjust_soft_keyboard/index.ts
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import RNUtils from '@mattermost/rnutils';
|
||||
import {useEffect, useRef} from 'react';
|
||||
import {Keyboard} from 'react-native';
|
||||
import {Navigation, type EventSubscription} from 'react-native-navigation';
|
||||
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
export function useAndroidAdjustSoftKeyboard(screen?: AvailableScreens) {
|
||||
const timeout = useRef<NodeJS.Timeout>();
|
||||
|
||||
useEffect(() => {
|
||||
let unsubscribe: EventSubscription;
|
||||
const listener = {
|
||||
componentDidAppear: () => {
|
||||
if (Keyboard.isVisible()) {
|
||||
Keyboard.dismiss();
|
||||
}
|
||||
timeout.current = setTimeout(() => {
|
||||
RNUtils.setSoftKeyboardToAdjustNothing();
|
||||
}, 10);
|
||||
},
|
||||
componentDidDisappear: () => {
|
||||
RNUtils.setSoftKeyboardToAdjustResize();
|
||||
},
|
||||
};
|
||||
|
||||
if (screen) {
|
||||
unsubscribe = Navigation.events().registerComponentListener(listener, screen);
|
||||
}
|
||||
|
||||
return () => {
|
||||
clearTimeout(timeout.current);
|
||||
unsubscribe?.remove();
|
||||
};
|
||||
}, []);
|
||||
}
|
||||
|
|
@ -1,10 +1,8 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import RNUtils from '@mattermost/rnutils';
|
||||
import React, {useCallback, useEffect, useState} from 'react';
|
||||
import {type LayoutChangeEvent, StyleSheet, View} from 'react-native';
|
||||
import {Navigation} from 'react-native-navigation';
|
||||
import {type Edge, SafeAreaView, useSafeAreaInsets} from 'react-native-safe-area-context';
|
||||
|
||||
import {storeLastViewedChannelIdAndServer, removeLastViewedChannelIdAndServer} from '@actions/app/global';
|
||||
|
|
@ -12,6 +10,7 @@ import FloatingCallContainer from '@calls/components/floating_call_container';
|
|||
import FreezeScreen from '@components/freeze_screen';
|
||||
import PostDraft from '@components/post_draft';
|
||||
import {ExtraKeyboardProvider} from '@context/extra_keyboard';
|
||||
import {useAndroidAdjustSoftKeyboard} from '@hooks/android_adjust_soft_keyboard';
|
||||
import useAndroidHardwareBackHandler from '@hooks/android_back_handler';
|
||||
import {useChannelSwitch} from '@hooks/channel_switch';
|
||||
import {useIsTablet} from '@hooks/device';
|
||||
|
|
@ -80,20 +79,7 @@ const Channel = ({
|
|||
}, [componentId]);
|
||||
|
||||
useAndroidHardwareBackHandler(componentId, handleBack);
|
||||
|
||||
useEffect(() => {
|
||||
const listener = {
|
||||
componentDidAppear: () => {
|
||||
RNUtils.setSoftKeyboardToAdjustNothing();
|
||||
},
|
||||
componentDidDisappear: () => {
|
||||
RNUtils.setSoftKeyboardToAdjustResize();
|
||||
},
|
||||
};
|
||||
const unsubscribe = Navigation.events().registerComponentListener(listener, componentId!);
|
||||
|
||||
return () => unsubscribe.remove();
|
||||
}, []);
|
||||
useAndroidAdjustSoftKeyboard(componentId);
|
||||
|
||||
const marginTop = defaultHeight + (isTablet ? 0 : -insets.top);
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import RNUtils from '@mattermost/rnutils';
|
||||
import {uniqueId} from 'lodash';
|
||||
import React, {useCallback, useEffect, useState} from 'react';
|
||||
import {type LayoutChangeEvent, StyleSheet, View} from 'react-native';
|
||||
import {Navigation} from 'react-native-navigation';
|
||||
import {type Edge, SafeAreaView} from 'react-native-safe-area-context';
|
||||
|
||||
import {storeLastViewedThreadIdAndServer, removeLastViewedThreadIdAndServer} from '@actions/app/global';
|
||||
|
|
@ -15,6 +13,7 @@ import PostDraft from '@components/post_draft';
|
|||
import RoundedHeaderContext from '@components/rounded_header_context';
|
||||
import {Screens} from '@constants';
|
||||
import {ExtraKeyboardProvider} from '@context/extra_keyboard';
|
||||
import {useAndroidAdjustSoftKeyboard} from '@hooks/android_adjust_soft_keyboard';
|
||||
import useAndroidHardwareBackHandler from '@hooks/android_back_handler';
|
||||
import useDidUpdate from '@hooks/did_update';
|
||||
import {popTopScreen, setButtons} from '@screens/navigation';
|
||||
|
|
@ -58,20 +57,7 @@ const Thread = ({
|
|||
}, [componentId]);
|
||||
|
||||
useAndroidHardwareBackHandler(componentId, close);
|
||||
|
||||
useEffect(() => {
|
||||
const listener = {
|
||||
componentDidAppear: () => {
|
||||
RNUtils.setSoftKeyboardToAdjustNothing();
|
||||
},
|
||||
componentDidDisappear: () => {
|
||||
RNUtils.setSoftKeyboardToAdjustResize();
|
||||
},
|
||||
};
|
||||
const unsubscribe = Navigation.events().registerComponentListener(listener, componentId!);
|
||||
|
||||
return () => unsubscribe.remove();
|
||||
}, []);
|
||||
useAndroidAdjustSoftKeyboard(componentId);
|
||||
|
||||
useEffect(() => {
|
||||
if (isCRTEnabled && rootId) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue