@yuneta/gobj-ui 7.25.7 → 7.25.9
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 +60 -1
- package/dist/gobj-ui.cjs.js +355 -101
- package/dist/gobj-ui.es.js +355 -101
- package/package.json +1 -1
- package/src/c_yui_schema_editor.js +384 -77
- package/src/schema_editor_reload.wiring.test.js +787 -0
- package/src/shell_modals.js +58 -33
- package/src/shell_toast_once.test.js +106 -3
package/dist/gobj-ui.es.js
CHANGED
|
@@ -3022,12 +3022,20 @@ function maybe_apply_translator($node, opts) {
|
|
|
3022
3022
|
* Returns `{ close() }` so callers can dismiss programmatically.
|
|
3023
3023
|
*
|
|
3024
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
|
|
3026
|
-
*
|
|
3027
|
-
*
|
|
3028
|
-
*
|
|
3029
|
-
*
|
|
3030
|
-
*
|
|
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.
|
|
3031
3039
|
***************************************************************/
|
|
3032
3040
|
function show_notification(shell, kind, message, opts) {
|
|
3033
3041
|
let $layer = notification_layer(shell);
|
|
@@ -3035,12 +3043,10 @@ function show_notification(shell, kind, message, opts) {
|
|
|
3035
3043
|
log_warning("yui_shell_show_*: shell has no notification layer");
|
|
3036
3044
|
return { close: () => {} };
|
|
3037
3045
|
}
|
|
3046
|
+
let timeout = opts && opts.timeout != null ? opts.timeout : 5e3;
|
|
3038
3047
|
let key = typeof message === "string" ? `${kind}\u0000${message}` : null;
|
|
3039
3048
|
if (key !== null) {
|
|
3040
|
-
for (let $shown of Array.from($layer.children)) if ($shown.__toast_key__ === key && $shown.__toast__)
|
|
3041
|
-
$shown.__toast__.restart();
|
|
3042
|
-
return $shown.__toast__.handle;
|
|
3043
|
-
}
|
|
3049
|
+
for (let $shown of Array.from($layer.children)) if ($shown.__toast_key__ === key && $shown.__toast__) return $shown.__toast__.hold(timeout);
|
|
3044
3050
|
}
|
|
3045
3051
|
let p_attrs = typeof message === "string" ? { i18n: message } : {};
|
|
3046
3052
|
let $note = createElement2([
|
|
@@ -3051,6 +3057,8 @@ function show_notification(shell, kind, message, opts) {
|
|
|
3051
3057
|
},
|
|
3052
3058
|
[["button", {
|
|
3053
3059
|
class: "TOAST_CLOSE delete",
|
|
3060
|
+
title: t("close"),
|
|
3061
|
+
"data-i18n-title": "close",
|
|
3054
3062
|
"aria-label": t("close"),
|
|
3055
3063
|
"data-i18n-aria-label": "close"
|
|
3056
3064
|
}], [
|
|
@@ -3064,35 +3072,38 @@ function show_notification(shell, kind, message, opts) {
|
|
|
3064
3072
|
]);
|
|
3065
3073
|
$layer.appendChild($note);
|
|
3066
3074
|
maybe_apply_translator($note, opts);
|
|
3067
|
-
let
|
|
3075
|
+
let holders = /* @__PURE__ */ new Set();
|
|
3068
3076
|
let closed = false;
|
|
3069
|
-
let
|
|
3077
|
+
let close_toast = function() {
|
|
3070
3078
|
if (closed) return;
|
|
3071
3079
|
closed = true;
|
|
3072
|
-
if (timeout_id) {
|
|
3073
|
-
clearTimeout(timeout_id);
|
|
3074
|
-
timeout_id = null;
|
|
3080
|
+
for (let holder of holders) if (holder.timeout_id) {
|
|
3081
|
+
clearTimeout(holder.timeout_id);
|
|
3082
|
+
holder.timeout_id = null;
|
|
3075
3083
|
}
|
|
3084
|
+
holders.clear();
|
|
3076
3085
|
if ($note.parentNode) $note.parentNode.removeChild($note);
|
|
3077
3086
|
};
|
|
3078
|
-
let
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
+
let hold = function(ms) {
|
|
3088
|
+
let holder = { timeout_id: null };
|
|
3089
|
+
let release = function() {
|
|
3090
|
+
if (!holders.has(holder)) return;
|
|
3091
|
+
if (holder.timeout_id) {
|
|
3092
|
+
clearTimeout(holder.timeout_id);
|
|
3093
|
+
holder.timeout_id = null;
|
|
3094
|
+
}
|
|
3095
|
+
holders.delete(holder);
|
|
3096
|
+
if (holders.size === 0) close_toast();
|
|
3097
|
+
};
|
|
3098
|
+
holders.add(holder);
|
|
3099
|
+
if (ms > 0) holder.timeout_id = setTimeout(release, ms);
|
|
3100
|
+
return { close: release };
|
|
3087
3101
|
};
|
|
3088
|
-
|
|
3089
|
-
|
|
3102
|
+
let $del = $note.querySelector(".delete");
|
|
3103
|
+
if ($del) $del.addEventListener("click", close_toast);
|
|
3090
3104
|
$note.__toast_key__ = key;
|
|
3091
|
-
$note.__toast__ = {
|
|
3092
|
-
|
|
3093
|
-
restart
|
|
3094
|
-
};
|
|
3095
|
-
return handle;
|
|
3105
|
+
$note.__toast__ = { hold };
|
|
3106
|
+
return hold(timeout);
|
|
3096
3107
|
}
|
|
3097
3108
|
function yui_shell_show_info(shell, message, opts) {
|
|
3098
3109
|
return show_notification(shell, "info", message, opts);
|
|
@@ -3171,6 +3182,8 @@ function yui_shell_show_modal(shell, content, opts) {
|
|
|
3171
3182
|
{
|
|
3172
3183
|
class: "MODAL_CLOSE yui-dialog-x",
|
|
3173
3184
|
type: "button",
|
|
3185
|
+
title: t("close"),
|
|
3186
|
+
"data-i18n-title": "close",
|
|
3174
3187
|
"aria-label": t("close"),
|
|
3175
3188
|
"data-i18n-aria-label": "close"
|
|
3176
3189
|
},
|
|
@@ -3192,6 +3205,8 @@ function yui_shell_show_modal(shell, content, opts) {
|
|
|
3192
3205
|
]];
|
|
3193
3206
|
if (with_close) modal_children.push(["button", {
|
|
3194
3207
|
class: "MODAL_CLOSE modal-close is-large",
|
|
3208
|
+
title: t("close"),
|
|
3209
|
+
"data-i18n-title": "close",
|
|
3195
3210
|
"aria-label": t("close"),
|
|
3196
3211
|
"data-i18n-aria-label": "close"
|
|
3197
3212
|
}]);
|
|
@@ -3346,6 +3361,8 @@ function build_dialog(shell, message, buttons, opts) {
|
|
|
3346
3361
|
]);
|
|
3347
3362
|
$card_children.push(["button", {
|
|
3348
3363
|
class: "CONFIRM_CLOSE delete yui-confirm-x",
|
|
3364
|
+
title: t("close"),
|
|
3365
|
+
"data-i18n-title": "close",
|
|
3349
3366
|
"aria-label": t("close"),
|
|
3350
3367
|
"data-i18n-aria-label": "close"
|
|
3351
3368
|
}], [
|
|
@@ -72159,10 +72176,13 @@ var attrs_table$1 = [
|
|
|
72159
72176
|
];
|
|
72160
72177
|
var PRIVATE_DATA$1 = {
|
|
72161
72178
|
records: null,
|
|
72179
|
+
records_before: null,
|
|
72162
72180
|
model: null,
|
|
72181
|
+
model_gen: 0,
|
|
72163
72182
|
pending: 0,
|
|
72164
72183
|
load_error: "",
|
|
72165
72184
|
load_round: 0,
|
|
72185
|
+
load_keeps_written: false,
|
|
72166
72186
|
treedb_id: "",
|
|
72167
72187
|
topic_name: "",
|
|
72168
72188
|
diagram: false,
|
|
@@ -72173,6 +72193,7 @@ var PRIVATE_DATA$1 = {
|
|
|
72173
72193
|
order_undo: null,
|
|
72174
72194
|
diagram_gobj: null,
|
|
72175
72195
|
dialog: null,
|
|
72196
|
+
dialog_gen: 0,
|
|
72176
72197
|
save_queue: null,
|
|
72177
72198
|
save_return: "",
|
|
72178
72199
|
save_wrote: false,
|
|
@@ -72333,6 +72354,29 @@ function refuse_if_readonly(gobj, event) {
|
|
|
72333
72354
|
yui_shell_show_error(yui_shell_of(gobj), "the treedb is read-only", { t });
|
|
72334
72355
|
return true;
|
|
72335
72356
|
}
|
|
72357
|
+
/***************************************************************
|
|
72358
|
+
* A decision taken on a model a load has since REPLACED: the Save
|
|
72359
|
+
* of a form, a confirmation, an import. Each carries the
|
|
72360
|
+
* `model_gen` it was built on (a number, not the model: a kw is
|
|
72361
|
+
* dumped by the machine trace).
|
|
72362
|
+
*
|
|
72363
|
+
* Refused, and said. A column form sends every field it shows, so
|
|
72364
|
+
* its Save after a reload wrote the OLD record over the newer one
|
|
72365
|
+
* (fourth independent review, probe A3); a form whose column the
|
|
72366
|
+
* reload removed answered "names no column" for ever. end_load()
|
|
72367
|
+
* closes such a dialog when the load lands; this is the same rule
|
|
72368
|
+
* for whatever arrives anyway -- a stale screen, a confirmation
|
|
72369
|
+
* that was up, a keyboard.
|
|
72370
|
+
*
|
|
72371
|
+
* A kw with no stamp is not a dialog's and is not asked.
|
|
72372
|
+
***************************************************************/
|
|
72373
|
+
function built_on_a_replaced_model(gobj, event, kw) {
|
|
72374
|
+
let priv = gobj.priv;
|
|
72375
|
+
if (!kw || kw.model_gen === void 0 || kw.model_gen === priv.model_gen) return false;
|
|
72376
|
+
log_warning(`${gobj_short_name(gobj)}: ${event} refused, it was decided on the schemas a reload replaced`);
|
|
72377
|
+
yui_shell_show_error(yui_shell_of(gobj), "the schemas were read again: open the dialog again", { t });
|
|
72378
|
+
return true;
|
|
72379
|
+
}
|
|
72336
72380
|
/***************************
|
|
72337
72381
|
* The model
|
|
72338
72382
|
***************************/
|
|
@@ -72341,10 +72385,36 @@ function refuse_if_readonly(gobj, event) {
|
|
|
72341
72385
|
* schema set of the yuno is here: everything below — the
|
|
72342
72386
|
* drawing, the validation, the export — is computed from these
|
|
72343
72387
|
* and asks the backend nothing more.
|
|
72388
|
+
*
|
|
72389
|
+
* THE LOADING SCREEN IS DRAWN, not assumed. ST_LOADING declares
|
|
72390
|
+
* none of the actions of a screen, so the screen it replaces goes
|
|
72391
|
+
* and the toolbar stops answering: left up, a click on a card, on
|
|
72392
|
+
* Back or on the drawing answered "Event NOT DEFINED in state
|
|
72393
|
+
* ST_LOADING" and was lost (third independent review). A dialog
|
|
72394
|
+
* left open is answered in ST_LOADING instead (ac_wait_for_the_load),
|
|
72395
|
+
* and a load that LANDS closes it (end_load()): it was built on the
|
|
72396
|
+
* model the load replaced, and its Save sends every field it shows.
|
|
72397
|
+
* An import plan is forgotten here for the same reason: it is a list
|
|
72398
|
+
* of writes computed against the model going away.
|
|
72399
|
+
*
|
|
72400
|
+
* `keep_written`: a load the host did not ask for -- the reconnect,
|
|
72401
|
+
* a write that turned out done -- keeps what this session wrote.
|
|
72402
|
+
* Only the host's EV_REFRESH (a Save sends it) forgets it.
|
|
72403
|
+
*
|
|
72404
|
+
* A load that cannot leave WHOLE did not happen (out of session it
|
|
72405
|
+
* is not even asked): the records the model was built on are put
|
|
72406
|
+
* back (emptied, the next write patched them into a model with no
|
|
72407
|
+
* treedb), the round is spent so a request that did leave is
|
|
72408
|
+
* stale, and the session back asks again. A load that left and
|
|
72409
|
+
* FAILED keeps them the same way (`records_before`,
|
|
72410
|
+
* ac_mt_command_answer()).
|
|
72344
72411
|
***************************************************************/
|
|
72345
|
-
function request_model(gobj) {
|
|
72412
|
+
function request_model(gobj, keep_written) {
|
|
72346
72413
|
let priv = gobj.priv;
|
|
72347
72414
|
if (!gobj_read_pointer_attr(gobj, "gobj_remote_yuno")) return -1;
|
|
72415
|
+
forget_import_plan(gobj);
|
|
72416
|
+
let previous = priv.records;
|
|
72417
|
+
priv.records_before = previous;
|
|
72348
72418
|
priv.records = {
|
|
72349
72419
|
treedbs: [],
|
|
72350
72420
|
topics: [],
|
|
@@ -72353,22 +72423,32 @@ function request_model(gobj) {
|
|
|
72353
72423
|
priv.load_error = "";
|
|
72354
72424
|
priv.pending = 0;
|
|
72355
72425
|
priv.load_round++;
|
|
72426
|
+
priv.load_keeps_written = !!keep_written;
|
|
72356
72427
|
gobj_change_state(gobj, "ST_LOADING");
|
|
72357
|
-
|
|
72358
|
-
|
|
72359
|
-
|
|
72360
|
-
|
|
72361
|
-
|
|
72362
|
-
|
|
72363
|
-
|
|
72364
|
-
|
|
72365
|
-
|
|
72366
|
-
|
|
72428
|
+
set_busy(gobj, false);
|
|
72429
|
+
render(gobj);
|
|
72430
|
+
let left = 0;
|
|
72431
|
+
if (transport_in_session(gobj)) {
|
|
72432
|
+
for (let topic_name of [
|
|
72433
|
+
T_TREEDBS,
|
|
72434
|
+
T_TOPICS,
|
|
72435
|
+
T_COLS
|
|
72436
|
+
]) if (remote_command(gobj, "nodes", {
|
|
72437
|
+
topic_name,
|
|
72438
|
+
options: { list_dict: true }
|
|
72439
|
+
}, { round: priv.load_round }) === 0) left++;
|
|
72440
|
+
}
|
|
72441
|
+
if (left < 3) {
|
|
72442
|
+
priv.records = previous;
|
|
72443
|
+
priv.records_before = null;
|
|
72444
|
+
priv.load_round++;
|
|
72445
|
+
priv.reload_on_open = true;
|
|
72367
72446
|
priv.load_error = t("cannot reach the treedb");
|
|
72368
|
-
|
|
72369
|
-
|
|
72447
|
+
if (priv.model) yui_shell_show_error(yui_shell_of(gobj), "cannot reach the treedb", { t });
|
|
72448
|
+
end_load(gobj);
|
|
72370
72449
|
return -1;
|
|
72371
72450
|
}
|
|
72451
|
+
priv.pending = left;
|
|
72372
72452
|
return 0;
|
|
72373
72453
|
}
|
|
72374
72454
|
/***************************************************************
|
|
@@ -72431,15 +72511,19 @@ function build_model(gobj) {
|
|
|
72431
72511
|
* From here on, "did this session change it?" has an answer.
|
|
72432
72512
|
*
|
|
72433
72513
|
* Called ONLY when records arrive from the store: that is what a
|
|
72434
|
-
* measurement of "changed since" can be measured against.
|
|
72435
|
-
*
|
|
72436
|
-
*
|
|
72437
|
-
*
|
|
72514
|
+
* measurement of "changed since" can be measured against.
|
|
72515
|
+
*
|
|
72516
|
+
* `written` is forgotten only by a load the HOST asked for (its
|
|
72517
|
+
* EV_REFRESH, which a Save sends). Since M36 a write moves no
|
|
72518
|
+
* version, so `written` is the draft chip of what this session
|
|
72519
|
+
* wrote and no longer half of a version measurement: a reload of
|
|
72520
|
+
* the reconnect, or of a write that turned out done, forgot the
|
|
72521
|
+
* chips of writes the host had not been told about.
|
|
72438
72522
|
***************************************************************/
|
|
72439
72523
|
function start_measuring(gobj) {
|
|
72440
72524
|
let priv = gobj.priv;
|
|
72441
72525
|
priv.baseline = {};
|
|
72442
|
-
priv.written = {};
|
|
72526
|
+
if (!priv.load_keeps_written) priv.written = {};
|
|
72443
72527
|
priv.host_ids = {};
|
|
72444
72528
|
if (!priv.model) return;
|
|
72445
72529
|
for (let treedb of priv.model.treedbs) for (let topic of treedb.topics) priv.baseline[topic.id] = topic.topic_version;
|
|
@@ -72452,9 +72536,10 @@ function start_measuring(gobj) {
|
|
|
72452
72536
|
* until the host saves it (C_TREEDB's save-schema, which raises the
|
|
72453
72537
|
* versions of what changed). Two sources say a topic is one, and
|
|
72454
72538
|
* they are kept APART because they are forgotten differently:
|
|
72455
|
-
* - `written`: what THIS session wrote
|
|
72456
|
-
*
|
|
72457
|
-
*
|
|
72539
|
+
* - `written`: what THIS session wrote. Only a load the HOST asked
|
|
72540
|
+
* for forgets it (its EV_REFRESH, which a Save sends); the
|
|
72541
|
+
* reconnect's reload, the one owed after a write and a late
|
|
72542
|
+
* write's keep it (start_measuring(), request_model());
|
|
72458
72543
|
* - `host_ids`: what the host said last (EV_DRAFTS), replaced WHOLE
|
|
72459
72544
|
* by the next EV_DRAFTS. Folded into `written`, a topic the host
|
|
72460
72545
|
* named once stayed a draft after the Save that published it.
|
|
@@ -72621,8 +72706,12 @@ function render_toolbar(gobj) {
|
|
|
72621
72706
|
let state = gobj_current_state(gobj);
|
|
72622
72707
|
let readonly = is_readonly(gobj);
|
|
72623
72708
|
let has_model = !!priv.model;
|
|
72709
|
+
let on_a_treedb = state !== "ST_IDLE" && state !== "ST_EMPTY";
|
|
72710
|
+
let treedb_here = on_a_treedb && !!current_treedb(gobj);
|
|
72711
|
+
let topic_here = treedb_here && !!current_topic(gobj);
|
|
72712
|
+
let in_flight = state === "ST_SAVING" || state === "ST_LOADING";
|
|
72624
72713
|
let $left = [];
|
|
72625
|
-
if (priv.treedb_id) $left.push([
|
|
72714
|
+
if (priv.treedb_id && on_a_treedb) $left.push([
|
|
72626
72715
|
"button",
|
|
72627
72716
|
{
|
|
72628
72717
|
class: "SCHEMA_BACK button",
|
|
@@ -72695,17 +72784,17 @@ function render_toolbar(gobj) {
|
|
|
72695
72784
|
$trail
|
|
72696
72785
|
]);
|
|
72697
72786
|
let $right = [];
|
|
72698
|
-
if (priv.treedb_id && has_model) {
|
|
72787
|
+
if (priv.treedb_id && has_model && treedb_here) {
|
|
72699
72788
|
$right.push(toolbar_button("SCHEMA_DIAGRAM_BTN", "yi-compass-drafting", priv.diagram ? "topics" : "diagram", "EV_TOGGLE_DIAGRAM", false));
|
|
72700
72789
|
$right.push(toolbar_button("SCHEMA_VALIDATE_BTN", "yi-square-check", "check", "EV_VALIDATE", false));
|
|
72701
72790
|
$right.push(toolbar_button("SCHEMA_EXPORT_BTN", "yi-download", "export", "EV_EXPORT", false));
|
|
72702
72791
|
if (!readonly) $right.push(toolbar_button("SCHEMA_IMPORT_BTN", "yi-upload", "import", "EV_IMPORT", false));
|
|
72703
72792
|
}
|
|
72704
|
-
if (priv.topic_name && !readonly && !priv.diagram) {
|
|
72793
|
+
if (priv.topic_name && !readonly && !priv.diagram && topic_here) {
|
|
72705
72794
|
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));
|
|
72706
72795
|
$right.push(toolbar_button("SCHEMA_ADD_COL_BTN", "yi-plus", "new column", "EV_ADD_COLUMN", false));
|
|
72707
72796
|
}
|
|
72708
|
-
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));
|
|
72797
|
+
if (priv.treedb_id && !priv.topic_name && !readonly && !priv.diagram && treedb_here) $right.push(toolbar_button("SCHEMA_ADD_TOPIC_BTN", "yi-plus", "new topic", "EV_ADD_TOPIC", false));
|
|
72709
72798
|
$right.push(toolbar_button("SCHEMA_REFRESH_BTN", "yi-arrows-rotate", "refresh", "EV_REFRESH", state === "ST_LOADING"));
|
|
72710
72799
|
let $bar = createElement2([
|
|
72711
72800
|
"div",
|
|
@@ -72731,7 +72820,7 @@ function render_toolbar(gobj) {
|
|
|
72731
72820
|
]);
|
|
72732
72821
|
for (let $button of $bar.querySelectorAll("button")) {
|
|
72733
72822
|
$button.__gobj__ = gobj;
|
|
72734
|
-
if (
|
|
72823
|
+
if (in_flight) $button.disabled = true;
|
|
72735
72824
|
}
|
|
72736
72825
|
$toolbar.appendChild($bar);
|
|
72737
72826
|
refresh_language($toolbar, t);
|
|
@@ -72749,16 +72838,19 @@ function render(gobj) {
|
|
|
72749
72838
|
render_toolbar(gobj);
|
|
72750
72839
|
if (state === "ST_LOADING") {
|
|
72751
72840
|
show_notice(gobj, "loading the schemas");
|
|
72841
|
+
destroy_diagram(gobj);
|
|
72752
72842
|
clear($body);
|
|
72753
72843
|
return;
|
|
72754
72844
|
}
|
|
72755
72845
|
if (state === "ST_IDLE") {
|
|
72756
72846
|
show_notice(gobj, priv.load_error ? "cannot load the schemas" : "not connected", priv.load_error);
|
|
72847
|
+
destroy_diagram(gobj);
|
|
72757
72848
|
clear($body);
|
|
72758
72849
|
return;
|
|
72759
72850
|
}
|
|
72760
72851
|
if (state === "ST_EMPTY") {
|
|
72761
72852
|
show_notice(gobj, "this yuno stores no schema");
|
|
72853
|
+
destroy_diagram(gobj);
|
|
72762
72854
|
clear($body);
|
|
72763
72855
|
return;
|
|
72764
72856
|
}
|
|
@@ -73447,12 +73539,16 @@ function render_diagram(gobj) {
|
|
|
73447
73539
|
let priv = gobj.priv;
|
|
73448
73540
|
let $body = $of(gobj, ".SCHEMA_BODY");
|
|
73449
73541
|
let treedb = current_treedb(gobj);
|
|
73450
|
-
if (!$body
|
|
73542
|
+
if (!$body) return;
|
|
73543
|
+
if (!treedb) {
|
|
73544
|
+
destroy_diagram(gobj);
|
|
73545
|
+
clear($body);
|
|
73451
73546
|
show_notice(gobj, "that treedb is not here any more", priv.treedb_id);
|
|
73452
73547
|
return;
|
|
73453
73548
|
}
|
|
73454
73549
|
if (!gclass_find_by_name("C_YUI_TREEDB_SCHEMA")) {
|
|
73455
73550
|
log_error(`${gobj_short_name(gobj)}: C_YUI_TREEDB_SCHEMA not registered by the app`);
|
|
73551
|
+
clear($body);
|
|
73456
73552
|
show_notice(gobj, "the schema drawing is not available");
|
|
73457
73553
|
return;
|
|
73458
73554
|
}
|
|
@@ -73518,6 +73614,7 @@ function close_dialog(gobj) {
|
|
|
73518
73614
|
function open_dialog(gobj, $content, title, logical, title_prefix) {
|
|
73519
73615
|
let priv = gobj.priv;
|
|
73520
73616
|
close_dialog(gobj);
|
|
73617
|
+
priv.dialog_gen = priv.model_gen;
|
|
73521
73618
|
priv.dialog = yui_shell_show_modal(yui_shell_of(gobj), $content, {
|
|
73522
73619
|
dialog: true,
|
|
73523
73620
|
title,
|
|
@@ -73564,11 +73661,28 @@ function name_control($spec, label) {
|
|
|
73564
73661
|
$spec[1]["aria-label"] = t(label);
|
|
73565
73662
|
$spec[1]["data-i18n-aria-label"] = label;
|
|
73566
73663
|
}
|
|
73664
|
+
if (!$spec[1].title) {
|
|
73665
|
+
$spec[1].title = t(label);
|
|
73666
|
+
$spec[1]["data-i18n-title"] = label;
|
|
73667
|
+
}
|
|
73567
73668
|
return true;
|
|
73568
73669
|
}
|
|
73569
73670
|
for (let child of $spec[2] || []) if (name_control(child, label)) return true;
|
|
73570
73671
|
return false;
|
|
73571
73672
|
}
|
|
73673
|
+
/***************************************************************
|
|
73674
|
+
* The four attributes that NAME a control -- a title and an
|
|
73675
|
+
* aria-label, each with the key a language change re-translates
|
|
73676
|
+
* it by. A visible label is not a name (CLAUDE.md).
|
|
73677
|
+
***************************************************************/
|
|
73678
|
+
function name_attrs(key, attrs) {
|
|
73679
|
+
return Object.assign({
|
|
73680
|
+
title: t(key),
|
|
73681
|
+
"data-i18n-title": key,
|
|
73682
|
+
"aria-label": t(key),
|
|
73683
|
+
"data-i18n-aria-label": key
|
|
73684
|
+
}, attrs);
|
|
73685
|
+
}
|
|
73572
73686
|
function field(logical, name, label, $control, help) {
|
|
73573
73687
|
name_control($control, label);
|
|
73574
73688
|
return [
|
|
@@ -73745,7 +73859,7 @@ function read_flags($root) {
|
|
|
73745
73859
|
* than not offering it.
|
|
73746
73860
|
***************************************************************/
|
|
73747
73861
|
function open_column_form(gobj, topic, col, prefill) {
|
|
73748
|
-
gobj.priv;
|
|
73862
|
+
let priv = gobj.priv;
|
|
73749
73863
|
let record = prefill || col && col.record || {};
|
|
73750
73864
|
let creating = !col;
|
|
73751
73865
|
let type = record.type || "string";
|
|
@@ -73755,6 +73869,7 @@ function open_column_form(gobj, topic, col, prefill) {
|
|
|
73755
73869
|
let hook_col = hook_topic ? hook[hook_topic] : "";
|
|
73756
73870
|
let treedb = current_treedb(gobj);
|
|
73757
73871
|
let sibling_topics = treedb ? treedb.topics.map((x) => x.name) : [];
|
|
73872
|
+
let model_gen = priv.model_gen;
|
|
73758
73873
|
let $form = createElement2([
|
|
73759
73874
|
"div",
|
|
73760
73875
|
{ class: "SCHEMA_COL_FORM box" },
|
|
@@ -73817,10 +73932,10 @@ function open_column_form(gobj, topic, col, prefill) {
|
|
|
73817
73932
|
},
|
|
73818
73933
|
[[
|
|
73819
73934
|
"button",
|
|
73820
|
-
{
|
|
73935
|
+
name_attrs("cancel", {
|
|
73821
73936
|
class: "SCHEMA_COL_FORM_CANCEL button",
|
|
73822
73937
|
type: "button"
|
|
73823
|
-
},
|
|
73938
|
+
}),
|
|
73824
73939
|
[[
|
|
73825
73940
|
"span",
|
|
73826
73941
|
{ i18n: "cancel" },
|
|
@@ -73828,10 +73943,10 @@ function open_column_form(gobj, topic, col, prefill) {
|
|
|
73828
73943
|
]]
|
|
73829
73944
|
], [
|
|
73830
73945
|
"button",
|
|
73831
|
-
{
|
|
73946
|
+
name_attrs("save", {
|
|
73832
73947
|
class: "SCHEMA_COL_FORM_SAVE button is-primary",
|
|
73833
73948
|
type: "button"
|
|
73834
|
-
},
|
|
73949
|
+
}),
|
|
73835
73950
|
[[
|
|
73836
73951
|
"span",
|
|
73837
73952
|
{ class: "icon" },
|
|
@@ -73871,7 +73986,8 @@ function open_column_form(gobj, topic, col, prefill) {
|
|
|
73871
73986
|
topic: topic.name,
|
|
73872
73987
|
col: creating ? "" : col.name,
|
|
73873
73988
|
creating,
|
|
73874
|
-
values
|
|
73989
|
+
values,
|
|
73990
|
+
model_gen
|
|
73875
73991
|
}, gobj);
|
|
73876
73992
|
});
|
|
73877
73993
|
open_dialog(gobj, $form, creating ? "new column" : "edit column", "SCHEMA_COL_FORM_DIALOG", creating ? topic.name : `${topic.name}.${col.name}`);
|
|
@@ -73894,6 +74010,7 @@ function open_topic_form(gobj, treedb, topic) {
|
|
|
73894
74010
|
let record = topic && topic.record || {};
|
|
73895
74011
|
let creating = !topic;
|
|
73896
74012
|
let col_names = topic ? topic.cols.map((c) => c.name) : [];
|
|
74013
|
+
let model_gen = gobj.priv.model_gen;
|
|
73897
74014
|
let $form = createElement2([
|
|
73898
74015
|
"div",
|
|
73899
74016
|
{ class: "SCHEMA_TOPIC_FORM box" },
|
|
@@ -73952,10 +74069,10 @@ function open_topic_form(gobj, treedb, topic) {
|
|
|
73952
74069
|
},
|
|
73953
74070
|
[[
|
|
73954
74071
|
"button",
|
|
73955
|
-
{
|
|
74072
|
+
name_attrs("cancel", {
|
|
73956
74073
|
class: "SCHEMA_TOPIC_FORM_CANCEL button",
|
|
73957
74074
|
type: "button"
|
|
73958
|
-
},
|
|
74075
|
+
}),
|
|
73959
74076
|
[[
|
|
73960
74077
|
"span",
|
|
73961
74078
|
{ i18n: "cancel" },
|
|
@@ -73963,10 +74080,10 @@ function open_topic_form(gobj, treedb, topic) {
|
|
|
73963
74080
|
]]
|
|
73964
74081
|
], [
|
|
73965
74082
|
"button",
|
|
73966
|
-
{
|
|
74083
|
+
name_attrs("save", {
|
|
73967
74084
|
class: "SCHEMA_TOPIC_FORM_SAVE button is-primary",
|
|
73968
74085
|
type: "button"
|
|
73969
|
-
},
|
|
74086
|
+
}),
|
|
73970
74087
|
[[
|
|
73971
74088
|
"span",
|
|
73972
74089
|
{ class: "icon" },
|
|
@@ -73989,7 +74106,8 @@ function open_topic_form(gobj, treedb, topic) {
|
|
|
73989
74106
|
gobj_send_event(gobj, "EV_SAVE_TOPIC", {
|
|
73990
74107
|
topic: creating ? "" : topic.name,
|
|
73991
74108
|
creating,
|
|
73992
|
-
values: read_form($form)
|
|
74109
|
+
values: read_form($form),
|
|
74110
|
+
model_gen
|
|
73993
74111
|
}, gobj);
|
|
73994
74112
|
});
|
|
73995
74113
|
open_dialog(gobj, $form, creating ? "new topic" : "edit topic", "SCHEMA_TOPIC_FORM_DIALOG", creating ? treedb.id : `${treedb.id}.${topic.name}`);
|
|
@@ -74269,6 +74387,7 @@ function copy_text(gobj, $text) {
|
|
|
74269
74387
|
* plan itself, so what is confirmed is what runs.
|
|
74270
74388
|
***************************************************************/
|
|
74271
74389
|
function open_import(gobj, treedb) {
|
|
74390
|
+
let model_gen = gobj.priv.model_gen;
|
|
74272
74391
|
let $content = createElement2([
|
|
74273
74392
|
"div",
|
|
74274
74393
|
{ class: "SCHEMA_IMPORT box" },
|
|
@@ -74283,12 +74402,12 @@ function open_import(gobj, treedb) {
|
|
|
74283
74402
|
],
|
|
74284
74403
|
[
|
|
74285
74404
|
"textarea",
|
|
74286
|
-
{
|
|
74405
|
+
name_attrs("schema", {
|
|
74287
74406
|
class: "SCHEMA_IMPORT_TEXT textarea is-small is-family-monospace",
|
|
74288
74407
|
rows: "12",
|
|
74289
74408
|
spellcheck: "false",
|
|
74290
74409
|
placeholder: "{\"id\": \"...\", \"schema_version\": \"1\", \"topics\": []}"
|
|
74291
|
-
},
|
|
74410
|
+
}),
|
|
74292
74411
|
""
|
|
74293
74412
|
],
|
|
74294
74413
|
[
|
|
@@ -74321,10 +74440,10 @@ function open_import(gobj, treedb) {
|
|
|
74321
74440
|
[
|
|
74322
74441
|
[
|
|
74323
74442
|
"button",
|
|
74324
|
-
{
|
|
74443
|
+
name_attrs("cancel", {
|
|
74325
74444
|
class: "SCHEMA_IMPORT_CANCEL button",
|
|
74326
74445
|
type: "button"
|
|
74327
|
-
},
|
|
74446
|
+
}),
|
|
74328
74447
|
[[
|
|
74329
74448
|
"span",
|
|
74330
74449
|
{ i18n: "cancel" },
|
|
@@ -74333,10 +74452,10 @@ function open_import(gobj, treedb) {
|
|
|
74333
74452
|
],
|
|
74334
74453
|
[
|
|
74335
74454
|
"button",
|
|
74336
|
-
{
|
|
74455
|
+
name_attrs("preview", {
|
|
74337
74456
|
class: "SCHEMA_IMPORT_PREVIEW button",
|
|
74338
74457
|
type: "button"
|
|
74339
|
-
},
|
|
74458
|
+
}),
|
|
74340
74459
|
[[
|
|
74341
74460
|
"span",
|
|
74342
74461
|
{ class: "icon" },
|
|
@@ -74349,11 +74468,11 @@ function open_import(gobj, treedb) {
|
|
|
74349
74468
|
],
|
|
74350
74469
|
[
|
|
74351
74470
|
"button",
|
|
74352
|
-
{
|
|
74471
|
+
name_attrs("import", {
|
|
74353
74472
|
class: "SCHEMA_IMPORT_RUN button is-warning",
|
|
74354
74473
|
type: "button",
|
|
74355
74474
|
disabled: "disabled"
|
|
74356
|
-
},
|
|
74475
|
+
}),
|
|
74357
74476
|
[[
|
|
74358
74477
|
"span",
|
|
74359
74478
|
{ class: "icon" },
|
|
@@ -74380,18 +74499,31 @@ function open_import(gobj, treedb) {
|
|
|
74380
74499
|
evt.stopPropagation();
|
|
74381
74500
|
gobj_send_event(gobj, "EV_PREVIEW_IMPORT", {
|
|
74382
74501
|
text: $text.value,
|
|
74383
|
-
prune: $prune.checked
|
|
74502
|
+
prune: $prune.checked,
|
|
74503
|
+
model_gen
|
|
74384
74504
|
}, gobj);
|
|
74385
74505
|
});
|
|
74386
74506
|
$run.addEventListener("click", (evt) => {
|
|
74387
74507
|
evt.stopPropagation();
|
|
74388
|
-
gobj_send_event(gobj, "EV_APPLY_IMPORT", {}, gobj);
|
|
74508
|
+
gobj_send_event(gobj, "EV_APPLY_IMPORT", { model_gen }, gobj);
|
|
74389
74509
|
});
|
|
74390
74510
|
gobj.priv.import_pane = $plan_pane;
|
|
74391
74511
|
gobj.priv.import_run = $run;
|
|
74392
74512
|
open_dialog(gobj, $content, "import", "SCHEMA_IMPORT_DIALOG", treedb.id);
|
|
74393
74513
|
}
|
|
74394
74514
|
/***************************************************************
|
|
74515
|
+
* The plan is a list of writes computed against the model shown:
|
|
74516
|
+
* a load replaces the model, so the plan goes with it and the
|
|
74517
|
+
* dialog, if it stays up (a load that failed keeps the model),
|
|
74518
|
+
* asks for a Preview again.
|
|
74519
|
+
***************************************************************/
|
|
74520
|
+
function forget_import_plan(gobj) {
|
|
74521
|
+
let priv = gobj.priv;
|
|
74522
|
+
priv.import_plan = null;
|
|
74523
|
+
clear(priv.import_pane);
|
|
74524
|
+
if (priv.import_run) priv.import_run.disabled = true;
|
|
74525
|
+
}
|
|
74526
|
+
/***************************************************************
|
|
74395
74527
|
* The plan, drawn where the paste is: one line per write, the
|
|
74396
74528
|
* deletions apart and counted, because they are what the
|
|
74397
74529
|
* operator is being asked about.
|
|
@@ -74518,7 +74650,8 @@ function open_orphans(gobj) {
|
|
|
74518
74650
|
{ class: "SCHEMA_ORPHAN_ACTIONS has-text-right" },
|
|
74519
74651
|
readonly ? [] : [row_icon(gobj, "SCHEMA_ORPHAN_DELETE", "yi-trash", "delete", "EV_DELETE_ORPHAN", {
|
|
74520
74652
|
kind,
|
|
74521
|
-
id: entry.id
|
|
74653
|
+
id: entry.id,
|
|
74654
|
+
model_gen: priv.model_gen
|
|
74522
74655
|
})]
|
|
74523
74656
|
]
|
|
74524
74657
|
]
|
|
@@ -74690,6 +74823,10 @@ function transport_dropped(gobj) {
|
|
|
74690
74823
|
priv.load_round++;
|
|
74691
74824
|
priv.pending = 0;
|
|
74692
74825
|
priv.load_error = "";
|
|
74826
|
+
if (priv.records_before) {
|
|
74827
|
+
priv.records = priv.records_before;
|
|
74828
|
+
priv.records_before = null;
|
|
74829
|
+
}
|
|
74693
74830
|
return end_load(gobj);
|
|
74694
74831
|
}
|
|
74695
74832
|
return 0;
|
|
@@ -74700,11 +74837,22 @@ function transport_dropped(gobj) {
|
|
|
74700
74837
|
* in flight (ac_show() kept it), or the one it had. Without a
|
|
74701
74838
|
* model there is no screen to go to: the position is kept for the
|
|
74702
74839
|
* next load, and the editor says why it is empty.
|
|
74840
|
+
*
|
|
74841
|
+
* A dialog built on the model this load REPLACED is closed, and the
|
|
74842
|
+
* operator told to open it again: it shows the old record, and its
|
|
74843
|
+
* Save would write every field of it over the newer one. A load
|
|
74844
|
+
* that failed or never left replaced nothing (`model_gen` did not
|
|
74845
|
+
* move), and the dialog stays with what was typed.
|
|
74703
74846
|
***************************************************************/
|
|
74704
74847
|
function end_load(gobj) {
|
|
74705
74848
|
let priv = gobj.priv;
|
|
74706
74849
|
let seg = priv.pending_seg;
|
|
74707
74850
|
priv.pending_seg = null;
|
|
74851
|
+
if (priv.dialog && priv.dialog_gen !== priv.model_gen) {
|
|
74852
|
+
log_warning(`${gobj_short_name(gobj)}: a dialog was open on the schemas the load replaced: closed`);
|
|
74853
|
+
close_dialog(gobj);
|
|
74854
|
+
yui_shell_show_error(yui_shell_of(gobj), "the schemas were read again: open the dialog again", { t });
|
|
74855
|
+
}
|
|
74708
74856
|
if (priv.model) {
|
|
74709
74857
|
if (seg !== null) return apply_seg(gobj, seg);
|
|
74710
74858
|
return go(gobj, priv.treedb_id, priv.topic_name, priv.diagram, false);
|
|
@@ -74722,26 +74870,21 @@ function end_load(gobj) {
|
|
|
74722
74870
|
***************************************************************/
|
|
74723
74871
|
function end_writes(gobj, error) {
|
|
74724
74872
|
let priv = gobj.priv;
|
|
74873
|
+
let seg = priv.pending_seg;
|
|
74725
74874
|
priv.save_queue = [];
|
|
74875
|
+
priv.pending_seg = null;
|
|
74726
74876
|
gobj_change_state(gobj, priv.save_return || "ST_TREEDBS");
|
|
74727
74877
|
priv.save_return = "";
|
|
74728
74878
|
show_notice(gobj, "");
|
|
74729
74879
|
set_busy(gobj, false);
|
|
74730
|
-
if (priv.pending_seg !== null) {
|
|
74731
|
-
let seg = priv.pending_seg;
|
|
74732
|
-
priv.pending_seg = null;
|
|
74733
|
-
if (!error) {
|
|
74734
|
-
apply_seg(gobj, seg);
|
|
74735
|
-
if (priv.save_wrote) gobj_publish_event(gobj, "EV_RECORD_WRITTEN", { treedb_name: gobj_read_str_attr(gobj, "treedb_name") });
|
|
74736
|
-
priv.save_wrote = false;
|
|
74737
|
-
return reload_if_owed(gobj);
|
|
74738
|
-
}
|
|
74739
|
-
}
|
|
74740
74880
|
if (error) yui_shell_show_error(yui_shell_of(gobj), error, { t });
|
|
74741
74881
|
if (priv.save_wrote) gobj_publish_event(gobj, "EV_RECORD_WRITTEN", { treedb_name: gobj_read_str_attr(gobj, "treedb_name") });
|
|
74742
74882
|
priv.save_wrote = false;
|
|
74743
|
-
|
|
74744
|
-
|
|
74883
|
+
if (seg !== null) apply_seg(gobj, seg);
|
|
74884
|
+
else {
|
|
74885
|
+
render(gobj);
|
|
74886
|
+
publish_check(gobj);
|
|
74887
|
+
}
|
|
74745
74888
|
reload_if_owed(gobj);
|
|
74746
74889
|
return error ? -1 : 0;
|
|
74747
74890
|
}
|
|
@@ -74758,7 +74901,7 @@ function reload_if_owed(gobj) {
|
|
|
74758
74901
|
priv.reload_on_open = true;
|
|
74759
74902
|
return 0;
|
|
74760
74903
|
}
|
|
74761
|
-
return request_model(gobj);
|
|
74904
|
+
return request_model(gobj, true);
|
|
74762
74905
|
}
|
|
74763
74906
|
/***************************************************************
|
|
74764
74907
|
* The record a column form produces.
|
|
@@ -74824,6 +74967,7 @@ function topic_record(gobj, treedb, topic, values) {
|
|
|
74824
74967
|
***************************************************************/
|
|
74825
74968
|
function go(gobj, treedb_id, topic_name, diagram, mirror) {
|
|
74826
74969
|
let priv = gobj.priv;
|
|
74970
|
+
priv.pending_seg = null;
|
|
74827
74971
|
priv.treedb_id = treedb_id || "";
|
|
74828
74972
|
priv.topic_name = topic_name || "";
|
|
74829
74973
|
priv.diagram = !!diagram;
|
|
@@ -74924,10 +75068,20 @@ function ac_mt_command_answer(gobj, event, kw, src) {
|
|
|
74924
75068
|
else if (priv.records && Array.isArray(priv.records[topic_name])) priv.records[topic_name] = Array.isArray(data) ? data : [];
|
|
74925
75069
|
if (priv.pending > 0) return 0;
|
|
74926
75070
|
if (priv.load_error) {
|
|
75071
|
+
if (priv.model) {
|
|
75072
|
+
log_warning(`${gobj_short_name(gobj)}: the schemas could not be read again (${priv.load_error}): the previous ones stay`);
|
|
75073
|
+
priv.records = priv.records_before;
|
|
75074
|
+
priv.records_before = null;
|
|
75075
|
+
priv.reload_on_open = true;
|
|
75076
|
+
yui_shell_show_error(yui_shell_of(gobj), "cannot read the schemas again: the previous ones stay", { t });
|
|
75077
|
+
return end_load(gobj);
|
|
75078
|
+
}
|
|
74927
75079
|
priv.model = null;
|
|
74928
75080
|
return end_load(gobj);
|
|
74929
75081
|
}
|
|
74930
75082
|
priv.order_undo = {};
|
|
75083
|
+
priv.records_before = null;
|
|
75084
|
+
priv.model_gen++;
|
|
74931
75085
|
build_model(gobj);
|
|
74932
75086
|
start_measuring(gobj);
|
|
74933
75087
|
return end_load(gobj);
|
|
@@ -74940,7 +75094,7 @@ function ac_mt_command_answer(gobj, event, kw, src) {
|
|
|
74940
75094
|
log_warning(`${gobj_short_name(gobj)}: '${command}' of '${topic_name}' answered for a write that is over (result ${result}): ignored`);
|
|
74941
75095
|
return 0;
|
|
74942
75096
|
}
|
|
74943
|
-
return late_write_done(gobj, command, topic_name);
|
|
75097
|
+
return late_write_done(gobj, command, topic_name, data);
|
|
74944
75098
|
}
|
|
74945
75099
|
if (result < 0) return end_writes(gobj, comment || t("the treedb refused the write"));
|
|
74946
75100
|
priv.save_wrote = true;
|
|
@@ -74949,9 +75103,19 @@ function ac_mt_command_answer(gobj, event, kw, src) {
|
|
|
74949
75103
|
let record = is_object(data) ? data : Array.isArray(data) && is_object(data[0]) ? data[0] : null;
|
|
74950
75104
|
if (record) patch_record(gobj, topic_name, record);
|
|
74951
75105
|
else {
|
|
74952
|
-
|
|
75106
|
+
let not_sent = priv.save_queue.length - 1;
|
|
75107
|
+
log_warning(`${gobj_short_name(gobj)}: '${command}' answered no record` + (not_sent > 0 ? `: ${not_sent} write(s) after it were not sent` : ""));
|
|
75108
|
+
if (not_sent > 0) yui_shell_show_error(yui_shell_of(gobj), "the treedb did not describe a write back: the writes after it were not sent", { t });
|
|
75109
|
+
if (topic_name === T_TOPICS || topic_name === T_COLS) {
|
|
75110
|
+
let write = priv.save_queue[0];
|
|
75111
|
+
mark_written(gobj, topic_name, write ? write.record : null);
|
|
75112
|
+
}
|
|
75113
|
+
priv.reload_after_write = false;
|
|
74953
75114
|
priv.save_queue = [];
|
|
74954
|
-
|
|
75115
|
+
priv.save_return = "";
|
|
75116
|
+
priv.save_wrote = false;
|
|
75117
|
+
gobj_publish_event(gobj, "EV_RECORD_WRITTEN", { treedb_name: gobj_read_str_attr(gobj, "treedb_name") });
|
|
75118
|
+
request_model(gobj, true);
|
|
74955
75119
|
return 0;
|
|
74956
75120
|
}
|
|
74957
75121
|
}
|
|
@@ -74978,11 +75142,18 @@ function ac_mt_command_answer(gobj, event, kw, src) {
|
|
|
74978
75142
|
* the view was answered once, as failed -- and echoes the node
|
|
74979
75143
|
* event instead, which this view does not hear: there, Refresh is
|
|
74980
75144
|
* what reads the store.
|
|
75145
|
+
*
|
|
75146
|
+
* It is a WRITE like any other for the host (EV_RECORD_WRITTEN):
|
|
75147
|
+
* the host was told nothing when it was given up, and it owns the
|
|
75148
|
+
* Save that publishes it. The reload keeps what this session wrote
|
|
75149
|
+
* (request_model()), and the record it answered marks its topic.
|
|
74981
75150
|
***************************************************************/
|
|
74982
|
-
function late_write_done(gobj, command, topic_name) {
|
|
75151
|
+
function late_write_done(gobj, command, topic_name, data) {
|
|
74983
75152
|
let priv = gobj.priv;
|
|
74984
75153
|
let state = gobj_current_state(gobj);
|
|
74985
75154
|
log_warning(`${gobj_short_name(gobj)}: '${command}' of '${topic_name}' was given up, and it was DONE: the model is read again`);
|
|
75155
|
+
mark_written_record(gobj, topic_name, is_object(data) ? data : Array.isArray(data) && is_object(data[0]) ? data[0] : null);
|
|
75156
|
+
gobj_publish_event(gobj, "EV_RECORD_WRITTEN", { treedb_name: gobj_read_str_attr(gobj, "treedb_name") });
|
|
74986
75157
|
if (state === "ST_LOADING") return 0;
|
|
74987
75158
|
if (state === "ST_SAVING") {
|
|
74988
75159
|
priv.reload_after_write = true;
|
|
@@ -74992,7 +75163,29 @@ function late_write_done(gobj, command, topic_name) {
|
|
|
74992
75163
|
priv.reload_on_open = true;
|
|
74993
75164
|
return 0;
|
|
74994
75165
|
}
|
|
74995
|
-
return request_model(gobj);
|
|
75166
|
+
return request_model(gobj, true);
|
|
75167
|
+
}
|
|
75168
|
+
/***************************************************************
|
|
75169
|
+
* The topic a record written belongs to, read off the RECORD and
|
|
75170
|
+
* not off the screen: a write answered late may be about a topic
|
|
75171
|
+
* the operator has left. A column names its topic in its fkey; a
|
|
75172
|
+
* topic is its own id.
|
|
75173
|
+
*
|
|
75174
|
+
* The fkey is read with parse_fkey_ref(), because the store answers
|
|
75175
|
+
* in list_dict -- `[{topic_name, id, hook_name}]` -- and not in the
|
|
75176
|
+
* `topics^<topic id>^cols` strings this view writes. Split as a
|
|
75177
|
+
* string, every production answer marked nothing (fourth independent
|
|
75178
|
+
* review, probe C).
|
|
75179
|
+
***************************************************************/
|
|
75180
|
+
function mark_written_record(gobj, topic_name, record) {
|
|
75181
|
+
let priv = gobj.priv;
|
|
75182
|
+
if (!record) return;
|
|
75183
|
+
if (topic_name === T_TOPICS && typeof record.id === "string" && record.id) {
|
|
75184
|
+
priv.written[record.id] = true;
|
|
75185
|
+
return;
|
|
75186
|
+
}
|
|
75187
|
+
if (topic_name !== T_COLS) return;
|
|
75188
|
+
for (let ref of parse_fkey_ref(record.topics)) priv.written[ref.id] = true;
|
|
74996
75189
|
}
|
|
74997
75190
|
/***************************************************************
|
|
74998
75191
|
* Remember which topic this write belongs to, which is what
|
|
@@ -75051,7 +75244,7 @@ function ac_transport_state(gobj, event, kw, src) {
|
|
|
75051
75244
|
}
|
|
75052
75245
|
if ((!priv.model || priv.reload_on_open) && state !== "ST_LOADING" && state !== "ST_SAVING") {
|
|
75053
75246
|
priv.reload_on_open = false;
|
|
75054
|
-
return request_model(gobj);
|
|
75247
|
+
return request_model(gobj, true);
|
|
75055
75248
|
}
|
|
75056
75249
|
render_toolbar(gobj);
|
|
75057
75250
|
return 0;
|
|
@@ -75066,7 +75259,30 @@ function ac_language_changed$1(gobj, event, kw, src) {
|
|
|
75066
75259
|
return 0;
|
|
75067
75260
|
}
|
|
75068
75261
|
function ac_refresh$1(gobj, event, kw, src) {
|
|
75069
|
-
return request_model(gobj);
|
|
75262
|
+
return request_model(gobj, false);
|
|
75263
|
+
}
|
|
75264
|
+
/***************************************************************
|
|
75265
|
+
* What a DIALOG sends while the model is being read again: the
|
|
75266
|
+
* Save of a form left open when the reload started, a
|
|
75267
|
+
* confirmation, an import. The loading screen took the rest of
|
|
75268
|
+
* the view away (request_model()); a dialog is the one thing the
|
|
75269
|
+
* operator can still reach, and what it asks is computed against
|
|
75270
|
+
* a model the answers in the air are about to replace.
|
|
75271
|
+
*
|
|
75272
|
+
* Refused, and SAID. Undeclared, it answered "Event NOT DEFINED
|
|
75273
|
+
* in state ST_LOADING" and the edit was gone with no word (third
|
|
75274
|
+
* independent review).
|
|
75275
|
+
*
|
|
75276
|
+
* And NOT "try again": the same Save, once the load was in, sent
|
|
75277
|
+
* the form built on the old model -- every field of the old record
|
|
75278
|
+
* over the newer one (fourth independent review). The load that
|
|
75279
|
+
* lands closes such a dialog (end_load()); one that fails leaves it
|
|
75280
|
+
* up, on the model it was built on.
|
|
75281
|
+
***************************************************************/
|
|
75282
|
+
function ac_wait_for_the_load(gobj, event, kw, src) {
|
|
75283
|
+
log_warning(`${gobj_short_name(gobj)}: ${event} refused, the schemas are loading`);
|
|
75284
|
+
yui_shell_show_error(yui_shell_of(gobj), "the schemas are loading: wait for them", { t });
|
|
75285
|
+
return -1;
|
|
75070
75286
|
}
|
|
75071
75287
|
/***************************************************************
|
|
75072
75288
|
* {drafts: {treedb_name: [topic names]}} -- the host says which
|
|
@@ -75131,6 +75347,7 @@ function ac_show_orphans(gobj, event, kw, src) {
|
|
|
75131
75347
|
* to feed.
|
|
75132
75348
|
***************************************************************/
|
|
75133
75349
|
function confirm_then(gobj, message, detail, kw) {
|
|
75350
|
+
kw = Object.assign({ model_gen: gobj.priv.model_gen }, kw);
|
|
75134
75351
|
yui_shell_confirm_danger(yui_shell_of(gobj), message, {
|
|
75135
75352
|
t,
|
|
75136
75353
|
detail
|
|
@@ -75192,6 +75409,7 @@ function ac_save_column(gobj, event, kw, src) {
|
|
|
75192
75409
|
let topic = find_topic(priv.model, priv.treedb_id, kw.topic);
|
|
75193
75410
|
let col = kw.creating ? null : find_col(priv.model, priv.treedb_id, kw.topic, kw.col);
|
|
75194
75411
|
if (refuse_if_readonly(gobj, event)) return -1;
|
|
75412
|
+
if (built_on_a_replaced_model(gobj, event, kw)) return -1;
|
|
75195
75413
|
if (!topic || !kw.creating && !col) {
|
|
75196
75414
|
log_error(`${gobj_short_name(gobj)}: ${event} names no column: ${kw.topic}.${kw.col}`);
|
|
75197
75415
|
return -1;
|
|
@@ -75313,6 +75531,7 @@ function ac_save_topic(gobj, event, kw, src) {
|
|
|
75313
75531
|
let treedb = current_treedb(gobj);
|
|
75314
75532
|
let topic = kw.creating ? null : find_topic(priv.model, priv.treedb_id, kw.topic);
|
|
75315
75533
|
if (refuse_if_readonly(gobj, event)) return -1;
|
|
75534
|
+
if (built_on_a_replaced_model(gobj, event, kw)) return -1;
|
|
75316
75535
|
if (!treedb || !kw.creating && !topic) {
|
|
75317
75536
|
log_error(`${gobj_short_name(gobj)}: ${event} names no topic: ${kw.topic}`);
|
|
75318
75537
|
return -1;
|
|
@@ -75355,6 +75574,7 @@ function ac_delete_topic(gobj, event, kw, src) {
|
|
|
75355
75574
|
***************************************************************/
|
|
75356
75575
|
function ac_delete_orphan(gobj, event, kw, src) {
|
|
75357
75576
|
if (refuse_if_readonly(gobj, event)) return -1;
|
|
75577
|
+
if (built_on_a_replaced_model(gobj, event, kw)) return -1;
|
|
75358
75578
|
if (empty_string(kw.id)) {
|
|
75359
75579
|
log_error(`${gobj_short_name(gobj)}: ${event} names nothing`);
|
|
75360
75580
|
return -1;
|
|
@@ -75411,6 +75631,7 @@ function ac_preview_import(gobj, event, kw, src) {
|
|
|
75411
75631
|
let priv = gobj.priv;
|
|
75412
75632
|
let treedb = current_treedb(gobj);
|
|
75413
75633
|
let incoming = null;
|
|
75634
|
+
if (built_on_a_replaced_model(gobj, event, kw)) return -1;
|
|
75414
75635
|
if (!treedb) {
|
|
75415
75636
|
log_error(`${gobj_short_name(gobj)}: ${event} with no treedb open`);
|
|
75416
75637
|
return -1;
|
|
@@ -75438,6 +75659,7 @@ function ac_preview_import(gobj, event, kw, src) {
|
|
|
75438
75659
|
function ac_apply_import(gobj, event, kw, src) {
|
|
75439
75660
|
let plan = gobj.priv.import_plan;
|
|
75440
75661
|
if (refuse_if_readonly(gobj, event)) return -1;
|
|
75662
|
+
if (built_on_a_replaced_model(gobj, event, kw)) return -1;
|
|
75441
75663
|
if (!plan || plan.writes.length === 0) {
|
|
75442
75664
|
log_error(`${gobj_short_name(gobj)}: ${event} with no plan to run`);
|
|
75443
75665
|
return -1;
|
|
@@ -75480,6 +75702,7 @@ function run_import(gobj) {
|
|
|
75480
75702
|
function ac_confirmed(gobj, event, kw, src) {
|
|
75481
75703
|
let priv = gobj.priv;
|
|
75482
75704
|
if (refuse_if_readonly(gobj, event)) return -1;
|
|
75705
|
+
if (built_on_a_replaced_model(gobj, event, kw)) return -1;
|
|
75483
75706
|
if (kw.what === "import") return run_import(gobj);
|
|
75484
75707
|
if (kw.what === "topic") {
|
|
75485
75708
|
let topic = find_topic(priv.model, priv.treedb_id, kw.topic);
|
|
@@ -75614,7 +75837,38 @@ function create_gclass$1(gclass_name) {
|
|
|
75614
75837
|
];
|
|
75615
75838
|
const states = [
|
|
75616
75839
|
["ST_IDLE", COMMON.slice()],
|
|
75617
|
-
["ST_LOADING", COMMON.
|
|
75840
|
+
["ST_LOADING", COMMON.concat([
|
|
75841
|
+
[
|
|
75842
|
+
"EV_SAVE_COLUMN",
|
|
75843
|
+
ac_wait_for_the_load,
|
|
75844
|
+
null
|
|
75845
|
+
],
|
|
75846
|
+
[
|
|
75847
|
+
"EV_SAVE_TOPIC",
|
|
75848
|
+
ac_wait_for_the_load,
|
|
75849
|
+
null
|
|
75850
|
+
],
|
|
75851
|
+
[
|
|
75852
|
+
"EV_DELETE_ORPHAN",
|
|
75853
|
+
ac_wait_for_the_load,
|
|
75854
|
+
null
|
|
75855
|
+
],
|
|
75856
|
+
[
|
|
75857
|
+
"EV_PREVIEW_IMPORT",
|
|
75858
|
+
ac_wait_for_the_load,
|
|
75859
|
+
null
|
|
75860
|
+
],
|
|
75861
|
+
[
|
|
75862
|
+
"EV_APPLY_IMPORT",
|
|
75863
|
+
ac_wait_for_the_load,
|
|
75864
|
+
null
|
|
75865
|
+
],
|
|
75866
|
+
[
|
|
75867
|
+
"EV_CONFIRMED",
|
|
75868
|
+
ac_wait_for_the_load,
|
|
75869
|
+
null
|
|
75870
|
+
]
|
|
75871
|
+
])],
|
|
75618
75872
|
["ST_EMPTY", COMMON.slice()],
|
|
75619
75873
|
["ST_TREEDBS", COMMON.concat([
|
|
75620
75874
|
[
|