|
@@ -27,7 +27,8 @@ import {
|
|
|
Actions,
|
|
|
isSameUser,
|
|
|
isSameDay,
|
|
|
- SystemMessage
|
|
|
+ SystemMessage,
|
|
|
+ MessageText
|
|
|
} from 'react-native-gifted-chat';
|
|
|
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
|
|
import { GestureHandlerRootView, Swipeable } from 'react-native-gesture-handler';
|
|
@@ -52,7 +53,8 @@ import {
|
|
|
usePostDeleteGroupMessageMutation,
|
|
|
usePostGetPinnedGroupMessageQuery,
|
|
|
usePostSetPinGroupMessageMutation,
|
|
|
- usePostGetGroupSettingsQuery
|
|
|
+ usePostGetGroupSettingsQuery,
|
|
|
+ usePostGetGroupMembersQuery
|
|
|
} from '@api/chat';
|
|
|
import { CustomMessage, GroupMessage, Reaction } from '../types';
|
|
|
import { API_HOST, WEBSOCKET_URL } from 'src/constants';
|
|
@@ -81,6 +83,7 @@ import GroupIcon from 'assets/icons/messages/group-chat.svg';
|
|
|
import { CACHED_ATTACHMENTS_DIR } from 'src/constants/constants';
|
|
|
import GroupStatusModal from '../Components/GroupStatusModal';
|
|
|
import PinIcon from 'assets/icons/messages/pin.svg';
|
|
|
+import MentionsList from '../Components/MentionsList';
|
|
|
|
|
|
const options = {
|
|
|
enableVibrateFallback: true,
|
|
@@ -120,12 +123,19 @@ const GroupChatScreen = ({ route }: { route: any }) => {
|
|
|
refetch: refetch,
|
|
|
isFetching: isFetching
|
|
|
} = usePostGetGroupChatQuery(token, group_token, 50, prevThenMessageId, true);
|
|
|
+ const [canSeeMembers, setCanSeeMembers] = useState(false);
|
|
|
+
|
|
|
const { data: pinData, refetch: refetchPinned } = usePostGetPinnedGroupMessageQuery(
|
|
|
token,
|
|
|
group_token,
|
|
|
true
|
|
|
);
|
|
|
const { data } = usePostGetGroupSettingsQuery(token, group_token, true);
|
|
|
+ const { data: members, refetch: refetchMembers } = usePostGetGroupMembersQuery(
|
|
|
+ token,
|
|
|
+ group_token,
|
|
|
+ canSeeMembers
|
|
|
+ );
|
|
|
|
|
|
const { mutateAsync: sendMessage } = usePostSendGroupMessageMutation();
|
|
|
|
|
@@ -176,6 +186,11 @@ const GroupChatScreen = ({ route }: { route: any }) => {
|
|
|
const updateUnreadMessagesCount = useMessagesStore((state) => state.updateUnreadMessagesCount);
|
|
|
const [insetsColor, setInsetsColor] = useState(Colors.FILL_LIGHT);
|
|
|
|
|
|
+ const [text, setText] = useState('');
|
|
|
+ const [mentionList, setMentionList] = useState<any>([]);
|
|
|
+ const [showMentions, setShowMentions] = useState(false);
|
|
|
+ const [inputHeight, setInputHeight] = useState(45);
|
|
|
+
|
|
|
const appState = useRef(AppState.currentState);
|
|
|
const textInputRef = useRef<TextInput>(null);
|
|
|
|
|
@@ -589,6 +604,12 @@ const GroupChatScreen = ({ route }: { route: any }) => {
|
|
|
|
|
|
const onShareLiveLocation = useCallback(() => {}, []);
|
|
|
|
|
|
+ useEffect(() => {
|
|
|
+ if (data && data.settings) {
|
|
|
+ setCanSeeMembers(data.settings.members_can_see_members === 1 || data.settings.admin === 1);
|
|
|
+ }
|
|
|
+ }, [data]);
|
|
|
+
|
|
|
useEffect(() => {
|
|
|
let unsubscribe: any;
|
|
|
|
|
@@ -1419,7 +1440,7 @@ const GroupChatScreen = ({ route }: { route: any }) => {
|
|
|
(newMessages: CustomMessage[] = []) => {
|
|
|
if (replyMessage) {
|
|
|
newMessages[0].replyMessage = {
|
|
|
- text: replyMessage.text,
|
|
|
+ text: transformMessageForServer(replyMessage.text),
|
|
|
id: replyMessage._id,
|
|
|
name: replyMessage.user._id !== +currentUserId ? (replyMessage.user.name as string) : 'Me'
|
|
|
};
|
|
@@ -1431,13 +1452,17 @@ const GroupChatScreen = ({ route }: { route: any }) => {
|
|
|
};
|
|
|
const message = { ...newMessages[0], pending: true, isSending: true, user };
|
|
|
|
|
|
- setMessages((previousMessages) => GiftedChat.append(previousMessages ?? [], [message]));
|
|
|
+ setMessages((previousMessages) =>
|
|
|
+ GiftedChat.append(previousMessages ?? [], [
|
|
|
+ { ...message, text: transformMessageForServer(newMessages[0].text) }
|
|
|
+ ])
|
|
|
+ );
|
|
|
|
|
|
sendMessage(
|
|
|
{
|
|
|
token,
|
|
|
to_group_token: group_token,
|
|
|
- text: message.text,
|
|
|
+ text: transformMessageForServer(message.text),
|
|
|
reply_to_id: replyMessage ? (replyMessage._id as number) : -1
|
|
|
},
|
|
|
{
|
|
@@ -1856,20 +1881,37 @@ const GroupChatScreen = ({ route }: { route: any }) => {
|
|
|
if (!chatData?.can_send_messages) return null;
|
|
|
|
|
|
return (
|
|
|
- <InputToolbar
|
|
|
- {...props}
|
|
|
- renderActions={() =>
|
|
|
- userType === 'normal' ? (
|
|
|
- <Actions
|
|
|
- icon={() => <MaterialCommunityIcons name="plus" size={28} color={Colors.DARK_BLUE} />}
|
|
|
- onPressActionButton={openAttachmentsModal}
|
|
|
- />
|
|
|
- ) : null
|
|
|
- }
|
|
|
- containerStyle={{
|
|
|
- backgroundColor: Colors.FILL_LIGHT
|
|
|
- }}
|
|
|
- />
|
|
|
+ <>
|
|
|
+ {showMentions && canSeeMembers ? (
|
|
|
+ <MentionsList
|
|
|
+ mentionList={mentionList}
|
|
|
+ inputHeight={inputHeight}
|
|
|
+ onMentionSelect={onMentionSelect}
|
|
|
+ />
|
|
|
+ ) : null}
|
|
|
+ <View
|
|
|
+ onLayout={(e) => {
|
|
|
+ setInputHeight(e.nativeEvent.layout.height);
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ <InputToolbar
|
|
|
+ {...props}
|
|
|
+ renderActions={() =>
|
|
|
+ userType === 'normal' ? (
|
|
|
+ <Actions
|
|
|
+ icon={() => (
|
|
|
+ <MaterialCommunityIcons name="plus" size={28} color={Colors.DARK_BLUE} />
|
|
|
+ )}
|
|
|
+ onPressActionButton={openAttachmentsModal}
|
|
|
+ />
|
|
|
+ ) : null
|
|
|
+ }
|
|
|
+ containerStyle={{
|
|
|
+ backgroundColor: Colors.FILL_LIGHT
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ </View>
|
|
|
+ </>
|
|
|
);
|
|
|
};
|
|
|
|
|
@@ -1897,6 +1939,42 @@ const GroupChatScreen = ({ route }: { route: any }) => {
|
|
|
return currentId === highlightedMessageId;
|
|
|
};
|
|
|
|
|
|
+ const onInputTextChanged = (value: string) => {
|
|
|
+ handleTyping(value.length > 0);
|
|
|
+
|
|
|
+ setText(value);
|
|
|
+
|
|
|
+ const mentionMatch = value.match(/(^|\s)(@\w*)$/);
|
|
|
+
|
|
|
+ if (mentionMatch) {
|
|
|
+ setShowMentions(true);
|
|
|
+ const searchText = mentionMatch[2].slice(1).toLowerCase();
|
|
|
+ setMentionList(
|
|
|
+ (members?.settings ?? [])?.filter(
|
|
|
+ (m) => m.name.toLowerCase().includes(searchText) && m.uid !== +currentUserId
|
|
|
+ )
|
|
|
+ );
|
|
|
+ } else {
|
|
|
+ setShowMentions(false);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const onMentionSelect = (member: { uid: number; name: string }) => {
|
|
|
+ const words = text.split(' ');
|
|
|
+ words[words.length - 1] = `@${member.name} `;
|
|
|
+ setText(words.join(' '));
|
|
|
+ setShowMentions(false);
|
|
|
+ };
|
|
|
+
|
|
|
+ const transformMessageForServer = (text: string) => {
|
|
|
+ let transformedText = text;
|
|
|
+ members?.settings?.forEach((member) => {
|
|
|
+ const mentionRegex = new RegExp(`@${member.name}\\b`, 'g');
|
|
|
+ transformedText = transformedText.replace(mentionRegex, `@{${member.uid}}`);
|
|
|
+ });
|
|
|
+ return transformedText;
|
|
|
+ };
|
|
|
+
|
|
|
return (
|
|
|
<SafeAreaView
|
|
|
edges={['top']}
|
|
@@ -1983,6 +2061,7 @@ const GroupChatScreen = ({ route }: { route: any }) => {
|
|
|
{messages ? (
|
|
|
<GiftedChat
|
|
|
messages={messages as CustomMessage[]}
|
|
|
+ text={text}
|
|
|
listViewProps={{
|
|
|
ref: flatList,
|
|
|
showsVerticalScrollIndicator: false,
|
|
@@ -2016,7 +2095,7 @@ const GroupChatScreen = ({ route }: { route: any }) => {
|
|
|
isCustomViewBottom={false}
|
|
|
messageContainerRef={messageContainerRef}
|
|
|
minComposerHeight={34}
|
|
|
- onInputTextChanged={(text) => handleTyping(text.length > 0)}
|
|
|
+ onInputTextChanged={onInputTextChanged}
|
|
|
textInputRef={textInputRef}
|
|
|
isTyping={isTyping ? true : false}
|
|
|
renderTypingIndicator={() => <TypingIndicator isTyping={isTyping} />}
|
|
@@ -2081,6 +2160,33 @@ const GroupChatScreen = ({ route }: { route: any }) => {
|
|
|
Clipboard.setString(url ?? '');
|
|
|
Alert.alert('Link copied');
|
|
|
}
|
|
|
+ },
|
|
|
+ {
|
|
|
+ pattern: /@\{(\d+)\}/g,
|
|
|
+ renderText: (messageText: string) => {
|
|
|
+ const tagId = messageText.slice(2, messageText.length - 1);
|
|
|
+ const user = (members?.settings ?? [])?.find((m) => m.uid === +tagId);
|
|
|
+
|
|
|
+ if (user) {
|
|
|
+ return (
|
|
|
+ <Text
|
|
|
+ style={{ color: Colors.ORANGE }}
|
|
|
+ onPress={() =>
|
|
|
+ navigation.navigate(
|
|
|
+ ...([
|
|
|
+ NAVIGATION_PAGES.PUBLIC_PROFILE_VIEW,
|
|
|
+ { userId: user.uid }
|
|
|
+ ] as never)
|
|
|
+ )
|
|
|
+ }
|
|
|
+ >
|
|
|
+ @{user.name}
|
|
|
+ </Text>
|
|
|
+ );
|
|
|
+ } else {
|
|
|
+ return messageText;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
]}
|
|
|
infiniteScroll={true}
|