@arrai-innovations/reactive-helpers 8.0.1 → 8.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/use/cancellableIntent.js +84 -35
- package/use/listInstance.js +7 -6
- package/use/listSubscription.js +6 -4
- package/use/paginatedListInstance.js +15 -2
package/package.json
CHANGED
package/use/cancellableIntent.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { isEmpty } from "lodash-es";
|
|
1
2
|
import identity from "lodash-es/identity";
|
|
2
3
|
import isEqual from "lodash-es/isEqual";
|
|
3
|
-
import { effectScope, nextTick, onScopeDispose, reactive, readonly, watch } from "vue";
|
|
4
|
+
import { computed, effectScope, nextTick, onScopeDispose, reactive, readonly, watch } from "vue";
|
|
4
5
|
import { deepUnref } from "vue-deepunref";
|
|
5
6
|
|
|
6
7
|
/*
|
|
@@ -8,7 +9,12 @@ import { deepUnref } from "vue-deepunref";
|
|
|
8
9
|
* Watch arguments should be a reactive object.
|
|
9
10
|
* If the promise is not resolved before the watch arguments change again, the previous promise is cancelled.
|
|
10
11
|
*/
|
|
11
|
-
export function useCancellableIntent({
|
|
12
|
+
export function useCancellableIntent({
|
|
13
|
+
awaitableWithCancel,
|
|
14
|
+
watchArguments = {},
|
|
15
|
+
guardArguments = {},
|
|
16
|
+
clearActiveOnResolved = true,
|
|
17
|
+
}) {
|
|
12
18
|
if (!awaitableWithCancel) {
|
|
13
19
|
throw new Error("awaitableWithCancel is required");
|
|
14
20
|
}
|
|
@@ -16,7 +22,9 @@ export function useCancellableIntent({ awaitableWithCancel, watchArguments = {},
|
|
|
16
22
|
throw new Error("awaitableWithCancel must be a function");
|
|
17
23
|
}
|
|
18
24
|
const state = reactive({
|
|
25
|
+
activeCount: undefined, // the active count doesn't mean much when not using clearActiveOnResolved
|
|
19
26
|
active: undefined,
|
|
27
|
+
resolvingCount: undefined,
|
|
20
28
|
resolving: undefined,
|
|
21
29
|
errored: false,
|
|
22
30
|
error: null,
|
|
@@ -34,8 +42,6 @@ export function useCancellableIntent({ awaitableWithCancel, watchArguments = {},
|
|
|
34
42
|
|
|
35
43
|
async function cancel() {
|
|
36
44
|
if (cancelFunction) {
|
|
37
|
-
state.active = false;
|
|
38
|
-
state.resolving = false;
|
|
39
45
|
const cancelPromise = cancelFunction().catch(console.error);
|
|
40
46
|
cancelFunction = null;
|
|
41
47
|
return cancelPromise;
|
|
@@ -43,52 +49,95 @@ export function useCancellableIntent({ awaitableWithCancel, watchArguments = {},
|
|
|
43
49
|
return false;
|
|
44
50
|
}
|
|
45
51
|
|
|
46
|
-
const
|
|
52
|
+
const doIntentWatch = async () => {
|
|
53
|
+
state.errored = false;
|
|
54
|
+
state.error = null;
|
|
55
|
+
if (state.activeCount === undefined) {
|
|
56
|
+
state.activeCount = 0;
|
|
57
|
+
}
|
|
58
|
+
state.activeCount += 1;
|
|
59
|
+
if (state.resolvingCount === undefined) {
|
|
60
|
+
state.resolvingCount = 0;
|
|
61
|
+
}
|
|
62
|
+
state.resolvingCount += 1;
|
|
63
|
+
let awaitablePromise = awaitableWithCancel();
|
|
64
|
+
|
|
65
|
+
if (awaitablePromise.cancel) {
|
|
66
|
+
cancelFunction = awaitablePromise.cancel;
|
|
67
|
+
}
|
|
68
|
+
// we don't want to await this, because we want to be able to cancel it
|
|
69
|
+
awaitablePromise
|
|
70
|
+
.catch(async (err) => {
|
|
71
|
+
await cancel();
|
|
72
|
+
console.error(err);
|
|
73
|
+
state.errored = true;
|
|
74
|
+
state.error = err;
|
|
75
|
+
throw err;
|
|
76
|
+
})
|
|
77
|
+
.finally(() => {
|
|
78
|
+
state.resolvingCount--;
|
|
79
|
+
if (state.clearActiveOnResolved) {
|
|
80
|
+
cancelFunction = null;
|
|
81
|
+
state.activeCount--;
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
let delayedWatch = null;
|
|
87
|
+
|
|
88
|
+
const intentWatch = async () => {
|
|
47
89
|
let newWatchValues = deepUnref(Object.values(watchArguments));
|
|
48
90
|
if (isEqual(newWatchValues, previousWatchValues)) {
|
|
49
91
|
return;
|
|
50
92
|
}
|
|
51
93
|
previousWatchValues = newWatchValues;
|
|
52
|
-
cancel()
|
|
94
|
+
await cancel();
|
|
53
95
|
if (Object.values(previousWatchValues).every(identity)) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
96
|
+
// if any guards are true, delay the watch.
|
|
97
|
+
if (guardArguments && !isEmpty(guardArguments) && Object.values(guardArguments).some(identity)) {
|
|
98
|
+
delayedWatch = doIntentWatch;
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
doIntentWatch();
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const guardWatch = async () => {
|
|
106
|
+
if (delayedWatch) {
|
|
107
|
+
// if all guards are false, run the watch
|
|
108
|
+
if (Object.values(guardArguments).every((x) => !x)) {
|
|
109
|
+
const myDelayedWatch = delayedWatch;
|
|
110
|
+
delayedWatch = null;
|
|
111
|
+
await myDelayedWatch();
|
|
66
112
|
}
|
|
67
|
-
awaitablePromise
|
|
68
|
-
.then(() => {
|
|
69
|
-
state.resolving = false;
|
|
70
|
-
if (state.clearActiveOnResolved) {
|
|
71
|
-
cancelFunction = null;
|
|
72
|
-
state.active = false;
|
|
73
|
-
}
|
|
74
|
-
})
|
|
75
|
-
.catch(async (err) => {
|
|
76
|
-
await cancel();
|
|
77
|
-
console.error(err);
|
|
78
|
-
state.errored = true;
|
|
79
|
-
state.error = err;
|
|
80
|
-
throw err;
|
|
81
|
-
});
|
|
82
113
|
}
|
|
83
114
|
};
|
|
84
115
|
|
|
85
116
|
es.run(() => {
|
|
86
|
-
|
|
117
|
+
state.active = computed(() => {
|
|
118
|
+
if (state.activeCount === undefined) {
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
return state.activeCount > 0;
|
|
122
|
+
});
|
|
123
|
+
state.resolving = computed(() => {
|
|
124
|
+
if (state.resolvingCount === undefined) {
|
|
125
|
+
return undefined;
|
|
126
|
+
}
|
|
127
|
+
return state.resolvingCount > 0;
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
watch(() => Object.values(watchArguments), intentWatch, {
|
|
87
131
|
// this can't be immediate because subscribe wants to look at our state, which won't exist yet.
|
|
88
132
|
deep: true,
|
|
89
133
|
});
|
|
90
134
|
|
|
91
|
-
|
|
135
|
+
watch(() => Object.values(guardArguments), guardWatch, {
|
|
136
|
+
// we can't possibly have a delayed watch immediately
|
|
137
|
+
deep: true,
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
nextTick().then(intentWatch);
|
|
92
141
|
|
|
93
142
|
onScopeDispose(cancel);
|
|
94
143
|
});
|
package/use/listInstance.js
CHANGED
|
@@ -114,6 +114,7 @@ export function useListInstance({ props, functions = {} }) {
|
|
|
114
114
|
});
|
|
115
115
|
|
|
116
116
|
const defaultPageCallback = (newObjects) => {
|
|
117
|
+
clearList();
|
|
117
118
|
newObjects.forEach((newObject) => {
|
|
118
119
|
if (newObject.id in state.objects) {
|
|
119
120
|
updateListObject(newObject);
|
|
@@ -139,11 +140,10 @@ export function useListInstance({ props, functions = {} }) {
|
|
|
139
140
|
listArgs: state.listArgs,
|
|
140
141
|
pageCallback: returnedObject.pageCallback,
|
|
141
142
|
});
|
|
143
|
+
let resolveState = false;
|
|
142
144
|
if (listPromise.cancel) {
|
|
143
145
|
returnPromise.cancel = async () => {
|
|
144
146
|
let promise = listPromise.cancel();
|
|
145
|
-
state.loading = false;
|
|
146
|
-
returnPromiseResolve(false);
|
|
147
147
|
if (promise) {
|
|
148
148
|
await promise;
|
|
149
149
|
}
|
|
@@ -152,14 +152,15 @@ export function useListInstance({ props, functions = {} }) {
|
|
|
152
152
|
// the indirection of promises here is to allow us to do additional work on listPromise's cancel
|
|
153
153
|
listPromise
|
|
154
154
|
.then(() => {
|
|
155
|
-
|
|
156
|
-
returnPromiseResolve(true);
|
|
155
|
+
resolveState = true;
|
|
157
156
|
})
|
|
158
157
|
.catch((error) => {
|
|
159
|
-
state.loading = false;
|
|
160
158
|
state.errored = true;
|
|
161
159
|
state.error = error;
|
|
162
|
-
|
|
160
|
+
})
|
|
161
|
+
.finally(() => {
|
|
162
|
+
state.loading = false;
|
|
163
|
+
returnPromiseResolve(resolveState);
|
|
163
164
|
});
|
|
164
165
|
return returnPromise;
|
|
165
166
|
}
|
package/use/listSubscription.js
CHANGED
|
@@ -196,15 +196,17 @@ export function useListSubscription({ listInstance, props, functions }) {
|
|
|
196
196
|
state.subscribed = toRef(subscribeIntent.state, "active");
|
|
197
197
|
|
|
198
198
|
listIntent = useCancellableIntent({
|
|
199
|
-
awaitableWithCancel:
|
|
200
|
-
listInstance.clearList();
|
|
201
|
-
return listInstance.list();
|
|
202
|
-
},
|
|
199
|
+
awaitableWithCancel: listInstance.list,
|
|
203
200
|
watchArguments: reactive({
|
|
204
201
|
intendToList: toRef(state, "intendToList"),
|
|
205
202
|
listArgs: toRef(parentState, "listArgs"),
|
|
206
203
|
retrieveArgs: toRef(parentState, "retrieveArgs"),
|
|
207
204
|
}),
|
|
205
|
+
// delay triggering a list until the last list has finished/cancelled
|
|
206
|
+
// cancel can still be triggered
|
|
207
|
+
guardArguments: reactive({
|
|
208
|
+
loading: toRef(parentState, "loading"),
|
|
209
|
+
}),
|
|
208
210
|
});
|
|
209
211
|
});
|
|
210
212
|
|
|
@@ -7,14 +7,27 @@ export function usePagedListInstance({ keepOldPages = false, ...useListInstanceA
|
|
|
7
7
|
listInstance.state.totalPages = 0;
|
|
8
8
|
listInstance.state.perPage = 0;
|
|
9
9
|
|
|
10
|
+
const superClearList = listInstance.clearList;
|
|
11
|
+
listInstance.clearList = () => {
|
|
12
|
+
superClearList();
|
|
13
|
+
listInstance.state.totalRecords = 0;
|
|
14
|
+
listInstance.state.totalPages = 0;
|
|
15
|
+
listInstance.state.perPage = 0;
|
|
16
|
+
};
|
|
17
|
+
|
|
10
18
|
listInstance.pageCallback = (newObjects, { totalRecords, totalPages, perPage }) => {
|
|
11
19
|
// with keepOldPages, you are responsible for clearing the list as needed
|
|
12
20
|
if (!keepOldPages) {
|
|
13
21
|
// display one page at a time, clear the list
|
|
14
22
|
listInstance.clearList();
|
|
15
23
|
}
|
|
16
|
-
|
|
17
|
-
|
|
24
|
+
newObjects.forEach((newObject) => {
|
|
25
|
+
if (newObject.id in listInstance.state.objects) {
|
|
26
|
+
listInstance.updateListObject(newObject);
|
|
27
|
+
} else {
|
|
28
|
+
listInstance.addListObject(newObject);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
18
31
|
if (totalRecords !== undefined) {
|
|
19
32
|
listInstance.state.totalRecords = totalRecords;
|
|
20
33
|
}
|