|
@@ -53,7 +53,7 @@ import {
|
|
|
usePostUnreactToMessageMutation
|
|
|
} from '@api/chat';
|
|
|
import { CustomMessage, Message, Reaction } from '../types';
|
|
|
-import { API_HOST } from 'src/constants';
|
|
|
+import { API_HOST, WEBSOCKET_URL } from 'src/constants';
|
|
|
import { getFontSize } from 'src/utils';
|
|
|
import ReactionBar from '../Components/ReactionBar';
|
|
|
import OptionsMenu from '../Components/OptionsMenu';
|
|
@@ -106,11 +106,76 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
|
|
|
const [highlightedMessageId, setHighlightedMessageId] = useState<number | null>(null);
|
|
|
const [isRerendering, setIsRerendering] = useState<boolean>(false);
|
|
|
+ const [isTyping, setIsTyping] = useState<boolean>(false);
|
|
|
|
|
|
const messageRefs = useRef<{ [key: string]: any }>({});
|
|
|
const flatList = useRef<FlatList | null>(null);
|
|
|
const scrollY = useSharedValue(0);
|
|
|
|
|
|
+ const socket = useRef<WebSocket | null>(null);
|
|
|
+
|
|
|
+ useEffect(() => {
|
|
|
+ socket.current = new WebSocket(WEBSOCKET_URL);
|
|
|
+
|
|
|
+ socket.current.onopen = () => {
|
|
|
+ socket.current?.send(JSON.stringify({ token }));
|
|
|
+ };
|
|
|
+
|
|
|
+ socket.current.onmessage = (event) => {
|
|
|
+ const data = JSON.parse(event.data);
|
|
|
+ handleWebSocketMessage(data);
|
|
|
+ };
|
|
|
+
|
|
|
+ socket.current.onclose = () => {
|
|
|
+ console.log('WebSocket connection closed chat screen');
|
|
|
+ };
|
|
|
+
|
|
|
+ return () => {
|
|
|
+ socket.current?.close();
|
|
|
+ };
|
|
|
+ }, [token]);
|
|
|
+
|
|
|
+ const handleWebSocketMessage = (data: any) => {
|
|
|
+ switch (data.action) {
|
|
|
+ case 'new_message':
|
|
|
+ if (data.conversation_with === id) {
|
|
|
+ refetch();
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case 'is_typing':
|
|
|
+ if (data.conversation_with === id) {
|
|
|
+ setIsTyping(true);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case 'stopped_typing':
|
|
|
+ if (data.conversation_with === id) {
|
|
|
+ setIsTyping(false);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case 'new_reaction':
|
|
|
+ if (data.conversation_with === id) {
|
|
|
+ refetch();
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const sendWebSocketMessage = (action: string) => {
|
|
|
+ if (socket.current && socket.current.readyState === WebSocket.OPEN) {
|
|
|
+ socket.current.send(JSON.stringify({ action, conversation_with: id }));
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleTyping = (isTyping: boolean) => {
|
|
|
+ if (isTyping) {
|
|
|
+ sendWebSocketMessage('is_typing');
|
|
|
+ } else {
|
|
|
+ sendWebSocketMessage('stopped_typing');
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
const mapApiMessageToGiftedMessage = (message: Message): CustomMessage => {
|
|
|
return {
|
|
|
_id: message.id,
|
|
@@ -142,19 +207,24 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
if (chatData?.messages) {
|
|
|
const mappedMessages = chatData.messages.map(mapApiMessageToGiftedMessage);
|
|
|
|
|
|
- const firstUnreadIndex = mappedMessages.findLastIndex(
|
|
|
- (msg) => !msg.received && !msg?.deleted && msg.user._id === id
|
|
|
- );
|
|
|
- if (firstUnreadIndex !== -1) {
|
|
|
- setUnreadMessageIndex(firstUnreadIndex);
|
|
|
+ if (unreadMessageIndex === null) {
|
|
|
+ const firstUnreadIndex = mappedMessages.findLastIndex(
|
|
|
+ (msg) => !msg.received && !msg?.deleted && msg.user._id === id
|
|
|
+ );
|
|
|
+
|
|
|
+ if (firstUnreadIndex !== -1) {
|
|
|
+ setUnreadMessageIndex(firstUnreadIndex);
|
|
|
|
|
|
- const unreadMarker: any = {
|
|
|
- _id: 'unreadMarker',
|
|
|
- text: 'Unread messages',
|
|
|
- system: true
|
|
|
- };
|
|
|
+ const unreadMarker: any = {
|
|
|
+ _id: 'unreadMarker',
|
|
|
+ text: 'Unread messages',
|
|
|
+ system: true
|
|
|
+ };
|
|
|
|
|
|
- mappedMessages.splice(firstUnreadIndex, 0, unreadMarker);
|
|
|
+ mappedMessages.splice(firstUnreadIndex + 1, 0, unreadMarker);
|
|
|
+ } else {
|
|
|
+ setUnreadMessageIndex(0);
|
|
|
+ }
|
|
|
}
|
|
|
setMessages(mappedMessages);
|
|
|
}
|
|
@@ -185,6 +255,8 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
{
|
|
|
onSuccess: (res) => {
|
|
|
newViewableUnreadMessages.forEach((id) => sentToServer.current.add(id));
|
|
|
+ // sendWebSocketMessage('messages_read');
|
|
|
+ sendWebSocketMessage('new_message');
|
|
|
}
|
|
|
}
|
|
|
);
|
|
@@ -276,6 +348,8 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
{
|
|
|
onSuccess: () => {
|
|
|
setMessages((prevMessages) => prevMessages.filter((msg) => msg._id !== messageId));
|
|
|
+ // sendWebSocketMessage('message_deleted');
|
|
|
+ sendWebSocketMessage('new_message');
|
|
|
}
|
|
|
}
|
|
|
);
|
|
@@ -330,6 +404,7 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
return msg;
|
|
|
})
|
|
|
);
|
|
|
+ sendWebSocketMessage('new_reaction');
|
|
|
}
|
|
|
}
|
|
|
);
|
|
@@ -484,14 +559,6 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
}, [navigation])
|
|
|
);
|
|
|
|
|
|
- useEffect(() => {
|
|
|
- const intervalId = setInterval(() => {
|
|
|
- refetch();
|
|
|
- }, 5000);
|
|
|
-
|
|
|
- return () => clearInterval(intervalId);
|
|
|
- }, [refetch]);
|
|
|
-
|
|
|
const onSend = useCallback(
|
|
|
(newMessages: CustomMessage[] = []) => {
|
|
|
if (replyMessage) {
|
|
@@ -511,7 +578,7 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
reply_to_id: replyMessage ? (replyMessage._id as number) : -1
|
|
|
},
|
|
|
{
|
|
|
- onSuccess: (res) => console.log('res', res),
|
|
|
+ onSuccess: () => sendWebSocketMessage('new_message'),
|
|
|
onError: (err) => console.log('err', err)
|
|
|
}
|
|
|
);
|
|
@@ -644,7 +711,7 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
reactToMessage(
|
|
|
{ token, message_id: messageId, reaction: reaction, conversation_with_user: id },
|
|
|
{
|
|
|
- onSuccess: (res) => console.log('res', res),
|
|
|
+ onSuccess: () => sendWebSocketMessage('new_reaction'),
|
|
|
onError: (err) => console.log('err', err)
|
|
|
}
|
|
|
);
|
|
@@ -978,6 +1045,8 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
isCustomViewBottom={false}
|
|
|
messageContainerRef={messageContainerRef}
|
|
|
minComposerHeight={34}
|
|
|
+ onInputTextChanged={(text) => handleTyping(text.length > 0)}
|
|
|
+ isTyping={isTyping}
|
|
|
renderSend={(props) => (
|
|
|
<View
|
|
|
style={{
|
|
@@ -1040,8 +1109,6 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
}
|
|
|
}
|
|
|
]}
|
|
|
- // inverted={true}
|
|
|
- // isTyping={true}
|
|
|
/>
|
|
|
|
|
|
<Modal visible={!!selectedMedia} transparent={true}>
|