set up listener to retrieve messages

fixed listener unsubscriptions in Room.tsx
pull/3/head
Simon Huang 5 years ago
parent 4c5ccc0afb
commit 1cde39461a

@ -1,9 +1,3 @@
ion-grid {
border-left: solid 1px #bbb;
border-right: solid 1px #bbb;
height: 100%;
}
ion-col { ion-col {
padding: 10px; padding: 10px;
border-radius: 10px; border-radius: 10px;
@ -20,6 +14,17 @@ ion-textarea {
justify-content: flex-end; justify-content: flex-end;
} }
.message-grid {
padding-left: calc(100vw - 100%);
margin-right: 0;
height: 100%;
}
.message-input {
margin-right: 0;
border: solid 1px #bbb;
}
.my-msg { .my-msg {
text-align: right; text-align: right;
background: var(--ion-color-primary); background: var(--ion-color-primary);

@ -1,6 +1,6 @@
import { IonButton, IonCol, IonContent, IonFooter, IonGrid, IonRow, IonTextarea } from '@ionic/react'; import { IonButton, IonCol, IonContent, IonFooter, IonGrid, IonRow, IonTextarea } from '@ionic/react';
import React, { useState } from 'react'; import React, { useEffect, useState } from 'react';
import { db, timestamp } from '../services/firebase'; import { currTime, db, timestamp } from '../services/firebase';
import './Chat.css'; import './Chat.css';
type ChatProps = { type ChatProps = {
@ -8,53 +8,102 @@ type ChatProps = {
userId: string; userId: string;
}; };
type Message = {
id: string;
senderId: string;
sender: string;
content: string;
};
const Chat: React.FC<ChatProps> = ({ roomId, userId }) => { const Chat: React.FC<ChatProps> = ({ roomId, userId }) => {
const [message, setMessage] = useState(''); const [room] = useState(roomId);
const [message, setMessage] = useState(''); // Message to be sent
const [prevMessages, setPrevMessages] = useState<Message[]>([]); // Track previous messages for updating useEffect
const [newMessages, setNewMessages] = useState<Message[]>([]); // Newly retrieved messages
const [chats, setChats] = useState<Message[]>([
{ id: '', senderId: userId, sender: '', content: 'You have joined the room.' },
]); // All received messages
const [loading, setLoading] = useState(true);
// Listen for new messages
useEffect(() => {
const chatUnsubscribe = db
.collection('rooms')
.doc(room)
.collection('messages')
.orderBy('createdAt')
.where('createdAt', '>', currTime)
.onSnapshot(async (querySnapshot) => {
let newMsgs: Message[] = [];
const changes = querySnapshot.docChanges();
for (const change of changes) {
if (change.type === 'added') {
const data = change.doc.data();
const user = await db.collection('users').doc(data?.senderId).get();
newMsgs.push({
id: change.doc.id,
senderId: data?.senderId,
sender: user.data()?.name,
content: data?.content,
});
}
}
if (newMsgs.length !== 0) {
setNewMessages(newMsgs);
}
});
setLoading(false);
return () => {
chatUnsubscribe();
};
}, [room]);
// Only update array containing all messages when there are new messages
useEffect(() => {
if (prevMessages !== newMessages) {
setPrevMessages(newMessages);
setChats([...chats, ...newMessages]);
}
}, [prevMessages, newMessages, chats]);
// Send message to database and reset textarea field
const sendMessage = async () => { const sendMessage = async () => {
const messageId = await db.collection('rooms').doc(roomId).collection('messages').add({ await db.collection('rooms').doc(roomId).collection('messages').add({
createdAt: timestamp, createdAt: timestamp,
senderId: userId, senderId: userId,
content: message, content: message,
}); });
console.log(messageId);
setMessage('');
}; };
return ( return (
<> <>
<IonContent> <IonContent>
<IonGrid> <IonGrid class="message-grid">
<IonRow class="right-align"> {!loading ? (
<IonCol size="auto" class="my-msg"> chats.map((chat) => {
<b>Simon: </b> return (
<span>Hello!</span> <IonRow key={chat.id} class={chat.senderId === userId ? 'right-align' : ''}>
</IonCol> <IonCol size="auto" class={chat.senderId === userId ? 'my-msg' : 'other-msg'}>
</IonRow> {chat.sender !== '' ? <b>{chat.sender}: </b> : <></>}
<IonRow> <span>{chat.content}</span>
<IonCol size="auto" class="other-msg"> </IonCol>
<b>Manuel: </b> </IonRow>
<span>They are all asking me about what I'm going to do next</span> );
</IonCol> })
</IonRow> ) : (
<IonRow> <></>
<IonCol size="auto" class="other-msg"> )}
<b>Amaria: </b>
<span>Probably riding on</span>
</IonCol>
</IonRow>
<IonRow class="right-align">
<IonCol size="auto" class="my-msg">
<b>Simon: </b>
<span>my photo jet</span>
</IonCol>
</IonRow>
</IonGrid> </IonGrid>
</IonContent> </IonContent>
<IonFooter class="ion-no-border"> <IonFooter class="ion-no-border">
<IonGrid> <IonGrid class="message-input">
<IonRow> <IonRow>
<IonCol size="9"> <IonCol size="9">
<IonTextarea onIonChange={(e) => setMessage(e.detail.value!)}></IonTextarea> <IonTextarea onIonChange={(e) => setMessage(e.detail.value!)} value={message}></IonTextarea>
</IonCol> </IonCol>
<IonCol size="3" class="send-msg"> <IonCol size="3" class="send-msg">
<IonButton expand="block" color="primary" onClick={sendMessage} class="send-button"> <IonButton expand="block" color="primary" onClick={sendMessage} class="send-button">

@ -13,7 +13,6 @@ const Room: React.FC<RouteComponentProps<{ roomId: string }>> = ({ match }) => {
const [userId, setUserId] = useState(''); const [userId, setUserId] = useState('');
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [userCount, setUserCount] = useState(0); const [userCount, setUserCount] = useState(0);
const [didConnect, setDidConnect] = useState(false);
// Verify that the roomId exists in db // Verify that the roomId exists in db
useEffect(() => { useEffect(() => {
@ -49,7 +48,7 @@ const Room: React.FC<RouteComponentProps<{ roomId: string }>> = ({ match }) => {
// Subscribe listeners // Subscribe listeners
useEffect(() => { useEffect(() => {
if (!didConnect && userId !== '' && validRoom) { if (userId !== '' && validRoom) {
const populateRoom = () => { const populateRoom = () => {
const roomRef = rtdb.ref('/rooms/' + roomId); const roomRef = rtdb.ref('/rooms/' + roomId);
const availableRef = rtdb.ref('/available/'); const availableRef = rtdb.ref('/available/');
@ -68,7 +67,7 @@ const Room: React.FC<RouteComponentProps<{ roomId: string }>> = ({ match }) => {
}); });
// Re-add room into /available/ if the room was deleted // Re-add room into /available/ if the room was deleted
availableRef.on('child_removed', async (snapshot) => { availableRef.on('value', async (snapshot) => {
if (!snapshot.hasChild(roomId)) { if (!snapshot.hasChild(roomId)) {
await availableRef.child(roomId).set({ await availableRef.child(roomId).set({
name: 'Room Name', name: 'Room Name',
@ -77,7 +76,7 @@ const Room: React.FC<RouteComponentProps<{ roomId: string }>> = ({ match }) => {
} }
}); });
setLoading(false); // Ready when connection to rtdb is made setLoading(false); // Ready when connections to databases are made
// Unsubscribe listeners // Unsubscribe listeners
return () => { return () => {
@ -87,10 +86,13 @@ const Room: React.FC<RouteComponentProps<{ roomId: string }>> = ({ match }) => {
}; };
}; };
populateRoom(); const unsub = populateRoom();
setDidConnect(true); // Run this useEffect only once
return () => {
unsub();
};
} }
}, [userId, validRoom, roomId, userCount, loading, didConnect]); }, [userId, validRoom, roomId]);
// Handle disconnect events // Handle disconnect events
useEffect(() => { useEffect(() => {

Loading…
Cancel
Save