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 "github.com/posthog/posthog-go"
11 "tangled.sh/tangled.sh/core/api/tangled"
12 "tangled.sh/tangled.sh/core/appview"
13 "tangled.sh/tangled.sh/core/appview/db"
14 "tangled.sh/tangled.sh/core/appview/pages"
15)
16
17func (s *State) Follow(w http.ResponseWriter, r *http.Request) {
18 currentUser := s.oauth.GetUser(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 }
30
31 if currentUser.Did == subjectIdent.DID.String() {
32 log.Println("cant follow or unfollow yourself")
33 return
34 }
35
36 client, err := s.oauth.AuthorizedClient(r)
37 if err != nil {
38 log.Println("failed to authorize client")
39 return
40 }
41
42 switch r.Method {
43 case http.MethodPost:
44 createdAt := time.Now().Format(time.RFC3339)
45 rkey := appview.TID()
46 resp, err := client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
47 Collection: tangled.GraphFollowNSID,
48 Repo: currentUser.Did,
49 Rkey: rkey,
50 Record: &lexutil.LexiconTypeDecoder{
51 Val: &tangled.GraphFollow{
52 Subject: subjectIdent.DID.String(),
53 CreatedAt: createdAt,
54 }},
55 })
56 if err != nil {
57 log.Println("failed to create atproto record", err)
58 return
59 }
60
61 err = db.AddFollow(s.db, currentUser.Did, subjectIdent.DID.String(), rkey)
62 if err != nil {
63 log.Println("failed to follow", err)
64 return
65 }
66
67 log.Println("created atproto record: ", resp.Uri)
68
69 s.pages.FollowFragment(w, pages.FollowFragmentParams{
70 UserDid: subjectIdent.DID.String(),
71 FollowStatus: db.IsFollowing,
72 })
73
74 if !s.config.Core.Dev {
75 err = s.posthog.Enqueue(posthog.Capture{
76 DistinctId: currentUser.Did,
77 Event: "follow",
78 Properties: posthog.Properties{"subject": subjectIdent.DID.String()},
79 })
80 if err != nil {
81 log.Println("failed to enqueue posthog event:", err)
82 }
83 }
84
85 return
86 case http.MethodDelete:
87 // find the record in the db
88 follow, err := db.GetFollow(s.db, currentUser.Did, subjectIdent.DID.String())
89 if err != nil {
90 log.Println("failed to get follow relationship")
91 return
92 }
93
94 _, err = client.RepoDeleteRecord(r.Context(), &comatproto.RepoDeleteRecord_Input{
95 Collection: tangled.GraphFollowNSID,
96 Repo: currentUser.Did,
97 Rkey: follow.Rkey,
98 })
99
100 if err != nil {
101 log.Println("failed to unfollow")
102 return
103 }
104
105 err = db.DeleteFollowByRkey(s.db, currentUser.Did, follow.Rkey)
106 if err != nil {
107 log.Println("failed to delete follow from DB")
108 // this is not an issue, the firehose event might have already done this
109 }
110
111 s.pages.FollowFragment(w, pages.FollowFragmentParams{
112 UserDid: subjectIdent.DID.String(),
113 FollowStatus: db.IsNotFollowing,
114 })
115
116 if !s.config.Core.Dev {
117 err = s.posthog.Enqueue(posthog.Capture{
118 DistinctId: currentUser.Did,
119 Event: "unfollow",
120 Properties: posthog.Properties{"subject": subjectIdent.DID.String()},
121 })
122 if err != nil {
123 log.Println("failed to enqueue posthog event:", err)
124 }
125 }
126
127 return
128 }
129
130}