Adding Chat with firebase

Again I will be using firebase to demonstrate a proof of concept. This time we are building a chat box, or rather I have done so already. The chat can be seen in operation if you log into your account and navigate to the chat page from within your account page. Currently the chat box posts messages along with the user name and a time stamp. It displays messages no older than six hours and up to fifty of them, this as a personal preference. There are a few more subtleties to it which you can grasp from the code. This is pretty much a ready to copy and paste implementation, as long as you have configured your firebase correctly. I am yet to do a post on firebase alone, but you can grasp enough information about it from my other posts and the official documentation to implement the required functionality. You might have to change the directories of user information used below, depending on your setup. 

HTML:

<div id="chatboxMainContainer" class="chatbox-main-container">
<div id="chatboxContainer" class="chatbox-container">
<div class="chatbox-messages" id="chatboxMessages">
</div>
<div class="chatbox-input">
<p class="chatbox-username-text" id="chatboxUserNameText">Stranger:</p>
<input type="text" id="chatboxMessageInput" placeholder="your communicae" maxlength="200"/>
<button id="chatboxSendButton" class="chatboxButton sqs-block-button-element--medium sqs-button-element--primary sqs-block-button-element">Post</button>
</div>
</div>
</div>
JAVASCRIPT:

<script type="module">
//firebase init
import { initializeApp } from 'https://www.gstatic.com/firebasejs/10.12.5/firebase-app.js';
import { getAuth, onAuthStateChanged, signOut } from 'https://www.gstatic.com/firebasejs/10.12.5/firebase-auth.js';
import { getFirestore, doc, setDoc, getDoc, where, limit, startAfter, serverTimestamp, collection, addDoc, query, orderBy, onSnapshot } from 'https://www.gstatic.com/firebasejs/10.12.5/firebase-firestore.js';
const firebaseConfig = {
apiKey: "-----",
authDomain: "-----",
projectId: "------",
storageBucket: "----",
messagingSenderId: "-----",
appId: "-----",
measurementId: "------"
};
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const db = getFirestore(app);
//main
document.addEventListener('DOMContentLoaded', function () {  
const codenameText = document.getElementById('chatboxUserNameText');
let codename = 'Stranger';
var isMaster = false;
const cooldownMS = 20000; //how often can u place a msg in ms
const scrollTimeDelay = 1000; //time before scrolling down on msg post
let lastMessageTime = 0;
const subString = 'chatbox';
const getURL = window.location.href;
if (getURL.includes(subString)) {
const unsubscribe = auth.onAuthStateChanged(async (user) => {
if (user) {
const userId = user.uid;
const userDocRef = doc(db, 'users', userId);
const userDoc = await getDoc(userDocRef);
const userData = userDoc.data();
codename = userData.codename || 'Stranger';
isMaster = userData.Master === true;
codenameText.textContent = codename + ':';
} else {
window.location.href = '/login'; //redirect if not logged in
return;
}
unsubscribe();
});
const chatboxTextBlock = document.getElementById('block-yui_3_17_2_1_1724594598043_17623');
const chatboxText = chatboxTextBlock.querySelector('p');    
const chatboxContainer = document.getElementById('chatboxMainContainer');
const messagesRef = collection(db, 'chat');
const messagesContainer = document.getElementById('chatboxMessages');
const messageInput = document.getElementById('chatboxMessageInput');
const sendButton = document.getElementById('chatboxSendButton');
chatboxText.textContent = '';
chatboxTextBlock.appendChild(chatboxContainer);
//initialise popover
let hideTimeout;
var popover = document.getElementById('custom-popover');
var popoverMessage = document.getElementById('popover-message');
function showPopover(message) {
popoverMessage.textContent = message;
popover.classList.add('show');
popover.style.pointerEvents = 'auto';
clearTimeout(hideTimeout);
hideTimeout = setTimeout(function() {
popover.classList.remove('show');
popover.style.pointerEvents = 'none';
}, 3000);
}
//post message
sendButton.addEventListener('click', async () => {
const message = messageInput.value.trim();
const currentTime = Date.now();
if (!message) {
$('#popoverMessage').off('click');
popoverMessage.style.color = "#ea4b1a";
showPopover('Your silence has been noted, now speak.');  
return;
} else {
if (currentTime - lastMessageTime >= cooldownMS || isMaster == true) {
const user = auth.currentUser; 
try {
await addDoc(messagesRef, {
user: user.uid,
userCodename: codename || 'Stranger',
text: message,
timestamp: serverTimestamp()
});
messageInput.value = '';
lastMessageTime = currentTime;
setTimeout(function() {
messagesContainer.scrollTop = messagesContainer.scrollHeight;
}, scrollTimeDelay); //allow time to propagate the msg
} catch (error) {
console.error("Error adding document: ", error);
$('#popoverMessage').off('click');
popoverMessage.style.color = "#ea4b1a";
showPopover('There has been an error posting your message');  
}
} else {
//alert("You can only post once every nn seconds.");
$('#popoverMessage').off('click');
popoverMessage.style.color = "#ea4b1a";
showPopover('You can only post once every ' + cooldownMS/1000 + ' seconds'); 
}
}
});
messageInput.addEventListener('keydown', (event) => {
if (event.key === 'Enter') {
event.preventDefault();
sendButton.click(); 
}
});
//display messages
const displayMessages = (snapshot) => {
messagesContainer.innerHTML = '';
snapshot.forEach((doc) => {
const data = doc.data();
const timestamp = data.timestamp ? data.timestamp.toDate().toLocaleString() : 'Some time ago';
const messageElement = document.createElement('div');
messageElement.classList.add('chatboxmsgmessage');
messageElement.innerHTML = `
<div class="chatboxmsgname">${data.userCodename}:</div>
<div class="chatboxmsgtimestamp">${timestamp}</div>
<div class="chatboxmsgtext"><div class="chatboxmsgBullet"></div>${data.text}</div>
`;
messagesContainer.appendChild(messageElement);
});
};
const now = new Date();
const sixHoursAgo = new Date(now.getTime() - (6 * 60 * 60 * 1000)); //oldest msg
const q = query(
messagesRef,
orderBy('timestamp', 'asc'),
where('timestamp', '>=', sixHoursAgo),
limit(50) // Limit to the most recent msges
);
onSnapshot(q, displayMessages);
setTimeout(function() {
messagesContainer.scrollTop = messagesContainer.scrollHeight;
}, scrollTimeDelay);
//exit strategy
} else {
chatboxContainer.remove();
}
});
</script>
CSS:

