A locally focused bluesky appview
1package views
2
3import (
4 "fmt"
5
6 "github.com/bluesky-social/indigo/api/bsky"
7 "github.com/bluesky-social/indigo/lex/util"
8 "github.com/whyrusleeping/konbini/hydration"
9)
10
11// PostView builds a post view (app.bsky.feed.defs#postView)
12func PostView(post *hydration.PostInfo, author *hydration.ActorInfo) *bsky.FeedDefs_PostView {
13 view := &bsky.FeedDefs_PostView{
14 LexiconTypeID: "app.bsky.feed.defs#postView",
15 Uri: post.URI,
16 Cid: post.Cid,
17 Author: ProfileViewBasic(author),
18 Record: &util.LexiconTypeDecoder{
19 Val: post.Post,
20 },
21 IndexedAt: post.Post.CreatedAt, // Using createdAt as indexedAt for now
22 }
23
24 // Add engagement counts
25 if post.LikeCount > 0 {
26 lc := int64(post.LikeCount)
27 view.LikeCount = &lc
28 }
29 if post.RepostCount > 0 {
30 rc := int64(post.RepostCount)
31 view.RepostCount = &rc
32 }
33 if post.ReplyCount > 0 {
34 rpc := int64(post.ReplyCount)
35 view.ReplyCount = &rpc
36 }
37
38 // Add viewer state
39 if post.ViewerLike != "" {
40 view.Viewer = &bsky.FeedDefs_ViewerState{
41 Like: &post.ViewerLike,
42 }
43 }
44
45 // Add embed if it was hydrated
46 if post.EmbedInfo != nil {
47 view.Embed = post.EmbedInfo
48 }
49
50 return view
51}
52
53// FeedViewPost builds a feed view post (app.bsky.feed.defs#feedViewPost)
54func FeedViewPost(post *hydration.PostInfo, author *hydration.ActorInfo) *bsky.FeedDefs_FeedViewPost {
55 return &bsky.FeedDefs_FeedViewPost{
56 Post: PostView(post, author),
57 }
58}
59
60// ThreadViewPost builds a thread view post (app.bsky.feed.defs#threadViewPost)
61func ThreadViewPost(post *hydration.PostInfo, author *hydration.ActorInfo, parent, replies any) *bsky.FeedDefs_ThreadViewPost {
62 view := &bsky.FeedDefs_ThreadViewPost{
63 LexiconTypeID: "app.bsky.feed.defs#threadViewPost",
64 Post: PostView(post, author),
65 }
66
67 // TODO: Type parent and replies properly as union types
68 // For now leaving them as interface{} to be handled by handlers
69
70 return view
71}
72
73// GeneratorView builds a feed generator view (app.bsky.feed.defs#generatorView)
74func GeneratorView(uri, cid string, record *bsky.FeedGenerator, creator *hydration.ActorInfo, likeCount int64, viewerLike string, indexedAt string) *bsky.FeedDefs_GeneratorView {
75 view := &bsky.FeedDefs_GeneratorView{
76 LexiconTypeID: "app.bsky.feed.defs#generatorView",
77 Uri: uri,
78 Cid: cid,
79 Did: record.Did,
80 Creator: ProfileView(creator),
81 DisplayName: record.DisplayName,
82 Description: record.Description,
83 IndexedAt: indexedAt,
84 }
85
86 // Add optional fields
87 if record.Avatar != nil {
88 avatarURL := fmt.Sprintf("https://cdn.bsky.app/img/avatar/plain/%s/%s@jpeg", creator.DID, record.Avatar.Ref.String())
89 view.Avatar = &avatarURL
90 }
91
92 if record.DescriptionFacets != nil && len(record.DescriptionFacets) > 0 {
93 view.DescriptionFacets = record.DescriptionFacets
94 }
95
96 if record.AcceptsInteractions != nil {
97 view.AcceptsInteractions = record.AcceptsInteractions
98 }
99
100 if record.ContentMode != nil {
101 view.ContentMode = record.ContentMode
102 }
103
104 // Add like count if present
105 if likeCount > 0 {
106 view.LikeCount = &likeCount
107 }
108
109 // Add viewer state if viewer has liked
110 if viewerLike != "" {
111 view.Viewer = &bsky.FeedDefs_GeneratorViewerState{
112 Like: &viewerLike,
113 }
114 }
115
116 return view
117}