@yuneta/gobj-ui 7.25.6 → 7.25.8
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/README.md +53 -1
- package/dist/gobj-ui.cjs.js +330 -63
- package/dist/gobj-ui.es.js +330 -63
- package/package.json +1 -1
- package/src/c_yui_schema_editor.js +339 -66
- package/src/schema_editor_reload.wiring.test.js +473 -0
- package/src/schema_editor_transport.wiring.test.js +174 -1
- package/src/shell_modals.js +61 -12
- package/src/shell_toast_once.test.js +125 -0
package/dist/gobj-ui.es.js
CHANGED
|
@@ -3020,6 +3020,22 @@ function maybe_apply_translator($node, opts) {
|
|
|
3020
3020
|
* Non-blocking, auto-dismiss after `opts.timeout` ms (default
|
|
3021
3021
|
* 5000). `opts.timeout = 0` disables auto-dismiss.
|
|
3022
3022
|
* Returns `{ close() }` so callers can dismiss programmatically.
|
|
3023
|
+
*
|
|
3024
|
+
* ONE toast per message on screen: a string message of the same
|
|
3025
|
+
* kind as a toast still showing is not stacked under it. One
|
|
3026
|
+
* close of a transport settles every request it cut, each view
|
|
3027
|
+
* shows its failure, and the operator got a column of identical
|
|
3028
|
+
* "the connection dropped". A message built of nodes is never
|
|
3029
|
+
* merged: its text is not a key.
|
|
3030
|
+
*
|
|
3031
|
+
* A repeat is still a CALLER of its own: it gets its own handle
|
|
3032
|
+
* and its own time, and the toast stays while any caller holds
|
|
3033
|
+
* it -- until every handle is closed or has timed out. Handed the
|
|
3034
|
+
* first caller's handle and timer, a repeat asking `timeout: 0`
|
|
3035
|
+
* was dismissed on the first one's timer, and closing one
|
|
3036
|
+
* caller's handle closed a toast another was still showing
|
|
3037
|
+
* (third independent review). The operator's ✕ closes it for
|
|
3038
|
+
* everybody.
|
|
3023
3039
|
***************************************************************/
|
|
3024
3040
|
function show_notification(shell, kind, message, opts) {
|
|
3025
3041
|
let $layer = notification_layer(shell);
|
|
@@ -3027,6 +3043,11 @@ function show_notification(shell, kind, message, opts) {
|
|
|
3027
3043
|
log_warning("yui_shell_show_*: shell has no notification layer");
|
|
3028
3044
|
return { close: () => {} };
|
|
3029
3045
|
}
|
|
3046
|
+
let timeout = opts && opts.timeout != null ? opts.timeout : 5e3;
|
|
3047
|
+
let key = typeof message === "string" ? `${kind}\u0000${message}` : null;
|
|
3048
|
+
if (key !== null) {
|
|
3049
|
+
for (let $shown of Array.from($layer.children)) if ($shown.__toast_key__ === key && $shown.__toast__) return $shown.__toast__.hold(timeout);
|
|
3050
|
+
}
|
|
3030
3051
|
let p_attrs = typeof message === "string" ? { i18n: message } : {};
|
|
3031
3052
|
let $note = createElement2([
|
|
3032
3053
|
"div",
|
|
@@ -3049,22 +3070,38 @@ function show_notification(shell, kind, message, opts) {
|
|
|
3049
3070
|
]);
|
|
3050
3071
|
$layer.appendChild($note);
|
|
3051
3072
|
maybe_apply_translator($note, opts);
|
|
3052
|
-
let
|
|
3073
|
+
let holders = /* @__PURE__ */ new Set();
|
|
3053
3074
|
let closed = false;
|
|
3054
|
-
let
|
|
3075
|
+
let close_toast = function() {
|
|
3055
3076
|
if (closed) return;
|
|
3056
3077
|
closed = true;
|
|
3057
|
-
if (timeout_id) {
|
|
3058
|
-
clearTimeout(timeout_id);
|
|
3059
|
-
timeout_id = null;
|
|
3078
|
+
for (let holder of holders) if (holder.timeout_id) {
|
|
3079
|
+
clearTimeout(holder.timeout_id);
|
|
3080
|
+
holder.timeout_id = null;
|
|
3060
3081
|
}
|
|
3082
|
+
holders.clear();
|
|
3061
3083
|
if ($note.parentNode) $note.parentNode.removeChild($note);
|
|
3062
3084
|
};
|
|
3085
|
+
let hold = function(ms) {
|
|
3086
|
+
let holder = { timeout_id: null };
|
|
3087
|
+
let release = function() {
|
|
3088
|
+
if (!holders.has(holder)) return;
|
|
3089
|
+
if (holder.timeout_id) {
|
|
3090
|
+
clearTimeout(holder.timeout_id);
|
|
3091
|
+
holder.timeout_id = null;
|
|
3092
|
+
}
|
|
3093
|
+
holders.delete(holder);
|
|
3094
|
+
if (holders.size === 0) close_toast();
|
|
3095
|
+
};
|
|
3096
|
+
holders.add(holder);
|
|
3097
|
+
if (ms > 0) holder.timeout_id = setTimeout(release, ms);
|
|
3098
|
+
return { close: release };
|
|
3099
|
+
};
|
|
3063
3100
|
let $del = $note.querySelector(".delete");
|
|
3064
|
-
if ($del) $del.addEventListener("click",
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
return
|
|
3101
|
+
if ($del) $del.addEventListener("click", close_toast);
|
|
3102
|
+
$note.__toast_key__ = key;
|
|
3103
|
+
$note.__toast__ = { hold };
|
|
3104
|
+
return hold(timeout);
|
|
3068
3105
|
}
|
|
3069
3106
|
function yui_shell_show_info(shell, message, opts) {
|
|
3070
3107
|
return show_notification(shell, "info", message, opts);
|
|
@@ -72135,6 +72172,7 @@ var PRIVATE_DATA$1 = {
|
|
|
72135
72172
|
pending: 0,
|
|
72136
72173
|
load_error: "",
|
|
72137
72174
|
load_round: 0,
|
|
72175
|
+
load_keeps_written: false,
|
|
72138
72176
|
treedb_id: "",
|
|
72139
72177
|
topic_name: "",
|
|
72140
72178
|
diagram: false,
|
|
@@ -72151,6 +72189,9 @@ var PRIVATE_DATA$1 = {
|
|
|
72151
72189
|
write_tag: 0,
|
|
72152
72190
|
connected: false,
|
|
72153
72191
|
reload_on_open: false,
|
|
72192
|
+
dropped_round: 0,
|
|
72193
|
+
dropped_write: 0,
|
|
72194
|
+
reload_after_write: false,
|
|
72154
72195
|
pending_seg: null,
|
|
72155
72196
|
import_plan: null,
|
|
72156
72197
|
import_pane: null,
|
|
@@ -72310,10 +72351,28 @@ function refuse_if_readonly(gobj, event) {
|
|
|
72310
72351
|
* schema set of the yuno is here: everything below — the
|
|
72311
72352
|
* drawing, the validation, the export — is computed from these
|
|
72312
72353
|
* and asks the backend nothing more.
|
|
72354
|
+
*
|
|
72355
|
+
* THE LOADING SCREEN IS DRAWN, not assumed. ST_LOADING declares
|
|
72356
|
+
* none of the actions of a screen, so the screen it replaces goes
|
|
72357
|
+
* and the toolbar stops answering: left up, a click on a card, on
|
|
72358
|
+
* Back or on the drawing answered "Event NOT DEFINED in state
|
|
72359
|
+
* ST_LOADING" and was lost (third independent review). A dialog
|
|
72360
|
+
* left open is answered in ST_LOADING instead (ac_wait_for_the_load).
|
|
72361
|
+
*
|
|
72362
|
+
* `keep_written`: a load the host did not ask for -- the reconnect,
|
|
72363
|
+
* a write that turned out done -- keeps what this session wrote.
|
|
72364
|
+
* Only the host's EV_REFRESH (a Save sends it) forgets it.
|
|
72365
|
+
*
|
|
72366
|
+
* A load that cannot leave WHOLE did not happen (out of session it
|
|
72367
|
+
* is not even asked): the records the model was built on are put
|
|
72368
|
+
* back (emptied, the next write patched them into a model with no
|
|
72369
|
+
* treedb), the round is spent so a request that did leave is
|
|
72370
|
+
* stale, and the session back asks again.
|
|
72313
72371
|
***************************************************************/
|
|
72314
|
-
function request_model(gobj) {
|
|
72372
|
+
function request_model(gobj, keep_written) {
|
|
72315
72373
|
let priv = gobj.priv;
|
|
72316
72374
|
if (!gobj_read_pointer_attr(gobj, "gobj_remote_yuno")) return -1;
|
|
72375
|
+
let previous = priv.records;
|
|
72317
72376
|
priv.records = {
|
|
72318
72377
|
treedbs: [],
|
|
72319
72378
|
topics: [],
|
|
@@ -72322,22 +72381,31 @@ function request_model(gobj) {
|
|
|
72322
72381
|
priv.load_error = "";
|
|
72323
72382
|
priv.pending = 0;
|
|
72324
72383
|
priv.load_round++;
|
|
72384
|
+
priv.load_keeps_written = !!keep_written;
|
|
72325
72385
|
gobj_change_state(gobj, "ST_LOADING");
|
|
72326
|
-
|
|
72327
|
-
|
|
72328
|
-
|
|
72329
|
-
|
|
72330
|
-
|
|
72331
|
-
|
|
72332
|
-
|
|
72333
|
-
|
|
72334
|
-
|
|
72335
|
-
|
|
72386
|
+
set_busy(gobj, false);
|
|
72387
|
+
render(gobj);
|
|
72388
|
+
let left = 0;
|
|
72389
|
+
if (transport_in_session(gobj)) {
|
|
72390
|
+
for (let topic_name of [
|
|
72391
|
+
T_TREEDBS,
|
|
72392
|
+
T_TOPICS,
|
|
72393
|
+
T_COLS
|
|
72394
|
+
]) if (remote_command(gobj, "nodes", {
|
|
72395
|
+
topic_name,
|
|
72396
|
+
options: { list_dict: true }
|
|
72397
|
+
}, { round: priv.load_round }) === 0) left++;
|
|
72398
|
+
}
|
|
72399
|
+
if (left < 3) {
|
|
72400
|
+
priv.records = previous;
|
|
72401
|
+
priv.load_round++;
|
|
72402
|
+
priv.reload_on_open = true;
|
|
72336
72403
|
priv.load_error = t("cannot reach the treedb");
|
|
72337
|
-
|
|
72338
|
-
|
|
72404
|
+
if (priv.model) yui_shell_show_error(yui_shell_of(gobj), "cannot reach the treedb", { t });
|
|
72405
|
+
end_load(gobj);
|
|
72339
72406
|
return -1;
|
|
72340
72407
|
}
|
|
72408
|
+
priv.pending = left;
|
|
72341
72409
|
return 0;
|
|
72342
72410
|
}
|
|
72343
72411
|
/***************************************************************
|
|
@@ -72400,15 +72468,19 @@ function build_model(gobj) {
|
|
|
72400
72468
|
* From here on, "did this session change it?" has an answer.
|
|
72401
72469
|
*
|
|
72402
72470
|
* Called ONLY when records arrive from the store: that is what a
|
|
72403
|
-
* measurement of "changed since" can be measured against.
|
|
72404
|
-
*
|
|
72405
|
-
*
|
|
72406
|
-
*
|
|
72471
|
+
* measurement of "changed since" can be measured against.
|
|
72472
|
+
*
|
|
72473
|
+
* `written` is forgotten only by a load the HOST asked for (its
|
|
72474
|
+
* EV_REFRESH, which a Save sends). Since M36 a write moves no
|
|
72475
|
+
* version, so `written` is the draft chip of what this session
|
|
72476
|
+
* wrote and no longer half of a version measurement: a reload of
|
|
72477
|
+
* the reconnect, or of a write that turned out done, forgot the
|
|
72478
|
+
* chips of writes the host had not been told about.
|
|
72407
72479
|
***************************************************************/
|
|
72408
72480
|
function start_measuring(gobj) {
|
|
72409
72481
|
let priv = gobj.priv;
|
|
72410
72482
|
priv.baseline = {};
|
|
72411
|
-
priv.written = {};
|
|
72483
|
+
if (!priv.load_keeps_written) priv.written = {};
|
|
72412
72484
|
priv.host_ids = {};
|
|
72413
72485
|
if (!priv.model) return;
|
|
72414
72486
|
for (let treedb of priv.model.treedbs) for (let topic of treedb.topics) priv.baseline[topic.id] = topic.topic_version;
|
|
@@ -72590,8 +72662,10 @@ function render_toolbar(gobj) {
|
|
|
72590
72662
|
let state = gobj_current_state(gobj);
|
|
72591
72663
|
let readonly = is_readonly(gobj);
|
|
72592
72664
|
let has_model = !!priv.model;
|
|
72665
|
+
let on_a_treedb = state !== "ST_IDLE" && state !== "ST_EMPTY";
|
|
72666
|
+
let in_flight = state === "ST_SAVING" || state === "ST_LOADING";
|
|
72593
72667
|
let $left = [];
|
|
72594
|
-
if (priv.treedb_id) $left.push([
|
|
72668
|
+
if (priv.treedb_id && on_a_treedb) $left.push([
|
|
72595
72669
|
"button",
|
|
72596
72670
|
{
|
|
72597
72671
|
class: "SCHEMA_BACK button",
|
|
@@ -72664,17 +72738,17 @@ function render_toolbar(gobj) {
|
|
|
72664
72738
|
$trail
|
|
72665
72739
|
]);
|
|
72666
72740
|
let $right = [];
|
|
72667
|
-
if (priv.treedb_id && has_model) {
|
|
72741
|
+
if (priv.treedb_id && has_model && on_a_treedb) {
|
|
72668
72742
|
$right.push(toolbar_button("SCHEMA_DIAGRAM_BTN", "yi-compass-drafting", priv.diagram ? "topics" : "diagram", "EV_TOGGLE_DIAGRAM", false));
|
|
72669
72743
|
$right.push(toolbar_button("SCHEMA_VALIDATE_BTN", "yi-square-check", "check", "EV_VALIDATE", false));
|
|
72670
72744
|
$right.push(toolbar_button("SCHEMA_EXPORT_BTN", "yi-download", "export", "EV_EXPORT", false));
|
|
72671
72745
|
if (!readonly) $right.push(toolbar_button("SCHEMA_IMPORT_BTN", "yi-upload", "import", "EV_IMPORT", false));
|
|
72672
72746
|
}
|
|
72673
|
-
if (priv.topic_name && !readonly && !priv.diagram) {
|
|
72747
|
+
if (priv.topic_name && !readonly && !priv.diagram && on_a_treedb) {
|
|
72674
72748
|
if (undo_order_writes(gobj, current_topic(gobj)).length > 0) $right.push(toolbar_button("SCHEMA_UNDO_ORDER_BTN", "yi-arrow-rotate-left", "undo the order", "EV_UNDO_ORDER", false));
|
|
72675
72749
|
$right.push(toolbar_button("SCHEMA_ADD_COL_BTN", "yi-plus", "new column", "EV_ADD_COLUMN", false));
|
|
72676
72750
|
}
|
|
72677
|
-
if (priv.treedb_id && !priv.topic_name && !readonly && !priv.diagram) $right.push(toolbar_button("SCHEMA_ADD_TOPIC_BTN", "yi-plus", "new topic", "EV_ADD_TOPIC", false));
|
|
72751
|
+
if (priv.treedb_id && !priv.topic_name && !readonly && !priv.diagram && on_a_treedb) $right.push(toolbar_button("SCHEMA_ADD_TOPIC_BTN", "yi-plus", "new topic", "EV_ADD_TOPIC", false));
|
|
72678
72752
|
$right.push(toolbar_button("SCHEMA_REFRESH_BTN", "yi-arrows-rotate", "refresh", "EV_REFRESH", state === "ST_LOADING"));
|
|
72679
72753
|
let $bar = createElement2([
|
|
72680
72754
|
"div",
|
|
@@ -72700,7 +72774,7 @@ function render_toolbar(gobj) {
|
|
|
72700
72774
|
]);
|
|
72701
72775
|
for (let $button of $bar.querySelectorAll("button")) {
|
|
72702
72776
|
$button.__gobj__ = gobj;
|
|
72703
|
-
if (
|
|
72777
|
+
if (in_flight) $button.disabled = true;
|
|
72704
72778
|
}
|
|
72705
72779
|
$toolbar.appendChild($bar);
|
|
72706
72780
|
refresh_language($toolbar, t);
|
|
@@ -72718,16 +72792,19 @@ function render(gobj) {
|
|
|
72718
72792
|
render_toolbar(gobj);
|
|
72719
72793
|
if (state === "ST_LOADING") {
|
|
72720
72794
|
show_notice(gobj, "loading the schemas");
|
|
72795
|
+
destroy_diagram(gobj);
|
|
72721
72796
|
clear($body);
|
|
72722
72797
|
return;
|
|
72723
72798
|
}
|
|
72724
72799
|
if (state === "ST_IDLE") {
|
|
72725
72800
|
show_notice(gobj, priv.load_error ? "cannot load the schemas" : "not connected", priv.load_error);
|
|
72801
|
+
destroy_diagram(gobj);
|
|
72726
72802
|
clear($body);
|
|
72727
72803
|
return;
|
|
72728
72804
|
}
|
|
72729
72805
|
if (state === "ST_EMPTY") {
|
|
72730
72806
|
show_notice(gobj, "this yuno stores no schema");
|
|
72807
|
+
destroy_diagram(gobj);
|
|
72731
72808
|
clear($body);
|
|
72732
72809
|
return;
|
|
72733
72810
|
}
|
|
@@ -73416,12 +73493,16 @@ function render_diagram(gobj) {
|
|
|
73416
73493
|
let priv = gobj.priv;
|
|
73417
73494
|
let $body = $of(gobj, ".SCHEMA_BODY");
|
|
73418
73495
|
let treedb = current_treedb(gobj);
|
|
73419
|
-
if (!$body
|
|
73496
|
+
if (!$body) return;
|
|
73497
|
+
if (!treedb) {
|
|
73498
|
+
destroy_diagram(gobj);
|
|
73499
|
+
clear($body);
|
|
73420
73500
|
show_notice(gobj, "that treedb is not here any more", priv.treedb_id);
|
|
73421
73501
|
return;
|
|
73422
73502
|
}
|
|
73423
73503
|
if (!gclass_find_by_name("C_YUI_TREEDB_SCHEMA")) {
|
|
73424
73504
|
log_error(`${gobj_short_name(gobj)}: C_YUI_TREEDB_SCHEMA not registered by the app`);
|
|
73505
|
+
clear($body);
|
|
73425
73506
|
show_notice(gobj, "the schema drawing is not available");
|
|
73426
73507
|
return;
|
|
73427
73508
|
}
|
|
@@ -74619,6 +74700,15 @@ function transport_in_session(gobj) {
|
|
|
74619
74700
|
* one the drop abandoned (or a deadline settled) can still come
|
|
74620
74701
|
* back: counted, it ended the next load with the old data or
|
|
74621
74702
|
* moved the next write's queue.
|
|
74703
|
+
*
|
|
74704
|
+
* The state is part of the question, and that is only true
|
|
74705
|
+
* because NOTHING moves the editor out of ST_LOADING or ST_SAVING
|
|
74706
|
+
* but the end of what is in flight: a position the host sends
|
|
74707
|
+
* meanwhile waits in `pending_seg` (ac_show()). 7.25.6 let EV_SHOW
|
|
74708
|
+
* move a RELOAD out of ST_LOADING, and every answer of it was then
|
|
74709
|
+
* "for a load that is over": the records it had emptied stayed
|
|
74710
|
+
* empty, and the next write patched them into a model with no
|
|
74711
|
+
* treedb at all.
|
|
74622
74712
|
***************************************************************/
|
|
74623
74713
|
function is_current_load(gobj, md) {
|
|
74624
74714
|
return gobj_current_state(gobj) === "ST_LOADING" && kw_get_int(gobj, md, "round", 0, 0) === gobj.priv.load_round;
|
|
@@ -74639,20 +74729,42 @@ function transport_dropped(gobj) {
|
|
|
74639
74729
|
let state = gobj_current_state(gobj);
|
|
74640
74730
|
priv.reload_on_open = true;
|
|
74641
74731
|
if (state === "ST_SAVING") {
|
|
74732
|
+
log_warning(`${gobj_short_name(gobj)}: the transport dropped during a write: the model is read again on the reconnect`);
|
|
74733
|
+
priv.dropped_write = priv.write_tag;
|
|
74642
74734
|
priv.write_tag++;
|
|
74643
74735
|
return end_writes(gobj, "the connection dropped during the write");
|
|
74644
74736
|
}
|
|
74645
74737
|
if (state === "ST_LOADING") {
|
|
74738
|
+
log_warning(`${gobj_short_name(gobj)}: the transport dropped during the load: the model is read again on the reconnect`);
|
|
74739
|
+
priv.dropped_round = priv.load_round;
|
|
74646
74740
|
priv.load_round++;
|
|
74647
74741
|
priv.pending = 0;
|
|
74648
74742
|
priv.load_error = "";
|
|
74649
|
-
|
|
74650
|
-
gobj_change_state(gobj, "ST_IDLE");
|
|
74651
|
-
render(gobj);
|
|
74743
|
+
return end_load(gobj);
|
|
74652
74744
|
}
|
|
74653
74745
|
return 0;
|
|
74654
74746
|
}
|
|
74655
74747
|
/***************************************************************
|
|
74748
|
+
* A load is over -- landed, failed or cut -- and the editor goes
|
|
74749
|
+
* where the operator is: the position the host sent while it was
|
|
74750
|
+
* in flight (ac_show() kept it), or the one it had. Without a
|
|
74751
|
+
* model there is no screen to go to: the position is kept for the
|
|
74752
|
+
* next load, and the editor says why it is empty.
|
|
74753
|
+
***************************************************************/
|
|
74754
|
+
function end_load(gobj) {
|
|
74755
|
+
let priv = gobj.priv;
|
|
74756
|
+
let seg = priv.pending_seg;
|
|
74757
|
+
priv.pending_seg = null;
|
|
74758
|
+
if (priv.model) {
|
|
74759
|
+
if (seg !== null) return apply_seg(gobj, seg);
|
|
74760
|
+
return go(gobj, priv.treedb_id, priv.topic_name, priv.diagram, false);
|
|
74761
|
+
}
|
|
74762
|
+
if (seg !== null) adopt_seg(gobj, seg);
|
|
74763
|
+
gobj_change_state(gobj, "ST_IDLE");
|
|
74764
|
+
render(gobj);
|
|
74765
|
+
return 0;
|
|
74766
|
+
}
|
|
74767
|
+
/***************************************************************
|
|
74656
74768
|
* The queue is done, or gave up. Either way the model is what
|
|
74657
74769
|
* the answers made it, so the screen is redrawn from it and the
|
|
74658
74770
|
* host is told once — the tab above owns the pending flag and
|
|
@@ -74660,29 +74772,40 @@ function transport_dropped(gobj) {
|
|
|
74660
74772
|
***************************************************************/
|
|
74661
74773
|
function end_writes(gobj, error) {
|
|
74662
74774
|
let priv = gobj.priv;
|
|
74775
|
+
let seg = priv.pending_seg;
|
|
74663
74776
|
priv.save_queue = [];
|
|
74777
|
+
priv.pending_seg = null;
|
|
74664
74778
|
gobj_change_state(gobj, priv.save_return || "ST_TREEDBS");
|
|
74665
74779
|
priv.save_return = "";
|
|
74666
74780
|
show_notice(gobj, "");
|
|
74667
74781
|
set_busy(gobj, false);
|
|
74668
|
-
if (priv.pending_seg !== null) {
|
|
74669
|
-
let seg = priv.pending_seg;
|
|
74670
|
-
priv.pending_seg = null;
|
|
74671
|
-
if (!error) {
|
|
74672
|
-
apply_seg(gobj, seg);
|
|
74673
|
-
if (priv.save_wrote) gobj_publish_event(gobj, "EV_RECORD_WRITTEN", { treedb_name: gobj_read_str_attr(gobj, "treedb_name") });
|
|
74674
|
-
priv.save_wrote = false;
|
|
74675
|
-
return 0;
|
|
74676
|
-
}
|
|
74677
|
-
}
|
|
74678
74782
|
if (error) yui_shell_show_error(yui_shell_of(gobj), error, { t });
|
|
74679
74783
|
if (priv.save_wrote) gobj_publish_event(gobj, "EV_RECORD_WRITTEN", { treedb_name: gobj_read_str_attr(gobj, "treedb_name") });
|
|
74680
74784
|
priv.save_wrote = false;
|
|
74681
|
-
|
|
74682
|
-
|
|
74785
|
+
if (seg !== null) apply_seg(gobj, seg);
|
|
74786
|
+
else {
|
|
74787
|
+
render(gobj);
|
|
74788
|
+
publish_check(gobj);
|
|
74789
|
+
}
|
|
74790
|
+
reload_if_owed(gobj);
|
|
74683
74791
|
return error ? -1 : 0;
|
|
74684
74792
|
}
|
|
74685
74793
|
/***************************************************************
|
|
74794
|
+
* A write given up earlier turned out DONE while another one was
|
|
74795
|
+
* in flight (see ac_mt_command_answer()): the model is read again
|
|
74796
|
+
* now that nothing is.
|
|
74797
|
+
***************************************************************/
|
|
74798
|
+
function reload_if_owed(gobj) {
|
|
74799
|
+
let priv = gobj.priv;
|
|
74800
|
+
if (!priv.reload_after_write) return 0;
|
|
74801
|
+
priv.reload_after_write = false;
|
|
74802
|
+
if (!transport_in_session(gobj)) {
|
|
74803
|
+
priv.reload_on_open = true;
|
|
74804
|
+
return 0;
|
|
74805
|
+
}
|
|
74806
|
+
return request_model(gobj, true);
|
|
74807
|
+
}
|
|
74808
|
+
/***************************************************************
|
|
74686
74809
|
* The record a column form produces.
|
|
74687
74810
|
*
|
|
74688
74811
|
* Everything the store needs and the operator did not type is
|
|
@@ -74746,11 +74869,12 @@ function topic_record(gobj, treedb, topic, values) {
|
|
|
74746
74869
|
***************************************************************/
|
|
74747
74870
|
function go(gobj, treedb_id, topic_name, diagram, mirror) {
|
|
74748
74871
|
let priv = gobj.priv;
|
|
74872
|
+
priv.pending_seg = null;
|
|
74749
74873
|
priv.treedb_id = treedb_id || "";
|
|
74750
74874
|
priv.topic_name = topic_name || "";
|
|
74751
74875
|
priv.diagram = !!diagram;
|
|
74752
74876
|
let state = "ST_TREEDBS";
|
|
74753
|
-
if (!priv.model) state = "
|
|
74877
|
+
if (!priv.model) state = "ST_IDLE";
|
|
74754
74878
|
else if (priv.model.treedbs.length === 0) state = "ST_EMPTY";
|
|
74755
74879
|
else if (priv.treedb_id && priv.diagram) state = "ST_DIAGRAM";
|
|
74756
74880
|
else if (priv.treedb_id && priv.topic_name) state = "ST_COLUMNS";
|
|
@@ -74777,12 +74901,39 @@ function position_seg(gobj) {
|
|
|
74777
74901
|
* empty screen: a shared link outlives the schema it points at.
|
|
74778
74902
|
***************************************************************/
|
|
74779
74903
|
function apply_seg(gobj, seg) {
|
|
74904
|
+
let pos = seg_position(seg);
|
|
74905
|
+
return go(gobj, pos.treedb_id, pos.topic_name, pos.diagram, false);
|
|
74906
|
+
}
|
|
74907
|
+
function seg_position(seg) {
|
|
74780
74908
|
let parts = String(seg || "").split("/").filter((x) => x.length > 0);
|
|
74781
74909
|
let treedb_id = parts[0] || "";
|
|
74782
74910
|
let tail = parts[1] || "";
|
|
74783
|
-
if (!treedb_id) return
|
|
74784
|
-
|
|
74785
|
-
|
|
74911
|
+
if (!treedb_id) return {
|
|
74912
|
+
treedb_id: "",
|
|
74913
|
+
topic_name: "",
|
|
74914
|
+
diagram: false
|
|
74915
|
+
};
|
|
74916
|
+
if (tail === DIAGRAM_SEG) return {
|
|
74917
|
+
treedb_id,
|
|
74918
|
+
topic_name: "",
|
|
74919
|
+
diagram: true
|
|
74920
|
+
};
|
|
74921
|
+
return {
|
|
74922
|
+
treedb_id,
|
|
74923
|
+
topic_name: tail,
|
|
74924
|
+
diagram: false
|
|
74925
|
+
};
|
|
74926
|
+
}
|
|
74927
|
+
/***************************************************************
|
|
74928
|
+
* Where the operator is, taken WITHOUT a screen: there is no model
|
|
74929
|
+
* to draw it on yet, and the load that brings one goes there.
|
|
74930
|
+
***************************************************************/
|
|
74931
|
+
function adopt_seg(gobj, seg) {
|
|
74932
|
+
let priv = gobj.priv;
|
|
74933
|
+
let pos = seg_position(seg);
|
|
74934
|
+
priv.treedb_id = pos.treedb_id;
|
|
74935
|
+
priv.topic_name = pos.topic_name;
|
|
74936
|
+
priv.diagram = pos.diagram;
|
|
74786
74937
|
}
|
|
74787
74938
|
/***************************
|
|
74788
74939
|
* Actions
|
|
@@ -74810,6 +74961,7 @@ function ac_mt_command_answer(gobj, event, kw, src) {
|
|
|
74810
74961
|
if (command === "nodes") {
|
|
74811
74962
|
let topic_name = kw_get_str(gobj, md, "topic_name", "", 0);
|
|
74812
74963
|
if (!is_current_load(gobj, md)) {
|
|
74964
|
+
if (result < 0 && kw_get_int(gobj, md, "round", 0, 0) === priv.dropped_round) return 0;
|
|
74813
74965
|
log_warning(`${gobj_short_name(gobj)}: 'nodes' of '${topic_name}' answered for a load that is over: ignored`);
|
|
74814
74966
|
return 0;
|
|
74815
74967
|
}
|
|
@@ -74819,20 +74971,22 @@ function ac_mt_command_answer(gobj, event, kw, src) {
|
|
|
74819
74971
|
if (priv.pending > 0) return 0;
|
|
74820
74972
|
if (priv.load_error) {
|
|
74821
74973
|
priv.model = null;
|
|
74822
|
-
|
|
74823
|
-
render(gobj);
|
|
74824
|
-
return 0;
|
|
74974
|
+
return end_load(gobj);
|
|
74825
74975
|
}
|
|
74826
74976
|
priv.order_undo = {};
|
|
74827
74977
|
build_model(gobj);
|
|
74828
74978
|
start_measuring(gobj);
|
|
74829
|
-
return
|
|
74979
|
+
return end_load(gobj);
|
|
74830
74980
|
}
|
|
74831
74981
|
if (command === "update-node" || command === "delete-node") {
|
|
74832
74982
|
let topic_name = kw_get_str(gobj, md, "topic_name", "", 0);
|
|
74833
74983
|
if (!is_current_write(gobj, md)) {
|
|
74834
|
-
|
|
74835
|
-
|
|
74984
|
+
if (result < 0 && kw_get_int(gobj, md, "write", 0, 0) === priv.dropped_write) return 0;
|
|
74985
|
+
if (result < 0) {
|
|
74986
|
+
log_warning(`${gobj_short_name(gobj)}: '${command}' of '${topic_name}' answered for a write that is over (result ${result}): ignored`);
|
|
74987
|
+
return 0;
|
|
74988
|
+
}
|
|
74989
|
+
return late_write_done(gobj, command, topic_name, data);
|
|
74836
74990
|
}
|
|
74837
74991
|
if (result < 0) return end_writes(gobj, comment || t("the treedb refused the write"));
|
|
74838
74992
|
priv.save_wrote = true;
|
|
@@ -74842,8 +74996,15 @@ function ac_mt_command_answer(gobj, event, kw, src) {
|
|
|
74842
74996
|
if (record) patch_record(gobj, topic_name, record);
|
|
74843
74997
|
else {
|
|
74844
74998
|
log_warning(`${gobj_short_name(gobj)}: '${command}' answered no record`);
|
|
74999
|
+
if (topic_name === T_TOPICS || topic_name === T_COLS) {
|
|
75000
|
+
let write = priv.save_queue[0];
|
|
75001
|
+
mark_written(gobj, topic_name, write ? write.record : null);
|
|
75002
|
+
}
|
|
74845
75003
|
priv.save_queue = [];
|
|
74846
|
-
|
|
75004
|
+
priv.save_return = "";
|
|
75005
|
+
priv.save_wrote = false;
|
|
75006
|
+
gobj_publish_event(gobj, "EV_RECORD_WRITTEN", { treedb_name: gobj_read_str_attr(gobj, "treedb_name") });
|
|
75007
|
+
request_model(gobj, true);
|
|
74847
75008
|
return 0;
|
|
74848
75009
|
}
|
|
74849
75010
|
}
|
|
@@ -74858,6 +75019,62 @@ function ac_mt_command_answer(gobj, event, kw, src) {
|
|
|
74858
75019
|
return 0;
|
|
74859
75020
|
}
|
|
74860
75021
|
/***************************************************************
|
|
75022
|
+
* A write this view gave up on (a drop, a deadline) answers that
|
|
75023
|
+
* it was DONE: the store holds a change the model does not show.
|
|
75024
|
+
* The model is read again -- now, or when what is in flight ends,
|
|
75025
|
+
* or on the reconnect. A load in flight needs nothing: it was
|
|
75026
|
+
* asked after the write, on the same transport, and reads it.
|
|
75027
|
+
*
|
|
75028
|
+
* Only when the answer reaches this view. A routing adapter that
|
|
75029
|
+
* settled the write on its own deadline (gui_agent's
|
|
75030
|
+
* C_AGENT_TREEDB_LINK) does not deliver its late answer again --
|
|
75031
|
+
* the view was answered once, as failed -- and echoes the node
|
|
75032
|
+
* event instead, which this view does not hear: there, Refresh is
|
|
75033
|
+
* what reads the store.
|
|
75034
|
+
*
|
|
75035
|
+
* It is a WRITE like any other for the host (EV_RECORD_WRITTEN):
|
|
75036
|
+
* the host was told nothing when it was given up, and it owns the
|
|
75037
|
+
* Save that publishes it. The reload keeps what this session wrote
|
|
75038
|
+
* (request_model()), and the record it answered marks its topic.
|
|
75039
|
+
***************************************************************/
|
|
75040
|
+
function late_write_done(gobj, command, topic_name, data) {
|
|
75041
|
+
let priv = gobj.priv;
|
|
75042
|
+
let state = gobj_current_state(gobj);
|
|
75043
|
+
log_warning(`${gobj_short_name(gobj)}: '${command}' of '${topic_name}' was given up, and it was DONE: the model is read again`);
|
|
75044
|
+
mark_written_record(gobj, topic_name, is_object(data) ? data : Array.isArray(data) && is_object(data[0]) ? data[0] : null);
|
|
75045
|
+
gobj_publish_event(gobj, "EV_RECORD_WRITTEN", { treedb_name: gobj_read_str_attr(gobj, "treedb_name") });
|
|
75046
|
+
if (state === "ST_LOADING") return 0;
|
|
75047
|
+
if (state === "ST_SAVING") {
|
|
75048
|
+
priv.reload_after_write = true;
|
|
75049
|
+
return 0;
|
|
75050
|
+
}
|
|
75051
|
+
if (!transport_in_session(gobj)) {
|
|
75052
|
+
priv.reload_on_open = true;
|
|
75053
|
+
return 0;
|
|
75054
|
+
}
|
|
75055
|
+
return request_model(gobj, true);
|
|
75056
|
+
}
|
|
75057
|
+
/***************************************************************
|
|
75058
|
+
* The topic a record written belongs to, read off the RECORD and
|
|
75059
|
+
* not off the screen: a write answered late may be about a topic
|
|
75060
|
+
* the operator has left. A column names its topic in its fkey
|
|
75061
|
+
* (`topics^<topic id>^cols`); a topic is its own id.
|
|
75062
|
+
***************************************************************/
|
|
75063
|
+
function mark_written_record(gobj, topic_name, record) {
|
|
75064
|
+
let priv = gobj.priv;
|
|
75065
|
+
if (!record) return;
|
|
75066
|
+
if (topic_name === T_TOPICS && typeof record.id === "string" && record.id) {
|
|
75067
|
+
priv.written[record.id] = true;
|
|
75068
|
+
return;
|
|
75069
|
+
}
|
|
75070
|
+
if (topic_name !== T_COLS) return;
|
|
75071
|
+
let refs = Array.isArray(record.topics) ? record.topics : typeof record.topics === "string" ? [record.topics] : [];
|
|
75072
|
+
for (let ref of refs) {
|
|
75073
|
+
let parts = String(ref).split("^");
|
|
75074
|
+
if (parts.length >= 2 && parts[1]) priv.written[parts[1]] = true;
|
|
75075
|
+
}
|
|
75076
|
+
}
|
|
75077
|
+
/***************************************************************
|
|
74861
75078
|
* Remember which topic this write belongs to, which is what
|
|
74862
75079
|
* "its version still has to move" is asked about.
|
|
74863
75080
|
***************************************************************/
|
|
@@ -74877,7 +75094,8 @@ function mark_written(gobj, topic_name, record) {
|
|
|
74877
75094
|
function ac_show(gobj, event, kw, src) {
|
|
74878
75095
|
let priv = gobj.priv;
|
|
74879
75096
|
let seg = kw && kw.subpath || "";
|
|
74880
|
-
|
|
75097
|
+
let state = gobj_current_state(gobj);
|
|
75098
|
+
if (state === "ST_SAVING" || state === "ST_LOADING") {
|
|
74881
75099
|
priv.pending_seg = seg;
|
|
74882
75100
|
return 0;
|
|
74883
75101
|
}
|
|
@@ -74913,7 +75131,7 @@ function ac_transport_state(gobj, event, kw, src) {
|
|
|
74913
75131
|
}
|
|
74914
75132
|
if ((!priv.model || priv.reload_on_open) && state !== "ST_LOADING" && state !== "ST_SAVING") {
|
|
74915
75133
|
priv.reload_on_open = false;
|
|
74916
|
-
return request_model(gobj);
|
|
75134
|
+
return request_model(gobj, true);
|
|
74917
75135
|
}
|
|
74918
75136
|
render_toolbar(gobj);
|
|
74919
75137
|
return 0;
|
|
@@ -74928,7 +75146,25 @@ function ac_language_changed$1(gobj, event, kw, src) {
|
|
|
74928
75146
|
return 0;
|
|
74929
75147
|
}
|
|
74930
75148
|
function ac_refresh$1(gobj, event, kw, src) {
|
|
74931
|
-
return request_model(gobj);
|
|
75149
|
+
return request_model(gobj, false);
|
|
75150
|
+
}
|
|
75151
|
+
/***************************************************************
|
|
75152
|
+
* What a DIALOG sends while the model is being read again: the
|
|
75153
|
+
* Save of a form left open when the reload started, a
|
|
75154
|
+
* confirmation, an import. The loading screen took the rest of
|
|
75155
|
+
* the view away (request_model()); a dialog is the one thing the
|
|
75156
|
+
* operator can still reach, and what it asks is computed against
|
|
75157
|
+
* a model the answers in the air are about to replace.
|
|
75158
|
+
*
|
|
75159
|
+
* Refused, and SAID -- and the dialog stays open with what was
|
|
75160
|
+
* typed, so the same Save works once the load is in. Undeclared,
|
|
75161
|
+
* it answered "Event NOT DEFINED in state ST_LOADING" and the
|
|
75162
|
+
* edit was gone with no word (third independent review).
|
|
75163
|
+
***************************************************************/
|
|
75164
|
+
function ac_wait_for_the_load(gobj, event, kw, src) {
|
|
75165
|
+
log_warning(`${gobj_short_name(gobj)}: ${event} refused, the schemas are loading`);
|
|
75166
|
+
yui_shell_show_error(yui_shell_of(gobj), "the schemas are loading: try again when they are in", { t });
|
|
75167
|
+
return -1;
|
|
74932
75168
|
}
|
|
74933
75169
|
/***************************************************************
|
|
74934
75170
|
* {drafts: {treedb_name: [topic names]}} -- the host says which
|
|
@@ -75476,7 +75712,38 @@ function create_gclass$1(gclass_name) {
|
|
|
75476
75712
|
];
|
|
75477
75713
|
const states = [
|
|
75478
75714
|
["ST_IDLE", COMMON.slice()],
|
|
75479
|
-
["ST_LOADING", COMMON.
|
|
75715
|
+
["ST_LOADING", COMMON.concat([
|
|
75716
|
+
[
|
|
75717
|
+
"EV_SAVE_COLUMN",
|
|
75718
|
+
ac_wait_for_the_load,
|
|
75719
|
+
null
|
|
75720
|
+
],
|
|
75721
|
+
[
|
|
75722
|
+
"EV_SAVE_TOPIC",
|
|
75723
|
+
ac_wait_for_the_load,
|
|
75724
|
+
null
|
|
75725
|
+
],
|
|
75726
|
+
[
|
|
75727
|
+
"EV_DELETE_ORPHAN",
|
|
75728
|
+
ac_wait_for_the_load,
|
|
75729
|
+
null
|
|
75730
|
+
],
|
|
75731
|
+
[
|
|
75732
|
+
"EV_PREVIEW_IMPORT",
|
|
75733
|
+
ac_wait_for_the_load,
|
|
75734
|
+
null
|
|
75735
|
+
],
|
|
75736
|
+
[
|
|
75737
|
+
"EV_APPLY_IMPORT",
|
|
75738
|
+
ac_wait_for_the_load,
|
|
75739
|
+
null
|
|
75740
|
+
],
|
|
75741
|
+
[
|
|
75742
|
+
"EV_CONFIRMED",
|
|
75743
|
+
ac_wait_for_the_load,
|
|
75744
|
+
null
|
|
75745
|
+
]
|
|
75746
|
+
])],
|
|
75480
75747
|
["ST_EMPTY", COMMON.slice()],
|
|
75481
75748
|
["ST_TREEDBS", COMMON.concat([
|
|
75482
75749
|
[
|