summaryrefslogtreecommitdiff
path: root/internal/processing
diff options
context:
space:
mode:
Diffstat (limited to 'internal/processing')
-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
5 files changed, 169 insertions, 14 deletions
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 {