From a825d7ed515fc9cc0dbd74513e902c5f8f828fc1 Mon Sep 17 00:00:00 2001 From: Erik Martin-Dorel Date: Wed, 29 Sep 2021 14:55:52 +0200 Subject: [PATCH] feat: Improve description entrypoint (#423) * feat(description): Add learnocaml-exo-loading initial phase * in order to have as good an UX as that of exercises view * feat(description): Implement padding decoding * Aim: interoperate with learn-ocaml.el, which can encode the token by (base64-encode-string (format "%192s" "AAA-BBB-CCC-DDD") t) before opening the URL http://localhost:8080/description/:id#token1=:encoded with a 256-wide string for ":encoded" that couldn't be easily copied if, say, the user takes a screenshot of the exercise description. * For the moment, the support of legacy URLs http://localhost:8080/description/:id#token=AAA-BBB-CCC-DDD is still supported (but deprecated; it may be removed later on). --- src/app/learnocaml_description_main.ml | 68 +++++++++++++++++++++----- static/css/learnocaml_description.css | 10 ++++ static/description.html | 21 ++++++++ 3 files changed, 88 insertions(+), 11 deletions(-) diff --git a/src/app/learnocaml_description_main.ml b/src/app/learnocaml_description_main.ml index 44ac7be9c..6484d9f2a 100644 --- a/src/app/learnocaml_description_main.ml +++ b/src/app/learnocaml_description_main.ml @@ -8,13 +8,47 @@ open Learnocaml_data.Exercise.Meta let init_tabs, select_tab = mk_tab_handlers "text" ["text"; "meta"] +type encoded_token = + { + arg_name: string; + raw_arg: string; + token: Learnocaml_data.Token.t + } + +(** [get_arg_token ()] read (and decode if need be) the user token. + + @return [Some encoded_token] if a token was successfully read. + It returns [None] if no token was specified in the URL. + An exception is raised if an incorrect token was specified. *) +let get_encoded_token () = + match arg "token" with (* arg in plain text, deprecated in learn-ocaml 0.13 *) + | raw_arg -> + let token = Learnocaml_data.Token.parse raw_arg in + Some { arg_name = "token"; raw_arg; token } + | exception Not_found -> + match arg "token1" with (* encoding algo 1: space-padded token |> base64 *) + | raw_arg -> + begin match Base64.decode ~pad:true raw_arg with + (* ~pad:false would work also, but ~pad:true is stricter *) + | Ok pad_token -> + Some { arg_name = "token1"; raw_arg; + token = Learnocaml_data.Token.parse (String.trim pad_token) } + | Error (`Msg msg) -> failwith msg + end + | exception Not_found -> None + module Exercise_link = struct let exercise_link ?(cl = []) id content = - let token = Learnocaml_data.Token.(to_string (parse (arg "token"))) in - Tyxml_js.Html5.(a ~a:[ a_href ("/description/"^id^"#token="^token); - a_class cl ] - content) + match get_encoded_token () with + | Some { arg_name; raw_arg; _ } -> + Tyxml_js.Html5.(a ~a:[ a_href + (Printf.sprintf "/description/%s#%s=%s" + id arg_name raw_arg); + a_class cl ] + content) + | None -> + Tyxml_js.Html5.(a ~a:[ a_href "#" ; a_class cl ] content) end module Display = Display_exercise(Exercise_link) @@ -29,8 +63,8 @@ let () = Learnocaml_local_storage.init () ; let title_container = find_component "learnocaml-exo-tab-text-title" in let text_container = find_component "learnocaml-exo-tab-text-descr" in - try begin - let token = Learnocaml_data.Token.parse (arg "token") in + match get_encoded_token () with + | Some { arg_name = _; raw_arg = _; token } -> begin let exercise_fetch = retrieve (Learnocaml_api.Exercise (Some token, id)) in @@ -51,9 +85,21 @@ let () = d##write (Js.string (exercise_text ex_meta exo)); d##close) ; (* display meta *) - display_meta (Some token) ex_meta id + display_meta (Some token) ex_meta id >>= fun () -> + (* hide the initial/loading phase curtain *) + Lwt.return @@ hide_loading ~id:"learnocaml-exo-loading" () end - with Not_found -> - Lwt.return @@ - Manip.replaceChildren text_container - Tyxml_js.Html5.[ h1 [ txt "Error: Missing token" ] ] + | None -> + let elt = find_div_or_append_to_body "learnocaml-exo-loading" in + Manip.(addClass elt "loading-layer") ; + Manip.(removeClass elt "loaded") ; + Manip.(addClass elt "loading") ; + Manip.replaceChildren elt + Tyxml_js.Html5.[ + h1 [ txt "Error: missing token. \ + Use a link from "; + (* Note: could be put in a global constant *) + a ~a:[ a_href "https://github.com/pfitaxel/learn-ocaml.el" ] + [ txt "learn-ocaml-mode" ]; + txt "?" ] ]; + Lwt.return_unit diff --git a/static/css/learnocaml_description.css b/static/css/learnocaml_description.css index 43db782d9..609db063a 100644 --- a/static/css/learnocaml_description.css +++ b/static/css/learnocaml_description.css @@ -141,6 +141,16 @@ body { /* BEGIN excerpt from learnocaml_exercise.css */ +/* -------------------- loading splash screen --------------------- */ +#learnocaml-exo-loading { + position: absolute; + top: 0; left: 0; right: 0; bottom: 0; +} +#learnocaml-exo-loading.loading, +#learnocaml-exo-loading.loaded { + background: rgba(200,200,200,0.9); +} + .learnocaml-exo-meta-category ~ .exercise + .exercise { border-top: 1px #333 solid; } diff --git a/static/description.html b/static/description.html index 2efe7ea44..b8822e535 100644 --- a/static/description.html +++ b/static/description.html @@ -19,6 +19,27 @@ + +
+ + + + + + +
+ +
+
+
  • Preparing the environment
+
+ +