Creating chat "rooms" using Node, Express, Heroku, and Socket.io - ios

So I've been building an app for quite some time and I'm running into problems in terms of scalability. I'm new to Node, and Heroku for that matter. Please bear with me.
I originally followed this tutorial to get my node service up and running. Essentially, it creates a real-time chat service. However, my question now comes with creating 'rooms'. It doesn't make sense to me that I might have 15+ chats going on, yet they all are calling the same functions on the same clientSocket, and I have to determine what UI updates go to which clients on the front end. As of now, I have upwards of 15 clients all trying to interact on different chats, but I'm pushing updates to everyone at once (for example, when a message is posted), then determining who's UI to update based on which room ID I'm cacheing on each device. Seems like a terrible waste of computing power to me.
I'm thinking that the solution involves modifying how each client connects (which is the code snippet below). Is there a way to create location based 'rooms', for example, where the clients connected are the only ones getting those updates? Any idea how to go about this solution? If anyone is also willing to just explain what I'm not understanding about Node, Express, Heroku, Socket.io or others, please do let me know.
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var pg = require('pg');
var userList = [];
var typingUsers = {};
var ActiveQueue = [];
app.get('/', function(req, res){
res.send('<h1>Active RT Queue</h1>');
});
var conString = "postgres://url";
pg.defaults.ssl = true;
var client = new pg.Client(conString);
client.connect(function(err) {
if(err) {
return console.error('could not connect to postgres', err);
}
});
http.listen(process.env.PORT || 5000, function(){
console.log('Listening on *:5000');
});
io.on('connection', function(clientSocket){
console.log('a user connected');
clientSocket.on('disconnect', function(){
console.log('user disconnected');
var clientNickname;
for (var i=0; i<userList.length; i++) {
if (userList[i]["id"] == clientSocket.id) {
userList[i]["isConnected"] = false;
clientNickname = userList[i]["nickname"];
break;
}
}
delete typingUsers[clientNickname];
io.emit("userList", userList);
//io.emit("userExitUpdate", clientNickname);
//io.emit("userTypingUpdate", typingUsers);
});
clientSocket.on("exitUser", function(clientNickname){
for (var i=0; i<userList.length; i++) {
if (userList[i]["id"] == clientSocket.id) {
userList.splice(i, 1);
break;
}
}
io.emit("userExitUpdate", clientNickname);
});
clientSocket.on("connectUser", function(clientNickname) {
var message = "User " + clientNickname + " was connected.";
console.log(message);
var userInfo = {};
var foundUser = false;
for (var i=0; i<userList.length; i++) {
if (userList[i]["nickname"] == clientNickname) {
userList[i]["isConnected"] = true
userList[i]["id"] = clientSocket.id;
userInfo = userList[i];
foundUser = true;
break;
}
}
if (!foundUser) {
userInfo["id"] = clientSocket.id;
userInfo["nickname"] = clientNickname;
userInfo["isConnected"] = true
userList.push(userInfo);
}
io.emit("userList", userList);
io.emit("userConnectUpdate", userInfo)
});
///functions pertaining to transfer of messages and updating the UI follow

I would try something like this:
io.on('connection', function(clientSocket) {
clientSocket.on('room:general', function(data) {
var user = data.user;
var message = data.message;
console.log('%s sent new message: %s',user,message);
io.emit('room:general:newMessage', data);
});
//and so for each room
.........
});
and from front end you need to send JSONObject:
{
user:your_username,
message:user_message
}
,
socket.emit("room:general", json_object);
socket.on("room:general:newMessage", onYourDefinedEmiterListener);
..........
..........
//and so for each room
I never made Chat Application, hope it helps.

Related

Focus tab and change page with service worker

We need a little help with a service worker. What we want to do is to click on notification, to execute service worker code and to check if the site is yet opened in a tab: if the site is not opened, we want to open a new tab and to navigate to a predefined url, if it is opened, we want to focus tab and then to navigate to a predefined path of the site.
We tried the code below but it doesn't work, cause we get some errors such as 'the service worker is not the active one' and so on.
Any help is really appreciated
Thanks
event.waitUntil(clients.matchAll({type: 'window' }).then(function (clientList) {
let openNewWindow = true;
for (let i = 0; i < clientList.length; i++) {
const client = clientList[i];
if (client.url.includes('localhost') && 'focus' in client) {
openNewWindow = false;
client.focus()
.then(function (client2)
{ return client.navigate(openUrl)});
// });
}
}
if (openNewWindow) {
return clients.openWindow(openUrl);
}
}));
I don't know if you still need a solution, but we did it like this.
After click, we look for the right registration by a lookup. Because our solution has many different customers, and there can be multiple registrations.
When we found it, we send a message. Somewhere else we have a listener on those messages to handle the rounting with the angular app.
If there is no tab opened, we use winClients.openWindow(url)
self.addEventListener('notificationclick', event => handleClick (event));
const handleClick = async (event) => {
const data = event.notification.data
const winClients = clients;
const action = event.action;
event.notification.close();
event.waitUntil(
clients.matchAll({includeUncontrolled: true, type: 'window'}).then(clients => {
let found = false;
let url = data.fallback_url;
if (action === 'settings') {
url = data.actions.settings;
}
clients.every(client => {
if (client.url.includes(data.lookup)) {
found = true;
client.focus();
client.postMessage({action: 'NOTIFICATION_CLICK', message_id: data.message_id, navigate_url: url});
return false;
}
return true;
});
if (!found) {
winClients.openWindow(url);
}
})
);
};

SAPUI5 oData.V2 How to invoke a function after everything in a batch request is done?

I have an issue while making an SAPUI5 odata V2 batch request :
var that = this;
var oServiceModel = that.getModel("oServiceModel");
odataMod = this.getModel("Service");
odataMod.setUseBatch(true);
var aData = oServiceModel.getData();
var stupidService = _.filter(aData, function (ae) {
return ae.Info === "-N/A";
});
var i = 0 ;
_.forEach(stupidService, function (sap) {
oGlobalBusyDialog.setText("Deleting service :" + sap.ObjectID);
oGlobalBusyDialog.setTitle("Deleting Service");
oGlobalBusyDialog.open();
that.removeService(sap).then(function () {
if (i === 615) {
oGlobalBusyDialog.close();
}
}).catch(function () {});
});
my Delete function is like this:
removeService: function (service) {
var that = this;
return new Promise(
function (resolve, reject) {
odataMod.remove('/ProjectTaskServiceCollection(\'' + service.ObjectID + '\')/', {
success: function (oData) {
resolve(oData);
},
error: function (oResult) {
that.handleError(oResult);
oGlobalBusyDialog.close();
reject(oResult);
}
});
});
What's happening ,is that if I'm trying to delete 500 entry, and if 200 entry cannot be deleted, the error message gets displayed 200 times
How to make it in a way to only display the error message once ?
Also, I want to turn off the batch request once everything is done odataMod.setUseBatch(false); how to do it ?
*EDIT: *
I've manage to do :
var aDeffGroup = odataMod.getDeferredGroups();
//add your deffered group
aDeffGroup.push("deletionGroup");
for (var s = 0; s < 5; s++) {
odataMod.remove('/ProjectTaskServiceCollection(\'' + stupidService[s].ObjectID + '\')/', {
//pass groupid to remove method.
groupId: "deletionGroup"
});
}
odataMod.submitChanges({
// your deffered group id
groupId: "deletionGroup",
success: function() {
//Get message model data from Core and it contains all errors
// Use this data to show in dialog or in a popover or set this to your local model see below code
var aErrorData = sap.ui.getCore().getMessageManager().getMessageModel();
console.log(aErrorData);
}
});
yet stills my console.log(aErrorData); still prints multiple error message
Instead of doing individual deletion odata calls. Add these all remove methods in a single group, then call odatamod.submitChanges() method.
Example:
//get all deffered groups
var aDeffGroup = odataMod.getDeferredGroups();
//add your deffered group
aDeffGroup.push("deletionGroup");
//set it back again to odatamodel
odataMod.setDeferredGroups(aDeffGroup);
odataMod.remove('/ProjectTaskServiceCollection(\'' + service.ObjectID + '\')/', {
//pass groupid to remove method.
groupId: "deletionGroup"});
odataMod.submitChanges({
// your deffered group id
groupId:"deletionGroup",
success: function() {
//Get message model data from Core and it contains all errors
// Use this data to show in dialog or in a popover or set this to your local model see below code
var aErrorData = sap.ui.getCore().getMessageManager().getMessageModel();
});

indexeddb on IOS devices

I have a problem with an indexeddb query with index when running on IOS devices.
$.indexedDB(dbName).objectStore(tablename).index("INDICE").each(function(itemLocal) {
itemLocal.delete();
}, [VALORINDICE]).then(function() {
callback();
}, function() {
console.log("error");
});
The problem is if there is more than one record that matches the index, it does not eliminate them, it eliminates the first one and leaves. But if for example I put console.log (itemLocal) instead of itemLocal.delete() if it shows all those that match the index. Any suggestions of something that may be leaking?
I have tried with this code and I get the same error(code without api jquery)
var request = indexedDB.open(DATABASE_NAME);
request.onsuccess = function(event) {
var db = request.result;
var transaction = db.transaction(["TABLE"], "readwrite");
var table = transaction.objectStore("TABLE");
var index = table.index("INDEX");
var req = index.openCursor();
req.onsuccess = function() {
var cursor = req.result;
if (cursor) {
console.info(cursor.value);
cursor["delete"]();
cursor["continue"]();
}
};
req.onerror = function(e) {
console.error(e, req);
};
};
request.onerror = function(e) {
console.error(e, request);
};

Unable to save code Espruino NodeMCU

I had been working with Espruino for a bit and it is really a wonderful project. But, I am facing an issue for saving the code onto the flash, so that it can still be run when power is supplied to the board(NodeMCU), instead of the PC COM port. The code works completely fine until it is passed from the terminal. But, if I switch over the power supply it stops working.
Also, I tried the save() and E.on('init',function(){}) but to no avail. It still doesn't create a web server. If someone could help out here it could be great!
Thanks!
function main() {
var http = require('http');
var led = Pin(5);
http.createServer(function (req, res) {
var url = req.url;
res.writeHead(200);
if(url == "/on") {
digitalWrite(led, 1);
res.end("on");
} else if(url == "/off") {
digitalWrite(led, 0);
res.end("off");
} else {
res.end('Lol');
}
}).listen(80);
}
E.on('init', function(){
main();
});
Here's the code I wish to write to my flash for the IOT project I am working on
After fiddling around with the documentation and crawling the scrambled web for almost a whole day I found out a solution myself. The issue ->
function main() {
var wifi = require('Wifi');
wifi.startAP("testing");
wifi.save();
var http = require('http');
var led = Pin(5);
return http.createServer(function (req, res) {
var url = req.url;
res.writeHead(200);
if(url == "/on") {
digitalWrite(led, 1);
res.end("on");
} else if(url == "/off") {
digitalWrite(led, 0);
res.end("off");
} else {
res.end('Lol');
}
}).listen(80);
}
function test() {
console.log('Starting server');
setTimeout(function() {
var server = main();
console.log(server);
}, 5000);
}
E.on('init', function(){
test();
});
save();
The problem was that the MCU couldnot get enough time to connect to the wifi before the command for http.createServer() is executed. Since, it cannot obtain the ip address for the MCU, thus it is unable to process the http.createServer() command. Hence, a timeout was necessary to process a delay before it's execution.

How to create a "loading" spinner in Breeze?

I'm trying to create a loading spinner that will be displayed when breeze is communicating with the server. Is there some property in Breeze that is 'true' only when breeze is sending data to the server, receiving data, or waiting for a response (e.g. after an async call has been made but no response yet)? I thought of binding this data to a knockout observable and binding the spinner to this observable,
Thanks,
Elior
Use spin.js
http://fgnass.github.io/spin.js/
Its so simple..make it visible before you execute the query and disable it after the query succeeds or fails.
I don't see any property that is set or observable while Breeze is querying, but if you are using a datacontext, or some JavaScript module for your data calls, this is what you can do -
EDIT
Taking John's comments into account, I added a token'd way of tracking each query.
var activeQueries = ko.observableArray();
var isQuerying = ko.computed(function () {
return activeQueries().length !== 0;
});
var toggleQuery = function (token) {
if (activeQueries.indexOf(token) === -1)
{ activeQueries.push(token); }
else { activeQueries.remove(token); }
};
var getProducts = function (productsObservable, forceRemote) {
// Don't toggle if you aren't getting it remotely since this is synchronous
if (!forceRemote) {
var p = getLocal('Products', 'Product','product_id');
if (p.length > 0) {
productsObservable(p);
return Q.resolve();
}
}
// Create a token and toggle it
var token = 'products' + new Date().getTime();
toggleQuery(token);
var query = breeze.EntityQuery
.from("Products");
return manager.executeQuery(query).then(querySucceeded).fail(queryFailed);
function querySucceeded(data) {
var s = data.results;
log('Retrieved [Products] from remote data source', s, true);
// Toggle it off
toggleQuery(token);
return productsObservable(s);
}
};
You will need to make sure all of your fail logic toggles the query as well.
Then in your view where you want to place the spinner
var spinnerState = ko.computed(function () {
datacontext.isQuerying();
};

Resources