From 9d2277ab327fbf9705242f6f6e2d9375ac2b3b8f Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Sun, 5 Jul 2026 17:54:36 -0400 Subject: [PATCH] WIP on adding htmax option (#59) --- src/MyWebLog.Data/Converters.fs | 11 +++++++++- src/MyWebLog.Data/PostgresData.fs | 2 ++ src/MyWebLog.Data/RethinkDbData.fs | 12 +++++++++++ src/MyWebLog.Data/SQLiteData.fs | 2 ++ src/MyWebLog.Domain/DataTypes.fs | 4 ++-- src/MyWebLog.Domain/SupportTypes.fs | 32 +++++++++++++++++++++++++++++ src/MyWebLog.Domain/ViewModels.fs | 6 +++--- src/MyWebLog/DotLiquidBespoke.fs | 11 ++++++---- src/MyWebLog/Program.fs | 6 +++--- src/MyWebLog/Template.fs | 10 +++++++-- 10 files changed, 81 insertions(+), 15 deletions(-) diff --git a/src/MyWebLog.Data/Converters.fs b/src/MyWebLog.Data/Converters.fs index e8469d3..2b70e97 100644 --- a/src/MyWebLog.Data/Converters.fs +++ b/src/MyWebLog.Data/Converters.fs @@ -9,6 +9,14 @@ module Json = open Newtonsoft.Json + /// Converter for the type + type AutoHtmxTypeConverter() = + inherit JsonConverter() + override _.WriteJson(writer: JsonWriter, value: AutoHtmxType, _: JsonSerializer) = + writer.WriteValue(string value) + override _.ReadJson(reader: JsonReader, _: Type, _: AutoHtmxType, _: bool, _: JsonSerializer) = + (string >> AutoHtmxType.Parse) reader.Value + /// Converter for the type type CategoryIdConverter() = inherit JsonConverter() @@ -160,7 +168,8 @@ module Json = /// Configure a serializer to use these converters (and other settings) let configure (ser: JsonSerializer) = // Our converters - [ CategoryIdConverter() :> JsonConverter + [ AutoHtmxTypeConverter() :> JsonConverter + CategoryIdConverter() CommentIdConverter() CommentStatusConverter() CustomFeedIdConverter() diff --git a/src/MyWebLog.Data/PostgresData.fs b/src/MyWebLog.Data/PostgresData.fs index 14b6f21..0fde5f5 100644 --- a/src/MyWebLog.Data/PostgresData.fs +++ b/src/MyWebLog.Data/PostgresData.fs @@ -228,6 +228,8 @@ type PostgresData(log: ILogger, ser: JsonSerializer) = let migrateV2point2ToV3 () = backgroundTask { Utils.Migration.logStep log "v2.2 to v3" "Adding auto-OpenGraph flag to all web logs" do! Patch.byFields Table.WebLog Any [ Field.Exists (nameof WebLog.Empty.Id) ] {| AutoOpenGraph = true |} + do! Patch.byContains Table.WebLog {| AutoHtmx = true |} {| AutoHtmx = "Yes" |} + do! Patch.byContains Table.WebLog {| AutoHtmx = false |} {| AutoHtmx = "No" |} Utils.Migration.logStep log "v2.2 to v3" "Setting database version to v3" return! setDbVersion "v3" } diff --git a/src/MyWebLog.Data/RethinkDbData.fs b/src/MyWebLog.Data/RethinkDbData.fs index 18e0cf5..1a557cc 100644 --- a/src/MyWebLog.Data/RethinkDbData.fs +++ b/src/MyWebLog.Data/RethinkDbData.fs @@ -264,6 +264,18 @@ type RethinkDbData(conn: Net.IConnection, config: DataConfig, log: ILogger obj ] write; withRetryOnce; ignoreResult conn } + do! rethink { + withTable Table.WebLog + filter (nameof WebLog.Empty.AutoHtmx) true + update [ nameof WebLog.Empty.AutoHtmx, "Yes" ] + write; withRetryOnce; ignoreResult conn + } + do! rethink { + withTable Table.WebLog + filter (nameof WebLog.Empty.AutoHtmx) false + update [ nameof WebLog.Empty.AutoHtmx, "No" ] + write; withRetryOnce; ignoreResult conn + } Utils.Migration.logStep log "v2.2 to v3" "Setting database version to v3" do! setDbVersion "v3" } diff --git a/src/MyWebLog.Data/SQLiteData.fs b/src/MyWebLog.Data/SQLiteData.fs index 685f14c..48e5bd6 100644 --- a/src/MyWebLog.Data/SQLiteData.fs +++ b/src/MyWebLog.Data/SQLiteData.fs @@ -456,6 +456,8 @@ type SQLiteData(conn: SqliteConnection, log: ILogger, ser: JsonSeria let migrateV2point2ToV3 () = backgroundTask { Utils.Migration.logStep log "v2.2 to v3" "Adding auto-OpenGraph flag to all web logs" do! Patch.byFields Table.WebLog Any [ Field.Exists (nameof WebLog.Empty.Id) ] {| AutoOpenGraph = true |} + do! Patch.byFields Table.WebLog Any [ Field.Equal (nameof WebLog.Empty.AutoHtmx) true ] {| AutoHtmx = "Yes" |} + do! Patch.byFields Table.WebLog Any [ Field.Equal (nameof WebLog.Empty.AutoHtmx) false ] {| AutoHtmx = "No" |} Utils.Migration.logStep log "v2.2 to v3" "Setting database version to v3" do! setDbVersion "v3" } diff --git a/src/MyWebLog.Domain/DataTypes.fs b/src/MyWebLog.Domain/DataTypes.fs index 88f6134..b161704 100644 --- a/src/MyWebLog.Domain/DataTypes.fs +++ b/src/MyWebLog.Domain/DataTypes.fs @@ -341,7 +341,7 @@ type WebLog = { Rss: RssOptions /// Whether to automatically load htmx - AutoHtmx: bool + AutoHtmx: AutoHtmxType /// Where uploads are placed Uploads: UploadDestination @@ -365,7 +365,7 @@ type WebLog = { UrlBase = "" TimeZone = "" Rss = RssOptions.Empty - AutoHtmx = false + AutoHtmx = NoAutoHtmx Uploads = Database RedirectRules = [] AutoOpenGraph = true } diff --git a/src/MyWebLog.Domain/SupportTypes.fs b/src/MyWebLog.Domain/SupportTypes.fs index 7403e6e..79c24cc 100644 --- a/src/MyWebLog.Domain/SupportTypes.fs +++ b/src/MyWebLog.Domain/SupportTypes.fs @@ -105,6 +105,38 @@ type AccessLevel = AccessLevel.Weights[needed] <= AccessLevel.Weights[this] +/// How the htmx library will be auto-injected in a web log's theme pages +[] +type AutoHtmxType = + + /// Do not auto-inject htmx into the web log's theme pages + | NoAutoHtmx + + /// Inject the standard htmx library into the web log's theme pages + | AutoHtmx + + /// Inject the htmax library bundle into the web log's theme pages + | AutoHtmax + + /// Parse an auto-htmx type from its string representation + /// The string representation to be parsed + /// The AutoHtmxType instance parsed from the string + /// If the string is not valid + static member Parse typ = + match typ with + | "No" -> NoAutoHtmx + | "Yes" -> AutoHtmx + | "Max" -> AutoHtmax + | _ -> invalidArg (nameof typ) $"{typ} is not a valid auto-htmx type" + + /// + override this.ToString() = + match this with + | NoAutoHtmx -> "No" + | AutoHtmx -> "Yes" + | AutoHtmax -> "Max" + + /// An identifier for a category [] type CategoryId = diff --git a/src/MyWebLog.Domain/ViewModels.fs b/src/MyWebLog.Domain/ViewModels.fs index 2ec79f6..c94f899 100644 --- a/src/MyWebLog.Domain/ViewModels.fs +++ b/src/MyWebLog.Domain/ViewModels.fs @@ -1379,7 +1379,7 @@ type SettingsModel = { ThemeId: string /// Whether to automatically load htmx - AutoHtmx: bool + AutoHtmx: string /// The default location for uploads Uploads: string @@ -1399,7 +1399,7 @@ type SettingsModel = { PostsPerPage = webLog.PostsPerPage TimeZone = webLog.TimeZone ThemeId = string webLog.ThemeId - AutoHtmx = webLog.AutoHtmx + AutoHtmx = string webLog.AutoHtmx Uploads = string webLog.Uploads AutoOpenGraph = webLog.AutoOpenGraph } @@ -1415,7 +1415,7 @@ type SettingsModel = { PostsPerPage = this.PostsPerPage TimeZone = this.TimeZone ThemeId = ThemeId this.ThemeId - AutoHtmx = this.AutoHtmx + AutoHtmx = AutoHtmxType.Parse this.AutoHtmx Uploads = UploadDestination.Parse this.Uploads AutoOpenGraph = this.AutoOpenGraph } diff --git a/src/MyWebLog/DotLiquidBespoke.fs b/src/MyWebLog/DotLiquidBespoke.fs index 819d48a..2748712 100644 --- a/src/MyWebLog/DotLiquidBespoke.fs +++ b/src/MyWebLog/DotLiquidBespoke.fs @@ -5,9 +5,7 @@ open System open System.IO open System.Web open DotLiquid -open Giraffe.ViewEngine open MyWebLog.ViewModels -open MyWebLog.Views /// Extensions on the DotLiquid Context object type Context with @@ -158,8 +156,13 @@ type PageFootTag() = // spacer let s = " " - if webLog.AutoHtmx then - result.WriteLine $"{s}{RenderView.AsString.htmlNode Htmx.Script.local}" + match webLog.AutoHtmx with + | NoAutoHtmx -> None + | AutoHtmx -> (Permalink >> Some) Giraffe.Htmx.Common.StaticAssetUrl.htmx[1..] + | AutoHtmax -> (Permalink >> Some) Giraffe.Htmx.Common.StaticAssetUrl.htmax[1..] + |> function + | Some url -> result.WriteLine $"""{s}""" + | None -> () if assetExists "script.js" webLog then result.WriteLine $"""{s}""" diff --git a/src/MyWebLog/Program.fs b/src/MyWebLog/Program.fs index e5cb948..61df3c6 100644 --- a/src/MyWebLog/Program.fs +++ b/src/MyWebLog/Program.fs @@ -240,9 +240,9 @@ let main args = let _ = app.UseForwardedHeaders() - (app.Services.GetRequiredService().GetSection "CanonicalDomains").Value - |> isNotNull - |> function true -> app.UseCanonicalDomains() |> ignore | false -> () + match (app.Services.GetRequiredService().GetSection "CanonicalDomains").Value with + | null -> () + | _ -> app.UseCanonicalDomains() |> ignore app .UseCookiePolicy(CookiePolicyOptions(MinimumSameSitePolicy = SameSiteMode.Strict)) .UseMiddleware() diff --git a/src/MyWebLog/Template.fs b/src/MyWebLog/Template.fs index e522a5d..688e95a 100644 --- a/src/MyWebLog/Template.fs +++ b/src/MyWebLog/Template.fs @@ -255,10 +255,16 @@ let parser = it.RegisterEmptyTag("page_foot", fun writer encoder context -> let webLog = context.App.WebLog - if webLog.AutoHtmx then - context.App.WebLog.RelativeUrl(Permalink StaticAssetUrl.htmx[1..]) + match webLog.AutoHtmx with + | NoAutoHtmx -> None + | AutoHtmx -> (Permalink >> Some) StaticAssetUrl.htmx[1..] + | AutoHtmax -> (Permalink >> Some) StaticAssetUrl.htmax[1..] + |> function + | Some url -> + context.App.WebLog.RelativeUrl url |> sprintf "%s" s |> writer.WriteLine + | None -> () if assetExists "script.js" webLog then themeAsset (StringValue "script.js") context |> sprintf "%s" s