glib-web 3.0.2 → 3.0.4
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/action.js +15 -29
- package/actions/analytics/logEvent.js +2 -2
- package/actions/auth/saveCsrfToken.js +0 -6
- package/actions/cables/push.js +3 -1
- package/actions/http/get.js +28 -16
- package/actions/panels/scrollTo.js +32 -7
- package/actions/panels/scrollToBottom.js +1 -1
- package/actions/windows/closeWithReload.js +1 -1
- package/actions/ws/push.js +2 -2
- package/app.vue +8 -9
- package/components/_chip.vue +29 -12
- package/components/_responsive.vue +27 -10
- package/components/component.vue +10 -2
- package/components/datetime.vue +0 -2
- package/components/fields/check.vue +31 -23
- package/components/fields/hidden.vue +7 -2
- package/components/fields/newRichText.vue +1 -1
- package/components/fields/radioGroup.vue +2 -1
- package/components/fields/richText.vue +4 -4
- package/components/fields/submit.vue +5 -2
- package/components/fields/text.vue +22 -23
- package/components/fields/textarea.vue +2 -1
- package/components/fields/timer.vue +1 -2
- package/components/label.vue +8 -4
- package/components/mixins/events.js +14 -19
- package/components/mixins/generic.js +2 -2
- package/components/mixins/styles.js +1 -1
- package/components/mixins/ws/actionCable.js +2 -2
- package/components/mixins/ws/phoenixSocket.js +6 -7
- package/components/panels/horizontal.vue +8 -8
- package/components/panels/list.vue +22 -3
- package/components/panels/responsive.vue +2 -11
- package/components/panels/scroll.vue +0 -3
- package/components/panels/table.vue +0 -3
- package/components/panels/timeline.vue +3 -4
- package/components/switch.vue +5 -4
- package/nav/dialog.vue +18 -8
- package/package.json +2 -2
- package/utils/history.js +1 -1
- package/utils/http.js +56 -10
- package/utils/private/ws.js +2 -2
- package/utils/public.js +0 -6
- package/utils/settings.js +10 -2
- package/actions/commands/enqueue.js +0 -17
- package/utils/queue.js +0 -102
package/utils/http.js
CHANGED
|
@@ -33,7 +33,8 @@ export default class {
|
|
|
33
33
|
const formData = new FormData();
|
|
34
34
|
var params = data || {};
|
|
35
35
|
for (const key in params) {
|
|
36
|
-
formData.append(key, params[key]);
|
|
36
|
+
// formData.append(key, params[key]);
|
|
37
|
+
this._populateFormData(formData, key, params[key]);
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
if (needCsrfToken && !Type.isString(params["authenticity_token"])) {
|
|
@@ -42,8 +43,33 @@ export default class {
|
|
|
42
43
|
return formData;
|
|
43
44
|
}
|
|
44
45
|
|
|
46
|
+
static _populateFormData(formData, key, value) {
|
|
47
|
+
if (GLib.type.isArray(value)) {
|
|
48
|
+
for (const item of value) {
|
|
49
|
+
this._populateFormData(formData, `${key}[]`, item);
|
|
50
|
+
}
|
|
51
|
+
} else if (GLib.type.isObject(value)) {
|
|
52
|
+
for (const innerKey in value) {
|
|
53
|
+
this._populateFormData(
|
|
54
|
+
formData,
|
|
55
|
+
`${key}[${innerKey}]`,
|
|
56
|
+
value[innerKey]
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
} else {
|
|
60
|
+
formData.append(key, value);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
45
64
|
static load(properties, component) {
|
|
46
|
-
const
|
|
65
|
+
const urlString = properties["url"];
|
|
66
|
+
let url;
|
|
67
|
+
try {
|
|
68
|
+
url = new URL(urlString);
|
|
69
|
+
} catch (e) {
|
|
70
|
+
console.warn("Invalid URL", urlString);
|
|
71
|
+
throw e;
|
|
72
|
+
}
|
|
47
73
|
const domainMatched = window.location.hostname == url.hostname;
|
|
48
74
|
|
|
49
75
|
// If this is an external domain, we rely on `onUnload()` instead.
|
|
@@ -120,14 +146,14 @@ export default class {
|
|
|
120
146
|
Utils.http.forceComponentUpdate(() => {
|
|
121
147
|
vueApp.page = page
|
|
122
148
|
const redirectUrl = Utils.url.htmlUrl(response.url);
|
|
123
|
-
Utils.history.updatePage(page, redirectUrl);
|
|
149
|
+
Utils.history.updatePage(vueApp.page, redirectUrl);
|
|
124
150
|
|
|
125
151
|
GLib.action.execute(properties["onReload"], component);
|
|
126
152
|
});
|
|
127
153
|
});
|
|
128
154
|
}
|
|
129
155
|
|
|
130
|
-
static execute(properties, methodName, component, jsonHandler) {
|
|
156
|
+
static execute(properties, methodName, component, jsonHandler, errorHandler) {
|
|
131
157
|
this.startIndicator(component);
|
|
132
158
|
|
|
133
159
|
// `fetch()` only supports uppercase
|
|
@@ -138,18 +164,26 @@ export default class {
|
|
|
138
164
|
url = Utils.url.appendParams(url, body);
|
|
139
165
|
body = null;
|
|
140
166
|
}
|
|
141
|
-
|
|
167
|
+
const silent = properties.silent;
|
|
168
|
+
if (!silent) {
|
|
169
|
+
console.debug(`${method} ${url}`);
|
|
170
|
+
}
|
|
142
171
|
|
|
143
172
|
const request = new HttpRequest();
|
|
144
173
|
let response = null;
|
|
145
174
|
const vm = this;
|
|
146
175
|
|
|
147
176
|
const analyticsHeaders = this.analyticsHeaders(component);
|
|
177
|
+
const headers = Object.assign(
|
|
178
|
+
{ Accept: "text/html" },
|
|
179
|
+
analyticsHeaders,
|
|
180
|
+
GLib.settings.headerAugmenter(url, method)
|
|
181
|
+
);
|
|
148
182
|
|
|
149
183
|
fetch(url, {
|
|
150
184
|
method: method,
|
|
151
185
|
body: body,
|
|
152
|
-
headers:
|
|
186
|
+
headers: headers,
|
|
153
187
|
// Make sure to pass cookies for same origin URLs.
|
|
154
188
|
// Needed for some browsers, e.g. Edge and Android's native.
|
|
155
189
|
credentials: "same-origin",
|
|
@@ -158,24 +192,36 @@ export default class {
|
|
|
158
192
|
.then(res => {
|
|
159
193
|
vm.stopIndicator(component);
|
|
160
194
|
|
|
195
|
+
response = res;
|
|
161
196
|
if (res.status >= 500) {
|
|
162
197
|
throw "Server error";
|
|
163
198
|
} else if (res.status >= 400) {
|
|
164
199
|
throw "Not accessible";
|
|
165
200
|
} else {
|
|
166
|
-
response = res;
|
|
167
201
|
return res.json();
|
|
168
202
|
}
|
|
169
203
|
})
|
|
170
204
|
.then(data => {
|
|
171
|
-
|
|
205
|
+
if (!silent) {
|
|
206
|
+
console.debug("Success", data);
|
|
207
|
+
}
|
|
172
208
|
jsonHandler(data, response);
|
|
173
209
|
})
|
|
174
210
|
.catch(error => {
|
|
175
211
|
vm.stopIndicator(component);
|
|
212
|
+
|
|
176
213
|
if (!request.canceled) {
|
|
177
|
-
|
|
178
|
-
|
|
214
|
+
const message = error.toString();
|
|
215
|
+
|
|
216
|
+
console.error("Error:", message);
|
|
217
|
+
|
|
218
|
+
if (errorHandler) {
|
|
219
|
+
errorHandler(error, response);
|
|
220
|
+
} else {
|
|
221
|
+
if (!silent) {
|
|
222
|
+
Utils.launch.snackbar.error(message, component);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
179
225
|
} else {
|
|
180
226
|
console.info("Canceled");
|
|
181
227
|
}
|
package/utils/private/ws.js
CHANGED
|
@@ -9,13 +9,13 @@ export default class {
|
|
|
9
9
|
const ws = vueApp.webSocket;
|
|
10
10
|
Utils.type.ifObject(payload.wsHeader, header => {
|
|
11
11
|
Object.assign(ws.header, header);
|
|
12
|
-
console.
|
|
12
|
+
console.debug("New header: ", ws.header);
|
|
13
13
|
});
|
|
14
14
|
|
|
15
15
|
const actionName = payload.action;
|
|
16
16
|
if (actionName.startsWith("component/")) {
|
|
17
17
|
const methodName = actionName.replace(/^component\//, "");
|
|
18
|
-
console.
|
|
18
|
+
console.debug(`Executing component action: ${methodName}`);
|
|
19
19
|
component[`action_${methodName}`](payload);
|
|
20
20
|
} else {
|
|
21
21
|
GLib.action.execute(payload, component);
|
package/utils/public.js
CHANGED
|
@@ -5,11 +5,8 @@ import Type from "./type";
|
|
|
5
5
|
import Form from "./form";
|
|
6
6
|
import Component from "./component";
|
|
7
7
|
import Hash from "./hash";
|
|
8
|
-
import Queue from "./queue";
|
|
9
8
|
import Settings from "./settings";
|
|
10
9
|
|
|
11
|
-
const _queue = new Queue();
|
|
12
|
-
|
|
13
10
|
export default class {
|
|
14
11
|
static get action() {
|
|
15
12
|
return Action;
|
|
@@ -35,7 +32,4 @@ export default class {
|
|
|
35
32
|
static get settings() {
|
|
36
33
|
return Settings;
|
|
37
34
|
}
|
|
38
|
-
static get queue() {
|
|
39
|
-
return _queue;
|
|
40
|
-
}
|
|
41
35
|
}
|
package/utils/settings.js
CHANGED
|
@@ -5,8 +5,12 @@ class MutableSettings {
|
|
|
5
5
|
this.reactive = true;
|
|
6
6
|
this.themes = {};
|
|
7
7
|
this.gtagId = null;
|
|
8
|
-
this.errorHandler = err => {
|
|
9
|
-
console.error(err.message);
|
|
8
|
+
this.errorHandler = (err, message) => {
|
|
9
|
+
console.error(message || err.message);
|
|
10
|
+
};
|
|
11
|
+
this.headerAugmenter = (_url, _method) => {
|
|
12
|
+
// Set a custom augmenter to add custom HTTP headers.
|
|
13
|
+
return {};
|
|
10
14
|
};
|
|
11
15
|
}
|
|
12
16
|
}
|
|
@@ -26,6 +30,10 @@ export default class ImmutableSettings {
|
|
|
26
30
|
return settings.errorHandler;
|
|
27
31
|
}
|
|
28
32
|
|
|
33
|
+
static get headerAugmenter() {
|
|
34
|
+
return settings.headerAugmenter;
|
|
35
|
+
}
|
|
36
|
+
|
|
29
37
|
static get env() {
|
|
30
38
|
return process.env.NODE_ENV;
|
|
31
39
|
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
// import Hash from "../../utils/hash";
|
|
2
|
-
|
|
3
|
-
export default class {
|
|
4
|
-
// async executeJob(spec, component) {
|
|
5
|
-
// console.log("Waiting for next job1...");
|
|
6
|
-
// GLib.action.execute(spec.onExecute, component);
|
|
7
|
-
// await this.sleep(2000);
|
|
8
|
-
// console.log("Waiting for next job2...");
|
|
9
|
-
// }
|
|
10
|
-
execute(spec, component) {
|
|
11
|
-
GLib.queue.enqueue(() => {
|
|
12
|
-
GLib.action.execute(spec.onExecute, component);
|
|
13
|
-
// console.log("Waiting for next job0...");
|
|
14
|
-
// this.executeJob(spec, component);
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
}
|
package/utils/queue.js
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
// From https://stackoverflow.com/questions/47157428/how-to-implement-a-pseudo-blocking-async-queue-in-js-ts
|
|
2
|
-
|
|
3
|
-
// const jobQueue = [];
|
|
4
|
-
export default class {
|
|
5
|
-
constructor() {
|
|
6
|
-
// invariant: at least one of the arrays is empty
|
|
7
|
-
this.resolvers = [];
|
|
8
|
-
this.promises = [];
|
|
9
|
-
}
|
|
10
|
-
_add() {
|
|
11
|
-
this.promises.push(
|
|
12
|
-
new Promise(resolve => {
|
|
13
|
-
this.resolvers.push(resolve);
|
|
14
|
-
})
|
|
15
|
-
);
|
|
16
|
-
}
|
|
17
|
-
enqueue(t) {
|
|
18
|
-
// if (this.resolvers.length) this.resolvers.shift()(t);
|
|
19
|
-
// else this.promises.push(Promise.resolve(t));
|
|
20
|
-
if (!this.resolvers.length) this._add();
|
|
21
|
-
this.resolvers.shift()(t);
|
|
22
|
-
}
|
|
23
|
-
dequeue() {
|
|
24
|
-
if (!this.promises.length) this._add();
|
|
25
|
-
return this.promises.shift();
|
|
26
|
-
}
|
|
27
|
-
// now some utilities:
|
|
28
|
-
isEmpty() {
|
|
29
|
-
// there are no values available
|
|
30
|
-
return !this.promises.length; // this.length <= 0
|
|
31
|
-
}
|
|
32
|
-
isBlocked() {
|
|
33
|
-
// it's waiting for values
|
|
34
|
-
return !!this.resolvers.length; // this.length < 0
|
|
35
|
-
}
|
|
36
|
-
get length() {
|
|
37
|
-
return this.promises.length - this.resolvers.length;
|
|
38
|
-
}
|
|
39
|
-
sleep(ms) {
|
|
40
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
41
|
-
}
|
|
42
|
-
async start() {
|
|
43
|
-
// TODO: This is experimental
|
|
44
|
-
const delayBetweenJobs = 2000;
|
|
45
|
-
|
|
46
|
-
for await (const job of this) {
|
|
47
|
-
console.log("Executing job...");
|
|
48
|
-
job();
|
|
49
|
-
if (delayBetweenJobs) {
|
|
50
|
-
console.log("Waiting for next job1...");
|
|
51
|
-
await this.sleep(delayBetweenJobs);
|
|
52
|
-
console.log("Waiting for next job2...");
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
[Symbol.asyncIterator]() {
|
|
57
|
-
// Todo: Use AsyncIterator.from()
|
|
58
|
-
return {
|
|
59
|
-
next: () => this.dequeue().then(value => ({ done: false, value })),
|
|
60
|
-
[Symbol.asyncIterator]() {
|
|
61
|
-
return this;
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// export default class {
|
|
68
|
-
// constructor() {
|
|
69
|
-
// this.jobQueue = [];
|
|
70
|
-
// this.timerHandle = null;
|
|
71
|
-
// }
|
|
72
|
-
// // static get csrfElement() {
|
|
73
|
-
// // return document.querySelector(`meta[name="csrf-token"]`);
|
|
74
|
-
// // }
|
|
75
|
-
|
|
76
|
-
// // static getCsrf() {
|
|
77
|
-
// // return this.csrfElement.getAttribute("content");
|
|
78
|
-
// // }
|
|
79
|
-
|
|
80
|
-
// // static setCsrf(value) {
|
|
81
|
-
// // return this.csrfElement.setAttribute("content", value);
|
|
82
|
-
// // }
|
|
83
|
-
|
|
84
|
-
// start(interval) {
|
|
85
|
-
// this.timerHandle = setInterval(() => {
|
|
86
|
-
// console.log("DEQUQE1");
|
|
87
|
-
// const oldestJob = this.jobQueue.shift();
|
|
88
|
-
// if (oldestJob) {
|
|
89
|
-
// console.log("DEQUQE2");
|
|
90
|
-
// oldestJob();
|
|
91
|
-
// }
|
|
92
|
-
// }, interval);
|
|
93
|
-
// }
|
|
94
|
-
|
|
95
|
-
// stop() {
|
|
96
|
-
// clearInterval(this.timerHandle);
|
|
97
|
-
// }
|
|
98
|
-
|
|
99
|
-
// enqueue(job) {
|
|
100
|
-
// this.jobQueue.push(job);
|
|
101
|
-
// }
|
|
102
|
-
// }
|