From 5200ef7c2856efafa96cce253d906bb70f834254 Mon Sep 17 00:00:00 2001 From: Matt Keenan Date: Thu, 31 Oct 2024 11:51:46 -0400 Subject: [PATCH 1/2] Fix Saving --- src/haz3lweb/Main.re | 79 +++++++++++++++++++++++------------------- src/haz3lweb/Update.re | 4 ++- src/haz3lweb/dune | 3 +- src/util/BonsaiUtil.re | 42 ++++++++++++++++++++++ src/util/Util.re | 1 + src/util/dune | 3 +- 6 files changed, 94 insertions(+), 38 deletions(-) create mode 100644 src/util/BonsaiUtil.re diff --git a/src/haz3lweb/Main.re b/src/haz3lweb/Main.re index af8058b770..6491074920 100644 --- a/src/haz3lweb/Main.re +++ b/src/haz3lweb/Main.re @@ -4,8 +4,6 @@ open Haz3lweb; open Bonsai.Let_syntax; let scroll_to_caret = ref(true); -let edit_action_applied = ref(true); -let last_edit_action = ref(JsUtil.timestamp()); let restart_caret_animation = () => // necessary to trigger reflow @@ -19,16 +17,24 @@ let restart_caret_animation = () => | _ => () }; -let apply = (model, action, ~schedule_action): Model.t => { +let apply = (model, action, ~schedule_action, ~schedule_autosave): Model.t => { restart_caret_animation(); if (UpdateAction.is_edit(action)) { - last_edit_action := JsUtil.timestamp(); - edit_action_applied := true; + schedule_autosave( + BonsaiUtil.Alarm.Action.SetAlarm( + Core.Time_ns.add(Core.Time_ns.now(), Core.Time_ns.Span.of_sec(2.0)), + ), + ); + } else { + schedule_autosave( + BonsaiUtil.Alarm.Action.SnoozeAlarm( + Core.Time_ns.add(Core.Time_ns.now(), Core.Time_ns.Span.of_sec(2.0)), + ), + ); }; if (Update.should_scroll_to_caret(action)) { scroll_to_caret := true; }; - last_edit_action := JsUtil.timestamp(); switch ( try({ let new_model = Update.apply(model, action, ~schedule_action); @@ -53,16 +59,6 @@ let apply = (model, action, ~schedule_action): Model.t => { }; }; -let app = - Bonsai.state_machine0( - (module Model), - (module Update), - ~apply_action= - (~inject, ~schedule_event) => - apply(~schedule_action=x => schedule_event(inject(x))), - ~default_model=Model.load(Model.blank), - ); - /* This subcomponent is used to run an effect once when the app starts up, After the first draw */ let on_startup = effect => { @@ -80,31 +76,44 @@ let on_startup = effect => { }; let view = { - let%sub app = app; + let%sub save_scheduler = BonsaiUtil.Alarm.alarm; + let%sub app = + Bonsai.state_machine1( + (module Model), + (module Update), + ~apply_action= + (~inject, ~schedule_event, input) => { + let schedule_action = x => schedule_event(inject(x)); + let schedule_autosave = action => + switch (input) { + | Active((_, alarm_inject)) => + schedule_event(alarm_inject(action)) + | Inactive => () + }; + apply(~schedule_action, ~schedule_autosave); + }, + ~default_model=Model.load(Model.blank), + save_scheduler, + ); let%sub () = { on_startup( Bonsai.Value.map(~f=((_model, inject)) => inject(Startup), app), ); }; - let%sub after_display = { - let%arr (_model, inject) = app; - if (scroll_to_caret.contents) { - scroll_to_caret := false; - JsUtil.scroll_cursor_into_view_if_needed(); - }; - if (edit_action_applied^ - && JsUtil.timestamp() - -. last_edit_action^ > 1000.0) { - /* If an edit action has been applied, but no other edit action - has been applied for 1 second, save the model. */ - edit_action_applied := false; - print_endline("Saving..."); - inject(Update.Save); - } else { - Ui_effect.Ignore; - }; + let after_display = { + Bonsai.Effect.of_sync_fun( + () => + if (scroll_to_caret.contents) { + scroll_to_caret := false; + JsUtil.scroll_cursor_into_view_if_needed(); + }, + (), + ); }; - let%sub () = Bonsai.Edge.after_display(after_display); + let save_effect = Bonsai.Value.map(~f=((_, g)) => g(Update.Save), app); + let%sub () = BonsaiUtil.Alarm.listen(save_scheduler, ~event=save_effect); + let%sub () = + Bonsai.Edge.after_display(after_display |> Bonsai.Value.return); let%arr (model, inject) = app; Haz3lweb.Page.view(~inject, model); }; diff --git a/src/haz3lweb/Update.re b/src/haz3lweb/Update.re index 1aacb159f3..778df5114a 100644 --- a/src/haz3lweb/Update.re +++ b/src/haz3lweb/Update.re @@ -430,7 +430,9 @@ let apply = (model: Model.t, update: t, ~schedule_action): Result.t(Model.t) => | DebugConsole(key) => DebugConsole.print(model, key); Ok(model); - | Save => Model.save_and_return(model) + | Save => + print_endline("Saving..."); + Model.save_and_return(model); | InitImportAll(file) => JsUtil.read_file(file, data => schedule_action(FinishImportAll(data))); Ok(model); diff --git a/src/haz3lweb/dune b/src/haz3lweb/dune index d3e42ec636..d826004fca 100644 --- a/src/haz3lweb/dune +++ b/src/haz3lweb/dune @@ -55,7 +55,8 @@ ppx_let ppx_sexp_conv ppx_deriving.show - ppx_yojson_conv))) + ppx_yojson_conv + bonsai.ppx_bonsai))) (executable (name main) diff --git a/src/util/BonsaiUtil.re b/src/util/BonsaiUtil.re new file mode 100644 index 0000000000..127172ce21 --- /dev/null +++ b/src/util/BonsaiUtil.re @@ -0,0 +1,42 @@ +open Core; +open Bonsai; +open Bonsai.Let_syntax; + +module Alarm = { + module Action = { + [@deriving sexp] + type t = + | SetAlarm(Time_ns.Alternate_sexp.t) + | SnoozeAlarm(Time_ns.Alternate_sexp.t) + | UnsetAlarm; + }; + + let alarm = + state_machine0( + (module Time_ns.Alternate_sexp), + (module Action), + ~default_model=Time_ns.max_value_representable, + ~apply_action=(~inject as _, ~schedule_event as _, model, action) => { + switch (action) { + | SetAlarm(time) => time + | SnoozeAlarm(time) => Time_ns.max(time, model) + | UnsetAlarm => Time_ns.max_value_representable + } + }); + + let listen = (alarm, ~event) => { + let%sub before_or_after = Clock.at(alarm |> Value.map(~f=fst)); + Edge.on_change( + (module Clock.Before_or_after), + before_or_after, + ~callback={ + open Clock.Before_or_after; + let%map (_, inject) = alarm + and event = event; + fun + | After => Effect.Many([inject(Action.UnsetAlarm), event]) + | Before => Effect.Ignore; + }, + ); + }; +}; diff --git a/src/util/Util.re b/src/util/Util.re index c901907b60..2c7f084100 100644 --- a/src/util/Util.re +++ b/src/util/Util.re @@ -1,4 +1,5 @@ module Aba = Aba; +module BonsaiUtil = BonsaiUtil; module Direction = Direction; module Either = Either; module IntMap = IntMap; diff --git a/src/util/dune b/src/util/dune index a3494db579..07f7b7d8da 100644 --- a/src/util/dune +++ b/src/util/dune @@ -8,7 +8,8 @@ js_of_ocaml-ppx ppx_let ppx_sexp_conv - ppx_deriving.show))) + ppx_deriving.show + bonsai.ppx_bonsai))) (env (dev From 0b4eb934a2aa48b8a91d8242a7bf811375198269 Mon Sep 17 00:00:00 2001 From: Matt Keenan Date: Thu, 31 Oct 2024 11:55:53 -0400 Subject: [PATCH 2/2] Switch back to 1s --- src/haz3lweb/Main.re | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/haz3lweb/Main.re b/src/haz3lweb/Main.re index 6491074920..16811a32cb 100644 --- a/src/haz3lweb/Main.re +++ b/src/haz3lweb/Main.re @@ -22,13 +22,13 @@ let apply = (model, action, ~schedule_action, ~schedule_autosave): Model.t => { if (UpdateAction.is_edit(action)) { schedule_autosave( BonsaiUtil.Alarm.Action.SetAlarm( - Core.Time_ns.add(Core.Time_ns.now(), Core.Time_ns.Span.of_sec(2.0)), + Core.Time_ns.add(Core.Time_ns.now(), Core.Time_ns.Span.of_sec(1.0)), ), ); } else { schedule_autosave( BonsaiUtil.Alarm.Action.SnoozeAlarm( - Core.Time_ns.add(Core.Time_ns.now(), Core.Time_ns.Span.of_sec(2.0)), + Core.Time_ns.add(Core.Time_ns.now(), Core.Time_ns.Span.of_sec(1.0)), ), ); };