Remove only one of socket.io event handlers - javascript

I want to write an application that does the following:
Starts a counter starting from 00:00, and increases time by 30 minutes around every passing second
Clients can connect to the server and check the time that it currently has
Clients can press press a button that increases the time by an additional 30 minutes
I want to build on it further, this is why I'm using Socket.io, but for now I just want to accomplish these three things. I have the following code after server initialization:
const io = socketio(server)
io.on('connection', (socket) => {
logger.info(`There is a new connection! ${socket.id}`)
// Initialize time
initializeTime(socket)
// Start clock
startClock(io)
// Remove all handlers from 'connection'
io.removeAllListeners('connection', startClock)
// Increase time if user presses button
socket.on('increaseTime', increaseTime)
})
function initializeTime(socket) {
socket.emit('time', `${currentTime.utc().format('HH:mm')}`)
logger.info(`Clock set up to ${currentTime.utc().format('HH:mm')}`)
}
function startClock(io) {
setInterval(() => {
currentTime.add(30, 'minutes')
io.emit('time', `${currentTime.utc().format('HH:mm')}`)
logger.info(`Clock updated to ${currentTime.utc().format('HH:mm')}`)
}, 1250)
}
function increaseTime() {
currentTime.add(30, 'minutes')
this.emit('time', `${currentTime.utc().format('HH:mm')}`)
logger.info('Key pressed on client!')
}
With this implementation, only the user that was first connected can manually, additionally increase the time on the server. First, I tried to remove only the starClock function from the 'connection' event, but then I realized that I probably can't do that, I can only remove the handler that was passed as a callback argument. This is why I wrote 'removeAllListeners', to show that I (probably) understand that what I'm trying to do is not the right way, and with this approach I would remove everything else that I specified, and this is why only the first client could increase the time.
Then I tried something else, that works perfectly, but still seems like bad code:
io.on('connection', function iNeedThis(socket) {
logger.info(`There is a new connection! ${socket.id}`)
// Initialize time
initializeTime(socket)
// Increase time if user presses button
socket.on('increaseTime', increaseTime)
})
io.on('connection', function iDoNotNeedThis(socket) {
// Start clock
startClock(io)
io.removeListener('connection', iDoNotNeedThis)
})
What would be the solution to make sure that startClock runs on first connection, but only runs once, while everything else runs on all the connections. I feel like I'm missing something conceptually. Thank you!

Related

The Exact time distance from button clicked to when received data to a website

I'm creating a chrome extension. I need to know when a user clicked on a specific button how many mili seconds will take long, to receive that command to website server. I have a web Worker that is connected to that website too. Could I get the exact time when a button clicked to when data received by website's server? It doesn't matter how many mili seconds take that the respond back to me, the time of receiving request to server after click is mu issue now. can someone help me please?
I'm looking for javascript code to get the time distance between button clicked and received that by website's server.
It is easy to get the time when button is clicked by finding that button and attaching an event handler on it, that calls performance.now()
const t0 = 0;
document.getElementById('buttonId').addEventListener('click',() => {
t0 = performance.now();
});
Detecting when the server response is received is not trivial. You might need to use the chrome.webRequest api.
Alternatively, a simpler way would be to look for side-effects of the request, that happen in the DOM. (A loader appearing and disappearing, data rows appearing, button being disabled and re-enabled, etc).
You can either poll for these changes, or use the mutationObserver api to detect when elements containing expected attributes are available in the DOM.
Let's say you are polling for the button being re-enabled, every 10ms:
const t1 = 0;
const interval = window.setInterval(() => {
if (!document.getElementById('buttonId').getAttribute('disabled')) {
t1 = performance.now();
requestTime = t1 - t0;
console.log(requestTime);
window.clearInterval(interval);
}
},10);

Building an alarm clock style application

