diff --git a/app/static/js/app.js b/app/static/js/app.js
index e8ffddfc..fc52a7f2 100644
--- a/app/static/js/app.js
+++ b/app/static/js/app.js
@@ -2,33 +2,19 @@ nopaque.App = class App {
constructor() {
this.socket = io({transports: ['websocket'], upgrade: false});
- this.ui = new nopaque.UIExtension(this);
- this.liveUserRegistry = new nopaque.LiveUserRegistryExtension(this);
- this.users = new nopaque.UsersExtension(this);
+ // Endpoints
+ this.users = new nopaque.app.endpoints.Users(this);
+
+ // Extensions
+ this.toaster = new nopaque.app.extensions.Toaster(this);
+ this.ui = new nopaque.app.extensions.UI(this);
+ this.userHub = new nopaque.app.extensions.UserHub(this);
}
- // onPatch(patch) {
- // // Filter Patch to only include operations on users that are initialized
- // let regExp = new RegExp(`^/users/(${Object.keys(this.data.users).join('|')})`);
- // let filteredPatch = patch.filter(operation => regExp.test(operation.path));
-
- // // Handle job status updates
- // let subRegExp = new RegExp(`^/users/([A-Za-z0-9]*)/jobs/([A-Za-z0-9]*)/status$`);
- // let subFilteredPatch = filteredPatch
- // .filter((operation) => {return operation.op === 'replace';})
- // .filter((operation) => {return subRegExp.test(operation.path);});
- // for (let operation of subFilteredPatch) {
- // let [match, userId, jobId] = operation.path.match(subRegExp);
- // this.flash(`[${this.data.users[userId].jobs[jobId].title}] New status: `, 'job');
- // }
-
- // // Apply Patch
- // jsonpatch.applyPatch(this.data, filteredPatch);
- // }
-
init() {
+ // Initialize extensions
+ this.toaster.init();
this.ui.init();
- this.liveUserRegistry.init();
- this.users.init();
+ this.userHub.init();
}
};
diff --git a/app/static/js/app/endpoints/index.js b/app/static/js/app/endpoints/index.js
new file mode 100644
index 00000000..f94fbc36
--- /dev/null
+++ b/app/static/js/app/endpoints/index.js
@@ -0,0 +1 @@
+nopaque.app.endpoints = {};
diff --git a/app/static/js/app/users.js b/app/static/js/app/endpoints/users.js
similarity index 94%
rename from app/static/js/app/users.js
rename to app/static/js/app/endpoints/users.js
index 0b98f95d..b1bfa7fd 100644
--- a/app/static/js/app/users.js
+++ b/app/static/js/app/endpoints/users.js
@@ -1,12 +1,10 @@
-nopaque.UsersExtension = class UsersExtension {
+nopaque.app.endpoints.Users = class Users {
constructor(app) {
this.app = app;
this.socket = io('/users', {transports: ['websocket'], upgrade: false});
}
- init() {}
-
async get(userId) {
const response = await this.socket.emitWithAck('get', userId);
diff --git a/app/static/js/app/extensions/index.js b/app/static/js/app/extensions/index.js
new file mode 100644
index 00000000..163fecb9
--- /dev/null
+++ b/app/static/js/app/extensions/index.js
@@ -0,0 +1 @@
+nopaque.app.extensions = {};
diff --git a/app/static/js/app/extensions/toaster.js b/app/static/js/app/extensions/toaster.js
new file mode 100644
index 00000000..876fc7b3
--- /dev/null
+++ b/app/static/js/app/extensions/toaster.js
@@ -0,0 +1,51 @@
+nopaque.app.extensions.Toaster = class Toaster {
+ constructor(app) {
+ this.app = app;
+ }
+
+ init() {
+ this.app.userHub.addEventListener('patch', (event) => {this.#onPatch(event.detail);});
+ }
+
+ #onPatch(patch) {
+ // Handle job updates
+ const jobRegExp = new RegExp(`^/users/([A-Za-z0-9]+)/jobs/([A-Za-z0-9]+)`);
+ const jobPatch = patch.filter((operation) => {return jobRegExp.test(operation.path);});
+
+ this.#onJobPatch(jobPatch);
+
+ // Handle corpus updates
+ const corpusRegExp = new RegExp(`^/users/([A-Za-z0-9]+)/corpora/([A-Za-z0-9]+)`);
+ const corpusPatch = patch.filter((operation) => {return corpusRegExp.test(operation.path);});
+
+ this.#onCorpusPatch(corpusPatch);
+ }
+
+ async #onJobPatch(patch) {
+ // Handle job status updates
+ const jobStatusRegExp = new RegExp(`^/users/([A-Za-z0-9]+)/jobs/([A-Za-z0-9]+)/status$`);
+ const jobStatusPatch = patch.filter((operation) => {return operation.op === 'replace';});
+
+ for (let operation of jobStatusPatch) {
+ const [match, userId, jobId] = operation.path.match(jobStatusRegExp);
+ const user = await this.app.userHub.get(userId);
+ const job = user.jobs[jobId];
+
+ this.app.ui.flash(`[${job.title}] New status: `, 'job');
+ }
+ }
+
+ async #onCorpusPatch(patch) {
+ // Handle job status updates
+ const corpusStatusRegExp = new RegExp(`^/users/([A-Za-z0-9]+)/corpora/([A-Za-z0-9]+)/status$`);
+ const corpusStatusPatch = patch.filter((operation) => {return operation.op === 'replace';});
+
+ for (let operation of corpusStatusPatch) {
+ const [match, userId, corpusId] = operation.path.match(corpusStatusRegExp);
+ const user = await this.app.userHub.get(userId);
+ const corpus = user.corpora[corpusId];
+
+ this.app.ui.flash(`[${corpus.title}] New status: `, 'job');
+ }
+ }
+}
diff --git a/app/static/js/app/ui.js b/app/static/js/app/extensions/ui.js
similarity index 98%
rename from app/static/js/app/ui.js
rename to app/static/js/app/extensions/ui.js
index 150cccec..439d247d 100644
--- a/app/static/js/app/ui.js
+++ b/app/static/js/app/extensions/ui.js
@@ -1,4 +1,4 @@
-nopaque.UIExtension = class UIExtension {
+nopaque.app.extensions.UI = class UI {
constructor(app) {
this.app = app;
}
diff --git a/app/static/js/app/user-live-registry.js b/app/static/js/app/extensions/user-hub.js
similarity index 61%
rename from app/static/js/app/user-live-registry.js
rename to app/static/js/app/extensions/user-hub.js
index 27c1fe34..41f642ea 100644
--- a/app/static/js/app/user-live-registry.js
+++ b/app/static/js/app/extensions/user-hub.js
@@ -1,4 +1,4 @@
-nopaque.LiveUserRegistryExtension = class LiveUserRegistryExtension extends EventTarget {
+nopaque.app.extensions.UserHub = class UserHub extends EventTarget {
#data;
constructor(app) {
@@ -36,35 +36,33 @@ nopaque.LiveUserRegistryExtension = class LiveUserRegistryExtension extends Even
#onPatch(patch) {
// Filter patch to only include operations on users that are initialized
- let filterRegExp = new RegExp(`^/users/(${Object.keys(this.#data.users).join('|')})`);
- let filteredPatch = patch.filter(operation => filterRegExp.test(operation.path));
+ const filterRegExp = new RegExp(`^/users/(${Object.keys(this.#data.users).join('|')})`);
+ const filteredPatch = patch.filter(operation => filterRegExp.test(operation.path));
// Apply patch
jsonpatch.applyPatch(this.#data, filteredPatch);
// Notify event listeners
- let event = new CustomEvent('patch', {detail: filteredPatch});
- this.dispatchEvent(event);
+ const patchEventa = new CustomEvent('patch', {detail: filteredPatch});
+ this.dispatchEvent(patchEventa);
- /*
// Notify event listeners. Event type: "patch *"
- let event = new CustomEvent('patch *', {detail: filteredPatch});
- this.dispatchEvent(event);
+ const patchEvent = new CustomEvent('patch *', {detail: filteredPatch});
+ this.dispatchEvent(patchEvent);
// Group patches by user id: {: [op, ...], ...}
- let patches = {};
- let matchRegExp = new RegExp(`^/users/([A-Za-z0-9]+)`);
+ const patches = {};
+ const matchRegExp = new RegExp(`^/users/([A-Za-z0-9]+)`);
for (let operation of filteredPatch) {
- let [match, userId] = operation.path.match(matchRegExp);
+ const [match, userId] = operation.path.match(matchRegExp);
if (!(userId in patches)) {patches[userId] = [];}
patches[userId].push(operation);
}
// Notify event listeners. Event type: "patch "
for (let [userId, patch] of Object.entries(patches)) {
- let event = new CustomEvent(`patch ${userId}`, {detail: patch});
- this.dispatchEvent(event);
+ const userPatchEvent = new CustomEvent(`patch ${userId}`, {detail: patch});
+ this.dispatchEvent(userPatchEvent);
}
- */
}
}
diff --git a/app/static/js/app/index.js b/app/static/js/app/index.js
new file mode 100644
index 00000000..2d332651
--- /dev/null
+++ b/app/static/js/app/index.js
@@ -0,0 +1 @@
+nopaque.app = {};
diff --git a/app/static/js/resource-displays/resource-display.js b/app/static/js/resource-displays/resource-display.js
index 9591834e..4752a340 100644
--- a/app/static/js/resource-displays/resource-display.js
+++ b/app/static/js/resource-displays/resource-display.js
@@ -6,10 +6,10 @@ nopaque.resource_displays.ResourceDisplay = class ResourceDisplay {
this.userId = this.displayElement.dataset.userId;
this.isInitialized = false;
if (this.userId === undefined) {return;}
- app.liveUserRegistry.addEventListener('patch', (event) => {
+ app.userHub.addEventListener('patch', (event) => {
if (this.isInitialized) {this.onPatch(event.detail);}
});
- app.liveUserRegistry.get(this.userId).then((user) => {
+ app.userHub.get(this.userId).then((user) => {
this.init(user);
this.isInitialized = true;
});
diff --git a/app/static/js/resource-lists/corpus-file-list.js b/app/static/js/resource-lists/corpus-file-list.js
index 88761a47..8da872b8 100644
--- a/app/static/js/resource-lists/corpus-file-list.js
+++ b/app/static/js/resource-lists/corpus-file-list.js
@@ -14,10 +14,10 @@ nopaque.resource_lists.CorpusFileList = class CorpusFileList extends nopaque.res
this.hasPermissionView = listContainerElement.dataset?.hasPermissionView == 'true' || false;
this.hasPermissionManageFiles = listContainerElement.dataset?.hasPermissionManageFiles == 'true' || false;
if (this.userId === undefined || this.corpusId === undefined) {return;}
- app.liveUserRegistry.addEventListener('patch', (event) => {
+ app.userHub.addEventListener('patch', (event) => {
if (this.isInitialized) {this.onPatch(event.detail);}
});
- app.liveUserRegistry.get(this.userId).then((user) => {
+ app.userHub.get(this.userId).then((user) => {
// TODO: Make this better understandable
this.add(Object.values(user.corpora[this.corpusId].files || user.followed_corpora[this.corpusId].files));
this.isInitialized = true;
diff --git a/app/static/js/resource-lists/corpus-follower-list.js b/app/static/js/resource-lists/corpus-follower-list.js
index 8aa1f4d0..31031cbc 100644
--- a/app/static/js/resource-lists/corpus-follower-list.js
+++ b/app/static/js/resource-lists/corpus-follower-list.js
@@ -12,10 +12,10 @@ nopaque.resource_lists.CorpusFollowerList = class CorpusFollowerList extends nop
this.userId = listContainerElement.dataset.userId;
this.corpusId = listContainerElement.dataset.corpusId;
if (this.userId === undefined || this.corpusId === undefined) {return;}
- app.liveUserRegistry.addEventListener('patch', (event) => {
+ app.userHub.addEventListener('patch', (event) => {
if (this.isInitialized) {this.onPatch(event.detail);}
});
- app.liveUserRegistry.get(this.userId).then((user) => {
+ app.userHub.get(this.userId).then((user) => {
// TODO: Check if the following is better
// let corpusFollowerAssociations = Object.values(user.corpora[this.corpusId].corpus_follower_associations);
// let filteredList = corpusFollowerAssociations.filter(association => association.follower.id != currentUserId);
diff --git a/app/static/js/resource-lists/corpus-list.js b/app/static/js/resource-lists/corpus-list.js
index 21060dbc..ed242868 100644
--- a/app/static/js/resource-lists/corpus-list.js
+++ b/app/static/js/resource-lists/corpus-list.js
@@ -11,10 +11,10 @@ nopaque.resource_lists.CorpusList = class CorpusList extends nopaque.resource_li
this.selectedItemIds = new Set();
this.userId = listContainerElement.dataset.userId;
if (this.userId === undefined) {return;}
- app.liveUserRegistry.addEventListener('patch', (event) => {
+ app.userHub.addEventListener('patch', (event) => {
if (this.isInitialized) {this.onPatch(event.detail);}
});
- app.liveUserRegistry.get(this.userId).then((user) => {
+ app.userHub.get(this.userId).then((user) => {
this.add(this.aggregateData(user));
this.isInitialized = true;
});
diff --git a/app/static/js/resource-lists/job-input-list.js b/app/static/js/resource-lists/job-input-list.js
index 7c74e852..55b539d1 100644
--- a/app/static/js/resource-lists/job-input-list.js
+++ b/app/static/js/resource-lists/job-input-list.js
@@ -8,10 +8,10 @@ nopaque.resource_lists.JobInputList = class JobInputList extends nopaque.resourc
this.userId = listContainerElement.dataset.userId;
this.jobId = listContainerElement.dataset.jobId;
if (this.userId === undefined || this.jobId === undefined) {return;}
- // app.liveUserRegistry.addEventListener('patch', (event) => {
+ // app.userHub.addEventListener('patch', (event) => {
// if (this.isInitialized) {this.onPatch(event.detail);}
// });
- app.liveUserRegistry.get(this.userId).then((user) => {
+ app.userHub.get(this.userId).then((user) => {
this.add(Object.values(user.jobs[this.jobId].inputs));
this.isInitialized = true;
});
diff --git a/app/static/js/resource-lists/job-list.js b/app/static/js/resource-lists/job-list.js
index 8c352e7e..43a3b58f 100644
--- a/app/static/js/resource-lists/job-list.js
+++ b/app/static/js/resource-lists/job-list.js
@@ -12,10 +12,10 @@ nopaque.resource_lists.JobList = class JobList extends nopaque.resource_lists.Re
this.selectedItemIds = new Set();
this.userId = listContainerElement.dataset.userId;
if (this.userId === undefined) {return;}
- app.liveUserRegistry.addEventListener('patch', (event) => {
+ app.userHub.addEventListener('patch', (event) => {
if (this.isInitialized) {this.onPatch(event.detail);}
});
- app.liveUserRegistry.get(this.userId).then((user) => {
+ app.userHub.get(this.userId).then((user) => {
this.add(Object.values(user.jobs));
this.isInitialized = true;
});
diff --git a/app/static/js/resource-lists/job-result-list.js b/app/static/js/resource-lists/job-result-list.js
index be3d618c..c65ac2e3 100644
--- a/app/static/js/resource-lists/job-result-list.js
+++ b/app/static/js/resource-lists/job-result-list.js
@@ -8,10 +8,10 @@ nopaque.resource_lists.JobResultList = class JobResultList extends nopaque.resou
this.userId = listContainerElement.dataset.userId;
this.jobId = listContainerElement.dataset.jobId;
if (this.userId === undefined || this.jobId === undefined) {return;}
- app.liveUserRegistry.addEventListener('patch', (event) => {
+ app.userHub.addEventListener('patch', (event) => {
if (this.isInitialized) {this.onPatch(event.detail);}
});
- app.liveUserRegistry.get(this.userId).then((user) => {
+ app.userHub.get(this.userId).then((user) => {
this.add(Object.values(user.jobs[this.jobId].results));
this.isInitialized = true;
});
diff --git a/app/static/js/resource-lists/spacy-nlp-pipeline-model-list.js b/app/static/js/resource-lists/spacy-nlp-pipeline-model-list.js
index e44e35b9..49b97bfc 100644
--- a/app/static/js/resource-lists/spacy-nlp-pipeline-model-list.js
+++ b/app/static/js/resource-lists/spacy-nlp-pipeline-model-list.js
@@ -8,10 +8,10 @@ nopaque.resource_lists.SpaCyNLPPipelineModelList = class SpaCyNLPPipelineModelLi
this.isInitialized = false;
this.userId = listContainerElement.dataset.userId;
if (this.userId === undefined) {return;}
- app.liveUserRegistry.addEventListener('patch', (event) => {
+ app.userHub.addEventListener('patch', (event) => {
if (this.isInitialized) {this.onPatch(event.detail);}
});
- app.liveUserRegistry.get(this.userId).then((user) => {
+ app.userHub.get(this.userId).then((user) => {
this.add(Object.values(user.spacy_nlp_pipeline_models));
this.isInitialized = true;
});
diff --git a/app/static/js/resource-lists/tesseract-ocr-pipeline-model-list.js b/app/static/js/resource-lists/tesseract-ocr-pipeline-model-list.js
index 0b0cd384..f8e5986a 100644
--- a/app/static/js/resource-lists/tesseract-ocr-pipeline-model-list.js
+++ b/app/static/js/resource-lists/tesseract-ocr-pipeline-model-list.js
@@ -8,10 +8,10 @@ nopaque.resource_lists.TesseractOCRPipelineModelList = class TesseractOCRPipelin
this.isInitialized = false;
this.userId = listContainerElement.dataset.userId;
if (this.userId === undefined) {return;}
- app.liveUserRegistry.addEventListener('patch', (event) => {
+ app.userHub.addEventListener('patch', (event) => {
if (this.isInitialized) {this.onPatch(event.detail);}
});
- app.liveUserRegistry.get(this.userId).then((user) => {
+ app.userHub.get(this.userId).then((user) => {
this.add(Object.values(user.tesseract_ocr_pipeline_models));
this.isInitialized = true;
});
diff --git a/app/templates/_base/scripts.html.j2 b/app/templates/_base/scripts.html.j2
index 9559ab1c..5bb3d1de 100644
--- a/app/templates/_base/scripts.html.j2
+++ b/app/templates/_base/scripts.html.j2
@@ -9,9 +9,13 @@
output='gen/nopaque.%(version)s.js',
'js/index.js',
'js/app.js',
- 'js/app/ui.js',
- 'js/app/user-live-registry.js',
- 'js/app/users.js',
+ 'js/app/index.js',
+ 'js/app/endpoints/index.js',
+ 'js/app/endpoints/users.js',
+ 'js/app/extensions/index.js',
+ 'js/app/extensions/toaster.js',
+ 'js/app/extensions/ui.js',
+ 'js/app/extensions/user-hub.js',
'js/utils.js',
'js/forms/index.js',
@@ -80,16 +84,18 @@
const app = new nopaque.App();
app.init();
- {% if current_user.is_authenticated -%}
+ {% if current_user.is_authenticated %}
const currentUserId = {{ current_user.hashid|tojson }};
- app.liveUserRegistry.add(currentUserId)
+ app.userHub.add(currentUserId)
.catch((error) => {throw JSON.stringify(error);});
- {% if not current_user.terms_of_use_accepted -%}
+ {% if not current_user.terms_of_use_accepted %}
M.Modal.getInstance(document.querySelector('#terms-of-use-modal')).open();
- {% endif -%}
- {% endif -%}
+ {% endif %}
+ {% else %}
+ const currentUserId = null;
+ {% endif %}
// Display flashed messages
for (let [category, message] of {{ get_flashed_messages(with_categories=True)|tojson }}) {