@yuneta/gobj-ui 7.25.15 → 7.25.16

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 CHANGED
@@ -1809,6 +1809,21 @@ fails:
1809
1809
  gobj_send_event(engine, "EV_GRAPHS_WRITE_REFUSED", {topic: record.topic}, gobj);
1810
1810
  ```
1811
1811
 
1812
+ **A refused write stays OWED until a Save writes it** (since `7.25.16`). In
1813
+ `7.25.15` the Save lit by the refusal went dark again at the next repaint of
1814
+ the history buttons -- they decide the Save from `history.canUndo()`, and they
1815
+ run on every history change, every change of mode and every redraw of the
1816
+ theme -- and a refusal heard in reading was never shown. The engine now keeps
1817
+ the topic as owed: the Save stays lit while one is, in edition and on entering
1818
+ it; the Save that writes the topic again pays it, and a reload of the data
1819
+ forgets it (the view holds what the backend holds).
1820
+
1821
+ ```js
1822
+ gobj_send_event(engine, "EV_GRAPHS_WRITE_REFUSED", {topic: "users"}, host); // Save lit
1823
+ // ... a node moved and moved back (nothing to undo): Save still lit
1824
+ gobj_send_event(engine, "EV_SAVE_GRAPH", {}, engine); // writes users; Save dark
1825
+ ```
1826
+
1812
1827
  **Every saved look has a way back, in the context menu** (since `7.23.134`,
1813
1828
  replacing `reset sizes` / `reset topic sizes`). In edition, the node, the port
1814
1829
  and the edge menus each offer three resets — this one, its kind (`reset topic
@@ -2113,7 +2128,7 @@ columns**, each in its declared `order`, with the storage composed underneath
2113
2128
  the qualified id, the fkey to the parent, the place among the siblings, and the
2114
2129
  versions that publish the change.
2115
2130
 
