From ade3430fb3eb9c8cd1093de24362263ffbecbfd5 Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Mon, 4 Oct 2021 13:55:29 -0400 Subject: [PATCH] Fix log on flow; add page title --- src/MyPrayerJournal/Server/Handlers.fs | 40 ++++++++++++------- src/MyPrayerJournal/Server/Views.fs | 18 ++++++--- .../Server/wwwroot/script/mpj.js | 27 ++++++++++++- 3 files changed, 64 insertions(+), 21 deletions(-) diff --git a/src/MyPrayerJournal/Server/Handlers.fs b/src/MyPrayerJournal/Server/Handlers.fs index 099622a..abeb7e7 100644 --- a/src/MyPrayerJournal/Server/Handlers.fs +++ b/src/MyPrayerJournal/Server/Handlers.fs @@ -8,13 +8,20 @@ open Giraffe open Giraffe.Htmx open MyPrayerJournal.Data.Extensions -/// Send a partial result if this is not a full page load -let partialIfNotRefresh content : HttpHandler = +let writeView view : HttpHandler = fun next ctx -> task { - match ctx.Request.IsHtmx && not ctx.Request.IsHtmxRefresh with - | true -> return! ctx.WriteHtmlViewAsync content - | false -> return! Views.Layout.view content |> ctx.WriteHtmlViewAsync - } + return! ctx.WriteHtmlViewAsync view + } + +/// Send a partial result if this is not a full page load +let partialIfNotRefresh (pageTitle : string) content : HttpHandler = + fun next ctx -> + (next, ctx) + ||> match ctx.Request.IsHtmx && not ctx.Request.IsHtmxRefresh with + | true -> + ctx.Response.Headers.["X-Page-Title"] <- Microsoft.Extensions.Primitives.StringValues pageTitle + withHxTriggerAfterSettle "menu-refresh" >=> writeView content + | false -> writeView (Views.Layout.view pageTitle content) /// Handler to return Vue files module Vue = @@ -22,7 +29,7 @@ module Vue = /// The application index page let app : HttpHandler = withHxTrigger "menu-refresh" - >=> partialIfNotRefresh (ViewEngine.HtmlElements.str "It works") + >=> partialIfNotRefresh "" (ViewEngine.HtmlElements.str "It works") open System @@ -180,7 +187,11 @@ module Home = // GET / let home : HttpHandler = - withMenuRefresh >=> partialIfNotRefresh Views.Home.home + withMenuRefresh >=> partialIfNotRefresh "Welcome!" Views.Home.home + + // GET /user/log-on + let logOn : HttpHandler = + partialIfNotRefresh "Logging on..." Views.Home.logOn /// /journal URL @@ -192,7 +203,7 @@ module Journal = >=> withMenuRefresh >=> fun next ctx -> task { let usr = ctx.Request.Headers.["X-Given-Name"].[0] - return! partialIfNotRefresh (Views.Journal.journal usr) next ctx + return! partialIfNotRefresh "Your Prayer Journal" (Views.Journal.journal usr) next ctx } @@ -201,11 +212,11 @@ module Legal = // GET /legal/privacy-policy let privacyPolicy : HttpHandler = - withMenuRefresh >=> partialIfNotRefresh Views.Legal.privacyPolicy + withMenuRefresh >=> partialIfNotRefresh "Privacy Policy" Views.Legal.privacyPolicy // GET /legal/terms-of-service let termsOfService : HttpHandler = - withMenuRefresh >=> partialIfNotRefresh Views.Legal.termsOfService + withMenuRefresh >=> partialIfNotRefresh "Terms of Service" Views.Legal.termsOfService /// /api/request and /request(s) URLs @@ -292,7 +303,7 @@ module Request = >=> withMenuRefresh >=> fun next ctx -> task { let! reqs = Data.journalByUserId (userId ctx) (db ctx) - return! partialIfNotRefresh (Views.Request.active reqs) next ctx + return! partialIfNotRefresh "Active Requests" (Views.Request.active reqs) next ctx } /// GET /requests/answered @@ -301,7 +312,7 @@ module Request = >=> withMenuRefresh >=> fun next ctx -> task { let! reqs = Data.answeredRequests (userId ctx) (db ctx) - return! partialIfNotRefresh (Views.Request.answered reqs) next ctx + return! partialIfNotRefresh "Answered Requests" (Views.Request.answered reqs) next ctx } /// GET /api/request/[req-id] @@ -319,7 +330,7 @@ module Request = >=> withMenuRefresh >=> fun next ctx -> task { match! Data.tryFullRequestById (toReqId requestId) (userId ctx) (db ctx) with - | Some req -> return! partialIfNotRefresh (Views.Request.full req) next ctx + | Some req -> return! partialIfNotRefresh "Full Prayer Request" (Views.Request.full req) next ctx | None -> return! Error.notFound next ctx } @@ -402,6 +413,7 @@ let routes = route "s/answered" Request.answered routef "/%s/full" Request.getFull ] + route "/user/log-on" Home.logOn subRoute "/api/" [ GET [ subRoute "request" [ diff --git a/src/MyPrayerJournal/Server/Views.fs b/src/MyPrayerJournal/Server/Views.fs index 67fd1d2..5c7c7de 100644 --- a/src/MyPrayerJournal/Server/Views.fs +++ b/src/MyPrayerJournal/Server/Views.fs @@ -32,7 +32,7 @@ module Helpers = span [ _title (date.ToString "f") ] [ Dates.formatDistance DateTime.Now date |> str ] -/// View for home page +/// Views for home and log on pages module Home = /// The home page @@ -49,6 +49,13 @@ module Home = rawText "learn more about the site at the “Docs” link, also above." ] ] + + /// The log on page + let logOn = article [ _class "container mt-3" ] [ + p [] [ + em [] [ str "Verifying..." ] + ] + ] /// Views for legal pages @@ -406,8 +413,9 @@ module Request = /// Layout views module Layout = - let htmlHead = + let htmlHead pageTitle = head [] [ + title [] [ str pageTitle; rawText " « myPrayerJournal" ] link [ _href "https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" _rel "stylesheet" @@ -451,12 +459,12 @@ module Layout = ] /// Create the full view of the page - let view content = + let view pageTitle content = html [ _lang "en" ] [ - htmlHead + htmlHead pageTitle body [ _hxHeaders "" ] [ Navigation.navBar - main [] [ content ] + main [ _hxTrigger "setTitle from:body" ] [ content ] htmlFoot ] ] diff --git a/src/MyPrayerJournal/Server/wwwroot/script/mpj.js b/src/MyPrayerJournal/Server/wwwroot/script/mpj.js index 76747dd..6a62369 100644 --- a/src/MyPrayerJournal/Server/wwwroot/script/mpj.js +++ b/src/MyPrayerJournal/Server/wwwroot/script/mpj.js @@ -19,10 +19,12 @@ const mpj = { }, /** Whether the user is currently authenticated */ isAuthenticated: false, + /** Whether we should redirect to the journal the next time the menu items are refreshed */ + redirToJournal: false, /** Process a log on request */ logOn: async (e) => { e.preventDefault() - await mpj.auth.auth0.loginWithRedirect({ redirect_uri: window.location.origin }) + await mpj.auth.auth0.loginWithRedirect({ redirect_uri: `${window.location.origin}/user/log-on` }) }, /** Log the user off */ logOff: (e) => { @@ -54,6 +56,27 @@ window.onload = async () => { if (query.includes("code=") && query.includes("state=")) { await mpj.auth.auth0.handleRedirectCallback() await establishAuth() - window.history.replaceState({}, document.title, "/") + if (window.location.pathname === "/user/log-on") { + mpj.redirToJournal = true + } else { + window.history.replaceState({}, document.title, "/") + } } } + +htmx.on("htmx:afterOnLoad", function (evt) { + // Set the page title if a header was in the response + if (evt.detail.xhr.getAllResponseHeaders().indexOf("x-page-title") >= 0) { + const title = document.querySelector("title") + title.innerText = evt.detail.xhr.getResponseHeader("x-page-title") + title.innerHTML += " « myPrayerJournal" + } +}) +htmx.on("htmx:afterSettle", function (evt) { + // Redirect to the journal (once menu items load after log on) + if (mpj.redirToJournal + && ([...evt.target.attributes].find(it => it.name === "hx-target")?.value ?? "") === ".navbar-nav") { + mpj.redirToJournal = false + document.querySelector(`a[href="/journal"]`).click() + } +})