#chatboxMainContainer {
  max-width: 2200px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  position: relative;
  margin: 0;
  padding: 0;
}
.chatbox-container {
 // border: 2px solid #000000;
  border-radius: 0px;
  background: #ffc700;
  width: 95%;
  height: 800px;
  display: flex;
  flex-direction: column;
}
.chatbox-messages {
  flex: 1;
  padding: 0;
  padding-left: 10px;
  padding-right: 10px;
  overflow-y: scroll;
  border-bottom: 2px solid;
  border-top: 2px solid;
  border-image: linear-gradient(90deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 15%, rgba(0,0,0,1) 85%, rgba(0,0,0,0) 100%);
  border-image-slice: 1;
}
.chatbox-input {
  margin: 0;
  padding: 0;
  display: flex;
  background-color: #ffc700;
  margin-top: 20px;
}
.chatbox-username-text {
  padding: 0;
  margin: 0;
  letter-spacing: 2px;
  align-self: center;
}
.chatbox-input input {
  flex: 1;
  padding-left: 10px;
  padding-right: 10px;
  padding-bottom: 0px;
  border-radius: 0px;
  margin-right: 10px;
  background: #ffc700;
  border-top: 0;
  outline: none !important;
  letter-spacing: 1.4px;
}
.chatbox-input button {
  height: 50px;
  line-height: 8px !important;
  min-width: 250px;
  text-align: right;
  letter-spacing: 2px !important;
  background-color: #ffc700;
  color: #000000;
  border-radius: 0px;
  cursor: pointer;
}
.chatbox-input button:hover {
  background-color: #000000;
  color: #ffc700;
}
.chatboxmsgmessage {
  margin-bottom: 25px;
}
.chatboxmsgname {
  margin-bottom: -8px;
  letter-spacing: 2px;
  padding-top: 5px;
}
.chatboxmsgtimestamp {
  font-size: 0.8em;
  color: #000000;
  letter-spacing: 1.4px;
}
.chatboxmsgtext {
  letter-spacing: 1.4px;
  display: flex;
}
.chatboxmsgBullet {
  width: 40px;
  height: 40px;
  margin: 0;
  padding: 0;
  margin-right: 20px;
  margin-top: -5px;
  background-size: contain;
  background-position: center;
  background-repeat: no-repeat;
  background-image: url(https://images.squarespace-cdn.com/content/v1/6654b2fee26f292de13f1a9d/31b85fc9-5f5f-469d-8e71-8d50545821f4/BulletPointRightIcon1.png);
}
//mobile chatbox
@media only screen and (max-width:790px) {
.chatbox-input {
  display: block;
  }
.chatbox-input button {
  width: 100%;
  margin-top: 20px;
  }
.chatbox-input input {
  width: 94%;
  }
.chatbox-container {
  height: 800px;
  }
.chatbox-username-text {
  padding-left: 10px;
  }
}

I am again using my trusted pop-over to display alerts. Should you be interested in it, see its own blog post.

EDIT: Please note: do not place the unsubscribe function outside of the page check statement. If you do your site will be stuck in a redirect loop as soon as you log out, live and learn.

Previous
Previous

Adding LIVE IMAGE SEARCH

Next
Next

Random Shop product embellishment