summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
Diffstat (limited to 'internal')
-rw-r--r--internal/cache/db.go2
-rw-r--r--internal/cache/size.go2
-rw-r--r--internal/db/bundb/notification.go49
-rw-r--r--internal/db/bundb/notification_test.go4
-rw-r--r--internal/db/bundb/status.go102
-rw-r--r--internal/db/notification.go10
-rw-r--r--internal/db/status.go8
-rw-r--r--internal/gtsmodel/interaction.go7
-rw-r--r--internal/gtsmodel/notification.go4
-rw-r--r--internal/gtsmodel/status.go7
-rw-r--r--internal/gtsmodel/statusfave.go7
-rw-r--r--internal/processing/workers/fromclientapi.go8
-rw-r--r--internal/processing/workers/fromclientapi_test.go90
-rw-r--r--internal/processing/workers/fromfediapi.go8
-rw-r--r--internal/processing/workers/fromfediapi_test.go8
-rw-r--r--internal/processing/workers/surfacenotify.go69
-rw-r--r--internal/typeutils/internaltofrontend.go63
-rw-r--r--internal/webpush/realsender_test.go2
18 files changed, 372 insertions, 78 deletions
diff --git a/internal/cache/db.go b/internal/cache/db.go
index ac16e26c5..78cc01e06 100644
--- a/internal/cache/db.go
+++ b/internal/cache/db.go
@@ -1155,7 +1155,7 @@ func (c *Caches) initNotification() {
c.DB.Notification.Init(structr.CacheConfig[*gtsmodel.Notification]{
Indices: []structr.IndexConfig{
{Fields: "ID"},
- {Fields: "NotificationType,TargetAccountID,OriginAccountID,StatusID", AllowZero: true},
+ {Fields: "NotificationType,TargetAccountID,OriginAccountID,StatusOrEditID", AllowZero: true},
},
MaxSize: cap,
IgnoreErr: ignoreErrors,
diff --git a/internal/cache/size.go b/internal/cache/size.go
index b08a62a85..7898f9dfd 100644
--- a/internal/cache/size.go
+++ b/internal/cache/size.go
@@ -573,7 +573,7 @@ func sizeofNotification() uintptr {
CreatedAt: exampleTime,
TargetAccountID: exampleID,
OriginAccountID: exampleID,
- StatusID: exampleID,
+ StatusOrEditID: exampleID,
Read: func() *bool { ok := false; return &ok }(),
}))
}
diff --git a/internal/db/bundb/notification.go b/internal/db/bundb/notification.go
index 77d4861b2..2f4989c33 100644
--- a/internal/db/bundb/notification.go
+++ b/internal/db/bundb/notification.go
@@ -54,24 +54,28 @@ func (n *notificationDB) GetNotificationByID(ctx context.Context, id string) (*g
func (n *notificationDB) GetNotification(
ctx context.Context,
- notificationType gtsmodel.NotificationType,
- targetAccountID string,
- originAccountID string,
- statusID string,
+ notifType gtsmodel.NotificationType,
+ targetAcctID string,
+ originAcctID string,
+ statusOrEditID string,
) (*gtsmodel.Notification, error) {
return n.getNotification(
ctx,
- "NotificationType,TargetAccountID,OriginAccountID,StatusID",
+ "NotificationType,TargetAccountID,OriginAccountID,StatusOrEditID",
func(notif *gtsmodel.Notification) error {
- return n.db.NewSelect().
+ q := n.db.NewSelect().
Model(notif).
- Where("? = ?", bun.Ident("notification_type"), notificationType).
- Where("? = ?", bun.Ident("target_account_id"), targetAccountID).
- Where("? = ?", bun.Ident("origin_account_id"), originAccountID).
- Where("? = ?", bun.Ident("status_id"), statusID).
- Scan(ctx)
+ Where("? = ?", bun.Ident("notification_type"), notifType).
+ Where("? = ?", bun.Ident("target_account_id"), targetAcctID).
+ Where("? = ?", bun.Ident("origin_account_id"), originAcctID)
+
+ if statusOrEditID != "" {
+ q = q.Where("? = ?", bun.Ident("status_id"), statusOrEditID)
+ }
+
+ return q.Scan(ctx)
},
- notificationType, targetAccountID, originAccountID, statusID,
+ notifType, targetAcctID, originAcctID, statusOrEditID,
)
}
@@ -176,14 +180,29 @@ func (n *notificationDB) PopulateNotification(ctx context.Context, notif *gtsmod
}
}
- if notif.StatusID != "" && notif.Status == nil {
+ if notif.StatusOrEditID != "" && notif.Status == nil {
+ // Try getting status by ID first.
notif.Status, err = n.state.DB.GetStatusByID(
gtscontext.SetBarebones(ctx),
- notif.StatusID,
+ notif.StatusOrEditID,
)
- if err != nil {
+ if err != nil && !errors.Is(err, db.ErrNoEntries) {
+ // Only append real db error. It might be an edit ID.
errs.Appendf("error populating notif status: %w", err)
}
+
+ if notif.Status == nil {
+ // If it's still not set, try
+ // getting status by edit ID.
+ notif.Status, err = n.state.DB.GetStatusByEditID(
+ gtscontext.SetBarebones(ctx),
+ notif.StatusOrEditID,
+ )
+ if err != nil {
+ // Append any error here as it's an issue.
+ errs.Appendf("error populating notif status: %w", err)
+ }
+ }
}
return errs.Combine()
diff --git a/internal/db/bundb/notification_test.go b/internal/db/bundb/notification_test.go
index a6dcdd407..b3104f905 100644
--- a/internal/db/bundb/notification_test.go
+++ b/internal/db/bundb/notification_test.go
@@ -70,7 +70,7 @@ func (suite *NotificationTestSuite) spamNotifs() {
CreatedAt: time.Now(),
TargetAccountID: targetAccountID,
OriginAccountID: originAccountID,
- StatusID: statusID,
+ StatusOrEditID: statusID,
Read: util.Ptr(false),
}
@@ -263,7 +263,7 @@ func (suite *NotificationTestSuite) TestDeleteNotificationsPertainingToStatusID(
}
for _, n := range notif {
- if n.StatusID == testStatus.ID {
+ if n.StatusOrEditID == testStatus.ID {
suite.FailNowf("", "no notifications with status id %s should remain", testStatus.ID)
}
}
diff --git a/internal/db/bundb/status.go b/internal/db/bundb/status.go
index f33362a3d..cf4a2549a 100644
--- a/internal/db/bundb/status.go
+++ b/internal/db/bundb/status.go
@@ -732,3 +732,105 @@ func (s *statusDB) GetDirectStatusIDsBatch(ctx context.Context, minID string, ma
}
return statusIDs, nil
}
+
+func (s *statusDB) GetStatusInteractions(
+ ctx context.Context,
+ statusID string,
+ localOnly bool,
+) ([]gtsmodel.Interaction, error) {
+ // Prepare to get interactions.
+ interactions := []gtsmodel.Interaction{}
+
+ // Gather faves.
+ faves, err := s.state.DB.GetStatusFaves(ctx, statusID)
+ if err != nil && !errors.Is(err, db.ErrNoEntries) {
+ return nil, err
+ }
+
+ for _, fave := range faves {
+ // Get account at least.
+ if fave.Account == nil {
+ fave.Account, err = s.state.DB.GetAccountByID(ctx, fave.AccountID)
+ if err != nil {
+ log.Errorf(ctx, "error getting account for fave: %v", err)
+ continue
+ }
+ }
+
+ if localOnly && !fave.Account.IsLocal() {
+ // Skip not local.
+ continue
+ }
+
+ interactions = append(interactions, fave)
+ }
+
+ // Gather replies.
+ replies, err := s.state.DB.GetStatusReplies(ctx, statusID)
+ if err != nil && !errors.Is(err, db.ErrNoEntries) {
+ return nil, err
+ }
+
+ for _, reply := range replies {
+ // Get account at least.
+ if reply.Account == nil {
+ reply.Account, err = s.state.DB.GetAccountByID(ctx, reply.AccountID)
+ if err != nil {
+ log.Errorf(ctx, "error getting account for reply: %v", err)
+ continue
+ }
+ }
+
+ if localOnly && !reply.Account.IsLocal() {
+ // Skip not local.
+ continue
+ }
+
+ interactions = append(interactions, reply)
+ }
+
+ // Gather boosts.
+ boosts, err := s.state.DB.GetStatusBoosts(ctx, statusID)
+ if err != nil && !errors.Is(err, db.ErrNoEntries) {
+ return nil, err
+ }
+
+ for _, boost := range boosts {
+ // Get account at least.
+ if boost.Account == nil {
+ boost.Account, err = s.state.DB.GetAccountByID(ctx, boost.AccountID)
+ if err != nil {
+ log.Errorf(ctx, "error getting account for boost: %v", err)
+ continue
+ }
+ }
+
+ if localOnly && !boost.Account.IsLocal() {
+ // Skip not local.
+ continue
+ }
+
+ interactions = append(interactions, boost)
+ }
+
+ if len(interactions) == 0 {
+ return nil, db.ErrNoEntries
+ }
+
+ return interactions, nil
+}
+
+func (s *statusDB) GetStatusByEditID(
+ ctx context.Context,
+ editID string,
+) (*gtsmodel.Status, error) {
+ edit, err := s.state.DB.GetStatusEditByID(
+ gtscontext.SetBarebones(ctx),
+ editID,
+ )
+ if err != nil {
+ return nil, err
+ }
+
+ return s.GetStatusByID(ctx, edit.StatusID)
+}
diff --git a/internal/db/notification.go b/internal/db/notification.go
index 029a3bc62..4c282d279 100644
--- a/internal/db/notification.go
+++ b/internal/db/notification.go
@@ -39,8 +39,14 @@ type Notification interface {
GetNotificationsByIDs(ctx context.Context, ids []string) ([]*gtsmodel.Notification, error)
// GetNotification gets one notification according to the provided parameters, if it exists.
- // Since not all notifications are about a status, statusID can be an empty string.
- GetNotification(ctx context.Context, notificationType gtsmodel.NotificationType, targetAccountID string, originAccountID string, statusID string) (*gtsmodel.Notification, error)
+ // Since not all notifications are about a status or an edit, statusOrEditID can be empty.
+ GetNotification(
+ ctx context.Context,
+ notifType gtsmodel.NotificationType,
+ targetAcctID string,
+ originAcctID string,
+ statusOrEditID string,
+ ) (*gtsmodel.Notification, error)
// PopulateNotification ensures that the notification's struct fields are populated.
PopulateNotification(ctx context.Context, notif *gtsmodel.Notification) error
diff --git a/internal/db/status.go b/internal/db/status.go
index adb31d391..d1bdb6106 100644
--- a/internal/db/status.go
+++ b/internal/db/status.go
@@ -94,4 +94,12 @@ type Status interface {
// MaxDirectStatusID, and expects to eventually return the status with that ID.
// It is used only by the conversation advanced migration.
GetDirectStatusIDsBatch(ctx context.Context, minID string, maxIDInclusive string, count int) ([]string, error)
+
+ // GetStatusInteractions gets all abstract "interactions" of a status (likes, replies, boosts).
+ // If localOnly is true, will return only interactions performed by accounts on this instance.
+ // Aside from that, interactions are not filtered or deduplicated, it's up to the caller to do that.
+ GetStatusInteractions(ctx context.Context, statusID string, localOnly bool) ([]gtsmodel.Interaction, error)
+
+ // GetStatusByEditID gets one status corresponding to the given edit ID.
+ GetStatusByEditID(ctx context.Context, editID string) (*gtsmodel.Status, error)
}
diff --git a/internal/gtsmodel/interaction.go b/internal/gtsmodel/interaction.go
index 92dd1a4e0..0b9ee693e 100644
--- a/internal/gtsmodel/interaction.go
+++ b/internal/gtsmodel/interaction.go
@@ -95,3 +95,10 @@ func (ir *InteractionRequest) IsAccepted() bool {
func (ir *InteractionRequest) IsRejected() bool {
return !ir.RejectedAt.IsZero()
}
+
+// Interaction abstractly represents
+// one interaction with a status, via
+// liking, replying to, or boosting it.
+type Interaction interface {
+ GetAccount() *Account
+}
diff --git a/internal/gtsmodel/notification.go b/internal/gtsmodel/notification.go
index d160e0883..cc892bdd5 100644
--- a/internal/gtsmodel/notification.go
+++ b/internal/gtsmodel/notification.go
@@ -32,8 +32,8 @@ type Notification struct {
TargetAccount *Account `bun:"-"` // Account corresponding to TargetAccountID. Can be nil, always check first + select using ID if necessary.
OriginAccountID string `bun:"type:CHAR(26),nullzero,notnull"` // ID of the account that performed the action that created the notification.
OriginAccount *Account `bun:"-"` // Account corresponding to OriginAccountID. Can be nil, always check first + select using ID if necessary.
- StatusID string `bun:"type:CHAR(26),nullzero"` // If the notification pertains to a status, what is the database ID of that status?
- Status *Status `bun:"-"` // Status corresponding to StatusID. Can be nil, always check first + select using ID if necessary.
+ StatusOrEditID string `bun:"status_id,type:CHAR(26),nullzero"` // If the notification pertains to a status or a status edit event, what is the database ID of the status or status edit?
+ Status *Status `bun:"-"` // Status corresponding to StatusOrEditID. Can be nil, always check first + select using ID if necessary.
Read *bool `bun:",nullzero,notnull,default:false"` // Notification has been seen/read
}
diff --git a/internal/gtsmodel/status.go b/internal/gtsmodel/status.go
index eca50416e..884caac0c 100644
--- a/internal/gtsmodel/status.go
+++ b/internal/gtsmodel/status.go
@@ -89,6 +89,13 @@ func (s *Status) GetAccountID() string {
return s.AccountID
}
+// GetAccount returns the account that owns
+// this status. May be nil if status not populated.
+// Fulfils Interaction interface.
+func (s *Status) GetAccount() *Account {
+ return s.Account
+}
+
// GetBoostOfID implements timeline.Timelineable{}.
func (s *Status) GetBoostOfID() string {
return s.BoostOfID
diff --git a/internal/gtsmodel/statusfave.go b/internal/gtsmodel/statusfave.go
index 9d6c6335b..6fe4ed821 100644
--- a/internal/gtsmodel/statusfave.go
+++ b/internal/gtsmodel/statusfave.go
@@ -35,3 +35,10 @@ type StatusFave struct {
PreApproved bool `bun:"-"` // If true, then fave targets a status on our instance, has permission to do the interaction, and an Accept should be sent out for it immediately. Field not stored in the DB.
ApprovedByURI string `bun:",nullzero"` // URI of an Accept Activity that approves this Like.
}
+
+// GetAccount returns the account that owns
+// this fave. May be nil if fave not populated.
+// Fulfils Interaction interface.
+func (f *StatusFave) GetAccount() *Account {
+ return f.Account
+}
diff --git a/internal/processing/workers/fromclientapi.go b/internal/processing/workers/fromclientapi.go
index dbbeff220..5d9ebf41a 100644
--- a/internal/processing/workers/fromclientapi.go
+++ b/internal/processing/workers/fromclientapi.go
@@ -748,6 +748,14 @@ func (p *clientAPI) UpdateStatus(ctx context.Context, cMsg *messages.FromClientA
}
}
+ // Notify of the latest edit.
+ if editsLen := len(status.EditIDs); editsLen != 0 {
+ editID := status.EditIDs[editsLen-1]
+ if err := p.surface.notifyStatusEdit(ctx, status, editID); err != nil {
+ log.Errorf(ctx, "error notifying status edit: %v", err)
+ }
+ }
+
// Push message that the status has been edited to streams.
if err := p.surface.timelineStatusUpdate(ctx, status); err != nil {
log.Errorf(ctx, "error streaming status edit: %v", err)
diff --git a/internal/processing/workers/fromclientapi_test.go b/internal/processing/workers/fromclientapi_test.go
index c643e0c70..a1027f3e0 100644
--- a/internal/processing/workers/fromclientapi_test.go
+++ b/internal/processing/workers/fromclientapi_test.go
@@ -2149,6 +2149,96 @@ func (suite *FromClientAPITestSuite) TestProcessUpdateStatusWithFollowedHashtag(
suite.checkNotWebPushed(testStructs.WebPushSender, receivingAccount.ID)
}
+// Test that when someone edits a status that's been interacted with,
+// the interacter gets a notification that the status has been edited.
+func (suite *FromClientAPITestSuite) TestProcessUpdateStatusInteractedWith() {
+ testStructs := testrig.SetupTestStructs(rMediaPath, rTemplatePath)
+ defer testrig.TearDownTestStructs(testStructs)
+
+ var (
+ ctx = context.Background()
+ postingAccount = suite.testAccounts["local_account_1"]
+ receivingAccount = suite.testAccounts["admin_account"]
+ streams = suite.openStreams(ctx,
+ testStructs.Processor,
+ receivingAccount,
+ nil,
+ )
+ notifStream = streams[stream.TimelineNotifications]
+ )
+
+ // Copy the test status.
+ //
+ // This is one that the receiving account
+ // has interacted with (by replying).
+ testStatus := new(gtsmodel.Status)
+ *testStatus = *suite.testStatuses["local_account_1_status_1"]
+
+ // Create + store an edit.
+ edit := &gtsmodel.StatusEdit{
+ // Just set the ID + status ID, other
+ // fields don't matter for this test.
+ ID: "01JTR74W15VS6A6MK15N5JVJ55",
+ StatusID: testStatus.ID,
+ }
+
+ if err := testStructs.State.DB.PutStatusEdit(ctx, edit); err != nil {
+ suite.FailNow(err.Error())
+ }
+
+ // Set edit on status as
+ // it would be for real.
+ testStatus.EditIDs = []string{edit.ID}
+ testStatus.Edits = []*gtsmodel.StatusEdit{edit}
+
+ // Update the status.
+ if err := testStructs.Processor.Workers().ProcessFromClientAPI(
+ ctx,
+ &messages.FromClientAPI{
+ APObjectType: ap.ObjectNote,
+ APActivityType: ap.ActivityUpdate,
+ GTSModel: testStatus,
+ Origin: postingAccount,
+ },
+ ); err != nil {
+ suite.FailNow(err.Error())
+ }
+
+ // Wait for a notification to appear for the status.
+ var notif *gtsmodel.Notification
+ if !testrig.WaitFor(func() bool {
+ var err error
+ notif, err = testStructs.State.DB.GetNotification(
+ ctx,
+ gtsmodel.NotificationUpdate,
+ receivingAccount.ID,
+ postingAccount.ID,
+ edit.ID,
+ )
+ return err == nil
+ }) {
+ suite.FailNow("timed out waiting for edited status notification")
+ }
+
+ apiNotif, err := testStructs.TypeConverter.NotificationToAPINotification(ctx, notif, nil, nil)
+ if err != nil {
+ suite.FailNow(err.Error())
+ }
+
+ notifJSON, err := json.Marshal(apiNotif)
+ if err != nil {
+ suite.FailNow(err.Error())
+ }
+
+ // Check notif in stream.
+ suite.checkStreamed(
+ notifStream,
+ true,
+ string(notifJSON),
+ stream.EventTypeNotification,
+ )
+}
+
func (suite *FromClientAPITestSuite) TestProcessStatusDelete() {
testStructs := testrig.SetupTestStructs(rMediaPath, rTemplatePath)
defer testrig.TearDownTestStructs(testStructs)
diff --git a/internal/processing/workers/fromfediapi.go b/internal/processing/workers/fromfediapi.go
index 86d868530..d1e5bb2f7 100644
--- a/internal/processing/workers/fromfediapi.go
+++ b/internal/processing/workers/fromfediapi.go
@@ -1010,6 +1010,14 @@ func (p *fediAPI) UpdateStatus(ctx context.Context, fMsg *messages.FromFediAPI)
}
}
+ // Notify of the latest edit.
+ if editsLen := len(status.EditIDs); editsLen != 0 {
+ editID := status.EditIDs[editsLen-1]
+ if err := p.surface.notifyStatusEdit(ctx, status, editID); err != nil {
+ log.Errorf(ctx, "error notifying status edit: %v", err)
+ }
+ }
+
// Push message that the status has been edited to streams.
if err := p.surface.timelineStatusUpdate(ctx, status); err != nil {
log.Errorf(ctx, "error streaming status edit: %v", err)
diff --git a/internal/processing/workers/fromfediapi_test.go b/internal/processing/workers/fromfediapi_test.go
index 203863e12..d197f4122 100644
--- a/internal/processing/workers/fromfediapi_test.go
+++ b/internal/processing/workers/fromfediapi_test.go
@@ -102,7 +102,7 @@ func (suite *FromFediAPITestSuite) TestProcessFederationAnnounce() {
suite.Equal(gtsmodel.NotificationReblog, notif.NotificationType)
suite.Equal(boostedStatus.AccountID, notif.TargetAccountID)
suite.Equal(announceStatus.AccountID, notif.OriginAccountID)
- suite.Equal(announceStatus.ID, notif.StatusID)
+ suite.Equal(announceStatus.ID, notif.StatusOrEditID)
suite.False(*notif.Read)
}
@@ -173,7 +173,7 @@ func (suite *FromFediAPITestSuite) TestProcessReplyMention() {
suite.Equal(gtsmodel.NotificationMention, notif.NotificationType)
suite.Equal(replyingStatus.InReplyToAccountID, notif.TargetAccountID)
suite.Equal(replyingStatus.AccountID, notif.OriginAccountID)
- suite.Equal(replyingStatus.ID, notif.StatusID)
+ suite.Equal(replyingStatus.ID, notif.StatusOrEditID)
suite.False(*notif.Read)
ctx, _ := context.WithTimeout(context.Background(), time.Second*5)
@@ -245,7 +245,7 @@ func (suite *FromFediAPITestSuite) TestProcessFave() {
suite.Equal(gtsmodel.NotificationFavourite, notif.NotificationType)
suite.Equal(fave.TargetAccountID, notif.TargetAccountID)
suite.Equal(fave.AccountID, notif.OriginAccountID)
- suite.Equal(fave.StatusID, notif.StatusID)
+ suite.Equal(fave.StatusID, notif.StatusOrEditID)
suite.False(*notif.Read)
ctx, _ := context.WithTimeout(context.Background(), time.Second*5)
@@ -318,7 +318,7 @@ func (suite *FromFediAPITestSuite) TestProcessFaveWithDifferentReceivingAccount(
suite.Equal(gtsmodel.NotificationFavourite, notif.NotificationType)
suite.Equal(fave.TargetAccountID, notif.TargetAccountID)
suite.Equal(fave.AccountID, notif.OriginAccountID)
- suite.Equal(fave.StatusID, notif.StatusID)
+ suite.Equal(fave.StatusID, notif.StatusOrEditID)
suite.False(*notif.Read)
// 2. no notification should be streamed to the account that received the fave message, because they weren't the target
diff --git a/internal/processing/workers/surfacenotify.go b/internal/processing/workers/surfacenotify.go
index 8e034c23a..11c3fd059 100644
--- a/internal/processing/workers/surfacenotify.go
+++ b/internal/processing/workers/surfacenotify.go
@@ -30,6 +30,7 @@ import (
"code.superseriousbusiness.org/gotosocial/internal/gtsmodel"
"code.superseriousbusiness.org/gotosocial/internal/id"
"code.superseriousbusiness.org/gotosocial/internal/util"
+ "code.superseriousbusiness.org/gotosocial/internal/util/xslices"
)
// notifyPendingReply notifies the account replied-to
@@ -555,19 +556,67 @@ func (s *Surface) notifySignup(ctx context.Context, newUser *gtsmodel.User) erro
return errs.Combine()
}
+func (s *Surface) notifyStatusEdit(
+ ctx context.Context,
+ status *gtsmodel.Status,
+ editID string,
+) error {
+ // Get local-only interactions (we can't/don't notify remotes).
+ interactions, err := s.State.DB.GetStatusInteractions(ctx, status.ID, true)
+ if err != nil && !errors.Is(err, db.ErrNoEntries) {
+ return gtserror.Newf("db error getting status interactions: %w", err)
+ }
+
+ // Deduplicate interactions by account ID,
+ // we don't need to notify someone twice
+ // if they've both boosted *and* replied
+ // to an edited status, for example.
+ interactions = xslices.DeduplicateFunc(
+ interactions,
+ func(v gtsmodel.Interaction) string {
+ return v.GetAccount().ID
+ },
+ )
+
+ // Notify each account that's
+ // interacted with the status.
+ var errs gtserror.MultiError
+ for _, i := range interactions {
+ targetAcct := i.GetAccount()
+ if targetAcct.ID == status.AccountID {
+ // Don't notify an account
+ // if they've interacted
+ // with their *own* status.
+ continue
+ }
+
+ if err := s.Notify(ctx,
+ gtsmodel.NotificationUpdate,
+ targetAcct,
+ status.Account,
+ editID,
+ ); err != nil {
+ errs.Appendf("error notifying status edit: %w", err)
+ continue
+ }
+ }
+
+ return errs.Combine()
+}
+
func getNotifyLockURI(
notificationType gtsmodel.NotificationType,
targetAccount *gtsmodel.Account,
originAccount *gtsmodel.Account,
- statusID string,
+ statusOrEditID string,
) string {
builder := strings.Builder{}
builder.WriteString("notification:?")
builder.WriteString("type=" + notificationType.String())
- builder.WriteString("&target=" + targetAccount.URI)
- builder.WriteString("&origin=" + originAccount.URI)
- if statusID != "" {
- builder.WriteString("&statusID=" + statusID)
+ builder.WriteString("&targetAcct=" + targetAccount.URI)
+ builder.WriteString("&originAcct=" + originAccount.URI)
+ if statusOrEditID != "" {
+ builder.WriteString("&statusOrEditID=" + statusOrEditID)
}
return builder.String()
}
@@ -582,13 +631,13 @@ func getNotifyLockURI(
// for non-local first.
//
// targetAccount and originAccount must be
-// set, but statusID can be an empty string.
+// set, but statusOrEditID can be empty.
func (s *Surface) Notify(
ctx context.Context,
notificationType gtsmodel.NotificationType,
targetAccount *gtsmodel.Account,
originAccount *gtsmodel.Account,
- statusID string,
+ statusOrEditID string,
) error {
if targetAccount.IsRemote() {
// nothing to do.
@@ -601,7 +650,7 @@ func (s *Surface) Notify(
notificationType,
targetAccount,
originAccount,
- statusID,
+ statusOrEditID,
)
unlock := s.State.ProcessingLocks.Lock(lockURI)
@@ -617,7 +666,7 @@ func (s *Surface) Notify(
notificationType,
targetAccount.ID,
originAccount.ID,
- statusID,
+ statusOrEditID,
); err == nil {
// Notification exists;
// nothing to do.
@@ -636,7 +685,7 @@ func (s *Surface) Notify(
TargetAccount: targetAccount,
OriginAccountID: originAccount.ID,
OriginAccount: originAccount,
- StatusID: statusID,
+ StatusOrEditID: statusOrEditID,
}
if err := s.State.DB.PutNotification(ctx, notif); err != nil {
diff --git a/internal/typeutils/internaltofrontend.go b/internal/typeutils/internaltofrontend.go
index ffd971040..a22e504c0 100644
--- a/internal/typeutils/internaltofrontend.go
+++ b/internal/typeutils/internaltofrontend.go
@@ -2055,60 +2055,43 @@ func (c *Converter) NotificationToAPINotification(
filters []*gtsmodel.Filter,
mutes *usermute.CompiledUserMuteList,
) (*apimodel.Notification, error) {
- if n.TargetAccount == nil {
- tAccount, err := c.state.DB.GetAccountByID(ctx, n.TargetAccountID)
- if err != nil {
- return nil, fmt.Errorf("NotificationToapi: error getting target account with id %s from the db: %s", n.TargetAccountID, err)
- }
- n.TargetAccount = tAccount
- }
-
- if n.OriginAccount == nil {
- ogAccount, err := c.state.DB.GetAccountByID(ctx, n.OriginAccountID)
- if err != nil {
- return nil, fmt.Errorf("NotificationToapi: error getting origin account with id %s from the db: %s", n.OriginAccountID, err)
- }
- n.OriginAccount = ogAccount
+ // Ensure notif populated.
+ if err := c.state.DB.PopulateNotification(ctx, n); err != nil {
+ return nil, gtserror.Newf("error populating notification: %w", err)
}
+ // Get account that triggered this notif.
apiAccount, err := c.AccountToAPIAccountPublic(ctx, n.OriginAccount)
if err != nil {
- return nil, fmt.Errorf("NotificationToapi: error converting account to api: %s", err)
+ return nil, gtserror.Newf("error converting account to api: %w", err)
}
+ // Get status that triggered this notif, if set.
var apiStatus *apimodel.Status
- if n.StatusID != "" {
- if n.Status == nil {
- status, err := c.state.DB.GetStatusByID(ctx, n.StatusID)
- if err != nil {
- return nil, fmt.Errorf("NotificationToapi: error getting status with id %s from the db: %s", n.StatusID, err)
- }
- n.Status = status
+ if n.Status != nil {
+ apiStatus, err = c.StatusToAPIStatus(
+ ctx, n.Status,
+ n.TargetAccount,
+ statusfilter.FilterContextNotifications,
+ filters, mutes,
+ )
+ if err != nil && !errors.Is(err, statusfilter.ErrHideStatus) {
+ return nil, gtserror.Newf("error converting status to api: %w", err)
}
- if n.Status.Account == nil {
- if n.Status.AccountID == n.TargetAccount.ID {
- n.Status.Account = n.TargetAccount
- } else if n.Status.AccountID == n.OriginAccount.ID {
- n.Status.Account = n.OriginAccount
- }
+ if apiStatus == nil {
+ // Notif filtered for this
+ // status, nothing to do.
+ return nil, err
}
- var err error
- apiStatus, err = c.StatusToAPIStatus(ctx, n.Status, n.TargetAccount, statusfilter.FilterContextNotifications, filters, mutes)
- if err != nil {
- if errors.Is(err, statusfilter.ErrHideStatus) {
- return nil, err
- }
- return nil, fmt.Errorf("NotificationToapi: error converting status to api: %s", err)
+ if apiStatus.Reblog != nil {
+ // Use the actual reblog status
+ // for the notifications endpoint.
+ apiStatus = apiStatus.Reblog.Status
}
}
- if apiStatus != nil && apiStatus.Reblog != nil {
- // use the actual reblog status for the notifications endpoint
- apiStatus = apiStatus.Reblog.Status
- }
-
return &apimodel.Notification{
ID: n.ID,
Type: n.NotificationType.String(),
diff --git a/internal/webpush/realsender_test.go b/internal/webpush/realsender_test.go
index b0144d4d4..cf9cf4cac 100644
--- a/internal/webpush/realsender_test.go
+++ b/internal/webpush/realsender_test.go
@@ -260,7 +260,7 @@ func (suite *RealSenderStandardTestSuite) TestSendPolicyMismatch() {
NotificationType: gtsmodel.NotificationFavourite,
TargetAccountID: suite.testAccounts["local_account_1"].ID,
OriginAccountID: suite.testAccounts["remote_account_1"].ID,
- StatusID: "01F8MHAMCHF6Y650WCRSCP4WMY",
+ StatusOrEditID: "01F8MHAMCHF6Y650WCRSCP4WMY",
Read: util.Ptr(false),
}
if err := suite.db.PutNotification(context.Background(), notification); !suite.NoError(err) {