From d9502c7516bc2d4ae467c6ea8a2e4816b0885f37 Mon Sep 17 00:00:00 2001 From: muhamedsalih-tw <104364298+muhamedsalih-tw@users.noreply.github.com> Date: Thu, 17 Nov 2022 05:45:39 +0530 Subject: Transfrom workspace components to ts (#775) --- src/stores/lib/CachedRequest.js | 121 ------------------------------ src/stores/lib/CachedRequest.ts | 126 +++++++++++++++++++++++++++++++ src/stores/lib/Request.js | 155 --------------------------------------- src/stores/lib/Request.ts | 159 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 285 insertions(+), 276 deletions(-) delete mode 100644 src/stores/lib/CachedRequest.js create mode 100644 src/stores/lib/CachedRequest.ts delete mode 100644 src/stores/lib/Request.js create mode 100644 src/stores/lib/Request.ts (limited to 'src/stores/lib') diff --git a/src/stores/lib/CachedRequest.js b/src/stores/lib/CachedRequest.js deleted file mode 100644 index a6dd47f7d..000000000 --- a/src/stores/lib/CachedRequest.js +++ /dev/null @@ -1,121 +0,0 @@ -import { action } from 'mobx'; -import { isEqual, remove } from 'lodash'; -import Request from './Request'; - -export default class CachedRequest extends Request { - _apiCalls = []; - - _isInvalidated = true; - - execute(...callArgs) { - // Do not continue if this request is already loading - if (this._isWaitingForResponse) return this; - - // Very simple caching strategy -> only continue if the call / args changed - // or the request was invalidated manually from outside - const existingApiCall = this._findApiCall(callArgs); - - // Invalidate if new or different api call will be done - if (existingApiCall && existingApiCall !== this._currentApiCall) { - this._isInvalidated = true; - this._currentApiCall = existingApiCall; - } else if (!existingApiCall) { - this._isInvalidated = true; - this._currentApiCall = this._addApiCall(callArgs); - } - - // Do not continue if this request is not invalidated (see above) - if (!this._isInvalidated) return this; - - // This timeout is necessary to avoid warnings from mobx - // regarding triggering actions as side-effect of getters - setTimeout( - action(() => { - this.isExecuting = true; - // Apply the previous result from this call immediately (cached) - if (existingApiCall) { - this.result = existingApiCall.result; - } - }), - 0, - ); - - // Issue api call & save it as promise that is handled to update the results of the operation - this._promise = new Promise(resolve => { - this._api[this._method](...callArgs) - .then(result => { - setTimeout( - action(() => { - this.result = result; - if (this._currentApiCall) this._currentApiCall.result = result; - this.isExecuting = false; - this.isError = false; - this.wasExecuted = true; - this._isInvalidated = false; - this._isWaitingForResponse = false; - this._triggerHooks(); - resolve(result); - }), - 1, - ); - return result; - }) - .catch( - action(error => { - setTimeout( - action(() => { - this.error = error; - this.isExecuting = false; - this.isError = true; - this.wasExecuted = true; - this._isWaitingForResponse = false; - this._triggerHooks(); - // reject(error); - }), - 1, - ); - }), - ); - }); - - this._isWaitingForResponse = true; - return this; - } - - // eslint-disable-next-line unicorn/no-object-as-default-parameter - invalidate(options = { immediately: false }) { - this._isInvalidated = true; - if (options.immediately && this._currentApiCall) { - return this.execute(...this._currentApiCall.args); - } - return this; - } - - patch(modify) { - return new Promise(resolve => { - setTimeout( - action(() => { - const override = modify(this.result); - if (override !== undefined) this.result = override; - if (this._currentApiCall) this._currentApiCall.result = this.result; - resolve(this); - }), - 0, - ); - }); - } - - removeCacheForCallWith(...args) { - remove(this._apiCalls, c => isEqual(c.args, args)); - } - - _addApiCall(args) { - const newCall = { args, result: null }; - this._apiCalls.push(newCall); - return newCall; - } - - _findApiCall(args) { - return this._apiCalls.find(c => isEqual(c.args, args)); - } -} diff --git a/src/stores/lib/CachedRequest.ts b/src/stores/lib/CachedRequest.ts new file mode 100644 index 000000000..25cc365e2 --- /dev/null +++ b/src/stores/lib/CachedRequest.ts @@ -0,0 +1,126 @@ +import { action } from 'mobx'; +import { isEqual, remove } from 'lodash'; +import Request from './Request'; + +export default class CachedRequest extends Request { + _apiCalls: any[] = []; + + _isInvalidated = true; + + execute(...callArgs): this { + // Do not continue if this request is already loading + if (this.isWaitingForResponse) { + return this; + } + + // Very simple caching strategy -> only continue if the call / args changed + // or the request was invalidated manually from outside + const existingApiCall = this._findApiCall(callArgs); + + // Invalidate if new or different api call will be done + if (existingApiCall && existingApiCall !== this.currentApiCall) { + this._isInvalidated = true; + this.currentApiCall = existingApiCall; + } else if (!existingApiCall) { + this._isInvalidated = true; + this.currentApiCall = this._addApiCall(callArgs); + } + + // Do not continue if this request is not invalidated (see above) + if (!this._isInvalidated) { + return this; + } + + // This timeout is necessary to avoid warnings from mobx + // regarding triggering actions as side-effect of getters + setTimeout( + action(() => { + this.isExecuting = true; + // Apply the previous result from this call immediately (cached) + if (existingApiCall) { + this.result = existingApiCall.result; + } + }), + 0, + ); + + // Issue api call & save it as promise that is handled to update the results of the operation + this.promise = new Promise(resolve => { + this.api[this.method](...callArgs) + .then(result => { + setTimeout( + action(() => { + this.result = result; + if (this.currentApiCall) this.currentApiCall.result = result; + this.isExecuting = false; + this.isError = false; + this.wasExecuted = true; + this._isInvalidated = false; + this.isWaitingForResponse = false; + this._triggerHooks(); + resolve(result); + }), + 1, + ); + return result; + }) + .catch( + action(error => { + setTimeout( + action(() => { + this.error = error; + this.isExecuting = false; + this.isError = true; + this.wasExecuted = true; + this.isWaitingForResponse = false; + this._triggerHooks(); + // reject(error); + }), + 1, + ); + }), + ); + }); + + this.isWaitingForResponse = true; + return this; + } + + static defaultOptions = { immediately: false }; + + invalidate(options = CachedRequest.defaultOptions): this { + this._isInvalidated = true; + if (options.immediately && this.currentApiCall) { + return this.execute(...this.currentApiCall.args); + } + return this; + } + + patch(modify): Promise { + return new Promise(resolve => { + setTimeout( + action(() => { + const override = modify(this.result); + if (override !== undefined) this.result = override; + if (this.currentApiCall) this.currentApiCall.result = this.result; + resolve(this); + }), + 0, + ); + }); + } + + removeCacheForCallWith(...args: any): void { + remove(this._apiCalls, c => isEqual(c.args, args)); + } + + _addApiCall(args: any) { + const newCall = { args, result: null }; + this._apiCalls.push(newCall); + return newCall; + } + + _findApiCall(args: any) { + return this._apiCalls.find(c => isEqual(c.args, args)); + } +} diff --git a/src/stores/lib/Request.js b/src/stores/lib/Request.js deleted file mode 100644 index 60c943a42..000000000 --- a/src/stores/lib/Request.js +++ /dev/null @@ -1,155 +0,0 @@ -import { observable, action, computed, makeObservable } from 'mobx'; -import { isEqual } from 'lodash/fp'; - -export default class Request { - static _hooks = []; - - static registerHook(hook) { - Request._hooks.push(hook); - } - - @observable result = null; - - @observable error = null; - - @observable isExecuting = false; - - @observable isError = false; - - @observable wasExecuted = false; - - @action _reset() { - this.error = null; - this.result = null; - this.isExecuting = false; - this.isError = false; - this.wasExecuted = false; - this._isWaitingForResponse = false; - this._promise = Promise; - - return this; - } - - _promise = Promise; - - _api = {}; - - _method = ''; - - _isWaitingForResponse = false; - - _currentApiCall = null; - - constructor(api, method) { - makeObservable(this); - - this._api = api; - this._method = method; - } - - execute(...callArgs) { - // Do not continue if this request is already loading - if (this._isWaitingForResponse) return this; - - if (!this._api[this._method]) { - throw new Error( - `Missing method <${this._method}> on api object:`, - this._api, - ); - } - - // This timeout is necessary to avoid warnings from mobx - // regarding triggering actions as side-effect of getters - setTimeout( - action(() => { - this.isExecuting = true; - }), - 0, - ); - - // Issue api call & save it as promise that is handled to update the results of the operation - this._promise = new Promise((resolve, reject) => { - this._api[this._method](...callArgs) - .then(result => { - setTimeout( - action(() => { - this.error = null; - this.result = result; - if (this._currentApiCall) this._currentApiCall.result = result; - this.isExecuting = false; - this.isError = false; - this.wasExecuted = true; - this._isWaitingForResponse = false; - this._triggerHooks(); - resolve(result); - }), - 1, - ); - return result; - }) - .catch( - action(error => { - setTimeout( - action(() => { - this.error = error; - this.isExecuting = false; - this.isError = true; - this.wasExecuted = true; - this._isWaitingForResponse = false; - this._triggerHooks(); - reject(error); - }), - 1, - ); - }), - ); - }); - - this._isWaitingForResponse = true; - this._currentApiCall = { args: callArgs, result: null }; - return this; - } - - reload() { - const args = this._currentApiCall ? this._currentApiCall.args : []; - this.error = null; - return this.execute(...args); - } - - retry = () => this.reload(); - - isExecutingWithArgs(...args) { - return ( - this.isExecuting && - this._currentApiCall && - isEqual(this._currentApiCall.args, args) - ); - } - - @computed get isExecutingFirstTime() { - return !this.wasExecuted && this.isExecuting; - } - - /* eslint-disable unicorn/no-thenable */ - then(...args) { - if (!this._promise) - throw new Error( - 'You have to call Request::execute before you can access it as promise', - ); - return this._promise.then(...args); - } - - catch(...args) { - if (!this._promise) - throw new Error( - 'You have to call Request::execute before you can access it as promise', - ); - return this._promise.catch(...args); - } - - _triggerHooks() { - for (const hook of Request._hooks) hook(this); - } - - reset = () => this._reset(); -} diff --git a/src/stores/lib/Request.ts b/src/stores/lib/Request.ts new file mode 100644 index 000000000..f9424ac99 --- /dev/null +++ b/src/stores/lib/Request.ts @@ -0,0 +1,159 @@ +import { observable, action, computed, makeObservable } from 'mobx'; +import { isEqual } from 'lodash/fp'; + +type Hook = (request: Request) => void; + +export default class Request { + static _hooks: Hook[] = []; + + static registerHook(hook: Hook) { + Request._hooks.push(hook); + } + + @observable result: any = null; + + @observable error: any = null; + + @observable isExecuting = false; + + @observable isError = false; + + @observable wasExecuted = false; + + promise: any = Promise; + + protected api: any = {}; + + method = ''; + + protected isWaitingForResponse = false; + + protected currentApiCall: any = null; + + retry = () => this.reload(); + + reset = () => this._reset(); + + constructor(api, method) { + makeObservable(this); + + this.api = api; + this.method = method; + } + + @action _reset(): this { + this.error = null; + this.result = null; + this.isExecuting = false; + this.isError = false; + this.wasExecuted = false; + this.isWaitingForResponse = false; + this.promise = Promise; + + return this; + } + + execute(...callArgs: any[]): this { + // Do not continue if this request is already loading + if (this.isWaitingForResponse) return this; + + if (!this.api[this.method]) { + throw new Error( + `Missing method <${this.method}> on api object:`, + this.api, + ); + } + + // This timeout is necessary to avoid warnings from mobx + // regarding triggering actions as side-effect of getters + setTimeout( + action(() => { + this.isExecuting = true; + }), + 0, + ); + + // Issue api call & save it as promise that is handled to update the results of the operation + this.promise = new Promise((resolve, reject) => { + this.api[this.method](...callArgs) + .then(result => { + setTimeout( + action(() => { + this.error = null; + this.result = result; + if (this.currentApiCall) this.currentApiCall.result = result; + this.isExecuting = false; + this.isError = false; + this.wasExecuted = true; + this.isWaitingForResponse = false; + this._triggerHooks(); + resolve(result); + }), + 1, + ); + return result; + }) + .catch( + action(error => { + setTimeout( + action(() => { + this.error = error; + this.isExecuting = false; + this.isError = true; + this.wasExecuted = true; + this.isWaitingForResponse = false; + this._triggerHooks(); + reject(error); + }), + 1, + ); + }), + ); + }); + + this.isWaitingForResponse = true; + this.currentApiCall = { args: callArgs, result: null }; + return this; + } + + reload(): this { + const args = this.currentApiCall ? this.currentApiCall.args : []; + this.error = null; + return this.execute(...args); + } + + isExecutingWithArgs(...args: any[]): boolean { + return ( + this.isExecuting && + this.currentApiCall && + isEqual(this.currentApiCall.args, args) + ); + } + + @computed get isExecutingFirstTime(): boolean { + return !this.wasExecuted && this.isExecuting; + } + + /* eslint-disable unicorn/no-thenable */ + then(...args: any[]) { + if (!this.promise) + throw new Error( + 'You have to call Request::execute before you can access it as promise', + ); + return this.promise.then(...args); + } + + catch(...args: any[]) { + if (!this.promise) + throw new Error( + 'You have to call Request::execute before you can access it as promise', + ); + return this.promise.catch(...args); + } + + _triggerHooks(): void { + for (const hook of Request._hooks) { + hook(this); + } + } +} -- cgit v1.2.3-54-g00ecf