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.

Replace HTML entities with SVG icons and unify button styles

+49 -47
+6 -3
internal/server/articles_handler.go
··· 14 14 ) 15 15 16 16 func writeLikeButton(w http.ResponseWriter, articleID int64, liked bool, count int) { 17 - cls := "text-gray-300 hover:text-red-500" 17 + fill := "none" 18 + colorCls := "text-spot-muted" 18 19 if liked { 19 - cls = "text-red-500" 20 + fill = "currentColor" 21 + colorCls = "text-spot-red" 20 22 } 23 + const heartPath = `<path stroke-linecap="round" stroke-linejoin="round" d="M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12z"/>` 21 24 w.Header().Set("Content-Type", "text/html") 22 - _, _ = fmt.Fprintf(w, `<button hx-post="/articles/%d/like" hx-target="#like-btn" hx-swap="outerHTML" id="like-btn" class="text-lg %s">&#9829; <span class="text-sm text-gray-600">%d</span></button>`, articleID, cls, count) 25 + _, _ = fmt.Fprintf(w, `<button hx-post="/articles/%d/like" hx-target="#like-btn" hx-swap="outerHTML" id="like-btn" class="border border-spot-outline text-spot-text rounded-pill px-4 py-1.5 text-xs font-bold uppercase tracking-button hover:border-spot-text transition inline-flex items-center gap-1.5"><svg class="w-3.5 h-3.5 %s" fill="%s" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">%s</svg><span>%d</span></button>`, articleID, colorCls, fill, heartPath, count) 23 26 } 24 27 25 28 func (s *Server) handleArticles(w http.ResponseWriter, r *http.Request) {
+22 -25
internal/tmpl/article_detail.html
··· 1 1 {{define "article_detail.html"}} 2 2 <div class="max-w-3xl mx-auto"> 3 - <a href="/articles" class="text-sm text-spot-secondary hover:text-spot-text mb-4 inline-block transition">&larr; Back to articles</a> 3 + <a href="/articles" class="text-sm text-spot-secondary hover:text-spot-text mb-6 inline-flex items-center gap-1.5 transition"> 4 + <svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M15.75 19.5L8.25 12l7.5-7.5"/></svg> 5 + Back to articles 6 + </a> 4 7 5 8 <article> 6 9 <h1 class="text-2xl font-bold leading-tight"> ··· 8 11 {{else}}<span class="text-spot-text">{{.Article.Title}}</span>{{end}} 9 12 </h1> 10 13 11 - <div class="flex items-center gap-3 mt-3 text-sm text-spot-secondary"> 14 + <div class="flex items-center gap-2.5 mt-3 text-sm text-spot-secondary flex-wrap"> 12 15 {{if .Article.Author.Valid}}<span>{{.Article.Author.String}}</span>{{end}} 13 16 {{if .Article.Published.Valid}}<span>{{.Article.Published.Time.Format "Jan 2, 2006 15:04"}}</span>{{end}} 14 17 {{if .Feed}} 15 - <a href="/articles?feed={{.Feed.FeedURL}}" class="hover:text-spot-green transition"> 16 - {{if .Feed.FaviconURL.Valid}}<img src="{{.Feed.FaviconURL.String}}" class="w-4 h-4 inline-block mr-1 align-text-bottom rounded-sm">{{end}} 18 + <a href="/articles?feed={{.Feed.FeedURL}}" class="inline-flex items-center gap-1 hover:text-spot-green transition"> 19 + {{if .Feed.FaviconURL.Valid}}<img src="{{.Feed.FaviconURL.String}}" class="w-4 h-4 rounded-sm">{{end}} 17 20 {{if .Feed.Title.Valid}}{{.Feed.Title.String}}{{else}}{{.Feed.FeedURL}}{{end}} 18 21 </a> 19 22 {{end}} 20 23 </div> 21 24 22 - <div class="flex items-center gap-2 mt-6 flex-wrap"> 25 + <div class="flex items-center gap-2 mt-6"> 26 + <button hx-post="/articles/{{.Article.ID}}/like" hx-target="#like-btn" hx-swap="outerHTML" 27 + id="like-btn" 28 + class="border border-spot-outline text-spot-text rounded-pill px-4 py-1.5 text-xs font-bold uppercase tracking-button hover:border-spot-text transition inline-flex items-center gap-1.5"> 29 + <svg class="w-3.5 h-3.5 {{if .HasLiked}}text-spot-red{{else}}text-spot-muted{{end}}" fill="{{if .HasLiked}}currentColor{{else}}none{{end}}" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">{{template "icon-heart"}}</svg> 30 + <span>{{.LikeCount}}</span> 31 + </button> 32 + 23 33 <button hx-post="/articles/{{.Article.ID}}/{{if .ReadState.IsRead}}unread{{else}}read{{end}}" 24 34 hx-target="#read-btn" hx-swap="outerHTML" 25 35 id="read-btn" 26 - class="border border-spot-outline text-spot-text rounded-pill px-5 py-2 text-xs font-bold uppercase tracking-button hover:border-spot-text transition inline-flex items-center gap-1.5"> 27 - {{if .ReadState.IsRead}} 28 - <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M2.036 12.322a1.012 1.012 0 010-.639C3.423 7.51 7.36 4.5 12 4.5c4.64 0 8.577 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.64 0-8.577-3.007-9.963-7.178z"/><path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/></svg> 29 - Mark unread 30 - {{else}} 31 - <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M2.036 12.322a1.012 1.012 0 010-.639C3.423 7.51 7.36 4.5 12 4.5c4.64 0 8.577 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.64 0-8.577-3.007-9.963-7.178z"/><path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/></svg> 32 - Mark read 33 - {{end}} 34 - </button> 35 - 36 - <button hx-post="/articles/{{.Article.ID}}/like" hx-target="#like-btn" hx-swap="outerHTML" 37 - id="like-btn" 38 - class="border border-spot-outline text-spot-text rounded-pill px-5 py-2 text-xs font-bold uppercase tracking-button hover:border-spot-text transition inline-flex items-center gap-1.5"> 39 - <span class="{{if .HasLiked}}text-spot-red{{end}}">&#9829;</span> 40 - {{.LikeCount}} 36 + class="border border-spot-outline text-spot-text rounded-pill px-4 py-1.5 text-xs font-bold uppercase tracking-button hover:border-spot-text transition"> 37 + {{if .ReadState.IsRead}}Mark unread{{else}}Mark read{{end}} 41 38 </button> 42 39 43 40 {{if .Article.URL.Valid}} 44 41 <a href="{{.Article.URL.String}}" target="_blank" rel="noopener noreferrer" 45 - class="border border-spot-outline text-spot-text rounded-pill px-5 py-2 text-xs font-bold uppercase tracking-button hover:border-spot-text transition inline-flex items-center gap-1.5"> 42 + class="border border-spot-outline text-spot-text rounded-pill px-4 py-1.5 text-xs font-bold uppercase tracking-button hover:border-spot-text transition inline-flex items-center gap-1.5"> 46 43 <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M13.5 6H5.25A2.25 2.25 0 003 8.25v10.5A2.25 2.25 0 005.25 21h10.5A2.25 2.25 0 0018 18.75V10.5m-4.5-6H18m0 0v4.5m0-4.5L10.5 13.5"/></svg> 47 44 Original 48 45 </a> 49 46 50 47 <a href="https://bsky.app/intent/compose?text={{.Article.Title}}%20{{.Article.URL.String}}" 51 48 target="_blank" rel="noopener noreferrer" 52 - class="border border-spot-outline text-spot-text rounded-pill px-5 py-2 text-xs font-bold uppercase tracking-button hover:border-spot-text transition inline-flex items-center gap-1.5"> 49 + class="border border-spot-outline text-spot-text rounded-pill px-4 py-1.5 text-xs font-bold uppercase tracking-button hover:border-spot-text transition inline-flex items-center gap-1.5"> 53 50 <span class="w-3.5 h-3.5 inline-flex">{{template "icon-bluesky"}}</span> 54 51 Share 55 52 </a> 56 53 {{end}} 57 54 </div> 58 55 59 - <hr class="my-6 border-spot-divider-30"> 56 + <hr class="my-8 border-spot-divider"> 60 57 61 58 {{if .Article.Content.Valid}} 62 59 <div class="article-body"> ··· 71 68 {{end}} 72 69 </article> 73 70 74 - <hr class="my-8 border-spot-divider-30"> 71 + <hr class="my-8 border-spot-divider"> 75 72 76 73 <section> 77 74 <h2 class="text-lg font-semibold text-spot-text mb-4">Annotations</h2> ··· 92 89 </div> 93 90 <div class="flex gap-2"> 94 91 <input type="text" name="tags" placeholder="Tags (comma separated)" 95 - class="flex-1 bg-spot-hover text-spot-text rounded-lg px-4 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-spot-green placeholder:text-spot-placeholder"> 92 + class="flex-1 bg-spot-hover text-spot-text rounded-pill px-5 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-spot-green placeholder:text-spot-placeholder"> 96 93 <button type="submit" class="bg-spot-green text-white rounded-pill px-5 py-2 text-sm font-bold uppercase tracking-button hover:brightness-110 transition">Annotate</button> 97 94 </div> 98 95 </form>
+2 -2
internal/tmpl/articles.html
··· 31 31 {{range .Articles}} 32 32 {{template "article-card.html" .}} 33 33 {{else}} 34 - <div class="flex flex-col items-center justify-center py-16 text-center"> 35 - <div class="w-14 h-14 rounded-full bg-spot-hover flex items-center justify-center mb-4"> 34 + <div class="bg-spot-surface rounded-xl py-12 px-6 text-center"> 35 + <div class="w-14 h-14 rounded-full bg-spot-hover flex items-center justify-center mb-4 mx-auto"> 36 36 <svg class="w-7 h-7 text-spot-muted" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M19 20H5a2 2 0 01-2-2V6a2 2 0 012-2h10a2 2 0 012 2v1m2 13a2 2 0 01-2-2V7m2 13a2 2 0 002-2V9a2 2 0 00-2-2h-2"/></svg> 37 37 </div> 38 38 <p class="text-sm text-spot-secondary mb-1">No articles found</p>
+3 -3
internal/tmpl/dashboard.html
··· 34 34 {{range .Articles}} 35 35 {{template "article-card.html" .}} 36 36 {{else}} 37 - <div class="flex flex-col items-center justify-center py-12 text-center"> 38 - <div class="w-14 h-14 rounded-full bg-spot-hover flex items-center justify-center mb-4"> 37 + <div class="bg-spot-surface rounded-xl py-12 px-6 text-center"> 38 + <div class="w-14 h-14 rounded-full bg-spot-hover flex items-center justify-center mb-4 mx-auto"> 39 39 <svg class="w-7 h-7 text-spot-muted" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/></svg> 40 40 </div> 41 41 <p class="text-sm text-spot-secondary mb-1">No unread articles</p> ··· 79 79 <span class="text-spot-muted">{{if .FeedTitle}}{{.FeedTitle}}{{end}}</span> 80 80 </div> 81 81 <div class="flex items-center gap-3 mt-2 text-xs text-spot-secondary"> 82 - <span>&#9829; {{.LikeCount}}</span> 82 + <span class="inline-flex items-center gap-1"><svg class="w-4 h-4 text-spot-muted" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">{{template "icon-heart"}}</svg> {{.LikeCount}}</span> 83 83 <span class="text-spot-muted">{{.AnnotationCount}} notes</span> 84 84 </div> 85 85 </a>
+2 -2
internal/tmpl/feeds.html
··· 86 86 </div> 87 87 </div> 88 88 {{else}} 89 - <div class="flex flex-col items-center justify-center py-12 text-center"> 90 - <div class="w-14 h-14 rounded-full bg-spot-hover flex items-center justify-center mb-4"> 89 + <div class="flex flex-col items-center justify-center py-12 px-6 text-center"> 90 + <div class="w-14 h-14 rounded-full bg-spot-hover flex items-center justify-center mb-4 mx-auto"> 91 91 <svg class="w-7 h-7 text-spot-muted" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M6 5c7.18 0 13 5.82 13 13M6 11a7 7 0 017 7m-7-1a1 1 0 11-2 0 1 1 0 012 0z"/></svg> 92 92 </div> 93 93 <p class="text-sm text-spot-secondary mb-1">No feeds yet</p>
+1 -1
internal/tmpl/index.html
··· 46 46 <div class="text-xs text-spot-secondary mt-0.5">example.com &middot; 2h ago</div> 47 47 </div> 48 48 <div class="flex items-center gap-1 text-spot-green shrink-0"> 49 - <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12Z"/></svg> 49 + <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">{{template "icon-heart"}}</svg> 50 50 <span class="text-xs">12</span> 51 51 </div> 52 52 </div>
+5 -5
internal/tmpl/library.html
··· 12 12 {{range .Articles}} 13 13 {{template "article-card.html" .}} 14 14 {{else}} 15 - <div class="flex flex-col items-center justify-center py-12 text-center"> 16 - <div class="w-14 h-14 rounded-full bg-spot-hover flex items-center justify-center mb-4"> 17 - <svg class="w-7 h-7 text-spot-muted" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12z"/></svg> 15 + <div class="bg-spot-surface rounded-xl py-12 px-6 text-center"> 16 + <div class="w-14 h-14 rounded-full bg-spot-hover flex items-center justify-center mb-4 mx-auto"> 17 + <svg class="w-7 h-7 text-spot-muted" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24">{{template "icon-heart"}}</svg> 18 18 </div> 19 19 <p class="text-sm text-spot-secondary mb-1">No liked articles yet</p> 20 20 <p class="text-xs text-spot-muted">Like articles to save them here.</p> ··· 47 47 {{range .Annotations}} 48 48 {{template "annotation-card.html" dict "annotation" . "userDID" $.CurrentUserDID}} 49 49 {{else}} 50 - <div class="flex flex-col items-center justify-center py-12 text-center"> 51 - <div class="w-14 h-14 rounded-full bg-spot-hover flex items-center justify-center mb-4"> 50 + <div class="bg-spot-surface rounded-xl py-12 px-6 text-center"> 51 + <div class="w-14 h-14 rounded-full bg-spot-hover flex items-center justify-center mb-4 mx-auto"> 52 52 <svg class="w-7 h-7 text-spot-muted" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M7 8h10M7 12h4m1 8l-4-4H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-3l-4 4z"/></svg> 53 53 </div> 54 54 <p class="text-sm text-spot-secondary mb-1">No annotations yet</p>
+1 -1
internal/tmpl/partials/article-card.html
··· 15 15 <p class="text-sm text-spot-secondary mt-2 line-clamp-2">{{plainText .Summary.String}}</p> 16 16 {{end}}{{end}} 17 17 </div> 18 - <div class="flex flex-col items-center gap-1 shrink-0"> 18 + <div class="flex flex-col items-center gap-3 shrink-0 pt-1"> 19 19 {{template "like-button.html" .}} 20 20 {{if .IsRead.Bool}} 21 21 <span id="read-btn-{{.ID}}" class="text-[10px] text-spot-green uppercase tracking-button">Read</span>
+1
internal/tmpl/partials/icon-heart.html
··· 1 + {{define "icon-heart"}}<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12z"/></svg>{{end}}
+3 -1
internal/tmpl/partials/like-button.html
··· 1 1 {{define "like-button.html"}} 2 2 <button hx-post="/articles/{{.ID}}/like" hx-target="this" hx-swap="outerHTML" 3 - class="text-spot-muted hover:text-spot-red text-lg transition">&#9829;</button> 3 + class="text-spot-muted hover:text-spot-red transition"> 4 + <svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">{{template "icon-heart"}}</svg> 5 + </button> 4 6 {{end}}
+3 -3
internal/tmpl/trending.html
··· 27 27 {{end}} 28 28 </div> 29 29 <div class="flex flex-col items-end gap-1 shrink-0 text-sm text-spot-secondary"> 30 - <span>&#9829; {{$t.LikeCount}}</span> 30 + <span class="inline-flex items-center gap-1"><svg class="w-4 h-4 text-spot-muted" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">{{template "icon-heart"}}</svg> {{$t.LikeCount}}</span> 31 31 <span class="text-spot-muted">{{$t.AnnotationCount}} notes</span> 32 32 </div> 33 33 </div> 34 34 </article> 35 35 {{else}} 36 - <div class="flex flex-col items-center justify-center py-16 text-center"> 37 - <div class="w-14 h-14 rounded-full bg-spot-hover flex items-center justify-center mb-4"> 36 + <div class="bg-spot-surface rounded-xl py-12 px-6 text-center"> 37 + <div class="w-14 h-14 rounded-full bg-spot-hover flex items-center justify-center mb-4 mx-auto"> 38 38 <svg class="w-7 h-7 text-spot-muted" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6"/></svg> 39 39 </div> 40 40 <p class="text-sm text-spot-secondary mb-1">No trending articles yet</p>
-1
main.go
··· 99 99 cancel() 100 100 101 101 fmt.Println("glean stopped") 102 - os.Exit(0) 103 102 } 104 103 105 104 func envOr(key, fallback string) string {