commits
Lewis: May this revision serve well! <lewis@tangled.org>
Lewis: May this revision serve well! <lewis@tangled.org>
Lewis: May this revision serve well! <lewis@tangled.org>
Lewis: May this revision serve well! <lewis@tangled.org>
Lewis: May this revision serve well! <lewis@tangled.org>
appview,knotserver: validate git repo ownership according to knot
Lewis: May this revision serve well! <lewis@tangled.org>
Lewis: May this revision serve well! <lewis@tangled.org>
Lewis: May this revision serve well! <lewis@tangled.org>
Lewis: May this revision serve well! <lewis@tangled.org>
Lewis: May this revision serve well! <lewis@tangled.org>
Lewis: May this revision serve well! <lewis@tangled.org>
Lewis: May this revision serve well! <lewis@tangled.org>
Signed-off-by: oppiliappan <me@oppi.li>
Signed-off-by: oppiliappan <me@oppi.li>
Signed-off-by: Anirudh Oppiliappan <anirudh@tangled.org>
Signed-off-by: oppiliappan <me@oppi.li>
Signed-off-by: oppiliappan <me@oppi.li>
Signed-off-by: oppiliappan <me@oppi.li>
Signed-off-by: oppiliappan <me@oppi.li>
Signed-off-by: oppiliappan <me@oppi.li>
Signed-off-by: oppiliappan <me@oppi.li>
Signed-off-by: Anirudh Oppiliappan <anirudh@tangled.org>
Signed-off-by: Seongmin Lee <git@boltless.me>
will just create an empty commit with title/body if a mailbox is
supplied with an empty patch.
Signed-off-by: oppiliappan <me@oppi.li>
set `alignItems: "center"` to footer elements.
reduce `paddingBottom` to `36` for issue & prs cards.
Signed-off-by: eti <eti@eti.tf>
Standalone tailwindcss embeds frozen caniuse-lite, triggers Browserslist warning on startup. Set BROWSERSLIST_IGNORE_OLD_DATA to silence it.
Signed-off-by: eti <eti@eti.tf>
Signed-off-by: eti <eti@eti.tf>
Signed-off-by: Matías Insaurralde <matias@insaurral.de>
Signed-off-by: Patrick Dewey <p@pdewey.com>
larger hitslop for thumbs
Signed-off-by: oppiliappan <me@oppi.li>
This is API call is idempotent. If the contact doesn't previously exist
on Resend, adding to Segment will fail.
Signed-off-by: Anirudh Oppiliappan <anirudh@tangled.org>
Signed-off-by: oppiliappan <me@oppi.li>
Signed-off-by: oppiliappan <me@oppi.li>
Signed-off-by: oppiliappan <me@oppi.li>
the newsletter widget used only localStorage to remember whether a user
had signed up or dismissed it, so the cta kept reappearing whenever a
user opened tangled on another device or browser. for logged-in users,
store the state in a newsletter_preferences table keyed on did with an
enum status ('subscribed' | 'dismissed') and the email they gave us.
the home and timeline handlers read this row to decide whether to
render the widget, and the server-rendered gfi banner widens when the
widget is gone so the grid doesn't leave an empty column.
resend stays the source of truth for the mailing list itself (sending,
bounces, one-click unsubscribes) — the new table only answers the
render-time question 'should this did see the widget right now?',
which resend cannot cheaply answer because it's keyed on email rather
than did and would add a network hop to every timeline render.
anonymous visitors keep the localStorage fallback. the client-side
'already dismissed in a past session' path deliberately only calls
hide() (not dismiss()) so that a stale localStorage flag can't clobber
a subscribed row set from another device.
Signed-off-by: eti <eti@eti.tf>
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-<Id>) 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 <eti@eti.tf>
upgrade to resend-go/v3 and add a newsletter signup endpoint. replace
the join CTA with a newsletter signup form. rework the timeline page to
be a two-column layout on desktop.
Signed-off-by: Anirudh Oppiliappan <anirudh@tangled.org>
Signed-off-by: eti <eti@eti.tf>
Signed-off-by: Matías Insaurralde <matias@insaurral.de>
Signed-off-by: Matías Insaurralde <matias@insaurral.de>
Avoid allocating a new http.Client on every resyncRepo call.
http.Client is safe for concurrent use and reusing it enables
TCP connection pooling across checks to the same knot.
Signed-off-by: Matías Insaurralde <matias@insaurral.de>
Signed-off-by: eti <eti@eti.tf>
Signed-off-by: Techno Duck <duck@technoduck.me>
Replace the footer's leading slot with the author's avatar and handle for
issue and pull request cards. For long handles, ellipsize and drop the
reaction and comment counts to keep the tangled logo visible.
appview/{issues,pulls}: send author info to ogre
Resolve the author's handle and avatar URL from the issue/pull owner DID
and pass them in the payload so ogre can render the author in the card
footer.
Signed-off-by: eti eti@eti.tf
That's token is meant to be public.
Signed-off-by: Anirudh Oppiliappan <anirudh@tangled.org>
Sets the distinct id to 'anonymous' for logged out searches. Let's stick
to this for any future logged out events.
Signed-off-by: Anirudh Oppiliappan <anirudh@tangled.org>
having a primary call to action always present in the nav bar was
creating issues with other call to actions at a hierarchical level
this change prevents buttons clashing against each other because of
their level of importance
*As a rule of thumb, the main call to action on a page should be
heaviest in weight when compared to other buttons and/or links. All
other action items should appear secondary in terms of color, placement,
shape, and overall weight allocation.*
refs:
-
https://www.smashingmagazine.com/2009/10/call-to-action-buttons-examples-and-best-practices/#offer-secondary-alternative-actions
-
https://uxmag.com/articles/usability-tip-one-main-call-to-action-item-per-task#:~:text=As%20a%20rule,to%20move%20forward.
Signed-off-by: eti <eti@eti.tf>
Lewis: May this revision serve well! <lewis@tangled.org>
Lewis: May this revision serve well! <lewis@tangled.org>
the newsletter widget used only localStorage to remember whether a user
had signed up or dismissed it, so the cta kept reappearing whenever a
user opened tangled on another device or browser. for logged-in users,
store the state in a newsletter_preferences table keyed on did with an
enum status ('subscribed' | 'dismissed') and the email they gave us.
the home and timeline handlers read this row to decide whether to
render the widget, and the server-rendered gfi banner widens when the
widget is gone so the grid doesn't leave an empty column.
resend stays the source of truth for the mailing list itself (sending,
bounces, one-click unsubscribes) — the new table only answers the
render-time question 'should this did see the widget right now?',
which resend cannot cheaply answer because it's keyed on email rather
than did and would add a network hop to every timeline render.
anonymous visitors keep the localStorage fallback. the client-side
'already dismissed in a past session' path deliberately only calls
hide() (not dismiss()) so that a stale localStorage flag can't clobber
a subscribed row set from another device.
Signed-off-by: eti <eti@eti.tf>
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-<Id>) 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 <eti@eti.tf>
Replace the footer's leading slot with the author's avatar and handle for
issue and pull request cards. For long handles, ellipsize and drop the
reaction and comment counts to keep the tangled logo visible.
appview/{issues,pulls}: send author info to ogre
Resolve the author's handle and avatar URL from the issue/pull owner DID
and pass them in the payload so ogre can render the author in the card
footer.
Signed-off-by: eti eti@eti.tf
having a primary call to action always present in the nav bar was
creating issues with other call to actions at a hierarchical level
this change prevents buttons clashing against each other because of
their level of importance
*As a rule of thumb, the main call to action on a page should be
heaviest in weight when compared to other buttons and/or links. All
other action items should appear secondary in terms of color, placement,
shape, and overall weight allocation.*
refs:
-
https://www.smashingmagazine.com/2009/10/call-to-action-buttons-examples-and-best-practices/#offer-secondary-alternative-actions
-
https://uxmag.com/articles/usability-tip-one-main-call-to-action-item-per-task#:~:text=As%20a%20rule,to%20move%20forward.
Signed-off-by: eti <eti@eti.tf>