diff --git a/internal/render/golden_test.go b/internal/render/golden_test.go index f1ebec5..3a709ff 100644 --- a/internal/render/golden_test.go +++ b/internal/render/golden_test.go @@ -110,7 +110,8 @@ func goldenCases() map[string]func(io.Writer) { "me_anon": func(w io.Writer) { Me(w, nil, "") }, "me_unlinked": func(w io.Writer) { Me(w, nil, fp) }, "me_signedin": func(w io.Writer) { Me(w, viewer, fp) }, - "profile": func(w io.Writer) { Profile(w, "byjp.me", profile) }, + "profile": func(w io.Writer) { Profile(w, "byjp.me", profile, 3) }, + "profile_no_pubs": func(w io.Writer) { Profile(w, "byjp.me", profile, -1) }, "list": func(w io.Writer) { ListPage(w, "Posts", "/p/byjp.me/posts/", items, "cursor123") }, "list_empty": func(w io.Writer) { ListPage(w, "Likes", "/p/byjp.me/likes/", nil, "") }, "post": func(w io.Writer) { Post(w, rich) }, diff --git a/internal/render/render.go b/internal/render/render.go index f97f0e7..a6af19f 100644 --- a/internal/render/render.go +++ b/internal/render/render.go @@ -71,13 +71,17 @@ type meData struct { func Me(w io.Writer, v *Viewer, fp string) { execute(w, "me.tmpl", meData{v, fp}) } type profileData struct { - Actor string - P *bsky.Profile + Actor string + P *bsky.Profile + PubCount int // publication count; negative means unknown (omit it) } // Profile writes a profile page: account details and links to the post lists. -func Profile(w io.Writer, urlActor string, p *bsky.Profile) { - execute(w, "profile.tmpl", profileData{urlActor, p}) +// pubCount is the number of publications; pass a negative value to omit it (the +// only count cheap enough to fetch alongside the profile — posts come from the +// profile itself, and replies/reposts/likes have no inexpensive total). +func Profile(w io.Writer, urlActor string, p *bsky.Profile, pubCount int) { + execute(w, "profile.tmpl", profileData{urlActor, p, pubCount}) } type listData struct { diff --git a/internal/render/templates/profile.tmpl b/internal/render/templates/profile.tmpl index f23992a..6e0e425 100644 --- a/internal/render/templates/profile.tmpl +++ b/internal/render/templates/profile.tmpl @@ -10,8 +10,8 @@ {{end}} ## Browse -=> /p/{{.Actor}}/posts/ Posts +=> /p/{{.Actor}}/posts/ Posts ({{number .P.PostsCount}}) => /p/{{.Actor}}/replies/ Replies => /p/{{.Actor}}/reposts/ Reposts => /p/{{.Actor}}/likes/ Likes -=> /p/{{.Actor}}/publications/ Publications +=> /p/{{.Actor}}/publications/ Publications{{if ge .PubCount 0}} ({{number .PubCount}}){{end}} diff --git a/internal/render/testdata/golden/profile.gmi b/internal/render/testdata/golden/profile.gmi index 4c2b487..077f2ed 100644 --- a/internal/render/testdata/golden/profile.gmi +++ b/internal/render/testdata/golden/profile.gmi @@ -11,8 +11,8 @@ Joined 15 Jan 2023 ## Browse -=> /p/byjp.me/posts/ Posts +=> /p/byjp.me/posts/ Posts (7,890) => /p/byjp.me/replies/ Replies => /p/byjp.me/reposts/ Reposts => /p/byjp.me/likes/ Likes -=> /p/byjp.me/publications/ Publications +=> /p/byjp.me/publications/ Publications (3) diff --git a/internal/render/testdata/golden/profile_no_pubs.gmi b/internal/render/testdata/golden/profile_no_pubs.gmi new file mode 100644 index 0000000..c6f6d88 --- /dev/null +++ b/internal/render/testdata/golden/profile_no_pubs.gmi @@ -0,0 +1,18 @@ +# JP + +@byjp.me + +Maker of things. +Second line. + +7,890 posts · 1,234 followers · 56 following +Joined 15 Jan 2023 +=> /p/byjp.me/avatar 🖼️ Avatar + +## Browse + +=> /p/byjp.me/posts/ Posts (7,890) +=> /p/byjp.me/replies/ Replies +=> /p/byjp.me/reposts/ Reposts +=> /p/byjp.me/likes/ Likes +=> /p/byjp.me/publications/ Publications diff --git a/internal/server/server.go b/internal/server/server.go index ea253b7..60a5a67 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -141,8 +141,16 @@ func (s *server) handleProfile(w gemini.ResponseWriter, r *gemini.Request) { writeError(w, r, err, "profile "+actor) return } + + // One extra (best-effort) call for the publication count; on error the + // profile still renders, just without that number. + pubCount := -1 + if pubs, err := s.client.Publications(r.Context, profile.DID); err == nil { + pubCount = len(pubs) + } + w.SetHeader(gemini.CodeSuccess, gemini.DefaultMIMEType) - render.Profile(w, actor, profile) + render.Profile(w, actor, profile, pubCount) } // handleAvatar proxies the actor's avatar image from the Bluesky CDN.