diff --git a/extensions/sticky/extend.php b/extensions/sticky/extend.php index 79144acf7..4ef2ab7c8 100644 --- a/extensions/sticky/extend.php +++ b/extensions/sticky/extend.php @@ -50,4 +50,8 @@ return [ (new Extend\SearchDriver(DatabaseSearchDriver::class)) ->addFilter(DiscussionSearcher::class, StickyFilter::class) ->addMutator(DiscussionSearcher::class, PinStickiedDiscussionsToTop::class), + + (new Extend\Settings()) + ->default('flarum-sticky.only_sticky_unread_discussions', true) + ->serializeToForum('onlyStickyUnreadDiscussions', 'flarum-sticky.only_sticky_unread_discussions', 'boolval'), ]; diff --git a/extensions/sticky/js/src/admin/extend.tsx b/extensions/sticky/js/src/admin/extend.tsx index 02d6f3def..2093712b7 100644 --- a/extensions/sticky/js/src/admin/extend.tsx +++ b/extensions/sticky/js/src/admin/extend.tsx @@ -5,13 +5,21 @@ import commonExtend from '../common/extend'; export default [ ...commonExtend, - new Extend.Admin().permission( - () => ({ - icon: 'fas fa-thumbtack', - label: app.translator.trans('flarum-sticky.admin.permissions.sticky_discussions_label'), - permission: 'discussion.sticky', - }), - 'moderate', - 95 - ), + new Extend.Admin() + .permission( + () => ({ + icon: 'fas fa-thumbtack', + label: app.translator.trans('flarum-sticky.admin.permissions.sticky_discussions_label'), + permission: 'discussion.sticky', + }), + 'moderate', + 95 + ) + .setting(() => ({ + setting: 'flarum-sticky.only_sticky_unread_discussions', + name: 'onlyStickyUnreadDiscussions', + type: 'boolean', + label: app.translator.trans('flarum-sticky.admin.settings.only_sticky_unread_discussions_label'), + help: app.translator.trans('flarum-sticky.admin.settings.only_sticky_unread_discussions_help'), + })), ]; diff --git a/extensions/sticky/locale/en.yml b/extensions/sticky/locale/en.yml index 64407a52f..05b37d701 100644 --- a/extensions/sticky/locale/en.yml +++ b/extensions/sticky/locale/en.yml @@ -6,6 +6,9 @@ flarum-sticky: # Translations in this namespace are used by the admin interface. admin: + settings: + only_sticky_unread_discussions_label: Only sticky unread discussions + only_sticky_unread_discussions_help: On the All Discussions page, unread sticky discussions pin to the top, while read sticky discussions follow the regular order. # These translations are used in the Permissions page of the admin interface. permissions: diff --git a/extensions/sticky/src/PinStickiedDiscussionsToTop.php b/extensions/sticky/src/PinStickiedDiscussionsToTop.php index dcb53e355..9be1a1ddf 100755 --- a/extensions/sticky/src/PinStickiedDiscussionsToTop.php +++ b/extensions/sticky/src/PinStickiedDiscussionsToTop.php @@ -12,15 +12,30 @@ namespace Flarum\Sticky; use DateTime; use Flarum\Search\Database\DatabaseSearchState; use Flarum\Search\SearchCriteria; +use Flarum\Settings\SettingsRepositoryInterface; use Flarum\Tags\Search\Filter\TagFilter; use Illuminate\Database\Query\Builder; class PinStickiedDiscussionsToTop { + public function __construct( + protected SettingsRepositoryInterface $settings + ) { + } + public function __invoke(DatabaseSearchState $state, SearchCriteria $criteria): void { if ($criteria->sortIsDefault && ! $state->isFulltextSearch()) { $query = $state->getQuery()->getQuery(); + $onlyStickyUnread = $this->settings->get('flarum-sticky.only_sticky_unread_discussions'); + + // If only sticky unread discussions is disabled, then pin all stickied + // discussions to the top whether they are read or not. + if (! $onlyStickyUnread) { + $this->pinStickiedToTop($query); + + return; + } // If we are viewing a specific tag, then pin all stickied // discussions to the top no matter what. @@ -28,11 +43,7 @@ class PinStickiedDiscussionsToTop if ($count = count($filters)) { if ($count === 1 && $filters[0] instanceof TagFilter) { - if (! is_array($query->orders)) { - $query->orders = []; - } - - array_unshift($query->orders, ['column' => 'is_sticky', 'direction' => 'desc']); + $this->pinStickiedToTop($query); } return; @@ -76,4 +87,21 @@ class PinStickiedDiscussionsToTop unset($query->offset, $sticky->offset); } } + + /** + * Pin all stickied discussions to the top of the query. + * This is done by prepending an order clause to the query. + * + * @param $query + * + * @return void + */ + protected function pinStickiedToTop($query): void + { + if (! is_array($query->orders)) { + $query->orders = []; + } + + array_unshift($query->orders, ['column' => 'is_sticky', 'direction' => 'desc']); + } } diff --git a/extensions/sticky/tests/integration/api/ListDiscussionsTest.php b/extensions/sticky/tests/integration/api/ListDiscussionsTest.php index b8518f41e..65154b927 100644 --- a/extensions/sticky/tests/integration/api/ListDiscussionsTest.php +++ b/extensions/sticky/tests/integration/api/ListDiscussionsTest.php @@ -104,6 +104,40 @@ class ListDiscussionsTest extends TestCase $this->assertEqualsCanonicalizing([2, 4, 3, 1], Arr::pluck($data['data'], 'id')); } + #[Test] + public function list_discussions_sticky_first_all_read_as_user_filter_read_off() + { + $this->setting('flarum-sticky.only_sticky_unread_discussions', false); + $response = $this->send( + $this->request('GET', '/api/discussions', [ + 'authenticatedAs' => 3 + ]) + ); + + $this->assertEquals(200, $response->getStatusCode()); + + $data = json_decode($response->getBody()->getContents(), true); + + $this->assertEquals([3, 1, 2, 4], Arr::pluck($data['data'], 'id')); + } + + #[Test] + public function list_discussions_sticky_first_all_read_as_user_filter_read_on() + { + $this->setting('flarum-sticky.only_sticky_unread_discussions', true); + $response = $this->send( + $this->request('GET', '/api/discussions', [ + 'authenticatedAs' => 3 + ]) + ); + + $this->assertEquals(200, $response->getStatusCode()); + + $data = json_decode($response->getBody()->getContents(), true); + + $this->assertEquals([2, 4, 3, 1], Arr::pluck($data['data'], 'id')); + } + #[Test] public function list_discussions_shows_stick_first_on_a_tag() {