|
@@ -47,7 +47,8 @@ import {
|
|
|
usePostGetChatWithQuery,
|
|
|
usePostMessagesReadMutation,
|
|
|
usePostReactToMessageMutation,
|
|
|
- usePostSendMessageMutation
|
|
|
+ usePostSendMessageMutation,
|
|
|
+ usePostEditMessageMutation
|
|
|
} from '@api/chat';
|
|
|
import { CustomMessage, Message, Reaction } from '../types';
|
|
|
import { API_HOST, APP_VERSION, WEBSOCKET_URL } from 'src/constants';
|
|
@@ -60,7 +61,7 @@ import { SheetManager } from 'react-native-actions-sheet';
|
|
|
import { NAVIGATION_PAGES } from 'src/types';
|
|
|
import { usePushNotification } from 'src/contexts/PushNotificationContext';
|
|
|
import ReactionsListModal from '../Components/ReactionsListModal';
|
|
|
-import { dismissChatNotifications } from '../utils';
|
|
|
+import { dismissChatNotifications, isMessageEdited } from '../utils';
|
|
|
import { useMessagesStore } from 'src/stores/unreadMessagesStore';
|
|
|
import FileViewer from 'react-native-file-viewer';
|
|
|
import * as FileSystem from 'expo-file-system';
|
|
@@ -141,6 +142,7 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
const { mutateAsync: markMessagesAsRead } = usePostMessagesReadMutation();
|
|
|
const { mutateAsync: deleteMessage } = usePostDeleteMessageMutation();
|
|
|
const { mutateAsync: reactToMessage } = usePostReactToMessageMutation();
|
|
|
+ const { mutateAsync: editMessage } = usePostEditMessageMutation();
|
|
|
|
|
|
const [highlightedMessageId, setHighlightedMessageId] = useState<number | null>(null);
|
|
|
const [isRerendering, setIsRerendering] = useState<boolean>(false);
|
|
@@ -154,6 +156,9 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
const [hasMoreMessages, setHasMoreMessages] = useState(true);
|
|
|
const updateUnreadMessagesCount = useMessagesStore((state) => state.updateUnreadMessagesCount);
|
|
|
|
|
|
+ const [text, setText] = useState('');
|
|
|
+ const [editingMessage, setEditingMessage] = useState<CustomMessage | null>(null);
|
|
|
+
|
|
|
const appState = useRef(AppState.currentState);
|
|
|
const textInputRef = useRef<TextInput>(null);
|
|
|
|
|
@@ -192,6 +197,7 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
},
|
|
|
pending: true,
|
|
|
isSending: true,
|
|
|
+ edited: false,
|
|
|
image: file.type === 'image' ? file.uri : undefined,
|
|
|
video: file.type === 'video' ? file.uri : undefined
|
|
|
};
|
|
@@ -262,6 +268,7 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
user: { _id: +currentUserId, name: 'Me' },
|
|
|
pending: true,
|
|
|
deleted: false,
|
|
|
+ edited: false,
|
|
|
reactions: {},
|
|
|
attachment: {
|
|
|
id: -1,
|
|
@@ -341,6 +348,7 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
deleted: false,
|
|
|
reactions: {},
|
|
|
isSending: true,
|
|
|
+ edited: false,
|
|
|
attachment: {
|
|
|
id: -1,
|
|
|
filename: file.name ?? 'File',
|
|
@@ -567,7 +575,7 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
user: { _id: +currentUserId, name: 'Me' },
|
|
|
system: false
|
|
|
};
|
|
|
- setMessages((prev) => GiftedChat.append(prev, [liveMsg]));
|
|
|
+ // setMessages((prev) => GiftedChat.append(prev, [liveMsg]));
|
|
|
}, []);
|
|
|
|
|
|
useEffect(() => {
|
|
@@ -717,6 +725,20 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
}
|
|
|
break;
|
|
|
|
|
|
+ case 'edited_message':
|
|
|
+ if (data.conversation_with === id && data.message) {
|
|
|
+ setMessages(
|
|
|
+ (prevMessages) =>
|
|
|
+ prevMessages?.map((msg) => {
|
|
|
+ if (msg._id === data.message.id) {
|
|
|
+ return { ...msg, text: data.message.text, edited: true };
|
|
|
+ }
|
|
|
+ return msg;
|
|
|
+ }) ?? []
|
|
|
+ );
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
default:
|
|
|
break;
|
|
|
}
|
|
@@ -847,6 +869,13 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
data.messages_ids = readMessagesIds;
|
|
|
}
|
|
|
|
|
|
+ if (action === 'edited_message' && message) {
|
|
|
+ data.message = {
|
|
|
+ id: message._id,
|
|
|
+ text: message.text
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
socket.current.send(JSON.stringify(data));
|
|
|
}
|
|
|
};
|
|
@@ -882,6 +911,7 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
sent: message.status === 2,
|
|
|
received: message.status === 3,
|
|
|
deleted: message.status === 4,
|
|
|
+ edited: isMessageEdited(message.edits),
|
|
|
isSending: false,
|
|
|
video:
|
|
|
message.attachement !== -1 && message.attachement?.filetype?.startsWith('video')
|
|
@@ -1020,6 +1050,17 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
|
|
|
const clearReplyMessage = () => setReplyMessage(null);
|
|
|
|
|
|
+ const clearEditMessage = () => {
|
|
|
+ setEditingMessage(null);
|
|
|
+ setText('');
|
|
|
+ };
|
|
|
+
|
|
|
+ const onInputTextChanged = (value: string) => {
|
|
|
+ handleTyping(value.length > 0);
|
|
|
+
|
|
|
+ setText(value);
|
|
|
+ };
|
|
|
+
|
|
|
const handleLongPress = (message: CustomMessage, props: BubbleProps<CustomMessage>) => {
|
|
|
const messageRef = messageRefs.current[message._id];
|
|
|
|
|
@@ -1042,8 +1083,8 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- if (spaceBelow < 160) {
|
|
|
- const extraShift = 160 - spaceBelow;
|
|
|
+ if (spaceBelow < 180) {
|
|
|
+ const extraShift = 180 - spaceBelow;
|
|
|
finalY -= extraShift;
|
|
|
}
|
|
|
|
|
@@ -1052,7 +1093,7 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
finalY += extraShift;
|
|
|
}
|
|
|
|
|
|
- if (spaceBelow < 160 || spaceAbove < 50) {
|
|
|
+ if (spaceBelow < 180 || spaceAbove < 50) {
|
|
|
const targetY = screenHeight / 2 - height / 2;
|
|
|
scrollY.value = withTiming(finalY - finalY);
|
|
|
}
|
|
@@ -1140,6 +1181,10 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
downloadFileToDevice(selectedMessage.currentMessage);
|
|
|
setIsModalVisible(false);
|
|
|
break;
|
|
|
+ case 'edit':
|
|
|
+ handleEditMessage(selectedMessage.currentMessage);
|
|
|
+ setIsModalVisible(false);
|
|
|
+ break;
|
|
|
default:
|
|
|
break;
|
|
|
}
|
|
@@ -1233,7 +1278,10 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
</TouchableOpacity>
|
|
|
)}
|
|
|
<View style={styles.timeContainer}>
|
|
|
- <Text style={styles.timeText}>{formattedTime}</Text>
|
|
|
+ {time.currentMessage.edited && <Text style={styles.timeText}>Edited</Text>}
|
|
|
+ <Text style={[styles.timeText, time.currentMessage.edited ? { paddingLeft: 0 } : {}]}>
|
|
|
+ {formattedTime}
|
|
|
+ </Text>
|
|
|
{renderTicks(time.currentMessage)}
|
|
|
</View>
|
|
|
</View>
|
|
@@ -1294,8 +1342,55 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
}, [navigation])
|
|
|
);
|
|
|
|
|
|
+ const handleEditMessage = (message: CustomMessage) => {
|
|
|
+ setReplyMessage(null);
|
|
|
+ setEditingMessage(message);
|
|
|
+ setText(message.text);
|
|
|
+ textInputRef.current?.focus();
|
|
|
+ };
|
|
|
+
|
|
|
const onSend = useCallback(
|
|
|
(newMessages: CustomMessage[] = []) => {
|
|
|
+ if (editingMessage) {
|
|
|
+ if (editingMessage.text !== newMessages[0].text) {
|
|
|
+ setMessages((prevMessages) =>
|
|
|
+ (prevMessages ?? []).map((msg) =>
|
|
|
+ msg._id === editingMessage._id ? { ...msg, text: newMessages[0].text } : msg
|
|
|
+ )
|
|
|
+ );
|
|
|
+
|
|
|
+ editMessage(
|
|
|
+ {
|
|
|
+ token,
|
|
|
+ to_uid: id,
|
|
|
+ message_id: editingMessage._id,
|
|
|
+ text: newMessages[0].text
|
|
|
+ },
|
|
|
+ {
|
|
|
+ onSuccess: (res) => {
|
|
|
+ const editedMessage = {
|
|
|
+ _id: editingMessage._id,
|
|
|
+ text: newMessages[0].text
|
|
|
+ };
|
|
|
+
|
|
|
+ setMessages((previousMessages) =>
|
|
|
+ (previousMessages ?? []).map((msg) =>
|
|
|
+ msg._id === editingMessage._id
|
|
|
+ ? { ...msg, isSending: false, edited: true }
|
|
|
+ : msg
|
|
|
+ )
|
|
|
+ );
|
|
|
+ sendWebSocketMessage('edited_message', editedMessage as unknown as CustomMessage);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ clearEditMessage();
|
|
|
+ clearReplyMessage();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
if (replyMessage) {
|
|
|
newMessages[0].replyMessage = {
|
|
|
text: replyMessage.text,
|
|
@@ -1334,7 +1429,7 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
|
|
|
clearReplyMessage();
|
|
|
},
|
|
|
- [replyMessage]
|
|
|
+ [replyMessage, editingMessage]
|
|
|
);
|
|
|
|
|
|
const addReaction = (messageId: number, reaction: string) => {
|
|
@@ -1685,7 +1780,7 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
<InputToolbar
|
|
|
{...props}
|
|
|
renderActions={() =>
|
|
|
- userType === 'normal' ? (
|
|
|
+ userType === 'normal' && !editingMessage ? (
|
|
|
<Actions
|
|
|
icon={() => <MaterialCommunityIcons name="plus" size={28} color={Colors.DARK_BLUE} />}
|
|
|
onPressActionButton={openAttachmentsModal}
|
|
@@ -1768,6 +1863,7 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
{messages ? (
|
|
|
<GiftedChat
|
|
|
messages={messages as CustomMessage[]}
|
|
|
+ text={text}
|
|
|
listViewProps={{
|
|
|
ref: flatList,
|
|
|
showsVerticalScrollIndicator: false,
|
|
@@ -1795,24 +1891,46 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
isCustomViewBottom={false}
|
|
|
messageContainerRef={messageContainerRef}
|
|
|
minComposerHeight={34}
|
|
|
- onInputTextChanged={(text) => handleTyping(text.length > 0)}
|
|
|
+ onInputTextChanged={onInputTextChanged}
|
|
|
textInputRef={textInputRef}
|
|
|
isTyping={isTyping}
|
|
|
- renderSend={(props) => (
|
|
|
- <View style={styles.sendBtn}>
|
|
|
- {props.text?.trim() && (
|
|
|
- <Send
|
|
|
- {...props}
|
|
|
- containerStyle={{
|
|
|
- justifyContent: 'center'
|
|
|
- }}
|
|
|
- >
|
|
|
- <SendIcon fill={Colors.DARK_BLUE} />
|
|
|
- </Send>
|
|
|
- )}
|
|
|
- {!props.text?.trim() && <SendIcon fill={Colors.LIGHT_GRAY} />}
|
|
|
- </View>
|
|
|
- )}
|
|
|
+ renderSend={(props) =>
|
|
|
+ editingMessage ? (
|
|
|
+ <View style={[styles.sendBtn, { paddingHorizontal: 8 }]}>
|
|
|
+ {props.text?.trim() && (
|
|
|
+ <Send
|
|
|
+ {...props}
|
|
|
+ containerStyle={{
|
|
|
+ justifyContent: 'center'
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ <View style={styles.editBtn}>
|
|
|
+ <MaterialCommunityIcons name="check" size={22} color={Colors.WHITE} />
|
|
|
+ </View>
|
|
|
+ </Send>
|
|
|
+ )}
|
|
|
+ {!props.text?.trim() && (
|
|
|
+ <View style={[styles.editBtn, { backgroundColor: Colors.LIGHT_GRAY }]}>
|
|
|
+ <MaterialCommunityIcons name="check" size={22} color={Colors.WHITE} />
|
|
|
+ </View>
|
|
|
+ )}
|
|
|
+ </View>
|
|
|
+ ) : (
|
|
|
+ <View style={styles.sendBtn}>
|
|
|
+ {props.text?.trim() && (
|
|
|
+ <Send
|
|
|
+ {...props}
|
|
|
+ containerStyle={{
|
|
|
+ justifyContent: 'center'
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ <SendIcon fill={Colors.DARK_BLUE} />
|
|
|
+ </Send>
|
|
|
+ )}
|
|
|
+ {!props.text?.trim() && <SendIcon fill={Colors.LIGHT_GRAY} />}
|
|
|
+ </View>
|
|
|
+ )
|
|
|
+ }
|
|
|
renderMessageVideo={(props) => (
|
|
|
<RenderMessageVideo
|
|
|
props={props}
|
|
@@ -1834,7 +1952,12 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
/>
|
|
|
)}
|
|
|
renderChatFooter={() => (
|
|
|
- <ReplyMessageBar clearReply={clearReplyMessage} message={replyMessage} />
|
|
|
+ <ReplyMessageBar
|
|
|
+ clearReply={clearReplyMessage}
|
|
|
+ clearEditMessage={clearEditMessage}
|
|
|
+ message={replyMessage}
|
|
|
+ editingMessage={editingMessage}
|
|
|
+ />
|
|
|
)}
|
|
|
renderAvatar={null}
|
|
|
maxComposerHeight={100}
|