Wix release the new wix-realtime which allow developers to build interactive apps like chat app, multi-player game etc. So, In this example i will discuss how we can build a chat app where a user can send a message to person B and person B immediatly get the message. If you want to test it live here is the link: https://test.salman2301.com
Overview
Front-end we use custom element, which allows to show the message side by side and also with custome element, we can have a fix height and scroll bar so user can scroll to previous messages. We can use repeater for this but we need to use pagination or infinite scrolling which is why i didn't. On the Backend, we use wix-realtime API with datahook - datahook runs the code when a new row is inserted in the database.
- realtime API allow us to do pub/sub ( publish/ subscriber) where sender or publisher send message to a channel and the reciever or subscriber can reciver the message.
Dataflow
If a user Person A send a message to Person B, the message will be inserted in the database and this runs the afterInsert data hook and the data hook use realtime api to publish to a channel, the name of the channel is same as the Person B user id. In this way, only the person B recieve the message and On the front-end, let's assume that the person B is online and we subscribe to the channel and the name of the channel Is the current user id. We get the user id from wix-user module wixUser.currentUser.id.
Database
Database Name: message Fields:
field_key data
from - USER_ID
to - USER_ID
message - Message
Code
// backend/data.js import {publish} from 'wix-realtime-backend'; // datahook runs when a new row is inserted in the database // item contains inserted row information { from: "uuid", to:"uuid", message:"hello"} export function message_afterInsert(item, context) { const { from, to, message, _id } = item; const channel = { name : to}; let data = { message, from, _id }; // publish which take a channel name and data. publish(channel, data, {includePublisher: true}); return item; }
// Page code import * as realtime from "wix-realtime"; import wixUsers from "wix-users"; import wixData from "wix-data"; import { getUsers, getMe } from "backend/getUsers.jsw"; let lastUserId, lastLoginEmail, lastPicture; let userId = wixUsers.currentUser.id; let owner; $w.onReady(function () { // $w('#dataset1').onReady(init); init(); $w("#inSearch").onKeyPress((e) => { setTimeout(() => { if (e.key === "Enter") filterMem(); if (e.key === "Escape") { $w("#inSearch").value = ""; filterMem(); } }, 40); }); $w("#ChatElement").setAttribute( "append-msg", JSON.stringify({ user: { name: "salman", }, msg: " Hello this is a test", date: new Date().toISOString(), isOwner: true, }) ); getMe().then((data) => { owner = data; }); }); function init() { // wix real time const channel = { name: userId }; realtime.subscribe(channel, newMessage); // update UI filterMem(); // events $w("#repeaterUser").onItemReady(($item, itemData, i) => { let { loginEmail, picture } = itemData; $item("#image1").src = picture; $item("#textEmail").text = loginEmail; }); $w("#containerUser").onClick((e) => { switchUser(e.context.itemId); }); $w("#btnSend").onClick(sendMessage); $w("#inMessage").onKeyPress((e) => { const { ctrlKey, key } = e; if (key === "Enter" && !ctrlKey) { sendMessage(); } }); } async function filterMem() { let inMember = $w("#inSearch").value || undefined; getUsers(inMember) .then((data) => { $w("#repeaterUser").data = data.items; if (!lastUserId) { switchUser(data.items[0]._id); } }) .catch(console.error); } async function switchUser(toUserId) { lastUserId = toUserId; $w("#repeaterUser").onItemReady(($item, itemData, i) => { let { _id, loginEmail, picture } = itemData; if (_id === toUserId) { $item("#imgCurrUser").show(); $w("#textHeadUser").text = `Connected to ${loginEmail}`; lastLoginEmail = loginEmail; lastPicture = picture; refreshMsg(); } else { $item("#imgCurrUser").hide(); } }); } async function newMessage({ payload }) { appendMsg(payload._id); } async function refreshMsg() { $w("#ChatElement").setAttribute("messages", "[]"); let resMsgs = await wixData .query("message") .eq("from", userId) .eq("to", lastUserId) .or(wixData.query("message").eq("from", lastUserId).eq("to", userId)) .ascending("_createdDate") .find(); let msg = resMsgs.items; let username = lastLoginEmail.split("@")[0]; msg = msg.map((el) => { let isOwner = userId === el.from; let userImage = isOwner ? owner.picture : lastPicture; userImage = encodeURI(userImage); if (!userImage.startsWith("https://")) userImage = undefined; return { _id: el._id, isOwner: isOwner, user: { name: username, image: userImage, }, msg: el.message, date: el._createdDate, data: el, }; }); $w("#ChatElement").setAttribute("messages", JSON.stringify(msg)); if (msg.length === 0) { $w("#textNoMsg").show(); } else { $w("#textNoMsg").hide(); } } async function appendMsg(msgId) { let resMsgs = await wixData.query("message").eq("_id", msgId).find(); let message = resMsgs.items[0]; console.log({ resMsgs }); let username = lastLoginEmail.split("@")[0]; let isOwner = userId === message.from; let userImage = isOwner ? owner.picture : lastPicture; userImage = encodeURI(userImage); if (!userImage.startsWith("https://")) userImage = undefined; let msg = { _id: message._id, isOwner: isOwner, user: { name: username, image: userImage, }, msg: message.message, date: message._createdDate, data: message, }; if (!(lastUserId === message.from || lastUserId === message.to)) { console.error(" No for the current user show alert!"); return; } $w("#ChatElement").setAttribute("append-msg", JSON.stringify(msg)); } async function sendMessage() { try { $w("#btnSend").disable(); let msg = $w("#inMessage").value; if (!msg) return; let toInsert = { from: userId, to: lastUserId, message: msg, }; let inserted = await wixData.insert("message", toInsert); appendMsg(inserted._id); $w("#btnSend").enable(); $w("#inMessage").value = ""; } catch (e) { console.log("ERROR : ", e.message); $w("#btnSend").enable(); $w("#inMessage").value = ""; } }
For the custome element check my github https://github.com/Salman2301/wix-chat-component