Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix timer slowdown on inactive tab #554

Merged
merged 1 commit into from
Mar 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
4 changes: 2 additions & 2 deletions src/components/metadata-editor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -594,7 +594,7 @@ import {
import { VueSpinnerOval } from 'vue3-spinners';
import { VueFinalModal } from 'vue-final-modal';
import { useUserStore } from '../stores/userStore';
import { computed } from "vue";
import { computed } from 'vue';

import JSZip from 'jszip';
import axios from 'axios';
Expand Down Expand Up @@ -763,7 +763,7 @@ export default class MetadataEditorV extends Vue {
// Initialize Storylines config and the configuration structure.
this.configs = { en: undefined, fr: undefined };
this.configFileStructure = undefined;

// set any metadata default values for creating new product
if (!this.loadExisting) {
// set current date as default
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
Loading