@yuneta/gobj-ui 7.25.5 → 7.25.7

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
@@ -2154,6 +2154,41 @@ else.
2154
2154
  - In: `EV_SHOW` (`{subpath}` — the tail it owns is `<treedb>[/<topic>]` or
2155
2155
  `<treedb>/diagram`), `EV_HIDE`, `EV_TRANSPORT_STATE`, `EV_REFRESH`,
2156
2156
  `EV_LANGUAGE_CHANGED`, `EV_MT_COMMAND_ANSWER`.
2157
+ - A **drop** of the transport — `EV_TRANSPORT_STATE {connected: false}`, or a
2158
+ failed answer that arrives while the transport is out of `ST_SESSION` — ends
2159
+ the load or the write it cut, and the model is asked again on the next
2160
+ `{connected: true}`. A write cut this way is shown as
2161
+ `the connection dropped during the write` (an i18n key the consumer's
2162
+ locales carry). Each request carries its round in `__md_command__`
2163
+ (`round` / `write`), so an answer of what the drop cut is ignored with a
2164
+ warning. The drop itself is logged ONCE; the failures a routing adapter
2165
+ settles for the same drop are that one event and are not logged again.
2166
+ - **Nothing moves the editor out of `ST_LOADING` or `ST_SAVING` but the end of
2167
+ what is in flight** (7.25.7). An `EV_SHOW` that arrives meanwhile waits, the
2168
+ last one wins, and it is applied when the load lands, fails or is cut, or
2169
+ when the write ends. 7.25.6 let it move a RELOAD out of `ST_LOADING`: every
2170
+ answer of that load was then "for a load that is over", and the next write
2171
+ patched the records the reload had emptied into a model with no treedb.
2172
+ With no model and no load, `EV_SHOW` keeps the position and stays in
2173
+ `ST_IDLE`; the next load goes there.
2174
+
2175
+ ```js
2176
+ gobj_send_event(editor, "EV_REFRESH", {}, host); // ST_LOADING
2177
+ gobj_send_event(editor, "EV_SHOW", {subpath: "db"}, host); // waits
2178
+ // ... the three `nodes` answers land -> ST_TOPICS of `db`
2179
+ ```
2180
+ - A write this view gave up on (a drop, a deadline) whose answer still says it
2181
+ was DONE makes the editor read the model again (at once, after the write in
2182
+ flight, or on the reconnect). Through a routing adapter that settled the
2183
+ write on its OWN deadline the late answer does not reach the view -- the
2184
+ adapter echoes a node event instead, which this view does not hear -- and
2185
+ Refresh is what reads the store.
2186
+
2187
+ ```js
2188
+ // the host forwards its session edges; nothing else is needed
2189
+ gobj_send_event(editor, "EV_TRANSPORT_STATE", {connected: false}, host);
2190
+ gobj_send_event(editor, "EV_TRANSPORT_STATE", {connected: true}, host);
2191
+ ```
2157
2192
  - Out: `EV_POSITION_CHANGED` (`{subpath}` — the host writes the url; this view
2158
2193
  navigates nothing itself), `EV_RECORD_WRITTEN` (whoever owns the Apply needs
2159
2194
  to know the yuno has not re-read its schema yet), `EV_SCHEMA_CHECKED`
