Skip to content

Commit 258c8a4

Browse files
authored
feat(Android): Enable edge-to-edge (#6380)
1 parent 131be73 commit 258c8a4

File tree

66 files changed

+4101
-2335
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+4101
-2335
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
export const KeyboardController = {
2+
addListener: jest.fn(),
3+
removeListener: jest.fn(),
4+
dismiss: jest.fn(() => Promise.resolve()),
5+
setFocusTo: jest.fn(async () => {})
6+
};
7+
8+
export const useKeyboardHandler = () => {};
9+
10+
export const useReanimatedKeyboardAnimation = () => ({
11+
height: { value: 0 },
12+
progress: { value: 0 }
13+
});
14+
15+
export default { KeyboardController, useKeyboardHandler, useReanimatedKeyboardAnimation };
Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
<resources>
22
<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
33
<item name="android:colorEdgeEffect">#aaaaaa</item>
4-
<item name="colorPrimaryDark">@color/splashBackground</item>
54
<item name="android:forceDarkAllowed">false</item>
65
<item name="android:editTextBackground">@drawable/rn_edit_text_material</item>
7-
<item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
86
</style>
97
</resources>

app/containers/AppVersion.tsx

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import React from 'react';
22
import { StyleSheet, Text, View } from 'react-native';
33

4-
import { themes } from '../lib/constants';
54
import sharedStyles from '../views/Styles';
65
import { getReadableVersion } from '../lib/methods/helpers';
76
import I18n from '../i18n';
8-
import { TSupportedThemes } from '../theme';
7+
import { useTheme } from '../theme';
98

109
const styles = StyleSheet.create({
1110
container: {
@@ -21,13 +20,16 @@ const styles = StyleSheet.create({
2120
}
2221
});
2322

24-
const AppVersion = React.memo(({ theme }: { theme: TSupportedThemes }) => (
25-
<View style={styles.container}>
26-
<Text style={[styles.text, { color: themes[theme].fontSecondaryInfo }]}>
27-
{I18n.t('Version_no', { version: '' })}
28-
<Text style={styles.bold}>{getReadableVersion}</Text>
29-
</Text>
30-
</View>
31-
));
23+
const AppVersion = React.memo(() => {
24+
const { colors } = useTheme();
25+
return (
26+
<View style={styles.container}>
27+
<Text style={[styles.text, { color: colors.fontSecondaryInfo }]}>
28+
{I18n.t('Version_no', { version: '' })}
29+
<Text style={styles.bold}>{getReadableVersion}</Text>
30+
</Text>
31+
</View>
32+
);
33+
});
3234

3335
export default AppVersion;

app/containers/EmojiPicker/index.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React, { useCallback, useState } from 'react';
22
import { View } from 'react-native';
33
import { Route } from 'reanimated-tab-view';
4+
import { useSafeAreaInsets } from 'react-native-safe-area-context';
45

56
import EmojiCategory from './EmojiCategory';
67
import Footer from './Footer';
@@ -11,6 +12,7 @@ import { addFrequentlyUsed } from '../../lib/methods';
1112
import { IEmojiPickerProps, EventTypes } from './interfaces';
1213
import { CustomIcon, TIconsName } from '../CustomIcon';
1314
import { TabView } from '../TabView';
15+
import { useTheme } from '../../theme';
1416

1517
const routes = categories.tabs.map(tab => ({
1618
key: tab.category,
@@ -25,6 +27,8 @@ const EmojiPicker = ({
2527
searchedEmojis = []
2628
}: IEmojiPickerProps): React.ReactElement | null => {
2729
const [parentWidth, setParentWidth] = useState(0);
30+
const { bottom } = useSafeAreaInsets();
31+
const { colors } = useTheme();
2832

2933
const handleEmojiSelect = useCallback(
3034
(emoji: IEmoji) => {
@@ -51,7 +55,9 @@ const EmojiPicker = ({
5155
);
5256

5357
return (
54-
<View style={styles.emojiPickerContainer} onLayout={e => setParentWidth(e.nativeEvent.layout.width)}>
58+
<View
59+
style={[styles.emojiPickerContainer, { marginBottom: bottom, backgroundColor: colors.surfaceLight }]}
60+
onLayout={e => setParentWidth(e.nativeEvent.layout.width)}>
5561
{searching ? (
5662
<EmojiCategory
5763
emojis={searchedEmojis}

app/containers/FormContainer.tsx

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import React from 'react';
22
import { ScrollView, ScrollViewProps, StyleSheet, View } from 'react-native';
33

4-
import { themes } from '../lib/constants';
54
import sharedStyles from '../views/Styles';
65
import scrollPersistTaps from '../lib/methods/helpers/scrollPersistTaps';
76
import KeyboardView from './KeyboardView';
@@ -36,22 +35,19 @@ export const FormContainerInner = ({
3635
);
3736

3837
const FormContainer = ({ children, testID, showAppVersion = true, ...props }: IFormContainer) => {
39-
const { theme } = useTheme();
38+
const { colors } = useTheme();
4039

4140
return (
42-
<KeyboardView
43-
style={{ backgroundColor: themes[theme].surfaceRoom }}
44-
contentContainerStyle={sharedStyles.container}
45-
keyboardVerticalOffset={128}>
41+
<KeyboardView>
4642
<StatusBar />
4743
<ScrollView
4844
style={sharedStyles.container}
4945
contentContainerStyle={[sharedStyles.containerScrollView, styles.scrollView]}
5046
{...scrollPersistTaps}
5147
{...props}>
52-
<SafeAreaView testID={testID} style={{ backgroundColor: themes[theme].surfaceRoom }}>
48+
<SafeAreaView testID={testID} style={{ backgroundColor: colors.surfaceRoom }}>
5349
{children}
54-
<>{showAppVersion && <AppVersion theme={theme} />}</>
50+
<>{showAppVersion && <AppVersion />}</>
5551
</SafeAreaView>
5652
</ScrollView>
5753
</KeyboardView>

app/containers/KeyboardView.tsx

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,20 @@
11
import React from 'react';
2-
import { KeyboardAwareScrollView, KeyboardAwareScrollViewProps } from '@codler/react-native-keyboard-aware-scroll-view';
2+
import { KeyboardAvoidingView } from 'react-native-keyboard-controller';
33

4-
import scrollPersistTaps from '../lib/methods/helpers/scrollPersistTaps';
4+
import { useTheme } from '../theme';
55

6-
interface IKeyboardViewProps extends KeyboardAwareScrollViewProps {
7-
keyboardVerticalOffset?: number;
8-
scrollEnabled?: boolean;
6+
interface IKeyboardViewProps {
7+
backgroundColor?: string;
98
children: React.ReactElement[] | React.ReactElement;
109
}
1110

12-
const KeyboardView = ({ style, contentContainerStyle, scrollEnabled, keyboardVerticalOffset, children }: IKeyboardViewProps) => (
13-
<KeyboardAwareScrollView
14-
{...scrollPersistTaps}
15-
style={style}
16-
contentContainerStyle={contentContainerStyle}
17-
scrollEnabled={scrollEnabled}
18-
alwaysBounceVertical={false}
19-
extraHeight={keyboardVerticalOffset}>
20-
{children}
21-
</KeyboardAwareScrollView>
22-
);
11+
const KeyboardView = ({ backgroundColor, children }: IKeyboardViewProps) => {
12+
const { colors } = useTheme();
13+
return (
14+
<KeyboardAvoidingView style={[{ flex: 1, backgroundColor: backgroundColor || colors.surfaceRoom }]} behavior='padding'>
15+
{children}
16+
</KeyboardAvoidingView>
17+
);
18+
};
2319

2420
export default KeyboardView;

app/containers/MessageComposer/MessageComposer.test.tsx

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import { IPermissionsState } from '../../reducers/permissions';
1212
import { IMessage } from '../../definitions';
1313
import { colors } from '../../lib/constants';
1414
import { IRoomContext, RoomContext } from '../../views/RoomView/context';
15+
import * as EmojiKeyboardHook from './hooks/useEmojiKeyboard';
16+
import { initStore } from '../../lib/store/auxStore';
1517

1618
jest.useFakeTimers();
1719

@@ -37,6 +39,7 @@ const initialStoreState = () => {
3739
const permissions: IPermissionsState = { 'mobile-upload-file': ['user'] };
3840
mockedStore.dispatch(setPermissions(permissions));
3941
mockedStore.dispatch(addSettings({ Message_AudioRecorderEnabled: true }));
42+
initStore(mockedStore);
4043
};
4144
initialStoreState();
4245

@@ -60,6 +63,72 @@ const Render = ({ context }: { context?: Partial<IRoomContext> }) => (
6063
</Provider>
6164
);
6265

66+
const sharedValue = {
67+
value: false,
68+
get: () => sharedValue.value,
69+
set: (v: boolean) => {
70+
sharedValue.value = v;
71+
},
72+
addListener: jest.fn(),
73+
removeListener: jest.fn(),
74+
modify: jest.fn()
75+
};
76+
77+
const sharedValueSearchbar = {
78+
value: false,
79+
get: () => sharedValueSearchbar.value,
80+
set: (v: boolean) => {
81+
sharedValueSearchbar.value = v;
82+
},
83+
addListener: jest.fn(),
84+
removeListener: jest.fn(),
85+
modify: jest.fn()
86+
};
87+
88+
const keyboardHeightSharedValue = {
89+
value: 0,
90+
get: () => keyboardHeightSharedValue.value,
91+
set: (v: number) => {
92+
keyboardHeightSharedValue.value = v;
93+
},
94+
addListener: jest.fn(),
95+
removeListener: jest.fn(),
96+
modify: jest.fn()
97+
};
98+
99+
let showEmojiKeyboard = false;
100+
let showEmojiSearchbar = false;
101+
102+
beforeEach(() => {
103+
showEmojiKeyboard = false;
104+
showEmojiSearchbar = false;
105+
jest.spyOn(EmojiKeyboardHook, 'useEmojiKeyboard').mockReturnValue({
106+
showEmojiPickerSharedValue: sharedValue,
107+
showEmojiKeyboard,
108+
openEmojiKeyboard: jest.fn(),
109+
closeEmojiKeyboard: jest.fn(),
110+
showEmojiSearchbarSharedValue: sharedValueSearchbar,
111+
showEmojiSearchbar,
112+
openEmojiSearchbar: jest.fn(),
113+
closeEmojiSearchbar: jest.fn(),
114+
resetKeyboard: jest.fn(),
115+
keyboardHeight: keyboardHeightSharedValue
116+
});
117+
sharedValue.value = false; // reset before each test
118+
sharedValueSearchbar.value = false;
119+
keyboardHeightSharedValue.value = 0;
120+
});
121+
122+
jest.mock('./hooks/useSubscription', () => ({
123+
useSubscription: jest.fn().mockReturnValue({
124+
t: 'd',
125+
rid: 'rid',
126+
tmid: undefined,
127+
fname: 'Rocket Chat',
128+
name: 'Rocket Chat'
129+
})
130+
}));
131+
63132
describe('MessageComposer', () => {
64133
describe('Toolbar', () => {
65134
test('tap actions', async () => {
@@ -71,10 +140,29 @@ describe('MessageComposer', () => {
71140
});
72141

73142
test('tap emoji', async () => {
74-
render(<Render />);
143+
const { rerender } = render(<Render />);
75144

76145
await fireEvent(screen.getByTestId('message-composer-input'), 'focus');
77146
await user.press(screen.getByTestId('message-composer-open-emoji'));
147+
148+
// Simulate the state change that would happen when emoji button is pressed
149+
sharedValue.value = true;
150+
showEmojiKeyboard = true;
151+
jest.spyOn(EmojiKeyboardHook, 'useEmojiKeyboard').mockReturnValue({
152+
showEmojiPickerSharedValue: sharedValue,
153+
showEmojiKeyboard,
154+
openEmojiKeyboard: jest.fn(),
155+
closeEmojiKeyboard: jest.fn(),
156+
showEmojiSearchbarSharedValue: sharedValueSearchbar,
157+
showEmojiSearchbar,
158+
openEmojiSearchbar: jest.fn(),
159+
closeEmojiSearchbar: jest.fn(),
160+
resetKeyboard: jest.fn(),
161+
keyboardHeight: keyboardHeightSharedValue
162+
});
163+
164+
rerender(<Render />);
165+
78166
expect(screen.getByTestId('message-composer-close-emoji')).toBeOnTheScreen();
79167
expect(screen.toJSON()).toMatchSnapshot();
80168
});

0 commit comments

Comments
 (0)