Skip to content

Commit 1e421f4

Browse files
committed
fix: refine the implementation
1 parent a00a670 commit 1e421f4

File tree

9 files changed

+194
-260
lines changed

9 files changed

+194
-260
lines changed

package/src/components/AutoCompleteInput/AutoCompleteInput.tsx

Lines changed: 79 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
1-
import React, { useEffect, useState } from 'react';
2-
import { I18nManager, StyleSheet, TextInput, TextInputProps } from 'react-native';
1+
import React, { useCallback, useEffect, useMemo, useState } from 'react';
2+
import {
3+
I18nManager,
4+
NativeSyntheticEvent,
5+
StyleSheet,
6+
TextInput,
7+
TextInputContentSizeChangeEventData,
8+
TextInputSelectionChangeEventData,
9+
} from 'react-native';
310

411
import { TextComposerState } from 'stream-chat';
512

@@ -34,6 +41,7 @@ type AutoCompleteInputPropsWithContext = Pick<
3441
| 'giphyEnabled'
3542
| 'maxMessageLength'
3643
| 'numberOfLines'
44+
| 'onChangeText'
3745
| 'setGiphyActive'
3846
| 'setInputBoxRef'
3947
> &
@@ -59,44 +67,70 @@ const AutoCompleteInputWithContext = (props: AutoCompleteInputPropsWithContext)
5967
giphyEnabled,
6068
maxMessageLength,
6169
numberOfLines,
70+
onChangeText,
6271
setGiphyActive,
6372
setInputBoxRef,
6473
t,
6574
} = props;
6675
const [localText, setLocalText] = useState('');
6776
const [selection, setSelection] = useState({ end: 0, start: 0 });
77+
const [textHeight, setTextHeight] = useState(0);
6878
const messageComposer = useMessageComposer();
79+
const { textComposer } = messageComposer;
6980

70-
const { text } = useStateStore(messageComposer.textComposer.state, messageComposerStateSelector);
81+
const { text } = useStateStore(textComposer.state, messageComposerStateSelector);
7182

7283
useEffect(() => {
7384
setLocalText(text);
7485
}, [text]);
7586

76-
const handleSelectionChange: TextInputProps['onSelectionChange'] = ({
77-
nativeEvent: { selection },
78-
}) => {
79-
setSelection(selection);
80-
};
81-
82-
const onTextChangeHandler = async (newText: string) => {
83-
setLocalText(newText);
84-
const isGiphy = giphyEnabled && newText && newText.startsWith('/giphy ');
85-
86-
if (isGiphy) {
87-
setGiphyActive(true);
88-
}
89-
90-
await messageComposer.textComposer.handleChange({
91-
selection: {
92-
end: selection.end + 1,
93-
start: selection.start + 1,
94-
},
95-
text: isGiphy ? newText.slice(7) : newText,
96-
});
97-
};
87+
const handleSelectionChange = useCallback(
88+
(e: NativeSyntheticEvent<TextInputSelectionChangeEventData>) => {
89+
const { selection } = e.nativeEvent;
90+
if (additionalTextInputProps?.onSelectionChange) {
91+
additionalTextInputProps.onSelectionChange(e);
92+
return;
93+
}
94+
setSelection(selection);
95+
},
96+
[additionalTextInputProps],
97+
);
9898

99-
const [textHeight, setTextHeight] = useState(0);
99+
const onTextChangeHandler = useCallback(
100+
async (newText: string) => {
101+
if (onChangeText) {
102+
onChangeText(newText);
103+
return;
104+
}
105+
if (additionalTextInputProps?.onChangeText) {
106+
additionalTextInputProps.onChangeText(newText);
107+
return;
108+
}
109+
setLocalText(newText);
110+
const isGiphy = giphyEnabled && newText && newText.startsWith('/giphy ');
111+
112+
if (isGiphy) {
113+
setGiphyActive(true);
114+
}
115+
116+
await textComposer.handleChange({
117+
selection: {
118+
end: selection.end + 1,
119+
start: selection.start + 1,
120+
},
121+
text: isGiphy ? newText.slice(7) : newText,
122+
});
123+
},
124+
[
125+
additionalTextInputProps,
126+
giphyEnabled,
127+
onChangeText,
128+
textComposer,
129+
selection.end,
130+
selection.start,
131+
setGiphyActive,
132+
],
133+
);
100134

101135
const {
102136
theme: {
@@ -105,27 +139,30 @@ const AutoCompleteInputWithContext = (props: AutoCompleteInputPropsWithContext)
105139
},
106140
} = useTheme();
107141

108-
const placeholderText = giphyActive
109-
? t('Search GIFs')
110-
: cooldownActive
111-
? t('Slow mode ON')
112-
: t('Send a message');
142+
const placeholderText = useMemo(() => {
143+
return giphyActive
144+
? t('Search GIFs')
145+
: cooldownActive
146+
? t('Slow mode ON')
147+
: t('Send a message');
148+
}, [cooldownActive, giphyActive, t]);
149+
150+
const handleContentSizeChange = useCallback(
151+
({
152+
nativeEvent: { contentSize },
153+
}: NativeSyntheticEvent<TextInputContentSizeChangeEventData>) => {
154+
setTextHeight(contentSize.height);
155+
},
156+
[],
157+
);
113158

