From 5f834944d3c08c0f4afa816a6e59577ea662d0f4 Mon Sep 17 00:00:00 2001 From: huoxin233 <23447157+huoxin233@users.noreply.github.com> Date: Mon, 16 Mar 2026 23:13:32 +0800 Subject: [PATCH 1/8] feat: add support for auto follow discussions on create --- extensions/subscriptions/extend.php | 5 +++- .../js/src/forum/addSubscriptionSettings.tsx | 18 ++++++++++++ extensions/subscriptions/locale/en.yml | 1 + .../src/Listener/FollowAfterCreate.php | 29 +++++++++++++++++++ 4 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 extensions/subscriptions/src/Listener/FollowAfterCreate.php diff --git a/extensions/subscriptions/extend.php b/extensions/subscriptions/extend.php index 8bb29b5440..6e057fa370 100644 --- a/extensions/subscriptions/extend.php +++ b/extensions/subscriptions/extend.php @@ -17,6 +17,7 @@ use Flarum\Post\Event\Hidden; use Flarum\Post\Event\Posted; use Flarum\Post\Event\Restored; +use Flarum\Discussion\Event\Started; use Flarum\Search\Database\DatabaseSearchDriver; use Flarum\Subscriptions\Api\UserResourceFields; use Flarum\Subscriptions\Filter\SubscriptionFilter; @@ -60,7 +61,8 @@ ->listen(Hidden::class, Listener\DeleteNotificationWhenPostIsHiddenOrDeleted::class) ->listen(Restored::class, Listener\RestoreNotificationWhenPostIsRestored::class) ->listen(Deleted::class, Listener\DeleteNotificationWhenPostIsHiddenOrDeleted::class) - ->listen(Posted::class, Listener\FollowAfterReply::class), + ->listen(Posted::class, Listener\FollowAfterReply::class) + ->listen(Started::class, Listener\FollowAfterCreate::class), (new Extend\SearchDriver(DatabaseSearchDriver::class)) ->addFilter(DiscussionSearcher::class, SubscriptionFilter::class) @@ -68,5 +70,6 @@ (new Extend\User()) ->registerPreference('followAfterReply', 'boolval', false) + ->registerPreference('followAfterCreate', 'boolval', true) ->registerPreference('flarum-subscriptions.notify_for_all_posts', 'boolval', false), ]; diff --git a/extensions/subscriptions/js/src/forum/addSubscriptionSettings.tsx b/extensions/subscriptions/js/src/forum/addSubscriptionSettings.tsx index 192272b564..bfd05a3ffa 100644 --- a/extensions/subscriptions/js/src/forum/addSubscriptionSettings.tsx +++ b/extensions/subscriptions/js/src/forum/addSubscriptionSettings.tsx @@ -23,6 +23,24 @@ export default function () { ); + items.add( + 'followAfterCreate', + { + this.followAfterCreateLoading = true; + + this.user.savePreferences({ followAfterCreate: value }).then(() => { + this.followAfterCreateLoading = false; + m.redraw(); + }); + }} + loading={this.followAfterCreateLoading} + > + {app.translator.trans('flarum-subscriptions.forum.settings.follow_after_create_label')} + + ); + items.add( 'notifyForAllPosts', actor; + + if ($actor && $actor->exists && $actor->getPreference('followAfterCreate')) { + $actor->assertRegistered(); + + $state = $event->discussion->stateFor($actor); + + $state->subscription = 'follow'; + $state->save(); + } + } +} From 47c1507a41b9d193eabba8496c1f87a0964165c1 Mon Sep 17 00:00:00 2001 From: huoxin233 <23447157+huoxin233@users.noreply.github.com> Date: Sat, 21 Mar 2026 15:49:22 +0800 Subject: [PATCH 2/8] chore: update follow_after_create_label tense to match existing patterns --- extensions/subscriptions/locale/en.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/subscriptions/locale/en.yml b/extensions/subscriptions/locale/en.yml index d0864cdb21..5615582f16 100644 --- a/extensions/subscriptions/locale/en.yml +++ b/extensions/subscriptions/locale/en.yml @@ -31,7 +31,7 @@ flarum-subscriptions: # These translations are used in the Settings page. settings: follow_after_reply_label: Automatically follow discussions that I reply to - follow_after_create_label: Automatically follow discussions that I created + follow_after_create_label: Automatically follow discussions that I create notify_for_all_posts_label: Notify about every new post instead of only the last in a discussion notify_new_post_label: Someone posts in a discussion I'm following From 9725493a358b18cfcff5ccf2f18303e7f5350fb6 Mon Sep 17 00:00:00 2001 From: huoxin233 <23447157+huoxin233@users.noreply.github.com> Date: Sat, 21 Mar 2026 15:49:46 +0800 Subject: [PATCH 3/8] fix: change followAfterCreate default preference to false --- extensions/subscriptions/extend.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/subscriptions/extend.php b/extensions/subscriptions/extend.php index 6e057fa370..b3dc063f9c 100644 --- a/extensions/subscriptions/extend.php +++ b/extensions/subscriptions/extend.php @@ -70,6 +70,6 @@ (new Extend\User()) ->registerPreference('followAfterReply', 'boolval', false) - ->registerPreference('followAfterCreate', 'boolval', true) + ->registerPreference('followAfterCreate', 'boolval', false) ->registerPreference('flarum-subscriptions.notify_for_all_posts', 'boolval', false), ]; From e03a3a8b9c234e5b7f31c420c7fe8ecfd6392507 Mon Sep 17 00:00:00 2001 From: huoxin233 <23447157+huoxin233@users.noreply.github.com> Date: Sat, 21 Mar 2026 16:01:34 +0800 Subject: [PATCH 4/8] fix: explicitly declare SettingsPage loading generic properties in shims --- extensions/subscriptions/js/src/@types/shims.d.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/extensions/subscriptions/js/src/@types/shims.d.ts b/extensions/subscriptions/js/src/@types/shims.d.ts index 057f872464..c166382259 100644 --- a/extensions/subscriptions/js/src/@types/shims.d.ts +++ b/extensions/subscriptions/js/src/@types/shims.d.ts @@ -1,7 +1,16 @@ import 'flarum/common/models/Discussion'; +import 'flarum/forum/components/SettingsPage'; declare module 'flarum/common/models/Discussion' { export default interface Discussion { subscription(): string; } } + +declare module 'flarum/forum/components/SettingsPage' { + export default interface SettingsPage { + followAfterReplyLoading: boolean; + followAfterCreateLoading: boolean; + notifyForAllPostsLoading: boolean; + } +} From 9a91b4ce34d3b3797e8db32091f028b1f4743d02 Mon Sep 17 00:00:00 2001 From: huoxin233 <23447157+huoxin233@users.noreply.github.com> Date: Sat, 21 Mar 2026 17:38:43 +0800 Subject: [PATCH 5/8] test: add integration tests for FollowAfterCreate --- .../api/discussions/FollowAfterCreateTest.php | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 extensions/subscriptions/tests/integration/api/discussions/FollowAfterCreateTest.php diff --git a/extensions/subscriptions/tests/integration/api/discussions/FollowAfterCreateTest.php b/extensions/subscriptions/tests/integration/api/discussions/FollowAfterCreateTest.php new file mode 100644 index 0000000000..f88d7deff0 --- /dev/null +++ b/extensions/subscriptions/tests/integration/api/discussions/FollowAfterCreateTest.php @@ -0,0 +1,89 @@ +extension('flarum-subscriptions'); + + $this->prepareDatabase([ + User::class => [ + $this->normalUser(), + ['id' => 3, 'username' => 'acme_follow', 'email' => 'acme@machine.local', 'is_email_confirmed' => 1, 'preferences' => json_encode(['followAfterCreate' => true])], + ['id' => 4, 'username' => 'acme_no_follow', 'email' => 'acme2@machine.local', 'is_email_confirmed' => 1, 'preferences' => json_encode(['followAfterCreate' => false])], + ], + ]); + } + + #[Test] + public function user_with_preference_true_follows_after_creating_discussion() + { + $this->app(); + + $response = $this->send( + $this->request('POST', '/api/discussions', [ + 'authenticatedAs' => 3, + 'json' => [ + 'data' => [ + 'type' => 'discussions', + 'attributes' => [ + 'title' => 'Test Discussion', + 'content' => 'Test content that needs to be sufficiently long.' + ], + ], + ], + ]) + ); + + $this->assertEquals(201, $response->getStatusCode()); + + $discussionId = json_decode($response->getBody()->getContents(), true)['data']['id']; + + $this->assertEquals('follow', $this->database()->table('discussion_user')->where('discussion_id', $discussionId)->where('user_id', 3)->value('subscription')); + } + + #[Test] + public function user_with_preference_false_does_not_follow_after_creating_discussion() + { + $this->app(); + + $response = $this->send( + $this->request('POST', '/api/discussions', [ + 'authenticatedAs' => 4, + 'json' => [ + 'data' => [ + 'type' => 'discussions', + 'attributes' => [ + 'title' => 'Test Discussion', + 'content' => 'Test content that needs to be sufficiently long.' + ], + ], + ], + ]) + ); + + $this->assertEquals(201, $response->getStatusCode()); + + $discussionId = json_decode($response->getBody()->getContents(), true)['data']['id']; + + $this->assertNull($this->database()->table('discussion_user')->where('discussion_id', $discussionId)->where('user_id', 4)->value('subscription')); + } +} From 27dada17f970014599f128a2487dd267d46e4142 Mon Sep 17 00:00:00 2001 From: huoxin233 <23447157+huoxin233@users.noreply.github.com> Date: Sat, 21 Mar 2026 17:47:36 +0800 Subject: [PATCH 6/8] test: add integration tests for FollowAfterReply --- .../api/discussions/FollowAfterReplyTest.php | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 extensions/subscriptions/tests/integration/api/discussions/FollowAfterReplyTest.php diff --git a/extensions/subscriptions/tests/integration/api/discussions/FollowAfterReplyTest.php b/extensions/subscriptions/tests/integration/api/discussions/FollowAfterReplyTest.php new file mode 100644 index 0000000000..e15902a01c --- /dev/null +++ b/extensions/subscriptions/tests/integration/api/discussions/FollowAfterReplyTest.php @@ -0,0 +1,98 @@ +extension('flarum-subscriptions'); + + $this->prepareDatabase([ + User::class => [ + $this->normalUser(), + ['id' => 3, 'username' => 'acme_follow', 'email' => 'acme@machine.local', 'is_email_confirmed' => 1, 'preferences' => json_encode(['followAfterReply' => true])], + ['id' => 4, 'username' => 'acme_no_follow', 'email' => 'acme2@machine.local', 'is_email_confirmed' => 1, 'preferences' => json_encode(['followAfterReply' => false])], + ], + Discussion::class => [ + ['id' => 1, 'title' => __CLASS__, 'created_at' => Carbon::now(), 'last_posted_at' => Carbon::now(), 'user_id' => 1, 'first_post_id' => 1, 'comment_count' => 1, 'last_post_number' => 1, 'last_post_id' => 1], + ], + Post::class => [ + ['id' => 1, 'discussion_id' => 1, 'created_at' => Carbon::createFromDate(1975, 5, 21)->toDateTimeString(), 'user_id' => 1, 'type' => 'comment', 'content' => '

