nandi/gleanpublic Fork 0
b6dcf3d
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.

Improve recommendation cardsUnverified

Julien Robert committed 2026-04-22T17:31:55+02:00 Browse files
b6dcf3d parent: f847c6f
modified internal/cluster/scoring.go +12 -10
@@ -27,15 +27,16 @@ type PersonRecommendation struct {
2727 }
2828
2929 type ArticleRecommendation struct {
30- ArticleID int64
31- Title string
32- URL string
33- FeedURL string
34- FeedTitle string
35- Author string
36- Summary string
37- Published sql.NullTime
38- Score float64
30+ ArticleID int64
31+ Title string
32+ URL string
33+ FeedURL string
34+ FeedTitle string
35+ FaviconURL string
36+ Author string
37+ Summary string
38+ Published sql.NullTime
39+ Score float64
3940 }
4041
4142 func (e *Engine) GetFeedRecommendations(ctx context.Context, userDID string, limit int) ([]*FeedRecommendation, error) {
@@ -215,6 +216,7 @@ func (e *Engine) ComputeArticleRecommendationsOnDemand(ctx context.Context, user
215216 GROUP BY l.feed_url, l.article_url
216217 )
217218 SELECT a.id, a.title, COALESCE(a.url, ''), la.feed_url, COALESCE(f.title, ''),
219+ COALESCE(f.favicon_url, ''),
218220 COALESCE(a.author, ''), COALESCE(a.summary, ''), a.published,
219221 COALESCE(la.like_signal, 0) * ?
220222 + COALESCE(sl.social, 0) * ?
@@ -237,7 +239,7 @@ func (e *Engine) ComputeArticleRecommendationsOnDemand(ctx context.Context, user
237239 for rows.Next() {
238240 rec := &ArticleRecommendation{}
239241 if err := rows.Scan(&rec.ArticleID, &rec.Title, &rec.URL, &rec.FeedURL, &rec.FeedTitle,
240- &rec.Author, &rec.Summary, &rec.Published, &rec.Score); err != nil {
242+ &rec.FaviconURL, &rec.Author, &rec.Summary, &rec.Published, &rec.Score); err != nil {
241243 return nil, err
242244 }
243245 recs = append(recs, rec)
@@ -27,15 +27,16 @@ type PersonRecommendation struct {
27 }27 }
28 28
29 type ArticleRecommendation struct {29 type ArticleRecommendation struct {
30- ArticleID int6430+ ArticleID int64
31- Title string31+ Title string
32- URL string32+ URL string
33- FeedURL string33+ FeedURL string
34- FeedTitle string34+ FeedTitle string
35- Author string35+ FaviconURL string
36- Summary string36+ Author string
37- Published sql.NullTime37+ Summary string
38- Score float6438+ Published sql.NullTime
39+ Score float64
39 }40 }
40 41
41 func (e *Engine) GetFeedRecommendations(ctx context.Context, userDID string, limit int) ([]*FeedRecommendation, error) {42 func (e *Engine) GetFeedRecommendations(ctx context.Context, userDID string, limit int) ([]*FeedRecommendation, error) {
@@ -215,6 +216,7 @@ func (e *Engine) ComputeArticleRecommendationsOnDemand(ctx context.Context, user
215 GROUP BY l.feed_url, l.article_url216 GROUP BY l.feed_url, l.article_url
216 )217 )
217 SELECT a.id, a.title, COALESCE(a.url, ''), la.feed_url, COALESCE(f.title, ''),218 SELECT a.id, a.title, COALESCE(a.url, ''), la.feed_url, COALESCE(f.title, ''),
219+ COALESCE(f.favicon_url, ''),
218 COALESCE(a.author, ''), COALESCE(a.summary, ''), a.published,220 COALESCE(a.author, ''), COALESCE(a.summary, ''), a.published,
219 COALESCE(la.like_signal, 0) * ?221 COALESCE(la.like_signal, 0) * ?
220 + COALESCE(sl.social, 0) * ?222 + COALESCE(sl.social, 0) * ?
@@ -237,7 +239,7 @@ func (e *Engine) ComputeArticleRecommendationsOnDemand(ctx context.Context, user
237 for rows.Next() {239 for rows.Next() {
238 rec := &ArticleRecommendation{}240 rec := &ArticleRecommendation{}
239 if err := rows.Scan(&rec.ArticleID, &rec.Title, &rec.URL, &rec.FeedURL, &rec.FeedTitle,241 if err := rows.Scan(&rec.ArticleID, &rec.Title, &rec.URL, &rec.FeedURL, &rec.FeedTitle,
240- &rec.Author, &rec.Summary, &rec.Published, &rec.Score); err != nil {242+ &rec.FaviconURL, &rec.Author, &rec.Summary, &rec.Published, &rec.Score); err != nil {
241 return nil, err243 return nil, err
242 }244 }
243 recs = append(recs, rec)245 recs = append(recs, rec)
modified internal/tmpl/dashboard.html +3 -11
@@ -50,7 +50,7 @@
5050 <h2 class="text-lg font-semibold text-spot-text mb-4">Popular feeds to get started</h2>
5151 <div class="space-y-3">
5252 {{range .FeedRecommendations}}
53- {{template "recommendation-card.html" (dict "title" .Title "feed_url" .FeedURL "description" .Description "favicon_url" .FaviconURL "subscriber_count" .SubscriberCount "CSRFToken" $.CSRFToken)}}
53+ {{template "recommendation-feed-card.html" (dict "title" .Title "feed_url" .FeedURL "description" .Description "favicon_url" .FaviconURL "subscriber_count" .SubscriberCount "CSRFToken" $.CSRFToken)}}
5454 {{end}}
5555 </div>
5656 </div>
@@ -68,15 +68,7 @@
6868 <h2 class="text-lg font-semibold text-spot-text mb-3">Recommended for you</h2>
6969 <div class="space-y-3">
7070 {{range .ArticleRecommendations}}
71- <div class="bg-spot-surface rounded-xl px-5 py-4 hover:bg-spot-hover-50 transition">
72- <a href="/articles/{{.ArticleID}}" class="font-bold text-spot-text hover:text-spot-green transition text-lg leading-tight">{{.Title}}</a>
73- <div class="text-sm text-spot-secondary mt-1 flex items-center gap-2">
74- {{if .Author}}<span>{{.Author}}</span>{{end}}
75- <span class="text-spot-muted"><a href="/articles?feed={{.FeedURL}}" class="hover:text-spot-green transition">{{.FeedTitle}}</a></span>
76- {{if .Published.Valid}}<span>{{.Published.Time.Format "Jan 2"}}</span>{{end}}
77- </div>
78- {{if .Summary}}<p class="text-sm text-spot-secondary mt-2 line-clamp-2">{{.Summary}}</p>{{end}}
79- </div>
71+ {{template "recommendation-article-card.html" .}}
8072 {{end}}
8173 </div>
8274 </div>
@@ -131,7 +123,7 @@
131123 <h2 class="text-lg font-semibold text-spot-text mb-4">Recommended feeds</h2>
132124 <div class="space-y-3">
133125 {{range .FeedRecommendations}}
134- {{template "recommendation-card.html" (dict "title" .Title "feed_url" .FeedURL "description" .Description "favicon_url" .FaviconURL "subscriber_count" .SubscriberCount "CSRFToken" $.CSRFToken)}}
126+ {{template "recommendation-feed-card.html" (dict "title" .Title "feed_url" .FeedURL "description" .Description "favicon_url" .FaviconURL "subscriber_count" .SubscriberCount "CSRFToken" $.CSRFToken)}}
135127 {{end}}
136128 </div>
137129 </div>
@@ -50,7 +50,7 @@
50 <h2 class="text-lg font-semibold text-spot-text mb-4">Popular feeds to get started</h2>50 <h2 class="text-lg font-semibold text-spot-text mb-4">Popular feeds to get started</h2>
51 <div class="space-y-3">51 <div class="space-y-3">
52 {{range .FeedRecommendations}}52 {{range .FeedRecommendations}}
53- {{template "recommendation-card.html" (dict "title" .Title "feed_url" .FeedURL "description" .Description "favicon_url" .FaviconURL "subscriber_count" .SubscriberCount "CSRFToken" $.CSRFToken)}}53+ {{template "recommendation-feed-card.html" (dict "title" .Title "feed_url" .FeedURL "description" .Description "favicon_url" .FaviconURL "subscriber_count" .SubscriberCount "CSRFToken" $.CSRFToken)}}
54 {{end}}54 {{end}}
55 </div>55 </div>
56 </div>56 </div>
@@ -68,15 +68,7 @@
68 <h2 class="text-lg font-semibold text-spot-text mb-3">Recommended for you</h2>68 <h2 class="text-lg font-semibold text-spot-text mb-3">Recommended for you</h2>
69 <div class="space-y-3">69 <div class="space-y-3">
70 {{range .ArticleRecommendations}}70 {{range .ArticleRecommendations}}
71- <div class="bg-spot-surface rounded-xl px-5 py-4 hover:bg-spot-hover-50 transition">71+ {{template "recommendation-article-card.html" .}}
72- <a href="/articles/{{.ArticleID}}" class="font-bold text-spot-text hover:text-spot-green transition text-lg leading-tight">{{.Title}}</a>
73- <div class="text-sm text-spot-secondary mt-1 flex items-center gap-2">
74- {{if .Author}}<span>{{.Author}}</span>{{end}}
75- <span class="text-spot-muted"><a href="/articles?feed={{.FeedURL}}" class="hover:text-spot-green transition">{{.FeedTitle}}</a></span>
76- {{if .Published.Valid}}<span>{{.Published.Time.Format "Jan 2"}}</span>{{end}}
77- </div>
78- {{if .Summary}}<p class="text-sm text-spot-secondary mt-2 line-clamp-2">{{.Summary}}</p>{{end}}
79- </div>
80 {{end}}72 {{end}}
81 </div>73 </div>
82 </div>74 </div>
@@ -131,7 +123,7 @@
131 <h2 class="text-lg font-semibold text-spot-text mb-4">Recommended feeds</h2>123 <h2 class="text-lg font-semibold text-spot-text mb-4">Recommended feeds</h2>
132 <div class="space-y-3">124 <div class="space-y-3">
133 {{range .FeedRecommendations}}125 {{range .FeedRecommendations}}
134- {{template "recommendation-card.html" (dict "title" .Title "feed_url" .FeedURL "description" .Description "favicon_url" .FaviconURL "subscriber_count" .SubscriberCount "CSRFToken" $.CSRFToken)}}126+ {{template "recommendation-feed-card.html" (dict "title" .Title "feed_url" .FeedURL "description" .Description "favicon_url" .FaviconURL "subscriber_count" .SubscriberCount "CSRFToken" $.CSRFToken)}}
135 {{end}}127 {{end}}
136 </div>128 </div>
137 </div>129 </div>
modified internal/tmpl/feeds.html +1 -1
@@ -19,7 +19,7 @@
1919 <h2 class="text-sm font-bold text-spot-text uppercase tracking-wide mb-3">Recommended feeds</h2>
2020 <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
2121 {{range .FeedRecommendations}}
22- {{template "recommendation-card.html" (dict "title" .Title "feed_url" .FeedURL "description" .Description "favicon_url" .FaviconURL "subscriber_count" .SubscriberCount "CSRFToken" $.CSRFToken)}}
22+ {{template "recommendation-feed-card.html" (dict "title" .Title "feed_url" .FeedURL "description" .Description "favicon_url" .FaviconURL "subscriber_count" .SubscriberCount "CSRFToken" $.CSRFToken)}}
2323 {{end}}
2424 </div>
2525 </div>
@@ -19,7 +19,7 @@
19 <h2 class="text-sm font-bold text-spot-text uppercase tracking-wide mb-3">Recommended feeds</h2>19 <h2 class="text-sm font-bold text-spot-text uppercase tracking-wide mb-3">Recommended feeds</h2>
20 <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">20 <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
21 {{range .FeedRecommendations}}21 {{range .FeedRecommendations}}
22- {{template "recommendation-card.html" (dict "title" .Title "feed_url" .FeedURL "description" .Description "favicon_url" .FaviconURL "subscriber_count" .SubscriberCount "CSRFToken" $.CSRFToken)}}22+ {{template "recommendation-feed-card.html" (dict "title" .Title "feed_url" .FeedURL "description" .Description "favicon_url" .FaviconURL "subscriber_count" .SubscriberCount "CSRFToken" $.CSRFToken)}}
23 {{end}}23 {{end}}
24 </div>24 </div>
25 </div>25 </div>
added internal/tmpl/partials/recommendation-article-card.html +27 -0
new file mode 100644
@@ -0,0 +1,27 @@
1+{{define "recommendation-article-card.html"}}
2+<article data-article-url="{{.URL}}" class="bg-spot-surface rounded-xl px-5 py-4 hover:bg-spot-hover-50 transition shadow-spot relative">
3+ <div class="flex items-start justify-between gap-4">
4+ <div class="min-w-0 flex-1">
5+ <div class="flex items-center gap-2">
6+ <span class="w-2 h-2 rounded-full bg-spot-green shrink-0"></span>
7+ <a href="/articles/{{.ArticleID}}" class="font-bold text-spot-text hover:text-spot-green transition text-lg leading-tight">{{.Title}}</a>
8+ </div>
9+ <div class="text-sm text-spot-secondary mt-1 flex items-center gap-2">
10+ {{if .FaviconURL}}{{template "favicon" dict "src" .FaviconURL "size" "w-4 h-4"}}{{end}}
11+ {{if .Author}}<span>{{.Author}}</span>{{end}}
12+ {{if .Published.Valid}}<span>{{.Published.Time.Format "Jan 2"}}</span>{{end}}
13+ <span class="text-spot-muted"><a href="/articles?feed={{.FeedURL}}" class="hover:text-spot-green transition">{{if .FeedTitle}}{{.FeedTitle}}{{else}}{{.FeedURL}}{{end}}</a></span>
14+ </div>
15+ {{if .Summary}}<p class="text-sm text-spot-secondary mt-2 line-clamp-2">{{plainText .Summary}}</p>{{end}}
16+ </div>
17+ <div class="shrink-0 pt-1">
18+ <button hx-post="/articles/dismiss" hx-swap="none" hx-include="#dismiss-{{.ArticleID}}"
19+ class="text-[10px] text-spot-secondary hover:text-spot-text uppercase tracking-button transition flex items-center gap-1">
20+ <input type="hidden" name="article_url" value="{{.URL}}" id="dismiss-{{.ArticleID}}">
21+ <svg class="w-3 h-3" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/></svg>
22+ Dismiss
23+ </button>
24+ </div>
25+ </div>
26+</article>
27+{{end}}
\ No newline at end of file
new file mode 100644
@@ -0,0 +1,27 @@
1+{{define "recommendation-article-card.html"}}
2+<article data-article-url="{{.URL}}" class="bg-spot-surface rounded-xl px-5 py-4 hover:bg-spot-hover-50 transition shadow-spot relative">
3+ <div class="flex items-start justify-between gap-4">
4+ <div class="min-w-0 flex-1">
5+ <div class="flex items-center gap-2">
6+ <span class="w-2 h-2 rounded-full bg-spot-green shrink-0"></span>
7+ <a href="/articles/{{.ArticleID}}" class="font-bold text-spot-text hover:text-spot-green transition text-lg leading-tight">{{.Title}}</a>
8+ </div>
9+ <div class="text-sm text-spot-secondary mt-1 flex items-center gap-2">
10+ {{if .FaviconURL}}{{template "favicon" dict "src" .FaviconURL "size" "w-4 h-4"}}{{end}}
11+ {{if .Author}}<span>{{.Author}}</span>{{end}}
12+ {{if .Published.Valid}}<span>{{.Published.Time.Format "Jan 2"}}</span>{{end}}
13+ <span class="text-spot-muted"><a href="/articles?feed={{.FeedURL}}" class="hover:text-spot-green transition">{{if .FeedTitle}}{{.FeedTitle}}{{else}}{{.FeedURL}}{{end}}</a></span>
14+ </div>
15+ {{if .Summary}}<p class="text-sm text-spot-secondary mt-2 line-clamp-2">{{plainText .Summary}}</p>{{end}}
16+ </div>
17+ <div class="shrink-0 pt-1">
18+ <button hx-post="/articles/dismiss" hx-swap="none" hx-include="#dismiss-{{.ArticleID}}"
19+ class="text-[10px] text-spot-secondary hover:text-spot-text uppercase tracking-button transition flex items-center gap-1">
20+ <input type="hidden" name="article_url" value="{{.URL}}" id="dismiss-{{.ArticleID}}">
21+ <svg class="w-3 h-3" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/></svg>
22+ Dismiss
23+ </button>
24+ </div>
25+ </div>
26+</article>
27+{{end}}
\ No newline at end of file\ No newline at end of file
renamed internal/tmpl/partials/recommendation-feed-card.html +1 -1
similarity index 97%
rename from internal/tmpl/partials/recommendation-card.html
rename to internal/tmpl/partials/recommendation-feed-card.html
@@ -1,4 +1,4 @@
1-{{define "recommendation-card.html"}}
1+{{define "recommendation-feed-card.html"}}
22 <div class="recommendation-card bg-spot-surface rounded-xl p-3 hover:bg-spot-hover-50 transition">
33 <div class="flex items-center justify-between gap-2">
44 <a href="/articles?feed={{.feed_url}}" class="min-w-0 flex items-center gap-2 flex-1">
similarity index 97%
rename from internal/tmpl/partials/recommendation-card.html
rename to internal/tmpl/partials/recommendation-feed-card.html
@@ -1,4 +1,4 @@
1-{{define "recommendation-card.html"}}1+{{define "recommendation-feed-card.html"}}
2 <div class="recommendation-card bg-spot-surface rounded-xl p-3 hover:bg-spot-hover-50 transition">2 <div class="recommendation-card bg-spot-surface rounded-xl p-3 hover:bg-spot-hover-50 transition">
3 <div class="flex items-center justify-between gap-2">3 <div class="flex items-center justify-between gap-2">
4 <a href="/articles?feed={{.feed_url}}" class="min-w-0 flex items-center gap-2 flex-1">4 <a href="/articles?feed={{.feed_url}}" class="min-w-0 flex items-center gap-2 flex-1">