From 97ca49ba9114b18ca6ed024c1f4fb2ac63d006c3 Mon Sep 17 00:00:00 2001 From: eti Date: Tue, 21 Apr 2026 17:24:50 +0200 Subject: [PATCH] appview: deduplicate newsletter widget, single-render timeline grid the newsletter widget was being rendered twice on the timeline (once for mobile, once for desktop) via hidden/visible wrapper divs. both copies shared the same element ids (newsletter-widget, newsletter-widget-msg), so desktop submissions swapped into the hidden mobile copy and appeared to do nothing. collapse the two layouts into one responsive grid with tailwind order/col-start utilities so the widget lives in the dom exactly once, and drop the mutationobserver wiring in favour of a small delegated click listener that also handles gfi banner widening on dismiss. consolidate the three signup form variants (banner, widget, home hero) into a single newsletterForm fragment that takes a dict(Id, Variant). each instance gets a unique response-span id (newsletter-msg-) so multiple forms can coexist on a page without id collisions. move the handler's inline html/tailwind response strings into a newsletterResponse template rendered through pages, so the response id round-trips with the form's hx-target via an hx-vals target field. drop the stray blank line in base.html. Signed-off-by: eti --- appview/pages/pages.go | 12 +++ appview/pages/templates/layouts/base.html | 1 - .../timeline/fragments/newsletter.html | 16 ---- .../timeline/fragments/newsletterForm.html | 55 ++++++++++++ .../fragments/newsletterResponse.html | 16 ++++ .../timeline/fragments/newsletterWidget.html | 29 +------ appview/pages/templates/timeline/home.html | 26 ++---- .../pages/templates/timeline/timeline.html | 84 +++++++++++-------- appview/state/state.go | 19 ++++- 9 files changed, 155 insertions(+), 103 deletions(-) delete mode 100644 appview/pages/templates/timeline/fragments/newsletter.html create mode 100644 appview/pages/templates/timeline/fragments/newsletterForm.html create mode 100644 appview/pages/templates/timeline/fragments/newsletterResponse.html diff --git a/appview/pages/pages.go b/appview/pages/pages.go index d0988ee2..15dff888 100644 --- a/appview/pages/pages.go +++ b/appview/pages/pages.go @@ -523,6 +523,18 @@ func (p *Pages) UpgradeBanner(w io.Writer, params UpgradeBannerParams) error { return p.executePlain("banner", w, params) } +type NewsletterResponseParams struct { + // Id identifies the calling form instance; the response span's id will + // be "newsletter-msg-" so it round-trips with the form's hx-target. + Id string + // Error, when non-empty, switches the template to the error variant. + Error string +} + +func (p *Pages) NewsletterResponse(w io.Writer, params NewsletterResponseParams) error { + return p.executePlain("timeline/fragments/newsletterResponse", w, params) +} + type KnotsParams struct { LoggedInUser *oauth.MultiAccountUser Registrations []models.Registration diff --git a/appview/pages/templates/layouts/base.html b/appview/pages/templates/layouts/base.html index e8538b25..bad39985 100644 --- a/appview/pages/templates/layouts/base.html +++ b/appview/pages/templates/layouts/base.html @@ -61,7 +61,6 @@ {{ template "fragments/posthog" . }} - {{ block "topbarLayout" . }}
diff --git a/appview/pages/templates/timeline/fragments/newsletter.html b/appview/pages/templates/timeline/fragments/newsletter.html deleted file mode 100644 index 3e05d912..00000000 --- a/appview/pages/templates/timeline/fragments/newsletter.html +++ /dev/null @@ -1,16 +0,0 @@ -{{ define "timeline/fragments/newsletter" }} -
-
-

We've got a newsletter! Punch in your email to get our updates sent straight to your inbox.

- - - - - -
-
- -{{ end }} diff --git a/appview/pages/templates/timeline/fragments/newsletterForm.html b/appview/pages/templates/timeline/fragments/newsletterForm.html new file mode 100644 index 00000000..11a4630d --- /dev/null +++ b/appview/pages/templates/timeline/fragments/newsletterForm.html @@ -0,0 +1,55 @@ +{{/* + Shared newsletter signup form. Variants style the form differently, but the + POST target, response-target id and request plumbing stay identical. + + Params (dict): + Id string - unique id suffix (e.g. "widget", "home"); used to build + the response span's id so multiple signup forms can + coexist on one page without colliding. + Variant string - "card" (compact sidebar card) | "hero" (large CTA) +*/}} +{{ define "timeline/fragments/newsletterForm" }} + {{ $id := .Id }} + {{ $variant := .Variant }} + {{ if eq $variant "hero" }} +
+ + + + +
+ {{ else }} +
+ + + + +
+ {{ end }} +{{ end }} diff --git a/appview/pages/templates/timeline/fragments/newsletterResponse.html b/appview/pages/templates/timeline/fragments/newsletterResponse.html new file mode 100644 index 00000000..a122689c --- /dev/null +++ b/appview/pages/templates/timeline/fragments/newsletterResponse.html @@ -0,0 +1,16 @@ +{{/* + Response fragment swapped into place by htmx after a /newsletter/signup + submission. Returns a whose id matches the form's hx-target so the + swap is clean and no hard-coded id leaks into Go. + + Params: + Id string - matches the Id used in newsletterForm (e.g. "widget", "home") + Error string - if non-empty, renders the error variant +*/}} +{{ define "timeline/fragments/newsletterResponse" }} + {{ if .Error }} + {{ .Error }} + {{ else }} + You're signed up! + {{ end }} +{{ end }} diff --git a/appview/pages/templates/timeline/fragments/newsletterWidget.html b/appview/pages/templates/timeline/fragments/newsletterWidget.html index afa81307..6098aaf2 100644 --- a/appview/pages/templates/timeline/fragments/newsletterWidget.html +++ b/appview/pages/templates/timeline/fragments/newsletterWidget.html @@ -1,13 +1,10 @@ {{ define "timeline/fragments/newsletterWidget" }}
+ class="relative border border-green-800/15 dark:border-green-700 rounded bg-green-50 dark:bg-green-950 p-4"> - - + {{ template "timeline/fragments/newsletterForm" (dict "Id" "widget" "Variant" "card") }}
- {{ end }} diff --git a/appview/pages/templates/timeline/home.html b/appview/pages/templates/timeline/home.html index d1804d16..a0ca103b 100644 --- a/appview/pages/templates/timeline/home.html +++ b/appview/pages/templates/timeline/home.html @@ -507,25 +507,11 @@ {{ define "newsletter" }}
-

Stay in the loop

-

- We've got a newsletter! Punch in your email to stay updated on what we're shipping at Tangled. -

-
- - - - -
+

Stay in the loop

+

+ We've got a newsletter! Punch in your email to stay updated on what we're shipping at Tangled. +

+ {{ template "timeline/fragments/newsletterForm" (dict "Id" "home" "Variant" "hero") }} +
{{ end }} diff --git a/appview/pages/templates/timeline/timeline.html b/appview/pages/templates/timeline/timeline.html index a0286385..e2d6f344 100644 --- a/appview/pages/templates/timeline/timeline.html +++ b/appview/pages/templates/timeline/timeline.html @@ -16,62 +16,74 @@ {{ end }} {{ define "content" }} -
- {{ template "timeline/fragments/newsletter" . }} - {{ template "timeline/fragments/goodfirstissues" . }} - {{ template "timeline/fragments/trending" . }} - {{ template "timeline/fragments/timeline" . }} -
+{{/* + Single responsive grid. Every fragment is rendered once, with CSS alone + handling the mobile → desktop reflow. Only trending has two variants + (horizontal scroll for mobile, vertical sidebar for desktop); both are + present in the DOM and toggled via utility classes. -