foo bar

', 'number' => 1], + ], + ]); + } + + #[Test] + public function user_with_preference_true_follows_after_replying_to_discussion() + { + $this->app(); + + $response = $this->send( + $this->request('POST', '/api/posts', [ + 'authenticatedAs' => 3, + 'json' => [ + 'data' => [ + 'type' => 'posts', + 'attributes' => [ + 'content' => 'reply with predetermined content for automated testing' + ], + 'relationships' => [ + 'discussion' => ['data' => ['id' => 1]], + ], + ], + ], + ]) + ); + + $this->assertEquals(201, $response->getStatusCode()); + + $this->assertEquals('follow', $this->database()->table('discussion_user')->where('discussion_id', 1)->where('user_id', 3)->value('subscription')); + } + + #[Test] + public function user_with_preference_false_does_not_follow_after_replying_to_discussion() + { + $this->app(); + + $response = $this->send( + $this->request('POST', '/api/posts', [ + 'authenticatedAs' => 4, + 'json' => [ + 'data' => [ + 'type' => 'posts', + 'attributes' => [ + 'content' => 'reply with predetermined content for automated testing' + ], + 'relationships' => [ + 'discussion' => ['data' => ['id' => 1]], + ], + ], + ], + ]) + ); + + $this->assertEquals(201, $response->getStatusCode()); + + $this->assertNull($this->database()->table('discussion_user')->where('discussion_id', 1)->where('user_id', 4)->value('subscription')); + } +} From 849b21a26de82bf1efd6891b9b86e5eeec9ec96d Mon Sep 17 00:00:00 2001 From: huoxin233 <23447157+huoxin233@users.noreply.github.com> Date: Sat, 21 Mar 2026 17:49:59 +0800 Subject: [PATCH 7/8] chore: apply fix from styleci --- extensions/subscriptions/extend.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/subscriptions/extend.php b/extensions/subscriptions/extend.php index b3dc063f9c..f563b6b81f 100644 --- a/extensions/subscriptions/extend.php +++ b/extensions/subscriptions/extend.php @@ -10,6 +10,7 @@ use Flarum\Api\Resource; use Flarum\Approval\Event\PostWasApproved; use Flarum\Discussion\Event\Saving; +use Flarum\Discussion\Event\Started; use Flarum\Discussion\Search\DiscussionSearcher; use Flarum\Discussion\UserState; use Flarum\Extend; @@ -17,7 +18,6 @@ use Flarum\Post\Event\Hidden; use Flarum\Post\Event\Posted; use Flarum\Post\Event\Restored; -use Flarum\Discussion\Event\Started; use Flarum\Search\Database\DatabaseSearchDriver; use Flarum\Subscriptions\Api\UserResourceFields; use Flarum\Subscriptions\Filter\SubscriptionFilter; From f6fd60bdfb1a7060fd93165438338d1efec923b7 Mon Sep 17 00:00:00 2001 From: huoxin233 <23447157+huoxin233@users.noreply.github.com> Date: Sun, 22 Mar 2026 19:22:46 +0800 Subject: [PATCH 8/8] chore(ts): resolve strict null checks in SettingsPage --- .../js/src/forum/addSubscriptionSettings.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions/subscriptions/js/src/forum/addSubscriptionSettings.tsx b/extensions/subscriptions/js/src/forum/addSubscriptionSettings.tsx index d537bc834a..5dc59df832 100644 --- a/extensions/subscriptions/js/src/forum/addSubscriptionSettings.tsx +++ b/extensions/subscriptions/js/src/forum/addSubscriptionSettings.tsx @@ -8,7 +8,7 @@ export default function () { items.add( 'followAfterReply', { this.followAfterReplyLoading = true; @@ -26,11 +26,11 @@ export default function () { items.add( 'followAfterCreate', { + state={!!this.user?.preferences()?.followAfterCreate} + onchange={(value: boolean) => { this.followAfterCreateLoading = true; - this.user.savePreferences({ followAfterCreate: value }).then(() => { + this.user!.savePreferences({ followAfterCreate: value }).then(() => { this.followAfterCreateLoading = false; m.redraw(); });