Page MenuHomePhorge

D6369.1765266299.diff
No OneTemporary

Size
4 KB
Referenced Files
None
Subscribers
None

D6369.1765266299.diff

diff --git a/native/chat/typeahead-tooltip.react.js b/native/chat/typeahead-tooltip.react.js
new file mode 100644
--- /dev/null
+++ b/native/chat/typeahead-tooltip.react.js
@@ -0,0 +1,148 @@
+// @flow
+
+import * as React from 'react';
+import { Platform, Text } from 'react-native';
+import { PanGestureHandler, FlatList } from 'react-native-gesture-handler';
+
+import {
+ type TypeaheadMatchedStrings,
+ getNewTextAndSelection,
+} from 'lib/shared/typeahead-utils';
+import type { RelativeMemberInfo } from 'lib/types/thread-types';
+
+import Button from '../components/button.react';
+import { useStyles } from '../themes/colors';
+import type { Selection } from './chat-input-bar.react';
+
+export type TypeaheadTooltipProps = {
+ +text: string,
+ +matchedStrings: TypeaheadMatchedStrings,
+ +suggestedUsers: $ReadOnlyArray<RelativeMemberInfo>,
+ +focusAndUpdateTextAndSelection: (text: string, selection: Selection) => void,
+};
+
+function TypeaheadTooltip(props: TypeaheadTooltipProps): React.Node {
+ const {
+ text,
+ matchedStrings,
+ suggestedUsers,
+ focusAndUpdateTextAndSelection,
+ } = props;
+
+ const { textBeforeAtSymbol, usernamePrefix } = matchedStrings;
+
+ const styles = useStyles(unboundStyles);
+
+ const renderTypeaheadButton = React.useCallback(
+ ({ item }: { item: RelativeMemberInfo, ... }) => {
+ const onPress = () => {
+ const { newText, newSelectionStart } = getNewTextAndSelection(
+ textBeforeAtSymbol,
+ text,
+ usernamePrefix,
+ item,
+ );
+
+ focusAndUpdateTextAndSelection(newText, {
+ start: newSelectionStart,
+ end: newSelectionStart,
+ });
+ };
+
+ return (
+ <Button onPress={onPress} style={styles.button} iosActiveOpacity={0.85}>
+ <Text style={styles.buttonLabel} numberOfLines={1}>
+ @{item.username}
+ </Text>
+ </Button>
+ );
+ },
+ [
+ focusAndUpdateTextAndSelection,
+ styles.button,
+ styles.buttonLabel,
+ text,
+ textBeforeAtSymbol,
+ usernamePrefix,
+ ],
+ );
+
+ // This is a hack that was introduced due to a buggy behavior of a
+ // absolutely positioned FlatList on Android.
+
+ // There was a bug that was present when there were too few items in a
+ // FlatList and it wasn't scrollable. It was only present on Android as
+ // iOS has a default "bounce" animation, even if the list is too short.
+ // The bug manifested itself when we tried to scroll the FlatList.
+ // Because it was unscrollable we were really scrolling FlatList
+ // below it (in the ChatList) as FlatList here has "position: absolute"
+ // and is positioned over the other FlatList.
+
+ // The hack here solves it by using a PanGestureHandler. This way Pan events
+ // on TypeaheadTooltip FlatList are always caught by handler.
+ // When the FlatList is scrollable it scrolls normally, because handler
+ // passes those events down to it.
+
+ // If it's not scrollable, the PanGestureHandler "swallows" them.
+ // Normally it would trigger onGestureEvent callback, but we don't need to
+ // handle those events. We just want them to be ignored
+ // and that's what's actually happening.
+
+ const flatList = React.useMemo(
+ () => (
+ <FlatList
+ style={styles.container}
+ contentContainerStyle={styles.contentContainer}
+ data={suggestedUsers}
+ renderItem={renderTypeaheadButton}
+ keyboardShouldPersistTaps="always"
+ />
+ ),
+ [
+ renderTypeaheadButton,
+ styles.container,
+ styles.contentContainer,
+ suggestedUsers,
+ ],
+ );
+
+ const listWithConditionalHandler = React.useMemo(() => {
+ if (Platform.OS === 'android') {
+ return <PanGestureHandler>{flatList}</PanGestureHandler>;
+ }
+ return flatList;
+ }, [flatList]);
+
+ return listWithConditionalHandler;
+}
+
+const unboundStyles = {
+ container: {
+ position: 'absolute',
+ maxHeight: 200,
+ left: 0,
+ right: 0,
+ bottom: '100%',
+ backgroundColor: 'typeaheadTooltipBackground',
+ borderBottomWidth: 1,
+ borderTopWidth: 1,
+ borderColor: 'typeaheadTooltipBorder',
+ borderStyle: 'solid',
+ },
+ contentContainer: {
+ padding: 8,
+ },
+ button: {
+ flexDirection: 'row',
+ innerHeight: 24,
+ padding: 8,
+ color: 'typeaheadTooltipText',
+ },
+ buttonLabel: {
+ color: 'white',
+ fontSize: 16,
+ fontWeight: '400',
+ },
+};
+
+export default TypeaheadTooltip;
diff --git a/native/themes/colors.js b/native/themes/colors.js
--- a/native/themes/colors.js
+++ b/native/themes/colors.js
@@ -187,6 +187,9 @@
subthreadsModalClose: '#808080',
subthreadsModalBackgroud: '#1F1F1F',
subthreadsModalSearch: 'rgba(255, 255, 255, 0.04)',
+ typeaheadTooltipBackground: '#1F1F1f',
+ typeaheadTooltipBorder: '#404040',
+ typeaheadTooltipText: 'white',
});
const colors = { light, dark };

File Metadata

Mime Type
text/plain
Expires
Tue, Dec 9, 7:44 AM (45 m, 53 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5851362
Default Alt Text
D6369.1765266299.diff (4 KB)

Event Timeline