114159
return (
115160
<TextInput
116161
autoFocus={giphyActive}
117162
maxLength={maxMessageLength}
118163
multiline
119164
onChangeText={onTextChangeHandler}
120-
onContentSizeChange={({
121-
nativeEvent: {
122-
contentSize: { height },
123-
},
124-
}) => {
125-
if (!textHeight) {
126-
setTextHeight(height);
127-
}
128-
}}
165+
onContentSizeChange={handleContentSizeChange}
129166
onSelectionChange={handleSelectionChange}
130167
placeholder={placeholderText}
131168
placeholderTextColor={grey}
@@ -183,6 +220,7 @@ export const AutoCompleteInput = (props: AutoCompleteInputProps) => {
183220
giphyActive,
184221
maxMessageLength,
185222
numberOfLines,
223+
onChangeText,
186224
setGiphyActive,
187225
setInputBoxRef,
188226
} = useMessageInputContext();
@@ -196,6 +234,7 @@ export const AutoCompleteInput = (props: AutoCompleteInputProps) => {
196234
giphyEnabled,
197235
maxMessageLength,
198236
numberOfLines,
237+
onChangeText,
199238
setGiphyActive,
200239
setInputBoxRef,
201240
t,

package/src/components/AutoCompleteInput/AutoCompleteSuggestionHeader.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import { Lightning } from '../../icons/Lightning';
88
import { Smile } from '../../icons/Smile';
99

1010
export type AutoCompleteSuggestionHeaderProps = {
11-
queryText: string;
12-
triggerType: string;
11+
queryText?: string;
12+
triggerType?: string;
1313
};
1414

1515
const styles = StyleSheet.create({

package/src/components/AutoCompleteInput/AutoCompleteSuggestionItem.tsx

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

44
import { CommandSuggestion, TextComposerSuggestion, UserSuggestion } from 'stream-chat';
55

66
import { AutoCompleteSuggestionCommandIcon } from './AutoCompleteSuggestionCommandIcon';
77

8+
import { useMessageComposer } from '../../contexts/messageInputContext/hooks/useMessageComposer';
89
import { useTheme } from '../../contexts/themeContext/ThemeContext';
910
import type { Emoji } from '../../emoji-data';
1011
import { AtMentions } from '../../icons/AtMentions';
@@ -13,7 +14,7 @@ import { Avatar } from '../Avatar/Avatar';
1314

1415
export type AutoCompleteSuggestionItemProps = {
1516
itemProps: TextComposerSuggestion;
16-
triggerType: string;
17+
triggerType?: string;
1718
};
1819

1920
export const MentionSuggestionItem = (item: UserSuggestion) => {
@@ -91,22 +92,44 @@ const CommandSuggestionItem = (item: CommandSuggestion) => {
9192
);
9293
};
9394

94-
export const AutoCompleteSuggestionItem = ({
95-
itemProps,
96-
triggerType,
97-
}: AutoCompleteSuggestionItemProps) => {
95+
const renderSuggestionItem = (item: TextComposerSuggestion, triggerType?: string) => {
9896
switch (triggerType) {
9997
case 'mention':
100-
return <MentionSuggestionItem {...(itemProps as UserSuggestion)} />;
98+
return <MentionSuggestionItem {...(item as UserSuggestion)} />;
10199
case 'emoji':
102-
return <EmojiSuggestionItem {...(itemProps as Emoji)} />;
100+
return <EmojiSuggestionItem {...(item as Emoji)} />;
103101
case 'command':
104-
return <CommandSuggestionItem {...itemProps} />;
102+
return <CommandSuggestionItem {...(item as CommandSuggestion)} />;
105103
default:
106104
return null;
107105
}
108106
};
109107

108+
export const AutoCompleteSuggestionItem = ({
109+
itemProps,
110+
triggerType,
111+
}: AutoCompleteSuggestionItemProps) => {
112+
const messageComposer = useMessageComposer();
113+
const { textComposer } = messageComposer;
114+
115+
const {
116+
theme: {
117+
messageInput: {
118+
suggestions: { item: itemStyle },
119+
},
120+
},
121+
} = useTheme();
122+
123+
return (
124+
<Pressable
125+
onPress={() => textComposer.handleSelect(itemProps)}
126+
style={({ pressed }) => [{ opacity: pressed ? 0.2 : 1 }, itemStyle]}
127+
>
128+
{renderSuggestionItem(itemProps, triggerType)}
129+
</Pressable>
130+
);
131+
};
132+
110133
const styles = StyleSheet.create({
111134
args: {
112135
fontSize: 14,

0 commit comments

Comments
 (0)