nandi/gleanpublic⑂ Fork 0
⑂ eed7f2e
Commits
⬇ Clone ▾
git clone https://git.rickub.com/nandi/glean.git
git clone ssh://git@rickub.com/nandi/glean.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Handle empty user fields in navbar and improve UI responsivenessUnverified

Julien Robert committed 2026-04-21T22:01:07+02:00 Browse files
eed7f2e parent: 8467321
modified internal/db/user.go +3 -3
@@ -19,9 +19,9 @@ func (db *DB) CreateUser(ctx context.Context, did, handle, displayName, avatarUR
1919 INSERT INTO users (did, handle, display_name, avatar_url, updated_at)
2020 VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP)
2121 ON CONFLICT(did) DO UPDATE SET
22- handle = excluded.handle,
23- display_name = excluded.display_name,
24- avatar_url = excluded.avatar_url,
22+ handle = COALESCE(NULLIF(excluded.handle, ''), users.handle),
23+ display_name = COALESCE(NULLIF(excluded.display_name, ''), users.display_name),
24+ avatar_url = COALESCE(NULLIF(excluded.avatar_url, ''), users.avatar_url),
2525 updated_at = CURRENT_TIMESTAMP
2626 `, did, handle, displayName, avatarURL)
2727 if err != nil {
@@ -19,9 +19,9 @@ func (db *DB) CreateUser(ctx context.Context, did, handle, displayName, avatarUR
19 INSERT INTO users (did, handle, display_name, avatar_url, updated_at)19 INSERT INTO users (did, handle, display_name, avatar_url, updated_at)
20 VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP)20 VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP)
21 ON CONFLICT(did) DO UPDATE SET21 ON CONFLICT(did) DO UPDATE SET
22- handle = excluded.handle,22+ handle = COALESCE(NULLIF(excluded.handle, ''), users.handle),
23- display_name = excluded.display_name,23+ display_name = COALESCE(NULLIF(excluded.display_name, ''), users.display_name),
24- avatar_url = excluded.avatar_url,24+ avatar_url = COALESCE(NULLIF(excluded.avatar_url, ''), users.avatar_url),
25 updated_at = CURRENT_TIMESTAMP25 updated_at = CURRENT_TIMESTAMP
26 `, did, handle, displayName, avatarURL)26 `, did, handle, displayName, avatarURL)
27 if err != nil {27 if err != nil {
modified internal/tmpl/feeds.html +6 -6
@@ -1,5 +1,5 @@
11 {{define "feeds.html"}}
2-<div class="flex items-center justify-between mb-2">
2+<div class="flex items-center justify-between gap-3 mb-2 flex-wrap">
33 <h1 class="text-2xl font-bold text-spot-text">Feeds <span class="text-base font-normal text-spot-secondary">({{.SubscriptionCount}})</span></h1>
44 <div class="flex items-center gap-3">
55 <button hx-post="/feeds/refresh" hx-target="#feed-list" hx-swap="innerHTML"
@@ -17,16 +17,16 @@
1717 <h3 class="text-sm font-bold text-spot-red mb-2">Feeds with errors ({{len .DeadFeeds}})</h3>
1818 <div class="space-y-2">
1919 {{range .DeadFeeds}}
20- <div class="dead-feed-item flex items-center justify-between text-sm">
21- <div class="min-w-0 flex-1 flex items-center gap-3">
22- <span class="text-spot-text truncate">{{if .Title.Valid}}{{.Title.String}}{{else}}{{.FeedURL}}{{end}}</span>
23- <div class="flex items-center gap-2 shrink-0">
20+ <div class="dead-feed-item flex items-start justify-between gap-3 text-sm flex-wrap">
21+ <div class="min-w-0 flex-1">
22+ <span class="text-spot-text truncate block">{{if .Title.Valid}}{{.Title.String}}{{else}}{{.FeedURL}}{{end}}</span>
23+ <div class="flex items-center gap-2 mt-1 flex-wrap">
2424 <span class="text-spot-red text-xs">{{.ErrorCount}} errors</span>
2525 {{if .LastError.Valid}}<span class="text-spot-secondary text-xs truncate max-w-48" title="{{.LastError.String}}">{{.LastError.String}}</span>{{end}}
2626 </div>
2727 </div>
2828 <form hx-delete="/feeds/remove" hx-target="closest .dead-feed-item" hx-swap="outerHTML swap:0.3s"
29- hx-confirm="Unsubscribe from this broken feed?" class="inline shrink-0 ml-3">
29+ hx-confirm="Unsubscribe from this broken feed?" class="inline shrink-0">
3030 {{csrfInput $.CSRFToken}}
3131 <input type="hidden" name="url" value="{{.FeedURL}}">
3232 <button type="submit" class="text-xs text-spot-secondary hover:text-spot-red transition font-bold uppercase">Unsubscribe</button>
@@ -1,5 +1,5 @@
1 {{define "feeds.html"}}1 {{define "feeds.html"}}
2-<div class="flex items-center justify-between mb-2">2+<div class="flex items-center justify-between gap-3 mb-2 flex-wrap">
3 <h1 class="text-2xl font-bold text-spot-text">Feeds <span class="text-base font-normal text-spot-secondary">({{.SubscriptionCount}})</span></h1>3 <h1 class="text-2xl font-bold text-spot-text">Feeds <span class="text-base font-normal text-spot-secondary">({{.SubscriptionCount}})</span></h1>
4 <div class="flex items-center gap-3">4 <div class="flex items-center gap-3">
5 <button hx-post="/feeds/refresh" hx-target="#feed-list" hx-swap="innerHTML"5 <button hx-post="/feeds/refresh" hx-target="#feed-list" hx-swap="innerHTML"
@@ -17,16 +17,16 @@
17 <h3 class="text-sm font-bold text-spot-red mb-2">Feeds with errors ({{len .DeadFeeds}})</h3>17 <h3 class="text-sm font-bold text-spot-red mb-2">Feeds with errors ({{len .DeadFeeds}})</h3>
18 <div class="space-y-2">18 <div class="space-y-2">
19 {{range .DeadFeeds}}19 {{range .DeadFeeds}}
20- <div class="dead-feed-item flex items-center justify-between text-sm">20+ <div class="dead-feed-item flex items-start justify-between gap-3 text-sm flex-wrap">
21- <div class="min-w-0 flex-1 flex items-center gap-3">21+ <div class="min-w-0 flex-1">
22- <span class="text-spot-text truncate">{{if .Title.Valid}}{{.Title.String}}{{else}}{{.FeedURL}}{{end}}</span>22+ <span class="text-spot-text truncate block">{{if .Title.Valid}}{{.Title.String}}{{else}}{{.FeedURL}}{{end}}</span>
23- <div class="flex items-center gap-2 shrink-0">23+ <div class="flex items-center gap-2 mt-1 flex-wrap">
24 <span class="text-spot-red text-xs">{{.ErrorCount}} errors</span>24 <span class="text-spot-red text-xs">{{.ErrorCount}} errors</span>
25 {{if .LastError.Valid}}<span class="text-spot-secondary text-xs truncate max-w-48" title="{{.LastError.String}}">{{.LastError.String}}</span>{{end}}25 {{if .LastError.Valid}}<span class="text-spot-secondary text-xs truncate max-w-48" title="{{.LastError.String}}">{{.LastError.String}}</span>{{end}}
26 </div>26 </div>
27 </div>27 </div>
28 <form hx-delete="/feeds/remove" hx-target="closest .dead-feed-item" hx-swap="outerHTML swap:0.3s"28 <form hx-delete="/feeds/remove" hx-target="closest .dead-feed-item" hx-swap="outerHTML swap:0.3s"
29- hx-confirm="Unsubscribe from this broken feed?" class="inline shrink-0 ml-3">29+ hx-confirm="Unsubscribe from this broken feed?" class="inline shrink-0">
30 {{csrfInput $.CSRFToken}}30 {{csrfInput $.CSRFToken}}
31 <input type="hidden" name="url" value="{{.FeedURL}}">31 <input type="hidden" name="url" value="{{.FeedURL}}">
32 <button type="submit" class="text-xs text-spot-secondary hover:text-spot-red transition font-bold uppercase">Unsubscribe</button>32 <button type="submit" class="text-xs text-spot-secondary hover:text-spot-red transition font-bold uppercase">Unsubscribe</button>
modified internal/tmpl/partials/feed-item.html +3 -3
@@ -1,16 +1,16 @@
11 {{define "feed-item.html"}}
2-<div class="feed-item px-5 py-4 flex items-center justify-between hover:bg-spot-hover-50 transition rounded-xl">
2+<div class="feed-item px-5 py-4 flex items-center justify-between gap-3 hover:bg-spot-hover-50 transition rounded-xl">
33 <a href="/articles?feed={{.FeedURL}}" class="min-w-0 flex-1 flex items-center gap-3">
44 {{if .FaviconURL.Valid}}<img src="{{.FaviconURL.String}}" class="w-5 h-5 rounded shrink-0" loading="lazy">{{else}}<span class="shrink-0 w-5 h-5 flex items-center justify-center text-spot-muted"><svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">{{template "icon-globe"}}</svg></span>{{end}}
55 <div class="min-w-0 flex-1">
6- <div class="flex items-center gap-2">
6+ <div class="flex items-center gap-2 flex-wrap">
77 <span class="font-bold text-spot-text truncate">{{if .FeedTitle}}{{.FeedTitle}}{{else}}{{.FeedURL}}{{end}}</span>
88 {{if .Category.Valid}}<span class="text-xs bg-spot-hover text-spot-secondary px-2 py-0.5 rounded-full shrink-0">{{.Category.String}}</span>{{end}}
99 </div>
1010 <div class="text-xs text-spot-muted truncate mt-0.5">{{.FeedURL}}</div>
1111 </div>
1212 </a>
13- <div class="flex items-center gap-3">
13+ <div class="flex items-center gap-3 shrink-0">
1414 {{if .UnreadCount}}<span class="text-xs bg-spot-green/20 text-spot-green px-2.5 py-0.5 rounded-full font-bold">{{.UnreadCount}}</span>{{end}}
1515 <form hx-delete="/feeds/remove" hx-target="closest .feed-item" hx-swap="outerHTML swap:0.3s"
1616 hx-confirm="Unsubscribe from this feed?" class="inline">
@@ -1,16 +1,16 @@
1 {{define "feed-item.html"}}1 {{define "feed-item.html"}}
2-<div class="feed-item px-5 py-4 flex items-center justify-between hover:bg-spot-hover-50 transition rounded-xl">2+<div class="feed-item px-5 py-4 flex items-center justify-between gap-3 hover:bg-spot-hover-50 transition rounded-xl">
3 <a href="/articles?feed={{.FeedURL}}" class="min-w-0 flex-1 flex items-center gap-3">3 <a href="/articles?feed={{.FeedURL}}" class="min-w-0 flex-1 flex items-center gap-3">
4 {{if .FaviconURL.Valid}}<img src="{{.FaviconURL.String}}" class="w-5 h-5 rounded shrink-0" loading="lazy">{{else}}<span class="shrink-0 w-5 h-5 flex items-center justify-center text-spot-muted"><svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">{{template "icon-globe"}}</svg></span>{{end}}4 {{if .FaviconURL.Valid}}<img src="{{.FaviconURL.String}}" class="w-5 h-5 rounded shrink-0" loading="lazy">{{else}}<span class="shrink-0 w-5 h-5 flex items-center justify-center text-spot-muted"><svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">{{template "icon-globe"}}</svg></span>{{end}}
5 <div class="min-w-0 flex-1">5 <div class="min-w-0 flex-1">
6- <div class="flex items-center gap-2">6+ <div class="flex items-center gap-2 flex-wrap">
7 <span class="font-bold text-spot-text truncate">{{if .FeedTitle}}{{.FeedTitle}}{{else}}{{.FeedURL}}{{end}}</span>7 <span class="font-bold text-spot-text truncate">{{if .FeedTitle}}{{.FeedTitle}}{{else}}{{.FeedURL}}{{end}}</span>
8 {{if .Category.Valid}}<span class="text-xs bg-spot-hover text-spot-secondary px-2 py-0.5 rounded-full shrink-0">{{.Category.String}}</span>{{end}}8 {{if .Category.Valid}}<span class="text-xs bg-spot-hover text-spot-secondary px-2 py-0.5 rounded-full shrink-0">{{.Category.String}}</span>{{end}}
9 </div>9 </div>
10 <div class="text-xs text-spot-muted truncate mt-0.5">{{.FeedURL}}</div>10 <div class="text-xs text-spot-muted truncate mt-0.5">{{.FeedURL}}</div>
11 </div>11 </div>
12 </a>12 </a>
13- <div class="flex items-center gap-3">13+ <div class="flex items-center gap-3 shrink-0">
14 {{if .UnreadCount}}<span class="text-xs bg-spot-green/20 text-spot-green px-2.5 py-0.5 rounded-full font-bold">{{.UnreadCount}}</span>{{end}}14 {{if .UnreadCount}}<span class="text-xs bg-spot-green/20 text-spot-green px-2.5 py-0.5 rounded-full font-bold">{{.UnreadCount}}</span>{{end}}
15 <form hx-delete="/feeds/remove" hx-target="closest .feed-item" hx-swap="outerHTML swap:0.3s"15 <form hx-delete="/feeds/remove" hx-target="closest .feed-item" hx-swap="outerHTML swap:0.3s"
16 hx-confirm="Unsubscribe from this feed?" class="inline">16 hx-confirm="Unsubscribe from this feed?" class="inline">