From 1cde39461ad965b71983629a1aa9f2b38cba6a67 Mon Sep 17 00:00:00 2001 From: Simon Huang Date: Sun, 16 Aug 2020 19:12:11 -0400 Subject: [PATCH] set up listener to retrieve messages fixed listener unsubscriptions in Room.tsx --- src/components/Chat.css | 17 +++--- src/components/Chat.tsx | 113 ++++++++++++++++++++++++++++------------ src/pages/Room.tsx | 16 +++--- 3 files changed, 101 insertions(+), 45 deletions(-) diff --git a/src/components/Chat.css b/src/components/Chat.css index a6d0288..2427fdf 100644 --- a/src/components/Chat.css +++ b/src/components/Chat.css @@ -1,9 +1,3 @@ -ion-grid { - border-left: solid 1px #bbb; - border-right: solid 1px #bbb; - height: 100%; -} - ion-col { padding: 10px; border-radius: 10px; @@ -20,6 +14,17 @@ ion-textarea { 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 { text-align: right; background: var(--ion-color-primary); diff --git a/src/components/Chat.tsx b/src/components/Chat.tsx index e402a2a..c7f5d3b 100644 --- a/src/components/Chat.tsx +++ b/src/components/Chat.tsx @@ -1,6 +1,6 @@ import { IonButton, IonCol, IonContent, IonFooter, IonGrid, IonRow, IonTextarea } from '@ionic/react'; -import React, { useState } from 'react'; -import { db, timestamp } from '../services/firebase'; +import React, { useEffect, useState } from 'react'; +import { currTime, db, timestamp } from '../services/firebase'; import './Chat.css'; type ChatProps = { @@ -8,53 +8,102 @@ type ChatProps = { userId: string; }; +type Message = { + id: string; + senderId: string; + sender: string; + content: string; +}; + const Chat: React.FC = ({ roomId, userId }) => { - const [message, setMessage] = useState(''); + const [room] = useState(roomId); + const [message, setMessage] = useState(''); // Message to be sent + const [prevMessages, setPrevMessages] = useState([]); // Track previous messages for updating useEffect + const [newMessages, setNewMessages] = useState([]); // Newly retrieved messages + const [chats, setChats] = useState([ + { 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 messageId = await db.collection('rooms').doc(roomId).collection('messages').add({ + await db.collection('rooms').doc(roomId).collection('messages').add({ createdAt: timestamp, senderId: userId, content: message, }); - console.log(messageId); + + setMessage(''); }; return ( <> - - - - Simon: - Hello! - - - - - Manuel: - They are all asking me about what I'm going to do next - - - - - Amaria: - Probably riding on - - - - - Simon: - my photo jet - - + + {!loading ? ( + chats.map((chat) => { + return ( + + + {chat.sender !== '' ? {chat.sender}: : <>} + {chat.content} + + + ); + }) + ) : ( + <> + )} - + - setMessage(e.detail.value!)}> + setMessage(e.detail.value!)} value={message}> diff --git a/src/pages/Room.tsx b/src/pages/Room.tsx index bc3de26..d1ee778 100644 --- a/src/pages/Room.tsx +++ b/src/pages/Room.tsx @@ -13,7 +13,6 @@ const Room: React.FC> = ({ match }) => { const [userId, setUserId] = useState(''); const [loading, setLoading] = useState(true); const [userCount, setUserCount] = useState(0); - const [didConnect, setDidConnect] = useState(false); // Verify that the roomId exists in db useEffect(() => { @@ -49,7 +48,7 @@ const Room: React.FC> = ({ match }) => { // Subscribe listeners useEffect(() => { - if (!didConnect && userId !== '' && validRoom) { + if (userId !== '' && validRoom) { const populateRoom = () => { const roomRef = rtdb.ref('/rooms/' + roomId); const availableRef = rtdb.ref('/available/'); @@ -68,7 +67,7 @@ const Room: React.FC> = ({ match }) => { }); // 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)) { await availableRef.child(roomId).set({ name: 'Room Name', @@ -77,7 +76,7 @@ const Room: React.FC> = ({ match }) => { } }); - setLoading(false); // Ready when connection to rtdb is made + setLoading(false); // Ready when connections to databases are made // Unsubscribe listeners return () => { @@ -87,10 +86,13 @@ const Room: React.FC> = ({ match }) => { }; }; - populateRoom(); - setDidConnect(true); // Run this useEffect only once + const unsub = populateRoom(); + + return () => { + unsub(); + }; } - }, [userId, validRoom, roomId, userCount, loading, didConnect]); + }, [userId, validRoom, roomId]); // Handle disconnect events useEffect(() => {