From bcdfaa1bf1e1cd884e1b41375fedfa9084c4557d Mon Sep 17 00:00:00 2001 From: Lewis Date: Fri, 29 May 2026 12:36:48 +0300 Subject: [PATCH] appview: ingest reactions Lewis: May this revision serve well! --- appview/db/reaction.go | 6 ++-- appview/ingester.go | 57 ++++++++++++++++++++++++++++++++++++++ appview/models/reaction.go | 11 ++++++++ appview/state/reaction.go | 12 +++----- appview/state/state.go | 1 + 5 files changed, 76 insertions(+), 11 deletions(-) diff --git a/appview/db/reaction.go b/appview/db/reaction.go index 50645584..c7f584c4 100644 --- a/appview/db/reaction.go +++ b/appview/db/reaction.go @@ -10,9 +10,9 @@ import ( "tangled.org/core/orm" ) -func AddReaction(e Execer, reactedByDid string, threadAt syntax.ATURI, kind models.ReactionKind, rkey string) error { - query := `insert or ignore into reactions (reacted_by_did, thread_at, kind, rkey) values (?, ?, ?, ?)` - _, err := e.Exec(query, reactedByDid, threadAt, kind, rkey) +func AddReaction(e Execer, reactedByDid string, threadAt syntax.ATURI, kind models.ReactionKind, rkey string, created time.Time) error { + query := `insert or ignore into reactions (reacted_by_did, thread_at, kind, rkey, created) values (?, ?, ?, ?, ?)` + _, err := e.Exec(query, reactedByDid, threadAt, kind, rkey, created.UTC().Format(time.RFC3339)) return err } diff --git a/appview/ingester.go b/appview/ingester.go index 11f20b83..6c0ad056 100644 --- a/appview/ingester.go +++ b/appview/ingester.go @@ -79,6 +79,8 @@ func (i *Ingester) Ingest() processFunc { err = i.ingestVouch(ctx, e) case tangled.FeedStarNSID: err = i.ingestStar(ctx, e) + case tangled.FeedReactionNSID: + err = i.ingestReaction(e) case tangled.PublicKeyNSID: err = i.ingestPublicKey(e) case tangled.RepoArtifactNSID: @@ -1662,6 +1664,61 @@ func (i *Ingester) ingestComment(e *jmodels.Event) error { return nil } +func (i *Ingester) ingestReaction(e *jmodels.Event) error { + did := e.Did + rkey := e.Commit.RKey + + l := i.Logger.With("handler", "ingestReaction", "nsid", e.Commit.Collection, "did", did, "rkey", rkey) + l.Info("ingesting record") + + switch e.Commit.Operation { + case jmodels.CommitOperationCreate, jmodels.CommitOperationUpdate: + raw := json.RawMessage(e.Commit.Record) + record := tangled.FeedReaction{} + if err := json.Unmarshal(raw, &record); err != nil { + return fmt.Errorf("invalid record: %w", err) + } + + subjectUri, err := syntax.ParseATURI(record.Subject) + if err != nil { + return fmt.Errorf("invalid reaction subject %q: %w", record.Subject, err) + } + subjectUri = models.NormalizeReactionSubject(subjectUri) + + kind, ok := models.ParseReactionKind(record.Reaction) + if !ok { + return fmt.Errorf("invalid reaction kind: %q", record.Reaction) + } + + created, parseErr := time.Parse(time.RFC3339, record.CreatedAt) + if parseErr != nil { + created = time.Now() + } + + tx, err := i.Db.Begin() + if err != nil { + return fmt.Errorf("failed to start transaction: %w", err) + } + defer tx.Rollback() + + if err := db.DeleteReactionByRkey(tx, did, rkey); err != nil { + return fmt.Errorf("failed to clear existing reaction: %w", err) + } + if err := db.AddReaction(tx, did, subjectUri, kind, rkey, created); err != nil { + return fmt.Errorf("failed to add reaction: %w", err) + } + + return tx.Commit() + + case jmodels.CommitOperationDelete: + if err := db.DeleteReactionByRkey(i.Db, did, rkey); err != nil { + return fmt.Errorf("failed to delete reaction record: %w", err) + } + } + + return nil +} + func (i *Ingester) ingestLabelDefinition(e *jmodels.Event) error { did := e.Did rkey := e.Commit.RKey diff --git a/appview/models/reaction.go b/appview/models/reaction.go index 3cb21acf..d24845bd 100644 --- a/appview/models/reaction.go +++ b/appview/models/reaction.go @@ -1,9 +1,12 @@ package models import ( + "fmt" "time" "github.com/bluesky-social/indigo/atproto/syntax" + + "tangled.org/core/api/tangled" ) type ReactionKind string @@ -60,3 +63,11 @@ type ReactionDisplayData struct { Count int Users []string } + +func NormalizeReactionSubject(subject syntax.ATURI) syntax.ATURI { + switch subject.Collection() { + case tangled.RepoIssueCommentNSID, tangled.RepoPullCommentNSID: + return syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", subject.Authority(), tangled.FeedCommentNSID, subject.RecordKey())) + } + return subject +} diff --git a/appview/state/reaction.go b/appview/state/reaction.go index b5c4cfce..ba1a5238 100644 --- a/appview/state/reaction.go +++ b/appview/state/reaction.go @@ -1,7 +1,6 @@ package state import ( - "fmt" "net/http" "time" @@ -33,10 +32,7 @@ func (s *State) React(w http.ResponseWriter, r *http.Request) { } // override collection NSID to new one - switch subjectUri.Collection() { - case tangled.RepoIssueCommentNSID, tangled.RepoPullCommentNSID: - subjectUri = syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", subjectUri.Authority(), tangled.FeedCommentNSID, subjectUri.RecordKey())) - } + subjectUri = models.NormalizeReactionSubject(subjectUri) reactionKind, ok := models.ParseReactionKind(r.URL.Query().Get("kind")) if !ok { @@ -52,7 +48,7 @@ func (s *State) React(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodPost: - createdAt := time.Now().Format(time.RFC3339) + createdAt := time.Now() rkey := tid.TID() resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ Collection: tangled.FeedReactionNSID, @@ -62,7 +58,7 @@ func (s *State) React(w http.ResponseWriter, r *http.Request) { Val: &tangled.FeedReaction{ Subject: subjectUri.String(), Reaction: reactionKind.String(), - CreatedAt: createdAt, + CreatedAt: createdAt.Format(time.RFC3339), }, }, }) @@ -71,7 +67,7 @@ func (s *State) React(w http.ResponseWriter, r *http.Request) { return } - err = db.AddReaction(s.db, currentUser.Did, subjectUri, reactionKind, rkey) + err = db.AddReaction(s.db, currentUser.Did, subjectUri, reactionKind, rkey, createdAt) if err != nil { l.Error("failed to react", "err", err) return diff --git a/appview/state/state.go b/appview/state/state.go index 941aef1b..b9da5ea2 100644 --- a/appview/state/state.go +++ b/appview/state/state.go @@ -127,6 +127,7 @@ func Make(ctx context.Context, config *config.Config) (*State, error) { []string{ tangled.ActorProfileNSID, tangled.FeedStarNSID, + tangled.FeedReactionNSID, tangled.FeedCommentNSID, tangled.GraphFollowNSID, tangled.GraphVouchNSID, -- 2.51.2