I am building a webapp (referred to as the "noticeboard") for a friend's business, to aid with their packaging and dispatch operation. It is built using HTML, CSS & JS. The backend is built in PHP / MYSQL.
The noticeboard is for the benefit of their staff and displays dispatch cut-off ("event") times, i.e as follows:
Dispatch Time 1 : 09:00
Dispatch Time 2 : 11:30
Dispatch Time 3 : 14:30
Dispatch Time 4 : 16:00
They update these times on a regular basis, as their schedule depends on their delivery firm's schedule. There is an AJAX request running every 15 mins which simply fetches the latest times (JSON format) from the database and updates the noticeboard. Although I could just simply implement an "auto browser refresh" every 15 minutes, I found this was a bit inconsistent and sometimes a "page cannot be found" error message would be displayed.
The noticeboard also displays a real-time clock. I have built this using moment.js.
The system runs 24/7 in a Chrome browser running on Windows 10. Nothing else is running on the machine.
At the moment the noticeboard simply displays these times. I need to take this one step further and make it function almost like an alarm clock. What I'm basically looking to achieve is 15 minutes before each event, it needs to highlight the upcoming event time (i.e. using jQuery addClass()). Then as soon as that event time is reached, play a buzzer sound (some kind of MP3 file). This needs to happen automatically every day for every event. Remember the event times are always changing, so it would need to be smart enough to recognise this.
What techniques can I use to achieve this functionality? I have been reading up on things like setTimeout() and setInterval(), however I'm not sure these are able to "auto-update" themselves once they have been set (i.e. if an event time changes). Do I need to look at a nodeJs based solution? I don't have any experience in nodeJs but if that's the best way to achieve this then I'm willing to give it a go. Otherwise I'm more than happy to try out something in vanilla JS.
Here's how I would approach it using setTimeout() but obviously this doesn't dynamically update:
// set the number of ms to 15 mins before the event time
var eventInterval = 36000000;
// setTimeout function for the event
setTimeout(function() {
// add "active" class to highlight the event
$('.event').addClass('active');
// after 15 mins have elapsed, remove the "active" class
setTimeout(function() {
$('.event').removeClass('active');
}, 90000);
}, eventInterval;
Your approach is fine, however, you need to do that EVERY TIME you get an AJAX response. setTimeout returns a timeoutId, which then you can use for cancelling the timeout with clearTimeout(timeoutId).
var reminderTime = 15 * 60 * 1000;
var timeoutIds = [];
function setTime(timestamp) {
// Set the interval 15 minutes before the event time.
var interval = timestamp - reminderTime;
var timeoutId = setTimeout(function() {
// add "active" class to highlight the event
$('.event').addClass('active');
// after 15 mins have elapsed, remove the "active" class
setTimeout(function() {
$('.event').removeClass('active');
}, 90000);
}, interval);
timeoutIds.push(timeoutId);
}
$.get("http://myserver/getTimes", function(times) {
// Reset all the setTimeouts
timeoutIds.forEach(function(timeoutId) {
clearTimeout(timeoutId);
});
// Assuming times is an array of timestamps
times.forEach(setTime);
});

How to prevent users from affecting number of button clicked times

I have a game written in JavaScript and what it basically does is start a ten seconds timer and register the number of times the user is able to click on a certain button, before the timer elapses.
How the code works:
When a user clicks on the button, an element gets added to an array, using push function, then a different function returns the length of the array as the number of times clicked.
The problem with this:
If a user opens up the dev tools and alters the number of times an element is added to the array per click, this will change the outcome of the result.
My Approach:
What I decided to do is to store the length before I ran the push function and also after I ran the push function, then compare their differences and if it's greater than 1, it means something is not right. This seemed to work in my head until I wrote it down in code and discovered that if the user pushed multiple times before I checked the differences then it would go unnoticed. Please big brained guys, help me.
My code:
$('body').on('click', '.btn.z', function () {
// start listening
startCountingClicks()
})
var timerStarted = false;
var tc = [];
function startCountingClicks () {
$('.btn.z').html('ClickZed');
$('.Score').html('Your Score: '+gettc()+" clicks");
if (timerStarted == false) {
startTimer(10, $('#time'));
}
// user does multiple push before this function: startCountingClicks is called
var previous_length = tc.length; // get length before push
tc.push(1);
var new_length = tc.length; // get length after push
if (new_length - previous_length !== 1) {
console.log("fraud"); // this is supposed to catch a thief!!!
}
console.log(new_length+" "+previous_length);
timerStarted = true;
}
function gettc (){
// get number of clicks
return tc.length ;
}
A code that totally breaks this:
$('button').click(function(){tc.push(1); tc.push(1)})
EDIT:
I do not wish to protect against dev tools, though I am not against that method if it works. I just wish to get a better way of counting my clicks, a way that can't be affected by writing code on the dev tools.
You can't really stop people from doing stuff on the client side. It is pointless trying to prevent that. The best thing you can do is make sure whatever is sent matches what you expect on the server side.

Knockout setTimeout delay

When a user logs into my website, I store a "logged in until" parameter on my server for that user, which is updated each time they interact with the server. So if they are inactive for X seconds, they should be logged out and the page on their client refreshed to show the login screen. Everything works nicely except the function keeps calling itself at 1 second intervals, and not the 1700+ second interval I'm looking for. As each call hits the server, I don't want this to keep running more often than it needs to.
This function is contained within my knockout viewmodel. logoutTimer is declared as a global variable. Watching the console log I can see the delay shows the correct time, but it seems to be running about every 1 second.
self.autoLoginCheck = function() {
clearTimeout(logoutTimer);
//If not, refresh the main screen => that'll push them to the login screen automatically
myurl = "/api/checkLoginStatus.php";
var parameters = {
'userPublicID':self.userInfo().userPublicID
}
$.post(myurl, parameters, function(data) {
var getResponse = JSON.parse(data);
if (getResponse.loggedIn < 1 ) {
//This is the log out condition
location.reload(true);
} else {
//Check how many more seconds the current log in is good for, that's when we'll check it again
var msdelay = getResponse.nextCheck * 1000;
console.log("Delay is " + msdelay);
logoutTimer = setTimeout(self.autoLoginCheck(), msdelay);
}
});
}
I know this type of question has been asked many times, but reading all those answers I can't see why this function is running every second and not waiting 30 minutes. Thanks to whoever shows me the obvious!
The first parameter to setTimeout should be a function, not the result of a function.
logoutTimer = setTimeout(self.autoLoginCheck, msdelay);

Synchronizing sync and local chrome.storage

I would like to know how to handle both local and sync storage in the right way in Chrome extension please.
This is my case:
I'm working on an extension for only a specific site (for now),
which contains a content-script and a popup.
The popup contains options where the user can make changes, then the values are sent to the content-script to show the changes on the page.
I'm looking to make as less saving and retrieving storage tasks as possible, and that in the end it will get saved in the sync storage and not just in local.
The sync storage got a per-minute limit, where the local one doesn't.
I know how to listen to the popup closed call from the content-script using a long-lived connection and listen to the onConnect and onDisconnect, and then I can do a save task, but is there a better way to save reading and writing to the storage?
All I can think of was having a background script where I can store the changes in variables and then just send them back and forward to and from the content-script and popup, so it's like having a storage without actually using the storage, but then how can I detect when the user leaves the specific domain and then do the single saving task, and also close/stop the background/event script?
The current limit on chrome.storage.sync sustained operations is 1 every 2 seconds (more accurately 1800 per hour), and a burst rate limit of 120 per minute.
So, your job is to ensure sync happens no more often than once per 2 seconds.
I would make an event page that deals with chrome.storage.onChanged event and syncs the two areas. Which is a surprisingly hard task due to local echo!
// event.js, goes into background.scripts in manifest
// Those will not persist if event page is unloaded
var timeout;
var queuedChanges = {};
var syncStamp = 1;
chrome.storage.onChanged.addListener(function(changes, area) {
// Check if it's an echo of our changes
if(changes._syncStamp && changes._syncStamp.newValue == syncStamp) {
return;
}
if(area == "local") {
// Change in local storage: queue a flush to sync
// Reset timeout
if(timeout) { clearTimeout(timeout); }
// Merge changes with already queued ones
for(var key in changes) {
// Just overwrite old change; we don't care about last newValue
queuedChanges[key] = changes[key];
}
// Schedule flush
timeout = setTimeout(flushToSync, 3000);
} else {
// Change in sync storage: copy to local
if(changes._syncStamp && changes._syncStamp.newValue) {
// Ignore those changes when they echo as local
syncStamp = changes._syncStamp.newValue;
}
commitChanges(changes, chrome.storage.local);
}
});
function flushToSync() {
// Be mindful of what gets synced: there are also size quotas
// If needed, filter queuedChanges here
// Generate a new sync stamp
// With random instead of sequential, there's a really tiny chance
// changes will be ignored, but no chance of stamp overflow
syncStamp = Math.random();
queuedChanges._syncStamp = {newValue: syncStamp};
// Process queue for committing
commitChanges(queuedChanges, chrome.storage.sync);
// Reset queue
queuedChanges = {};
timeout = undefined;
}
function commitChanges(changes, storage) {
var setData = {};
for(var key in changes) {
setData[key] = changes[key].newValue;
}
storage.set(setData, function() {
if(chrome.runtime.lastError) {
console.error(chrome.runtime.lastError.message);
}
});
}
The idea here is to sync 3 seconds after the last change to local. Each new change is added to the queue and resets the countdown. And while Chrome normally does not honor DOM timers in event pages, 3 seconds is short enough to complete before the page is shut down.
Also, note that updating an area from this code will fire the event again. This is considered a bug (compare with window.onstorage not firing for changes within current document), but meanwhile I added the _syncStamp property. It is used to distinguish the local echo, though there is a tiny chance that the stamp will result in a collision
Your other code (content script) should probably also rely on onChanged event instead of a custom "okay, I changed a value!" message.

Categories

Resources