aboutsummaryrefslogtreecommitdiffstats
path: root/src/stores/lib/CachedRequest.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/stores/lib/CachedRequest.ts')
-rw-r--r--src/stores/lib/CachedRequest.ts126
1 files changed, 126 insertions, 0 deletions
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 @@
1import { action } from 'mobx';
2import { isEqual, remove } from 'lodash';
3import Request from './Request';
4
5export default class CachedRequest extends Request {
6 _apiCalls: any[] = [];
7
8 _isInvalidated = true;
9
10 execute(...callArgs): this {
11 // Do not continue if this request is already loading
12 if (this.isWaitingForResponse) {
13 return this;
14 }
15
16 // Very simple caching strategy -> only continue if the call / args changed
17 // or the request was invalidated manually from outside
18 const existingApiCall = this._findApiCall(callArgs);
19
20 // Invalidate if new or different api call will be done
21 if (existingApiCall && existingApiCall !== this.currentApiCall) {
22 this._isInvalidated = true;
23 this.currentApiCall = existingApiCall;
24 } else if (!existingApiCall) {
25 this._isInvalidated = true;
26 this.currentApiCall = this._addApiCall(callArgs);
27 }
28
29 // Do not continue if this request is not invalidated (see above)
30 if (!this._isInvalidated) {
31 return this;
32 }
33
34 // This timeout is necessary to avoid warnings from mobx
35 // regarding triggering actions as side-effect of getters
36 setTimeout(
37 action(() => {
38 this.isExecuting = true;
39 // Apply the previous result from this call immediately (cached)
40 if (existingApiCall) {
41 this.result = existingApiCall.result;
42 }
43 }),
44 0,
45 );
46
47 // Issue api call & save it as promise that is handled to update the results of the operation
48 this.promise = new Promise(resolve => {
49 this.api[this.method](...callArgs)
50 .then(result => {
51 setTimeout(
52 action(() => {
53 this.result = result;
54 if (this.currentApiCall) this.currentApiCall.result = result;
55 this.isExecuting = false;
56 this.isError = false;
57 this.wasExecuted = true;
58 this._isInvalidated = false;
59 this.isWaitingForResponse = false;
60 this._triggerHooks();
61 resolve(result);
62 }),
63 1,
64 );
65 return result;
66 })
67 .catch(
68 action(error => {
69 setTimeout(
70 action(() => {
71 this.error = error;
72 this.isExecuting = false;
73 this.isError = true;
74 this.wasExecuted = true;
75 this.isWaitingForResponse = false;
76 this._triggerHooks();
77 // reject(error);
78 }),
79 1,
80 );
81 }),
82 );
83 });
84
85 this.isWaitingForResponse = true;
86 return this;
87 }
88
89 static defaultOptions = { immediately: false };
90
91 invalidate(options = CachedRequest.defaultOptions): this {
92 this._isInvalidated = true;
93 if (options.immediately && this.currentApiCall) {
94 return this.execute(...this.currentApiCall.args);
95 }
96 return this;
97 }
98
99 patch(modify): Promise<this> {
100 return new Promise(resolve => {
101 setTimeout(
102 action(() => {
103 const override = modify(this.result);
104 if (override !== undefined) this.result = override;
105 if (this.currentApiCall) this.currentApiCall.result = this.result;
106 resolve(this);
107 }),
108 0,
109 );
110 });
111 }
112
113 removeCacheForCallWith(...args: any): void {
114 remove(this._apiCalls, c => isEqual(c.args, args));
115 }
116
117 _addApiCall(args: any) {
118 const newCall = { args, result: null };
119 this._apiCalls.push(newCall);
120 return newCall;
121 }
122
123 _findApiCall(args: any) {
124 return this._apiCalls.find(c => isEqual(c.args, args));
125 }
126}