glib-web 2.6.3 → 2.6.5
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 +7 -18
- 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 +0 -3
- package/actions/panels/scrollTo.js +1 -1
- package/actions/panels/scrollToBottom.js +1 -1
- package/actions/windows/closeWithReload.js +1 -1
- package/actions/ws/push.js +2 -2
- package/app.vue +2 -3
- package/components/datetime.vue +0 -2
- package/components/fields/newRichText.vue +1 -1
- package/components/fields/timer.vue +1 -2
- package/components/image.vue +0 -8
- package/components/label.vue +8 -4
- package/components/markdown.vue +1 -1
- package/components/mixins/generic.js +2 -2
- package/components/mixins/styles.js +2 -2
- package/components/mixins/ws/actionCable.js +2 -2
- package/components/mixins/ws/phoenixSocket.js +6 -7
- package/components/panels/custom.vue +1 -0
- package/components/panels/horizontal.vue +1 -2
- package/components/panels/list.vue +25 -35
- package/components/panels/responsive.vue +1 -0
- package/components/panels/scroll.vue +0 -3
- package/components/panels/table.vue +0 -3
- package/nav/dialog.vue +1 -2
- package/package.json +1 -1
- package/utils/history.js +1 -1
- package/utils/http.js +35 -4
- package/utils/private/ws.js +2 -2
- package/utils/public.js +0 -6
- package/utils/settings.js +8 -0
- package/actions/commands/enqueue.js +0 -17
- package/utils/queue.js +0 -104
package/action.js
CHANGED
|
@@ -52,7 +52,6 @@ import ActionsAnalyticsLogEvent from "./actions/analytics/logEvent";
|
|
|
52
52
|
|
|
53
53
|
import ActionCommandsCopy from "./actions/commands/copy";
|
|
54
54
|
import ActionCommandsCustom from "./actions/commands/custom";
|
|
55
|
-
import ActionCommandsEnqueue from "./actions/commands/enqueue";
|
|
56
55
|
|
|
57
56
|
import ActionToursStart from "./actions/tours/start";
|
|
58
57
|
import ActionToursStop from "./actions/tours/stop";
|
|
@@ -112,7 +111,6 @@ const actions = {
|
|
|
112
111
|
|
|
113
112
|
"commands/copy": ActionCommandsCopy,
|
|
114
113
|
"commands/custom": ActionCommandsCustom,
|
|
115
|
-
"commands/enqueue": ActionCommandsEnqueue,
|
|
116
114
|
|
|
117
115
|
"tours/start": ActionToursStart,
|
|
118
116
|
"tours/stop": ActionToursStop,
|
|
@@ -126,6 +124,10 @@ export default class Action {
|
|
|
126
124
|
if (!TypeUtils.isObject(spec)) {
|
|
127
125
|
return;
|
|
128
126
|
}
|
|
127
|
+
if (!TypeUtils.isObject(component)) {
|
|
128
|
+
console.warn("Action requires a component");
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
129
131
|
|
|
130
132
|
const name = spec.action;
|
|
131
133
|
if (TypeUtils.isString(name)) {
|
|
@@ -134,28 +136,15 @@ export default class Action {
|
|
|
134
136
|
} else {
|
|
135
137
|
this.executeGlobal(name, spec, component, params);
|
|
136
138
|
}
|
|
137
|
-
|
|
138
|
-
// const actionName = name.replace(/-v1$/, "");
|
|
139
|
-
|
|
140
|
-
// try {
|
|
141
|
-
// const action = new actions[actionName]();
|
|
142
|
-
// const logDisabled = action.logDisabled && action.logDisabled();
|
|
143
|
-
// if (!logDisabled) {
|
|
144
|
-
// console.log(`Executing "${actionName}"`);
|
|
145
|
-
// }
|
|
146
|
-
// action.execute(spec, null, component, params);
|
|
147
|
-
// } catch (e) {
|
|
148
|
-
// console.log(`Failed executing command: "${e.message}"`);
|
|
149
|
-
// }
|
|
150
139
|
} else {
|
|
151
|
-
console.
|
|
140
|
+
console.warn(`Invalid action: "${name}"`);
|
|
152
141
|
}
|
|
153
142
|
}
|
|
154
143
|
|
|
155
144
|
static executeLocal(name, spec, component) {
|
|
156
145
|
const methodName = name.replace(/^component\//, "");
|
|
157
146
|
const componentName = GLib.component.vueName(component);
|
|
158
|
-
console.
|
|
147
|
+
console.debug(
|
|
159
148
|
`Executing component action on ${componentName}: ${methodName}`
|
|
160
149
|
);
|
|
161
150
|
component[`action_${methodName}`](spec);
|
|
@@ -172,7 +161,7 @@ export default class Action {
|
|
|
172
161
|
const action = new registry[actionName]();
|
|
173
162
|
const logDisabled = action.logDisabled && action.logDisabled();
|
|
174
163
|
if (!logDisabled) {
|
|
175
|
-
console.
|
|
164
|
+
console.debug(`Executing "${actionName}"`);
|
|
176
165
|
}
|
|
177
166
|
action.execute(spec, component, params);
|
|
178
167
|
} catch (e) {
|
|
@@ -14,10 +14,10 @@ export default class {
|
|
|
14
14
|
const suffix = params.referer_group ? "w/ referer" : "w/o referer";
|
|
15
15
|
params.build_env = Utils.settings.env;
|
|
16
16
|
// params.build_version = Utils.settings.appVersion;
|
|
17
|
-
console.
|
|
17
|
+
console.debug(`Logging ${suffix}:`, properties.name, params);
|
|
18
18
|
firebase.analytics().logEvent(properties.name, params);
|
|
19
19
|
} else {
|
|
20
|
-
console.
|
|
20
|
+
console.debug("Waiting for firebase to be initialized...");
|
|
21
21
|
setTimeout(() => {
|
|
22
22
|
this.execute(properties);
|
|
23
23
|
}, 100);
|
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
// import Storage from '../../utils/storage'
|
|
2
|
-
// import Keys from '../../keys'
|
|
3
|
-
|
|
4
1
|
export default class {
|
|
5
2
|
execute(properties, component) {
|
|
6
|
-
// console.log(properties['token'])
|
|
7
|
-
// Storage.setLocal(Keys.Db.csrfToken, properties['token'])
|
|
8
|
-
|
|
9
3
|
Utils.dom.setCsrf(properties["token"]);
|
|
10
4
|
Action.execute(properties["onSave"], component);
|
|
11
5
|
}
|
package/actions/cables/push.js
CHANGED
|
@@ -4,7 +4,9 @@ export default class {
|
|
|
4
4
|
const ws = window.vueApp.actionCable;
|
|
5
5
|
const channel = ws.channels[channelName];
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
// TODO: Use logDisabled() that reads params from server to decide whether we want to print the log or not.
|
|
8
|
+
// This is because `push` action's logs are a bit sensitive.
|
|
9
|
+
console.debug("Pushing to", channel);
|
|
8
10
|
|
|
9
11
|
Utils.type.ifString(properties.event, eventName => {
|
|
10
12
|
if (channel) {
|
package/actions/http/get.js
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
export default class {
|
|
2
2
|
execute(properties, controller) {
|
|
3
3
|
const currentUrl = window.location.href;
|
|
4
|
-
// const htmlUrl = Utils.url.htmlUrl(properties["url"]);
|
|
5
4
|
|
|
6
5
|
GLib.http.execute(properties, "GET", controller, (page, response) => {
|
|
7
6
|
Utils.type.ifString(properties.historyUrl, historyUrl => {
|
|
8
7
|
const cleanUrl = Utils.url.htmlUrl(historyUrl);
|
|
9
8
|
if (cleanUrl !== currentUrl) {
|
|
10
|
-
console.log("URL", cleanUrl, response.url, response);
|
|
11
9
|
const data = Object.assign({}, window.vueApp.page, {
|
|
12
10
|
replayGetResponse: page.onResponse
|
|
13
11
|
});
|
|
14
12
|
|
|
15
|
-
// Utils.history.pushPage(data, historyUrl);
|
|
16
13
|
Utils.history.pushPage(data, cleanUrl);
|
|
17
14
|
}
|
|
18
15
|
});
|
|
@@ -5,7 +5,7 @@ export default class {
|
|
|
5
5
|
Utils.launch.dialog.closestBody(component) || Utils.history._pageBody;
|
|
6
6
|
|
|
7
7
|
const selector = `#${properties.viewId}`;
|
|
8
|
-
console.
|
|
8
|
+
console.debug("Scrolling to", selector);
|
|
9
9
|
const element = pageBody.querySelector(selector);
|
|
10
10
|
element.scrollIntoView({
|
|
11
11
|
behavior: properties["animate"] ? "smooth" : "auto"
|
|
@@ -4,7 +4,7 @@ export default class {
|
|
|
4
4
|
// TODO: make this support any panelId
|
|
5
5
|
const target = properties.panelId ? `_innerBody` : "_pageBody";
|
|
6
6
|
const pageBody = Utils.history[target];
|
|
7
|
-
console.
|
|
7
|
+
console.debug(`Scrolling ${target} to bottom`);
|
|
8
8
|
pageBody.scrollTo({
|
|
9
9
|
top: pageBody.scrollHeight,
|
|
10
10
|
behavior: properties["animate"] ? "smooth" : "auto"
|
|
@@ -6,7 +6,7 @@ export default class {
|
|
|
6
6
|
const data = Object.assign({}, properties);
|
|
7
7
|
if (!Utils.history.back()) {
|
|
8
8
|
data.url = fallbackUrl;
|
|
9
|
-
console.
|
|
9
|
+
console.debug("Use fallbackUrl", data.url);
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
// Allow time for history.back() to complete, which is important for actions that need
|
package/actions/ws/push.js
CHANGED
|
@@ -13,14 +13,14 @@ export default class {
|
|
|
13
13
|
channel
|
|
14
14
|
.push(eventName, payload)
|
|
15
15
|
.receive("ok", resp => {
|
|
16
|
-
console.
|
|
16
|
+
console.debug(
|
|
17
17
|
`Push to '${topicName}/${eventName}' succeeded`,
|
|
18
18
|
resp
|
|
19
19
|
);
|
|
20
20
|
Utils.ws.handleResponse(resp.onResponse, component);
|
|
21
21
|
})
|
|
22
22
|
.receive("error", resp => {
|
|
23
|
-
console.
|
|
23
|
+
console.debug(`Push to '${topicName}/${eventName}' failed`, resp);
|
|
24
24
|
Utils.ws.handleResponse(resp.onResponse, component);
|
|
25
25
|
});
|
|
26
26
|
} else {
|
package/app.vue
CHANGED
|
@@ -91,7 +91,7 @@ export default {
|
|
|
91
91
|
}
|
|
92
92
|
},
|
|
93
93
|
created() {
|
|
94
|
-
console.
|
|
94
|
+
console.debug(
|
|
95
95
|
`Version: ${Utils.settings.appVersion} (${Utils.settings.env})`
|
|
96
96
|
);
|
|
97
97
|
Utils.history.saveInitialContent(this.page);
|
|
@@ -145,7 +145,6 @@ export default {
|
|
|
145
145
|
});
|
|
146
146
|
},
|
|
147
147
|
updateMainHeight() {
|
|
148
|
-
console.debug("Setting body height");
|
|
149
148
|
this.mainHeight = window.innerHeight - this.$refs.appBar.$el.offsetHeight;
|
|
150
149
|
}
|
|
151
150
|
}
|
|
@@ -196,7 +195,7 @@ body,
|
|
|
196
195
|
background: yellow !important;
|
|
197
196
|
}
|
|
198
197
|
// Don't override the default button's effect.
|
|
199
|
-
.glib-clickable:not(.v-btn) {
|
|
198
|
+
.glib-clickable:not(.v-btn):not(.disabled) {
|
|
200
199
|
cursor: pointer;
|
|
201
200
|
|
|
202
201
|
// // So that we can display a semi-transparent layer on hover (see below)
|
package/components/datetime.vue
CHANGED
|
@@ -108,10 +108,9 @@ export default {
|
|
|
108
108
|
}
|
|
109
109
|
},
|
|
110
110
|
$tearDown() {
|
|
111
|
-
console.log("Cleaning up timer...");
|
|
112
111
|
if (this.timer != null) {
|
|
113
112
|
clearInterval(this.timer);
|
|
114
|
-
console.
|
|
113
|
+
console.debug(`Timer stopped: ${this.timer}`);
|
|
115
114
|
this.timer == null;
|
|
116
115
|
}
|
|
117
116
|
},
|
package/components/image.vue
CHANGED
|
@@ -57,23 +57,15 @@ export default {
|
|
|
57
57
|
let width, height;
|
|
58
58
|
const aspectRatio = image.naturalWidth / image.naturalHeight;
|
|
59
59
|
|
|
60
|
-
console.log("FIT1");
|
|
61
|
-
|
|
62
60
|
if (this.spec.width == "matchParent") {
|
|
63
|
-
console.log("FIT2");
|
|
64
|
-
|
|
65
61
|
width = "100%";
|
|
66
62
|
// This breaks the image height when displayed in a badge.
|
|
67
63
|
// height = `${100 / aspectRatio}%`;
|
|
68
64
|
} else {
|
|
69
65
|
if (this.spec.width) {
|
|
70
|
-
console.log("FIT3");
|
|
71
|
-
|
|
72
66
|
width = `${this.spec.width}px`;
|
|
73
67
|
height = `${this.spec.width / aspectRatio}px`;
|
|
74
68
|
} else if (this.spec.height) {
|
|
75
|
-
console.log("FIT4", this.spec.height);
|
|
76
|
-
|
|
77
69
|
width = `${this.spec.height * aspectRatio}px`;
|
|
78
70
|
height = `${this.spec.height}px`;
|
|
79
71
|
}
|
package/components/label.vue
CHANGED
|
@@ -31,10 +31,14 @@ export default {
|
|
|
31
31
|
|
|
32
32
|
this.text = this.spec.text;
|
|
33
33
|
},
|
|
34
|
-
action_set(spec) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
// action_set(spec) {
|
|
35
|
+
// if (spec.user_id !== spec.filterKey) {
|
|
36
|
+
// this.text = spec.text;
|
|
37
|
+
// }
|
|
38
|
+
// },
|
|
39
|
+
action_merge(mergedSpec) {
|
|
40
|
+
Object.assign(this.spec, mergedSpec);
|
|
41
|
+
this.$ready();
|
|
38
42
|
}
|
|
39
43
|
}
|
|
40
44
|
};
|
package/components/markdown.vue
CHANGED
|
@@ -30,7 +30,7 @@ export default {
|
|
|
30
30
|
$ready() {
|
|
31
31
|
if (this.spec.previewVideo) {
|
|
32
32
|
this.youtubeId = this.extractYoutubeId(this.spec.text);
|
|
33
|
-
console.
|
|
33
|
+
console.debug("Detected Youtube ID", this.youtubeId);
|
|
34
34
|
}
|
|
35
35
|
// Set all links to be openWeb or open in a new tab by default
|
|
36
36
|
if (!this.spec.openWeb) {
|
|
@@ -54,7 +54,7 @@ export default {
|
|
|
54
54
|
const channel = ws.channels[topicName];
|
|
55
55
|
|
|
56
56
|
if (!channel) {
|
|
57
|
-
console.
|
|
57
|
+
console.debug(`Topic not ready: '${topicName}'`);
|
|
58
58
|
setTimeout(() => {
|
|
59
59
|
// Wait until $wsInitPhoenixSocket() has executed.
|
|
60
60
|
this.$wsSubscribeEvents(spec);
|
|
@@ -63,7 +63,7 @@ export default {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
for (const eventName of events) {
|
|
66
|
-
console.
|
|
66
|
+
console.debug(`Registering event '${eventName}'`);
|
|
67
67
|
channel.on(eventName, payload => {
|
|
68
68
|
console.debug(`Received '${eventName}' event`, payload);
|
|
69
69
|
Utils.ws.handleResponse(payload.onResponse, this);
|
|
@@ -65,12 +65,12 @@ export default {
|
|
|
65
65
|
const topDialog = Utils.launch.dialog.topDialog();
|
|
66
66
|
if (topDialog) {
|
|
67
67
|
if (!topDialog.isFormDirty) {
|
|
68
|
-
console.
|
|
68
|
+
console.debug("Dialog form is now dirty");
|
|
69
69
|
topDialog.isFormDirty = true;
|
|
70
70
|
}
|
|
71
71
|
} else {
|
|
72
72
|
if (!window.vueApp.isFormDirty) {
|
|
73
|
-
console.
|
|
73
|
+
console.debug("Window form is now dirty");
|
|
74
74
|
window.vueApp.isFormDirty = true;
|
|
75
75
|
}
|
|
76
76
|
}
|
|
@@ -19,13 +19,13 @@ export default {
|
|
|
19
19
|
channel: channelName
|
|
20
20
|
});
|
|
21
21
|
|
|
22
|
-
console.
|
|
22
|
+
console.debug("Connecting to channel", subscription);
|
|
23
23
|
|
|
24
24
|
consumer.subscriptions.create(subscription, {
|
|
25
25
|
connected() {
|
|
26
26
|
const ws = window.vueApp.actionCable;
|
|
27
27
|
ws.channels[channelName] = this;
|
|
28
|
-
console.
|
|
28
|
+
console.debug("Connected to channel", channelName);
|
|
29
29
|
},
|
|
30
30
|
|
|
31
31
|
disconnected() {},
|
|
@@ -12,7 +12,6 @@ export default {
|
|
|
12
12
|
Utils.type.ifObject(
|
|
13
13
|
spec,
|
|
14
14
|
ws => {
|
|
15
|
-
console.log("Connecting socket...");
|
|
16
15
|
Utils.launch.snackbar.indicator("Connecting...");
|
|
17
16
|
Utils.http.startIndicator(this);
|
|
18
17
|
|
|
@@ -54,14 +53,14 @@ export default {
|
|
|
54
53
|
|
|
55
54
|
this._wsSocket = socket;
|
|
56
55
|
socket.onOpen(() => {
|
|
57
|
-
console.
|
|
56
|
+
console.debug("Connected socket");
|
|
58
57
|
this._wsJoinChannel(socket, spec);
|
|
59
58
|
Utils.http.stopIndicator(this);
|
|
60
59
|
});
|
|
61
60
|
socket.onError(() => {
|
|
62
61
|
// Start the indicator again because this can trigger even after successfully connected.
|
|
63
62
|
Utils.http.startIndicator(this);
|
|
64
|
-
console.
|
|
63
|
+
console.debug("Unable to connect socket");
|
|
65
64
|
});
|
|
66
65
|
socket.connect();
|
|
67
66
|
},
|
|
@@ -80,7 +79,7 @@ export default {
|
|
|
80
79
|
const channel = socket.channel(topicName, {});
|
|
81
80
|
|
|
82
81
|
for (const eventName of events) {
|
|
83
|
-
console.
|
|
82
|
+
console.debug(`Registering event '${eventName}'`);
|
|
84
83
|
channel.on(eventName, payload => {
|
|
85
84
|
console.debug(`Received '${eventName}' event`, payload);
|
|
86
85
|
Utils.ws.handleResponse(payload.onResponse, this);
|
|
@@ -90,11 +89,11 @@ export default {
|
|
|
90
89
|
channel
|
|
91
90
|
.join()
|
|
92
91
|
.receive("ok", resp => {
|
|
93
|
-
console.
|
|
92
|
+
console.debug(`Channel joined '${topicName}'`, resp);
|
|
94
93
|
ws.channels[topicName] = channel;
|
|
95
94
|
})
|
|
96
95
|
.receive("error", resp => {
|
|
97
|
-
console.
|
|
96
|
+
console.debug(`Unable to join channel '${topicName}'`, resp);
|
|
98
97
|
Utils.launch.snackbar.error(
|
|
99
98
|
"Unable to connect. Please try again.",
|
|
100
99
|
this
|
|
@@ -103,7 +102,7 @@ export default {
|
|
|
103
102
|
},
|
|
104
103
|
_wsDisconnectSocket() {
|
|
105
104
|
if (this._wsSocket) {
|
|
106
|
-
console.
|
|
105
|
+
console.debug("Disconnecting socket");
|
|
107
106
|
// Sometimes this doesn't prevent errorneous socket from trying to reconnect. In this case,
|
|
108
107
|
// the socket will keep attempting to connect.
|
|
109
108
|
this._wsSocket.disconnect();
|
|
@@ -58,6 +58,7 @@ export default {
|
|
|
58
58
|
$ready() {
|
|
59
59
|
const onClick = this.spec.onClick ? { onClick: this.spec.onClick } : {};
|
|
60
60
|
this.customData = Object.assign(onClick, this.spec.data);
|
|
61
|
+
// Fix an issue where the panel doesn't get updated
|
|
61
62
|
if (this.$refs.ccomp) {
|
|
62
63
|
this.$refs.ccomp.$forceUpdate();
|
|
63
64
|
}
|
|
@@ -29,7 +29,6 @@
|
|
|
29
29
|
>
|
|
30
30
|
<!-- Using `item.id` as key is important to make sure the item gets updated
|
|
31
31
|
when dragging ends. -->
|
|
32
|
-
<!-- TODO: This div is causing image issue -->
|
|
33
32
|
<div
|
|
34
33
|
v-for="(item, index) in childViews"
|
|
35
34
|
:key="item.id || index"
|
|
@@ -132,7 +131,7 @@ export default {
|
|
|
132
131
|
|
|
133
132
|
const targetRowIndex = event.newIndex;
|
|
134
133
|
if (event.from == event.to && event.oldIndex == targetRowIndex) {
|
|
135
|
-
console.
|
|
134
|
+
console.debug("Reordering canceled");
|
|
136
135
|
return;
|
|
137
136
|
}
|
|
138
137
|
|
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<v-list
|
|
3
|
-
:two-line="twoLine"
|
|
4
|
-
:three-line="threeLine"
|
|
5
|
-
class="py-0"
|
|
6
|
-
:class="$classes()"
|
|
7
|
-
:style="$styles()"
|
|
8
|
-
>
|
|
2
|
+
<v-list class="py-0" :class="$classes()" :style="$styles()">
|
|
9
3
|
<div ref="topAnchor">
|
|
10
4
|
<div v-if="prevPageUrl" class="py-3 px-4">
|
|
11
5
|
Loading...
|
|
@@ -28,9 +22,10 @@
|
|
|
28
22
|
>
|
|
29
23
|
<div
|
|
30
24
|
v-for="(row, rowIndex) in section.rows"
|
|
31
|
-
:key="`${sectionIndex}_${rowIndex}`"
|
|
25
|
+
:key="row.id || `${sectionIndex}_${rowIndex}`"
|
|
32
26
|
:ref="`row_${sectionIndex}_${rowIndex}`"
|
|
33
27
|
:data-dragRowId="row.id"
|
|
28
|
+
class="row_container"
|
|
34
29
|
>
|
|
35
30
|
<!-- <v-divider v-if="!spec.responsiveCols && rowIndex == 0" /> -->
|
|
36
31
|
|
|
@@ -88,30 +83,6 @@ export default {
|
|
|
88
83
|
dragSupport: null
|
|
89
84
|
};
|
|
90
85
|
},
|
|
91
|
-
computed: {
|
|
92
|
-
twoLine() {
|
|
93
|
-
for (const section of this.sections) {
|
|
94
|
-
const rows = section.rows || [];
|
|
95
|
-
for (const row of rows) {
|
|
96
|
-
if (row.subtitle) {
|
|
97
|
-
return true;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
return false;
|
|
102
|
-
},
|
|
103
|
-
threeLine() {
|
|
104
|
-
for (const section of this.sections) {
|
|
105
|
-
const rows = section.rows || [];
|
|
106
|
-
for (const row of rows) {
|
|
107
|
-
if (row.subsubtitle) {
|
|
108
|
-
return true;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
return false;
|
|
113
|
-
}
|
|
114
|
-
},
|
|
115
86
|
methods: {
|
|
116
87
|
$ready() {
|
|
117
88
|
this.sections = this.spec.sections || [];
|
|
@@ -139,7 +110,6 @@ export default {
|
|
|
139
110
|
return row;
|
|
140
111
|
},
|
|
141
112
|
action_loadNext(spec) {
|
|
142
|
-
console.log("TODO: load new comments and append to this list", spec);
|
|
143
113
|
// TODO
|
|
144
114
|
// - Load next rows/items from the Rails server
|
|
145
115
|
// - You can pass some parameters (e.g. currentItemId) for pagination
|
|
@@ -227,7 +197,7 @@ export default {
|
|
|
227
197
|
const targetSectionedRowIndex = event.newIndex;
|
|
228
198
|
// const sourceSectionedRowIndex = event.oldIndex;
|
|
229
199
|
if (event.from == event.to && event.oldIndex == targetSectionedRowIndex) {
|
|
230
|
-
console.
|
|
200
|
+
console.debug("Reordering canceled");
|
|
231
201
|
return;
|
|
232
202
|
}
|
|
233
203
|
|
|
@@ -235,7 +205,6 @@ export default {
|
|
|
235
205
|
this.$recursiveUpdate();
|
|
236
206
|
|
|
237
207
|
const targetSectionIndex = this.getSectionIndex(event.to);
|
|
238
|
-
// const targetSection = this.getSection(targetSectionIndex);
|
|
239
208
|
|
|
240
209
|
const targetAbsoluteIndex =
|
|
241
210
|
this.previousSectionsRowCount(event.to) + targetSectionedRowIndex;
|
|
@@ -260,6 +229,27 @@ export default {
|
|
|
260
229
|
});
|
|
261
230
|
|
|
262
231
|
GLib.action.execute(mergedSpec, this);
|
|
232
|
+
|
|
233
|
+
this.hackLingeringHighlight();
|
|
234
|
+
},
|
|
235
|
+
hackLingeringHighlight() {
|
|
236
|
+
// After a row finished dragging, the row below it will automatically be
|
|
237
|
+
// "active&hover" for some reason, which will cause it to be incorrectly
|
|
238
|
+
// highlighted
|
|
239
|
+
// This seems to be a browser/library bug, so as a workaround, we temporarily
|
|
240
|
+
// apply the "disabled" class so the highlighting doesn't happen.
|
|
241
|
+
this.$el
|
|
242
|
+
.querySelectorAll(":active .row_container > .glib-clickable:hover")
|
|
243
|
+
.forEach(activeRow => {
|
|
244
|
+
activeRow.classList.add("disabled");
|
|
245
|
+
document.addEventListener(
|
|
246
|
+
"mousemove",
|
|
247
|
+
function() {
|
|
248
|
+
activeRow.classList.remove("disabled");
|
|
249
|
+
},
|
|
250
|
+
{ once: true }
|
|
251
|
+
);
|
|
252
|
+
});
|
|
263
253
|
},
|
|
264
254
|
getSectionIndex(element) {
|
|
265
255
|
return element.dataset.dragsectionindex;
|
|
@@ -23,7 +23,6 @@ export default {
|
|
|
23
23
|
// // Let the styles be handled by the child panel.
|
|
24
24
|
// const outerSpec = {};
|
|
25
25
|
// outerSpec["padding"] = this.spec["outerPadding"];
|
|
26
|
-
// console.log("outerSpec", outerSpec);
|
|
27
26
|
// const styles = this.$styles(outerSpec);
|
|
28
27
|
//
|
|
29
28
|
// const styles = this.genericStyles();
|
|
@@ -36,8 +35,6 @@ export default {
|
|
|
36
35
|
// width: this.spec.width,
|
|
37
36
|
// height: this.spec.height
|
|
38
37
|
// });
|
|
39
|
-
//
|
|
40
|
-
// console.log("styles", styles);
|
|
41
38
|
|
|
42
39
|
// Let the specified styles be handled by the child panel.
|
|
43
40
|
const styles = this.$styles({
|
|
@@ -139,9 +139,6 @@ export default {
|
|
|
139
139
|
// const createdAt = row[5]
|
|
140
140
|
// const activationState = row[14] ? 'active' : 'pending'
|
|
141
141
|
// count += 1
|
|
142
|
-
// if (email.includes("'")) {
|
|
143
|
-
// console.log("INVALID NAME", name)
|
|
144
|
-
// }
|
|
145
142
|
// str += `{ name: "${name}".to_s, email: '${email}'.to_s, created_at: '${createdAt}', activation_state: '${activationState}'},\n`
|
|
146
143
|
// }
|
|
147
144
|
// }
|
package/nav/dialog.vue
CHANGED
|
@@ -134,7 +134,7 @@ export default {
|
|
|
134
134
|
});
|
|
135
135
|
},
|
|
136
136
|
$tearDown() {
|
|
137
|
-
console.
|
|
137
|
+
console.debug("Dialog destroyed");
|
|
138
138
|
this.stack.remove(this);
|
|
139
139
|
},
|
|
140
140
|
close() {
|
|
@@ -192,7 +192,6 @@ export default {
|
|
|
192
192
|
this.model = true;
|
|
193
193
|
},
|
|
194
194
|
updateMainHeight() {
|
|
195
|
-
console.debug("Setting body height");
|
|
196
195
|
this.mainHeight = window.innerHeight - 140;
|
|
197
196
|
}
|
|
198
197
|
}
|
package/package.json
CHANGED
package/utils/history.js
CHANGED
package/utils/http.js
CHANGED
|
@@ -31,7 +31,8 @@ export default class {
|
|
|
31
31
|
const formData = new FormData();
|
|
32
32
|
var params = data || {};
|
|
33
33
|
for (const key in params) {
|
|
34
|
-
formData.append(key, params[key]);
|
|
34
|
+
// formData.append(key, params[key]);
|
|
35
|
+
this._populateFormData(formData, key, params[key]);
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
if (needCsrfToken && !Type.isString(params["authenticity_token"])) {
|
|
@@ -40,8 +41,33 @@ export default class {
|
|
|
40
41
|
return formData;
|
|
41
42
|
}
|
|
42
43
|
|
|
44
|
+
static _populateFormData(formData, key, value) {
|
|
45
|
+
if (GLib.type.isArray(value)) {
|
|
46
|
+
for (const item of value) {
|
|
47
|
+
this._populateFormData(formData, `${key}[]`, item);
|
|
48
|
+
}
|
|
49
|
+
} else if (GLib.type.isObject(value)) {
|
|
50
|
+
for (const innerKey in value) {
|
|
51
|
+
this._populateFormData(
|
|
52
|
+
formData,
|
|
53
|
+
`${key}[${innerKey}]`,
|
|
54
|
+
value[innerKey]
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
} else {
|
|
58
|
+
formData.append(key, value);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
43
62
|
static load(properties, component) {
|
|
44
|
-
const
|
|
63
|
+
const urlString = properties["url"];
|
|
64
|
+
let url;
|
|
65
|
+
try {
|
|
66
|
+
url = new URL(urlString);
|
|
67
|
+
} catch (e) {
|
|
68
|
+
console.warn("Invalid URL", urlString);
|
|
69
|
+
throw e;
|
|
70
|
+
}
|
|
45
71
|
const domainMatched = window.location.hostname == url.hostname;
|
|
46
72
|
|
|
47
73
|
// If this is an external domain, we rely on `onUnload()` instead.
|
|
@@ -134,18 +160,23 @@ export default class {
|
|
|
134
160
|
url = Utils.url.appendParams(url, body);
|
|
135
161
|
body = null;
|
|
136
162
|
}
|
|
137
|
-
console.
|
|
163
|
+
console.debug(`${method} ${url}`);
|
|
138
164
|
|
|
139
165
|
const request = new HttpRequest();
|
|
140
166
|
let response = null;
|
|
141
167
|
const vm = this;
|
|
142
168
|
|
|
143
169
|
const analyticsHeaders = this.analyticsHeaders(component);
|
|
170
|
+
const headers = Object.assign(
|
|
171
|
+
{ Accept: "text/html" },
|
|
172
|
+
analyticsHeaders,
|
|
173
|
+
GLib.settings.headerAugmenter(url, method)
|
|
174
|
+
);
|
|
144
175
|
|
|
145
176
|
fetch(url, {
|
|
146
177
|
method: method,
|
|
147
178
|
body: body,
|
|
148
|
-
headers:
|
|
179
|
+
headers: headers,
|
|
149
180
|
// Make sure to pass cookies for same origin URLs.
|
|
150
181
|
// Needed for some browsers, e.g. Edge and Android's native.
|
|
151
182
|
credentials: "same-origin",
|
package/utils/private/ws.js
CHANGED
|
@@ -7,13 +7,13 @@ export default class {
|
|
|
7
7
|
const ws = window.vueApp.webSocket;
|
|
8
8
|
Utils.type.ifObject(payload.wsHeader, header => {
|
|
9
9
|
Object.assign(ws.header, header);
|
|
10
|
-
console.
|
|
10
|
+
console.debug("New header: ", ws.header);
|
|
11
11
|
});
|
|
12
12
|
|
|
13
13
|
const actionName = payload.action;
|
|
14
14
|
if (actionName.startsWith("component/")) {
|
|
15
15
|
const methodName = actionName.replace(/^component\//, "");
|
|
16
|
-
console.
|
|
16
|
+
console.debug(`Executing component action: ${methodName}`);
|
|
17
17
|
component[`action_${methodName}`](payload);
|
|
18
18
|
} else {
|
|
19
19
|
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
|
@@ -6,6 +6,10 @@ class MutableSettings {
|
|
|
6
6
|
this.errorHandler = err => {
|
|
7
7
|
console.error(err.message);
|
|
8
8
|
};
|
|
9
|
+
this.headerAugmenter = (_url, _method) => {
|
|
10
|
+
// Set a custom augmenter to add custom HTTP headers.
|
|
11
|
+
return {};
|
|
12
|
+
};
|
|
9
13
|
}
|
|
10
14
|
}
|
|
11
15
|
const settings = new MutableSettings();
|
|
@@ -24,6 +28,10 @@ export default class ImmutableSettings {
|
|
|
24
28
|
return settings.errorHandler;
|
|
25
29
|
}
|
|
26
30
|
|
|
31
|
+
static get headerAugmenter() {
|
|
32
|
+
return settings.headerAugmenter;
|
|
33
|
+
}
|
|
34
|
+
|
|
27
35
|
static get env() {
|
|
28
36
|
return process.env.NODE_ENV;
|
|
29
37
|
}
|
|
@@ -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,104 +0,0 @@
|
|
|
1
|
-
// From https://stackoverflow.com/questions/47157428/how-to-implement-a-pseudo-blocking-async-queue-in-js-ts
|
|
2
|
-
|
|
3
|
-
import Vue from "vue";
|
|
4
|
-
|
|
5
|
-
// const jobQueue = [];
|
|
6
|
-
export default class {
|
|
7
|
-
constructor() {
|
|
8
|
-
// invariant: at least one of the arrays is empty
|
|
9
|
-
this.resolvers = [];
|
|
10
|
-
this.promises = [];
|
|
11
|
-
}
|
|
12
|
-
_add() {
|
|
13
|
-
this.promises.push(
|
|
14
|
-
new Promise(resolve => {
|
|
15
|
-
this.resolvers.push(resolve);
|
|
16
|
-
})
|
|
17
|
-
);
|
|
18
|
-
}
|
|
19
|
-
enqueue(t) {
|
|
20
|
-
// if (this.resolvers.length) this.resolvers.shift()(t);
|
|
21
|
-
// else this.promises.push(Promise.resolve(t));
|
|
22
|
-
if (!this.resolvers.length) this._add();
|
|
23
|
-
this.resolvers.shift()(t);
|
|
24
|
-
}
|
|
25
|
-
dequeue() {
|
|
26
|
-
if (!this.promises.length) this._add();
|
|
27
|
-
return this.promises.shift();
|
|
28
|
-
}
|
|
29
|
-
// now some utilities:
|
|
30
|
-
isEmpty() {
|
|
31
|
-
// there are no values available
|
|
32
|
-
return !this.promises.length; // this.length <= 0
|
|
33
|
-
}
|
|
34
|
-
isBlocked() {
|
|
35
|
-
// it's waiting for values
|
|
36
|
-
return !!this.resolvers.length; // this.length < 0
|
|
37
|
-
}
|
|
38
|
-
get length() {
|
|
39
|
-
return this.promises.length - this.resolvers.length;
|
|
40
|
-
}
|
|
41
|
-
sleep(ms) {
|
|
42
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
43
|
-
}
|
|
44
|
-
async start() {
|
|
45
|
-
// TODO: This is experimental
|
|
46
|
-
const delayBetweenJobs = 2000;
|
|
47
|
-
|
|
48
|
-
for await (const job of this) {
|
|
49
|
-
console.log("Executing job...");
|
|
50
|
-
job();
|
|
51
|
-
if (delayBetweenJobs) {
|
|
52
|
-
console.log("Waiting for next job1...");
|
|
53
|
-
await this.sleep(delayBetweenJobs);
|
|
54
|
-
console.log("Waiting for next job2...");
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
[Symbol.asyncIterator]() {
|
|
59
|
-
// Todo: Use AsyncIterator.from()
|
|
60
|
-
return {
|
|
61
|
-
next: () => this.dequeue().then(value => ({ done: false, value })),
|
|
62
|
-
[Symbol.asyncIterator]() {
|
|
63
|
-
return this;
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// export default class {
|
|
70
|
-
// constructor() {
|
|
71
|
-
// this.jobQueue = [];
|
|
72
|
-
// this.timerHandle = null;
|
|
73
|
-
// }
|
|
74
|
-
// // static get csrfElement() {
|
|
75
|
-
// // return document.querySelector(`meta[name="csrf-token"]`);
|
|
76
|
-
// // }
|
|
77
|
-
|
|
78
|
-
// // static getCsrf() {
|
|
79
|
-
// // return this.csrfElement.getAttribute("content");
|
|
80
|
-
// // }
|
|
81
|
-
|
|
82
|
-
// // static setCsrf(value) {
|
|
83
|
-
// // return this.csrfElement.setAttribute("content", value);
|
|
84
|
-
// // }
|
|
85
|
-
|
|
86
|
-
// start(interval) {
|
|
87
|
-
// this.timerHandle = setInterval(() => {
|
|
88
|
-
// console.log("DEQUQE1");
|
|
89
|
-
// const oldestJob = this.jobQueue.shift();
|
|
90
|
-
// if (oldestJob) {
|
|
91
|
-
// console.log("DEQUQE2");
|
|
92
|
-
// oldestJob();
|
|
93
|
-
// }
|
|
94
|
-
// }, interval);
|
|
95
|
-
// }
|
|
96
|
-
|
|
97
|
-
// stop() {
|
|
98
|
-
// clearInterval(this.timerHandle);
|
|
99
|
-
// }
|
|
100
|
-
|
|
101
|
-
// enqueue(job) {
|
|
102
|
-
// this.jobQueue.push(job);
|
|
103
|
-
// }
|
|
104
|
-
// }
|