open Brr module PM = Prosemirror module Js = Js_of_ocaml.Js module Actions = Editor_actions (** Create a new editor view [build_view element state] will create the editor and attach it to [element]. *) let build_view : PM.t -> Jstr.t option -> El.t -> PM.View.editor_view Js.t * float = fun pm page_id editor -> (* Remove all the elements if any *) El.set_children editor []; (* TODO This could be improved, instead of creating a new schema, just fetch the node and marks from the plungin *) let custom_schema = Plugins.Footnotes.footnote_schema pm (PM.SchemaBasic.schema pm) in (* Recreate the full schema by adding all the nodes and marks from the plugings *) let specs = PM.Model.schema_spec (PM.SchemaList.add_list_nodes pm (custom_schema##.spec##.nodes) (Jstr.v "paragraph block*") (Some (Jstr.v "block"))) (Some custom_schema##.spec##.marks) None in let full_schema = PM.Model.schema pm specs in let stored_content = State.Storage.load page_id in (* This variable contains the last update time, either because it is stored, or because it is the date where we create the first page. *) let last_backup = Js.Opt.get stored_content##.date (fun () -> (new%js Js.date_now)##getTime) in let props = PM.View.direct_editor_props () in props##.state := State.state_of_storage pm stored_content full_schema; (* Add the custom nodes *) props##.nodeViews := PM.O.init [| ( "footnote", (Plugins.Footnotes.footnote_view pm)) |]; let view = PM.View.editor_view pm editor props in view, last_backup module Store = struct type t = El.t let update : t -> State.t -> State.t = fun title_element state -> let title = El.prop (El.Prop.value) title_element in let new_date = (new%js Js.date_now)##getTime in let content_obj = object%js val content = Js.some @@ Jv.Id.to_jv (state.view##.state##toJSON ()) val title = Js.some title val date = Js.some new_date end in let save = State.Storage.save content_obj state.page_id (* There three date here : - The actual date at the time we save the note - The date associated with the note when we loaded it first time - The date associated with the note at the time we want to update it The two last may differ if the note has been updated in another one tab. *) ~check:(fun ~previous ~update -> let _ = update in Js.Opt.case previous##.date (fun () -> true) (fun date -> (* I do not figure how the previous date could be older than the last backup. It could be either : - equal (if we are the only one to update it) - more recent (if the content has been updated elsewhere) but older shoud be a bug. *) let is_ok = date <= state.last_backup in if (not is_ok) then ( let open Console in log [ Jstr.v "Last backup date is " ; new%js Js.date_fromTimeValue state.last_backup ; Jstr.v " but date is " ; new%js Js.date_fromTimeValue date] ); is_ok)) in begin match save with | Ok true -> { state with last_backup = new_date } | Ok false -> Console.(log [Jstr.v "Didn't save"]); state | Error other -> (* TODO In case of error, notify the user *) Console.(log [Jstr.v "Couldn't save" ; other]); state end end let app id content = let title_element = Document.find_el_by_id G.document (Jstr.v "title") in (* Check the pre-requisite *) match title_element, (Jv.is_none id), (Jv.is_none content), Blog.Sidebar.get () with | Some title, false, false, Some sidebar -> let () = Blog.Sidebar.clean sidebar in let pm = PM.v () in let editor:El.t = Jv.Id.of_jv id in (* Load the cache for the given page *) let page_id = State.Storage.page_id () in let view, last_backup = build_view pm page_id editor in let init_state = State.init pm view last_backup page_id in let side_elements = Editor_actions.build pm in let btn_events = Editor_actions.get_event side_elements in let app_state = State.run ~eq:State.eq init_state (Note.E.select [ Brr_note.Evr.on_els Ev.focusout (fun _ _ -> State.E ( title , (module Store:State.Event with type t = Store.t))) [ editor ; title ] ; btn_events ]) in let childs = Editor_actions.complete side_elements app_state in let () = El.append_children sidebar childs in let _ = Note.(Logr.hold (S.log app_state (fun _ -> ()))) in () | _ -> Console.(error [str "No element with id '%s' '%s' found"; id ; content]) let () = let open Jv in let editor = obj [| "attach_prosemirror", (repr app) |] in set global "editor" editor