DEV: Plugin-api methods for user-notifications route customizations (#24873)

This commit is contained in:
Mark VanLandingham 2023-12-13 15:15:42 -06:00 committed by GitHub
parent f7aefffea7
commit c051bfc2fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 96 additions and 2 deletions

View File

@ -523,6 +523,7 @@ GEM
PLATFORMS
aarch64-linux
arm64-darwin-20
arm64-darwin-21
arm64-darwin-22
x86_64-darwin-18
x86_64-darwin-19

View File

@ -11,6 +11,11 @@ import { iconHTML } from "discourse-common/lib/icon-library";
import discourseComputed from "discourse-common/utils/decorators";
import I18n from "discourse-i18n";
const _beforeLoadMoreCallbacks = [];
export function addBeforeLoadMoreCallback(fn) {
_beforeLoadMoreCallbacks.push(fn);
}
export default class UserNotificationsController extends Controller {
@service modal;
@service appEvents;
@ -102,6 +107,14 @@ export default class UserNotificationsController extends Controller {
@action
loadMore() {
if (
_beforeLoadMoreCallbacks.length &&
!_beforeLoadMoreCallbacks.some((fn) => fn(this))
) {
// Return early if any callbacks return false, short-circuiting the default loading more logic
return;
}
this.model.loadMore();
}
}

View File

@ -31,6 +31,7 @@ import { addUserMenuProfileTabItem } from "discourse/components/user-menu/profil
import { addDiscoveryQueryParam } from "discourse/controllers/discovery/list";
import { registerFullPageSearchType } from "discourse/controllers/full-page-search";
import { registerCustomPostMessageCallback as registerCustomPostMessageCallback1 } from "discourse/controllers/topic";
import { addBeforeLoadMoreCallback as addBeforeLoadMoreNotificationsCallback } from "discourse/controllers/user-notifications";
import { registerCustomUserNavMessagesDropdownRow } from "discourse/controllers/user-private-messages";
import {
addExtraIconRenderer,
@ -88,6 +89,7 @@ import {
addSaveableUserOptionField,
} from "discourse/models/user";
import { setNewCategoryDefaultColors } from "discourse/routes/new-category";
import { setNotificationsLimit } from "discourse/routes/user-notifications";
import { addComposerSaveErrorCallback } from "discourse/services/composer";
import {
addToHeaderIcons,
@ -141,7 +143,7 @@ import { modifySelectKit } from "select-kit/mixins/plugin-api";
// docs/CHANGELOG-JAVASCRIPT-PLUGIN-API.md whenever you change the version
// using the format described at https://keepachangelog.com/en/1.0.0/.
export const PLUGIN_API_VERSION = "1.18.0";
export const PLUGIN_API_VERSION = "1.19.0";
// This helper prevents us from applying the same `modifyClass` over and over in test mode.
function canModify(klass, type, resolverName, changes) {
@ -819,6 +821,21 @@ class PluginApi {
registerCustomPostMessageCallback1(type, callback);
}
/**
* Registers a callback that will be evaluated when infinite scrolling would cause
* more notifications to be loaded. This can be used to prevent loading more unless
* a specific condition is met.
*
* Example:
*
* api.addBeforeLoadMoreNotificationsCallback((controller) => {
* return controller.allowLoadMore;
* });
*/
addBeforeLoadMoreNotificationsCallback(fn) {
addBeforeLoadMoreNotificationsCallback(fn);
}
/**
* Changes a setting associated with a widget. For example, if
* you wanted small avatars in the post stream:
@ -1773,6 +1790,18 @@ class PluginApi {
setNewCategoryDefaultColors(backgroundColor, textColor);
}
/**
* Change the number of notifications that are loaded at /my/notifications
*
* ```
* api.setNotificationsLimit(20)
* ```
*
**/
setNotificationsLimit(limit) {
setNotificationsLimit(limit);
}
/**
* Add a callback to modify search results before displaying them.
*

View File

@ -2,6 +2,13 @@ import ViewingActionType from "discourse/mixins/viewing-action-type";
import DiscourseRoute from "discourse/routes/discourse";
import I18n from "discourse-i18n";
const DEFAULT_LIMIT = 60;
let limit = DEFAULT_LIMIT;
export function setNotificationsLimit(newLimit) {
limit = newLimit;
}
export default DiscourseRoute.extend(ViewingActionType, {
controllerName: "user-notifications",
queryParams: { filter: { refreshModel: true } },
@ -16,6 +23,7 @@ export default DiscourseRoute.extend(ViewingActionType, {
return this.store.find("notification", {
username,
filter: params.filter,
limit,
});
}
},

View File

@ -27,6 +27,10 @@
<UserMenu::MenuItem @item={{item}} />
{{/each}}
<ConditionalLoadingSpinner @condition={{this.loading}} />
<PluginOutlet
@name="user-notifications-list-bottom"
@outletArgs={{hash controller=this}}
/>
</div>
{{/if}}
{{/if}}

View File

@ -7,6 +7,14 @@ in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.19.0] - 2023-12-13
### Added
- Added `setNotificationsLimit` function, which sets a new limit for how many notifications are loaded for the user notifications route
- Added `addBeforeLoadMoreNotificationsCallback` function, which takes a function as the argument. All added callbacks are evaluated before `loadMore` is triggered for user notifications. If any callback returns false, notifications will not be loaded.
## [1.18.0] - 2023-12-1
### Added

View File

@ -52,6 +52,10 @@ module PageObjects
self
end
def click_primary_navigation_item(name)
page.find(primary_navigation_selector(name)).click
end
private
def primary_navigation_selector(name)

View File

@ -27,6 +27,10 @@ module PageObjects
def has_no_notification?(notification)
page.has_no_css?(".notification a[href='#{notification.url}']")
end
def has_notification_count_of?(count)
page.has_css?(".notification", count: count)
end
end
end
end

View File

@ -3,6 +3,7 @@
describe "User notifications", type: :system do
fab!(:user)
let(:user_notifications_page) { PageObjects::Pages::UserNotifications.new }
let(:user_page) { PageObjects::Pages::User.new }
fab!(:read_notification) { Fabricate(:notification, user: user, read: true) }
fab!(:unread_notification) { Fabricate(:notification, user: user, read: false) }
@ -10,7 +11,7 @@ describe "User notifications", type: :system do
before { sign_in(user) }
describe "filtering" do
it "saves custom picture and system assigned pictures" do
it "correctly filters all / read / unread notifications" do
user_notifications_page.visit(user)
user_notifications_page.filter_dropdown
expect(user_notifications_page).to have_selected_filter_value("all")
@ -28,4 +29,26 @@ describe "User notifications", type: :system do
expect(user_notifications_page).to have_notification(unread_notification)
end
end
describe "setNotificationLimit & addBeforeLoadMoreNotificationsCallback plugin-api functions" do
it "Allows blocking loading via callback and limit" do
user_page.visit(user)
page.execute_script <<~JS
require("discourse/lib/plugin-api").withPluginApi("1.19.0", (api) => {
api.setNotificationsLimit(1);
api.addBeforeLoadMoreNotificationsCallback(() => {
return false;
})
})
JS
user_page.click_primary_navigation_item("notifications")
# It is 1 here because we blocked infinite scrolling. Even though the limit is 1,
# without the callback, we would have 2 items here as it immediately fires another request.
expect(user_notifications_page).to have_notification_count_of(1)
end
end
end