Skip to content

Commit 3884a6a

Browse files
committed
Implemented client for fetching /api/chats/message
1 parent eec0149 commit 3884a6a

File tree

6 files changed

+126
-22
lines changed

6 files changed

+126
-22
lines changed

frontend/src/features/chat/api/chatApi.ts

+29-4
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,45 @@
11
import { axiosInstance } from '@lib/axios';
22
import { AxiosError } from 'axios';
33
import { ChatMessagesResponse } from '../types';
4+
import { authTokenKey } from '@config';
45

56
// Function to fetch chat messages with proper types
67
export async function fetchChatMessagesFn(
78
chatRoomId: string,
89
pageParam?: string // Make pageParam optional as it may not be provided for the initial request
910
): Promise<ChatMessagesResponse> {
10-
const chatMessagesURL = `/api/chats/${chatRoomId}/messages?cursor=${
11-
pageParam ?? ''
12-
}`;
11+
// Parse the string into a Date object
12+
const dateTimeString = '2023-11-09 21:06:13.567';
13+
const date = new Date(dateTimeString);
1314

15+
// Format the Date object as an ISO 8601 string (commonly used in APIs)
16+
const isoDateString = date.toISOString();
17+
const chatMessagesURL = `/api/chats/${chatRoomId}/messages?cursor=${isoDateString}`;
18+
19+
console.log(isoDateString); // Outputs: 2023-11-09T21:06:13.567Z (note the 'Z' indicating UTC)
20+
21+
const supabaseData = localStorage.getItem(authTokenKey);
22+
23+
let accessToken = '';
24+
25+
if (supabaseData) {
26+
const parsedData = JSON.parse(supabaseData);
27+
accessToken = parsedData.access_token;
28+
} else {
29+
console.error('No Supabase data found in Local Storage');
30+
}
1431
try {
32+
console.log('Token = ', accessToken);
1533
const { data } = await axiosInstance.get<ChatMessagesResponse>(
16-
chatMessagesURL
34+
chatMessagesURL,
35+
{
36+
headers: {
37+
'Content-Type': 'application/json',
38+
Authorization: `Bearer ${accessToken}`,
39+
},
40+
}
1741
);
42+
1843
return data;
1944
} catch (error) {
2045
const axiosError = error as AxiosError;
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
import React, { useRef, useState } from 'react';
21
import styles from './ChatArea.module.scss';
32
import { Message } from './Message';
43
import { useChatMessageStore } from '@stores';
5-
import { MessageType } from '../types';
4+
import { MessageType, ServerMessageType } from '../types';
5+
import { useChatMessages } from '@hooks';
6+
import { useEffect } from 'react';
7+
import { authTokenKey } from '@config';
68

79
type ChatAreaProps = {
810
messages: MessageType[];
@@ -12,16 +14,67 @@ export function ChatArea({ messages: propMessages }: Readonly<ChatAreaProps>) {
1214
const { messages: storeMessages } = useChatMessageStore((state) => ({
1315
messages: state.messages,
1416
}));
15-
const [loading, setLoading] = useState(false);
16-
const [cursor, setCursor] = useState<string | null>(null);
17-
const chatAreaRef = useRef<HTMLDivElement>(null); // Ref for the chat area container */
17+
// Call the custom hook and pass the chatRoomId to it
18+
const chatRoomId = '25e4eb83-5210-448d-be58-3a4c355113be';
19+
const {
20+
data,
21+
error,
22+
fetchNextPage,
23+
hasNextPage,
24+
isFetchingNextPage,
25+
status,
26+
} = useChatMessages(chatRoomId);
27+
useEffect(() => {
28+
console.log('Data loaded', data?.pages[0].messages);
29+
}, [data]);
1830

31+
if (status === 'loading') return <p>Loading...</p>;
32+
if (status === 'error') return <p>Error: {error.message}</p>;
33+
34+
const supabaseData = localStorage.getItem(authTokenKey);
35+
36+
let currentUserId = '';
37+
38+
if (supabaseData) {
39+
const parsedData = JSON.parse(supabaseData);
40+
currentUserId = parsedData.user.id;
41+
console.log('Current user id', currentUserId);
42+
} else {
43+
console.error('No Supabase data found in Local Storage');
44+
}
45+
46+
const restAPIMessages: ServerMessageType[] =
47+
data?.pages.flatMap((page) =>
48+
page.messages.map((message: unknown) => {
49+
// Assert the message to be of type ServerMessageType
50+
const serverMessage = message as unknown as ServerMessageType;
51+
52+
const adaptedMessage: ServerMessageType = {
53+
id: serverMessage.id,
54+
chat_room_id: serverMessage.chat_room_id,
55+
sender:
56+
serverMessage.sender_id === currentUserId ? 'sent' : 'received', // Set the sender based on the current user's ID
57+
media_url: serverMessage.media_url,
58+
created_at: serverMessage.created_at,
59+
content: serverMessage.content,
60+
sender_id: serverMessage.sender_id,
61+
};
62+
63+
return adaptedMessage;
64+
})
65+
) ?? [];
66+
67+
const combinedMessages = [...storeMessages, ...propMessages].sort(
68+
(a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()
69+
);
1970
return (
2071
<div className={styles.chatArea}>
21-
{[...storeMessages, ...propMessages].map((message, timestamp) => (
22-
<Message key={timestamp} {...message} text={message.content} />
72+
{restAPIMessages.map((message) => (
73+
<Message {...message} sender={message.sender} text={message.content} />
74+
))}
75+
{combinedMessages.map((message) => (
76+
<Message {...message} text={message.content} />
2377
))}
24-
{loading && <div>Loading more messages...</div>}
2578
</div>
2679
);
2780
}

frontend/src/features/chat/components/Message.tsx

+1-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React from 'react';
22
import styles from './Message.module.scss';
33

44
type MessageProps = {
5-
sender: 'received' | 'sent';
5+
sender: string;
66
text: string;
77
img?: string;
88
};
@@ -14,9 +14,6 @@ export const Message = ({ sender, text, img }: MessageProps) => {
1414
sender === 'received' ? styles.messageReceived : styles.messageSent
1515
}
1616
>
17-
{sender === 'received' && (
18-
<img src={img} alt="Sender's Profile" className="profileImage" />
19-
)}
2017
<p>{text}</p>
2118
</div>
2219
);

frontend/src/features/chat/types/index.ts

+15-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,24 @@
11
export type MessageType = {
22
sender: 'received' | 'sent';
3+
senderID: string;
34
content: string;
4-
timestamp: Date;
5+
createdAt: Date;
56
img?: string; // Optional image URL for the sender's profile (for 'received' messages)
67
};
78

9+
// A type for the incoming message that reflects the server's response
10+
export type ServerMessageType = {
11+
id: number;
12+
chat_room_id: string;
13+
sender_id: string;
14+
content: string;
15+
media_url: string;
16+
created_at: Date;
17+
sender: 'received' | 'sent';
18+
};
19+
20+
export type CombinedMessageType = ChatMessage & ServerMessageType;
21+
822
// TypeScript interfaces to represent the API response structure
923
export interface ChatMessage {
1024
id: number;

frontend/src/hooks/useWebSocketConnection.ts

+17-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { WEBSOCKET_URL, authTokenKey } from '@config';
2-
import { MessageType } from '@features/chat';
2+
import { MessageType, ServerMessageType } from '@features/chat';
33
import { useChatMessageStore } from '@stores';
44
import { useQueryClient } from '@tanstack/react-query';
55
import { useEffect, useRef, useState } from 'react';
@@ -16,7 +16,8 @@ export const useWebSocketConnection = () => {
1616
const sendMessage = (message: string) => {
1717
if (webSocket.current) {
1818
console.log('Sending message:', message);
19-
webSocket.current.send(message);
19+
20+
webSocket.current.send(message); // serialize the object to a JSON string
2021
}
2122
};
2223

@@ -47,7 +48,20 @@ export const useWebSocketConnection = () => {
4748
};
4849

4950
const handleWebSocketOnMessage = (event: MessageEvent) => {
50-
const receivedMessage: MessageType = JSON.parse(event.data);
51+
const serverMessage: ServerMessageType = JSON.parse(event.data);
52+
53+
// Convert the snake_case properties from the server message to camelCase for the MessageType
54+
const receivedMessage: MessageType = {
55+
sender: 'received',
56+
senderID: serverMessage.sender_id,
57+
content: serverMessage.content,
58+
createdAt: new Date(serverMessage.created_at),
59+
img: serverMessage.media_url || undefined,
60+
};
61+
62+
console.log('Receiving messages..', receivedMessage);
63+
64+
// receivedMessage.sender = 'received';
5165

5266
addMessage(receivedMessage);
5367
};

frontend/src/pages/LobbyPage/Lobby.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import { ChatArea, InputArea, MessageType } from '@features/chat';
2-
import React, { useState } from 'react';
2+
import { useState } from 'react';
33

44
export function Lobby() {
55
const [messages, setMessages] = useState<MessageType[]>([]);
66

77
const handleNewMessage = (text: string) => {
88
const newMessage: MessageType = {
99
sender: 'sent',
10+
senderID: '1', // TODO: replace with actual user ID
1011
content: text,
11-
timestamp: new Date(),
12+
createdAt: new Date(),
1213
};
1314
setMessages((prevMessages) => [...prevMessages, newMessage]);
1415
};

0 commit comments

Comments
 (0)