+26
-57
appview/db/follow.go
+26
-57
appview/db/follow.go
···
5
5
"log"
6
6
"strings"
7
7
"time"
8
+
9
+
"tangled.org/core/appview/models"
8
10
)
9
11
10
-
type Follow struct {
11
-
UserDid string
12
-
SubjectDid string
13
-
FollowedAt time.Time
14
-
Rkey string
15
-
}
16
-
17
-
func AddFollow(e Execer, follow *Follow) error {
12
+
func AddFollow(e Execer, follow *models.Follow) error {
18
13
query := `insert or ignore into follows (user_did, subject_did, rkey) values (?, ?, ?)`
19
14
_, err := e.Exec(query, follow.UserDid, follow.SubjectDid, follow.Rkey)
20
15
return err
21
16
}
22
17
23
18
// Get a follow record
24
-
func GetFollow(e Execer, userDid, subjectDid string) (*Follow, error) {
19
+
func GetFollow(e Execer, userDid, subjectDid string) (*models.Follow, error) {
25
20
query := `select user_did, subject_did, followed_at, rkey from follows where user_did = ? and subject_did = ?`
26
21
row := e.QueryRow(query, userDid, subjectDid)
27
22
28
-
var follow Follow
23
+
var follow models.Follow
29
24
var followedAt string
30
25
err := row.Scan(&follow.UserDid, &follow.SubjectDid, &followedAt, &follow.Rkey)
31
26
if err != nil {
···
55
50
return err
56
51
}
57
52
58
-
type FollowStats struct {
59
-
Followers int64
60
-
Following int64
61
-
}
62
-
63
-
func GetFollowerFollowingCount(e Execer, did string) (FollowStats, error) {
53
+
func GetFollowerFollowingCount(e Execer, did string) (models.FollowStats, error) {
64
54
var followers, following int64
65
55
err := e.QueryRow(
66
56
`SELECT
···
68
58
COUNT(CASE WHEN user_did = ? THEN 1 END) AS following
69
59
FROM follows;`, did, did).Scan(&followers, &following)
70
60
if err != nil {
71
-
return FollowStats{}, err
61
+
return models.FollowStats{}, err
72
62
}
73
-
return FollowStats{
63
+
return models.FollowStats{
74
64
Followers: followers,
75
65
Following: following,
76
66
}, nil
77
67
}
78
68
79
-
func GetFollowerFollowingCounts(e Execer, dids []string) (map[string]FollowStats, error) {
69
+
func GetFollowerFollowingCounts(e Execer, dids []string) (map[string]models.FollowStats, error) {
80
70
if len(dids) == 0 {
81
71
return nil, nil
82
72
}
···
112
102
) g on f.did = g.did`,
113
103
placeholderStr, placeholderStr)
114
104
115
-
result := make(map[string]FollowStats)
105
+
result := make(map[string]models.FollowStats)
116
106
117
107
rows, err := e.Query(query, args...)
118
108
if err != nil {
···
126
116
if err := rows.Scan(&did, &followers, &following); err != nil {
127
117
return nil, err
128
118
}
129
-
result[did] = FollowStats{
119
+
result[did] = models.FollowStats{
130
120
Followers: followers,
131
121
Following: following,
132
122
}
···
134
124
135
125
for _, did := range dids {
136
126
if _, exists := result[did]; !exists {
137
-
result[did] = FollowStats{
127
+
result[did] = models.FollowStats{
138
128
Followers: 0,
139
129
Following: 0,
140
130
}
···
144
134
return result, nil
145
135
}
146
136
147
-
func GetFollows(e Execer, limit int, filters ...filter) ([]Follow, error) {
148
-
var follows []Follow
137
+
func GetFollows(e Execer, limit int, filters ...filter) ([]models.Follow, error) {
138
+
var follows []models.Follow
149
139
150
140
var conditions []string
151
141
var args []any
···
177
167
return nil, err
178
168
}
179
169
for rows.Next() {
180
-
var follow Follow
170
+
var follow models.Follow
181
171
var followedAt string
182
172
err := rows.Scan(
183
173
&follow.UserDid,
···
200
190
return follows, nil
201
191
}
202
192
203
-
func GetFollowers(e Execer, did string) ([]Follow, error) {
193
+
func GetFollowers(e Execer, did string) ([]models.Follow, error) {
204
194
return GetFollows(e, 0, FilterEq("subject_did", did))
205
195
}
206
196
207
-
func GetFollowing(e Execer, did string) ([]Follow, error) {
197
+
func GetFollowing(e Execer, did string) ([]models.Follow, error) {
208
198
return GetFollows(e, 0, FilterEq("user_did", did))
209
199
}
210
200
211
-
type FollowStatus int
212
-
213
-
const (
214
-
IsNotFollowing FollowStatus = iota
215
-
IsFollowing
216
-
IsSelf
217
-
)
218
-
219
-
func (s FollowStatus) String() string {
220
-
switch s {
221
-
case IsNotFollowing:
222
-
return "IsNotFollowing"
223
-
case IsFollowing:
224
-
return "IsFollowing"
225
-
case IsSelf:
226
-
return "IsSelf"
227
-
default:
228
-
return "IsNotFollowing"
229
-
}
230
-
}
231
-
232
-
func getFollowStatuses(e Execer, userDid string, subjectDids []string) (map[string]FollowStatus, error) {
201
+
func getFollowStatuses(e Execer, userDid string, subjectDids []string) (map[string]models.FollowStatus, error) {
233
202
if len(subjectDids) == 0 || userDid == "" {
234
-
return make(map[string]FollowStatus), nil
203
+
return make(map[string]models.FollowStatus), nil
235
204
}
236
205
237
-
result := make(map[string]FollowStatus)
206
+
result := make(map[string]models.FollowStatus)
238
207
239
208
for _, subjectDid := range subjectDids {
240
209
if userDid == subjectDid {
241
-
result[subjectDid] = IsSelf
210
+
result[subjectDid] = models.IsSelf
242
211
} else {
243
-
result[subjectDid] = IsNotFollowing
212
+
result[subjectDid] = models.IsNotFollowing
244
213
}
245
214
}
246
215
···
281
250
if err := rows.Scan(&subjectDid); err != nil {
282
251
return nil, err
283
252
}
284
-
result[subjectDid] = IsFollowing
253
+
result[subjectDid] = models.IsFollowing
285
254
}
286
255
287
256
return result, nil
288
257
}
289
258
290
-
func GetFollowStatus(e Execer, userDid, subjectDid string) FollowStatus {
259
+
func GetFollowStatus(e Execer, userDid, subjectDid string) models.FollowStatus {
291
260
statuses, err := getFollowStatuses(e, userDid, []string{subjectDid})
292
261
if err != nil {
293
-
return IsNotFollowing
262
+
return models.IsNotFollowing
294
263
}
295
264
return statuses[subjectDid]
296
265
}
297
266
298
-
func GetFollowStatuses(e Execer, userDid string, subjectDids []string) (map[string]FollowStatus, error) {
267
+
func GetFollowStatuses(e Execer, userDid string, subjectDids []string) (map[string]models.FollowStatus, error) {
299
268
return getFollowStatuses(e, userDid, subjectDids)
300
269
}
+6
-5
appview/db/timeline.go
+6
-5
appview/db/timeline.go
···
5
5
"time"
6
6
7
7
"github.com/bluesky-social/indigo/atproto/syntax"
8
+
"tangled.org/core/appview/models"
8
9
)
9
10
10
11
type TimelineEvent struct {
11
12
*Repo
12
-
*Follow
13
+
*models.Follow
13
14
*Star
14
15
15
16
EventAt time.Time
···
19
20
20
21
// optional: populate only if event is Follow
21
22
*Profile
22
-
*FollowStats
23
-
*FollowStatus
23
+
*models.FollowStats
24
+
*models.FollowStatus
24
25
25
26
// optional: populate only if event is Repo
26
27
IsStarred bool
···
211
212
return nil, err
212
213
}
213
214
214
-
var followStatuses map[string]FollowStatus
215
+
var followStatuses map[string]models.FollowStatus
215
216
if loggedInUserDid != "" {
216
217
followStatuses, err = GetFollowStatuses(e, loggedInUserDid, subjects)
217
218
if err != nil {
···
224
225
profile, _ := profiles[f.SubjectDid]
225
226
followStatMap, _ := followStatMap[f.SubjectDid]
226
227
227
-
followStatus := IsNotFollowing
228
+
followStatus := models.IsNotFollowing
228
229
if followStatuses != nil {
229
230
followStatus = followStatuses[f.SubjectDid]
230
231
}
+1
-1
appview/ingester.go
+1
-1
appview/ingester.go
+38
appview/models/follow.go
+38
appview/models/follow.go
···
1
+
package models
2
+
3
+
import (
4
+
"time"
5
+
)
6
+
7
+
type Follow struct {
8
+
UserDid string
9
+
SubjectDid string
10
+
FollowedAt time.Time
11
+
Rkey string
12
+
}
13
+
14
+
type FollowStats struct {
15
+
Followers int64
16
+
Following int64
17
+
}
18
+
19
+
type FollowStatus int
20
+
21
+
const (
22
+
IsNotFollowing FollowStatus = iota
23
+
IsFollowing
24
+
IsSelf
25
+
)
26
+
27
+
func (s FollowStatus) String() string {
28
+
switch s {
29
+
case IsNotFollowing:
30
+
return "IsNotFollowing"
31
+
case IsFollowing:
32
+
return "IsFollowing"
33
+
case IsSelf:
34
+
return "IsSelf"
35
+
default:
36
+
return "IsNotFollowing"
37
+
}
38
+
}
+3
-2
appview/notify/merged_notifier.go
+3
-2
appview/notify/merged_notifier.go
···
4
4
"context"
5
5
6
6
"tangled.org/core/appview/db"
7
+
"tangled.org/core/appview/models"
7
8
)
8
9
9
10
type mergedNotifier struct {
···
39
40
}
40
41
}
41
42
42
-
func (m *mergedNotifier) NewFollow(ctx context.Context, follow *db.Follow) {
43
+
func (m *mergedNotifier) NewFollow(ctx context.Context, follow *models.Follow) {
43
44
for _, notifier := range m.notifiers {
44
45
notifier.NewFollow(ctx, follow)
45
46
}
46
47
}
47
-
func (m *mergedNotifier) DeleteFollow(ctx context.Context, follow *db.Follow) {
48
+
func (m *mergedNotifier) DeleteFollow(ctx context.Context, follow *models.Follow) {
48
49
for _, notifier := range m.notifiers {
49
50
notifier.DeleteFollow(ctx, follow)
50
51
}
+5
-4
appview/notify/notifier.go
+5
-4
appview/notify/notifier.go
···
4
4
"context"
5
5
6
6
"tangled.org/core/appview/db"
7
+
"tangled.org/core/appview/models"
7
8
)
8
9
9
10
type Notifier interface {
···
14
15
15
16
NewIssue(ctx context.Context, issue *db.Issue)
16
17
17
-
NewFollow(ctx context.Context, follow *db.Follow)
18
-
DeleteFollow(ctx context.Context, follow *db.Follow)
18
+
NewFollow(ctx context.Context, follow *models.Follow)
19
+
DeleteFollow(ctx context.Context, follow *models.Follow)
19
20
20
21
NewPull(ctx context.Context, pull *db.Pull)
21
22
NewPullComment(ctx context.Context, comment *db.PullComment)
···
39
40
40
41
func (m *BaseNotifier) NewIssue(ctx context.Context, issue *db.Issue) {}
41
42
42
-
func (m *BaseNotifier) NewFollow(ctx context.Context, follow *db.Follow) {}
43
-
func (m *BaseNotifier) DeleteFollow(ctx context.Context, follow *db.Follow) {}
43
+
func (m *BaseNotifier) NewFollow(ctx context.Context, follow *models.Follow) {}
44
+
func (m *BaseNotifier) DeleteFollow(ctx context.Context, follow *models.Follow) {}
44
45
45
46
func (m *BaseNotifier) NewPull(ctx context.Context, pull *db.Pull) {}
46
47
func (m *BaseNotifier) NewPullComment(ctx context.Context, comment *db.PullComment) {}
+3
-3
appview/pages/pages.go
+3
-3
appview/pages/pages.go
···
411
411
type ProfileCard struct {
412
412
UserDid string
413
413
UserHandle string
414
-
FollowStatus db.FollowStatus
414
+
FollowStatus models.FollowStatus
415
415
Punchcard *db.Punchcard
416
416
Profile *db.Profile
417
417
Stats ProfileStats
···
489
489
490
490
type FollowCard struct {
491
491
UserDid string
492
-
FollowStatus db.FollowStatus
492
+
FollowStatus models.FollowStatus
493
493
FollowersCount int64
494
494
FollowingCount int64
495
495
Profile *db.Profile
···
521
521
522
522
type FollowFragmentParams struct {
523
523
UserDid string
524
-
FollowStatus db.FollowStatus
524
+
FollowStatus models.FollowStatus
525
525
}
526
526
527
527
func (p *Pages) FollowFragment(w io.Writer, params FollowFragmentParams) error {
+3
-2
appview/posthog/notifier.go
+3
-2
appview/posthog/notifier.go
···
6
6
7
7
"github.com/posthog/posthog-go"
8
8
"tangled.org/core/appview/db"
9
+
"tangled.org/core/appview/models"
9
10
"tangled.org/core/appview/notify"
10
11
)
11
12
···
98
99
}
99
100
}
100
101
101
-
func (n *posthogNotifier) NewFollow(ctx context.Context, follow *db.Follow) {
102
+
func (n *posthogNotifier) NewFollow(ctx context.Context, follow *models.Follow) {
102
103
err := n.client.Enqueue(posthog.Capture{
103
104
DistinctId: follow.UserDid,
104
105
Event: "follow",
···
109
110
}
110
111
}
111
112
112
-
func (n *posthogNotifier) DeleteFollow(ctx context.Context, follow *db.Follow) {
113
+
func (n *posthogNotifier) DeleteFollow(ctx context.Context, follow *models.Follow) {
113
114
err := n.client.Enqueue(posthog.Capture{
114
115
DistinctId: follow.UserDid,
115
116
Event: "unfollow",
+4
-3
appview/state/follow.go
+4
-3
appview/state/follow.go
···
9
9
lexutil "github.com/bluesky-social/indigo/lex/util"
10
10
"tangled.org/core/api/tangled"
11
11
"tangled.org/core/appview/db"
12
+
"tangled.org/core/appview/models"
12
13
"tangled.org/core/appview/pages"
13
14
"tangled.org/core/tid"
14
15
)
···
59
60
60
61
log.Println("created atproto record: ", resp.Uri)
61
62
62
-
follow := &db.Follow{
63
+
follow := &models.Follow{
63
64
UserDid: currentUser.Did,
64
65
SubjectDid: subjectIdent.DID.String(),
65
66
Rkey: rkey,
···
75
76
76
77
s.pages.FollowFragment(w, pages.FollowFragmentParams{
77
78
UserDid: subjectIdent.DID.String(),
78
-
FollowStatus: db.IsFollowing,
79
+
FollowStatus: models.IsFollowing,
79
80
})
80
81
81
82
return
···
106
107
107
108
s.pages.FollowFragment(w, pages.FollowFragmentParams{
108
109
UserDid: subjectIdent.DID.String(),
109
-
FollowStatus: db.IsNotFollowing,
110
+
FollowStatus: models.IsNotFollowing,
110
111
})
111
112
112
113
s.notifier.DeleteFollow(r.Context(), follow)
+9
-8
appview/state/profile.go
+9
-8
appview/state/profile.go
···
17
17
"github.com/gorilla/feeds"
18
18
"tangled.org/core/api/tangled"
19
19
"tangled.org/core/appview/db"
20
+
"tangled.org/core/appview/models"
20
21
"tangled.org/core/appview/pages"
21
22
)
22
23
···
76
77
}
77
78
78
79
loggedInUser := s.oauth.GetUser(r)
79
-
followStatus := db.IsNotFollowing
80
+
followStatus := models.IsNotFollowing
80
81
if loggedInUser != nil {
81
82
followStatus = db.GetFollowStatus(s.db, loggedInUser.Did, did)
82
83
}
···
271
272
272
273
func (s *State) followPage(
273
274
r *http.Request,
274
-
fetchFollows func(db.Execer, string) ([]db.Follow, error),
275
-
extractDid func(db.Follow) string,
275
+
fetchFollows func(db.Execer, string) ([]models.Follow, error),
276
+
extractDid func(models.Follow) string,
276
277
) (*FollowsPageParams, error) {
277
278
l := s.logger.With("handler", "reposPage")
278
279
···
329
330
followCards := make([]pages.FollowCard, len(follows))
330
331
for i, did := range followDids {
331
332
followStats := followStatsMap[did]
332
-
followStatus := db.IsNotFollowing
333
+
followStatus := models.IsNotFollowing
333
334
if _, exists := loggedInUserFollowing[did]; exists {
334
-
followStatus = db.IsFollowing
335
+
followStatus = models.IsFollowing
335
336
} else if loggedInUser != nil && loggedInUser.Did == did {
336
-
followStatus = db.IsSelf
337
+
followStatus = models.IsSelf
337
338
}
338
339
339
340
var profile *db.Profile
···
358
359
}
359
360
360
361
func (s *State) followersPage(w http.ResponseWriter, r *http.Request) {
361
-
followPage, err := s.followPage(r, db.GetFollowers, func(f db.Follow) string { return f.UserDid })
362
+
followPage, err := s.followPage(r, db.GetFollowers, func(f models.Follow) string { return f.UserDid })
362
363
if err != nil {
363
364
s.pages.Notice(w, "all-followers", "Failed to load followers")
364
365
return
···
372
373
}
373
374
374
375
func (s *State) followingPage(w http.ResponseWriter, r *http.Request) {
375
-
followPage, err := s.followPage(r, db.GetFollowing, func(f db.Follow) string { return f.SubjectDid })
376
+
followPage, err := s.followPage(r, db.GetFollowing, func(f models.Follow) string { return f.SubjectDid })
376
377
if err != nil {
377
378
s.pages.Notice(w, "all-following", "Failed to load following")
378
379
return