From 9fa766963a7cf9578fec450bf95dd086feca330d Mon Sep 17 00:00:00 2001 From: DanyLE Date: Fri, 16 Jun 2023 17:54:12 +0200 Subject: [PATCH] feat: Introduce API.Task API that allow to track promise object via AntOS announcement system --- d.ts/antos.d.ts | 81 +++++------------------- src/core/Announcerment.ts | 17 ----- src/core/BaseApplication.ts | 68 ++------------------ src/core/core.ts | 107 ++++++++++++-------------------- src/core/tags/SystemPanelTag.ts | 24 ++++--- 5 files changed, 74 insertions(+), 223 deletions(-) diff --git a/d.ts/antos.d.ts b/d.ts/antos.d.ts index aaefdf5..59f6f8f 100644 --- a/d.ts/antos.d.ts +++ b/d.ts/antos.d.ts @@ -2034,13 +2034,21 @@ declare namespace OS { */ var lang: GenericObject; /** - * Re-export the system announcement {@link OS.announcer.getMID} function to the - * core API + * A task is a Promise object that is tracked by AntOS via the + * Announcerment system + * + * Task manager implementation can subscribe to the following global events + * - ANTOS-TASK-PENDING : a new task/promise is created and executing + * - ANTOS-TASK-FULFILLED: a fullfilled task is a resolved promise + * - ANTOS-TASK-REJECTED: a rejected task is a rejected or error promise + * + * Whenever a task is created by this API, it states will be automatically announced + * to any subscribers of these events * * @export - * @returns {number} + * @param {Promise} a Promise object */ - function mid(): number; + function Task(fn: (resolve: (any: any) => void, reject: (any: any) => void) => void): Promise; /** * REST-based API. * @@ -2086,28 +2094,6 @@ declare namespace OS { * @param {*} b file content */ function saveblob(name: string, b: any): void; - /** - * Helper function to trigger the global `loading` - * event. This event should be triggered in the - * beginning of a heavy task - * - * @export - * @param {number} q message id, see {@link mid} - * @param {string} p message string - */ - function loading(q: number, p: string): void; - /** - * Helper function to trigger the global `loaded` - * event: This event should be triggered in the - * end of a heavy task that has previously triggered - * the `loading` event - * - * @export - * @param {number} q the message id of the corresponding `loading` event - * @param {string} p the message string - * @param {string} m message status (`OK` of `FAIL`) - */ - function loaded(q: number, p: string, m: string): void; /** * Perform an REST GET request * @@ -2348,21 +2334,6 @@ declare namespace OS { * @memberof BaseApplication */ sysdock: GUI.tag.AppDockTag; - /** - * Loading animation check timeout - * - * @private - * @memberof BaseApplication - */ - private _loading_toh; - /** - * Store pending loading task - * - * @private - * @type {number[]} - * @memberof BaseApplication - */ - private _pending_task; /** *Creates an instance of BaseApplication. * @param {string} name application name @@ -2393,9 +2364,8 @@ declare namespace OS { protected loadScheme(): void; /** * API function to perform an heavy task. - * This function will trigger the global `loading` - * event at the beginning of the task, and the `loaded` - * event after finishing the task + * This function will create a Task that is tracked by any + * task manager implementation * * @protected * @param {Promise} promise the promise on a task to be performed @@ -2569,14 +2539,6 @@ declare namespace OS { * @memberof BaseApplication */ protected cleanup(e: BaseEvent): void; - /** - * Check if the loading tasks ended, - * if it the case, stop the animation - * - * @private - * @memberof BaseApplication - */ - private animation_check; } } } @@ -6452,7 +6414,7 @@ declare namespace OS { * Store pending loading task * * @private - * @type {number[]} + * @type {Promise[]} * @memberof SystemPanelTag */ private _pending_task; @@ -10681,12 +10643,6 @@ declare namespace OS { * and callbacks */ var observable: API.Announcer; - /** - * This variable is used to allocate the `id` of all messages - * passing between publishers and subscribers in the - * system announcement - */ - var quota: 0; /** * Placeholder of all global events listeners */ @@ -10759,13 +10715,6 @@ declare namespace OS { * @returns {void} */ function unregister(app: BaseModel): void; - /** - * Allocate message id - * - * @export - * @returns {number} - */ - function getMID(): number; } } declare namespace OS { diff --git a/src/core/Announcerment.ts b/src/core/Announcerment.ts index bb599be..493bc24 100644 --- a/src/core/Announcerment.ts +++ b/src/core/Announcerment.ts @@ -290,12 +290,6 @@ namespace OS { * and callbacks */ export var observable: API.Announcer = new API.Announcer(); - /** - * This variable is used to allocate the `id` of all messages - * passing between publishers and subscribers in the - * system announcement - */ - export var quota: 0; /** * Placeholder of all global events listeners */ @@ -412,16 +406,5 @@ namespace OS { } announcer.listeners.delete(app); } - - /** - * Allocate message id - * - * @export - * @returns {number} - */ - export function getMID(): number { - quota += 1; - return quota; - } } } diff --git a/src/core/BaseApplication.ts b/src/core/BaseApplication.ts index cbb77c9..2be1ece 100644 --- a/src/core/BaseApplication.ts +++ b/src/core/BaseApplication.ts @@ -61,22 +61,6 @@ namespace OS { */ sysdock: GUI.tag.AppDockTag; - /** - * Loading animation check timeout - * - * @private - * @memberof BaseApplication - */ - private _loading_toh: any; - /** - * Store pending loading task - * - * @private - * @type {number[]} - * @memberof BaseApplication - */ - private _pending_task: number[]; - /** *Creates an instance of BaseApplication. * @param {string} name application name @@ -90,8 +74,6 @@ namespace OS { } this.setting = setting.applications[this.name]; this.keycomb = {}; - this._loading_toh = undefined; - this._pending_task = []; } /** @@ -139,25 +121,7 @@ namespace OS { this.applySetting(m.message as string); } }); - this.subscribe("loading", (o: API.AnnouncementDataType) => { - if(o.u_data != this.pid) - { - return; - } - this._pending_task.push(o.id); - this.trigger("loading", undefined); - }); - this.subscribe("loaded", (o: API.AnnouncementDataType) => { - const i = this._pending_task.indexOf(o.id); - if (i >= 0) { - this._pending_task.splice(i, 1); - } - if (this._pending_task.length === 0) { - // set time out - if(!this._loading_toh) - this._loading_toh = setTimeout(() => this.animation_check(),1000); - } - }); + this.updateLocale(this.systemsetting.system.locale); return this.loadScheme(); } @@ -178,9 +142,8 @@ namespace OS { /** * API function to perform an heavy task. - * This function will trigger the global `loading` - * event at the beginning of the task, and the `loaded` - * event after finishing the task + * This function will create a Task that is tracked by any + * task manager implementation * * @protected * @param {Promise} promise the promise on a task to be performed @@ -188,15 +151,11 @@ namespace OS { * @memberof BaseApplication */ protected load(promise: Promise): Promise { - const q = this._api.mid(); - return new Promise(async (resolve, reject) => { - this._api.loading(q, this.name); + return this._api.Task(async (resolve, reject) => { try { await promise; - this._api.loaded(q, this.name, "OK"); - return resolve(); + return resolve(undefined); } catch (e) { - this._api.loaded(q, this.name, "FAIL"); return reject(__e(e)); } }); @@ -505,23 +464,6 @@ namespace OS { * @memberof BaseApplication */ protected cleanup(e: BaseEvent): void {} - - /** - * Check if the loading tasks ended, - * if it the case, stop the animation - * - * @private - * @memberof BaseApplication - */ - private animation_check(): void { - if(this._pending_task.length === 0) - { - this.trigger("loaded", undefined); - } - if(this._loading_toh) - clearTimeout(this._loading_toh); - this._loading_toh = undefined; - } } BaseApplication.type = ModelType.Application; diff --git a/src/core/core.ts b/src/core/core.ts index ba91d89..ae60acf 100644 --- a/src/core/core.ts +++ b/src/core/core.ts @@ -878,7 +878,6 @@ namespace OS { $("#wrapper").empty(); GUI.clearTheme(); announcer.observable = new API.Announcer(); - announcer.quota = 0; resetSetting(); PM.processes = {}; PM.pidalloc = 0; @@ -1183,14 +1182,44 @@ namespace OS { export var lang: GenericObject = {}; /** - * Re-export the system announcement {@link OS.announcer.getMID} function to the - * core API - * + * A task is a Promise object that is tracked by AntOS via the + * Announcerment system + * + * Task manager implementation can subscribe to the following global events + * - ANTOS-TASK-PENDING : a new task/promise is created and executing + * - ANTOS-TASK-FULFILLED: a fullfilled task is a resolved promise + * - ANTOS-TASK-REJECTED: a rejected task is a rejected or error promise + * + * Whenever a task is created by this API, it states will be automatically announced + * to any subscribers of these events + * * @export - * @returns {number} + * @param {Promise} a Promise object */ - export function mid(): number { - return announcer.getMID(); + export function Task(fn: ( resolve: (any)=>void,reject:(any)=>void) => void ): Promise + { + return new Promise(async (ok,nok) =>{ + const promise = new Promise(fn); + const ann:API.AnnouncementDataType> = {} as API.AnnouncementDataType>; + ann.name = "OS"; + ann.u_data = promise; + ann.id = Math.floor(Math.random() * 1e6); + try + { + ann.message = "ANTOS-TASK-PENDING"; + announcer.trigger(ann.message, ann); + const data = await promise; + ok(data); + ann.message = "ANTOS-TASK-FULFILLED"; + announcer.trigger(ann.message, ann); + } + catch(e) + { + ann.message = "ANTOS-TASK-REJECTED"; + announcer.trigger(ann.message, ann); + nok(__e(e)); + } + }); } /** @@ -1205,9 +1234,7 @@ namespace OS { * @returns {Promise} a promise on the result data */ export function post(p: string, d: any): Promise { - return new Promise(function (resolve, reject) { - const q = announcer.getMID(); - API.loading(q, p); + return API.Task(function (resolve, reject) { return $.ajax({ type: "POST", url: p, @@ -1226,11 +1253,9 @@ namespace OS { success: null, }) .done(function (data) { - API.loaded(q, p, "OK"); return resolve(data); }) .fail(function (j, s, e) { - API.loaded(q, p, "FAIL"); return reject(API.throwe(s)); }); }); @@ -1248,21 +1273,17 @@ namespace OS { * @returns {Promise} a promise on the returned binary data */ export function blob(p: string): Promise { - return new Promise(function (resolve, reject) { - const q = announcer.getMID(); + return API.Task(function (resolve, reject) { const r = new XMLHttpRequest(); r.open("GET", p, true); r.responseType = "arraybuffer"; r.onload = function (e) { if (this.status === 200 && this.readyState === 4) { - API.loaded(q, p, "OK"); resolve(this.response); } else { - API.loaded(q, p, "FAIL"); reject(API.throwe(__("Unable to get blob: {0}", p))); } }; - API.loading(q, p); r.send(); }); } @@ -1278,8 +1299,7 @@ namespace OS { * @returns {Promise} */ export function upload(p: string, d: string): Promise { - return new Promise(function (resolve, reject) { - const q = announcer.getMID(); + return API.Task(function (resolve, reject) { //insert a temporal file selector const o = $("") @@ -1287,9 +1307,6 @@ namespace OS { .attr("multiple", "true"); o.on("change", function () { const files = (o[0] as HTMLInputElement).files; - const n_files = files.length; - if (n_files > 0) - API.loading(q, p); const formd = new FormData(); formd.append("path", d); jQuery.each(files, (i, file) => { @@ -1303,11 +1320,9 @@ namespace OS { processData: false, }) .done(function (data) { - API.loaded(q, p, "OK"); resolve(data); }) .fail(function (j, s, e) { - API.loaded(q, p, "FAIL"); o.remove(); reject(API.throwe(s)); }); @@ -1337,44 +1352,6 @@ namespace OS { o.remove(); } - /** - * Helper function to trigger the global `loading` - * event. This event should be triggered in the - * beginning of a heavy task - * - * @export - * @param {number} q message id, see {@link mid} - * @param {string} p message string - */ - export function loading(q: number, p: string): void { - const data: API.AnnouncementDataType = {} as API.AnnouncementDataType; - data.id = q; - data.message = p; - data.name = p; - data.u_data = 0; //PM.pidactive; - announcer.trigger("loading", data); - } - - /** - * Helper function to trigger the global `loaded` - * event: This event should be triggered in the - * end of a heavy task that has previously triggered - * the `loading` event - * - * @export - * @param {number} q the message id of the corresponding `loading` event - * @param {string} p the message string - * @param {string} m message status (`OK` of `FAIL`) - */ - export function loaded(q: number, p: string, m: string): void { - const data: API.AnnouncementDataType = {} as API.AnnouncementDataType; - data.id = q; - data.message = p; - data.name = "OS"; - data.u_data = false; - announcer.trigger("loaded", data); - } - /** * Perform an REST GET request * @@ -1388,7 +1365,7 @@ namespace OS { * @returns {Promise} a Promise on the requested data */ export function get(p: string, t: string = undefined): Promise { - return new Promise(function (resolve, reject) { + return API.Task(function (resolve, reject) { const conf: any = { type: "GET", url: p, @@ -1396,15 +1373,11 @@ namespace OS { if (t) { conf.dataType = t; } - const q = announcer.getMID(); - API.loading(q, p); return $.ajax(conf) .done(function (data) { - API.loaded(q, p, "OK"); return resolve(data); }) .fail(function (j, s, e) { - API.loaded(q, p, "FAIL"); return reject(API.throwe(s)); }); }); diff --git a/src/core/tags/SystemPanelTag.ts b/src/core/tags/SystemPanelTag.ts index 64520fb..044c9df 100644 --- a/src/core/tags/SystemPanelTag.ts +++ b/src/core/tags/SystemPanelTag.ts @@ -34,10 +34,10 @@ namespace OS { * Store pending loading task * * @private - * @type {number[]} + * @type {Promise[]} * @memberof SystemPanelTag */ - private _pending_task: number[]; + private _pending_task: Promise[]; /** * Flag indicate where the selected application shall be openned @@ -519,17 +519,13 @@ namespace OS { this.refs.osmenu.contextmenuHandle = (e, m) => { }; systray.contextmenuHandle = (e, m) => { }; this.refs.panel.contextmenuHandle = (e, m) => { }; - announcer.on("loading", (o: API.AnnouncementDataType) => { - if(o.u_data != 0) - { - return; - } + announcer.on("ANTOS-TASK-PENDING", (o: API.AnnouncementDataType>) => { if(this._pending_task.length == 0) { $(this.refs.panel).addClass("loading"); systray.iconclass = "fa-spin fa fa-cog"; } - this._pending_task.push(o.id); + this._pending_task.push(o.u_data); $(GUI.workspace).css("cursor", "wait"); }); @@ -538,8 +534,8 @@ namespace OS { e.data.stopPropagation(); this.show_systray(); }; - announcer.on("loaded", (o: API.AnnouncementDataType) => { - const i = this._pending_task.indexOf(o.id); + const remove_task = (o: Promise) => { + const i = this._pending_task.indexOf(o); if (i >= 0) { this._pending_task.splice(i, 1); } @@ -549,7 +545,15 @@ namespace OS { if(!this._loading_toh) this._loading_toh = setTimeout(() => this.animation_check(),1000); } + }; + announcer.on("ANTOS-TASK-FULFILLED", (o: API.AnnouncementDataType>) => { + remove_task(o.u_data); }); + + announcer.on("ANTOS-TASK-REJECTED", (o: API.AnnouncementDataType>) => { + remove_task(o.u_data); + }); + announcer.on("desktopresize", (e) => { this.calibrate(); });