-1
README.md
-1
README.md
+76
hydration/actor.go
+76
hydration/actor.go
···
6
6
"fmt"
7
7
"log/slog"
8
8
"strings"
9
+
"sync"
9
10
10
11
"github.com/bluesky-social/indigo/api/bsky"
11
12
"github.com/bluesky-social/indigo/atproto/syntax"
···
52
53
}
53
54
54
55
return info, nil
56
+
}
57
+
58
+
type ActorInfoDetailed struct {
59
+
ActorInfo
60
+
FollowCount int64
61
+
FollowerCount int64
62
+
PostCount int64
63
+
}
64
+
65
+
func (h *Hydrator) HydrateActorDetailed(ctx context.Context, did string) (*ActorInfoDetailed, error) {
66
+
act, err := h.HydrateActor(ctx, did)
67
+
if err != nil {
68
+
return nil, err
69
+
}
70
+
71
+
actd := ActorInfoDetailed{
72
+
ActorInfo: *act,
73
+
}
74
+
75
+
var wg sync.WaitGroup
76
+
wg.Add(3)
77
+
go func() {
78
+
defer wg.Done()
79
+
c, err := h.getFollowCountForUser(ctx, did)
80
+
if err != nil {
81
+
slog.Error("failed to get follow count", "did", did, "error", err)
82
+
}
83
+
actd.FollowCount = c
84
+
}()
85
+
go func() {
86
+
defer wg.Done()
87
+
c, err := h.getFollowerCountForUser(ctx, did)
88
+
if err != nil {
89
+
slog.Error("failed to get follower count", "did", did, "error", err)
90
+
}
91
+
actd.FollowerCount = c
92
+
}()
93
+
go func() {
94
+
defer wg.Done()
95
+
c, err := h.getPostCountForUser(ctx, did)
96
+
if err != nil {
97
+
slog.Error("failed to get post count", "did", did, "error", err)
98
+
}
99
+
actd.PostCount = c
100
+
}()
101
+
wg.Wait()
102
+
103
+
return &actd, nil
104
+
}
105
+
106
+
func (h *Hydrator) getFollowCountForUser(ctx context.Context, did string) (int64, error) {
107
+
var count int64
108
+
if err := h.db.Raw("SELECT count(*) FROM follows WHERE author = (SELECT id FROM repos WHERE did = ?)", did).Scan(&count).Error; err != nil {
109
+
return 0, err
110
+
}
111
+
112
+
return count, nil
113
+
}
114
+
115
+
func (h *Hydrator) getFollowerCountForUser(ctx context.Context, did string) (int64, error) {
116
+
var count int64
117
+
if err := h.db.Raw("SELECT count(*) FROM follows WHERE subject = (SELECT id FROM repos WHERE did = ?)", did).Scan(&count).Error; err != nil {
118
+
return 0, err
119
+
}
120
+
121
+
return count, nil
122
+
}
123
+
124
+
func (h *Hydrator) getPostCountForUser(ctx context.Context, did string) (int64, error) {
125
+
var count int64
126
+
if err := h.db.Raw("SELECT count(*) FROM posts WHERE author = (SELECT id FROM repos WHERE did = ?)", did).Scan(&count).Error; err != nil {
127
+
return 0, err
128
+
}
129
+
130
+
return count, nil
55
131
}
56
132
57
133
// HydrateActors hydrates multiple actors
+4
-7
views/actor.go
+4
-7
views/actor.go
···
57
57
}
58
58
59
59
// ProfileViewDetailed builds a detailed profile view (app.bsky.actor.defs#profileViewDetailed)
60
-
func ProfileViewDetailed(actor *hydration.ActorInfo, followerCount, followsCount, postsCount int) *bsky.ActorDefs_ProfileViewDetailed {
60
+
func ProfileViewDetailed(actor *hydration.ActorInfoDetailed) *bsky.ActorDefs_ProfileViewDetailed {
61
61
view := &bsky.ActorDefs_ProfileViewDetailed{
62
62
Did: actor.DID,
63
63
Handle: actor.Handle,
···
85
85
}
86
86
87
87
// Add counts
88
-
fc := int64(followerCount)
89
-
fsc := int64(followsCount)
90
-
pc := int64(postsCount)
91
-
view.FollowersCount = &fc
92
-
view.FollowsCount = &fsc
93
-
view.PostsCount = &pc
88
+
view.FollowersCount = &actor.FollowerCount
89
+
view.FollowsCount = &actor.FollowCount
90
+
view.PostsCount = &actor.PostCount
94
91
95
92
return view
96
93
}
+2
-11
xrpc/actor/getProfile.go
+2
-11
xrpc/actor/getProfile.go
···
30
30
}
31
31
32
32
// Hydrate actor info
33
-
actorInfo, err := hydrator.HydrateActor(ctx, did)
33
+
actorInfo, err := hydrator.HydrateActorDetailed(ctx, did)
34
34
if err != nil {
35
35
return c.JSON(http.StatusNotFound, map[string]interface{}{
36
36
"error": "ActorNotFound",
···
38
38
})
39
39
}
40
40
41
-
// Get follower/follows/posts counts
42
-
// TODO: These queries should be optimized
43
-
var followerCount, followsCount, postsCount int
44
-
45
-
// We'll return 0 for now - can optimize later
46
-
followerCount = 0
47
-
followsCount = 0
48
-
postsCount = 0
49
-
50
41
// Build response
51
-
profile := views.ProfileViewDetailed(actorInfo, followerCount, followsCount, postsCount)
42
+
profile := views.ProfileViewDetailed(actorInfo)
52
43
53
44
return c.JSON(http.StatusOK, profile)
54
45
}
+2
-2
xrpc/actor/getProfiles.go
+2
-2
xrpc/actor/getProfiles.go
···
38
38
}
39
39
40
40
// Hydrate actor info
41
-
actorInfo, err := hydrator.HydrateActor(ctx, did)
41
+
actorInfo, err := hydrator.HydrateActorDetailed(ctx, did)
42
42
if err != nil {
43
43
// Skip actors that can't be hydrated
44
44
continue
···
58
58
(SELECT COUNT(*) FROM posts WHERE author = (SELECT id FROM repos WHERE did = ?)) as posts
59
59
`, did, did, did).Scan(&c)
60
60
61
-
profiles = append(profiles, views.ProfileViewDetailed(actorInfo, c.Followers, c.Follows, c.Posts))
61
+
profiles = append(profiles, views.ProfileViewDetailed(actorInfo))
62
62
}
63
63
64
64
return c.JSON(http.StatusOK, map[string]interface{}{
+5
-5
xrpc/feed/getPostThread.go
+5
-5
xrpc/feed/getPostThread.go
···
49
49
50
50
// Query all posts in this thread
51
51
type threadPost struct {
52
-
ID uint
53
-
Rkey string
54
-
ReplyTo uint
55
-
InThread uint
56
-
AuthorDID string
52
+
ID uint
53
+
Rkey string
54
+
ReplyTo uint
55
+
InThread uint
56
+
AuthorDID string
57
57
}
58
58
var threadPosts []threadPost
59
59
db.Raw(`