Skip to content

Commit

Permalink
Merge pull request #554 from mohsin-r/541
Browse files Browse the repository at this point in the history
Fix timer slowdown on inactive tab (#554)
  • Loading branch information
szczz authored Mar 6, 2025
2 parents 886beab + 10033ab commit 7a87131
Show file tree
Hide file tree
Showing 7 changed files with 204 additions and 16 deletions.
1 change: 1 addition & 0 deletions index-ca-en.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

<title>RAMP Storylines Editor</title>

<script src="./scripts/hack-timer/HackTimer.js" type="text/javascript" charset="utf-8"></script>
<script src="https://code.highcharts.com/highcharts.js" type="text/javascript" charset="utf-8"></script>
<script src="https://code.highcharts.com/modules/data.js" type="text/javascript" charset="utf-8"></script>
<script src="scripts/highcharts-editor/highcharts-editor.complete.js" type="text/javascript"></script>
Expand Down
1 change: 1 addition & 0 deletions index-ca-fr.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

<title>Éditeur de scénarios de la PCAR</title>

<script src="./scripts/hack-timer/HackTimer.js" type="text/javascript" charset="utf-8"></script>
<script src="https://code.highcharts.com/highcharts.js" type="text/javascript" charset="utf-8"></script>
<script src="https://code.highcharts.com/modules/data.js" type="text/javascript" charset="utf-8"></script>
<script src="scripts/highcharts-editor/highcharts-editor.complete.js" type="text/javascript"></script>
Expand Down
1 change: 1 addition & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />

<script src="./scripts/hack-timer/HackTimer.js" type="text/javascript" charset="utf-8"></script>
<script src="https://code.highcharts.com/highcharts.js" type="text/javascript" charset="utf-8"></script>
<script src="https://code.highcharts.com/modules/data.js" type="text/javascript" charset="utf-8"></script>
<script src="scripts/highcharts-editor/highcharts-editor.complete.js" type="text/javascript"></script>
Expand Down
149 changes: 149 additions & 0 deletions scripts/hack-timer/HackTimer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
(function (workerScript) {
if (!/MSIE 10/i.test(navigator.userAgent)) {
try {
var blob = new Blob([
"\
var fakeIdToId = {};\
onmessage = function (event) {\
var data = event.data,\
name = data.name,\
fakeId = data.fakeId,\
time;\
if(data.hasOwnProperty('time')) {\
time = data.time;\
}\
switch (name) {\
case 'setInterval':\
fakeIdToId[fakeId] = setInterval(function () {\
postMessage({fakeId: fakeId});\
}, time);\
break;\
case 'clearInterval':\
if (fakeIdToId.hasOwnProperty (fakeId)) {\
clearInterval(fakeIdToId[fakeId]);\
delete fakeIdToId[fakeId];\
}\
break;\
case 'setTimeout':\
fakeIdToId[fakeId] = setTimeout(function () {\
postMessage({fakeId: fakeId});\
if (fakeIdToId.hasOwnProperty (fakeId)) {\
delete fakeIdToId[fakeId];\
}\
}, time);\
break;\
case 'clearTimeout':\
if (fakeIdToId.hasOwnProperty (fakeId)) {\
clearTimeout(fakeIdToId[fakeId]);\
delete fakeIdToId[fakeId];\
}\
break;\
}\
}\
"
]);
// Obtain a blob URL reference to our worker 'file'.
workerScript = window.URL.createObjectURL(blob);
} catch (error) {
/* Blob is not supported, use external script instead */
}
}
var worker,
fakeIdToCallback = {},
lastFakeId = 0,
maxFakeId = 0x7fffffff, // 2 ^ 31 - 1, 31 bit, positive values of signed 32 bit integer
logPrefix = 'HackTimer.js by turuslan: ';
if (typeof Worker !== 'undefined') {
function getFakeId() {
do {
if (lastFakeId == maxFakeId) {
lastFakeId = 0;
} else {
lastFakeId++;
}
} while (fakeIdToCallback.hasOwnProperty(lastFakeId));
return lastFakeId;
}
try {
worker = new Worker(workerScript);
window.setInterval = function (callback, time /* , parameters */) {
var fakeId = getFakeId();
fakeIdToCallback[fakeId] = {
callback: callback,
parameters: Array.prototype.slice.call(arguments, 2)
};
worker.postMessage({
name: 'setInterval',
fakeId: fakeId,
time: time
});
return fakeId;
};
window.clearInterval = function (fakeId) {
if (fakeIdToCallback.hasOwnProperty(fakeId)) {
delete fakeIdToCallback[fakeId];
worker.postMessage({
name: 'clearInterval',
fakeId: fakeId
});
}
};
window.setTimeout = function (callback, time /* , parameters */) {
var fakeId = getFakeId();
fakeIdToCallback[fakeId] = {
callback: callback,
parameters: Array.prototype.slice.call(arguments, 2),
isTimeout: true
};
worker.postMessage({
name: 'setTimeout',
fakeId: fakeId,
time: time
});
return fakeId;
};
window.clearTimeout = function (fakeId) {
if (fakeIdToCallback.hasOwnProperty(fakeId)) {
delete fakeIdToCallback[fakeId];
worker.postMessage({
name: 'clearTimeout',
fakeId: fakeId
});
}
};
worker.onmessage = function (event) {
var data = event.data,
fakeId = data.fakeId,
request,
parameters,
callback;
if (fakeIdToCallback.hasOwnProperty(fakeId)) {
request = fakeIdToCallback[fakeId];
callback = request.callback;
parameters = request.parameters;
if (request.hasOwnProperty('isTimeout') && request.isTimeout) {
delete fakeIdToCallback[fakeId];
}
}
if (typeof callback === 'string') {
try {
callback = new Function(callback);
} catch (error) {
console.log(logPrefix + 'Error parsing callback code string: ', error);
}
}
if (typeof callback === 'function') {
callback.apply(window, parameters);
}
};
worker.onerror = function (event) {
console.log(event);
};
} catch (error) {
console.log(logPrefix + 'Initialisation failed');
console.error(error);
}
} else {
console.log(logPrefix + 'Initialisation failed - HTML5 Web Worker is not supported');
}
})('HackTimerWorker.js');
37 changes: 37 additions & 0 deletions scripts/hack-timer/HackTimerWorker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
var fakeIdToId = {};
onmessage = function (event) {
var data = event.data,
name = data.name,
fakeId = data.fakeId,
time;
if (data.hasOwnProperty('time')) {
time = data.time;
}
switch (name) {
case 'setInterval':
fakeIdToId[fakeId] = setInterval(function () {
postMessage({ fakeId: fakeId });
}, time);
break;
case 'clearInterval':
if (fakeIdToId.hasOwnProperty(fakeId)) {
clearInterval(fakeIdToId[fakeId]);
delete fakeIdToId[fakeId];
}
break;
case 'setTimeout':
fakeIdToId[fakeId] = setTimeout(function () {
postMessage({ fakeId: fakeId });
if (fakeIdToId.hasOwnProperty(fakeId)) {
delete fakeIdToId[fakeId];
}
}, time);
break;
case 'clearTimeout':
if (fakeIdToId.hasOwnProperty(fakeId)) {
clearTimeout(fakeIdToId[fakeId]);
delete fakeIdToId[fakeId];
}
break;
}
};
16 changes: 8 additions & 8 deletions server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -595,10 +595,10 @@ function logger(type, message) {
const clients = new Set();

// Used to broadcast messages to all connected clients
function broadcastToClients(message){
function broadcastToClients(message) {
const payload = JSON.stringify(message);
clients.forEach((client) => {
if(client.readyState === WebSocket.OPEN){
if (client.readyState === (process.env.SERVER_CURR_ENV !== '#{CURR_ENV}#' ? WebSocket.OPEN : 1)) {
logger('INFO', `Payload sent to the client`);
client.send(payload);
}
Expand All @@ -614,7 +614,7 @@ wss.on('connection', (ws) => {
// { uuid: <uuid>, lock: false }
ws.on('message', function (msg) {
const message = JSON.parse(msg);
const {uuid, lock} = message;
const { uuid, lock } = message;

if (!uuid) {
ws.send(JSON.stringify({ status: 'fail', message: 'UUID not provided.' }));
Expand All @@ -640,8 +640,8 @@ wss.on('connection', (ws) => {
ws.send(JSON.stringify({ status: 'success', secret }));

broadcastToClients({
type:'lock',
uuid,
type: 'lock',
uuid
});
}
} else {
Expand All @@ -663,8 +663,8 @@ wss.on('connection', (ws) => {
ws.send(JSON.stringify({ status: 'success' }));

broadcastToClients({
type:'unlock',
uuid,
type: 'unlock',
uuid
});
}
}
Expand All @@ -680,7 +680,7 @@ wss.on('connection', (ws) => {
delete lockedUuids[ws.uuid];
broadcastToClients({
type: 'unlock',
uuid: ws.uuid,
uuid: ws.uuid
});
}
}
Expand Down
15 changes: 7 additions & 8 deletions src/stores/lockStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ export const useLockStore = defineStore('lock', {
result: {} as any,
broadcast: undefined as BroadcastChannel | undefined,
confirmationTimeout: undefined as NodeJS.Timeout | undefined, // the timer to show the session extension confirmation modal
endTimeout: undefined as NodeJS.Timeout | undefined, // the timer to kill the session due to timeout
endTimeout: undefined as NodeJS.Timeout | undefined // the timer to kill the session due to timeout
}),
actions: {
// Opens a connection with the web socket
initConnection() {
return new Promise((resolve) => {
return new Promise<void>((resolve) => {
const socketUrl = `${
import.meta.env.VITE_APP_CURR_ENV ? import.meta.env.VITE_APP_API_URL : 'http://localhost:6040'
}`;
Expand All @@ -37,7 +37,7 @@ export const useLockStore = defineStore('lock', {
};
});
},
// Attempts to lock a storyline for this user.
// Attempts to lock a storyline for this user.
// Returns a promise that resolves if the lock was successfully fetched and rejects if it was not.
async lockStoryline(uuid: string): Promise<void> {
// Stop the previous storyline's timer
Expand All @@ -55,16 +55,15 @@ export const useLockStore = defineStore('lock', {
const handleMessage = (event: MessageEvent) => {
const data = JSON.parse(event.data);

if(data !== undefined){
if(data.status === 'fail'){
if (data !== undefined) {
if (data.status === 'fail') {
this.socket!.removeEventListener('message', handleMessage);
reject(new Error(data.message || 'Failed to lock storyline.'));
}
else if (data.status === 'success') {
} else if (data.status === 'success') {
this.socket!.removeEventListener('message', handleMessage);

this.uuid = uuid;
this.secret = data.secret;
this.secret = data.secret;
this.broadcast = new BroadcastChannel(data.secret);

resolve();
Expand Down

0 comments on commit 7a87131

Please sign in to comment.