@arrai-innovations/reactive-helpers 1.2.3 → 2.0.0
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/.lintstagedrc +3 -0
- package/README.md +21 -16
- package/package.json +1 -1
- package/tests/unit/crudPromise.js +23 -0
- package/tests/unit/poll.js +50 -0
- package/tests/unit/use/listInstance.spec.js +47 -89
- package/tests/unit/use/listSort.spec.js +1 -1
- package/tests/unit/use/listSubscription.spec.js +184 -104
- package/tests/unit/use/objectInstance.spec.js +156 -147
- package/tests/unit/use/objectSubscription.spec.js +144 -120
- package/tests/unit/use/watches.spec.js +2 -2
- package/use/listInstance.js +39 -28
- package/use/listSubscription.js +80 -34
- package/use/objectInstance.js +34 -43
- package/use/objectSubscription.js +66 -71
- package/use/paginatedListInstance.js +2 -2
- package/utils/cancellableIntent.js +93 -0
- package/utils/index.js +1 -0
package/use/listInstance.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { cloneDeep
|
|
2
|
-
import { computed, effectScope, reactive
|
|
1
|
+
import { cloneDeep } from "lodash";
|
|
2
|
+
import { computed, effectScope, reactive } from "vue";
|
|
3
3
|
import { assignReactiveObject } from "../utils/assignReactiveObject";
|
|
4
4
|
import { getFakeId } from "../utils/getFakeId";
|
|
5
5
|
import inspect from "browser-util-inspect";
|
|
@@ -30,23 +30,24 @@ export function useListInstances(listInstanceArgs) {
|
|
|
30
30
|
return instances;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
export function useListInstance({ crudArgs,
|
|
33
|
+
export function useListInstance({ crudArgs, listArgs = {}, retrieveArgs = {} }) {
|
|
34
34
|
const state = reactive({
|
|
35
|
-
|
|
35
|
+
crud: {
|
|
36
36
|
args: {},
|
|
37
37
|
list: undefined,
|
|
38
38
|
},
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
retrieveArgs,
|
|
40
|
+
listArgs,
|
|
41
41
|
objects: {},
|
|
42
42
|
loading: undefined,
|
|
43
43
|
errored: false,
|
|
44
44
|
error: null,
|
|
45
45
|
order: [],
|
|
46
46
|
});
|
|
47
|
-
|
|
47
|
+
// prevent linking of all instances to the same default .args object
|
|
48
|
+
Object.assign(state.crud, cloneDeep(defaultCrud));
|
|
48
49
|
if (crudArgs) {
|
|
49
|
-
assignReactiveObject(state.
|
|
50
|
+
assignReactiveObject(state.crud.args, crudArgs);
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
const defaultPageCallback = (newObjects) => {
|
|
@@ -59,37 +60,47 @@ export function useListInstance({ crudArgs, defaultListArgs = {}, defaultRetriev
|
|
|
59
60
|
});
|
|
60
61
|
};
|
|
61
62
|
|
|
62
|
-
|
|
63
|
+
function list() {
|
|
64
|
+
// this function cannot be async, or the resulting promise will lose its .cancel() method
|
|
63
65
|
if (state.loading) {
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
if (isEmpty(retrieveArgs) && !isEmpty(unref(state.defaultRetrieveArgs))) {
|
|
67
|
-
retrieveArgs = unref(state.defaultRetrieveArgs);
|
|
68
|
-
}
|
|
69
|
-
if (isEmpty(listArgs) && !isEmpty(unref(state.defaultListArgs))) {
|
|
70
|
-
listArgs = unref(state.defaultListArgs);
|
|
66
|
+
return Promise.reject(new ListError("already loading."));
|
|
71
67
|
}
|
|
68
|
+
let returnPromiseResolve;
|
|
69
|
+
const returnPromise = new Promise((resolve) => {
|
|
70
|
+
returnPromiseResolve = resolve;
|
|
71
|
+
});
|
|
72
72
|
state.loading = true;
|
|
73
73
|
state.errored = false;
|
|
74
74
|
state.error = null;
|
|
75
|
-
|
|
76
|
-
.
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
75
|
+
const listPromise = state.crud.list({
|
|
76
|
+
crudArgs: state.crud.args,
|
|
77
|
+
retrieveArgs: state.retrieveArgs,
|
|
78
|
+
listArgs: state.listArgs,
|
|
79
|
+
pageCallback: returnedObject.pageCallback,
|
|
80
|
+
});
|
|
81
|
+
if (listPromise.cancel) {
|
|
82
|
+
returnPromise.cancel = async () => {
|
|
83
|
+
let promise = listPromise.cancel();
|
|
84
|
+
state.loading = false;
|
|
85
|
+
returnPromiseResolve(false);
|
|
86
|
+
if (promise) {
|
|
87
|
+
await promise;
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
// the indirection of promises here is to allow us to do additional work on listPromise's cancel
|
|
92
|
+
listPromise
|
|
82
93
|
.then(() => {
|
|
83
|
-
|
|
94
|
+
state.loading = false;
|
|
95
|
+
returnPromiseResolve(true);
|
|
84
96
|
})
|
|
85
97
|
.catch((error) => {
|
|
98
|
+
state.loading = false;
|
|
86
99
|
state.errored = true;
|
|
87
100
|
state.error = error;
|
|
88
|
-
|
|
89
|
-
})
|
|
90
|
-
.finally(() => {
|
|
91
|
-
state.loading = false;
|
|
101
|
+
returnPromiseResolve(false);
|
|
92
102
|
});
|
|
103
|
+
return returnPromise;
|
|
93
104
|
}
|
|
94
105
|
|
|
95
106
|
function addListObject(object) {
|
package/use/listSubscription.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { computed, effectScope, reactive, toRef } from "vue";
|
|
2
2
|
import { useListInstance } from "./listInstance";
|
|
3
3
|
import { cloneDeep, isEmpty, isObject } from "lodash";
|
|
4
4
|
import { assignReactiveObject } from "../utils/assignReactiveObject";
|
|
5
5
|
import inspect from "browser-util-inspect";
|
|
6
|
+
import { useCancellableIntent } from "../utils/cancellableIntent";
|
|
6
7
|
|
|
7
8
|
export class ListSubscriptionError extends Error {
|
|
8
9
|
constructor(message) {
|
|
@@ -12,11 +13,13 @@ export class ListSubscriptionError extends Error {
|
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
const defaultCrud = {
|
|
16
|
+
args: {},
|
|
15
17
|
subscribe: undefined,
|
|
16
18
|
};
|
|
17
19
|
|
|
18
|
-
export function setListSubscriptionCrud({ subscribe }) {
|
|
20
|
+
export function setListSubscriptionCrud({ subscribe, args = {} }) {
|
|
19
21
|
defaultCrud.subscribe = subscribe;
|
|
22
|
+
Object.assign(defaultCrud.args, args);
|
|
20
23
|
}
|
|
21
24
|
|
|
22
25
|
export function useListSubscriptions(args, listInstances = {}) {
|
|
@@ -27,35 +30,41 @@ export function useListSubscriptions(args, listInstances = {}) {
|
|
|
27
30
|
return subscriptions;
|
|
28
31
|
}
|
|
29
32
|
|
|
30
|
-
export function useListSubscription({ listInstance, crudArgs,
|
|
33
|
+
export function useListSubscription({ listInstance, crudArgs, listArgs, retrieveArgs }) {
|
|
31
34
|
if (!listInstance) {
|
|
32
|
-
listInstance = useListInstance({ crudArgs,
|
|
35
|
+
listInstance = useListInstance({ crudArgs, listArgs, retrieveArgs });
|
|
33
36
|
}
|
|
34
|
-
let
|
|
37
|
+
let subscribeIntent, listIntent;
|
|
35
38
|
const state = reactive({
|
|
36
|
-
|
|
39
|
+
crud: {
|
|
40
|
+
args: {},
|
|
41
|
+
subscribe: undefined,
|
|
42
|
+
},
|
|
43
|
+
subscriptionLoading: undefined,
|
|
44
|
+
subscriptionErrored: false,
|
|
45
|
+
subscriptionError: null,
|
|
46
|
+
intendToList: false,
|
|
37
47
|
intendToSubscribe: false,
|
|
38
48
|
});
|
|
39
|
-
|
|
49
|
+
// prevent linking of all instances to the same default .args object
|
|
50
|
+
Object.assign(state.crud, cloneDeep(defaultCrud));
|
|
51
|
+
if (crudArgs) {
|
|
52
|
+
assignReactiveObject(state.crud.args, crudArgs);
|
|
53
|
+
}
|
|
40
54
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
retrieveArgs = cloneDeep(listInstance.state.defaultRetrieveArgs);
|
|
55
|
+
function publicSubscribe({ list = true } = {}) {
|
|
56
|
+
let didSubscribe = false;
|
|
57
|
+
if (!state.intendToSubscribe) {
|
|
58
|
+
state.intendToSubscribe = true;
|
|
59
|
+
didSubscribe = true;
|
|
47
60
|
}
|
|
48
|
-
if (
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
subscriptionEventCallback,
|
|
54
|
-
});
|
|
55
|
-
state.subscribed = true;
|
|
56
|
-
return true;
|
|
61
|
+
if (list) {
|
|
62
|
+
if (!state.intendToList) {
|
|
63
|
+
state.intendToList = true;
|
|
64
|
+
didSubscribe = true;
|
|
65
|
+
}
|
|
57
66
|
}
|
|
58
|
-
return
|
|
67
|
+
return didSubscribe;
|
|
59
68
|
}
|
|
60
69
|
|
|
61
70
|
function subscriptionEventCallback(data, action) {
|
|
@@ -72,14 +81,17 @@ export function useListSubscription({ listInstance, crudArgs, defaultListArgs, d
|
|
|
72
81
|
}
|
|
73
82
|
}
|
|
74
83
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
state.
|
|
79
|
-
|
|
80
|
-
return returnValue;
|
|
84
|
+
function publicUnsubscribe() {
|
|
85
|
+
let didUnsubscribe = false;
|
|
86
|
+
if (state.intendToSubscribe) {
|
|
87
|
+
state.intendToSubscribe = false;
|
|
88
|
+
didUnsubscribe = true;
|
|
81
89
|
}
|
|
82
|
-
|
|
90
|
+
if (state.intendToList) {
|
|
91
|
+
state.intendToList = false;
|
|
92
|
+
didUnsubscribe = true;
|
|
93
|
+
}
|
|
94
|
+
return didUnsubscribe;
|
|
83
95
|
}
|
|
84
96
|
|
|
85
97
|
function addFromSubscription(data) {
|
|
@@ -128,19 +140,53 @@ export function useListSubscription({ listInstance, crudArgs, defaultListArgs, d
|
|
|
128
140
|
const es = effectScope();
|
|
129
141
|
|
|
130
142
|
es.run(() => {
|
|
143
|
+
state.loading = computed(() => listInstance.state.loading || state.subscriptionLoading);
|
|
144
|
+
state.errored = computed(() => listInstance.state.errored || state.subscriptionErrored);
|
|
145
|
+
state.error = computed(() => listInstance.state.error || state.subscriptionError);
|
|
146
|
+
|
|
147
|
+
state.retrieveArgs = computed(() => listInstance.state.retrieveArgs);
|
|
148
|
+
state.listArgs = computed(() => listInstance.state.listArgs);
|
|
131
149
|
state.objects = toRef(listInstance.state, "objects");
|
|
132
150
|
state.order = toRef(listInstance.state, "order");
|
|
133
151
|
state.objectsInOrder = toRef(listInstance.state, "objectsInOrder");
|
|
134
|
-
|
|
135
|
-
|
|
152
|
+
|
|
153
|
+
subscribeIntent = useCancellableIntent({
|
|
154
|
+
awaitableWithCancel: () => {
|
|
155
|
+
// this function cannot be async, or the resulting promise will lose its .cancel() method
|
|
156
|
+
const subscribePromise = state.crud.subscribe({
|
|
157
|
+
crudArgs: cloneDeep(state.crud.args),
|
|
158
|
+
listArgs: cloneDeep(listInstance.state.listArgs),
|
|
159
|
+
retrieveArgs: cloneDeep(listInstance.state.retrieveArgs),
|
|
160
|
+
subscriptionEventCallback,
|
|
161
|
+
});
|
|
162
|
+
// catching makes a new promise, we need to make sure the cancel method lives on.
|
|
163
|
+
const catchPromise = subscribePromise.catch((err) => {
|
|
164
|
+
console.error(err);
|
|
165
|
+
state.subscriptionErrored = true;
|
|
166
|
+
state.subscriptionError = err;
|
|
167
|
+
});
|
|
168
|
+
catchPromise.cancel = subscribePromise.cancel;
|
|
169
|
+
return catchPromise;
|
|
170
|
+
},
|
|
171
|
+
watchArguments: [toRef(state, "intendToSubscribe"), toRef(state, "listArgs"), toRef(state, "retrieveArgs")],
|
|
172
|
+
clearActiveOnResolved: false,
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
state.subscribed = toRef(subscribeIntent.state, "active");
|
|
176
|
+
|
|
177
|
+
listIntent = useCancellableIntent({
|
|
178
|
+
awaitableWithCancel: listInstance.list,
|
|
179
|
+
watchArguments: [toRef(state, "intendToList"), toRef(state, "listArgs"), toRef(state, "retrieveArgs")],
|
|
136
180
|
});
|
|
137
181
|
});
|
|
138
182
|
|
|
139
183
|
return {
|
|
140
184
|
state,
|
|
141
185
|
listInstance,
|
|
142
|
-
|
|
143
|
-
|
|
186
|
+
listIntent,
|
|
187
|
+
subscribeIntent,
|
|
188
|
+
subscribe: publicSubscribe,
|
|
189
|
+
unsubscribe: publicUnsubscribe,
|
|
144
190
|
effectScope: es,
|
|
145
191
|
};
|
|
146
192
|
}
|
package/use/objectInstance.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { cloneDeep
|
|
2
|
-
import { effectScope, reactive
|
|
1
|
+
import { cloneDeep } from "lodash";
|
|
2
|
+
import { effectScope, reactive } from "vue";
|
|
3
3
|
import { assignReactiveObject } from "../utils/assignReactiveObject";
|
|
4
4
|
|
|
5
5
|
export class ObjectError extends Error {
|
|
@@ -24,7 +24,7 @@ export function setObjectInstanceCrud({ retrieve, create, update, patch, delete:
|
|
|
24
24
|
defaultCrud.update = update;
|
|
25
25
|
defaultCrud.patch = patch;
|
|
26
26
|
defaultCrud.delete = deleteFn;
|
|
27
|
-
|
|
27
|
+
Object.assign(defaultCrud.args, args);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
export function useObjectInstances(instanceArgs) {
|
|
@@ -35,9 +35,9 @@ export function useObjectInstances(instanceArgs) {
|
|
|
35
35
|
return instances;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
export function useObjectInstance({ crudArgs, retrieveArgs }) {
|
|
38
|
+
export function useObjectInstance({ crudArgs, id, retrieveArgs }) {
|
|
39
39
|
const state = reactive({
|
|
40
|
-
|
|
40
|
+
crud: {
|
|
41
41
|
args: {},
|
|
42
42
|
retrieve: undefined,
|
|
43
43
|
create: undefined,
|
|
@@ -46,32 +46,32 @@ export function useObjectInstance({ crudArgs, retrieveArgs }) {
|
|
|
46
46
|
delete: undefined,
|
|
47
47
|
},
|
|
48
48
|
object: {},
|
|
49
|
-
|
|
49
|
+
id,
|
|
50
|
+
retrieveArgs,
|
|
50
51
|
loading: undefined,
|
|
51
52
|
errored: false,
|
|
52
53
|
error: null,
|
|
53
54
|
deleted: false,
|
|
54
55
|
});
|
|
55
|
-
|
|
56
|
+
// prevent linking of all instances to the same default .args object
|
|
57
|
+
Object.assign(state.crud, cloneDeep(defaultCrud));
|
|
56
58
|
if (crudArgs) {
|
|
57
|
-
assignReactiveObject(state.
|
|
59
|
+
assignReactiveObject(state.crud.args, crudArgs);
|
|
58
60
|
}
|
|
59
61
|
|
|
60
|
-
|
|
62
|
+
function retrieve() {
|
|
63
|
+
// this function cannot be async, or the resulting promise will lose its .cancel() method
|
|
61
64
|
if (state.loading) {
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
if (isEmpty(retrieveArgs) && !isEmpty(unref(state.defaultRetrieveArgs))) {
|
|
65
|
-
retrieveArgs = unref(state.defaultRetrieveArgs);
|
|
65
|
+
return Promise.reject(new ObjectError("already loading."));
|
|
66
66
|
}
|
|
67
67
|
state.loading = true;
|
|
68
68
|
state.errored = false;
|
|
69
69
|
state.error = null;
|
|
70
|
-
return state.
|
|
70
|
+
return state.crud
|
|
71
71
|
.retrieve({
|
|
72
|
-
crudArgs: state.
|
|
73
|
-
id,
|
|
74
|
-
retrieveArgs,
|
|
72
|
+
crudArgs: state.crud.args,
|
|
73
|
+
id: state.id,
|
|
74
|
+
retrieveArgs: state.retrieveArgs,
|
|
75
75
|
})
|
|
76
76
|
.then((object) => {
|
|
77
77
|
assignReactiveObject(state.object, object);
|
|
@@ -87,21 +87,18 @@ export function useObjectInstance({ crudArgs, retrieveArgs }) {
|
|
|
87
87
|
});
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
async function create({ object
|
|
90
|
+
async function create({ object }) {
|
|
91
91
|
if (state.loading) {
|
|
92
92
|
throw new ObjectError("already loading.");
|
|
93
93
|
}
|
|
94
|
-
if (isEmpty(retrieveArgs) && !isEmpty(unref(state.defaultRetrieveArgs))) {
|
|
95
|
-
retrieveArgs = unref(state.defaultRetrieveArgs);
|
|
96
|
-
}
|
|
97
94
|
state.loading = true;
|
|
98
95
|
state.errored = false;
|
|
99
96
|
state.error = null;
|
|
100
|
-
return state.
|
|
97
|
+
return state.crud
|
|
101
98
|
.create({
|
|
102
|
-
crudArgs: state.
|
|
99
|
+
crudArgs: state.crud.args,
|
|
103
100
|
object,
|
|
104
|
-
retrieveArgs,
|
|
101
|
+
retrieveArgs: state.retrieveArgs,
|
|
105
102
|
})
|
|
106
103
|
.then((object) => {
|
|
107
104
|
assignReactiveObject(state.object, object);
|
|
@@ -117,21 +114,18 @@ export function useObjectInstance({ crudArgs, retrieveArgs }) {
|
|
|
117
114
|
});
|
|
118
115
|
}
|
|
119
116
|
|
|
120
|
-
async function update({ object
|
|
117
|
+
async function update({ object }) {
|
|
121
118
|
if (state.loading) {
|
|
122
119
|
throw new ObjectError("already loading.");
|
|
123
120
|
}
|
|
124
|
-
if (isEmpty(retrieveArgs) && !isEmpty(unref(state.defaultRetrieveArgs))) {
|
|
125
|
-
retrieveArgs = unref(state.defaultRetrieveArgs);
|
|
126
|
-
}
|
|
127
121
|
state.loading = true;
|
|
128
122
|
state.errored = false;
|
|
129
123
|
state.error = null;
|
|
130
|
-
return state.
|
|
124
|
+
return state.crud
|
|
131
125
|
.update({
|
|
132
|
-
crudArgs: state.
|
|
126
|
+
crudArgs: state.crud.args,
|
|
133
127
|
object,
|
|
134
|
-
retrieveArgs,
|
|
128
|
+
retrieveArgs: state.retrieveArgs,
|
|
135
129
|
})
|
|
136
130
|
.then((object) => {
|
|
137
131
|
assignReactiveObject(state.object, object);
|
|
@@ -147,22 +141,19 @@ export function useObjectInstance({ crudArgs, retrieveArgs }) {
|
|
|
147
141
|
});
|
|
148
142
|
}
|
|
149
143
|
|
|
150
|
-
async function patch({
|
|
144
|
+
async function patch({ partialObject }) {
|
|
151
145
|
if (state.loading) {
|
|
152
146
|
throw new ObjectError("already loading.");
|
|
153
147
|
}
|
|
154
|
-
if (isEmpty(retrieveArgs) && !isEmpty(unref(state.defaultRetrieveArgs))) {
|
|
155
|
-
retrieveArgs = unref(state.defaultRetrieveArgs);
|
|
156
|
-
}
|
|
157
148
|
state.loading = true;
|
|
158
149
|
state.errored = false;
|
|
159
150
|
state.error = null;
|
|
160
|
-
return state.
|
|
151
|
+
return state.crud
|
|
161
152
|
.patch({
|
|
162
|
-
crudArgs: state.
|
|
163
|
-
id,
|
|
153
|
+
crudArgs: state.crud.args,
|
|
154
|
+
id: state.id,
|
|
164
155
|
partialObject,
|
|
165
|
-
retrieveArgs,
|
|
156
|
+
retrieveArgs: state.retrieveArgs,
|
|
166
157
|
})
|
|
167
158
|
.then((object) => {
|
|
168
159
|
assignReactiveObject(state.object, object);
|
|
@@ -178,17 +169,17 @@ export function useObjectInstance({ crudArgs, retrieveArgs }) {
|
|
|
178
169
|
});
|
|
179
170
|
}
|
|
180
171
|
|
|
181
|
-
async function deleteFn(
|
|
172
|
+
async function deleteFn() {
|
|
182
173
|
if (state.loading) {
|
|
183
174
|
throw new ObjectError("already loading.");
|
|
184
175
|
}
|
|
185
176
|
state.loading = true;
|
|
186
177
|
state.errored = false;
|
|
187
178
|
state.error = null;
|
|
188
|
-
return state.
|
|
179
|
+
return state.crud
|
|
189
180
|
.delete({
|
|
190
|
-
crudArgs: state.
|
|
191
|
-
id,
|
|
181
|
+
crudArgs: state.crud.args,
|
|
182
|
+
id: state.id,
|
|
192
183
|
})
|
|
193
184
|
.then(() => {
|
|
194
185
|
state.deleted = true;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { computed, effectScope,
|
|
1
|
+
import { cloneDeep } from "lodash";
|
|
2
|
+
import { computed, effectScope, reactive, toRef } from "vue";
|
|
3
3
|
import { assignReactiveObject } from "../utils/assignReactiveObject";
|
|
4
4
|
import { useObjectInstance } from "./objectInstance";
|
|
5
|
+
import { useCancellableIntent } from "../utils/cancellableIntent";
|
|
5
6
|
|
|
6
7
|
export class ObjectSubscriptionError extends Error {
|
|
7
8
|
constructor(message) {
|
|
@@ -10,12 +11,14 @@ export class ObjectSubscriptionError extends Error {
|
|
|
10
11
|
}
|
|
11
12
|
}
|
|
12
13
|
|
|
13
|
-
const defaultCrud =
|
|
14
|
+
const defaultCrud = {
|
|
15
|
+
args: {},
|
|
14
16
|
subscribe: undefined,
|
|
15
|
-
}
|
|
17
|
+
};
|
|
16
18
|
|
|
17
|
-
export function setObjectSubscriptionCrud({ subscribe }) {
|
|
19
|
+
export function setObjectSubscriptionCrud({ subscribe, args = {} }) {
|
|
18
20
|
defaultCrud.subscribe = subscribe;
|
|
21
|
+
Object.assign(defaultCrud.args, args);
|
|
19
22
|
}
|
|
20
23
|
|
|
21
24
|
export function useObjectSubscriptions(subscriptionArgs) {
|
|
@@ -27,61 +30,70 @@ export function useObjectSubscriptions(subscriptionArgs) {
|
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
export function useObjectSubscription({ objectInstance, crudArgs, id, retrieveArgs = {} }) {
|
|
33
|
+
if (retrieveArgs && objectInstance) {
|
|
34
|
+
throw new ObjectSubscriptionError(
|
|
35
|
+
"Cannot use retrieveArgs and objectInstance together, set retrieveArgs on objectInstance instead"
|
|
36
|
+
);
|
|
37
|
+
}
|
|
30
38
|
if (!objectInstance) {
|
|
31
|
-
objectInstance = useObjectInstance({ crudArgs, retrieveArgs });
|
|
39
|
+
objectInstance = useObjectInstance({ crudArgs, id, retrieveArgs });
|
|
32
40
|
}
|
|
33
41
|
const state = reactive({
|
|
34
|
-
|
|
42
|
+
crud: {
|
|
43
|
+
args: {},
|
|
35
44
|
subscribe: undefined,
|
|
36
45
|
},
|
|
37
46
|
id,
|
|
38
|
-
retrieveArgs,
|
|
39
47
|
subscriptionLoading: undefined,
|
|
40
48
|
subscriptionErrored: false,
|
|
41
49
|
subscriptionError: null,
|
|
42
|
-
subscribed:
|
|
50
|
+
subscribed: undefined,
|
|
43
51
|
intendToSubscribe: false,
|
|
44
52
|
intendToRetrieve: false,
|
|
45
53
|
});
|
|
46
|
-
|
|
47
|
-
|
|
54
|
+
// prevent linking of all instances to the same default .args object
|
|
55
|
+
Object.assign(state.crud, cloneDeep(defaultCrud));
|
|
56
|
+
if (crudArgs) {
|
|
57
|
+
assignReactiveObject(state.crud.args, crudArgs);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let subscribeIntent, retrieveIntent;
|
|
48
61
|
|
|
49
62
|
function updateFromSubscription(data) {
|
|
50
|
-
assignReactiveObject(
|
|
63
|
+
assignReactiveObject(state.object, data);
|
|
51
64
|
}
|
|
52
65
|
|
|
53
66
|
function deleteFromSubscription() {
|
|
54
|
-
|
|
55
|
-
assignReactiveObject(
|
|
67
|
+
state.deleted = true;
|
|
68
|
+
assignReactiveObject(state.object, {});
|
|
56
69
|
}
|
|
57
70
|
|
|
58
|
-
|
|
71
|
+
function publicSubscribe({ retrieve = true } = {}) {
|
|
72
|
+
let didSubscribe = false;
|
|
59
73
|
if (!state.intendToSubscribe) {
|
|
60
74
|
state.intendToSubscribe = true;
|
|
75
|
+
didSubscribe = true;
|
|
61
76
|
}
|
|
62
77
|
if (retrieve) {
|
|
63
78
|
if (!state.intendToRetrieve) {
|
|
64
79
|
state.intendToRetrieve = true;
|
|
80
|
+
didSubscribe = true;
|
|
65
81
|
}
|
|
66
82
|
}
|
|
67
|
-
return
|
|
83
|
+
return didSubscribe;
|
|
68
84
|
}
|
|
69
85
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
if (![state.id, state.retrieveArgs].every(identity)) {
|
|
75
|
-
// delayed until stuff is true;
|
|
76
|
-
return false;
|
|
86
|
+
function subscribe() {
|
|
87
|
+
// this function cannot be async, or the resulting promise will lose its .cancel() method
|
|
88
|
+
if (subscribeIntent.state.active || state.subscribed) {
|
|
89
|
+
return Promise.reject(new ObjectSubscriptionError("already subscribed or subscribing."));
|
|
77
90
|
}
|
|
78
91
|
state.subscriptionLoading = true;
|
|
79
92
|
state.subscriptionErrored = false;
|
|
80
93
|
state.subscriptionError = null;
|
|
81
94
|
let subscribePromise;
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
crudArgs: objectInstance.state.objectInstanceCrud.args,
|
|
95
|
+
subscribePromise = state.crud.subscribe({
|
|
96
|
+
crudArgs: state.crud.args,
|
|
85
97
|
id,
|
|
86
98
|
retrieveArgs: state.retrieveArgs,
|
|
87
99
|
callback: (data, action) => {
|
|
@@ -92,7 +104,14 @@ export function useObjectSubscription({ objectInstance, crudArgs, id, retrieveAr
|
|
|
92
104
|
}
|
|
93
105
|
},
|
|
94
106
|
});
|
|
95
|
-
|
|
107
|
+
let cancelSubscription = async () => {
|
|
108
|
+
let cancelPromise = subscribePromise.cancel();
|
|
109
|
+
cancelSubscription = null;
|
|
110
|
+
state.subscribed = false;
|
|
111
|
+
return cancelPromise;
|
|
112
|
+
};
|
|
113
|
+
// then/catch/finally makes a new promise, we need to make sure the cancel method lives on.
|
|
114
|
+
const catchPromise = subscribePromise
|
|
96
115
|
.then(() => {
|
|
97
116
|
state.subscribed = true;
|
|
98
117
|
return Promise.resolve(true);
|
|
@@ -110,26 +129,21 @@ export function useObjectSubscription({ objectInstance, crudArgs, id, retrieveAr
|
|
|
110
129
|
.finally(() => {
|
|
111
130
|
state.subscriptionLoading = false;
|
|
112
131
|
});
|
|
132
|
+
catchPromise.cancel = cancelSubscription;
|
|
133
|
+
return catchPromise;
|
|
113
134
|
}
|
|
114
135
|
|
|
115
|
-
|
|
136
|
+
function publicUnsubscribe() {
|
|
137
|
+
let didUnsubscribe = false;
|
|
116
138
|
if (state.intendToSubscribe) {
|
|
117
139
|
state.intendToSubscribe = false;
|
|
140
|
+
didUnsubscribe = true;
|
|
118
141
|
}
|
|
119
142
|
if (state.intendToRetrieve) {
|
|
120
143
|
state.intendToRetrieve = false;
|
|
144
|
+
didUnsubscribe = true;
|
|
121
145
|
}
|
|
122
|
-
return
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
async function unsubscribe() {
|
|
126
|
-
if (cancelSubscription) {
|
|
127
|
-
state.subscribed = false;
|
|
128
|
-
let returnPromise = cancelSubscription();
|
|
129
|
-
cancelSubscription = null;
|
|
130
|
-
return returnPromise;
|
|
131
|
-
}
|
|
132
|
-
return false;
|
|
146
|
+
return didUnsubscribe;
|
|
133
147
|
}
|
|
134
148
|
|
|
135
149
|
const es = effectScope();
|
|
@@ -139,46 +153,27 @@ export function useObjectSubscription({ objectInstance, crudArgs, id, retrieveAr
|
|
|
139
153
|
state.errored = computed(() => objectInstance.state.errored || state.subscriptionErrored);
|
|
140
154
|
state.error = computed(() => objectInstance.state.error || state.subscriptionError);
|
|
141
155
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
const everyNew = newArgs.every((e) => e);
|
|
146
|
-
const everyOld = oldArgs.every((e) => e);
|
|
147
|
-
if (everyOld) {
|
|
148
|
-
await unsubscribe();
|
|
149
|
-
}
|
|
150
|
-
if (everyNew) {
|
|
151
|
-
if (!cancelSubscription && !state.subscribed) {
|
|
152
|
-
await subscribe().catch(console.error);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
},
|
|
156
|
-
{
|
|
157
|
-
deep: true,
|
|
158
|
-
}
|
|
159
|
-
);
|
|
156
|
+
state.retrieveArgs = computed(() => objectInstance.state.retrieveArgs);
|
|
157
|
+
state.object = toRef(objectInstance.state, "object");
|
|
158
|
+
state.deleted = toRef(objectInstance.state, "deleted");
|
|
160
159
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
if (!objectInstance.state.loading) {
|
|
167
|
-
await objectInstance.retrieve({ id: state.id, ...state.retrieveArgs }).catch(console.error);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
},
|
|
171
|
-
{ deep: true }
|
|
172
|
-
);
|
|
160
|
+
subscribeIntent = useCancellableIntent({
|
|
161
|
+
awaitableWithCancel: subscribe,
|
|
162
|
+
watchArguments: [toRef(state, "intendToSubscribe"), toRef(state, "id"), toRef(state, "retrieveArgs")],
|
|
163
|
+
clearActiveOnResolved: false,
|
|
164
|
+
});
|
|
173
165
|
|
|
174
|
-
|
|
175
|
-
|
|
166
|
+
retrieveIntent = useCancellableIntent({
|
|
167
|
+
awaitableWithCancel: objectInstance.retrieve,
|
|
168
|
+
watchArguments: [toRef(state, "intendToRetrieve"), toRef(state, "id"), toRef(state, "retrieveArgs")],
|
|
176
169
|
});
|
|
177
170
|
});
|
|
178
171
|
|
|
179
172
|
return {
|
|
180
173
|
state,
|
|
181
174
|
objectInstance,
|
|
175
|
+
subscribeIntent,
|
|
176
|
+
retrieveIntent,
|
|
182
177
|
subscribe: publicSubscribe,
|
|
183
178
|
unsubscribe: publicUnsubscribe,
|
|
184
179
|
updateFromSubscription,
|