forked from
tangled.org/core
Monorepo for Tangled
1package state
2
3import (
4 "net/http"
5 "time"
6
7 comatproto "github.com/bluesky-social/indigo/api/atproto"
8 "github.com/bluesky-social/indigo/atproto/syntax"
9 lexutil "github.com/bluesky-social/indigo/lex/util"
10
11 "tangled.org/core/api/tangled"
12 "tangled.org/core/appview/db"
13 "tangled.org/core/appview/models"
14 "tangled.org/core/appview/pages"
15 "tangled.org/core/tid"
16)
17
18func (s *State) React(w http.ResponseWriter, r *http.Request) {
19 l := s.logger.With("handler", "React")
20 currentUser := s.oauth.GetMultiAccountUser(r)
21
22 subject := r.URL.Query().Get("subject")
23 if subject == "" {
24 l.Warn("invalid form")
25 return
26 }
27
28 subjectUri, err := syntax.ParseATURI(subject)
29 if err != nil {
30 l.Warn("invalid form", "subject", subject, "err", err)
31 return
32 }
33
34 reactionKind, ok := models.ParseReactionKind(r.URL.Query().Get("kind"))
35 if !ok {
36 l.Warn("invalid reaction kind", "kind", r.URL.Query().Get("kind"))
37 return
38 }
39
40 client, err := s.oauth.AuthorizedClient(r)
41 if err != nil {
42 l.Error("failed to authorize client", "err", err)
43 return
44 }
45
46 switch r.Method {
47 case http.MethodPost:
48 createdAt := time.Now().Format(time.RFC3339)
49 rkey := tid.TID()
50 resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
51 Collection: tangled.FeedReactionNSID,
52 Repo: currentUser.Active.Did,
53 Rkey: rkey,
54 Record: &lexutil.LexiconTypeDecoder{
55 Val: &tangled.FeedReaction{
56 Subject: subjectUri.String(),
57 Reaction: reactionKind.String(),
58 CreatedAt: createdAt,
59 },
60 },
61 })
62 if err != nil {
63 l.Error("failed to create atproto record", "err", err)
64 return
65 }
66
67 err = db.AddReaction(s.db, currentUser.Active.Did, subjectUri, reactionKind, rkey)
68 if err != nil {
69 l.Error("failed to react", "err", err)
70 return
71 }
72
73 reactionMap, err := db.GetReactionMap(s.db, 20, subjectUri)
74 if err != nil {
75 l.Error("failed to get reactions", "subjectUri", subjectUri, "err", err)
76 }
77
78 l.Info("created atproto record", "uri", resp.Uri)
79
80 s.pages.ThreadReactionFragment(w, pages.ThreadReactionFragmentParams{
81 ThreadAt: subjectUri,
82 Kind: reactionKind,
83 Count: reactionMap[reactionKind].Count,
84 Users: reactionMap[reactionKind].Users,
85 IsReacted: true,
86 })
87
88 return
89 case http.MethodDelete:
90 reaction, err := db.GetReaction(s.db, currentUser.Active.Did, subjectUri, reactionKind)
91 if err != nil {
92 l.Error("failed to get reaction relationship", "did", currentUser.Active.Did, "subjectUri", subjectUri, "err", err)
93 return
94 }
95
96 _, err = comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{
97 Collection: tangled.FeedReactionNSID,
98 Repo: currentUser.Active.Did,
99 Rkey: reaction.Rkey,
100 })
101
102 if err != nil {
103 l.Error("failed to remove reaction", "err", err)
104 return
105 }
106
107 err = db.DeleteReactionByRkey(s.db, currentUser.Active.Did, reaction.Rkey)
108 if err != nil {
109 l.Warn("failed to delete reaction from DB", "err", err)
110 // this is not an issue, the firehose event might have already done this
111 }
112
113 reactionMap, err := db.GetReactionMap(s.db, 20, subjectUri)
114 if err != nil {
115 l.Error("failed to get reactions", "subjectUri", subjectUri, "err", err)
116 return
117 }
118
119 s.pages.ThreadReactionFragment(w, pages.ThreadReactionFragmentParams{
120 ThreadAt: subjectUri,
121 Kind: reactionKind,
122 Count: reactionMap[reactionKind].Count,
123 Users: reactionMap[reactionKind].Users,
124 IsReacted: false,
125 })
126
127 return
128 }
129}