mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
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:
@@ -5,6 +5,7 @@ import showModal from "discourse/lib/show-modal";
|
|||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
import UserMenuNotificationItem from "discourse/lib/user-menu/notification-item";
|
import UserMenuNotificationItem from "discourse/lib/user-menu/notification-item";
|
||||||
import UserMenuBookmarkItem from "discourse/lib/user-menu/bookmark-item";
|
import UserMenuBookmarkItem from "discourse/lib/user-menu/bookmark-item";
|
||||||
|
import Bookmark from "discourse/models/bookmark";
|
||||||
|
|
||||||
export default class UserMenuBookmarksList extends UserMenuNotificationsList {
|
export default class UserMenuBookmarksList extends UserMenuNotificationsList {
|
||||||
get dismissTypes() {
|
get dismissTypes() {
|
||||||
@@ -44,29 +45,34 @@ export default class UserMenuBookmarksList extends UserMenuNotificationsList {
|
|||||||
return this.currentUser.get(key) || 0;
|
return this.currentUser.get(key) || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchItems() {
|
async fetchItems() {
|
||||||
return ajax(`/u/${this.currentUser.username}/user-menu-bookmarks`).then(
|
const data = await ajax(
|
||||||
(data) => {
|
`/u/${this.currentUser.username}/user-menu-bookmarks`
|
||||||
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;
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
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() {
|
dismissWarningModal() {
|
||||||
|
|||||||
@@ -28,33 +28,40 @@ export default class UserMenuItemsList extends Component {
|
|||||||
return "user-menu/items-list-empty-state";
|
return "user-menu/items-list-empty-state";
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchItems() {
|
async fetchItems() {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`the fetchItems method must be implemented in ${this.constructor.name}`
|
`the fetchItems method must be implemented in ${this.constructor.name}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshList() {
|
async refreshList() {
|
||||||
this.#load();
|
await this.#load();
|
||||||
}
|
}
|
||||||
|
|
||||||
dismissWarningModal() {
|
dismissWarningModal() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
#load() {
|
async #load() {
|
||||||
const cached = this.#getCachedItems();
|
const cached = this.#getCachedItems();
|
||||||
if (cached?.length) {
|
if (cached?.length) {
|
||||||
this.items = cached;
|
this.items = cached;
|
||||||
} else {
|
} else {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
}
|
}
|
||||||
this.fetchItems()
|
try {
|
||||||
.then((items) => {
|
const items = await this.fetchItems();
|
||||||
this.#setCachedItems(items);
|
this.#setCachedItems(items);
|
||||||
this.items = items;
|
this.items = items;
|
||||||
})
|
} catch (err) {
|
||||||
.finally(() => (this.loading = false));
|
// eslint-disable-next-line no-console
|
||||||
|
console.error(
|
||||||
|
`an error occurred when loading items for ${this.constructor.name}`,
|
||||||
|
err
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#getCachedItems() {
|
#getCachedItems() {
|
||||||
|
|||||||
@@ -45,31 +45,34 @@ export default class UserMenuMessagesList extends UserMenuNotificationsList {
|
|||||||
return this.currentUser.get(key) || 0;
|
return this.currentUser.get(key) || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchItems() {
|
async fetchItems() {
|
||||||
return ajax(
|
const data = await ajax(
|
||||||
`/u/${this.currentUser.username}/user-menu-private-messages`
|
`/u/${this.currentUser.username}/user-menu-private-messages`
|
||||||
).then(async (data) => {
|
);
|
||||||
const content = [];
|
const content = [];
|
||||||
data.notifications.forEach((rawNotification) => {
|
|
||||||
const notification = Notification.create(rawNotification);
|
const notifications = data.notifications.map((n) => Notification.create(n));
|
||||||
content.push(
|
await Notification.applyTransformations(notifications);
|
||||||
new UserMenuNotificationItem({
|
notifications.forEach((notification) => {
|
||||||
notification,
|
|
||||||
currentUser: this.currentUser,
|
|
||||||
siteSettings: this.siteSettings,
|
|
||||||
site: this.site,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
const topics = data.topics.map((t) => Topic.create(t));
|
|
||||||
await Topic.applyTransformations(topics);
|
|
||||||
content.push(
|
content.push(
|
||||||
...topics.map((topic) => {
|
new UserMenuNotificationItem({
|
||||||
return new UserMenuMessageItem({ message: topic });
|
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() {
|
dismissWarningModal() {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { postRNWebviewMessage } from "discourse/lib/utilities";
|
|||||||
import showModal from "discourse/lib/show-modal";
|
import showModal from "discourse/lib/show-modal";
|
||||||
import { inject as service } from "@ember/service";
|
import { inject as service } from "@ember/service";
|
||||||
import UserMenuNotificationItem from "discourse/lib/user-menu/notification-item";
|
import UserMenuNotificationItem from "discourse/lib/user-menu/notification-item";
|
||||||
|
import Notification from "discourse/models/notification";
|
||||||
|
|
||||||
export default class UserMenuNotificationsList extends UserMenuItemsList {
|
export default class UserMenuNotificationsList extends UserMenuItemsList {
|
||||||
@service currentUser;
|
@service currentUser;
|
||||||
@@ -54,7 +55,7 @@ export default class UserMenuNotificationsList extends UserMenuItemsList {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchItems() {
|
async fetchItems() {
|
||||||
const params = {
|
const params = {
|
||||||
limit: 30,
|
limit: 30,
|
||||||
recent: true,
|
recent: true,
|
||||||
@@ -67,19 +68,19 @@ export default class UserMenuNotificationsList extends UserMenuItemsList {
|
|||||||
params.filter_by_types = types.join(",");
|
params.filter_by_types = types.join(",");
|
||||||
params.silent = true;
|
params.silent = true;
|
||||||
}
|
}
|
||||||
return this.store
|
const collection = await this.store
|
||||||
.findStale("notification", params)
|
.findStale("notification", params)
|
||||||
.refresh()
|
.refresh();
|
||||||
.then((c) => {
|
const notifications = collection.content;
|
||||||
return c.content.map((notification) => {
|
await Notification.applyTransformations(notifications);
|
||||||
return new UserMenuNotificationItem({
|
return notifications.map((notification) => {
|
||||||
notification,
|
return new UserMenuNotificationItem({
|
||||||
currentUser: this.currentUser,
|
notification,
|
||||||
siteSettings: this.siteSettings,
|
currentUser: this.currentUser,
|
||||||
site: this.site,
|
siteSettings: this.siteSettings,
|
||||||
});
|
site: this.site,
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
dismissWarningModal() {
|
dismissWarningModal() {
|
||||||
|
|||||||
@@ -1952,7 +1952,7 @@ class PluginApi {
|
|||||||
* @callback registerModelTransformerCallback
|
* @callback registerModelTransformerCallback
|
||||||
* @param {Object[]} A list of model instances
|
* @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.
|
* @param {registerModelTransformerCallback} transformer - Callback function that receives a list of model objects of the specified type and applies transformation on them.
|
||||||
*/
|
*/
|
||||||
registerModelTransformer(modelName, transformer) {
|
registerModelTransformer(modelName, transformer) {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import getURL from "discourse-common/lib/get-url";
|
|||||||
import { longDate } from "discourse/lib/formatter";
|
import { longDate } from "discourse/lib/formatter";
|
||||||
import { none } from "@ember/object/computed";
|
import { none } from "@ember/object/computed";
|
||||||
import { capitalize } from "@ember/string";
|
import { capitalize } from "@ember/string";
|
||||||
|
import { applyModelTransformations } from "discourse/lib/model-transformers";
|
||||||
|
|
||||||
export const AUTO_DELETE_PREFERENCES = {
|
export const AUTO_DELETE_PREFERENCES = {
|
||||||
NEVER: 0,
|
NEVER: 0,
|
||||||
@@ -168,6 +169,10 @@ Bookmark.reopenClass({
|
|||||||
args.user = User.create(args.user);
|
args.user = User.create(args.user);
|
||||||
return this._super(args);
|
return this._super(args);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async applyTransformations(bookmarks) {
|
||||||
|
await applyModelTransformations("bookmark", bookmarks);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default Bookmark;
|
export default Bookmark;
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
import RestModel from "discourse/models/rest";
|
import RestModel from "discourse/models/rest";
|
||||||
import { tracked } from "@glimmer/tracking";
|
import { tracked } from "@glimmer/tracking";
|
||||||
|
import { applyModelTransformations } from "discourse/lib/model-transformers";
|
||||||
|
|
||||||
export default class Notification extends RestModel {
|
export default class Notification extends RestModel {
|
||||||
|
static async applyTransformations(notifications) {
|
||||||
|
await applyModelTransformations("notification", notifications);
|
||||||
|
}
|
||||||
|
|
||||||
@tracked read;
|
@tracked read;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
test("messages tab applies model transformations registered by plugins", async function (assert) {
|
||||||
withPluginApi("0.1", (api) => {
|
withPluginApi("0.1", (api) => {
|
||||||
api.registerModelTransformer("topic", (topics) => {
|
api.registerModelTransformer("topic", (topics) => {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export default {
|
|||||||
post_number: 1,
|
post_number: 1,
|
||||||
topic_id: 130,
|
topic_id: 130,
|
||||||
slug: "lorem-ipsum-dolor-sit-amet",
|
slug: "lorem-ipsum-dolor-sit-amet",
|
||||||
|
fancy_title: "edited topic 443",
|
||||||
data: {
|
data: {
|
||||||
topic_title: "edited topic 443",
|
topic_title: "edited topic 443",
|
||||||
display_username: "velesin",
|
display_username: "velesin",
|
||||||
@@ -26,6 +27,7 @@ export default {
|
|||||||
post_number: 1,
|
post_number: 1,
|
||||||
topic_id: 1234,
|
topic_id: 1234,
|
||||||
slug: "a-slug",
|
slug: "a-slug",
|
||||||
|
fancy_title: "some title",
|
||||||
data: { topic_title: "some title", display_username: "velesin" },
|
data: { topic_title: "some title", display_username: "velesin" },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user