DEV: Apply model transformer API on more models (#18087)

This commit extends the plugin API introduced in 40fd82e2d1 to the `Bookmark` and `Notification` models. It also refactors the code that's responsible for loading items in the experimental menu to use `async`...`await` instead of `Promise`s.
This commit is contained in:
Osama Sayegh 2022-08-26 00:44:06 +03:00 committed by GitHub
parent 14ab819c1d
commit 97f094e5ee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 143 additions and 65 deletions

View File

@ -5,6 +5,7 @@ import showModal from "discourse/lib/show-modal";
import I18n from "I18n";
import UserMenuNotificationItem from "discourse/lib/user-menu/notification-item";
import UserMenuBookmarkItem from "discourse/lib/user-menu/bookmark-item";
import Bookmark from "discourse/models/bookmark";
export default class UserMenuBookmarksList extends UserMenuNotificationsList {
get dismissTypes() {
@ -44,29 +45,34 @@ export default class UserMenuBookmarksList extends UserMenuNotificationsList {
return this.currentUser.get(key) || 0;
}
fetchItems() {
return ajax(`/u/${this.currentUser.username}/user-menu-bookmarks`).then(
(data) => {
const content = [];
data.notifications.forEach((rawNotification) => {
const notification = Notification.create(rawNotification);
content.push(
new UserMenuNotificationItem({
notification,
currentUser: this.currentUser,
siteSettings: this.siteSettings,
site: this.site,
})
);
});
content.push(
...data.bookmarks.map((bookmark) => {
return new UserMenuBookmarkItem({ bookmark });
})
);
return content;
}
async fetchItems() {
const data = await ajax(
`/u/${this.currentUser.username}/user-menu-bookmarks`
);
const content = [];
const notifications = data.notifications.map((n) => Notification.create(n));
await Notification.applyTransformations(notifications);
notifications.forEach((notification) => {
content.push(
new UserMenuNotificationItem({
notification,
currentUser: this.currentUser,
siteSettings: this.siteSettings,
site: this.site,
})
);
});
const bookmarks = data.bookmarks.map((b) => Bookmark.create(b));
await Bookmark.applyTransformations(bookmarks);
content.push(
...bookmarks.map((bookmark) => {
return new UserMenuBookmarkItem({ bookmark });
})
);
return content;
}
dismissWarningModal() {

View File

@ -28,33 +28,40 @@ export default class UserMenuItemsList extends Component {
return "user-menu/items-list-empty-state";
}
fetchItems() {
async fetchItems() {
throw new Error(
`the fetchItems method must be implemented in ${this.constructor.name}`
);
}
refreshList() {
this.#load();
async refreshList() {
await this.#load();
}
dismissWarningModal() {
return null;
}
#load() {
async #load() {
const cached = this.#getCachedItems();
if (cached?.length) {
this.items = cached;
} else {
this.loading = true;
}
this.fetchItems()
.then((items) => {
this.#setCachedItems(items);
this.items = items;
})
.finally(() => (this.loading = false));
try {
const items = await this.fetchItems();
this.#setCachedItems(items);
this.items = items;
} catch (err) {
// eslint-disable-next-line no-console
console.error(
`an error occurred when loading items for ${this.constructor.name}`,
err
);
} finally {
this.loading = false;
}
}
#getCachedItems() {

View File

@ -45,31 +45,34 @@ export default class UserMenuMessagesList extends UserMenuNotificationsList {
return this.currentUser.get(key) || 0;
}
fetchItems() {
return ajax(
async fetchItems() {
const data = await ajax(
`/u/${this.currentUser.username}/user-menu-private-messages`
).then(async (data) => {
const content = [];
data.notifications.forEach((rawNotification) => {
const notification = Notification.create(rawNotification);
content.push(
new UserMenuNotificationItem({
notification,
currentUser: this.currentUser,
siteSettings: this.siteSettings,
site: this.site,
})
);
});
const topics = data.topics.map((t) => Topic.create(t));
await Topic.applyTransformations(topics);
);
const content = [];
const notifications = data.notifications.map((n) => Notification.create(n));
await Notification.applyTransformations(notifications);
notifications.forEach((notification) => {
content.push(
...topics.map((topic) => {
return new UserMenuMessageItem({ message: topic });
new UserMenuNotificationItem({
notification,
currentUser: this.currentUser,
siteSettings: this.siteSettings,
site: this.site,
})
);
return content;
});
const topics = data.topics.map((t) => Topic.create(t));
await Topic.applyTransformations(topics);
content.push(
...topics.map((topic) => {
return new UserMenuMessageItem({ message: topic });
})
);
return content;
}
dismissWarningModal() {

View File

@ -6,6 +6,7 @@ import { postRNWebviewMessage } from "discourse/lib/utilities";
import showModal from "discourse/lib/show-modal";
import { inject as service } from "@ember/service";
import UserMenuNotificationItem from "discourse/lib/user-menu/notification-item";
import Notification from "discourse/models/notification";
export default class UserMenuNotificationsList extends UserMenuItemsList {
@service currentUser;
@ -54,7 +55,7 @@ export default class UserMenuNotificationsList extends UserMenuItemsList {
}
}
fetchItems() {
async fetchItems() {
const params = {
limit: 30,
recent: true,
@ -67,19 +68,19 @@ export default class UserMenuNotificationsList extends UserMenuItemsList {
params.filter_by_types = types.join(",");
params.silent = true;
}
return this.store
const collection = await this.store
.findStale("notification", params)
.refresh()
.then((c) => {
return c.content.map((notification) => {
return new UserMenuNotificationItem({
notification,
currentUser: this.currentUser,
siteSettings: this.siteSettings,
site: this.site,
});
});
.refresh();
const notifications = collection.content;
await Notification.applyTransformations(notifications);
return notifications.map((notification) => {
return new UserMenuNotificationItem({
notification,
currentUser: this.currentUser,
siteSettings: this.siteSettings,
site: this.site,
});
});
}
dismissWarningModal() {

View File

@ -1952,7 +1952,7 @@ class PluginApi {
* @callback registerModelTransformerCallback
* @param {Object[]} A list of model instances
*
* @param {string} modelName - Model type on which transformation should be applied. Currently the only valid type is "topic".
* @param {string} modelName - Model type on which transformation should be applied. Currently valid types are "topic", "notification" and "bookmark".
* @param {registerModelTransformerCallback} transformer - Callback function that receives a list of model objects of the specified type and applies transformation on them.
*/
registerModelTransformer(modelName, transformer) {

View File

@ -12,6 +12,7 @@ import getURL from "discourse-common/lib/get-url";
import { longDate } from "discourse/lib/formatter";
import { none } from "@ember/object/computed";
import { capitalize } from "@ember/string";
import { applyModelTransformations } from "discourse/lib/model-transformers";
export const AUTO_DELETE_PREFERENCES = {
NEVER: 0,
@ -168,6 +169,10 @@ Bookmark.reopenClass({
args.user = User.create(args.user);
return this._super(args);
},
async applyTransformations(bookmarks) {
await applyModelTransformations("bookmark", bookmarks);
},
});
export default Bookmark;

View File

@ -1,6 +1,11 @@
import RestModel from "discourse/models/rest";
import { tracked } from "@glimmer/tracking";
import { applyModelTransformations } from "discourse/lib/model-transformers";
export default class Notification extends RestModel {
static async applyTransformations(notifications) {
await applyModelTransformations("notification", notifications);
}
@tracked read;
}

View File

@ -189,6 +189,55 @@ acceptance("User menu", function (needs) {
);
});
test("notifications tab applies model transformations registered by plugins", async function (assert) {
withPluginApi("0.1", (api) => {
api.registerModelTransformer("notification", (notifications) => {
notifications.forEach((notification, index) => {
if (notification.fancy_title) {
notification.fancy_title = `pluginNotificationTransformer ${index} ${notification.fancy_title}`;
}
});
});
});
await visit("/");
await click(".d-header-icons .current-user");
const notifications = queryAll(
"#quick-access-all-notifications ul li.notification"
);
assert.strictEqual(
notifications[0].textContent.replace(/\s+/g, " ").trim(),
"velesin pluginNotificationTransformer 0 edited topic 443"
);
assert.strictEqual(
notifications[1].textContent.replace(/\s+/g, " ").trim(),
"velesin pluginNotificationTransformer 1 some title"
);
});
test("bookmarks tab applies model transformations registered by plugins", async function (assert) {
withPluginApi("0.1", (api) => {
api.registerModelTransformer("bookmark", (bookmarks) => {
bookmarks.forEach((bookmark) => {
if (bookmark.title) {
bookmark.title = `pluginBookmarkTransformer ${bookmark.title}`;
}
});
});
});
await visit("/");
await click(".d-header-icons .current-user");
await click("#user-menu-button-bookmarks");
const bookmarks = queryAll("#quick-access-bookmarks ul li.bookmark");
assert.strictEqual(
bookmarks[0].textContent.replace(/\s+/g, " ").trim(),
"osama pluginBookmarkTransformer Test poll topic hello world"
);
});
test("messages tab applies model transformations registered by plugins", async function (assert) {
withPluginApi("0.1", (api) => {
api.registerModelTransformer("topic", (topics) => {

View File

@ -10,6 +10,7 @@ export default {
post_number: 1,
topic_id: 130,
slug: "lorem-ipsum-dolor-sit-amet",
fancy_title: "edited topic 443",
data: {
topic_title: "edited topic 443",
display_username: "velesin",
@ -26,6 +27,7 @@ export default {
post_number: 1,
topic_id: 1234,
slug: "a-slug",
fancy_title: "some title",
data: { topic_title: "some title", display_username: "velesin" },
},
{