+65
-5
appview/db/follow.go
+65
-5
appview/db/follow.go
···
229
229
}
230
230
}
231
231
232
+
func getFollowStatuses(e Execer, userDid string, subjectDids []string) (map[string]FollowStatus, error) {
233
+
if len(subjectDids) == 0 || userDid == "" {
234
+
return make(map[string]FollowStatus), nil
235
+
}
236
+
237
+
result := make(map[string]FollowStatus)
238
+
239
+
for _, subjectDid := range subjectDids {
240
+
if userDid == subjectDid {
241
+
result[subjectDid] = IsSelf
242
+
} else {
243
+
result[subjectDid] = IsNotFollowing
244
+
}
245
+
}
246
+
247
+
var querySubjects []string
248
+
for _, subjectDid := range subjectDids {
249
+
if userDid != subjectDid {
250
+
querySubjects = append(querySubjects, subjectDid)
251
+
}
252
+
}
253
+
254
+
if len(querySubjects) == 0 {
255
+
return result, nil
256
+
}
257
+
258
+
placeholders := make([]string, len(querySubjects))
259
+
args := make([]any, len(querySubjects)+1)
260
+
args[0] = userDid
261
+
262
+
for i, subjectDid := range querySubjects {
263
+
placeholders[i] = "?"
264
+
args[i+1] = subjectDid
265
+
}
266
+
267
+
query := fmt.Sprintf(`
268
+
SELECT subject_did
269
+
FROM follows
270
+
WHERE user_did = ? AND subject_did IN (%s)
271
+
`, strings.Join(placeholders, ","))
272
+
273
+
rows, err := e.Query(query, args...)
274
+
if err != nil {
275
+
return nil, err
276
+
}
277
+
defer rows.Close()
278
+
279
+
for rows.Next() {
280
+
var subjectDid string
281
+
if err := rows.Scan(&subjectDid); err != nil {
282
+
return nil, err
283
+
}
284
+
result[subjectDid] = IsFollowing
285
+
}
286
+
287
+
return result, nil
288
+
}
289
+
232
290
func GetFollowStatus(e Execer, userDid, subjectDid string) FollowStatus {
233
-
if userDid == subjectDid {
234
-
return IsSelf
235
-
} else if _, err := GetFollow(e, userDid, subjectDid); err != nil {
291
+
statuses, err := getFollowStatuses(e, userDid, []string{subjectDid})
292
+
if err != nil {
236
293
return IsNotFollowing
237
-
} else {
238
-
return IsFollowing
239
294
}
295
+
return statuses[subjectDid]
296
+
}
297
+
298
+
func GetFollowStatuses(e Execer, userDid string, subjectDids []string) (map[string]FollowStatus, error) {
299
+
return getFollowStatuses(e, userDid, subjectDids)
240
300
}
+53
-3
appview/db/star.go
+53
-3
appview/db/star.go
···
94
94
return stars, nil
95
95
}
96
96
97
+
// getStarStatuses returns a map of repo URIs to star status for a given user
98
+
// This is an internal helper function to avoid N+1 queries
99
+
func getStarStatuses(e Execer, userDid string, repoAts []syntax.ATURI) (map[string]bool, error) {
100
+
if len(repoAts) == 0 || userDid == "" {
101
+
return make(map[string]bool), nil
102
+
}
103
+
104
+
placeholders := make([]string, len(repoAts))
105
+
args := make([]any, len(repoAts)+1)
106
+
args[0] = userDid
107
+
108
+
for i, repoAt := range repoAts {
109
+
placeholders[i] = "?"
110
+
args[i+1] = repoAt.String()
111
+
}
112
+
113
+
query := fmt.Sprintf(`
114
+
SELECT repo_at
115
+
FROM stars
116
+
WHERE starred_by_did = ? AND repo_at IN (%s)
117
+
`, strings.Join(placeholders, ","))
118
+
119
+
rows, err := e.Query(query, args...)
120
+
if err != nil {
121
+
return nil, err
122
+
}
123
+
defer rows.Close()
124
+
125
+
result := make(map[string]bool)
126
+
// Initialize all repos as not starred
127
+
for _, repoAt := range repoAts {
128
+
result[repoAt.String()] = false
129
+
}
130
+
131
+
// Mark starred repos as true
132
+
for rows.Next() {
133
+
var repoAt string
134
+
if err := rows.Scan(&repoAt); err != nil {
135
+
return nil, err
136
+
}
137
+
result[repoAt] = true
138
+
}
139
+
140
+
return result, nil
141
+
}
142
+
97
143
func GetStarStatus(e Execer, userDid string, repoAt syntax.ATURI) bool {
98
-
if _, err := GetStar(e, userDid, repoAt); err != nil {
144
+
statuses, err := getStarStatuses(e, userDid, []syntax.ATURI{repoAt})
145
+
if err != nil {
99
146
return false
100
-
} else {
101
-
return true
102
147
}
148
+
return statuses[repoAt.String()]
103
149
}
104
150
151
+
// GetStarStatuses returns a map of repo URIs to star status for a given user
152
+
func GetStarStatuses(e Execer, userDid string, repoAts []syntax.ATURI) (map[string]bool, error) {
153
+
return getStarStatuses(e, userDid, repoAts)
154
+
}
105
155
func GetStars(e Execer, limit int, filters ...filter) ([]Star, error) {
106
156
var conditions []string
107
157
var args []any
+40
-5
appview/db/timeline.go
+40
-5
appview/db/timeline.go
···
3
3
import (
4
4
"sort"
5
5
"time"
6
+
7
+
"github.com/bluesky-social/indigo/atproto/syntax"
6
8
)
7
9
8
10
type TimelineEvent struct {
···
30
32
func MakeTimeline(e Execer, limit int, loggedInUserDid string) ([]TimelineEvent, error) {
31
33
var events []TimelineEvent
32
34
33
-
repos, err := getTimelineRepos(e, limit)
35
+
repos, err := getTimelineRepos(e, limit, loggedInUserDid)
34
36
if err != nil {
35
37
return nil, err
36
38
}
···
61
63
return events, nil
62
64
}
63
65
64
-
func getTimelineRepos(e Execer, limit int) ([]TimelineEvent, error) {
66
+
func getTimelineRepos(e Execer, limit int, loggedInUserDid string) ([]TimelineEvent, error) {
65
67
repos, err := GetRepos(e, limit)
66
68
if err != nil {
67
69
return nil, err
···
88
90
uriToRepo[r.RepoAt().String()] = r
89
91
}
90
92
93
+
var starStatuses map[string]bool
94
+
if loggedInUserDid != "" {
95
+
var repoAts []syntax.ATURI
96
+
for _, r := range repos {
97
+
repoAts = append(repoAts, r.RepoAt())
98
+
}
99
+
var err error
100
+
starStatuses, err = GetStarStatuses(e, loggedInUserDid, repoAts)
101
+
if err != nil {
102
+
return nil, err
103
+
}
104
+
}
105
+
91
106
var events []TimelineEvent
92
107
for _, r := range repos {
93
108
var source *Repo
···
97
112
}
98
113
}
99
114
115
+
var isStarred bool
116
+
if starStatuses != nil {
117
+
isStarred = starStatuses[r.RepoAt().String()]
118
+
}
119
+
120
+
var starCount int64
121
+
if r.RepoStats != nil {
122
+
starCount = int64(r.RepoStats.StarCount)
123
+
}
124
+
100
125
events = append(events, TimelineEvent{
101
-
Repo: &r,
102
-
EventAt: r.Created,
103
-
Source: source,
126
+
Repo: &r,
127
+
EventAt: r.Created,
128
+
Source: source,
129
+
IsStarred: isStarred,
130
+
StarCount: starCount,
104
131
})
105
132
}
106
133
···
157
184
followStatMap, err := GetFollowerFollowingCounts(e, subjects)
158
185
if err != nil {
159
186
return nil, err
187
+
}
188
+
189
+
var followStatuses map[string]FollowStatus
190
+
if loggedInUserDid != "" {
191
+
followStatuses, err = GetFollowStatuses(e, loggedInUserDid, subjects)
192
+
if err != nil {
193
+
return nil, err
194
+
}
160
195
}
161
196
162
197
var events []TimelineEvent