Учебное пособие по приложению Firebase Chat | 2023
В сообщении блога мы рассмотрим процесс создания приложения чата с использованием Firebase.
Чтобы просмотреть полное приложение, нажмите здесь: Учебное пособие по приложению Firebase Chat
Что такое Firebase?
Firebase — это тип бессерверной серверной части, предоставляемой Google. Он предлагает набор услуг для использования в качестве серверной части мобильных или веб-приложений.
Он предлагает такие услуги, как база данных NoSQL, социальная аутентификация, уведомления и услуги связи в реальном времени.
В следующем руководстве мы будем использовать базу данных Firebase в реальном времени в качестве серверной части для нашего чат-сервиса.
Итак, приступим.
Предварительное условие
Чтобы разрешить этот учебник, вам потребуется:
- Базовые знания HTML и CSS
- Знаком с JavaScript
Шаг 1. Зарегистрируйте учетную запись Firebase
Если у вас еще нет учетной записи Firebase, перейдите на страницу firebase.google.com и зарегистрируйте бесплатную учетную запись Firebase.
Нажмите кнопку «Начать», чтобы создать бесплатную учетную запись.
Назовите свой проект как угодно, мы назовем наш проект «firebase-chatapp».
Шаг 2: Настройка базы данных Firestore
После создания учетной записи firebase мы создадим базу данных Firestore, в этой базе данных Firestore будут храниться наши сообщения чата, и мы будем использовать базу данных для подписки на обновления в реальном времени.
После создания проекта с помощью боковой панели выберите «База данных Firestore».
Затем нажмите кнопку «Создать базу данных»
Выберите вариант «Начать в рабочем режиме», и мы вручную настроим ACL в соответствии с нашим чатом.
Новинка в DeadSimpleChat? Это готовый чат, который вы можете легко добавить на свой веб-сайт или в приложение — без сложного кода. Chat API и приложение SDK SaaS, социальная платформа, образование, игры и финансы Зарегистрируйтесь бесплатно
Шаг 3. Разрешение анонимной аутентификации
Для нашего чата мы хотим, чтобы пользователи просто вводили имя пользователя и присоединялись к чату, для этого нам потребуется «анонимная» аутентификация.
Чтобы включить это, мы выберем опцию «Аутентификация» на боковой панели.
Нажмите кнопку «Начать» на странице аутентификации.
Теперь выберите опцию «Анонимно».
И включите его и сохраните
Теперь вернитесь в «База данных Firestore», выберите «Правила» и обновите существующее правило до следующего:
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /{document=**} { allow read, write: if request.auth != null; } } }
Это позволит аутентифицированным пользователям читать и записывать в базу данных firebase.
Шаг 4. Получение конфигурации Firebase
Конфигурация firebase необходима для подключения нашего приложения чата к серверу firebase. Чтобы получить конфиг, перейдите в настройки проекта.
Щелкните значок, чтобы сгенерировать учетные данные для веб-приложения.
Назовите приложение как угодно
Выберите использовать тег «‹script›» и скопируйте конфиг.
Это все шаги, необходимые для настройки проекта firebase, теперь давайте начнем создавать наше фактическое приложение для чата.
Шаг 4. Создание исходного пользовательского интерфейса чата
В нашем чат-приложении нам потребуется интерфейс, который позволит пользователю вводить свое имя пользователя.
И нам потребуется основной пользовательский интерфейс чата, где будут отображаться все сообщения, и область ввода, позволяющая пользователю отправлять новые сообщения.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Firebase Chat</title> <script src="https://cdn.tailwindcss.com"></script> </head> <body> <script type="module" src="main.js"></script> <div id="joinView" class="bg-gray-100 w-96 items-center mx-auto flex flex-col mt-10 rounded-md p-4 space-y-4" > <h1 class="text-center font-bold">Join Chat</h1> <div class="flex space-x-1"> <input id="usernameInput" type="text" class="bg-white border border-gray-200 rounded-md px-2" placeholder="Enter your name" /> <button id="joinButton" class="bg-indigo-600 px-2 py-1 text-sm text-white rounded-md" > Join </button> </div> </div> <div id="chatsView" class="w-64 h-96 mx-auto flex flex-col mt-10 hidden"> <div class="messages flex-1 bg-gray-100"> <ul id="messageList"> <li> <div class="flex space-x-2 pl-2 pt-2"> <div class="flex flex-col"> <div class="flex items-baseline space-x-2"> <div class="text-sm font-bold">Tommy Lee</div> <div class="text-sm text-gray-400">5:20 pm</div> </div> <div class="text-sm text-gray-500">Hello world</div> </div> </div> </li> <li> <div class="flex space-x-2 pl-2 pt-2"> <div class="flex flex-col"> <div class="flex items-baseline space-x-2"> <div class="text-sm font-bold">Tommy Lee</div> <div class="text-sm text-gray-400">5:21 pm</div> </div> <div class="text-sm text-gray-500">Testing</div> </div> </div> </li> <li> <div class="flex space-x-2 pl-2 pt-2"> <div class="flex flex-col"> <div class="flex items-baseline space-x-2"> <div class="text-sm font-bold">James Bond</div> <div class="text-sm text-gray-400">5:21 pm</div> </div> <div class="text-sm text-gray-500">Good Job</div> </div> </div> </li> </ul> </div> <div class="input h-11 w-full flex border border-slate-200 rounded-md"> <input id="messageInput" type="text" class="outline-none flex-1 px-1" /> <div class="p-1"> <button id="sendButton" class="bg-indigo-600 p-2 rounded-lg"> <svg xmlns="http://www.w3.org/2000/svg" fill="white" viewBox="0 0 24 24" stroke-width="1.5" stroke="black" class="w-4 h-4" > <path stroke-linecap="round" stroke-linejoin="round" d="M6 12L3.269 3.126A59.768 59.768 0 0121.485 12 59.77 59.77 0 013.27 20.876L5.999 12zm0 0h7.5" /> </svg> </button> </div> </div> </div> </body> </html>
Мы будем использовать Tailwind CSS для оформления пользовательского интерфейса, приведенный выше код содержит наш базовый пользовательский интерфейс.
Мы дали идентификатор joinView
контейнеру, содержащему текстовое поле для ввода имени пользователя и кнопку присоединения.
Присваиваем id chatsViews
основному контейнеру чата
Когда пользователь не присоединился к комнате чата, мы скроем контейнер chatsView
, а когда пользователь присоединится к комнате чата, мы скроем контейнер joinView
и покажем контейнер chatsView
.
Мы также создали тег ul и присвоили ему идентификатор messageList
, он будет содержать все сообщения, отправляемые в чате.
Мы будем перерисовывать этот список каждый раз, когда в чат отправляется новое сообщение.
Шаг 5: Подключение пользовательского интерфейса
Теперь давайте подключим пользовательский интерфейс с помощью JavaScript и Firebase SDK, создадим main.js
Импорт Firebase SDK
import { initializeApp } from "https://www.gstatic.com/firebasejs/9.15.0/firebase-app.js"; // Add Firebase products that you want to use import { getAuth, signInAnonymously, } from "https://www.gstatic.com/firebasejs/9.15.0/firebase-auth.js"; import { getFirestore, addDoc, collection, onSnapshot, doc, getDocs, query, where, } from "https://www.gstatic.com/firebasejs/9.15.0/firebase-firestore.js";
Вставьте конфигурацию firebase, полученную на шаге 3.
// Your web app's Firebase configuration const firebaseConfig = { apiKey: "AIzaSyCRa0JplErns226xER0Fpk1cEulP2c6y0Q", authDomain: "fir-chat-a082b.firebaseapp.com", databaseURL: "https://fir-chat-a082b-default-rtdb.firebaseio.com", projectId: "fir-chat-a082b", storageBucket: "fir-chat-a082b.appspot.com", messagingSenderId: "589407773218", appId: "1:589407773218:web:676cf16926d1dee647607b", };
Мы будем вызывать метод Firebase initializeApp
и передавать ему файл firebaseConfig
.
const app = initializeApp(firebaseConfig);
Получите экземпляр базы данных Firestore и Auth. Мы будем использовать базу данных Firestore для отправки сообщений и получения обновлений в реальном времени о сообщениях.
const db = getFirestore(app); const auth = getAuth(app);
Теперь мы будем обращаться к элементам HTML в нашем пользовательском интерфейсе, чтобы прослушивать события, а также отображать и скрывать элементы пользовательского интерфейса.
const joinButton = document.getElementById("joinButton"); const usernameInput = document.getElementById("usernameInput"); const messageInput = document.getElementById("messageInput"); const sendButton = document.getElementById("sendButton"); const joinView = document.getElementById("joinView"); const chatsView = document.getElementById("chatsView");
Обработка присоединения
Мы добавим прослушиватель событий щелчка к кнопке «Присоединиться». При нажатии кнопки присоединения мы проверим, есть ли какое-либо значение в поле usernameInput
.
Если пользователь ввел имя пользователя в поле usernameInput
, мы вызовем метод signInAnonymously
Firebase SDK, чтобы войти в чат.
Без вызова этого метода мы не можем читать или записывать в базу данных Firestore.
После успешного входа мы скроем joinView
и покажем chatsView
.
let specifiedUsername = ""; let userLoggedIn = false; joinButton.addEventListener("click", () => { specifiedUsername = usernameInput.value; if (!specifiedUsername) { alert("username cannot be empty"); return; } signInAnonymously(auth) .then(async () => { joinView.classList.add("hidden"); chatsView.classList.remove("hidden"); userLoggedIn = true; await loadHistoricalMessages(); await subscribeToNewMessages(); writeMessagesArray(); console.log("User logged-in"); }) .catch((error) => { const errorCode = error.code; const errorMessage = error.message; console.log(errorCode, errorMessage); }); });
Загрузка предыдущих сообщений
В предыдущем фрагменте кода мы зарегистрировали пользователя в чате.
И, как вы можете видеть в фрагменте кода, мы также вызываем метод loadHistoricalMessages()
, поэтому мы реализуем этот метод в этом разделе.
В методе loadHistoricalMessages()
мы будем запрашивать базу данных Firestore, чтобы получить все предыдущие сообщения, и будем хранить их в глобальном массиве messages
.
Чтобы получить исторические сообщения, мы вызовем метод getDocs
:
async function loadHistoricalMessages() { messages = []; const querySnapshot = await getDocs(collection(db, "messages")); querySnapshot.forEach((doc) => { messages.push({ id: doc.id, ...doc.data(), }); }); console.log(messages); return messages; }
Прослушивание новых сообщений
Чтобы прослушать новые сообщения, мы вызовем метод onSnapshot
SDK Firestore.
Метод onSnapShot
принимает запрос, так как мы хотим прослушать всю коллекцию, мы не будем указывать никаких условий в запросе.
Это приведет к тому, что метод будет срабатывать каждый раз, когда в чате будет отправлено новое сообщение.
И когда сработает метод onSnapShot
, мы отправим новые сообщения в наш глобальный массив messages
.
Но метод onSnapShot
возвращает не только новое сообщение, но и некоторые предыдущие сообщения вместе с новым сообщением.
Мы не хотим помещать другие сообщения в массив, мы просто хотим добавить новое сообщение, потому что это приведет к тому, что в чате будут отображаться дубликаты сообщений.
Чтобы предотвратить это, мы создадим хеш-карту идентификаторов всех существующих сообщений, а затем пройдемся по newMessages
, и мы не будем отправлять сообщения, идентификатор которых соответствует существующим сообщениям.
function subscribeToNewMessages() { const q = query(collection(db, "messages")); const unsubscribe = onSnapshot(q, (querySnapshot) => { const newMessages = []; querySnapshot.forEach((doc) => { newMessages.push({ id: doc.id, ...doc.data(), }); }); /** * Creating hash map of the existing messages. */ let existingMessageHash = {}; for (let message of messages) { existingMessageHash[message.id] = true; } /** * Push only those messages which do not * exist in the hashMap */ for (let message of newMessages) { if (!existingMessageHash[message.id]) { messages.push(message); } } writeMessagesArray(); }); }
Мы создали hashMap идентификатора сообщения, чтобы предотвратить создание вложенного цикла for.
Отображение сообщений в пользовательском интерфейсе
Мы вызывали метод writeMessagesArray()
в методе signInAnonymously
, а также в слушателе subscribeToNewMessages
.
Метод writeMessagesArray()
будет отображать сообщения в массиве сообщений в нашем пользовательском интерфейсе чата.
function writeMessagesArray() { const html = []; for (let message of messages) { html.push(messageTemplate(message.message, message.user, message.created)); } document.getElementById("messageList").innerHTML = html.join(""); } function messageTemplate(message, username, timestamp) { return `<li> <div class="flex space-x-2 pl-2 pt-2"> <div class="flex flex-col"> <div class="flex items-baseline space-x-2"> <div class="text-sm font-bold">${username}</div> <div class="text-sm text-gray-400">${ new Date(timestamp.seconds * 1000).toLocaleDateString() + " " + new Date(timestamp.seconds * 1000).toLocaleTimeString() }</div> </div> <div class="text-sm text-gray-500">${message}</div> </div> </div> </li>`; }
Метод просматривает каждое сообщение в массиве сообщений и вызывает метод messageTemplate
.
Методы messageTemplate
содержат HTML-код для отображения сообщения, принимают имя пользователя, сообщение и отметку времени и возвращают HTML-код сообщения.
Мы помещаем HTML в массив и добавляем HTML к тегу messageList
ul.
Отправка сообщений
До сих пор мы прошли процесс присоединения к чату, загрузки исторических сообщений, прослушивания новых сообщений и вывода сообщений на экран.
Теперь давайте реализуем часть фактической отправки сообщения.
Чтобы отправить сообщение, мы добавим прослушиватель кликов в файл sendButton
. При нажатии на sendButton
мы вызовем метод addDoc
базы данных Firestore и сохраним сообщение в базе данных Firestore.
Наша модель сообщения будет содержать следующие свойства:
user
– имя пользователяmessage
– настоящие сообщенияcreated
– отметка времени создания сообщения
sendButton.addEventListener("click", async () => { const message = messageInput.value; messageInput.value = ""; const docRef = await addDoc(collection(db, "messages"), { user: specifiedUsername, message: message, created: new Date(), }); console.log(docRef); });
Вот полный код файла main.js
.
import { initializeApp } from "https://www.gstatic.com/firebasejs/9.15.0/firebase-app.js"; // Add Firebase products that you want to use import { getAuth, signInAnonymously, } from "https://www.gstatic.com/firebasejs/9.15.0/firebase-auth.js"; import { getFirestore, addDoc, collection, onSnapshot, doc, getDocs, query, where, } from "https://www.gstatic.com/firebasejs/9.15.0/firebase-firestore.js"; // Your web app's Firebase configuration const firebaseConfig = { apiKey: "AIzaSyCRa0JplErns226xER0Fpk1cEulP2c6y0Q", authDomain: "fir-chat-a082b.firebaseapp.com", databaseURL: "https://fir-chat-a082b-default-rtdb.firebaseio.com", projectId: "fir-chat-a082b", storageBucket: "fir-chat-a082b.appspot.com", messagingSenderId: "589407773218", appId: "1:589407773218:web:676cf16926d1dee647607b", }; // Initialize Firebase const app = initializeApp(firebaseConfig); const db = getFirestore(app); const auth = getAuth(app); const joinButton = document.getElementById("joinButton"); const usernameInput = document.getElementById("usernameInput"); const messageInput = document.getElementById("messageInput"); const sendButton = document.getElementById("sendButton"); const joinView = document.getElementById("joinView"); const chatsView = document.getElementById("chatsView"); let messages = []; let specifiedUsername = ""; let userLoggedIn = false; joinButton.addEventListener("click", () => { specifiedUsername = usernameInput.value; if (!specifiedUsername) { alert("username cannot be empty"); return; } signInAnonymously(auth) .then(async () => { joinView.classList.add("hidden"); chatsView.classList.remove("hidden"); userLoggedIn = true; await loadHistoricalMessages(); await subscribeToNewMessages(); writeMessagesArray(); console.log("User logged-in"); }) .catch((error) => { const errorCode = error.code; const errorMessage = error.message; console.log(errorCode, errorMessage); }); }); sendButton.addEventListener("click", async () => { const message = messageInput.value; messageInput.value = ""; const docRef = await addDoc(collection(db, "messages"), { user: specifiedUsername, message: message, created: new Date(), }); console.log(docRef); }); function subscribeToNewMessages() { const q = query(collection(db, "messages")); const unsubscribe = onSnapshot(q, (querySnapshot) => { const newMessages = []; querySnapshot.forEach((doc) => { newMessages.push({ id: doc.id, ...doc.data(), }); }); /** * Creating hash map of the existing messages. */ let existingMessageHash = {}; for (let message of messages) { existingMessageHash[message.id] = true; } /** * Push only those messages which do not * exist in the hashMap */ for (let message of newMessages) { if (!existingMessageHash[message.id]) { messages.push(message); } } writeMessagesArray(); }); } async function loadHistoricalMessages() { messages = []; const querySnapshot = await getDocs(collection(db, "messages")); querySnapshot.forEach((doc) => { messages.push({ id: doc.id, ...doc.data(), }); }); console.log(messages); return messages; } function writeMessagesArray() { const html = []; for (let message of messages) { html.push(messageTemplate(message.message, message.user, message.created)); } document.getElementById("messageList").innerHTML = html.join(""); } function messageTemplate(message, username, timestamp) { return `<li> <div class="flex space-x-2 pl-2 pt-2"> <div class="flex flex-col"> <div class="flex items-baseline space-x-2"> <div class="text-sm font-bold">${username}</div> <div class="text-sm text-gray-400">${ new Date(timestamp.seconds * 1000).toLocaleDateString() + " " + new Date(timestamp.seconds * 1000).toLocaleTimeString() }</div> </div> <div class="text-sm text-gray-500">${message}</div> </div> </div> </li>`; }
Шаг 6: Вот и все
Мы создали простое приложение для группового чата, используя базу данных Firebase и Firestore.
Вы можете перейти на панель инструментов Firebase FireStore и увидеть сообщение, отправленное в коллекции.
Добавьте чат в свое веб-приложение с помощью Dead Simple Chat
Dead Simple Chat Chat API и SDK для вашего веб-приложения или мобильного приложения.
Он легко масштабируется и может быть интегрирован за считанные минуты. Он имеет Chat SDK, функции настройки и модерации, чтобы сделать чат пригодным для любого варианта использования чата, будь то прямая трансляция, групповой чат или чат 1–1.