fix: NavBottomFAB position (#5555)
This commit is contained in:
parent
c8e1f20d1e
commit
b189f59950
|
@ -30,6 +30,7 @@ import log from '../../lib/methods/helpers/log';
|
||||||
import { prepareQuoteMessage } from './helpers';
|
import { prepareQuoteMessage } from './helpers';
|
||||||
import { RecordAudio } from './components/RecordAudio';
|
import { RecordAudio } from './components/RecordAudio';
|
||||||
import { useKeyboardListener } from './hooks';
|
import { useKeyboardListener } from './hooks';
|
||||||
|
import { emitter } from '../../lib/methods/helpers/emitter';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
|
@ -193,6 +194,7 @@ export const MessageComposer = ({
|
||||||
|
|
||||||
const onHeightChanged = (height: number) => {
|
const onHeightChanged = (height: number) => {
|
||||||
setTrackingViewHeight(height);
|
setTrackingViewHeight(height);
|
||||||
|
emitter.emit(`setComposerHeight${tmid ? 'Thread' : ''}`, height);
|
||||||
};
|
};
|
||||||
|
|
||||||
const backgroundColor = action === 'edit' ? colors.statusBackgroundWarning2 : colors.surfaceLight;
|
const backgroundColor = action === 'edit' ? colors.statusBackgroundWarning2 : colors.surfaceLight;
|
||||||
|
|
|
@ -16,7 +16,7 @@ import { getRoomTitle, parseJson } from '../../../lib/methods/helpers';
|
||||||
import { MAX_HEIGHT, MIN_HEIGHT, NO_CANNED_RESPONSES, MARKDOWN_STYLES } from '../constants';
|
import { MAX_HEIGHT, MIN_HEIGHT, NO_CANNED_RESPONSES, MARKDOWN_STYLES } from '../constants';
|
||||||
import database from '../../../lib/database';
|
import database from '../../../lib/database';
|
||||||
import Navigation from '../../../lib/navigation/appNavigation';
|
import Navigation from '../../../lib/navigation/appNavigation';
|
||||||
import { emitter } from '../emitter';
|
import { emitter } from '../../../lib/methods/helpers/emitter';
|
||||||
import { useRoomContext } from '../../../views/RoomView/context';
|
import { useRoomContext } from '../../../views/RoomView/context';
|
||||||
import { getMessageById } from '../../../lib/database/services/Message';
|
import { getMessageById } from '../../../lib/database/services/Message';
|
||||||
import { generateTriggerId } from '../../../lib/methods';
|
import { generateTriggerId } from '../../../lib/methods';
|
||||||
|
|
|
@ -3,7 +3,7 @@ import React, { ReactElement } from 'react';
|
||||||
import { ActionsButton, BaseButton } from '..';
|
import { ActionsButton, BaseButton } from '..';
|
||||||
import { useMessageComposerApi } from '../../context';
|
import { useMessageComposerApi } from '../../context';
|
||||||
import { Gap } from '../Gap';
|
import { Gap } from '../Gap';
|
||||||
import { emitter } from '../../emitter';
|
import { emitter } from '../../../../lib/methods/helpers/emitter';
|
||||||
import { useRoomContext } from '../../../../views/RoomView/context';
|
import { useRoomContext } from '../../../../views/RoomView/context';
|
||||||
|
|
||||||
export const Default = (): ReactElement | null => {
|
export const Default = (): ReactElement | null => {
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { BaseButton } from '..';
|
||||||
import { useMessageComposerApi } from '../../context';
|
import { useMessageComposerApi } from '../../context';
|
||||||
import { Gap } from '../Gap';
|
import { Gap } from '../Gap';
|
||||||
import { TMarkdownStyle } from '../../interfaces';
|
import { TMarkdownStyle } from '../../interfaces';
|
||||||
import { emitter } from '../../emitter';
|
import { emitter } from '../../../../lib/methods/helpers/emitter';
|
||||||
|
|
||||||
export const Markdown = (): ReactElement => {
|
export const Markdown = (): ReactElement => {
|
||||||
const { setMarkdownToolbar } = useMessageComposerApi();
|
const { setMarkdownToolbar } = useMessageComposerApi();
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
import mitt from 'mitt';
|
|
||||||
|
|
||||||
import { TMarkdownStyle } from './interfaces';
|
|
||||||
|
|
||||||
type Events = {
|
|
||||||
toolbarMention: undefined;
|
|
||||||
addMarkdown: {
|
|
||||||
style: TMarkdownStyle;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const emitter = mitt<Events>();
|
|
||||||
|
|
||||||
emitter.on('*', (type, e) => console.log(type, e));
|
|
|
@ -1,26 +1,37 @@
|
||||||
import { MutableRefObject, useEffect } from 'react';
|
import { MutableRefObject, useEffect } from 'react';
|
||||||
import { Keyboard } from 'react-native';
|
import { Keyboard } from 'react-native';
|
||||||
|
import { useIsFocused } from '@react-navigation/native';
|
||||||
|
|
||||||
import { useMessageComposerApi } from '../context';
|
import { useMessageComposerApi } from '../context';
|
||||||
import { ITrackingView } from '../interfaces';
|
import { ITrackingView } from '../interfaces';
|
||||||
|
import { TKeyEmitterEvent, emitter } from '../../../lib/methods/helpers/emitter';
|
||||||
|
import { useRoomContext } from '../../../views/RoomView/context';
|
||||||
|
|
||||||
export const useKeyboardListener = (ref: MutableRefObject<ITrackingView>) => {
|
export const useKeyboardListener = (ref: MutableRefObject<ITrackingView>) => {
|
||||||
const { setKeyboardHeight } = useMessageComposerApi();
|
const { setKeyboardHeight } = useMessageComposerApi();
|
||||||
|
const { tmid } = useRoomContext();
|
||||||
|
const isFocused = useIsFocused();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!isFocused) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const keyboardEvent: TKeyEmitterEvent = `setKeyboardHeight${tmid ? 'Thread' : ''}`;
|
||||||
const showListener = Keyboard.addListener('keyboardWillShow', async () => {
|
const showListener = Keyboard.addListener('keyboardWillShow', async () => {
|
||||||
if (ref?.current) {
|
if (ref?.current) {
|
||||||
const props = await ref.current.getNativeProps();
|
const props = await ref.current.getNativeProps();
|
||||||
setKeyboardHeight(props.keyboardHeight);
|
setKeyboardHeight(props.keyboardHeight);
|
||||||
|
emitter.emit(keyboardEvent, props.keyboardHeight);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const hideListener = Keyboard.addListener('keyboardWillHide', () => {
|
const hideListener = Keyboard.addListener('keyboardWillHide', () => {
|
||||||
setKeyboardHeight(0);
|
setKeyboardHeight(0);
|
||||||
|
emitter.emit(keyboardEvent, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
showListener.remove();
|
showListener.remove();
|
||||||
hideListener.remove();
|
hideListener.remove();
|
||||||
};
|
};
|
||||||
}, [ref, setKeyboardHeight]);
|
}, [ref, setKeyboardHeight, tmid, isFocused]);
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
import mitt from 'mitt';
|
||||||
|
|
||||||
|
import { TMarkdownStyle } from '../../../containers/MessageComposer/interfaces';
|
||||||
|
|
||||||
|
export type TEmitterEvents = {
|
||||||
|
toolbarMention: undefined;
|
||||||
|
addMarkdown: {
|
||||||
|
style: TMarkdownStyle;
|
||||||
|
};
|
||||||
|
setKeyboardHeight: number;
|
||||||
|
setKeyboardHeightThread: number;
|
||||||
|
setComposerHeight: number;
|
||||||
|
setComposerHeightThread: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TKeyEmitterEvent = keyof TEmitterEvents;
|
||||||
|
|
||||||
|
export const emitter = mitt<TEmitterEvents>();
|
||||||
|
|
||||||
|
emitter.on('*', (type, e) => console.log(type, e));
|
|
@ -42,6 +42,9 @@ class EventEmitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use lib/methods/helpers/emitter.ts
|
||||||
|
*/
|
||||||
emit(event: string, ...args: TEventEmitterEmmitArgs[]) {
|
emit(event: string, ...args: TEventEmitterEmmitArgs[]) {
|
||||||
if (typeof this.events[event] === 'object') {
|
if (typeof this.events[event] === 'object') {
|
||||||
this.events[event].forEach((listener: Function) => {
|
this.events[event].forEach((listener: Function) => {
|
||||||
|
|
|
@ -16,4 +16,5 @@ export * from './isValidEmail';
|
||||||
export * from './random';
|
export * from './random';
|
||||||
export * from './image';
|
export * from './image';
|
||||||
export * from './askAndroidMediaPermissions';
|
export * from './askAndroidMediaPermissions';
|
||||||
|
export * from './emitter';
|
||||||
export * from './parseJson';
|
export * from './parseJson';
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
import React from 'react';
|
import React, { memo } from 'react';
|
||||||
import { StyleSheet, View, Platform } from 'react-native';
|
import { StyleSheet, View } from 'react-native';
|
||||||
|
|
||||||
import { CustomIcon } from '../../../../containers/CustomIcon';
|
import { CustomIcon } from '../../../../containers/CustomIcon';
|
||||||
import { useTheme } from '../../../../theme';
|
import { useTheme } from '../../../../theme';
|
||||||
import Touch from '../../../../containers/Touch';
|
import Touch from '../../../../containers/Touch';
|
||||||
|
import { useNavBottomStyle } from '../hooks';
|
||||||
|
import { EDGE_DISTANCE } from '../constants';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
right: 15
|
right: EDGE_DISTANCE
|
||||||
},
|
},
|
||||||
button: {
|
button: {
|
||||||
borderRadius: 25
|
borderRadius: 25
|
||||||
|
@ -23,39 +25,17 @@ const styles = StyleSheet.create({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const NavBottomFAB = ({
|
const NavBottomFAB = memo(
|
||||||
visible,
|
({ visible, onPress, isThread }: { visible: boolean; onPress: Function; isThread: boolean }): React.ReactElement | null => {
|
||||||
onPress,
|
|
||||||
isThread
|
|
||||||
}: {
|
|
||||||
visible: boolean;
|
|
||||||
onPress: Function;
|
|
||||||
isThread: boolean;
|
|
||||||
}): React.ReactElement | null => {
|
|
||||||
const { colors } = useTheme();
|
const { colors } = useTheme();
|
||||||
|
const positionStyle = useNavBottomStyle(isThread);
|
||||||
|
|
||||||
if (!visible) {
|
if (!visible) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View style={[styles.container, positionStyle]} testID='nav-jump-to-bottom'>
|
||||||
style={[
|
|
||||||
styles.container,
|
|
||||||
{
|
|
||||||
...Platform.select({
|
|
||||||
ios: {
|
|
||||||
bottom: 100 + (isThread ? 40 : 0)
|
|
||||||
},
|
|
||||||
android: {
|
|
||||||
top: 15,
|
|
||||||
scaleY: -1
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
testID='nav-jump-to-bottom'
|
|
||||||
>
|
|
||||||
<Touch onPress={() => onPress()} style={[styles.button, { backgroundColor: colors.backgroundColor }]}>
|
<Touch onPress={() => onPress()} style={[styles.button, { backgroundColor: colors.backgroundColor }]}>
|
||||||
<View style={[styles.content, { borderColor: colors.borderColor }]}>
|
<View style={[styles.content, { borderColor: colors.borderColor }]}>
|
||||||
<CustomIcon name='chevron-down' color={colors.auxiliaryTintColor} size={36} />
|
<CustomIcon name='chevron-down' color={colors.auxiliaryTintColor} size={36} />
|
||||||
|
@ -63,6 +43,7 @@ const NavBottomFAB = ({
|
||||||
</Touch>
|
</Touch>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
);
|
||||||
|
|
||||||
export default NavBottomFAB;
|
export default NavBottomFAB;
|
||||||
|
|
|
@ -5,3 +5,5 @@ export const VIEWABILITY_CONFIG = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SCROLL_LIMIT = 200;
|
export const SCROLL_LIMIT = 200;
|
||||||
|
|
||||||
|
export const EDGE_DISTANCE = 15;
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
|
export * from './useNavBottomStyle';
|
||||||
export * from './useMessages';
|
export * from './useMessages';
|
||||||
export * from './useScroll';
|
export * from './useScroll';
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './useNavBottomStyle';
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { ViewStyle } from 'react-native';
|
||||||
|
|
||||||
|
import { EDGE_DISTANCE } from '../../constants';
|
||||||
|
|
||||||
|
export const useNavBottomStyle = (): ViewStyle => ({
|
||||||
|
top: EDGE_DISTANCE,
|
||||||
|
scaleY: -1
|
||||||
|
});
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { ViewStyle } from 'react-native';
|
||||||
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
|
|
||||||
|
import { TKeyEmitterEvent, emitter } from '../../../../../lib/methods/helpers';
|
||||||
|
import { EDGE_DISTANCE } from '../../constants';
|
||||||
|
|
||||||
|
export const useNavBottomStyle = (isThread: boolean): ViewStyle => {
|
||||||
|
const [keyboardHeight, setKeyboardHeight] = useState(0);
|
||||||
|
const [composerHeight, setComposerHeight] = useState(0);
|
||||||
|
const { bottom } = useSafeAreaInsets();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const keyboardEvent: TKeyEmitterEvent = `setKeyboardHeight${isThread ? 'Thread' : ''}`;
|
||||||
|
const composerEvent: TKeyEmitterEvent = `setComposerHeight${isThread ? 'Thread' : ''}`;
|
||||||
|
emitter.on(keyboardEvent, height => {
|
||||||
|
if (height !== keyboardHeight) {
|
||||||
|
setKeyboardHeight(height);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
emitter.on(composerEvent, height => {
|
||||||
|
if (height !== composerHeight) {
|
||||||
|
setComposerHeight(height);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
emitter.off(keyboardEvent);
|
||||||
|
emitter.off(composerEvent);
|
||||||
|
};
|
||||||
|
}, [isThread, keyboardHeight, composerHeight]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
bottom: keyboardHeight + composerHeight + (keyboardHeight ? 0 : bottom) + EDGE_DISTANCE
|
||||||
|
};
|
||||||
|
};
|
|
@ -1,4 +1,4 @@
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import { ViewToken, ViewabilityConfigCallbackPairs } from 'react-native';
|
import { ViewToken, ViewabilityConfigCallbackPairs } from 'react-native';
|
||||||
|
|
||||||
import { IListContainerRef, IListProps, TListRef, TMessagesIdsRef } from '../definitions';
|
import { IListContainerRef, IListProps, TListRef, TMessagesIdsRef } from '../definitions';
|
||||||
|
@ -20,9 +20,9 @@ export const useScroll = ({ listRef, messagesIds }: { listRef: TListRef; message
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
const jumpToBottom = () => {
|
const jumpToBottom = useCallback(() => {
|
||||||
listRef.current?.scrollToOffset({ offset: -100 });
|
listRef.current?.scrollToOffset({ offset: -100 });
|
||||||
};
|
}, [listRef]);
|
||||||
|
|
||||||
const onViewableItemsChanged: IListProps['onViewableItemsChanged'] = ({ viewableItems: vi }) => {
|
const onViewableItemsChanged: IListProps['onViewableItemsChanged'] = ({ viewableItems: vi }) => {
|
||||||
viewableItems.current = vi;
|
viewableItems.current = vi;
|
||||||
|
|
Loading…
Reference in New Issue