2116
- **An edit here is a draft** (7.23.196, with the SDK's M36 design). A write moves
2131
+ **An edit here is a draft** (7.23.196, with SDK 7.25.0). A write moves
2117
2132
  no version and reaches no treedb. The HOST publishes it with `C_TREEDB`'s
2118
2133
  `save-schema`, which raises the `topic_version` of each topic that changed
2119
2134
  (the one that regenerates `topic_cols.json`) and the `schema_version` (the one
@@ -2206,6 +2221,7 @@ else.
2206
2221
  master the tranger, so it refuses every write), `base_route`, `$container`.
2207
2222
  - In: `EV_SHOW` (`{subpath}` — the tail it owns is `<treedb>[/<topic>]` or
2208
2223
  `<treedb>/diagram`), `EV_HIDE`, `EV_TRANSPORT_STATE`, `EV_REFRESH`,
2224
+ `EV_DRAFTS` (`{drafts: {<treedb>: [<topic>, …]}}`, see above),
2209
2225
  `EV_LANGUAGE_CHANGED`, `EV_MT_COMMAND_ANSWER`.
2210
2226
  - A **drop** of the transport — `EV_TRANSPORT_STATE {connected: false}`, or a
2211
2227
  failed answer that arrives while the transport is out of `ST_SESSION` — ends
@@ -2230,6 +2246,19 @@ else.
2230
2246
  gobj_send_event(editor, "EV_SHOW", {subpath: "db"}, host); // waits
2231
2247
  // ... the three `nodes` answers land -> ST_TOPICS of `db`
2232
2248
  ```
2249
+
2250
+ **`EV_REFRESH` waits too** (7.25.16). Heard in `ST_SAVING` -- a host that
2251
+ sends it after a Save while the writes are still in flight -- it is logged
2252
+ and run when the writes END, forgetting the drafts as a Refresh does. It
2253
+ ran at once: the load replaced `ST_SAVING`, and what was still queued (the
2254
+ topic of a topic delete, after its columns) was never sent, with no word.
2255
+
2256
+ ```js
2257
+ gobj_send_event(editor, "EV_CONFIRMED", {what: "topic", topic: "users",
2258
+ model_gen: n}, editor); // ST_SAVING, 2 deletes
2259
+ gobj_send_event(editor, "EV_REFRESH", {}, host); // logged, waits
2260
+ // ... both deletes answered -> ST_LOADING -> the model is read again
2261
+ ```
2233
2262
  - **The loading screen is drawn** (7.25.8): the body is cleared, the drawing
2234
2263
  destroyed and every toolbar button disabled, as in `ST_SAVING`, because
2235
2264
  `ST_LOADING` declares none of a screen's actions. A **dialog** left open is
@@ -2315,6 +2344,28 @@ else.
2315
2344
  // ... the load lands: the same click now opens the form
2316
2345
  ```
2317
2346
 
2347
+ **The Save of a form open when the reload is owed keeps what was typed**
2348
+ (7.25.16). It runs the owed reload and says
2349
+ `the schemas shown may be out of date: they are read again, and the form opens again on them with your changes`.
2350
+ The load that lands closes the form and opens it again on the schemas it
2351
+ read, with the fields the operator CHANGED put back on top -- only those,
2352
+ so a field left as it was shows what the store holds now and the next Save
2353
+ does not write the old value over a newer one. A new column or topic comes
2354
+ back whole (nothing of it is in the store). Before, the form was closed and
2355
+ what was typed was lost. It is not opened again when what it edited is not
2356
+ on the schemas read (a column deleted meanwhile, another treedb, another
2357
+ screen): that is said with
2358
+ `the schemas were read again and what the form was editing is not there any more`.
2359
+ A form the operator closed while the load ran stays closed. Both texts are
2360
+ i18n keys the consumer's locales carry.
2361
+
2362
+ ```js
2363
+ // reload owed; column form of db.users.id open, header typed "Identifier"
2364
+ // Save -> refused, the model is read again
2365
+ // ... the load lands: the form opens again, header "Identifier", the rest
2366
+ // of the fields as the store holds them now; Save writes them
2367
+ ```
2368
+
2318
2369
  A write answered with no record reloads the model and
2319
2370
  says what it did not send after it
2320
2371
  (`the treedb did not describe a write back: the writes after it were not sent`).
@@ -3280,9 +3331,13 @@ a modal
3280
3331
  `TOAST*`. Every button of that chrome -- each ✕, a dialog's back arrow, a
3281
3332
  confirmation's answers (`CONFIRM_BTN`) -- carries a translatable `title` and
3282
3333
  `aria-label` (7.25.10). An answer is named by its own label, so pass i18n
3283
- KEYS as `yes_label` / `confirm_label` / … (`{yes_label: "install"}`): the
3284
- defaults (`"Yes"`, `"Delete"`) are not lower-case and no validated locale
3285
- can hold them.
3334
+ KEYS as `yes_label` / `confirm_label` / … (`{yes_label: "install"}`). The
3335
+ defaults are keys too since 7.25.16 -- `"ok"`, `"yes"`, `"no"`, `"delete"`
3336
+ and `"cancel"` (they were `"OK"`, `"Yes"`, … , which no validated locale can
3337
+ hold) -- so an app that mounts these dialogs defines those five keys, and
3338
+ its `validate-locales` asks for them. `yui_install_ask_once()` asks
3339
+ `"install this app"` (was `"Install this app?"`), answered `"install"` /
3340
+ `"not now"`.
3286
3341
 
3287
3342
  Those names identify the *kind* of block, not the *instance*: every window in
3288
3343
  the app is a `C_YUI_WINDOW`, every popup is a `MODAL`. To target **one**
@@ -3437,8 +3437,28 @@ function build_dialog(shell, message, buttons, opts) {
3437
3437
  if ($primary) $primary.focus();
3438
3438
  });
3439
3439
  }
