From be656cd0b72fe860c1b7fe7cf74bfb98d6f20d63 Mon Sep 17 00:00:00 2001 From: Ashley Date: Sun, 18 Oct 2026 21:42:27 +1030 Subject: [PATCH] fix: match legacy morph alias when clearing DM/media-tag notifications on status delete --- .../StatusPipeline/RemoteStatusDelete.php | 4 +- app/Jobs/StatusPipeline/StatusDelete.php | 4 +- .../StatusDeleteDmMediaTagMorphAliasTest.php | 147 ++++++++++++++++++ 3 files changed, 151 insertions(+), 4 deletions(-) create mode 100644 tests/Feature/StatusDeleteDmMediaTagMorphAliasTest.php diff --git a/app/Jobs/StatusPipeline/RemoteStatusDelete.php b/app/Jobs/StatusPipeline/RemoteStatusDelete.php index c25aa9112..efaca95a0 100644 --- a/app/Jobs/StatusPipeline/RemoteStatusDelete.php +++ b/app/Jobs/StatusPipeline/RemoteStatusDelete.php @@ -140,7 +140,7 @@ class RemoteStatusDelete implements ShouldBeUniqueUntilProcessing, ShouldQueue }); $dmIds = DirectMessage::whereStatusId($status->id)->pluck('id'); if ($dmIds->isNotEmpty()) { - Notification::whereItemType(DirectMessage::class) + Notification::whereIn('item_type', ['App\DirectMessage', DirectMessage::class]) ->whereIn('item_id', $dmIds) ->cursor() ->each(function ($not) { @@ -163,7 +163,7 @@ class RemoteStatusDelete implements ShouldBeUniqueUntilProcessing, ShouldQueue }); $mediaTagIds = MediaTag::where('status_id', $status->id)->pluck('id'); if ($mediaTagIds->isNotEmpty()) { - Notification::whereItemType(MediaTag::class) + Notification::whereIn('item_type', ['App\MediaTag', MediaTag::class]) ->whereIn('item_id', $mediaTagIds) ->cursor() ->each(function ($not) { diff --git a/app/Jobs/StatusPipeline/StatusDelete.php b/app/Jobs/StatusPipeline/StatusDelete.php index caffedc52..af983cd68 100644 --- a/app/Jobs/StatusPipeline/StatusDelete.php +++ b/app/Jobs/StatusPipeline/StatusDelete.php @@ -200,7 +200,7 @@ class StatusDelete implements ShouldQueue $dmIds = DirectMessage::whereStatusId($status->id)->pluck('id'); if ($dmIds->isNotEmpty()) { - Notification::whereItemType(DirectMessage::class) + Notification::whereIn('item_type', ['App\DirectMessage', DirectMessage::class]) ->whereIn('item_id', $dmIds) ->cursor() ->each(function ($not) { @@ -214,7 +214,7 @@ class StatusDelete implements ShouldQueue $mediaTagIds = MediaTag::where('status_id', $status->id)->pluck('id'); if ($mediaTagIds->isNotEmpty()) { - Notification::whereItemType(MediaTag::class) + Notification::whereIn('item_type', ['App\MediaTag', MediaTag::class]) ->whereIn('item_id', $mediaTagIds) ->cursor() ->each(function ($not) { diff --git a/tests/Feature/StatusDeleteDmMediaTagMorphAliasTest.php b/tests/Feature/StatusDeleteDmMediaTagMorphAliasTest.php new file mode 100644 index 000000000..b22669d26 --- /dev/null +++ b/tests/Feature/StatusDeleteDmMediaTagMorphAliasTest.php @@ -0,0 +1,147 @@ +to_id = $profileId + $seq; + $dm->from_id = $profileId; + $dm->status_id = $status->id; + $dm->type = 'text'; + $dm->save(); + + $n = new Notification; + $n->profile_id = $profileId; + $n->actor_id = $profileId; + $n->action = 'dm'; + $n->item_id = $dm->id; + $n->item_type = $itemType; + $n->save(); + + return $n; +} + +function makeTagNotification(Status $status, int $profileId, string $itemType, int $seq): Notification +{ + $tag = new MediaTag; + $tag->status_id = $status->id; + $tag->media_id = $seq; + $tag->profile_id = $profileId; + $tag->tagged_username = 'tagged'; + $tag->is_public = 1; + $tag->save(); + + $n = new Notification; + $n->profile_id = $profileId; + $n->actor_id = $profileId; + $n->action = 'tagged'; + $n->item_id = $tag->id; + $n->item_type = $itemType; + $n->save(); + + return $n; +} + +it('deletes both legacy App\\DirectMessage and modern DM notifications on status delete', function () { + config(['federation.activitypub.enabled' => false]); + + $user = User::factory()->create(); + $user->refresh(); + + $status = Status::factory()->create([ + 'profile_id' => $user->profile_id, + 'type' => 'photo', + ]); + + $legacy = makeDmNotification($status, $user->profile_id, 'App\DirectMessage', 1); + $modern = makeDmNotification($status, $user->profile_id, DirectMessage::class, 2); + + (new StatusDelete($status))->handle(); + + expect(Notification::find($modern->id))->toBeNull('modern DM notification should be removed'); + expect(Notification::find($legacy->id))->toBeNull('legacy App\\DirectMessage notification should also be removed'); +}); + +it('deletes both legacy App\\MediaTag and modern tag notifications on status delete', function () { + config(['federation.activitypub.enabled' => false]); + + $user = User::factory()->create(); + $user->refresh(); + + $status = Status::factory()->create([ + 'profile_id' => $user->profile_id, + 'type' => 'photo', + ]); + + $legacy = makeTagNotification($status, $user->profile_id, 'App\MediaTag', 1); + $modern = makeTagNotification($status, $user->profile_id, MediaTag::class, 2); + + (new StatusDelete($status))->handle(); + + expect(Notification::find($modern->id))->toBeNull('modern tag notification should be removed'); + expect(Notification::find($legacy->id))->toBeNull('legacy App\\MediaTag notification should also be removed'); +}); + +it('deletes both legacy and modern DM notifications on remote status delete', function () { + config(['federation.activitypub.enabled' => false]); + + $user = User::factory()->create(); + $user->refresh(); + + $status = Status::factory()->create([ + 'profile_id' => $user->profile_id, + 'type' => 'photo', + ]); + + $legacy = makeDmNotification($status, $user->profile_id, 'App\DirectMessage', 1); + $modern = makeDmNotification($status, $user->profile_id, DirectMessage::class, 2); + + (new RemoteStatusDelete($status))->handle(); + + expect(Notification::find($modern->id))->toBeNull('modern DM notification should be removed'); + expect(Notification::find($legacy->id))->toBeNull('legacy App\\DirectMessage notification should also be removed'); +}); + +it('deletes both legacy and modern tag notifications on remote status delete', function () { + config(['federation.activitypub.enabled' => false]); + + $user = User::factory()->create(); + $user->refresh(); + + $status = Status::factory()->create([ + 'profile_id' => $user->profile_id, + 'type' => 'photo', + ]); + + $legacy = makeTagNotification($status, $user->profile_id, 'App\MediaTag', 1); + $modern = makeTagNotification($status, $user->profile_id, MediaTag::class, 2); + + (new RemoteStatusDelete($status))->handle(); + + expect(Notification::find($modern->id))->toBeNull('modern tag notification should be removed'); + expect(Notification::find($legacy->id))->toBeNull('legacy App\\MediaTag notification should also be removed'); +});