@yuneta/gobj-ui 7.25.15 → 7.25.17
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 +106 -4
- package/dist/gobj-ui.cjs.js +274 -66
- package/dist/gobj-ui.es.js +274 -66
- package/package.json +2 -2
- package/src/c_g6_nodes_tree.js +37 -4
- package/src/c_yui_schema_editor.js +280 -47
- package/src/c_yui_treedb_graph.js +22 -6
- package/src/form_busy.test.js +1 -1
- package/src/form_writes_in_flight.test.js +1 -1
- package/src/g6_graphs_write_owed.wiring.test.js +200 -0
- package/src/host_drafts.test.js +2 -2
- package/src/schema_editor_drafts.wiring.test.js +1 -1
- package/src/schema_editor_reload.wiring.test.js +313 -4
- package/src/shell_modals.js +29 -8
- package/src/shell_toast_once.test.js +63 -0
- package/src/table_cell_text.test.js +1 -1
- package/src/treedb_graph_writes.wiring.test.js +8 -1
- package/src/treedb_topics_transport.wiring.test.js +1 -1
- package/src/yui_install.js +14 -3
- package/src/yui_install.test.js +23 -0
package/src/c_g6_nodes_tree.js
CHANGED
|
@@ -493,6 +493,7 @@ let PRIVATE_DATA = {
|
|
|
493
493
|
__graphs__: [], // Rows of __graphs__
|
|
494
494
|
_graph_properties: {}, // topic_name → {nodes: {node_id: {x,y,size,...}}}
|
|
495
495
|
_saved_graph_properties: {}, // the same, as the BACKEND has it: what a Save compares against
|
|
496
|
+
_graphs_writes_owed: {}, // topic_name → true: a __graphs__ write the host said did not land
|
|
496
497
|
yet_showed: false,
|
|
497
498
|
edit_mode: false,
|
|
498
499
|
operation_mode: null,
|
|
@@ -3283,6 +3284,9 @@ function save_topic_graph_properties(gobj, topic_name)
|
|
|
3283
3284
|
* The host says when one does NOT land (EV_GRAPHS_WRITE_REFUSED),
|
|
3284
3285
|
* and the next Save writes the topic again. */
|
|
3285
3286
|
priv._saved_graph_properties[topic_name] = json_deep_copy(properties);
|
|
3287
|
+
/* Paid: this is the write that was owed, if one was. Refused
|
|
3288
|
+
* again, EV_GRAPHS_WRITE_REFUSED owes it again. */
|
|
3289
|
+
delete priv._graphs_writes_owed[topic_name];
|
|
3286
3290
|
|
|
3287
3291
|
let kw_update = {
|
|
3288
3292
|
treedb_name: priv.treedb_name,
|
|
@@ -3388,9 +3392,16 @@ function update_history_buttons(gobj)
|
|
|
3388
3392
|
disableElements($container, ".EV_HISTORY_UNDO");
|
|
3389
3393
|
set_active_state($container, ".EV_HISTORY_UNDO", false);
|
|
3390
3394
|
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3395
|
+
/* No more undos: the graph is back to what was loaded
|
|
3396
|
+
* or saved -- unless a write of it did not land. That
|
|
3397
|
+
* one is still owed, and only a Save pays it. */
|
|
3398
|
+
if(graphs_write_is_owed(gobj)) {
|
|
3399
|
+
enableElements($container, ".EV_SAVE_GRAPH");
|
|
3400
|
+
set_submit_state($container, ".EV_SAVE_GRAPH", true);
|
|
3401
|
+
} else {
|
|
3402
|
+
disableElements($container, ".EV_SAVE_GRAPH");
|
|
3403
|
+
set_submit_state($container, ".EV_SAVE_GRAPH", false);
|
|
3404
|
+
}
|
|
3394
3405
|
}
|
|
3395
3406
|
} else {
|
|
3396
3407
|
disableElements($container, ".EV_HISTORY_REDO");
|
|
@@ -4763,6 +4774,17 @@ function run_preview_undo(gobj, key)
|
|
|
4763
4774
|
}
|
|
4764
4775
|
}
|
|
4765
4776
|
|
|
4777
|
+
/************************************************************
|
|
4778
|
+
* Is a `__graphs__` write owed? The host said one did not land
|
|
4779
|
+
* (EV_GRAPHS_WRITE_REFUSED), and no Save has written that topic
|
|
4780
|
+
* since. The Save button stays lit while it is: the history
|
|
4781
|
+
* knows nothing of it, so `canUndo()` alone would put it out.
|
|
4782
|
+
************************************************************/
|
|
4783
|
+
function graphs_write_is_owed(gobj)
|
|
4784
|
+
{
|
|
4785
|
+
return Object.keys(gobj.priv._graphs_writes_owed).length > 0;
|
|
4786
|
+
}
|
|
4787
|
+
|
|
4766
4788
|
function mark_graph_dirty(gobj)
|
|
4767
4789
|
{
|
|
4768
4790
|
let $container = gobj_read_attr(gobj, "$container");
|
|
@@ -10346,6 +10368,11 @@ function ac_clear_data(gobj, event, kw, src)
|
|
|
10346
10368
|
|
|
10347
10369
|
gobj_write_attr(gobj, "records", {});
|
|
10348
10370
|
|
|
10371
|
+
/* A write owed is owed on the arrangement being thrown away: the
|
|
10372
|
+
* reload brings what the backend holds, and that is what the view
|
|
10373
|
+
* will hold. */
|
|
10374
|
+
priv._graphs_writes_owed = {};
|
|
10375
|
+
|
|
10349
10376
|
/* The node counters belong to the data that is going away: a refresh
|
|
10350
10377
|
* that kept them would decide the layout on the previous treedb. */
|
|
10351
10378
|
priv._nodes_total = 0;
|
|
@@ -10495,10 +10522,13 @@ function ac_save_graph(gobj, event, kw, src)
|
|
|
10495
10522
|
let history = graph_get_plugin(gobj, "history");
|
|
10496
10523
|
if(history) {
|
|
10497
10524
|
history.clear();
|
|
10498
|
-
update_history_buttons(gobj);
|
|
10499
10525
|
}
|
|
10500
10526
|
|
|
10501
10527
|
save_geometry(gobj); // Publish an EV_UPDATE_NODE of each node
|
|
10528
|
+
|
|
10529
|
+
/* After the writes: they pay a write owed, and the buttons
|
|
10530
|
+
* are repainted from what is still owed. */
|
|
10531
|
+
update_history_buttons(gobj);
|
|
10502
10532
|
}
|
|
10503
10533
|
|
|
10504
10534
|
return 0;
|
|
@@ -10526,6 +10556,9 @@ function ac_graphs_write_refused(gobj, event, kw, src)
|
|
|
10526
10556
|
log_warning(`${gobj_short_name(gobj)}: the arrangement of '${topic_name}' ` +
|
|
10527
10557
|
`was not saved: the next Save writes it again`);
|
|
10528
10558
|
forget_refused_graphs_write(priv._saved_graph_properties, topic_name);
|
|
10559
|
+
/* Owed until a Save writes it: update_history_buttons() keeps
|
|
10560
|
+
* the Save lit while it is, in edition and on entering it. */
|
|
10561
|
+
priv._graphs_writes_owed[topic_name] = true;
|
|
10529
10562
|
if(priv.edit_mode) {
|
|
10530
10563
|
mark_graph_dirty(gobj);
|
|
10531
10564
|
}
|
|
@@ -117,6 +117,7 @@ import {
|
|
|
117
117
|
kw_get_int,
|
|
118
118
|
kw_get_dict,
|
|
119
119
|
is_object,
|
|
120
|
+
json_deep_copy,
|
|
120
121
|
is_array,
|
|
121
122
|
empty_string,
|
|
122
123
|
} from "@yuneta/gobj-js";
|
|
@@ -246,6 +247,8 @@ let PRIVATE_DATA = {
|
|
|
246
247
|
dropped_round: 0, /* the load a drop ended: its failures are the drop, said once */
|
|
247
248
|
dropped_write: 0, /* the same for the write a drop ended */
|
|
248
249
|
reload_after_write: false, /* a write given up turned out DONE while another was in flight */
|
|
250
|
+
refresh_after_write: false, /* EV_REFRESH heard while writes were in flight: run at their end */
|
|
251
|
+
reopen_form: null, /* the form whose Save ran the owed reload: opened again once it lands */
|
|
249
252
|
|
|
250
253
|
/* A position that arrived while a load or a write was in flight */
|
|
251
254
|
pending_seg: null,
|
|
@@ -400,7 +403,11 @@ function el_attrs(attrs)
|
|
|
400
403
|
}
|
|
401
404
|
|
|
402
405
|
/***************************************************************
|
|
403
|
-
* The body stops answering while a write is in flight.
|
|
406
|
+
* The body stops answering while a write is in flight. For the
|
|
407
|
+
* keyboard too: `pointer-events` stops the mouse alone, and the
|
|
408
|
+
* shell's focus trap puts the focus back on the row control that
|
|
409
|
+
* had it, where Enter or Delete sent an action ST_SAVING does
|
|
410
|
+
* not declare. `inert` takes the whole body out of reach.
|
|
404
411
|
***************************************************************/
|
|
405
412
|
function set_busy(gobj, busy)
|
|
406
413
|
{
|
|
@@ -408,6 +415,7 @@ function set_busy(gobj, busy)
|
|
|
408
415
|
|
|
409
416
|
if($body) {
|
|
410
417
|
$body.classList.toggle("SCHEMA_BUSY", !!busy);
|
|
418
|
+
$body.toggleAttribute("inert", !!busy);
|
|
411
419
|
}
|
|
412
420
|
}
|
|
413
421
|
|
|
@@ -522,12 +530,37 @@ function owes_reload(gobj)
|
|
|
522
530
|
state !== "ST_LOADING" && state !== "ST_SAVING";
|
|
523
531
|
}
|
|
524
532
|
|
|
525
|
-
function reload_first(gobj, event)
|
|
533
|
+
function reload_first(gobj, event, reopen)
|
|
526
534
|
{
|
|
535
|
+
let priv = gobj.priv;
|
|
536
|
+
|
|
527
537
|
if(!owes_reload(gobj)) {
|
|
528
538
|
return false;
|
|
529
539
|
}
|
|
530
|
-
|
|
540
|
+
priv.reload_on_open = false;
|
|
541
|
+
|
|
542
|
+
/* The Save of a FORM: what the operator typed is not thrown away
|
|
543
|
+
* with the model it was typed on. The load that lands closes the
|
|
544
|
+
* form (end_load()) and opens it again on the schemas it read,
|
|
545
|
+
* with the fields the operator CHANGED put back on top -- only
|
|
546
|
+
* those: a field left as it was shows what the store holds now,
|
|
547
|
+
* so the next Save does not write the old value over a newer
|
|
548
|
+
* one. Closed alone, the form took what was typed with it. */
|
|
549
|
+
if(reopen && priv.dialog) {
|
|
550
|
+
priv.reopen_form = Object.assign(json_deep_copy(reopen), {
|
|
551
|
+
dialog: priv.dialog,
|
|
552
|
+
treedb_id: priv.treedb_id
|
|
553
|
+
});
|
|
554
|
+
log_warning(`${gobj_short_name(gobj)}: ${event} refused, the schemas shown ` +
|
|
555
|
+
`may be out of date: they are read again first, and the form opens again ` +
|
|
556
|
+
`on them with its changes`);
|
|
557
|
+
yui_shell_show_error(yui_shell_of(gobj),
|
|
558
|
+
"the schemas shown may be out of date: they are read again, and the form opens again on them with your changes",
|
|
559
|
+
{t: t});
|
|
560
|
+
request_model(gobj, true);
|
|
561
|
+
return true;
|
|
562
|
+
}
|
|
563
|
+
|
|
531
564
|
log_warning(`${gobj_short_name(gobj)}: ${event} refused, the schemas shown ` +
|
|
532
565
|
`may be out of date: they are read again first`);
|
|
533
566
|
yui_shell_show_error(yui_shell_of(gobj),
|
|
@@ -537,6 +570,53 @@ function reload_first(gobj, event)
|
|
|
537
570
|
return true;
|
|
538
571
|
}
|
|
539
572
|
|
|
573
|
+
/***************************************************************
|
|
574
|
+
* The form reload_first() took down, opened again on the model
|
|
575
|
+
* the load just made, with the operator's changes on top.
|
|
576
|
+
*
|
|
577
|
+
* Only where it can mean the same thing: on the treedb it was
|
|
578
|
+
* opened on, on the screen whose Save it has, and on a record
|
|
579
|
+
* that is still there. Otherwise it is not opened, and that is
|
|
580
|
+
* said -- a form opened on another treedb, or on nothing, would
|
|
581
|
+
* save somewhere the operator never meant.
|
|
582
|
+
***************************************************************/
|
|
583
|
+
function reopen_form(gobj, r)
|
|
584
|
+
{
|
|
585
|
+
let priv = gobj.priv;
|
|
586
|
+
let state = gobj_current_state(gobj);
|
|
587
|
+
let treedb = current_treedb(gobj);
|
|
588
|
+
let topic = null;
|
|
589
|
+
let col = null;
|
|
590
|
+
let fits = false;
|
|
591
|
+
|
|
592
|
+
if(treedb && r.treedb_id === priv.treedb_id) {
|
|
593
|
+
if(r.form === "column" && state === "ST_COLUMNS") {
|
|
594
|
+
topic = find_topic(priv.model, priv.treedb_id, r.topic);
|
|
595
|
+
col = (topic && !r.creating) ?
|
|
596
|
+
find_col(priv.model, priv.treedb_id, r.topic, r.col) : null;
|
|
597
|
+
fits = !!topic && (r.creating || !!col);
|
|
598
|
+
} else if(r.form === "topic" && state === "ST_TOPICS") {
|
|
599
|
+
topic = r.creating ? null : find_topic(priv.model, priv.treedb_id, r.topic);
|
|
600
|
+
fits = r.creating || !!topic;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
if(!fits) {
|
|
604
|
+
log_warning(`${gobj_short_name(gobj)}: the ${r.form} form was not opened again: ` +
|
|
605
|
+
`what it edited is not on the schemas the reload read ` +
|
|
606
|
+
`(${r.treedb_id} ${r.topic}${r.col ? "." + r.col : ""}, now ${state})`);
|
|
607
|
+
yui_shell_show_error(yui_shell_of(gobj),
|
|
608
|
+
"the schemas were read again and what the form was editing is not there any more",
|
|
609
|
+
{t: t});
|
|
610
|
+
return -1;
|
|
611
|
+
}
|
|
612
|
+
if(r.form === "column") {
|
|
613
|
+
open_column_form(gobj, topic, col, null, r.changed);
|
|
614
|
+
} else {
|
|
615
|
+
open_topic_form(gobj, treedb, topic, r.changed);
|
|
616
|
+
}
|
|
617
|
+
return 0;
|
|
618
|
+
}
|
|
619
|
+
|
|
540
620
|
function reload_after_move(gobj)
|
|
541
621
|
{
|
|
542
622
|
if(!owes_reload(gobj)) {
|
|
@@ -632,7 +712,7 @@ function request_model(gobj, keep_written)
|
|
|
632
712
|
priv.records_before = null;
|
|
633
713
|
priv.load_round++;
|
|
634
714
|
priv.reload_on_open = true;
|
|
635
|
-
priv.load_error =
|
|
715
|
+
priv.load_error = "cannot reach the treedb"; /* a key: the notice translates it */
|
|
636
716
|
if(priv.model) {
|
|
637
717
|
yui_shell_show_error(yui_shell_of(gobj), "cannot reach the treedb", {t: t});
|
|
638
718
|
}
|
|
@@ -725,7 +805,7 @@ function build_model(gobj)
|
|
|
725
805
|
* measurement of "changed since" can be measured against.
|
|
726
806
|
*
|
|
727
807
|
* `written` is forgotten only by an EV_REFRESH (the view's own
|
|
728
|
-
* Refresh button, or a host that sends one). Since
|
|
808
|
+
* Refresh button, or a host that sends one). Since 7.23.196 a write moves no
|
|
729
809
|
* version, so `written` is the draft chip of what this session
|
|
730
810
|
* wrote and no longer half of a version measurement: a reload of
|
|
731
811
|
* the reconnect, or of a write that turned out done, forgot the
|
|
@@ -918,9 +998,14 @@ function show_notice(gobj, key, detail)
|
|
|
918
998
|
$notice.appendChild(createElement2(
|
|
919
999
|
["span", {class: "SCHEMA_NOTICE_TEXT", i18n: key}, t(key)]
|
|
920
1000
|
));
|
|
921
|
-
|
|
1001
|
+
/* The detail is a KEY too (ours, or the backend's words, which
|
|
1002
|
+
* translate to themselves when no locale has them), so a change
|
|
1003
|
+
* of language changes it with the rest. One that repeats the
|
|
1004
|
+
* title says nothing. */
|
|
1005
|
+
if(detail && detail !== key) {
|
|
922
1006
|
$notice.appendChild(createElement2(
|
|
923
|
-
["span", {class: "SCHEMA_NOTICE_DETAIL yui-text-quiet ml-2"
|
|
1007
|
+
["span", {class: "SCHEMA_NOTICE_DETAIL yui-text-quiet ml-2", i18n: `${detail}`},
|
|
1008
|
+
t(`${detail}`)]
|
|
924
1009
|
));
|
|
925
1010
|
}
|
|
926
1011
|
}
|
|
@@ -1915,6 +2000,75 @@ function read_form($root)
|
|
|
1915
2000
|
return out;
|
|
1916
2001
|
}
|
|
1917
2002
|
|
|
2003
|
+
/***************************************************************
|
|
2004
|
+
* A column form as its Save sends it: the fields and the flags.
|
|
2005
|
+
***************************************************************/
|
|
2006
|
+
function column_form_state($form)
|
|
2007
|
+
{
|
|
2008
|
+
let values = read_form($form);
|
|
2009
|
+
values.flag = read_flags($form);
|
|
2010
|
+
return values;
|
|
2011
|
+
}
|
|
2012
|
+
|
|
2013
|
+
/***************************************************************
|
|
2014
|
+
* The fields the operator CHANGED: those whose value is not the
|
|
2015
|
+
* one the form was opened with.
|
|
2016
|
+
***************************************************************/
|
|
2017
|
+
function form_changes(initial, values)
|
|
2018
|
+
{
|
|
2019
|
+
let changed = {};
|
|
2020
|
+
|
|
2021
|
+
for(let name of Object.keys(values)) {
|
|
2022
|
+
if(JSON.stringify(values[name]) !== JSON.stringify(initial[name])) {
|
|
2023
|
+
changed[name] = values[name];
|
|
2024
|
+
}
|
|
2025
|
+
}
|
|
2026
|
+
return changed;
|
|
2027
|
+
}
|
|
2028
|
+
|
|
2029
|
+
/***************************************************************
|
|
2030
|
+
* Put changes back into a form just built (reopen_form()). The
|
|
2031
|
+
* flags are drawn again for the type the changes leave, holding
|
|
2032
|
+
* the flags they name, so none of them is hidden by the type.
|
|
2033
|
+
***************************************************************/
|
|
2034
|
+
function apply_form_changes(gobj, $form, changes)
|
|
2035
|
+
{
|
|
2036
|
+
for(let name of Object.keys(changes)) {
|
|
2037
|
+
if(name === "flag") {
|
|
2038
|
+
continue;
|
|
2039
|
+
}
|
|
2040
|
+
let $control = $form.querySelector(`[data-name="${name}"]`);
|
|
2041
|
+
if(!$control) {
|
|
2042
|
+
log_warning(`${gobj_short_name(gobj)}: a change of '${name}' has no field ` +
|
|
2043
|
+
`in the form opened again: not put back`);
|
|
2044
|
+
continue;
|
|
2045
|
+
}
|
|
2046
|
+
if($control.type === "checkbox") {
|
|
2047
|
+
$control.checked = !!changes[name];
|
|
2048
|
+
} else {
|
|
2049
|
+
$control.value = changes[name];
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
|
|
2053
|
+
let $type = $form.querySelector('[data-name="type"]');
|
|
2054
|
+
if($type && $form.querySelector(".SCHEMA_FLAGS")) {
|
|
2055
|
+
let flags = Array.isArray(changes.flag) ? changes.flag : read_flags($form);
|
|
2056
|
+
redraw_flags(gobj, $form, $type.value, flags);
|
|
2057
|
+
}
|
|
2058
|
+
}
|
|
2059
|
+
|
|
2060
|
+
/***************************************************************
|
|
2061
|
+
* Draw the flags of a column form again, for `type`, with
|
|
2062
|
+
* `current` checked.
|
|
2063
|
+
***************************************************************/
|
|
2064
|
+
function redraw_flags(gobj, $form, type, current)
|
|
2065
|
+
{
|
|
2066
|
+
let $holder = $form.querySelector(".SCHEMA_FLAGS");
|
|
2067
|
+
let $new = createElement2(flags_field(gobj, type, current));
|
|
2068
|
+
$holder.parentNode.replaceChild($new, $holder);
|
|
2069
|
+
refresh_language($new, t);
|
|
2070
|
+
}
|
|
2071
|
+
|
|
1918
2072
|
/***************************************************************
|
|
1919
2073
|
* The flags of a column, as checkboxes that say what they do.
|
|
1920
2074
|
*
|
|
@@ -1977,7 +2131,7 @@ function read_flags($root)
|
|
|
1977
2131
|
* Offering a field that silently does something else is worse
|
|
1978
2132
|
* than not offering it.
|
|
1979
2133
|
***************************************************************/
|
|
1980
|
-
function open_column_form(gobj, topic, col, prefill)
|
|
2134
|
+
function open_column_form(gobj, topic, col, prefill, changes)
|
|
1981
2135
|
{
|
|
1982
2136
|
let priv = gobj.priv;
|
|
1983
2137
|
/* A DUPLICATE is a creation whose fields start filled: the name must
|
|
@@ -2051,11 +2205,7 @@ function open_column_form(gobj, topic, col, prefill)
|
|
|
2051
2205
|
* new type does not use. */
|
|
2052
2206
|
let $type = $form.querySelector('[data-name="type"]');
|
|
2053
2207
|
$type.addEventListener("change", () => {
|
|
2054
|
-
|
|
2055
|
-
let current = read_flags($form);
|
|
2056
|
-
let $new = createElement2(flags_field(gobj, $type.value, current));
|
|
2057
|
-
$holder.parentNode.replaceChild($new, $holder);
|
|
2058
|
-
refresh_language($new, t);
|
|
2208
|
+
redraw_flags(gobj, $form, $type.value, read_flags($form));
|
|
2059
2209
|
});
|
|
2060
2210
|
|
|
2061
2211
|
/* A flag that excludes another is answered here and not on save,
|
|
@@ -2075,15 +2225,22 @@ function open_column_form(gobj, topic, col, prefill)
|
|
|
2075
2225
|
evt.stopPropagation();
|
|
2076
2226
|
close_dialog(gobj);
|
|
2077
2227
|
});
|
|
2228
|
+
/* What the form showed before the operator touched it: what
|
|
2229
|
+
* was CHANGED is measured against it (form_changes()). */
|
|
2230
|
+
let initial = column_form_state($form);
|
|
2231
|
+
if(changes) {
|
|
2232
|
+
apply_form_changes(gobj, $form, changes);
|
|
2233
|
+
}
|
|
2234
|
+
|
|
2078
2235
|
$form.querySelector(".SCHEMA_COL_FORM_SAVE").addEventListener("click", (evt) => {
|
|
2079
2236
|
evt.stopPropagation();
|
|
2080
|
-
let values =
|
|
2081
|
-
values.flag = read_flags($form);
|
|
2237
|
+
let values = column_form_state($form);
|
|
2082
2238
|
gobj_send_event(gobj, "EV_SAVE_COLUMN", {
|
|
2083
2239
|
topic: topic.name,
|
|
2084
2240
|
col: creating ? "" : col.name,
|
|
2085
2241
|
creating: creating,
|
|
2086
2242
|
values: values,
|
|
2243
|
+
changed: creating ? values : form_changes(initial, values),
|
|
2087
2244
|
model_gen: model_gen
|
|
2088
2245
|
}, gobj);
|
|
2089
2246
|
});
|
|
@@ -2112,7 +2269,7 @@ function stringify_field(value)
|
|
|
2112
2269
|
* actually has: a pkey naming no column is a topic that does
|
|
2113
2270
|
* not open, and it is the first thing the validator looks for.
|
|
2114
2271
|
***************************************************************/
|
|
2115
|
-
function open_topic_form(gobj, treedb, topic)
|
|
2272
|
+
function open_topic_form(gobj, treedb, topic, changes)
|
|
2116
2273
|
{
|
|
2117
2274
|
let record = (topic && topic.record) || {};
|
|
2118
2275
|
let creating = !topic;
|
|
@@ -2171,12 +2328,19 @@ function open_topic_form(gobj, treedb, topic)
|
|
|
2171
2328
|
evt.stopPropagation();
|
|
2172
2329
|
close_dialog(gobj);
|
|
2173
2330
|
});
|
|
2331
|
+
let initial = read_form($form);
|
|
2332
|
+
if(changes) {
|
|
2333
|
+
apply_form_changes(gobj, $form, changes);
|
|
2334
|
+
}
|
|
2335
|
+
|
|
2174
2336
|
$form.querySelector(".SCHEMA_TOPIC_FORM_SAVE").addEventListener("click", (evt) => {
|
|
2175
2337
|
evt.stopPropagation();
|
|
2338
|
+
let values = read_form($form);
|
|
2176
2339
|
gobj_send_event(gobj, "EV_SAVE_TOPIC", {
|
|
2177
2340
|
topic: creating ? "" : topic.name,
|
|
2178
2341
|
creating: creating,
|
|
2179
|
-
values:
|
|
2342
|
+
values: values,
|
|
2343
|
+
changed: creating ? values : form_changes(initial, values),
|
|
2180
2344
|
model_gen: model_gen
|
|
2181
2345
|
}, gobj);
|
|
2182
2346
|
});
|
|
@@ -2756,27 +2920,46 @@ function end_load(gobj)
|
|
|
2756
2920
|
{
|
|
2757
2921
|
let priv = gobj.priv;
|
|
2758
2922
|
let seg = priv.pending_seg;
|
|
2923
|
+
let reopen = priv.reopen_form;
|
|
2759
2924
|
|
|
2760
2925
|
priv.pending_seg = null;
|
|
2926
|
+
priv.reopen_form = null;
|
|
2761
2927
|
if(priv.dialog && priv.dialog_gen !== priv.model_gen) {
|
|
2762
|
-
|
|
2763
|
-
|
|
2928
|
+
/* The form whose Save asked for this load is opened again,
|
|
2929
|
+
* below, once the screen is there (reload_first()). Any other
|
|
2930
|
+
* dialog is closed, and that is said. */
|
|
2931
|
+
if(!reopen || reopen.dialog !== priv.dialog) {
|
|
2932
|
+
reopen = null;
|
|
2933
|
+
log_warning(`${gobj_short_name(gobj)}: a dialog was open on the schemas ` +
|
|
2934
|
+
`the load replaced: closed`);
|
|
2935
|
+
yui_shell_show_error(yui_shell_of(gobj),
|
|
2936
|
+
"the schemas were read again: open the dialog again", {t: t});
|
|
2937
|
+
}
|
|
2764
2938
|
close_dialog(gobj);
|
|
2765
|
-
|
|
2766
|
-
|
|
2939
|
+
} else {
|
|
2940
|
+
/* Closed meanwhile by the operator, or a load that replaced
|
|
2941
|
+
* nothing: the form is where it was, or where it was left. */
|
|
2942
|
+
reopen = null;
|
|
2767
2943
|
}
|
|
2944
|
+
|
|
2945
|
+
let ret = 0;
|
|
2768
2946
|
if(priv.model) {
|
|
2769
2947
|
if(seg !== null) {
|
|
2770
|
-
|
|
2948
|
+
ret = apply_seg(gobj, seg);
|
|
2949
|
+
} else {
|
|
2950
|
+
ret = go(gobj, priv.treedb_id, priv.topic_name, priv.diagram, false);
|
|
2771
2951
|
}
|
|
2772
|
-
|
|
2952
|
+
} else {
|
|
2953
|
+
if(seg !== null) {
|
|
2954
|
+
adopt_seg(gobj, seg);
|
|
2955
|
+
}
|
|
2956
|
+
gobj_change_state(gobj, "ST_IDLE");
|
|
2957
|
+
render(gobj);
|
|
2773
2958
|
}
|
|
2774
|
-
if(
|
|
2775
|
-
|
|
2959
|
+
if(reopen) {
|
|
2960
|
+
reopen_form(gobj, reopen);
|
|
2776
2961
|
}
|
|
2777
|
-
|
|
2778
|
-
render(gobj);
|
|
2779
|
-
return 0;
|
|
2962
|
+
return ret;
|
|
2780
2963
|
}
|
|
2781
2964
|
|
|
2782
2965
|
/***************************************************************
|
|
@@ -2819,7 +3002,17 @@ function end_writes(gobj, error)
|
|
|
2819
3002
|
render(gobj);
|
|
2820
3003
|
publish_check(gobj);
|
|
2821
3004
|
}
|
|
2822
|
-
|
|
3005
|
+
if(priv.refresh_after_write) {
|
|
3006
|
+
/* The EV_REFRESH heard while the writes were in flight
|
|
3007
|
+
* (ac_refresh_after_the_writes()), run now that none is. It
|
|
3008
|
+
* reads the store whole, so it also pays a reload a late
|
|
3009
|
+
* write owed. */
|
|
3010
|
+
priv.refresh_after_write = false;
|
|
3011
|
+
priv.reload_after_write = false;
|
|
3012
|
+
request_model(gobj, false);
|
|
3013
|
+
} else {
|
|
3014
|
+
reload_if_owed(gobj);
|
|
3015
|
+
}
|
|
2823
3016
|
return error ? -1 : 0;
|
|
2824
3017
|
}
|
|
2825
3018
|
|
|
@@ -3167,7 +3360,7 @@ function ac_mt_command_answer(gobj, event, kw, src)
|
|
|
3167
3360
|
}
|
|
3168
3361
|
priv.pending--;
|
|
3169
3362
|
if(result < 0) {
|
|
3170
|
-
priv.load_error = comment ||
|
|
3363
|
+
priv.load_error = comment || "cannot load the schemas"; /* a key */
|
|
3171
3364
|
} else if(priv.records && Array.isArray(priv.records[topic_name])) {
|
|
3172
3365
|
priv.records[topic_name] = Array.isArray(data) ? data : [];
|
|
3173
3366
|
}
|
|
@@ -3244,10 +3437,11 @@ function ac_mt_command_answer(gobj, event, kw, src)
|
|
|
3244
3437
|
return end_writes(gobj, comment || t("the treedb refused the write"));
|
|
3245
3438
|
}
|
|
3246
3439
|
priv.save_wrote = true;
|
|
3440
|
+
let record = null;
|
|
3247
3441
|
if(command === "delete-node") {
|
|
3248
3442
|
forget_record(gobj, topic_name, kw_get_str(gobj, md, "record_id", "", 0));
|
|
3249
3443
|
} else {
|
|
3250
|
-
|
|
3444
|
+
record = is_object(data) ? data
|
|
3251
3445
|
: (Array.isArray(data) && is_object(data[0]) ? data[0] : null);
|
|
3252
3446
|
if(record) {
|
|
3253
3447
|
patch_record(gobj, topic_name, record);
|
|
@@ -3270,7 +3464,7 @@ function ac_mt_command_answer(gobj, event, kw, src)
|
|
|
3270
3464
|
}
|
|
3271
3465
|
if(topic_name === T_TOPICS || topic_name === T_COLS) {
|
|
3272
3466
|
let write = priv.save_queue[0];
|
|
3273
|
-
mark_written(gobj, topic_name, write ? write.record : null);
|
|
3467
|
+
mark_written(gobj, topic_name, null, write ? write.record : null);
|
|
3274
3468
|
}
|
|
3275
3469
|
priv.reload_after_write = false;
|
|
3276
3470
|
priv.save_queue = [];
|
|
@@ -3279,16 +3473,21 @@ function ac_mt_command_answer(gobj, event, kw, src)
|
|
|
3279
3473
|
gobj_publish_event(gobj, "EV_RECORD_WRITTEN", {
|
|
3280
3474
|
treedb_name: gobj_read_str_attr(gobj, "treedb_name")
|
|
3281
3475
|
});
|
|
3282
|
-
|
|
3476
|
+
/* An EV_REFRESH heard during the writes is paid by this
|
|
3477
|
+
* load: it forgets the drafts, as the refresh would. */
|
|
3478
|
+
let refresh = priv.refresh_after_write;
|
|
3479
|
+
priv.refresh_after_write = false;
|
|
3480
|
+
request_model(gobj, !refresh);
|
|
3283
3481
|
return 0;
|
|
3284
3482
|
}
|
|
3285
3483
|
}
|
|
3286
3484
|
if(topic_name === T_TOPICS || topic_name === T_COLS) {
|
|
3287
|
-
/* Which record it was
|
|
3288
|
-
* adapter echoes only what it was
|
|
3289
|
-
* record is not small enough to be
|
|
3485
|
+
/* Which record it was: the one the store answered, else the
|
|
3486
|
+
* one in the QUEUE -- an adapter echoes only what it was
|
|
3487
|
+
* asked to echo, and the record is not small enough to be
|
|
3488
|
+
* worth echoing. */
|
|
3290
3489
|
let write = priv.save_queue[0];
|
|
3291
|
-
mark_written(gobj, topic_name, write ? write.record : null);
|
|
3490
|
+
mark_written(gobj, topic_name, record, write ? write.record : null);
|
|
3292
3491
|
}
|
|
3293
3492
|
priv.save_queue.shift();
|
|
3294
3493
|
return run_next_write(gobj);
|
|
@@ -3357,35 +3556,46 @@ function late_write_done(gobj, command, topic_name, data)
|
|
|
3357
3556
|
function mark_written_record(gobj, topic_name, record)
|
|
3358
3557
|
{
|
|
3359
3558
|
let priv = gobj.priv;
|
|
3559
|
+
let marked = 0;
|
|
3360
3560
|
|
|
3361
3561
|
if(!record) {
|
|
3362
|
-
return;
|
|
3562
|
+
return 0;
|
|
3363
3563
|
}
|
|
3364
3564
|
if(topic_name === T_TOPICS && typeof record.id === "string" && record.id) {
|
|
3365
3565
|
priv.written[record.id] = true;
|
|
3366
|
-
return;
|
|
3566
|
+
return 1;
|
|
3367
3567
|
}
|
|
3368
3568
|
if(topic_name !== T_COLS) {
|
|
3369
|
-
return;
|
|
3569
|
+
return 0;
|
|
3370
3570
|
}
|
|
3371
3571
|
for(let ref of parse_fkey_ref(record.topics)) {
|
|
3372
3572
|
priv.written[ref.id] = true;
|
|
3573
|
+
marked++;
|
|
3373
3574
|
}
|
|
3575
|
+
return marked;
|
|
3374
3576
|
}
|
|
3375
3577
|
|
|
3376
3578
|
/***************************************************************
|
|
3377
3579
|
* Remember which topic this write belongs to, which is what
|
|
3378
3580
|
* "its version still has to move" is asked about.
|
|
3581
|
+
*
|
|
3582
|
+
* Asked of the RECORD first: the one the store answered (a new
|
|
3583
|
+
* topic has its id only there), then the one queued (a column
|
|
3584
|
+
* names its topic in its fkey). The screen is the last resort,
|
|
3585
|
+
* and a poor one: an import writes topics the operator is not
|
|
3586
|
+
* on, and from the topics screen there is no topic on it at all.
|
|
3379
3587
|
***************************************************************/
|
|
3380
|
-
function mark_written(gobj, topic_name,
|
|
3588
|
+
function mark_written(gobj, topic_name, answered, queued)
|
|
3381
3589
|
{
|
|
3382
3590
|
let priv = gobj.priv;
|
|
3383
|
-
let topic = current_topic(gobj);
|
|
3384
3591
|
|
|
3385
|
-
if(topic_name
|
|
3386
|
-
|
|
3592
|
+
if(mark_written_record(gobj, topic_name, answered) > 0) {
|
|
3593
|
+
return;
|
|
3594
|
+
}
|
|
3595
|
+
if(mark_written_record(gobj, topic_name, queued) > 0) {
|
|
3387
3596
|
return;
|
|
3388
3597
|
}
|
|
3598
|
+
let topic = current_topic(gobj);
|
|
3389
3599
|
if(topic) {
|
|
3390
3600
|
priv.written[topic.id] = true;
|
|
3391
3601
|
}
|
|
@@ -3475,6 +3685,25 @@ function ac_refresh(gobj, event, kw, src)
|
|
|
3475
3685
|
return request_model(gobj, false);
|
|
3476
3686
|
}
|
|
3477
3687
|
|
|
3688
|
+
/***************************************************************
|
|
3689
|
+
* EV_REFRESH while writes are in flight: the view's own Refresh
|
|
3690
|
+
* is busy then, so it is a host's (after a Save, say).
|
|
3691
|
+
*
|
|
3692
|
+
* Run when the writes END (end_writes()), not now. Run now, the
|
|
3693
|
+
* load replaced ST_SAVING and what was still queued was never
|
|
3694
|
+
* sent, with no word: nothing but the end of what is in flight
|
|
3695
|
+
* moves this view out of ST_LOADING or ST_SAVING.
|
|
3696
|
+
***************************************************************/
|
|
3697
|
+
function ac_refresh_after_the_writes(gobj, event, kw, src)
|
|
3698
|
+
{
|
|
3699
|
+
let priv = gobj.priv;
|
|
3700
|
+
|
|
3701
|
+
log_warning(`${gobj_short_name(gobj)}: ${event} while writes are in flight: ` +
|
|
3702
|
+
`the schemas are read again when the writes end`);
|
|
3703
|
+
priv.refresh_after_write = true;
|
|
3704
|
+
return 0;
|
|
3705
|
+
}
|
|
3706
|
+
|
|
3478
3707
|
/***************************************************************
|
|
3479
3708
|
* What a DIALOG sends while the model is being read again: the
|
|
3480
3709
|
* Save of a form left open when the reload started, a
|
|
@@ -3717,7 +3946,8 @@ function ac_save_column(gobj, event, kw, src)
|
|
|
3717
3946
|
if(refuse_if_readonly(gobj, event)) {
|
|
3718
3947
|
return -1;
|
|
3719
3948
|
}
|
|
3720
|
-
if(reload_first(gobj, event
|
|
3949
|
+
if(reload_first(gobj, event, {form: "column", topic: kw.topic, col: kw.col,
|
|
3950
|
+
creating: !!kw.creating, changed: kw.changed || {}})) {
|
|
3721
3951
|
return -1;
|
|
3722
3952
|
}
|
|
3723
3953
|
if(built_on_a_replaced_model(gobj, event, kw)) {
|
|
@@ -3892,7 +4122,8 @@ function ac_save_topic(gobj, event, kw, src)
|
|
|
3892
4122
|
if(refuse_if_readonly(gobj, event)) {
|
|
3893
4123
|
return -1;
|
|
3894
4124
|
}
|
|
3895
|
-
if(reload_first(gobj, event
|
|
4125
|
+
if(reload_first(gobj, event, {form: "topic", topic: kw.topic, col: "",
|
|
4126
|
+
creating: !!kw.creating, changed: kw.changed || {}})) {
|
|
3896
4127
|
return -1;
|
|
3897
4128
|
}
|
|
3898
4129
|
if(built_on_a_replaced_model(gobj, event, kw)) {
|
|
@@ -4306,7 +4537,9 @@ function create_gclass(gclass_name)
|
|
|
4306
4537
|
* the host are heard: every other action would be computed
|
|
4307
4538
|
* against a model the answer still in the air is about to
|
|
4308
4539
|
* change. */
|
|
4309
|
-
["ST_SAVING", COMMON.
|
|
4540
|
+
["ST_SAVING", COMMON.filter((a) => a[0] !== "EV_REFRESH").concat([
|
|
4541
|
+
["EV_REFRESH", ac_refresh_after_the_writes, null]
|
|
4542
|
+
])]
|
|
4310
4543
|
];
|
|
4311
4544
|
|
|
4312
4545
|
/*---------------------------------------------*
|