3440
+ /***************************************************************
3441
+ * The labels a confirmation shows when the caller names none.
3442
+ *
3443
+ * They are i18n KEYS, lower-case like every key: a button
3444
+ * translates its label with the app's t() -- its text, its title
3445
+ * and its aria-label. Until 7.25.16 they were "OK", "Yes", "No",
3446
+ * "Delete" and "Cancel", keys that no locale carries, so a dialog
3447
+ * that took the defaults read in English in every language.
3448
+ *
3449
+ * Written as `i18n:` entries so the validate-locales script of a
3450
+ * consumer finds them when it scans this module: an app that
3451
+ * mounts these dialogs must define the five keys.
3452
+ ***************************************************************/
3453
+ var DEFAULT_LABEL = Object.freeze({
3454
+ ok: Object.freeze({ i18n: "ok" }),
3455
+ yes: Object.freeze({ i18n: "yes" }),
3456
+ no: Object.freeze({ i18n: "no" }),
3457
+ delete: Object.freeze({ i18n: "delete" }),
3458
+ cancel: Object.freeze({ i18n: "cancel" })
3459
+ });
3440
3460
  function yui_shell_confirm_ok(shell, message, opts) {
3441
- let label = opts && opts.ok_label || "OK";
3461
+ let label = opts && opts.ok_label || DEFAULT_LABEL.ok.i18n;
3442
3462
  if (!opts || !opts.type) opts = Object.assign({}, opts, { type: "success" });
3443
3463
  return build_dialog(shell, message, [{
3444
3464
  label,
@@ -3447,8 +3467,8 @@ function yui_shell_confirm_ok(shell, message, opts) {
3447
3467
  }], opts).then(() => void 0);
3448
3468
  }
3449
3469
  function yui_shell_confirm_yesno(shell, message, opts) {
3450
- let yes_label = opts && opts.yes_label || "Yes";
3451
- let no_label = opts && opts.no_label || "No";
3470
+ let yes_label = opts && opts.yes_label || DEFAULT_LABEL.yes.i18n;
3471
+ let no_label = opts && opts.no_label || DEFAULT_LABEL.no.i18n;
3452
3472
  return build_dialog(shell, message, [{
3453
3473
  label: yes_label,
3454
3474
  value: "yes",
@@ -3473,8 +3493,8 @@ function yui_shell_confirm_yesno(shell, message, opts) {
3473
3493
  * pressed.
3474
3494
  ***************************************************************/
3475
3495
  function yui_shell_confirm_danger(shell, message, opts) {
3476
- let confirm_label = opts && opts.confirm_label || "Delete";
3477
- let cancel_label = opts && opts.cancel_label || "Cancel";
3496
+ let confirm_label = opts && opts.confirm_label || DEFAULT_LABEL.delete.i18n;
3497
+ let cancel_label = opts && opts.cancel_label || DEFAULT_LABEL.cancel.i18n;
3478
3498
  if (!opts || !opts.type) opts = Object.assign({}, opts, { type: "danger" });
3479
3499
  return build_dialog(shell, message, [{
3480
3500
  label: confirm_label,
@@ -3486,9 +3506,9 @@ function yui_shell_confirm_danger(shell, message, opts) {
3486
3506
  }], opts).then((v) => v === "confirm");
3487
3507
  }
3488
3508
  function yui_shell_confirm_yesnocancel(shell, message, opts) {
3489
- let yes_label = opts && opts.yes_label || "Yes";
3490
- let no_label = opts && opts.no_label || "No";
3491
- let cancel_label = opts && opts.cancel_label || "Cancel";
3509
+ let yes_label = opts && opts.yes_label || DEFAULT_LABEL.yes.i18n;
3510
+ let no_label = opts && opts.no_label || DEFAULT_LABEL.no.i18n;
3511
+ let cancel_label = opts && opts.cancel_label || DEFAULT_LABEL.cancel.i18n;
3492
3512
  return build_dialog(shell, message, [
3493
3513
  {
3494
3514
  label: yes_label,
@@ -59232,6 +59252,7 @@ var PRIVATE_DATA$4 = {
59232
59252
  __graphs__: [],
59233
59253
  _graph_properties: {},
59234
59254
  _saved_graph_properties: {},
59255
+ _graphs_writes_owed: {},
59235
59256
  yet_showed: false,
59236
59257
  edit_mode: false,
59237
59258
  operation_mode: null,
@@ -60927,6 +60948,7 @@ function save_topic_graph_properties(gobj, topic_name) {
60927
60948
  if (!topic_arrangement_changed(properties, priv._saved_graph_properties[topic_name])) return;
60928
60949
  properties.__origin__ = (0, _yuneta_gobj_js.gobj_read_str_attr)((0, _yuneta_gobj_js.gobj_yuno)(), "node_uuid");
60929
60950
  priv._saved_graph_properties[topic_name] = (0, _yuneta_gobj_js.json_deep_copy)(properties);
60951
+ delete priv._graphs_writes_owed[topic_name];
60930
60952
  let kw_update = {
60931
60953
  treedb_name: priv.treedb_name,
60932
60954
  topic_name: "__graphs__",
@@ -61008,8 +61030,13 @@ function update_history_buttons(gobj) {
61008
61030
  } else {
61009
61031
  disableElements($container, ".EV_HISTORY_UNDO");
61010
61032
  set_active_state($container, ".EV_HISTORY_UNDO", false);
61011
- disableElements($container, ".EV_SAVE_GRAPH");
61012
- set_submit_state($container, ".EV_SAVE_GRAPH", false);
61033
+ if (graphs_write_is_owed(gobj)) {
61034
+ enableElements($container, ".EV_SAVE_GRAPH");
61035
+ set_submit_state($container, ".EV_SAVE_GRAPH", true);
61036
+ } else {
61037
+ disableElements($container, ".EV_SAVE_GRAPH");
61038
+ set_submit_state($container, ".EV_SAVE_GRAPH", false);
61039
+ }
61013
61040
  }
61014
61041
  } else {
61015
61042
  disableElements($container, ".EV_HISTORY_REDO");
@@ -61966,6 +61993,15 @@ function run_preview_undo(gobj, key) {
61966
61993
  (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: cannot undo the preview: ${e}`);
61967
61994
  }
61968
61995
  }
61996
+ /************************************************************
61997
+ * Is a `__graphs__` write owed? The host said one did not land
61998
+ * (EV_GRAPHS_WRITE_REFUSED), and no Save has written that topic
61999
+ * since. The Save button stays lit while it is: the history
62000
+ * knows nothing of it, so `canUndo()` alone would put it out.
62001
+ ************************************************************/
62002
+ function graphs_write_is_owed(gobj) {
62003
+ return Object.keys(gobj.priv._graphs_writes_owed).length > 0;
62004
+ }
61969
62005
  function mark_graph_dirty(gobj) {
61970
62006
  let $container = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
61971
62007
  enableElements($container, ".EV_SAVE_GRAPH");
@@ -65706,6 +65742,7 @@ function ac_clear_data(gobj, event, kw, src) {
65706
65742
  deselect_node(gobj);
65707
65743
  hide_create_popover(gobj);
65708
65744
  (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "records", {});
65745
+ priv._graphs_writes_owed = {};
65709
65746
  priv._nodes_total = 0;
65710
65747
  priv._nodes_placed = 0;
65711
65748
  priv._fold_model = null;
@@ -65798,11 +65835,9 @@ function ac_save_graph(gobj, event, kw, src) {
65798
65835
  disableElements($container, ".EV_SAVE_GRAPH");
65799
65836
  set_submit_state($container, ".EV_SAVE_GRAPH", false);
65800
65837
  let history = graph_get_plugin(gobj, "history");
65801
- if (history) {
65802
- history.clear();
65803
- update_history_buttons(gobj);
65804
- }
65838
+ if (history) history.clear();
65805
65839
  save_geometry(gobj);
65840
+ update_history_buttons(gobj);
65806
65841
  }
65807
65842
  return 0;
65808
65843
  }
@@ -65825,6 +65860,7 @@ function ac_graphs_write_refused(gobj, event, kw, src) {
65825
65860
  }
65826
65861
  (0, _yuneta_gobj_js.log_warning)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: the arrangement of '${topic_name}' was not saved: the next Save writes it again`);
65827
65862
  forget_refused_graphs_write(priv._saved_graph_properties, topic_name);
65863
+ priv._graphs_writes_owed[topic_name] = true;
65828
65864
  if (priv.edit_mode) mark_graph_dirty(gobj);
65829
65865
  return 0;
65830
65866
  }
@@ -72334,6 +72370,8 @@ var PRIVATE_DATA$1 = {
72334
72370
  dropped_round: 0,
72335
72371
  dropped_write: 0,
72336
72372
  reload_after_write: false,
72373
+ refresh_after_write: false,
72374
+ reopen_form: null,
72337
72375
  pending_seg: null,
72338
72376
  import_plan: null,
72339
72377
  import_pane: null,
@@ -72539,14 +72577,61 @@ function owes_reload(gobj) {
72539
72577
  let state = (0, _yuneta_gobj_js.gobj_current_state)(gobj);
72540
72578
  return gobj.priv.reload_on_open && transport_in_session(gobj) && state !== "ST_LOADING" && state !== "ST_SAVING";
72541
72579
  }
72542
- function reload_first(gobj, event) {
72580
+ function reload_first(gobj, event, reopen) {
72581
+ let priv = gobj.priv;
72543
72582
  if (!owes_reload(gobj)) return false;
72544
- gobj.priv.reload_on_open = false;
72583
+ priv.reload_on_open = false;
72584
+ if (reopen && priv.dialog) {
72585
+ priv.reopen_form = Object.assign((0, _yuneta_gobj_js.json_deep_copy)(reopen), {
72586
+ dialog: priv.dialog,
72587
+ treedb_id: priv.treedb_id
72588
+ });
72589
+ (0, _yuneta_gobj_js.log_warning)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: ${event} refused, the schemas shown may be out of date: they are read again first, and the form opens again on them with its changes`);
72590
+ yui_shell_show_error(yui_shell_of(gobj), "the schemas shown may be out of date: they are read again, and the form opens again on them with your changes", { t: i18next.t });
72591
+ request_model(gobj, true);
72592
+ return true;
72593
+ }
72545
72594
  (0, _yuneta_gobj_js.log_warning)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: ${event} refused, the schemas shown may be out of date: they are read again first`);
72546
72595
  yui_shell_show_error(yui_shell_of(gobj), "the schemas shown may be out of date: they are read again, try again when they are in", { t: i18next.t });
72547
72596
  request_model(gobj, true);
72548
72597
  return true;
72549
72598
  }
72599
+ /***************************************************************
72600
+ * The form reload_first() took down, opened again on the model
72601
+ * the load just made, with the operator's changes on top.
72602
+ *
72603
+ * Only where it can mean the same thing: on the treedb it was
72604
+ * opened on, on the screen whose Save it has, and on a record
72605
+ * that is still there. Otherwise it is not opened, and that is
72606
+ * said -- a form opened on another treedb, or on nothing, would
72607
+ * save somewhere the operator never meant.
72608
+ ***************************************************************/
72609
+ function reopen_form(gobj, r) {
72610
+ let priv = gobj.priv;
72611
+ let state = (0, _yuneta_gobj_js.gobj_current_state)(gobj);
72612
+ let treedb = current_treedb(gobj);
72613
+ let topic = null;
72614
+ let col = null;
72615
+ let fits = false;
72616
+ if (treedb && r.treedb_id === priv.treedb_id) {
72617
+ if (r.form === "column" && state === "ST_COLUMNS") {
72618
+ topic = find_topic(priv.model, priv.treedb_id, r.topic);
72619
+ col = topic && !r.creating ? find_col(priv.model, priv.treedb_id, r.topic, r.col) : null;
72620
+ fits = !!topic && (r.creating || !!col);
72621
+ } else if (r.form === "topic" && state === "ST_TOPICS") {
72622
+ topic = r.creating ? null : find_topic(priv.model, priv.treedb_id, r.topic);
72623
+ fits = r.creating || !!topic;
72624
+ }
72625
+ }
72626
+ if (!fits) {
72627
+ (0, _yuneta_gobj_js.log_warning)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: the ${r.form} form was not opened again: what it edited is not on the schemas the reload read (${r.treedb_id} ${r.topic}${r.col ? "." + r.col : ""}, now ${state})`);
72628
+ yui_shell_show_error(yui_shell_of(gobj), "the schemas were read again and what the form was editing is not there any more", { t: i18next.t });
72629
+ return -1;
72630
+ }
72631
+ if (r.form === "column") open_column_form(gobj, topic, col, null, r.changed);
72632
+ else open_topic_form(gobj, treedb, topic, r.changed);
72633
+ return 0;
72634
+ }
72550
72635
  function reload_after_move(gobj) {
72551
72636
  if (!owes_reload(gobj)) return 0;
72552
72637
  gobj.priv.reload_on_open = false;
@@ -72692,7 +72777,7 @@ function build_model(gobj) {
72692
72777
  * measurement of "changed since" can be measured against.
72693
72778
  *
72694
72779
  * `written` is forgotten only by an EV_REFRESH (the view's own
72695
- * Refresh button, or a host that sends one). Since M36 a write moves no
72780
+ * Refresh button, or a host that sends one). Since 7.23.196 a write moves no
72696
72781
  * version, so `written` is the draft chip of what this session
72697
72782
  * wrote and no longer half of a version measurement: a reload of
72698
72783
  * the reconnect, or of a write that turned out done, forgot the
@@ -73970,6 +74055,55 @@ function read_form($root) {
73970
74055
  return out;
73971
74056
  }
73972
74057
  /***************************************************************
74058
+ * A column form as its Save sends it: the fields and the flags.
74059
+ ***************************************************************/
74060
+ function column_form_state($form) {
74061
+ let values = read_form($form);
74062
+ values.flag = read_flags($form);
74063
+ return values;
74064
+ }
74065
+ /***************************************************************
74066
+ * The fields the operator CHANGED: those whose value is not the
74067
+ * one the form was opened with.
74068
+ ***************************************************************/
74069
+ function form_changes(initial, values) {
74070
+ let changed = {};
74071
+ for (let name of Object.keys(values)) if (JSON.stringify(values[name]) !== JSON.stringify(initial[name])) changed[name] = values[name];
74072
+ return changed;
74073
+ }
74074
+ /***************************************************************
74075
+ * Put changes back into a form just built (reopen_form()). The
74076
+ * flags are drawn again for the type the changes leave, holding
74077
+ * the flags they name, so none of them is hidden by the type.
74078
+ ***************************************************************/
74079
+ function apply_form_changes(gobj, $form, changes) {
74080
+ for (let name of Object.keys(changes)) {
74081
+ if (name === "flag") continue;
74082
+ let $control = $form.querySelector(`[data-name="${name}"]`);
74083
+ if (!$control) {
74084
+ (0, _yuneta_gobj_js.log_warning)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: a change of '${name}' has no field in the form opened again: not put back`);
74085
+ continue;
74086
+ }
74087
+ if ($control.type === "checkbox") $control.checked = !!changes[name];
74088
+ else $control.value = changes[name];
74089
+ }
74090
+ let $type = $form.querySelector("[data-name=\"type\"]");
74091
+ if ($type && $form.querySelector(".SCHEMA_FLAGS")) {
74092
+ let flags = Array.isArray(changes.flag) ? changes.flag : read_flags($form);
74093
+ redraw_flags(gobj, $form, $type.value, flags);
74094
+ }
74095
+ }
74096
+ /***************************************************************
74097
+ * Draw the flags of a column form again, for `type`, with
74098
+ * `current` checked.
74099
+ ***************************************************************/
74100
+ function redraw_flags(gobj, $form, type, current) {
74101
+ let $holder = $form.querySelector(".SCHEMA_FLAGS");
74102
+ let $new = (0, _yuneta_gobj_js.createElement2)(flags_field(gobj, type, current));
74103
+ $holder.parentNode.replaceChild($new, $holder);
74104
+ (0, _yuneta_gobj_js.refresh_language)($new, i18next.t);
74105
+ }
74106
+ /***************************************************************
73973
74107
  * The flags of a column, as checkboxes that say what they do.
73974
74108
  *
73975
74109
  * Grouped the way they act, greyed when they are meaningless on
@@ -74043,7 +74177,7 @@ function read_flags($root) {
74043
74177
  * Offering a field that silently does something else is worse
74044
74178
  * than not offering it.
74045
74179
  ***************************************************************/
74046
- function open_column_form(gobj, topic, col, prefill) {
74180
+ function open_column_form(gobj, topic, col, prefill, changes) {
74047
74181
  let priv = gobj.priv;
74048
74182
  let record = prefill || col && col.record || {};
74049
74183
  let creating = !col;
@@ -74147,11 +74281,7 @@ function open_column_form(gobj, topic, col, prefill) {
74147
74281
  ]);
74148
74282
  let $type = $form.querySelector("[data-name=\"type\"]");
74149
74283
  $type.addEventListener("change", () => {
74150
- let $holder = $form.querySelector(".SCHEMA_FLAGS");
74151
- let current = read_flags($form);
74152
- let $new = (0, _yuneta_gobj_js.createElement2)(flags_field(gobj, $type.value, current));
74153
- $holder.parentNode.replaceChild($new, $holder);
74154
- (0, _yuneta_gobj_js.refresh_language)($new, i18next.t);
74284
+ redraw_flags(gobj, $form, $type.value, read_flags($form));
74155
74285
  });
74156
74286
  $form.addEventListener("change", (evt) => {
74157
74287
  let $box = evt.target.closest(".SCHEMA_FLAG_BOX");
@@ -74163,15 +74293,17 @@ function open_column_form(gobj, topic, col, prefill) {
74163
74293
  evt.stopPropagation();
74164
74294
  close_dialog(gobj);
74165
74295
  });
74296
+ let initial = column_form_state($form);
74297
+ if (changes) apply_form_changes(gobj, $form, changes);
74166
74298
  $form.querySelector(".SCHEMA_COL_FORM_SAVE").addEventListener("click", (evt) => {
74167
74299
  evt.stopPropagation();
74168
- let values = read_form($form);
74169
- values.flag = read_flags($form);
74300
+ let values = column_form_state($form);
74170
74301
  (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_SAVE_COLUMN", {
74171
74302
  topic: topic.name,
74172
74303
  col: creating ? "" : col.name,
74173
74304
  creating,
74174
74305
  values,
74306
+ changed: creating ? values : form_changes(initial, values),
74175
74307
  model_gen
74176
74308
  }, gobj);
74177
74309
  });
@@ -74191,7 +74323,7 @@ function stringify_field(value) {
74191
74323
  * actually has: a pkey naming no column is a topic that does
74192
74324
  * not open, and it is the first thing the validator looks for.
74193
74325
  ***************************************************************/
74194
- function open_topic_form(gobj, treedb, topic) {
74326
+ function open_topic_form(gobj, treedb, topic, changes) {
74195
74327
  let record = topic && topic.record || {};
74196
74328
  let creating = !topic;
74197
74329
  let col_names = topic ? topic.cols.map((c) => c.name) : [];
@@ -74286,12 +74418,16 @@ function open_topic_form(gobj, treedb, topic) {
74286
74418
  evt.stopPropagation();
74287
74419
  close_dialog(gobj);
74288
74420
  });
74421
+ let initial = read_form($form);
74422
+ if (changes) apply_form_changes(gobj, $form, changes);
74289
74423
  $form.querySelector(".SCHEMA_TOPIC_FORM_SAVE").addEventListener("click", (evt) => {
74290
74424
  evt.stopPropagation();
74425
+ let values = read_form($form);
74291
74426
  (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_SAVE_TOPIC", {
74292
74427
  topic: creating ? "" : topic.name,
74293
74428
  creating,
74294
- values: read_form($form),
74429
+ values,
74430
+ changed: creating ? values : form_changes(initial, values),
74295
74431
  model_gen
74296
74432
  }, gobj);
74297
74433
  });
@@ -75061,20 +75197,28 @@ function transport_dropped(gobj) {
75061
75197
  function end_load(gobj) {
75062
75198
  let priv = gobj.priv;
75063
75199
  let seg = priv.pending_seg;
75200
+ let reopen = priv.reopen_form;
75064
75201
  priv.pending_seg = null;
75202
+ priv.reopen_form = null;
75065
75203
  if (priv.dialog && priv.dialog_gen !== priv.model_gen) {
75066
- (0, _yuneta_gobj_js.log_warning)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: a dialog was open on the schemas the load replaced: closed`);
75204
+ if (!reopen || reopen.dialog !== priv.dialog) {
75205
+ reopen = null;
75206
+ (0, _yuneta_gobj_js.log_warning)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: a dialog was open on the schemas the load replaced: closed`);
75207
+ yui_shell_show_error(yui_shell_of(gobj), "the schemas were read again: open the dialog again", { t: i18next.t });
75208
+ }
75067
75209
  close_dialog(gobj);
75068
- yui_shell_show_error(yui_shell_of(gobj), "the schemas were read again: open the dialog again", { t: i18next.t });
75069
- }
75210
+ } else reopen = null;
75211
+ let ret = 0;
75070
75212
  if (priv.model) {
75071
- if (seg !== null) return apply_seg(gobj, seg);
75072
- return go(gobj, priv.treedb_id, priv.topic_name, priv.diagram, false);
75213
+ if (seg !== null) ret = apply_seg(gobj, seg);
75214
+ else ret = go(gobj, priv.treedb_id, priv.topic_name, priv.diagram, false);
75215
+ } else {
75216
+ if (seg !== null) adopt_seg(gobj, seg);
75217
+ (0, _yuneta_gobj_js.gobj_change_state)(gobj, "ST_IDLE");
75218
+ render(gobj);
75073
75219
  }
75074
- if (seg !== null) adopt_seg(gobj, seg);
75075
- (0, _yuneta_gobj_js.gobj_change_state)(gobj, "ST_IDLE");
75076
- render(gobj);
75077
- return 0;
75220
+ if (reopen) reopen_form(gobj, reopen);
75221
+ return ret;
75078
75222
  }
75079
75223
  /***************************************************************
75080
75224
  * The queue is done, or gave up. Either way the model is what
@@ -75099,7 +75243,11 @@ function end_writes(gobj, error) {
75099
75243
  render(gobj);
75100
75244
  publish_check(gobj);
75101
75245
  }
75102
- reload_if_owed(gobj);
75246
+ if (priv.refresh_after_write) {
75247
+ priv.refresh_after_write = false;
75248
+ priv.reload_after_write = false;
75249
+ request_model(gobj, false);
75250
+ } else reload_if_owed(gobj);
75103
75251
  return error ? -1 : 0;
75104
75252
  }
75105
75253
  /***************************************************************
@@ -75380,7 +75528,9 @@ function ac_mt_command_answer(gobj, event, kw, src) {
75380
75528
  priv.save_return = "";
75381
75529
  priv.save_wrote = false;
75382
75530
  (0, _yuneta_gobj_js.gobj_publish_event)(gobj, "EV_RECORD_WRITTEN", { treedb_name: (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "treedb_name") });
75383
- request_model(gobj, true);
75531
+ let refresh = priv.refresh_after_write;
75532
+ priv.refresh_after_write = false;
75533
+ request_model(gobj, !refresh);
75384
75534
  return 0;
75385
75535
  }
75386
75536
  }
@@ -75527,6 +75677,21 @@ function ac_refresh$1(gobj, event, kw, src) {
75527
75677
  return request_model(gobj, false);
75528
75678
  }
75529
75679
  /***************************************************************
75680
+ * EV_REFRESH while writes are in flight: the view's own Refresh
75681
+ * is busy then, so it is a host's (after a Save, say).
75682
+ *
75683
+ * Run when the writes END (end_writes()), not now. Run now, the
75684
+ * load replaced ST_SAVING and what was still queued was never
75685
+ * sent, with no word: nothing but the end of what is in flight
75686
+ * moves this view out of ST_LOADING or ST_SAVING.
75687
+ ***************************************************************/
75688
+ function ac_refresh_after_the_writes(gobj, event, kw, src) {
75689
+ let priv = gobj.priv;
75690
+ (0, _yuneta_gobj_js.log_warning)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: ${event} while writes are in flight: the schemas are read again when the writes end`);
75691
+ priv.refresh_after_write = true;
75692
+ return 0;
75693
+ }
75694
+ /***************************************************************
75530
75695
  * What a DIALOG sends while the model is being read again: the
75531
75696
  * Save of a form left open when the reload started, a
75532
75697
  * confirmation, an import. The loading screen took the rest of
@@ -75701,7 +75866,13 @@ function ac_save_column(gobj, event, kw, src) {
75701
75866
  let topic = find_topic(priv.model, priv.treedb_id, kw.topic);
75702
75867
  let col = kw.creating ? null : find_col(priv.model, priv.treedb_id, kw.topic, kw.col);
75703
75868
  if (refuse_if_readonly(gobj, event)) return -1;
75704
- if (reload_first(gobj, event)) return -1;
75869
+ if (reload_first(gobj, event, {
75870
+ form: "column",
75871
+ topic: kw.topic,
75872
+ col: kw.col,
75873
+ creating: !!kw.creating,
75874
+ changed: kw.changed || {}
75875
+ })) return -1;
75705
75876
  if (built_on_a_replaced_model(gobj, event, kw)) return -1;
75706
75877
  if (!topic || !kw.creating && !col) {
75707
75878
  (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: ${event} names no column: ${kw.topic}.${kw.col}`);
@@ -75829,7 +76000,13 @@ function ac_save_topic(gobj, event, kw, src) {
75829
76000
  let treedb = current_treedb(gobj);
75830
76001
  let topic = kw.creating ? null : find_topic(priv.model, priv.treedb_id, kw.topic);
75831
76002
  if (refuse_if_readonly(gobj, event)) return -1;
75832
- if (reload_first(gobj, event)) return -1;
76003
+ if (reload_first(gobj, event, {
76004
+ form: "topic",
76005
+ topic: kw.topic,
76006
+ col: "",
76007
+ creating: !!kw.creating,
76008
+ changed: kw.changed || {}
76009
+ })) return -1;
75833
76010
  if (built_on_a_replaced_model(gobj, event, kw)) return -1;
75834
76011
  if (!treedb || !kw.creating && !topic) {
75835
76012
  (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: ${event} names no topic: ${kw.topic}`);
@@ -76292,7 +76469,11 @@ function create_gclass$1(gclass_name) {
76292
76469
  null
76293
76470
  ]
76294
76471
  ])],
76295
- ["ST_SAVING", COMMON.slice()]
76472
+ ["ST_SAVING", COMMON.filter((a) => a[0] !== "EV_REFRESH").concat([[
76473
+ "EV_REFRESH",
76474
+ ac_refresh_after_the_writes,
76475
+ null
76476
+ ]])]
76296
76477
  ];
76297
76478
  const event_types = [
76298
76479
  ["EV_MT_COMMAND_ANSWER", _yuneta_gobj_js.event_flag_t.EVF_PUBLIC_EVENT],