Monorepo for Tangled
1package state
2
3import (
4 "log"
5 "net/http"
6 "time"
7
8 comatproto "github.com/bluesky-social/indigo/api/atproto"
9 lexutil "github.com/bluesky-social/indigo/lex/util"
10 "tangled.org/core/api/tangled"
11 "tangled.org/core/appview/db"
12 "tangled.org/core/appview/models"
13 "tangled.org/core/appview/pages"
14 "tangled.org/core/tid"
15)
16
17func (s *State) Follow(w http.ResponseWriter, r *http.Request) {
18 currentUser := s.oauth.GetMultiAccountUser(r)
19
20 subject := r.URL.Query().Get("subject")
21 if subject == "" {
22 log.Println("invalid form")
23 return
24 }
25
26 subjectIdent, err := s.idResolver.ResolveIdent(r.Context(), subject)
27 if err != nil {
28 log.Println("failed to follow, invalid did")
29 return
30 }
31
32 if currentUser.Active.Did == subjectIdent.DID.String() {
33 log.Println("cant follow or unfollow yourself")
34 return
35 }
36
37 client, err := s.oauth.AuthorizedClient(r)
38 if err != nil {
39 log.Println("failed to authorize client")
40 return
41 }
42
43 switch r.Method {
44 case http.MethodPost:
45 createdAt := time.Now().Format(time.RFC3339)
46 rkey := tid.TID()
47 resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
48 Collection: tangled.GraphFollowNSID,
49 Repo: currentUser.Active.Did,
50 Rkey: rkey,
51 Record: &lexutil.LexiconTypeDecoder{
52 Val: &tangled.GraphFollow{
53 Subject: subjectIdent.DID.String(),
54 CreatedAt: createdAt,
55 }},
56 })
57 if err != nil {
58 log.Println("failed to create atproto record", err)
59 return
60 }
61
62 log.Println("created atproto record: ", resp.Uri)
63
64 follow := &models.Follow{
65 UserDid: currentUser.Active.Did,
66 SubjectDid: subjectIdent.DID.String(),
67 Rkey: rkey,
68 }
69
70 err = db.AddFollow(s.db, follow)
71 if err != nil {
72 log.Println("failed to follow", err)
73 return
74 }
75
76 s.notifier.NewFollow(r.Context(), follow)
77
78 followStats, err := db.GetFollowerFollowingCount(s.db, subjectIdent.DID.String())
79 if err != nil {
80 log.Println("failed to get follow stats", err)
81 }
82
83 s.pages.FollowFragment(w, pages.FollowFragmentParams{
84 UserDid: subjectIdent.DID.String(),
85 FollowStatus: models.IsFollowing,
86 FollowersCount: followStats.Followers,
87 })
88
89 return
90 case http.MethodDelete:
91 // find the record in the db
92 follow, err := db.GetFollow(s.db, currentUser.Active.Did, subjectIdent.DID.String())
93 if err != nil {
94 log.Println("failed to get follow relationship")
95 return
96 }
97
98 _, err = comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{
99 Collection: tangled.GraphFollowNSID,
100 Repo: currentUser.Active.Did,
101 Rkey: follow.Rkey,
102 })
103
104 if err != nil {
105 log.Println("failed to unfollow")
106 return
107 }
108
109 err = db.DeleteFollowByRkey(s.db, currentUser.Active.Did, follow.Rkey)
110 if err != nil {
111 log.Println("failed to delete follow from DB")
112 // this is not an issue, the firehose event might have already done this
113 }
114
115 followStats, err := db.GetFollowerFollowingCount(s.db, subjectIdent.DID.String())
116 if err != nil {
117 log.Println("failed to get follow stats", err)
118 }
119
120 s.pages.FollowFragment(w, pages.FollowFragmentParams{
121 UserDid: subjectIdent.DID.String(),
122 FollowStatus: models.IsNotFollowing,
123 FollowersCount: followStats.Followers,
124 })
125
126 s.notifier.DeleteFollow(r.Context(), follow)
127
128 return
129 }
130
131}