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 createdAt := time.Now().Format(time.RFC3339)
42 rkey := tid.TID()
43 resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
44 Collection: tangled.FeedStarNSID,
45 Repo: currentUser.Active.Did,
46 Rkey: rkey,
47 Record: &lexutil.LexiconTypeDecoder{
48 Val: &tangled.FeedStar{
49 Subject: subjectUri.String(),
50 CreatedAt: createdAt,
51 }},
52 })
53 if err != nil {
54 log.Println("failed to create atproto record", err)
55 return
56 }
57 log.Println("created atproto record: ", resp.Uri)
58
59 star := &models.Star{
60 Did: currentUser.Active.Did,
61 RepoAt: subjectUri,
62 Rkey: rkey,
63 }
64
65 err = db.AddStar(s.db, star)
66 if err != nil {
67 log.Println("failed to star", err)
68 return
69 }
70
71 starCount, err := db.GetStarCount(s.db, subjectUri)
72 if err != nil {
73 log.Println("failed to get star count for ", subjectUri)
74 }
75
76 s.notifier.NewStar(r.Context(), star)
77
78 s.pages.StarBtnFragment(w, pages.StarBtnFragmentParams{
79 IsStarred: true,
80 SubjectAt: subjectUri,
81 StarCount: starCount,
82 })
83
84 return
85 case http.MethodDelete:
86 tx, err := s.db.BeginTx(r.Context(), nil)
87 if err != nil {
88 s.logger.Error("failed to start transaction", "err", err)
89 }
90 defer tx.Rollback()
91
92 stars, err := db.DeleteStar(tx, syntax.DID(currentUser.Active.Did), subjectUri)
93 if err != nil {
94 s.logger.Error("failed to delete stars from db", "err", err)
95 return
96 }
97
98 var writes []*comatproto.RepoApplyWrites_Input_Writes_Elem
99 for _, starAt := range stars {
100 writes = append(writes, &comatproto.RepoApplyWrites_Input_Writes_Elem{
101 RepoApplyWrites_Delete: &comatproto.RepoApplyWrites_Delete{
102 Collection: tangled.FeedStarNSID,
103 Rkey: starAt.RecordKey().String(),
104 },
105 })
106 }
107 _, err = comatproto.RepoApplyWrites(r.Context(), client, &comatproto.RepoApplyWrites_Input{
108 Repo: currentUser.Active.Did,
109 Writes: writes,
110 })
111 if err != nil {
112 s.logger.Error("failed to delete stars from PDS", "err", err)
113 return
114 }
115
116 if err := tx.Commit(); err != nil {
117 s.logger.Error("failed to commit transaction", "err", err)
118 // DB op failed but record is created in PDS. Ingester will backfill the missed operation
119 }
120
121 s.notifier.DeleteStar(r.Context(), &models.Star{
122 Did: currentUser.Active.Did,
123 RepoAt: subjectUri,
124 // Rkey
125 // Created
126 })
127
128 starCount, err := db.GetStarCount(s.db, subjectUri)
129 if err != nil {
130 log.Println("failed to get star count for ", subjectUri)
131 return
132 }
133
134 s.pages.StarBtnFragment(w, pages.StarBtnFragmentParams{
135 IsStarred: false,
136 SubjectAt: subjectUri,
137 StarCount: starCount,
138 })
139
140 return
141 }
142
143}