From 0cba307e36aac866fb00444b60fc19c6fdb14af3 Mon Sep 17 00:00:00 2001 From: Sergio Vera Date: Sun, 14 Jul 2024 20:16:47 +0200 Subject: [PATCH] Use response targets extension --- internal/webserver/controller/user/update.go | 4 +- .../embedded/js/foliate-js/reader.js | 2 +- .../webserver/embedded/js/response-targets.js | 129 ++++++++++++++++++ .../webserver/embedded/js/xh-error-check.js | 6 +- internal/webserver/embedded/views/layout.html | 57 ++------ .../embedded/views/partials/main.html | 44 ++++++ .../webserver/embedded/views/users/edit.html | 6 +- 7 files changed, 194 insertions(+), 54 deletions(-) create mode 100644 internal/webserver/embedded/js/response-targets.js create mode 100644 internal/webserver/embedded/views/partials/main.html diff --git a/internal/webserver/controller/user/update.go b/internal/webserver/controller/user/update.go index 02bb8df..64f4d18 100644 --- a/internal/webserver/controller/user/update.go +++ b/internal/webserver/controller/user/update.go @@ -57,7 +57,7 @@ func (u *Controller) updateUserData(c *fiber.Ctx, user *model.User, session mode "MinPasswordLength": u.config.MinPasswordLength, "UsernamePattern": model.UsernamePattern, "Errors": validationErrs, - }, "layout") + }, "partials/main") } if err := u.repository.Update(user); err != nil { @@ -89,7 +89,7 @@ func (u *Controller) updateUserData(c *fiber.Ctx, user *model.User, session mode "UsernamePattern": model.UsernamePattern, "Errors": validationErrs, "Message": "Profile updated", - }, "layout") + }, "partials/main") } func (u *Controller) validate(c *fiber.Ctx, user *model.User, session model.Session) (map[string]string, error) { diff --git a/internal/webserver/embedded/js/foliate-js/reader.js b/internal/webserver/embedded/js/foliate-js/reader.js index 1b3efa5..9dc4ee6 100644 --- a/internal/webserver/embedded/js/foliate-js/reader.js +++ b/internal/webserver/embedded/js/foliate-js/reader.js @@ -245,7 +245,7 @@ const url = document.getElementById('url').value if (url) fetch(url) .then(res => { if (res.status == 403) { - location.reload() + return location.reload() } return res.blob() }) diff --git a/internal/webserver/embedded/js/response-targets.js b/internal/webserver/embedded/js/response-targets.js new file mode 100644 index 0000000..951a709 --- /dev/null +++ b/internal/webserver/embedded/js/response-targets.js @@ -0,0 +1,129 @@ +(function() { + /** @type {import("../htmx").HtmxInternalApi} */ + var api + + var attrPrefix = 'hx-target-' + + // IE11 doesn't support string.startsWith + function startsWith(str, prefix) { + return str.substring(0, prefix.length) === prefix + } + + /** + * @param {HTMLElement} elt + * @param {number} respCode + * @returns {HTMLElement | null} + */ + function getRespCodeTarget(elt, respCodeNumber) { + if (!elt || !respCodeNumber) return null + + var respCode = respCodeNumber.toString() + + // '*' is the original syntax, as the obvious character for a wildcard. + // The 'x' alternative was added for maximum compatibility with HTML + // templating engines, due to ambiguity around which characters are + // supported in HTML attributes. + // + // Start with the most specific possible attribute and generalize from + // there. + var attrPossibilities = [ + respCode, + + respCode.substr(0, 2) + '*', + respCode.substr(0, 2) + 'x', + + respCode.substr(0, 1) + '*', + respCode.substr(0, 1) + 'x', + respCode.substr(0, 1) + '**', + respCode.substr(0, 1) + 'xx', + + '*', + 'x', + '***', + 'xxx' + ] + if (startsWith(respCode, '4') || startsWith(respCode, '5')) { + attrPossibilities.push('error') + } + + for (var i = 0; i < attrPossibilities.length; i++) { + var attr = attrPrefix + attrPossibilities[i] + var attrValue = api.getClosestAttributeValue(elt, attr) + if (attrValue) { + if (attrValue === 'this') { + return api.findThisElement(elt, attr) + } else { + return api.querySelectorExt(elt, attrValue) + } + } + } + + return null + } + + /** @param {Event} evt */ + function handleErrorFlag(evt) { + if (evt.detail.isError) { + if (htmx.config.responseTargetUnsetsError) { + evt.detail.isError = false + } + } else if (htmx.config.responseTargetSetsError) { + evt.detail.isError = true + } + } + + htmx.defineExtension('response-targets', { + + /** @param {import("../htmx").HtmxInternalApi} apiRef */ + init: function(apiRef) { + api = apiRef + + if (htmx.config.responseTargetUnsetsError === undefined) { + htmx.config.responseTargetUnsetsError = true + } + if (htmx.config.responseTargetSetsError === undefined) { + htmx.config.responseTargetSetsError = false + } + if (htmx.config.responseTargetPrefersExisting === undefined) { + htmx.config.responseTargetPrefersExisting = false + } + if (htmx.config.responseTargetPrefersRetargetHeader === undefined) { + htmx.config.responseTargetPrefersRetargetHeader = true + } + }, + + /** + * @param {string} name + * @param {Event} evt + */ + onEvent: function(name, evt) { + if (name === 'htmx:beforeSwap' && + evt.detail.xhr && + evt.detail.xhr.status !== 200) { + if (evt.detail.target) { + if (htmx.config.responseTargetPrefersExisting) { + evt.detail.shouldSwap = true + handleErrorFlag(evt) + return true + } + if (htmx.config.responseTargetPrefersRetargetHeader && + evt.detail.xhr.getAllResponseHeaders().match(/HX-Retarget:/i)) { + evt.detail.shouldSwap = true + handleErrorFlag(evt) + return true + } + } + if (!evt.detail.requestConfig) { + return true + } + var target = getRespCodeTarget(evt.detail.requestConfig.elt, evt.detail.xhr.status) + if (target) { + handleErrorFlag(evt) + evt.detail.shouldSwap = true + evt.detail.target = target + } + return true + } + } + }) + })() \ No newline at end of file diff --git a/internal/webserver/embedded/js/xh-error-check.js b/internal/webserver/embedded/js/xh-error-check.js index 5fa7836..485bb66 100644 --- a/internal/webserver/embedded/js/xh-error-check.js +++ b/internal/webserver/embedded/js/xh-error-check.js @@ -8,8 +8,12 @@ document.body.addEventListener('htmx:afterRequest', function (evt) { // Server error with response contents, equivalent to htmx:responseError const xhr = evt.detail.xhr; if (xhr.status == "403") { - location.reload(); + location.reload() } + if (xhr.status == "400") { + return + } + console.warn("Server error", evt.detail) errorTarget.innerText = `Unexpected server error: ${xhr.status} - ${xhr.statusText}`; errorTarget.removeAttribute("hidden"); diff --git a/internal/webserver/embedded/views/layout.html b/internal/webserver/embedded/views/layout.html index 3ed3f87..c684dc8 100644 --- a/internal/webserver/embedded/views/layout.html +++ b/internal/webserver/embedded/views/layout.html @@ -22,7 +22,7 @@ - +
-
-
- {{if .RemainingIndexingTime}} -
- -
- {{end}} - -
- -
- - {{if .Error}} -
- -
- {{end}} - - {{if .Warning}} -
- -
- {{end}} - - {{if .Message}} -
- -
- {{end}} - - {{embed}} -
+ {{template "partials/main" + dict "Lang" .Lang + "RemainingIndexingTime" .RemainingIndexingTime + "IndexingProgressPercentage" .IndexingProgressPercentage + "Error" .Error + "Warning" .Warning + "Message" .Message + "Embed" .Embed}}
- + diff --git a/internal/webserver/embedded/views/partials/main.html b/internal/webserver/embedded/views/partials/main.html new file mode 100644 index 0000000..30d7b49 --- /dev/null +++ b/internal/webserver/embedded/views/partials/main.html @@ -0,0 +1,44 @@ +
+ {{if .RemainingIndexingTime}} +
+ +
+ {{end}} + +
+ +
+ + {{if .Error}} +
+ +
+ {{end}} + + {{if .Warning}} +
+ +
+ {{end}} + + {{if .Message}} +
+ +
+ {{end}} + + {{embed}} +
diff --git a/internal/webserver/embedded/views/users/edit.html b/internal/webserver/embedded/views/users/edit.html index 525c798..2998371 100644 --- a/internal/webserver/embedded/views/users/edit.html +++ b/internal/webserver/embedded/views/users/edit.html @@ -8,10 +8,10 @@ type="button" role="tab" aria-controls="profile-tab-pane" aria-selected="false">{{t .Lang "Change password"}} -
+
-
+
@@ -103,4 +103,4 @@
- \ No newline at end of file + \ No newline at end of file