@@ -3025,6 +3025,14 @@ function maybe_apply_translator($node, opts) {
3025
3025
  * Non-blocking, auto-dismiss after `opts.timeout` ms (default
3026
3026
  * 5000). `opts.timeout = 0` disables auto-dismiss.
3027
3027
  * Returns `{ close() }` so callers can dismiss programmatically.
3028
+ *
3029
+ * ONE toast per message on screen: a string message of the same
3030
+ * kind as a toast still showing is not stacked under it -- that
3031
+ * toast is kept, its time starts again, and its handle is
3032
+ * returned. One close of a transport settles every request it
3033
+ * cut, each view shows its failure, and the operator got a
3034
+ * column of identical "the connection dropped". A message built
3035
+ * of nodes is never merged: its text is not a key.
3028
3036
  ***************************************************************/
3029
3037
  function show_notification(shell, kind, message, opts) {
3030
3038
  let $layer = notification_layer(shell);
@@ -3032,6 +3040,13 @@ function show_notification(shell, kind, message, opts) {
3032
3040
  (0, _yuneta_gobj_js.log_warning)("yui_shell_show_*: shell has no notification layer");
3033
3041
  return { close: () => {} };
3034
3042
  }
3043
+ let key = typeof message === "string" ? `${kind}\u0000${message}` : null;
3044
+ if (key !== null) {
3045
+ for (let $shown of Array.from($layer.children)) if ($shown.__toast_key__ === key && $shown.__toast__) {
3046
+ $shown.__toast__.restart();
3047
+ return $shown.__toast__.handle;
3048
+ }
3049
+ }
3035
3050
  let p_attrs = typeof message === "string" ? { i18n: message } : {};
3036
3051
  let $note = (0, _yuneta_gobj_js.createElement2)([
3037
3052
  "div",
@@ -3068,8 +3083,21 @@ function show_notification(shell, kind, message, opts) {
3068
3083
  let $del = $note.querySelector(".delete");
3069
3084
  if ($del) $del.addEventListener("click", close);
3070
3085
  let timeout = opts && opts.timeout != null ? opts.timeout : 5e3;
3071
- if (timeout > 0) timeout_id = setTimeout(close, timeout);
3072
- return { close };
3086
+ let restart = function() {
3087
+ if (timeout_id) {
3088
+ clearTimeout(timeout_id);
3089
+ timeout_id = null;
3090
+ }
3091
+ if (timeout > 0) timeout_id = setTimeout(close, timeout);
3092
+ };
3093
+ restart();
3094
+ let handle = { close };
3095
+ $note.__toast_key__ = key;
3096
+ $note.__toast__ = {
3097
+ handle,
3098
+ restart
3099
+ };
3100
+ return handle;
3073
3101
  }
3074
3102
  function yui_shell_show_info(shell, message, opts) {
3075
3103
  return show_notification(shell, "info", message, opts);
@@ -72101,6 +72129,12 @@ function write_options(op) {
72101
72129
  * `update-node` / `delete-node` on the treedb service, and it may
72102
72130
  * be a routing adapter two hops away (the agent console's
72103
72131
  * C_AGENT_TREEDB_LINK) — this view cannot tell, and must not.
72132
+ * A DROP of it (the host's EV_TRANSPORT_STATE, or a failure that
72133
+ * arrives while the transport is out of session) ends the load or
72134
+ * the write it cut, and the model is asked again when the session
72135
+ * is back. Every request carries its round (`round` for a load,
72136
+ * `write` for a write) in `__md_command__`, so an answer of what
72137
+ * was cut is ignored instead of counted in the next one.
72104
72138
  *
72105
72139
  * Copyright (c) 2026, ArtGins.
72106
72140
  * All Rights Reserved.
@@ -72133,6 +72167,7 @@ var PRIVATE_DATA$1 = {
72133
72167
  model: null,
72134
72168
  pending: 0,
72135
72169
  load_error: "",
72170
+ load_round: 0,
72136
72171
  treedb_id: "",
72137
72172
  topic_name: "",
72138
72173
  diagram: false,
@@ -72146,7 +72181,12 @@ var PRIVATE_DATA$1 = {
72146
72181
  save_queue: null,
72147
72182
  save_return: "",
72148
72183
  save_wrote: false,
72184
+ write_tag: 0,
72149
72185
  connected: false,
72186
+ reload_on_open: false,
72187
+ dropped_round: 0,
72188
+ dropped_write: 0,
72189
+ reload_after_write: false,
72150
72190
  pending_seg: null,
72151
72191
  import_plan: null,
72152
72192
  import_pane: null,
@@ -72317,6 +72357,7 @@ function request_model(gobj) {
72317
72357
  };
72318
72358
  priv.load_error = "";
72319
72359
  priv.pending = 0;
72360
+ priv.load_round++;
72320
72361
  (0, _yuneta_gobj_js.gobj_change_state)(gobj, "ST_LOADING");
72321
72362
  show_notice(gobj, "loading the schemas");
72322
72363
  for (let topic_name of [
@@ -72326,7 +72367,7 @@ function request_model(gobj) {
72326
72367
  ]) if (remote_command(gobj, "nodes", {
72327
72368
  topic_name,
72328
72369
  options: { list_dict: true }
72329
- }) === 0) priv.pending++;
72370
+ }, { round: priv.load_round }) === 0) priv.pending++;
72330
72371
  if (priv.pending === 0) {
72331
72372
  priv.load_error = (0, i18next.t)("cannot reach the treedb");
72332
72373
  (0, _yuneta_gobj_js.gobj_change_state)(gobj, "ST_IDLE");
@@ -72344,7 +72385,7 @@ function request_model(gobj) {
72344
72385
  * answer says which topic it is about, since the command is
72345
72386
  * `nodes` for all three.
72346
72387
  ***************************************************************/
72347
- function remote_command(gobj, command, kw) {
72388
+ function remote_command(gobj, command, kw, tag) {
72348
72389
  let remote = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "gobj_remote_yuno");
72349
72390
  let treedb_name = (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "treedb_name");
72350
72391
  if (!remote) {
@@ -72355,11 +72396,11 @@ function remote_command(gobj, command, kw) {
72355
72396
  service: treedb_name,
72356
72397
  treedb_name
72357
72398
  }, kw || {});
72358
- full_kw.__md_command__ = {
72399
+ full_kw.__md_command__ = Object.assign({
72359
72400
  purpose: PURPOSE,
72360
72401
  topic_name: kw && kw.topic_name || "",
72361
72402
  record_id: kw && kw.record_id || ""
72362
- };
72403
+ }, tag || {});
72363
72404
  delete full_kw.record_id;
72364
72405
  let ret = (0, _yuneta_gobj_js.gobj_command)(remote, command, full_kw, gobj);
72365
72406
  if (ret) {
@@ -74590,12 +74631,92 @@ function run_next_write(gobj) {
74590
74631
  let write = priv.save_queue[0];
74591
74632
  let command = write_command(write.op);
74592
74633
  let options = write_options(write.op);
74634
+ priv.write_tag++;
74593
74635
  if (remote_command(gobj, command, {
74594
74636
  topic_name: write.topic_name,
74595
74637
  record: write.record,
74596
74638
  options,
74597
74639
  record_id: write.record.id || ""
74598
- }) < 0) return end_writes(gobj, (0, i18next.t)("cannot reach the treedb"));
74640
+ }, { write: priv.write_tag }) < 0) return end_writes(gobj, (0, i18next.t)("cannot reach the treedb"));
74641
+ return 0;
74642
+ }
74643
+ /***************************************************************
74644
+ * Does the transport say it is in session? The state the library
74645
+ * reads off every transport (C_IEVENT_CLI and the routing adapters
74646
+ * name it the same).
74647
+ ***************************************************************/
74648
+ function transport_in_session(gobj) {
74649
+ let remote = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "gobj_remote_yuno");
74650
+ return !!remote && (0, _yuneta_gobj_js.gobj_current_state)(remote) === "ST_SESSION";
74651
+ }
74652
+ /***************************************************************
74653
+ * Is this answer of the load in flight, or of the write in
74654
+ * flight? Each request carries its round in `__md_command__`, and
74655
+ * one the drop abandoned (or a deadline settled) can still come
74656
+ * back: counted, it ended the next load with the old data or
74657
+ * moved the next write's queue.
74658
+ *
74659
+ * The state is part of the question, and that is only true
74660
+ * because NOTHING moves the editor out of ST_LOADING or ST_SAVING
74661
+ * but the end of what is in flight: a position the host sends
74662
+ * meanwhile waits in `pending_seg` (ac_show()). 7.25.6 let EV_SHOW
74663
+ * move a RELOAD out of ST_LOADING, and every answer of it was then
74664
+ * "for a load that is over": the records it had emptied stayed
74665
+ * empty, and the next write patched them into a model with no
74666
+ * treedb at all.
74667
+ ***************************************************************/
74668
+ function is_current_load(gobj, md) {
74669
+ return (0, _yuneta_gobj_js.gobj_current_state)(gobj) === "ST_LOADING" && (0, _yuneta_gobj_js.kw_get_int)(gobj, md, "round", 0, 0) === gobj.priv.load_round;
74670
+ }
74671
+ function is_current_write(gobj, md) {
74672
+ return (0, _yuneta_gobj_js.gobj_current_state)(gobj) === "ST_SAVING" && (0, _yuneta_gobj_js.kw_get_int)(gobj, md, "write", 0, 0) === gobj.priv.write_tag;
74673
+ }
74674
+ /***************************************************************
74675
+ * The transport dropped under a load or a write: its answers
74676
+ * will never come. The load goes back to the screen it replaced
74677
+ * (or to "not connected" when there was none), the write ends
74678
+ * with the drop said -- whether it landed is unknown -- and both
74679
+ * mark the model to be asked again when the session is back.
74680
+ * Bumping the round makes an answer of what was cut stale.
74681
+ ***************************************************************/
74682
+ function transport_dropped(gobj) {
74683
+ let priv = gobj.priv;
74684
+ let state = (0, _yuneta_gobj_js.gobj_current_state)(gobj);
74685
+ priv.reload_on_open = true;
74686
+ if (state === "ST_SAVING") {
74687
+ (0, _yuneta_gobj_js.log_warning)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: the transport dropped during a write: the model is read again on the reconnect`);
74688
+ priv.dropped_write = priv.write_tag;
74689
+ priv.write_tag++;
74690
+ return end_writes(gobj, "the connection dropped during the write");
74691
+ }
74692
+ if (state === "ST_LOADING") {
74693
+ (0, _yuneta_gobj_js.log_warning)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: the transport dropped during the load: the model is read again on the reconnect`);
74694
+ priv.dropped_round = priv.load_round;
74695
+ priv.load_round++;
74696
+ priv.pending = 0;
74697
+ priv.load_error = "";
74698
+ return end_load(gobj);
74699
+ }
74700
+ return 0;
74701
+ }
74702
+ /***************************************************************
74703
+ * A load is over -- landed, failed or cut -- and the editor goes
74704
+ * where the operator is: the position the host sent while it was
74705
+ * in flight (ac_show() kept it), or the one it had. Without a
74706
+ * model there is no screen to go to: the position is kept for the
74707
+ * next load, and the editor says why it is empty.
74708
+ ***************************************************************/
74709
+ function end_load(gobj) {
74710
+ let priv = gobj.priv;
74711
+ let seg = priv.pending_seg;
74712
+ priv.pending_seg = null;
74713
+ if (priv.model) {
74714
+ if (seg !== null) return apply_seg(gobj, seg);
74715
+ return go(gobj, priv.treedb_id, priv.topic_name, priv.diagram, false);
74716
+ }
74717
+ if (seg !== null) adopt_seg(gobj, seg);
74718
+ (0, _yuneta_gobj_js.gobj_change_state)(gobj, "ST_IDLE");
74719
+ render(gobj);
74599
74720
  return 0;
74600
74721
  }
74601
74722
  /***************************************************************
@@ -74618,7 +74739,7 @@ function end_writes(gobj, error) {
74618
74739
  apply_seg(gobj, seg);
74619
74740
  if (priv.save_wrote) (0, _yuneta_gobj_js.gobj_publish_event)(gobj, "EV_RECORD_WRITTEN", { treedb_name: (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "treedb_name") });
74620
74741
  priv.save_wrote = false;
74621
- return 0;
74742
+ return reload_if_owed(gobj);
74622
74743
  }
74623
74744
  }
74624
74745
  if (error) yui_shell_show_error(yui_shell_of(gobj), error, { t: i18next.t });
@@ -74626,9 +74747,25 @@ function end_writes(gobj, error) {
74626
74747
  priv.save_wrote = false;
74627
74748
  render(gobj);
74628
74749
  publish_check(gobj);
74750
+ reload_if_owed(gobj);
74629
74751
  return error ? -1 : 0;
74630
74752
  }
74631
74753
  /***************************************************************
74754
+ * A write given up earlier turned out DONE while another one was
74755
+ * in flight (see ac_mt_command_answer()): the model is read again
74756
+ * now that nothing is.
74757
+ ***************************************************************/
74758
+ function reload_if_owed(gobj) {
74759
+ let priv = gobj.priv;
74760
+ if (!priv.reload_after_write) return 0;
74761
+ priv.reload_after_write = false;
74762
+ if (!transport_in_session(gobj)) {
74763
+ priv.reload_on_open = true;
74764
+ return 0;
74765
+ }
74766
+ return request_model(gobj);
74767
+ }
74768
+ /***************************************************************
74632
74769
  * The record a column form produces.
74633
74770
  *
74634
74771
  * Everything the store needs and the operator did not type is
@@ -74696,7 +74833,7 @@ function go(gobj, treedb_id, topic_name, diagram, mirror) {
74696
74833
  priv.topic_name = topic_name || "";
74697
74834
  priv.diagram = !!diagram;
74698
74835
  let state = "ST_TREEDBS";
74699
- if (!priv.model) state = "ST_LOADING";
74836
+ if (!priv.model) state = "ST_IDLE";
74700
74837
  else if (priv.model.treedbs.length === 0) state = "ST_EMPTY";
74701
74838
  else if (priv.treedb_id && priv.diagram) state = "ST_DIAGRAM";
74702
74839
  else if (priv.treedb_id && priv.topic_name) state = "ST_COLUMNS";
@@ -74723,12 +74860,39 @@ function position_seg(gobj) {
74723
74860
  * empty screen: a shared link outlives the schema it points at.
74724
74861
  ***************************************************************/
74725
74862
  function apply_seg(gobj, seg) {
74863
+ let pos = seg_position(seg);
74864
+ return go(gobj, pos.treedb_id, pos.topic_name, pos.diagram, false);
74865
+ }
74866
+ function seg_position(seg) {
74726
74867
  let parts = String(seg || "").split("/").filter((x) => x.length > 0);
74727
74868
  let treedb_id = parts[0] || "";
74728
74869
  let tail = parts[1] || "";
74729
- if (!treedb_id) return go(gobj, "", "", false, false);
74730
- if (tail === DIAGRAM_SEG) return go(gobj, treedb_id, "", true, false);
74731
- return go(gobj, treedb_id, tail, false, false);
74870
+ if (!treedb_id) return {
74871
+ treedb_id: "",
74872
+ topic_name: "",
74873
+ diagram: false
74874
+ };
74875
+ if (tail === DIAGRAM_SEG) return {
74876
+ treedb_id,
74877
+ topic_name: "",
74878
+ diagram: true
74879
+ };
74880
+ return {
74881
+ treedb_id,
74882
+ topic_name: tail,
74883
+ diagram: false
74884
+ };
74885
+ }
74886
+ /***************************************************************
74887
+ * Where the operator is, taken WITHOUT a screen: there is no model
74888
+ * to draw it on yet, and the load that brings one goes there.
74889
+ ***************************************************************/
74890
+ function adopt_seg(gobj, seg) {
74891
+ let priv = gobj.priv;
74892
+ let pos = seg_position(seg);
74893
+ priv.treedb_id = pos.treedb_id;
74894
+ priv.topic_name = pos.topic_name;
74895
+ priv.diagram = pos.diagram;
74732
74896
  }
74733
74897
  /***************************
74734
74898
  * Actions
@@ -74750,25 +74914,39 @@ function ac_mt_command_answer(gobj, event, kw, src) {
74750
74914
  let kw_command = (0, _yuneta_gobj_js.kw_get_dict)(gobj, __command__, "kw", {}, 0);
74751
74915
  let md = (0, _yuneta_gobj_js.is_object)(kw_command.__md_command__) ? kw_command.__md_command__ : kw_command;
74752
74916
  if ((0, _yuneta_gobj_js.kw_get_str)(gobj, md, "purpose", "", 0) !== PURPOSE) return 0;
74917
+ if (result < 0 && !transport_in_session(gobj) && ((0, _yuneta_gobj_js.gobj_current_state)(gobj) === "ST_LOADING" || (0, _yuneta_gobj_js.gobj_current_state)(gobj) === "ST_SAVING")) {
74918
+ if (command === "nodes" && is_current_load(gobj, md) || command !== "nodes" && is_current_write(gobj, md)) return transport_dropped(gobj);
74919
+ }
74753
74920
  if (command === "nodes") {
74754
74921
  let topic_name = (0, _yuneta_gobj_js.kw_get_str)(gobj, md, "topic_name", "", 0);
74922
+ if (!is_current_load(gobj, md)) {
74923
+ if (result < 0 && (0, _yuneta_gobj_js.kw_get_int)(gobj, md, "round", 0, 0) === priv.dropped_round) return 0;
74924
+ (0, _yuneta_gobj_js.log_warning)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: 'nodes' of '${topic_name}' answered for a load that is over: ignored`);
74925
+ return 0;
74926
+ }
74755
74927
  priv.pending--;
74756
74928
  if (result < 0) priv.load_error = comment || (0, i18next.t)("cannot load the schemas");
74757
74929
  else if (priv.records && Array.isArray(priv.records[topic_name])) priv.records[topic_name] = Array.isArray(data) ? data : [];
74758
74930
  if (priv.pending > 0) return 0;
74759
74931
  if (priv.load_error) {
74760
74932
  priv.model = null;
74761
- (0, _yuneta_gobj_js.gobj_change_state)(gobj, "ST_IDLE");
74762
- render(gobj);
74763
- return 0;
74933
+ return end_load(gobj);
74764
74934
  }
74765
74935
  priv.order_undo = {};
74766
74936
  build_model(gobj);
74767
74937
  start_measuring(gobj);
74768
- return go(gobj, priv.treedb_id, priv.topic_name, priv.diagram, false);
74938
+ return end_load(gobj);
74769
74939
  }
74770
74940
  if (command === "update-node" || command === "delete-node") {
74771
74941
  let topic_name = (0, _yuneta_gobj_js.kw_get_str)(gobj, md, "topic_name", "", 0);
74942
+ if (!is_current_write(gobj, md)) {
74943
+ if (result < 0 && (0, _yuneta_gobj_js.kw_get_int)(gobj, md, "write", 0, 0) === priv.dropped_write) return 0;
74944
+ if (result < 0) {
74945
+ (0, _yuneta_gobj_js.log_warning)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: '${command}' of '${topic_name}' answered for a write that is over (result ${result}): ignored`);
74946
+ return 0;
74947
+ }
74948
+ return late_write_done(gobj, command, topic_name);
74949
+ }
74772
74950
  if (result < 0) return end_writes(gobj, comment || (0, i18next.t)("the treedb refused the write"));
74773
74951
  priv.save_wrote = true;
74774
74952
  if (command === "delete-node") forget_record(gobj, topic_name, (0, _yuneta_gobj_js.kw_get_str)(gobj, md, "record_id", "", 0));
@@ -74793,6 +74971,35 @@ function ac_mt_command_answer(gobj, event, kw, src) {
74793
74971
  return 0;
74794
74972
  }
74795
74973
  /***************************************************************
74974
+ * A write this view gave up on (a drop, a deadline) answers that
74975
+ * it was DONE: the store holds a change the model does not show.
74976
+ * The model is read again -- now, or when what is in flight ends,
74977
+ * or on the reconnect. A load in flight needs nothing: it was
74978
+ * asked after the write, on the same transport, and reads it.
74979
+ *
74980
+ * Only when the answer reaches this view. A routing adapter that
74981
+ * settled the write on its own deadline (gui_agent's
74982
+ * C_AGENT_TREEDB_LINK) does not deliver its late answer again --
74983
+ * the view was answered once, as failed -- and echoes the node
74984
+ * event instead, which this view does not hear: there, Refresh is
74985
+ * what reads the store.
74986
+ ***************************************************************/
74987
+ function late_write_done(gobj, command, topic_name) {
74988
+ let priv = gobj.priv;
74989
+ let state = (0, _yuneta_gobj_js.gobj_current_state)(gobj);
74990
+ (0, _yuneta_gobj_js.log_warning)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: '${command}' of '${topic_name}' was given up, and it was DONE: the model is read again`);
74991
+ if (state === "ST_LOADING") return 0;
74992
+ if (state === "ST_SAVING") {
74993
+ priv.reload_after_write = true;
74994
+ return 0;
74995
+ }
74996
+ if (!transport_in_session(gobj)) {
74997
+ priv.reload_on_open = true;
74998
+ return 0;
74999
+ }
75000
+ return request_model(gobj);
75001
+ }
75002
+ /***************************************************************
74796
75003
  * Remember which topic this write belongs to, which is what
74797
75004
  * "its version still has to move" is asked about.
74798
75005
  ***************************************************************/
@@ -74812,7 +75019,8 @@ function mark_written(gobj, topic_name, record) {
74812
75019
  function ac_show(gobj, event, kw, src) {
74813
75020
  let priv = gobj.priv;
74814
75021
  let seg = kw && kw.subpath || "";
74815
- if ((0, _yuneta_gobj_js.gobj_current_state)(gobj) === "ST_SAVING") {
75022
+ let state = (0, _yuneta_gobj_js.gobj_current_state)(gobj);
75023
+ if (state === "ST_SAVING" || state === "ST_LOADING") {
74816
75024
  priv.pending_seg = seg;
74817
75025
  return 0;
74818
75026
  }
@@ -74828,14 +75036,28 @@ function ac_hide(gobj, event, kw, src) {
74828
75036
  return 0;
74829
75037
  }
74830
75038
  /***************************************************************
74831
- * The session came up or went down. The model stays either way
74832
- * — a schema does not change under you — but a first load may
74833
- * have had no transport to run on.
75039
+ * The session came up or went down. The model stays -- a schema
75040
+ * does not change under you -- unless the drop cut a load or a
75041
+ * write (transport_dropped()): then it is asked again on the way
75042
+ * back, as it is when a first load had no transport to run on.
75043
+ *
75044
+ * Nothing else ends a load or a write the drop cut: their answers
75045
+ * will never come, and waiting in ST_LOADING skipped the reload
75046
+ * itself (M-1 of the independent review of 7.25.4).
74834
75047
  ***************************************************************/
74835
75048
  function ac_transport_state(gobj, event, kw, src) {
74836
75049
  let priv = gobj.priv;
75050
+ let state = (0, _yuneta_gobj_js.gobj_current_state)(gobj);
74837
75051
  priv.connected = !!(kw && kw.connected);
74838
- if (priv.connected && !priv.model && (0, _yuneta_gobj_js.gobj_current_state)(gobj) !== "ST_LOADING") return request_model(gobj);
75052
+ if (!priv.connected) {
75053
+ if (state === "ST_LOADING" || state === "ST_SAVING") return transport_dropped(gobj);
75054
+ render_toolbar(gobj);
75055
+ return 0;
75056
+ }
75057
+ if ((!priv.model || priv.reload_on_open) && state !== "ST_LOADING" && state !== "ST_SAVING") {
75058
+ priv.reload_on_open = false;
75059
+ return request_model(gobj);
75060
+ }
74839
75061
  render_toolbar(gobj);
74840
75062
  return 0;
74841
75063
  }