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();
});