mirror of
https://gitlab.ub.uni-bielefeld.de/sfb1288inf/nopaque.git
synced 2024-12-25 10:54:18 +00:00
Update JS code structure
This commit is contained in:
parent
a2904caea2
commit
12a3ac1d5d
@ -1,11 +1,51 @@
|
||||
from flask import current_app, Flask
|
||||
from flask_login import current_user
|
||||
from flask_socketio import join_room, leave_room
|
||||
from app import hashids, socketio
|
||||
from app import db, hashids, socketio
|
||||
from app.decorators import socketio_login_required
|
||||
from app.models import User
|
||||
|
||||
|
||||
@socketio.on('users.get_user')
|
||||
def _delete_user(app: Flask, user_id: int):
|
||||
with app.app_context():
|
||||
user = User.query.get(user_id)
|
||||
user.delete()
|
||||
db.session.commit()
|
||||
|
||||
|
||||
@socketio.on('users.delete')
|
||||
@socketio_login_required
|
||||
def delete_user(user_hashid: str) -> dict:
|
||||
user_id = hashids.decode(user_hashid)
|
||||
|
||||
if not isinstance(user_id, int):
|
||||
return {'status': 400, 'statusText': 'Bad Request'}
|
||||
|
||||
user = User.query.get(user_id)
|
||||
|
||||
if user is None:
|
||||
return {'status': 404, 'statusText': 'Not found'}
|
||||
|
||||
if not (
|
||||
user == current_user
|
||||
or current_user.is_administrator
|
||||
):
|
||||
return {'status': 403, 'statusText': 'Forbidden'}
|
||||
|
||||
socketio.start_background_task(
|
||||
_delete_user,
|
||||
current_app._get_current_object(),
|
||||
user.id
|
||||
)
|
||||
|
||||
return {
|
||||
'body': f'User "{user.username}" marked for deletion',
|
||||
'status': 202,
|
||||
'statusText': 'Accepted'
|
||||
}
|
||||
|
||||
|
||||
@socketio.on('users.get')
|
||||
@socketio_login_required
|
||||
def get_user(user_hashid: str) -> dict:
|
||||
user_id = hashids.decode(user_hashid)
|
||||
@ -34,7 +74,7 @@ def get_user(user_hashid: str) -> dict:
|
||||
}
|
||||
|
||||
|
||||
@socketio.on('users.subscribe_user')
|
||||
@socketio.on('users.subscribe')
|
||||
@socketio_login_required
|
||||
def subscribe_user(user_hashid: str) -> dict:
|
||||
user_id = hashids.decode(user_hashid)
|
||||
@ -58,9 +98,9 @@ def subscribe_user(user_hashid: str) -> dict:
|
||||
return {'status': 200, 'statusText': 'OK'}
|
||||
|
||||
|
||||
@socketio.on('users.unsubscribe_user')
|
||||
@socketio.on('users.unsubscribe')
|
||||
@socketio_login_required
|
||||
def on_unsubscribe_user(user_hashid: str) -> dict:
|
||||
def unsubscribe_user(user_hashid: str) -> dict:
|
||||
user_id = hashids.decode(user_hashid)
|
||||
|
||||
if not isinstance(user_id, int):
|
||||
|
@ -43,7 +43,7 @@ def resource_after_delete(mapper, connection, resource):
|
||||
}
|
||||
]
|
||||
room = f'/users/{resource.user_hashid}'
|
||||
socketio.emit('patch_user', jsonpatch, room=room)
|
||||
socketio.emit('users.patch', jsonpatch, room=room)
|
||||
|
||||
|
||||
def cfa_after_delete(mapper, connection, cfa):
|
||||
@ -55,7 +55,7 @@ def cfa_after_delete(mapper, connection, cfa):
|
||||
}
|
||||
]
|
||||
room = f'/users/{cfa.corpus.user.hashid}'
|
||||
socketio.emit('patch_user', jsonpatch, room=room)
|
||||
socketio.emit('users.patch', jsonpatch, room=room)
|
||||
|
||||
|
||||
def resource_after_insert(mapper, connection, resource):
|
||||
@ -70,7 +70,7 @@ def resource_after_insert(mapper, connection, resource):
|
||||
}
|
||||
]
|
||||
room = f'/users/{resource.user_hashid}'
|
||||
socketio.emit('patch_user', jsonpatch, room=room)
|
||||
socketio.emit('users.patch', jsonpatch, room=room)
|
||||
|
||||
|
||||
def cfa_after_insert(mapper, connection, cfa):
|
||||
@ -84,7 +84,7 @@ def cfa_after_insert(mapper, connection, cfa):
|
||||
}
|
||||
]
|
||||
room = f'/users/{cfa.corpus.user.hashid}'
|
||||
socketio.emit('patch_user', jsonpatch, room=room)
|
||||
socketio.emit('users.patch', jsonpatch, room=room)
|
||||
|
||||
|
||||
def resource_after_update(mapper, connection, resource):
|
||||
@ -110,7 +110,7 @@ def resource_after_update(mapper, connection, resource):
|
||||
)
|
||||
if jsonpatch:
|
||||
room = f'/users/{resource.user_hashid}'
|
||||
socketio.emit('patch_user', jsonpatch, room=room)
|
||||
socketio.emit('users.patch', jsonpatch, room=room)
|
||||
|
||||
|
||||
def job_after_update(mapper, connection, job):
|
||||
|
@ -1,201 +1,33 @@
|
||||
nopaque.App = class App {
|
||||
#promises;
|
||||
|
||||
constructor() {
|
||||
this.data = {
|
||||
users: {}
|
||||
};
|
||||
|
||||
this.#promises = {
|
||||
getUser: {},
|
||||
subscribeUser: {}
|
||||
};
|
||||
this.data = {};
|
||||
|
||||
this.socket = io({transports: ['websocket'], upgrade: false});
|
||||
|
||||
this.socket.on('patch_user', (patch) => {this.onPatch(patch);});
|
||||
this.ui = new nopaque.UIExtension(this);
|
||||
this.users = new nopaque.UsersExtension(this);
|
||||
}
|
||||
|
||||
getUser(userId) {
|
||||
if (userId in this.#promises.getUser) {
|
||||
return this.#promises.getUser[userId];
|
||||
}
|
||||
// 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));
|
||||
|
||||
this.#promises.getUser[userId] = new Promise((resolve, reject) => {
|
||||
this.socket.emit('users.get_user', userId, (response) => {
|
||||
if (response.status === 200) {
|
||||
this.data.users[userId] = response.body;
|
||||
resolve(this.data.users[userId]);
|
||||
} else {
|
||||
reject(`[${response.status}] ${response.statusText}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
// // 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(`[<a href="/jobs/${jobId}">${this.data.users[userId].jobs[jobId].title}</a>] New status: <span class="job-status-text" data-job-status="${operation.value}"></span>`, 'job');
|
||||
// }
|
||||
|
||||
return this.#promises.getUser[userId];
|
||||
}
|
||||
|
||||
subscribeUser(userId) {
|
||||
if (userId in this.#promises.subscribeUser) {
|
||||
return this.#promises.subscribeUser[userId];
|
||||
}
|
||||
|
||||
this.#promises.subscribeUser[userId] = new Promise((resolve, reject) => {
|
||||
this.socket.emit('users.subscribe_user', userId, (response) => {
|
||||
if (response.status === 200) {
|
||||
resolve(response);
|
||||
} else {
|
||||
reject(response);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return this.#promises.subscribeUser[userId];
|
||||
}
|
||||
|
||||
flash(message, category) {
|
||||
let iconPrefix = '';
|
||||
switch (category) {
|
||||
case 'corpus': {
|
||||
iconPrefix = '<i class="left material-icons">book</i>';
|
||||
break;
|
||||
}
|
||||
case 'error': {
|
||||
iconPrefix = '<i class="error-color-text left material-icons">error</i>';
|
||||
break;
|
||||
}
|
||||
case 'job': {
|
||||
iconPrefix = '<i class="left nopaque-icons">J</i>';
|
||||
break;
|
||||
}
|
||||
case 'settings': {
|
||||
iconPrefix = '<i class="left material-icons">settings</i>';
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
iconPrefix = '<i class="left material-icons">notifications</i>';
|
||||
break;
|
||||
}
|
||||
}
|
||||
let toast = M.toast(
|
||||
{
|
||||
html: `
|
||||
<span>${iconPrefix}${message}</span>
|
||||
<button class="action-button btn-flat toast-action white-text" data-action="close">
|
||||
<i class="material-icons">close</i>
|
||||
</button>
|
||||
`.trim()
|
||||
}
|
||||
);
|
||||
let toastCloseActionElement = toast.el.querySelector('.action-button[data-action="close"]');
|
||||
toastCloseActionElement.addEventListener('click', () => {toast.dismiss();});
|
||||
}
|
||||
|
||||
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(`[<a href="/jobs/${jobId}">${this.data.users[userId].jobs[jobId].title}</a>] New status: <span class="job-status-text" data-job-status="${operation.value}"></span>`, 'job');
|
||||
}
|
||||
|
||||
// Apply Patch
|
||||
jsonpatch.applyPatch(this.data, filteredPatch);
|
||||
}
|
||||
// // Apply Patch
|
||||
// jsonpatch.applyPatch(this.data, filteredPatch);
|
||||
// }
|
||||
|
||||
init() {
|
||||
this.initUi();
|
||||
}
|
||||
|
||||
initUi() {
|
||||
/* Pre-Initialization fixes */
|
||||
// #region
|
||||
|
||||
// Flask-WTF sets the standard HTML maxlength Attribute on input/textarea
|
||||
// elements to specify their maximum length (in characters). Unfortunatly
|
||||
// Materialize won't recognize the maxlength Attribute, instead it uses
|
||||
// the data-length Attribute. It's conversion time :)
|
||||
for (let elem of document.querySelectorAll('input[maxlength], textarea[maxlength]')) {
|
||||
elem.dataset.length = elem.getAttribute('maxlength');
|
||||
elem.removeAttribute('maxlength');
|
||||
}
|
||||
|
||||
// To work around some limitations with the Form setup of Flask-WTF.
|
||||
// HTML option elements with an empty value are considered as placeholder
|
||||
// elements. The user should not be able to actively select these options.
|
||||
// So they get the disabled attribute.
|
||||
for (let optionElement of document.querySelectorAll('option[value=""]')) {
|
||||
optionElement.disabled = true;
|
||||
}
|
||||
|
||||
// TODO: Check why we are doing this.
|
||||
for (let optgroupElement of document.querySelectorAll('optgroup[label=""]')) {
|
||||
for (let c of optgroupElement.children) {
|
||||
optgroupElement.parentElement.insertAdjacentElement('afterbegin', c);
|
||||
}
|
||||
optgroupElement.remove();
|
||||
}
|
||||
// #endregion
|
||||
|
||||
|
||||
/* Initialize Materialize Components */
|
||||
// #region
|
||||
|
||||
// Automatically initialize Materialize Components that do not require
|
||||
// additional configuration.
|
||||
M.AutoInit();
|
||||
|
||||
// CharacterCounters
|
||||
// Materialize didn't include the CharacterCounter plugin within the
|
||||
// AutoInit method (maybe they forgot it?). Anyway... We do it here. :)
|
||||
M.CharacterCounter.init(document.querySelectorAll('input[data-length]:not(.no-autoinit), textarea[data-length]:not(.no-autoinit)'));
|
||||
|
||||
// Header navigation processes and services Dropdown.
|
||||
M.Dropdown.init(
|
||||
document.querySelector('#navbar-data-processing-and-analysis-dropdown-trigger'),
|
||||
{
|
||||
constrainWidth: false,
|
||||
container: document.querySelector('#dropdowns'),
|
||||
coverTrigger: false
|
||||
}
|
||||
);
|
||||
|
||||
// Header navigation account Dropdown.
|
||||
M.Dropdown.init(
|
||||
document.querySelector('#navbar-account-dropdown-trigger'),
|
||||
{
|
||||
alignment: 'right',
|
||||
constrainWidth: false,
|
||||
container: document.querySelector('#dropdowns'),
|
||||
coverTrigger: false
|
||||
}
|
||||
);
|
||||
|
||||
// Terms of use modal
|
||||
M.Modal.init(
|
||||
document.querySelector('#terms-of-use-modal'),
|
||||
{
|
||||
dismissible: false,
|
||||
onCloseEnd: (modalElement) => {
|
||||
nopaque.requests.users.entity.acceptTermsOfUse();
|
||||
}
|
||||
}
|
||||
);
|
||||
// #endregion
|
||||
|
||||
|
||||
/* Initialize nopaque Components */
|
||||
// #region
|
||||
nopaque.resource_displays.AutoInit();
|
||||
nopaque.resource_lists.AutoInit();
|
||||
nopaque.forms.AutoInit();
|
||||
// #endregion
|
||||
this.ui.init();
|
||||
}
|
||||
};
|
||||
|
126
app/static/js/app.ui.js
Normal file
126
app/static/js/app.ui.js
Normal file
@ -0,0 +1,126 @@
|
||||
nopaque.UIExtension = class UIExtension {
|
||||
constructor(app) {
|
||||
this.app = app;
|
||||
}
|
||||
|
||||
init() {
|
||||
/* Pre-Initialization fixes */
|
||||
// #region
|
||||
|
||||
// Flask-WTF sets the standard HTML maxlength Attribute on input/textarea
|
||||
// elements to specify their maximum length (in characters). Unfortunatly
|
||||
// Materialize won't recognize the maxlength Attribute, instead it uses
|
||||
// the data-length Attribute. It's conversion time :)
|
||||
for (let elem of document.querySelectorAll('input[maxlength], textarea[maxlength]')) {
|
||||
elem.dataset.length = elem.getAttribute('maxlength');
|
||||
elem.removeAttribute('maxlength');
|
||||
}
|
||||
|
||||
// To work around some limitations with the Form setup of Flask-WTF.
|
||||
// HTML option elements with an empty value are considered as placeholder
|
||||
// elements. The user should not be able to actively select these options.
|
||||
// So they get the disabled attribute.
|
||||
for (let optionElement of document.querySelectorAll('option[value=""]')) {
|
||||
optionElement.disabled = true;
|
||||
}
|
||||
|
||||
// TODO: Check why we are doing this.
|
||||
for (let optgroupElement of document.querySelectorAll('optgroup[label=""]')) {
|
||||
for (let c of optgroupElement.children) {
|
||||
optgroupElement.parentElement.insertAdjacentElement('afterbegin', c);
|
||||
}
|
||||
optgroupElement.remove();
|
||||
}
|
||||
// #endregion
|
||||
|
||||
|
||||
/* Initialize Materialize Components */
|
||||
// #region
|
||||
|
||||
// Automatically initialize Materialize Components that do not require
|
||||
// additional configuration.
|
||||
M.AutoInit();
|
||||
|
||||
// CharacterCounters
|
||||
// Materialize didn't include the CharacterCounter plugin within the
|
||||
// AutoInit method (maybe they forgot it?). Anyway... We do it here. :)
|
||||
M.CharacterCounter.init(document.querySelectorAll('input[data-length]:not(.no-autoinit), textarea[data-length]:not(.no-autoinit)'));
|
||||
|
||||
// Header navigation processes and services Dropdown.
|
||||
M.Dropdown.init(
|
||||
document.querySelector('#navbar-data-processing-and-analysis-dropdown-trigger'),
|
||||
{
|
||||
constrainWidth: false,
|
||||
container: document.querySelector('#dropdowns'),
|
||||
coverTrigger: false
|
||||
}
|
||||
);
|
||||
|
||||
// Header navigation account Dropdown.
|
||||
M.Dropdown.init(
|
||||
document.querySelector('#navbar-account-dropdown-trigger'),
|
||||
{
|
||||
alignment: 'right',
|
||||
constrainWidth: false,
|
||||
container: document.querySelector('#dropdowns'),
|
||||
coverTrigger: false
|
||||
}
|
||||
);
|
||||
|
||||
// Terms of use modal
|
||||
M.Modal.init(
|
||||
document.querySelector('#terms-of-use-modal'),
|
||||
{
|
||||
dismissible: false,
|
||||
onCloseEnd: (modalElement) => {
|
||||
nopaque.requests.users.entity.acceptTermsOfUse();
|
||||
}
|
||||
}
|
||||
);
|
||||
// #endregion
|
||||
|
||||
|
||||
/* Initialize nopaque Components */
|
||||
// #region
|
||||
nopaque.resource_displays.AutoInit();
|
||||
nopaque.resource_lists.AutoInit();
|
||||
nopaque.forms.AutoInit();
|
||||
// #endregion
|
||||
}
|
||||
|
||||
flash(message, category) {
|
||||
let iconPrefix;
|
||||
|
||||
switch (category) {
|
||||
case 'corpus': {
|
||||
iconPrefix = '<i class="material-icons left">book</i>';
|
||||
break;
|
||||
}
|
||||
case 'job': {
|
||||
iconPrefix = '<i class="nopaque-icons left">J</i>';
|
||||
break;
|
||||
}
|
||||
case 'error': {
|
||||
iconPrefix = '<i class="material-icons left error-color-text">error</i>';
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
iconPrefix = '<i class="material-icons left">notifications</i>';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let toast = M.toast(
|
||||
{
|
||||
html: `
|
||||
<span>${iconPrefix}${message}</span>
|
||||
<button class="btn-flat toast-action white-text" data-toast-action="dismiss">
|
||||
<i class="material-icons">close</i>
|
||||
</button>
|
||||
`.trim()
|
||||
}
|
||||
);
|
||||
let dismissToastElement = toast.el.querySelector('.toast-action[data-toast-action="dismiss"]');
|
||||
dismissToastElement.addEventListener('click', () => {toast.dismiss();});
|
||||
}
|
||||
}
|
53
app/static/js/app.users.js
Normal file
53
app/static/js/app.users.js
Normal file
@ -0,0 +1,53 @@
|
||||
nopaque.UsersExtension = class UsersExtension {
|
||||
#data;
|
||||
#promises;
|
||||
|
||||
constructor(app) {
|
||||
this.app = app;
|
||||
|
||||
this.#data = {};
|
||||
this.app.data.users = this.#data;
|
||||
|
||||
this.#promises = {
|
||||
get: {},
|
||||
subscribe: {}
|
||||
};
|
||||
}
|
||||
|
||||
async #get(userId) {
|
||||
const response = await this.app.socket.emitWithAck('users.get', userId);
|
||||
|
||||
if (response.status != 200) {
|
||||
throw new Error(`[${response.status}] ${response.statusText}`);
|
||||
}
|
||||
|
||||
this.#data[userId] = response.body;
|
||||
return this.#data[userId];
|
||||
}
|
||||
|
||||
get(userId) {
|
||||
if (userId in this.#promises.get) {
|
||||
return this.#promises.get[userId];
|
||||
}
|
||||
|
||||
this.#promises.get[userId] = this.#get(userId);
|
||||
return this.#promises.get[userId];
|
||||
}
|
||||
|
||||
async #subscribe(userId) {
|
||||
const response = await this.app.socket.emitWithAck('users.subscribe', userId);
|
||||
|
||||
if (response.status != 200) {
|
||||
throw new Error(`[${response.status}] ${response.statusText}`);
|
||||
}
|
||||
}
|
||||
|
||||
subscribe(userId) {
|
||||
if (userId in this.#promises.subscribe) {
|
||||
return this.#promises.subscribe[userId];
|
||||
}
|
||||
|
||||
this.#promises.subscribe[userId] = this.#subscribe(userId);
|
||||
return this.#promises.subscribe[userId];
|
||||
}
|
||||
}
|
@ -66,7 +66,7 @@ nopaque.corpus_analysis.ConcordanceExtension = class ConcordanceExtension {
|
||||
errorString += `${error.constructor.name}`;
|
||||
this.elements.error.innerText = errorString;
|
||||
this.elements.error.classList.remove('hide');
|
||||
app.flash(errorString, 'error');
|
||||
app.ui.flash(errorString, 'error');
|
||||
this.elements.progress.classList.add('hide');
|
||||
}
|
||||
this.app.enableActionElements();
|
||||
@ -239,7 +239,7 @@ nopaque.corpus_analysis.ConcordanceExtension = class ConcordanceExtension {
|
||||
if (subcorpus.selectedItems.size === 0) {
|
||||
this.elements.progress.classList.add('hide');
|
||||
this.app.enableActionElements();
|
||||
app.flash('No matches selected', 'error');
|
||||
app.ui.flash('No matches selected', 'error');
|
||||
return;
|
||||
}
|
||||
promise = subcorpus.o.partialExport([...subcorpus.selectedItems], 50);
|
||||
@ -298,7 +298,7 @@ nopaque.corpus_analysis.ConcordanceExtension = class ConcordanceExtension {
|
||||
let subcorpus = this.data.subcorpora[this.settings.selectedSubcorpus];
|
||||
subcorpus.o.drop().then(
|
||||
(cQiStatus) => {
|
||||
app.flash(`${subcorpus.o.name} deleted`, 'corpus');
|
||||
app.ui.flash(`${subcorpus.o.name} deleted`, 'corpus');
|
||||
delete this.data.subcorpora[subcorpus.o.name];
|
||||
this.settings.selectedSubcorpus = undefined;
|
||||
for (let subcorpusName in this.data.subcorpora) {
|
||||
@ -320,7 +320,7 @@ nopaque.corpus_analysis.ConcordanceExtension = class ConcordanceExtension {
|
||||
},
|
||||
(cqiError) => {
|
||||
let errorString = `${cqiError.code}: ${cqiError.constructor.name}`;
|
||||
app.flash(errorString, 'error');
|
||||
app.ui.flash(errorString, 'error');
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -46,7 +46,7 @@ nopaque.corpus_analysis.ReaderExtension = class ReaderExtension {
|
||||
if ('description' in error) {errorString += `: ${error.description}`;}
|
||||
this.elements.error.innerText = errorString;
|
||||
this.elements.error.classList.remove('hide');
|
||||
app.flash(errorString, 'error');
|
||||
app.ui.flash(errorString, 'error');
|
||||
this.elements.progress.classList.add('hide');
|
||||
}
|
||||
this.app.enableActionElements();
|
||||
|
@ -101,7 +101,7 @@ nopaque.forms.BaseForm = class BaseForm {
|
||||
}
|
||||
}
|
||||
if (request.status === 500) {
|
||||
app.flash('Internal Server Error', 'error');
|
||||
app.ui.flash('Internal Server Error', 'error');
|
||||
}
|
||||
modal.close();
|
||||
});
|
||||
|
@ -25,16 +25,16 @@ nopaque.requests.JSONfetch = (input, init={}) => {
|
||||
let message = json.message;
|
||||
let category = json.category || 'message';
|
||||
if (message) {
|
||||
app.flash(message, category);
|
||||
app.ui.flash(message, category);
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
app.flash(`[${response.status}]: ${response.statusText}`, 'error');
|
||||
app.ui.flash(`[${response.status}]: ${response.statusText}`, 'error');
|
||||
}
|
||||
);
|
||||
},
|
||||
(response) => {
|
||||
app.flash('Something went wrong', 'error');
|
||||
app.ui.flash('Something went wrong', 'error');
|
||||
reject(response);
|
||||
}
|
||||
);
|
||||
|
@ -6,13 +6,13 @@ nopaque.resource_displays.ResourceDisplay = class ResourceDisplay {
|
||||
this.userId = this.displayElement.dataset.userId;
|
||||
this.isInitialized = false;
|
||||
if (this.userId) {
|
||||
app.subscribeUser(this.userId)
|
||||
app.users.subscribe(this.userId)
|
||||
.then((response) => {
|
||||
app.socket.on('patch_user', (patch) => {
|
||||
app.socket.on('users.patch', (patch) => {
|
||||
if (this.isInitialized) {this.onPatch(patch);}
|
||||
});
|
||||
});
|
||||
app.getUser(this.userId)
|
||||
app.users.get(this.userId)
|
||||
.then((user) => {
|
||||
this.init(user);
|
||||
this.isInitialized = true;
|
||||
|
@ -14,12 +14,12 @@ 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.subscribeUser(this.userId).then((response) => {
|
||||
app.socket.on('patch_user', (patch) => {
|
||||
app.users.subscribe(this.userId).then((response) => {
|
||||
app.socket.on('users.patch', (patch) => {
|
||||
if (this.isInitialized) {this.onPatch(patch);}
|
||||
});
|
||||
});
|
||||
app.getUser(this.userId).then((user) => {
|
||||
app.users.get(this.userId).then((user) => {
|
||||
this.add(Object.values(user.corpora[this.corpusId].files || user.followed_corpora[this.corpusId].files));
|
||||
this.isInitialized = true;
|
||||
});
|
||||
|
@ -12,12 +12,12 @@ 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.subscribeUser(this.userId).then((response) => {
|
||||
app.socket.on('patch_user', (patch) => {
|
||||
app.users.subscribe(this.userId).then((response) => {
|
||||
app.socket.on('users.patch', (patch) => {
|
||||
if (this.isInitialized) {this.onPatch(patch);}
|
||||
});
|
||||
});
|
||||
app.getUser(this.userId).then((user) => {
|
||||
app.users.get(this.userId).then((user) => {
|
||||
// let corpusFollowerAssociations = Object.values(user.corpora[this.corpusId].corpus_follower_associations);
|
||||
// let filteredList = corpusFollowerAssociations.filter(association => association.follower.id != currentUserId);
|
||||
// this.add(filteredList);
|
||||
|
@ -11,12 +11,12 @@ 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.subscribeUser(this.userId).then((response) => {
|
||||
app.socket.on('patch_user', (patch) => {
|
||||
app.users.subscribe(this.userId).then((response) => {
|
||||
app.socket.on('users.patch', (patch) => {
|
||||
if (this.isInitialized) {this.onPatch(patch);}
|
||||
});
|
||||
});
|
||||
app.getUser(this.userId).then((user) => {
|
||||
app.users.get(this.userId).then((user) => {
|
||||
this.add(this.aggregateData(user));
|
||||
this.isInitialized = true;
|
||||
});
|
||||
|
@ -8,8 +8,8 @@ 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.subscribeUser(this.userId);
|
||||
app.getUser(this.userId).then((user) => {
|
||||
app.users.subscribe(this.userId);
|
||||
app.users.get(this.userId).then((user) => {
|
||||
this.add(Object.values(user.jobs[this.jobId].inputs));
|
||||
this.isInitialized = true;
|
||||
});
|
||||
|
@ -12,12 +12,12 @@ 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.subscribeUser(this.userId).then((response) => {
|
||||
app.socket.on('patch_user', (patch) => {
|
||||
app.users.subscribe(this.userId).then((response) => {
|
||||
app.socket.on('users.patch', (patch) => {
|
||||
if (this.isInitialized) {this.onPatch(patch);}
|
||||
});
|
||||
});
|
||||
app.getUser(this.userId).then((user) => {
|
||||
app.users.get(this.userId).then((user) => {
|
||||
this.add(Object.values(user.jobs));
|
||||
this.isInitialized = true;
|
||||
});
|
||||
|
@ -8,12 +8,12 @@ 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.subscribeUser(this.userId).then((response) => {
|
||||
app.socket.on('patch_user', (patch) => {
|
||||
app.users.subscribe(this.userId).then((response) => {
|
||||
app.socket.on('users.patch', (patch) => {
|
||||
if (this.isInitialized) {this.onPatch(patch);}
|
||||
});
|
||||
});
|
||||
app.getUser(this.userId).then((user) => {
|
||||
app.users.get(this.userId).then((user) => {
|
||||
this.add(Object.values(user.jobs[this.jobId].results));
|
||||
this.isInitialized = true;
|
||||
});
|
||||
|
@ -8,12 +8,12 @@ nopaque.resource_lists.SpaCyNLPPipelineModelList = class SpaCyNLPPipelineModelLi
|
||||
this.isInitialized = false;
|
||||
this.userId = listContainerElement.dataset.userId;
|
||||
if (this.userId === undefined) {return;}
|
||||
app.subscribeUser(this.userId).then((response) => {
|
||||
app.socket.on('patch_user', (patch) => {
|
||||
app.users.subscribe(this.userId).then((response) => {
|
||||
app.socket.on('users.patch', (patch) => {
|
||||
if (this.isInitialized) {this.onPatch(patch);}
|
||||
});
|
||||
});
|
||||
app.getUser(this.userId).then((user) => {
|
||||
app.users.get(this.userId).then((user) => {
|
||||
this.add(Object.values(user.spacy_nlp_pipeline_models));
|
||||
this.isInitialized = true;
|
||||
});
|
||||
|
@ -8,12 +8,12 @@ nopaque.resource_lists.TesseractOCRPipelineModelList = class TesseractOCRPipelin
|
||||
this.isInitialized = false;
|
||||
this.userId = listContainerElement.dataset.userId;
|
||||
if (this.userId === undefined) {return;}
|
||||
app.subscribeUser(this.userId).then((response) => {
|
||||
app.socket.on('patch_user', (patch) => {
|
||||
app.users.subscribe(this.userId).then((response) => {
|
||||
app.socket.on('users.patch', (patch) => {
|
||||
if (this.isInitialized) {this.onPatch(patch);}
|
||||
});
|
||||
});
|
||||
app.getUser(this.userId).then((user) => {
|
||||
app.users.get(this.userId).then((user) => {
|
||||
this.add(Object.values(user.tesseract_ocr_pipeline_models));
|
||||
for (let uncheckedCheckbox of this.listjs.list.querySelectorAll('input[data-checked="True"]')) {
|
||||
uncheckedCheckbox.setAttribute('checked', '');
|
||||
|
@ -9,6 +9,8 @@
|
||||
output='gen/nopaque.%(version)s.js',
|
||||
'js/index.js',
|
||||
'js/app.js',
|
||||
'js/app.ui.js',
|
||||
'js/app.users.js',
|
||||
'js/utils.js',
|
||||
|
||||
'js/forms/index.js',
|
||||
@ -82,11 +84,11 @@
|
||||
const currentUserId = {{ current_user.hashid|tojson }};
|
||||
|
||||
// Subscribe to the current user's data events
|
||||
app.subscribeUser(currentUserId)
|
||||
app.users.subscribe(currentUserId)
|
||||
.catch((error) => {throw JSON.stringify(error);});
|
||||
|
||||
// Get the current user's data
|
||||
app.getUser(currentUserId, true, true)
|
||||
app.users.get(currentUserId, true, true)
|
||||
.catch((error) => {throw JSON.stringify(error);});
|
||||
|
||||
{% if not current_user.terms_of_use_accepted -%}
|
||||
@ -96,7 +98,7 @@
|
||||
|
||||
// Display flashed messages
|
||||
for (let [category, message] of {{ get_flashed_messages(with_categories=True)|tojson }}) {
|
||||
app.flash(message, message);
|
||||
app.ui.flash(message, message);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -364,8 +364,8 @@ shareLinkModalCreateButtonElement.addEventListener('click', (event) => {
|
||||
shareLinkModalOutputCopyButtonElement.addEventListener('click', (event) => {
|
||||
navigator.clipboard.writeText(shareLinkModalOutputFieldElement.value)
|
||||
.then(
|
||||
() => {app.flash('Copied!');},
|
||||
() => {app.flash('Could not copy to clipboard. Please copy manually.', 'error');}
|
||||
() => {app.ui.flash('Copied!');},
|
||||
() => {app.ui.flash('Could not copy to clipboard. Please copy manually.', 'error');}
|
||||
);
|
||||
|
||||
});
|
||||
|
@ -396,8 +396,8 @@ shareLinkModalCreateButtonElement.addEventListener('click', (event) => {
|
||||
shareLinkModalOutputCopyButtonElement.addEventListener('click', (event) => {
|
||||
navigator.clipboard.writeText(shareLinkModalOutputFieldElement.value)
|
||||
.then(
|
||||
() => {app.flash('Copied!');},
|
||||
() => {app.flash('Could not copy to clipboard. Please copy manually.', 'error');}
|
||||
() => {app.ui.flash('Copied!');},
|
||||
() => {app.ui.flash('Could not copy to clipboard. Please copy manually.', 'error');}
|
||||
);
|
||||
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user