mirror of
				https://gitlab.ub.uni-bielefeld.de/sfb1288inf/nopaque.git
				synced 2025-11-04 04:12:45 +00:00 
			
		
		
		
	Update javascript app structure
This commit is contained in:
		@@ -1,10 +1,9 @@
 | 
			
		||||
nopaque.App = class App {
 | 
			
		||||
  constructor() {
 | 
			
		||||
    this.data = {};
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -29,5 +28,7 @@ nopaque.App = class App {
 | 
			
		||||
 | 
			
		||||
  init() {
 | 
			
		||||
    this.ui.init();
 | 
			
		||||
    this.liveUserRegistry.init();
 | 
			
		||||
    this.users.init();
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,53 +0,0 @@
 | 
			
		||||
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];
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										70
									
								
								app/static/js/app/user-live-registry.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								app/static/js/app/user-live-registry.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,70 @@
 | 
			
		||||
nopaque.LiveUserRegistryExtension = class LiveUserRegistryExtension extends EventTarget {
 | 
			
		||||
  #data;
 | 
			
		||||
 | 
			
		||||
  constructor(app) {
 | 
			
		||||
    super();
 | 
			
		||||
 | 
			
		||||
    this.app = app;
 | 
			
		||||
 | 
			
		||||
    this.#data = {
 | 
			
		||||
      users: {},
 | 
			
		||||
      promises: {}
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  init() {
 | 
			
		||||
    this.app.users.socket.on('patch', (patch) => {this.#onPatch(patch)});
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  add(userId) {
 | 
			
		||||
    if (!(userId in this.#data.promises)) {
 | 
			
		||||
      this.#data.promises[userId] = this.#add(userId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return this.#data.promises[userId];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async #add(userId) {
 | 
			
		||||
    await this.app.users.subscribe(userId);
 | 
			
		||||
    this.#data.users[userId] = await this.app.users.get(userId);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async get(userId) {
 | 
			
		||||
    await this.add(userId);
 | 
			
		||||
    return this.#data.users[userId];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  #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));
 | 
			
		||||
 | 
			
		||||
    // Apply patch
 | 
			
		||||
    jsonpatch.applyPatch(this.#data, filteredPatch);
 | 
			
		||||
 | 
			
		||||
    // Notify event listeners
 | 
			
		||||
    let event = new CustomEvent('patch', {detail: filteredPatch});
 | 
			
		||||
    this.dispatchEvent(event);
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    // Notify event listeners. Event type: "patch *"
 | 
			
		||||
    let event = new CustomEvent('patch *', {detail: filteredPatch});
 | 
			
		||||
    this.dispatchEvent(event);
 | 
			
		||||
 | 
			
		||||
    // Group patches by user id: {<user-id>: [op, ...], ...}
 | 
			
		||||
    let patches = {};
 | 
			
		||||
    let matchRegExp = new RegExp(`^/users/([A-Za-z0-9]+)`);
 | 
			
		||||
    for (let operation of filteredPatch) {
 | 
			
		||||
      let [match, userId] = operation.path.match(matchRegExp);
 | 
			
		||||
      if (!(userId in patches)) {patches[userId] = [];}
 | 
			
		||||
      patches[userId].push(operation);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Notify event listeners. Event type: "patch <user-id>"
 | 
			
		||||
    for (let [userId, patch] of Object.entries(patches)) {
 | 
			
		||||
      let event = new CustomEvent(`patch ${userId}`, {detail: patch});
 | 
			
		||||
      this.dispatchEvent(event);
 | 
			
		||||
    }
 | 
			
		||||
    */
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										43
									
								
								app/static/js/app/users.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								app/static/js/app/users.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
			
		||||
nopaque.UsersExtension = class UsersExtension {
 | 
			
		||||
  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);
 | 
			
		||||
 | 
			
		||||
    if (response.status !== 200) {
 | 
			
		||||
      throw new Error(`[${response.status}] ${response.statusText}`);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return response.body;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async subscribe(userId) {
 | 
			
		||||
    const response = await this.socket.emitWithAck('subscribe', userId);
 | 
			
		||||
 | 
			
		||||
    if (response.status != 200) {
 | 
			
		||||
      throw new Error(`[${response.status}] ${response.statusText}`);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async unsubscribe(userId) {
 | 
			
		||||
    const response = await this.socket.emitWithAck('unsubscribe', userId);
 | 
			
		||||
 | 
			
		||||
    if (response.status != 200) {
 | 
			
		||||
      throw new Error(`[${response.status}] ${response.statusText}`);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async delete(userId) {
 | 
			
		||||
    const response = await this.socket.emitWithAck('delete', userId);
 | 
			
		||||
 | 
			
		||||
    if (response.status != 202) {
 | 
			
		||||
      throw new Error(`[${response.status}] ${response.statusText}`);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -5,19 +5,14 @@ nopaque.resource_displays.ResourceDisplay = class ResourceDisplay {
 | 
			
		||||
    this.displayElement = displayElement;
 | 
			
		||||
    this.userId = this.displayElement.dataset.userId;
 | 
			
		||||
    this.isInitialized = false;
 | 
			
		||||
    if (this.userId) {
 | 
			
		||||
      app.users.subscribe(this.userId)
 | 
			
		||||
        .then((response) => {
 | 
			
		||||
          app.socket.on('users.patch', (patch) => {
 | 
			
		||||
            if (this.isInitialized) {this.onPatch(patch);}
 | 
			
		||||
          });
 | 
			
		||||
        });
 | 
			
		||||
      app.users.get(this.userId)
 | 
			
		||||
        .then((user) => {
 | 
			
		||||
          this.init(user);
 | 
			
		||||
          this.isInitialized = true;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    if (this.userId === undefined) {return;}
 | 
			
		||||
    app.liveUserRegistry.addEventListener('patch', (event) => {
 | 
			
		||||
      if (this.isInitialized) {this.onPatch(event.detail);}
 | 
			
		||||
    });
 | 
			
		||||
    app.liveUserRegistry.get(this.userId).then((user) => {
 | 
			
		||||
      this.init(user);
 | 
			
		||||
      this.isInitialized = true;
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  init(user) {throw 'Not implemented';}
 | 
			
		||||
 
 | 
			
		||||
@@ -14,12 +14,11 @@ 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.users.subscribe(this.userId).then((response) => {
 | 
			
		||||
      app.socket.on('users.patch', (patch) => {
 | 
			
		||||
        if (this.isInitialized) {this.onPatch(patch);}
 | 
			
		||||
      });
 | 
			
		||||
    app.liveUserRegistry.addEventListener('patch', (event) => {
 | 
			
		||||
      if (this.isInitialized) {this.onPatch(event.detail);}
 | 
			
		||||
    });
 | 
			
		||||
    app.users.get(this.userId).then((user) => {
 | 
			
		||||
    app.liveUserRegistry.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;
 | 
			
		||||
    });
 | 
			
		||||
 
 | 
			
		||||
@@ -12,15 +12,16 @@ 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.users.subscribe(this.userId).then((response) => {
 | 
			
		||||
      app.socket.on('users.patch', (patch) => {
 | 
			
		||||
        if (this.isInitialized) {this.onPatch(patch);}
 | 
			
		||||
      });
 | 
			
		||||
    app.liveUserRegistry.addEventListener('patch', (event) => {
 | 
			
		||||
      if (this.isInitialized) {this.onPatch(event.detail);}
 | 
			
		||||
    });
 | 
			
		||||
    app.users.get(this.userId).then((user) => {
 | 
			
		||||
    app.liveUserRegistry.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);
 | 
			
		||||
      // this.add(filteredList);
 | 
			
		||||
 | 
			
		||||
      // TODO: Make this better understandable
 | 
			
		||||
      this.add(Object.values(user.corpora[this.corpusId].corpus_follower_associations));
 | 
			
		||||
      this.isInitialized = true;
 | 
			
		||||
    });
 | 
			
		||||
 
 | 
			
		||||
@@ -11,12 +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.users.subscribe(this.userId).then((response) => {
 | 
			
		||||
      app.socket.on('users.patch', (patch) => {
 | 
			
		||||
        if (this.isInitialized) {this.onPatch(patch);}
 | 
			
		||||
      });
 | 
			
		||||
    app.liveUserRegistry.addEventListener('patch', (event) => {
 | 
			
		||||
      if (this.isInitialized) {this.onPatch(event.detail);}
 | 
			
		||||
    });
 | 
			
		||||
    app.users.get(this.userId).then((user) => {
 | 
			
		||||
    app.liveUserRegistry.get(this.userId).then((user) => {
 | 
			
		||||
      this.add(this.aggregateData(user));
 | 
			
		||||
      this.isInitialized = true;
 | 
			
		||||
    });
 | 
			
		||||
 
 | 
			
		||||
@@ -8,8 +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.users.subscribe(this.userId);
 | 
			
		||||
    app.users.get(this.userId).then((user) => {
 | 
			
		||||
    // app.liveUserRegistry.addEventListener('patch', (event) => {
 | 
			
		||||
    //   if (this.isInitialized) {this.onPatch(event.detail);}
 | 
			
		||||
    // });
 | 
			
		||||
    app.liveUserRegistry.get(this.userId).then((user) => {
 | 
			
		||||
      this.add(Object.values(user.jobs[this.jobId].inputs));
 | 
			
		||||
      this.isInitialized = true;
 | 
			
		||||
    });
 | 
			
		||||
 
 | 
			
		||||
@@ -12,12 +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.users.subscribe(this.userId).then((response) => {
 | 
			
		||||
      app.socket.on('users.patch', (patch) => {
 | 
			
		||||
        if (this.isInitialized) {this.onPatch(patch);}
 | 
			
		||||
      });
 | 
			
		||||
    app.liveUserRegistry.addEventListener('patch', (event) => {
 | 
			
		||||
      if (this.isInitialized) {this.onPatch(event.detail);}
 | 
			
		||||
    });
 | 
			
		||||
    app.users.get(this.userId).then((user) => {
 | 
			
		||||
    app.liveUserRegistry.get(this.userId).then((user) => {
 | 
			
		||||
      this.add(Object.values(user.jobs));
 | 
			
		||||
      this.isInitialized = true;
 | 
			
		||||
    });
 | 
			
		||||
 
 | 
			
		||||
@@ -8,12 +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.users.subscribe(this.userId).then((response) => {
 | 
			
		||||
      app.socket.on('users.patch', (patch) => {
 | 
			
		||||
        if (this.isInitialized) {this.onPatch(patch);}
 | 
			
		||||
      });
 | 
			
		||||
    app.liveUserRegistry.addEventListener('patch', (event) => {
 | 
			
		||||
      if (this.isInitialized) {this.onPatch(event.detail);}
 | 
			
		||||
    });
 | 
			
		||||
    app.users.get(this.userId).then((user) => {
 | 
			
		||||
    app.liveUserRegistry.get(this.userId).then((user) => {
 | 
			
		||||
      this.add(Object.values(user.jobs[this.jobId].results));
 | 
			
		||||
      this.isInitialized = true;
 | 
			
		||||
    });
 | 
			
		||||
 
 | 
			
		||||
@@ -8,12 +8,10 @@ nopaque.resource_lists.SpaCyNLPPipelineModelList = class SpaCyNLPPipelineModelLi
 | 
			
		||||
    this.isInitialized = false;
 | 
			
		||||
    this.userId = listContainerElement.dataset.userId;
 | 
			
		||||
    if (this.userId === undefined) {return;}
 | 
			
		||||
    app.users.subscribe(this.userId).then((response) => {
 | 
			
		||||
      app.socket.on('users.patch', (patch) => {
 | 
			
		||||
        if (this.isInitialized) {this.onPatch(patch);}
 | 
			
		||||
      });
 | 
			
		||||
    app.liveUserRegistry.addEventListener('patch', (event) => {
 | 
			
		||||
      if (this.isInitialized) {this.onPatch(event.detail);}
 | 
			
		||||
    });
 | 
			
		||||
    app.users.get(this.userId).then((user) => {
 | 
			
		||||
    app.liveUserRegistry.get(this.userId).then((user) => {
 | 
			
		||||
      this.add(Object.values(user.spacy_nlp_pipeline_models));
 | 
			
		||||
      this.isInitialized = true;
 | 
			
		||||
    });
 | 
			
		||||
 
 | 
			
		||||
@@ -8,21 +8,11 @@ nopaque.resource_lists.TesseractOCRPipelineModelList = class TesseractOCRPipelin
 | 
			
		||||
    this.isInitialized = false;
 | 
			
		||||
    this.userId = listContainerElement.dataset.userId;
 | 
			
		||||
    if (this.userId === undefined) {return;}
 | 
			
		||||
    app.users.subscribe(this.userId).then((response) => {
 | 
			
		||||
      app.socket.on('users.patch', (patch) => {
 | 
			
		||||
        if (this.isInitialized) {this.onPatch(patch);}
 | 
			
		||||
      });
 | 
			
		||||
    app.liveUserRegistry.addEventListener('patch', (event) => {
 | 
			
		||||
      if (this.isInitialized) {this.onPatch(event.detail);}
 | 
			
		||||
    });
 | 
			
		||||
    app.users.get(this.userId).then((user) => {
 | 
			
		||||
    app.liveUserRegistry.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', '');
 | 
			
		||||
      }
 | 
			
		||||
      if (user.role.name !== ('Administrator' || 'Contributor')) {
 | 
			
		||||
        for (let switchElement of this.listjs.list.querySelectorAll('.is_public')) {
 | 
			
		||||
          switchElement.setAttribute('disabled', '');
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      this.isInitialized = true;
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user