A vibe coded tangled fork which supports pijul.
1package state
2
3import (
4 "log"
5 "net/http"
6 "time"
7
8 comatproto "github.com/bluesky-social/indigo/api/atproto"
9 "github.com/bluesky-social/indigo/atproto/syntax"
10 lexutil "github.com/bluesky-social/indigo/lex/util"
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) Star(w http.ResponseWriter, r *http.Request) {
19 currentUser := s.oauth.GetMultiAccountUser(r)
20
21 subject := r.URL.Query().Get("subject")
22 if subject == "" {
23 log.Println("invalid form")
24 return
25 }
26
27 subjectUri, err := syntax.ParseATURI(subject)
28 if err != nil {
29 log.Println("invalid form")
30 return
31 }
32
33 client, err := s.oauth.AuthorizedClient(r)
34 if err != nil {
35 log.Println("failed to authorize client", err)
36 return
37 }
38
39 switch r.Method {
40 case http.MethodPost:
41 star := models.Star{
42 Did: currentUser.Active.Did,
43 Rkey: tid.TID(),
44 RepoAt: subjectUri,
45 Created: time.Now(),
46 }
47
48 tx, err := s.db.BeginTx(r.Context(), nil)
49 if err != nil {
50 s.logger.Error("failed to start transaction", "err", err)
51 return
52 }
53 defer tx.Rollback()
54
55 if err := db.UpsertStar(tx, star); err != nil {
56 s.logger.Error("failed to star", "err", err)
57 return
58 }
59
60 record := star.AsRecord()
61 resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
62 Collection: tangled.FeedStarNSID,
63 Repo: currentUser.Active.Did,
64 Rkey: star.Rkey,
65 Record: &lexutil.LexiconTypeDecoder{
66 Val: &record,
67 },
68 })
69 if err != nil {
70 log.Println("failed to create atproto record", err)
71 return
72 }
73 log.Println("created atproto record: ", resp.Uri)
74
75 if err := tx.Commit(); err != nil {
76 s.logger.Error("failed to commit transaction", "err", err)
77 // DB op failed but record is created in PDS. Ingester will backfill the missed operation
78 }
79
80 s.notifier.NewStar(r.Context(), &star)
81
82 starCount, err := db.GetStarCount(s.db, subjectUri)
83 if err != nil {
84 log.Println("failed to get star count for ", subjectUri)
85 }
86
87 s.pages.StarBtnFragment(w, pages.StarBtnFragmentParams{
88 IsStarred: true,
89 SubjectAt: subjectUri,
90 StarCount: starCount,
91 })
92
93 return
94 case http.MethodDelete:
95 tx, err := s.db.BeginTx(r.Context(), nil)
96 if err != nil {
97 s.logger.Error("failed to start transaction", "err", err)
98 }
99 defer tx.Rollback()
100
101 stars, err := db.DeleteStar(tx, syntax.DID(currentUser.Active.Did), subjectUri)
102 if err != nil {
103 s.logger.Error("failed to delete stars from db", "err", err)
104 return
105 }
106
107 var writes []*comatproto.RepoApplyWrites_Input_Writes_Elem
108 for _, starAt := range stars {
109 writes = append(writes, &comatproto.RepoApplyWrites_Input_Writes_Elem{
110 RepoApplyWrites_Delete: &comatproto.RepoApplyWrites_Delete{
111 Collection: tangled.FeedStarNSID,
112 Rkey: starAt.RecordKey().String(),
113 },
114 })
115 }
116 _, err = comatproto.RepoApplyWrites(r.Context(), client, &comatproto.RepoApplyWrites_Input{
117 Repo: currentUser.Active.Did,
118 Writes: writes,
119 })
120 if err != nil {
121 s.logger.Error("failed to delete stars from PDS", "err", err)
122 return
123 }
124
125 if err := tx.Commit(); err != nil {
126 s.logger.Error("failed to commit transaction", "err", err)
127 // DB op failed but record is created in PDS. Ingester will backfill the missed operation
128 }
129
130 s.notifier.DeleteStar(r.Context(), &models.Star{
131 Did: currentUser.Active.Did,
132 RepoAt: subjectUri,
133 // Rkey
134 // Created
135 })
136
137 starCount, err := db.GetStarCount(s.db, subjectUri)
138 if err != nil {
139 log.Println("failed to get star count for ", subjectUri)
140 return
141 }
142
143 s.pages.StarBtnFragment(w, pages.StarBtnFragmentParams{
144 IsStarred: false,
145 SubjectAt: subjectUri,
146 StarCount: starCount,
147 })
148
149 return
150 }
151
152}