A social RSS reader built on the AT Protocol. glean.at
glean atproto atmosphere rss feed social app
14
fork

Configure Feed

Select the types of activity you want to include in your feed.

Improve recommendation cards

+44 -23
+12 -10
internal/cluster/scoring.go
··· 27 27 } 28 28 29 29 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 39 40 } 40 41 41 42 func (e *Engine) GetFeedRecommendations(ctx context.Context, userDID string, limit int) ([]*FeedRecommendation, error) { ··· 215 216 GROUP BY l.feed_url, l.article_url 216 217 ) 217 218 SELECT a.id, a.title, COALESCE(a.url, ''), la.feed_url, COALESCE(f.title, ''), 219 + COALESCE(f.favicon_url, ''), 218 220 COALESCE(a.author, ''), COALESCE(a.summary, ''), a.published, 219 221 COALESCE(la.like_signal, 0) * ? 220 222 + COALESCE(sl.social, 0) * ? ··· 237 239 for rows.Next() { 238 240 rec := &ArticleRecommendation{} 239 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 243 return nil, err 242 244 } 243 245 recs = append(recs, rec)
+3 -11
internal/tmpl/dashboard.html
··· 50 50 <h2 class="text-lg font-semibold text-spot-text mb-4">Popular feeds to get started</h2> 51 51 <div class="space-y-3"> 52 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 54 {{end}} 55 55 </div> 56 56 </div> ··· 68 68 <h2 class="text-lg font-semibold text-spot-text mb-3">Recommended for you</h2> 69 69 <div class="space-y-3"> 70 70 {{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" .}} 80 72 {{end}} 81 73 </div> 82 74 </div> ··· 131 123 <h2 class="text-lg font-semibold text-spot-text mb-4">Recommended feeds</h2> 132 124 <div class="space-y-3"> 133 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 127 {{end}} 136 128 </div> 137 129 </div>
+1 -1
internal/tmpl/feeds.html
··· 19 19 <h2 class="text-sm font-bold text-spot-text uppercase tracking-wide mb-3">Recommended feeds</h2> 20 20 <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3"> 21 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 23 {{end}} 24 24 </div> 25 25 </div>
+27
internal/tmpl/partials/recommendation-article-card.html
··· 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}}
+1 -1
internal/tmpl/partials/recommendation-card.html internal/tmpl/partials/recommendation-feed-card.html
··· 1 - {{define "recommendation-card.html"}} 1 + {{define "recommendation-feed-card.html"}} 2 2 <div class="recommendation-card bg-spot-surface rounded-xl p-3 hover:bg-spot-hover-50 transition"> 3 3 <div class="flex items-center justify-between gap-2"> 4 4 <a href="/articles?feed={{.feed_url}}" class="min-w-0 flex items-center gap-2 flex-1">