DEV: Render glimmer notification items for user notification list (#24802)
This removes the widget notifications list and renders the glimmer user menu notification items instead.
This commit is contained in:
parent
4904c2f11b
commit
223e413a6c
|
@ -0,0 +1,32 @@
|
||||||
|
import Component from "@glimmer/component";
|
||||||
|
import { longDate, relativeAge } from "discourse/lib/formatter";
|
||||||
|
|
||||||
|
export default class RelativeDate extends Component {
|
||||||
|
get datetime() {
|
||||||
|
if (this.memoizedDatetime) {
|
||||||
|
return this.memoizedDatetime;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.memoizedDatetime = new Date(this.args.date);
|
||||||
|
return this.memoizedDatetime;
|
||||||
|
}
|
||||||
|
|
||||||
|
get title() {
|
||||||
|
return longDate(this.datetime);
|
||||||
|
}
|
||||||
|
|
||||||
|
get time() {
|
||||||
|
return this.datetime.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<span
|
||||||
|
class="relative-date"
|
||||||
|
title={{this.title}}
|
||||||
|
data-time={{this.time}}
|
||||||
|
data-format="tiny"
|
||||||
|
>
|
||||||
|
{{relativeAge this.datetime}}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
}
|
|
@ -24,5 +24,11 @@
|
||||||
</span>
|
</span>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<PluginOutlet @name="menu-item-end" @outletArgs={{hash item=this}}>
|
||||||
|
{{#if this.endComponent}}
|
||||||
|
<this.endComponent />
|
||||||
|
{{/if}}
|
||||||
|
</PluginOutlet>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
|
@ -57,6 +57,10 @@ export default class UserMenuItem extends Component {
|
||||||
return this.#item.iconComponentArgs;
|
return this.#item.iconComponentArgs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get endComponent() {
|
||||||
|
return this.#item.endComponent;
|
||||||
|
}
|
||||||
|
|
||||||
get #item() {
|
get #item() {
|
||||||
return this.args.item;
|
return this.args.item;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
import MountWidget from "discourse/components/mount-widget";
|
|
||||||
import { observes } from "discourse-common/utils/decorators";
|
|
||||||
|
|
||||||
export default MountWidget.extend({
|
|
||||||
widget: "user-notifications-large",
|
|
||||||
notifications: null,
|
|
||||||
args: null,
|
|
||||||
|
|
||||||
init() {
|
|
||||||
this._super(...arguments);
|
|
||||||
|
|
||||||
this.args = { notifications: this.notifications };
|
|
||||||
},
|
|
||||||
|
|
||||||
@observes("notifications.length", "notifications.@each.read")
|
|
||||||
_triggerRefresh() {
|
|
||||||
this.set("args", {
|
|
||||||
notifications: this.notifications,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.queueRerender();
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
import Controller from "@ember/controller";
|
||||||
|
import { action } from "@ember/object";
|
||||||
|
import { inject as service } from "@ember/service";
|
||||||
|
import { htmlSafe } from "@ember/template";
|
||||||
|
import DismissNotificationConfirmationModal from "discourse/components/modal/dismiss-notification-confirmation";
|
||||||
|
import RelativeDate from "discourse/components/relative-date";
|
||||||
|
import { ajax } from "discourse/lib/ajax";
|
||||||
|
import UserMenuNotificationItem from "discourse/lib/user-menu/notification-item";
|
||||||
|
import getURL from "discourse-common/lib/get-url";
|
||||||
|
import { iconHTML } from "discourse-common/lib/icon-library";
|
||||||
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
|
import I18n from "discourse-i18n";
|
||||||
|
|
||||||
|
export default class UserNotificationsController extends Controller {
|
||||||
|
@service modal;
|
||||||
|
@service appEvents;
|
||||||
|
@service currentUser;
|
||||||
|
@service site;
|
||||||
|
@service siteSettings;
|
||||||
|
|
||||||
|
queryParams = ["filter"];
|
||||||
|
filter = "all";
|
||||||
|
|
||||||
|
get listContainerClassNames() {
|
||||||
|
return `user-notifications-list ${
|
||||||
|
this.siteSettings.show_user_menu_avatars ? "show-avatars" : ""
|
||||||
|
}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
@discourseComputed("filter")
|
||||||
|
isFiltered() {
|
||||||
|
return this.filter && this.filter !== "all";
|
||||||
|
}
|
||||||
|
|
||||||
|
@discourseComputed("model.content.@each")
|
||||||
|
items() {
|
||||||
|
return this.model.map((notification) => {
|
||||||
|
const props = {
|
||||||
|
appEvents: this.appEvents,
|
||||||
|
currentUser: this.currentUser,
|
||||||
|
siteSettings: this.siteSettings,
|
||||||
|
site: this.site,
|
||||||
|
notification,
|
||||||
|
endComponent: <template>
|
||||||
|
<RelativeDate @date={{notification.created_at}} />
|
||||||
|
</template>,
|
||||||
|
};
|
||||||
|
return new UserMenuNotificationItem(props);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@discourseComputed("model.content.@each.read")
|
||||||
|
allNotificationsRead() {
|
||||||
|
return !this.get("model.content").some(
|
||||||
|
(notification) => !notification.get("read")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@discourseComputed("isFiltered", "model.content.length")
|
||||||
|
doesNotHaveNotifications(isFiltered, contentLength) {
|
||||||
|
return !isFiltered && contentLength === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@discourseComputed("isFiltered", "model.content.length")
|
||||||
|
nothingFound(isFiltered, contentLength) {
|
||||||
|
return isFiltered && contentLength === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@discourseComputed()
|
||||||
|
emptyStateBody() {
|
||||||
|
return htmlSafe(
|
||||||
|
I18n.t("user.no_notifications_page_body", {
|
||||||
|
preferencesUrl: getURL("/my/preferences/notifications"),
|
||||||
|
icon: iconHTML("bell"),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async markRead() {
|
||||||
|
await ajax("/notifications/mark-read", { type: "PUT" });
|
||||||
|
this.model.forEach((notification) => notification.set("read", true));
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
async resetNew() {
|
||||||
|
if (this.currentUser.unread_high_priority_notifications > 0) {
|
||||||
|
this.modal.show(DismissNotificationConfirmationModal, {
|
||||||
|
model: {
|
||||||
|
confirmationMessage: I18n.t(
|
||||||
|
"notifications.dismiss_confirmation.body.default",
|
||||||
|
{
|
||||||
|
count: this.currentUser.unread_high_priority_notifications,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
dismissNotifications: () => this.markRead(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.markRead();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
loadMore() {
|
||||||
|
this.model.loadMore();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,76 +0,0 @@
|
||||||
import Controller from "@ember/controller";
|
|
||||||
import { inject as service } from "@ember/service";
|
|
||||||
import { htmlSafe } from "@ember/template";
|
|
||||||
import DismissNotificationConfirmationModal from "discourse/components/modal/dismiss-notification-confirmation";
|
|
||||||
import { ajax } from "discourse/lib/ajax";
|
|
||||||
import getURL from "discourse-common/lib/get-url";
|
|
||||||
import { iconHTML } from "discourse-common/lib/icon-library";
|
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
|
||||||
import I18n from "discourse-i18n";
|
|
||||||
|
|
||||||
export default Controller.extend({
|
|
||||||
modal: service(),
|
|
||||||
queryParams: ["filter"],
|
|
||||||
filter: "all",
|
|
||||||
|
|
||||||
@discourseComputed("filter")
|
|
||||||
isFiltered() {
|
|
||||||
return this.filter && this.filter !== "all";
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("model.content.@each.read")
|
|
||||||
allNotificationsRead() {
|
|
||||||
return !this.get("model.content").some(
|
|
||||||
(notification) => !notification.get("read")
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("isFiltered", "model.content.length")
|
|
||||||
doesNotHaveNotifications(isFiltered, contentLength) {
|
|
||||||
return !isFiltered && contentLength === 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("isFiltered", "model.content.length")
|
|
||||||
nothingFound(isFiltered, contentLength) {
|
|
||||||
return isFiltered && contentLength === 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed()
|
|
||||||
emptyStateBody() {
|
|
||||||
return htmlSafe(
|
|
||||||
I18n.t("user.no_notifications_page_body", {
|
|
||||||
preferencesUrl: getURL("/my/preferences/notifications"),
|
|
||||||
icon: iconHTML("bell"),
|
|
||||||
})
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
async markRead() {
|
|
||||||
await ajax("/notifications/mark-read", { type: "PUT" });
|
|
||||||
this.model.forEach((n) => n.set("read", true));
|
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
async resetNew() {
|
|
||||||
if (this.currentUser.unread_high_priority_notifications > 0) {
|
|
||||||
this.modal.show(DismissNotificationConfirmationModal, {
|
|
||||||
model: {
|
|
||||||
confirmationMessage: I18n.t(
|
|
||||||
"notifications.dismiss_confirmation.body.default",
|
|
||||||
{
|
|
||||||
count: this.currentUser.unread_high_priority_notifications,
|
|
||||||
}
|
|
||||||
),
|
|
||||||
dismissNotifications: () => this.markRead(),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.markRead();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
loadMore() {
|
|
||||||
this.model.loadMore();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -5,11 +5,19 @@ import UserMenuBaseItem from "discourse/lib/user-menu/base-item";
|
||||||
import getURL from "discourse-common/lib/get-url";
|
import getURL from "discourse-common/lib/get-url";
|
||||||
|
|
||||||
export default class UserMenuNotificationItem extends UserMenuBaseItem {
|
export default class UserMenuNotificationItem extends UserMenuBaseItem {
|
||||||
constructor({ notification, appEvents, currentUser, siteSettings, site }) {
|
constructor({
|
||||||
|
notification,
|
||||||
|
endComponent,
|
||||||
|
appEvents,
|
||||||
|
currentUser,
|
||||||
|
siteSettings,
|
||||||
|
site,
|
||||||
|
}) {
|
||||||
super(...arguments);
|
super(...arguments);
|
||||||
this.appEvents = appEvents;
|
this.appEvents = appEvents;
|
||||||
this.notification = notification;
|
|
||||||
this.currentUser = currentUser;
|
this.currentUser = currentUser;
|
||||||
|
this.endComponent = endComponent;
|
||||||
|
this.notification = notification;
|
||||||
this.siteSettings = siteSettings;
|
this.siteSettings = siteSettings;
|
||||||
this.site = site;
|
this.site = site;
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,11 @@
|
||||||
{{#if this.nothingFound}}
|
{{#if this.nothingFound}}
|
||||||
<div class="alert alert-info">{{i18n "notifications.empty"}}</div>
|
<div class="alert alert-info">{{i18n "notifications.empty"}}</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
<UserNotificationsLarge @notifications={{this.model}} />
|
<div class={{this.listContainerClassNames}}>
|
||||||
|
{{#each this.items as |item|}}
|
||||||
|
<UserMenu::MenuItem @item={{item}} />
|
||||||
|
{{/each}}
|
||||||
<ConditionalLoadingSpinner @condition={{this.loading}} />
|
<ConditionalLoadingSpinner @condition={{this.loading}} />
|
||||||
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
|
@ -1,19 +0,0 @@
|
||||||
import { DefaultNotificationItem } from "discourse/widgets/default-notification-item";
|
|
||||||
import { createWidgetFrom } from "discourse/widgets/widget";
|
|
||||||
import getURL from "discourse-common/lib/get-url";
|
|
||||||
import { iconNode } from "discourse-common/lib/icon-library";
|
|
||||||
import I18n from "discourse-i18n";
|
|
||||||
|
|
||||||
createWidgetFrom(DefaultNotificationItem, "admin-problems-notification-item", {
|
|
||||||
text() {
|
|
||||||
return I18n.t("notifications.admin_problems");
|
|
||||||
},
|
|
||||||
|
|
||||||
url() {
|
|
||||||
return getURL("/admin");
|
|
||||||
},
|
|
||||||
|
|
||||||
icon() {
|
|
||||||
return iconNode("gift");
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,34 +0,0 @@
|
||||||
import { formatUsername } from "discourse/lib/utilities";
|
|
||||||
import { DefaultNotificationItem } from "discourse/widgets/default-notification-item";
|
|
||||||
import { createWidgetFrom } from "discourse/widgets/widget";
|
|
||||||
import I18n from "discourse-i18n";
|
|
||||||
|
|
||||||
createWidgetFrom(
|
|
||||||
DefaultNotificationItem,
|
|
||||||
"bookmark-reminder-notification-item",
|
|
||||||
{
|
|
||||||
text(notificationName, data) {
|
|
||||||
const username = formatUsername(data.display_username);
|
|
||||||
const description = this.description(data);
|
|
||||||
|
|
||||||
return I18n.t("notifications.bookmark_reminder", {
|
|
||||||
description,
|
|
||||||
username,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
notificationTitle(notificationName, data) {
|
|
||||||
if (notificationName) {
|
|
||||||
if (data.bookmark_name) {
|
|
||||||
return I18n.t(`notifications.titles.${notificationName}_with_name`, {
|
|
||||||
name: data.bookmark_name,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return I18n.t(`notifications.titles.${notificationName}`);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
|
@ -1,22 +0,0 @@
|
||||||
import { formatUsername } from "discourse/lib/utilities";
|
|
||||||
import { DefaultNotificationItem } from "discourse/widgets/default-notification-item";
|
|
||||||
import { createWidgetFrom } from "discourse/widgets/widget";
|
|
||||||
import { iconNode } from "discourse-common/lib/icon-library";
|
|
||||||
import I18n from "discourse-i18n";
|
|
||||||
|
|
||||||
createWidgetFrom(DefaultNotificationItem, "custom-notification-item", {
|
|
||||||
notificationTitle(notificationName, data) {
|
|
||||||
return data.title ? I18n.t(data.title) : "";
|
|
||||||
},
|
|
||||||
|
|
||||||
text(notificationName, data) {
|
|
||||||
const username = formatUsername(data.display_username);
|
|
||||||
const description = this.description(data);
|
|
||||||
|
|
||||||
return I18n.t(data.message, { description, username });
|
|
||||||
},
|
|
||||||
|
|
||||||
icon(notificationName, data) {
|
|
||||||
return iconNode(`notification.${data.message}`);
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,19 +0,0 @@
|
||||||
import { DefaultNotificationItem } from "discourse/widgets/default-notification-item";
|
|
||||||
import { createWidgetFrom } from "discourse/widgets/widget";
|
|
||||||
import I18n from "discourse-i18n";
|
|
||||||
|
|
||||||
createWidgetFrom(
|
|
||||||
DefaultNotificationItem,
|
|
||||||
"group-message-summary-notification-item",
|
|
||||||
{
|
|
||||||
text(notificationName, data) {
|
|
||||||
const count = data.inbox_count;
|
|
||||||
const group_name = data.group_name;
|
|
||||||
|
|
||||||
return I18n.t("notifications.group_message_summary", {
|
|
||||||
count,
|
|
||||||
group_name,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
|
@ -1,13 +0,0 @@
|
||||||
import { userPath } from "discourse/lib/url";
|
|
||||||
import { DefaultNotificationItem } from "discourse/widgets/default-notification-item";
|
|
||||||
import { createWidgetFrom } from "discourse/widgets/widget";
|
|
||||||
|
|
||||||
createWidgetFrom(
|
|
||||||
DefaultNotificationItem,
|
|
||||||
"invitee-accepted-notification-item",
|
|
||||||
{
|
|
||||||
url(data) {
|
|
||||||
return userPath(data.display_username);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
|
@ -1,31 +0,0 @@
|
||||||
import { isEmpty } from "@ember/utils";
|
|
||||||
import { userPath } from "discourse/lib/url";
|
|
||||||
import { escapeExpression } from "discourse/lib/utilities";
|
|
||||||
import { DefaultNotificationItem } from "discourse/widgets/default-notification-item";
|
|
||||||
import { createWidgetFrom } from "discourse/widgets/widget";
|
|
||||||
import I18n from "discourse-i18n";
|
|
||||||
|
|
||||||
createWidgetFrom(
|
|
||||||
DefaultNotificationItem,
|
|
||||||
"liked-consolidated-notification-item",
|
|
||||||
{
|
|
||||||
url(data) {
|
|
||||||
return userPath(
|
|
||||||
`${
|
|
||||||
this.attrs.username || this.currentUser.username
|
|
||||||
}/notifications/likes-received?acting_username=${data.display_username}`
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
description(data) {
|
|
||||||
const description = I18n.t(
|
|
||||||
"notifications.liked_consolidated_description",
|
|
||||||
{
|
|
||||||
count: parseInt(data.count, 10),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return isEmpty(description) ? "" : escapeExpression(description);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
|
@ -1,32 +0,0 @@
|
||||||
import { formatUsername } from "discourse/lib/utilities";
|
|
||||||
import { DefaultNotificationItem } from "discourse/widgets/default-notification-item";
|
|
||||||
import { createWidgetFrom } from "discourse/widgets/widget";
|
|
||||||
import I18n from "discourse-i18n";
|
|
||||||
|
|
||||||
createWidgetFrom(DefaultNotificationItem, "liked-notification-item", {
|
|
||||||
text(notificationName, data) {
|
|
||||||
const username = formatUsername(data.display_username);
|
|
||||||
const description = this.description(data);
|
|
||||||
|
|
||||||
if (data.count > 1) {
|
|
||||||
const count = data.count - 1;
|
|
||||||
const username2 = formatUsername(data.username2);
|
|
||||||
|
|
||||||
if (count === 0) {
|
|
||||||
return I18n.t("notifications.liked_2", {
|
|
||||||
description,
|
|
||||||
username: `<span class="multi-username">${username}</span>`,
|
|
||||||
username2: `<span class="multi-username">${username2}</span>`,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return I18n.t("notifications.liked_many", {
|
|
||||||
description,
|
|
||||||
username: `<span class="multi-username">${username}</span>`,
|
|
||||||
count,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return I18n.t("notifications.liked", { description, username });
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,20 +0,0 @@
|
||||||
import { groupPath } from "discourse/lib/url";
|
|
||||||
import { DefaultNotificationItem } from "discourse/widgets/default-notification-item";
|
|
||||||
import { createWidgetFrom } from "discourse/widgets/widget";
|
|
||||||
import I18n from "discourse-i18n";
|
|
||||||
|
|
||||||
createWidgetFrom(
|
|
||||||
DefaultNotificationItem,
|
|
||||||
"membership-request-accepted-notification-item",
|
|
||||||
{
|
|
||||||
url(data) {
|
|
||||||
return groupPath(data.group_name);
|
|
||||||
},
|
|
||||||
|
|
||||||
text(notificationName, data) {
|
|
||||||
return I18n.t(`notifications.${notificationName}`, {
|
|
||||||
group_name: data.group_name,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
|
@ -1,23 +0,0 @@
|
||||||
import { userPath } from "discourse/lib/url";
|
|
||||||
import { DefaultNotificationItem } from "discourse/widgets/default-notification-item";
|
|
||||||
import { createWidgetFrom } from "discourse/widgets/widget";
|
|
||||||
import I18n from "discourse-i18n";
|
|
||||||
|
|
||||||
createWidgetFrom(
|
|
||||||
DefaultNotificationItem,
|
|
||||||
"membership-request-consolidated-notification-item",
|
|
||||||
{
|
|
||||||
url() {
|
|
||||||
return userPath(
|
|
||||||
`${this.attrs.username || this.currentUser.username}/messages`
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
text(notificationName, data) {
|
|
||||||
return I18n.t("notifications.membership_request_consolidated", {
|
|
||||||
group_name: data.group_name,
|
|
||||||
count: parseInt(data.count, 10),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
|
@ -1,19 +0,0 @@
|
||||||
import { DefaultNotificationItem } from "discourse/widgets/default-notification-item";
|
|
||||||
import { createWidgetFrom } from "discourse/widgets/widget";
|
|
||||||
import getURL from "discourse-common/lib/get-url";
|
|
||||||
import { iconNode } from "discourse-common/lib/icon-library";
|
|
||||||
import I18n from "discourse-i18n";
|
|
||||||
|
|
||||||
createWidgetFrom(DefaultNotificationItem, "new-features-notification-item", {
|
|
||||||
text() {
|
|
||||||
return I18n.t("notifications.new_features");
|
|
||||||
},
|
|
||||||
|
|
||||||
url() {
|
|
||||||
return getURL("/admin");
|
|
||||||
},
|
|
||||||
|
|
||||||
icon() {
|
|
||||||
return iconNode("gift");
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,48 +0,0 @@
|
||||||
import { dasherize } from "@ember/string";
|
|
||||||
import { h } from "virtual-dom";
|
|
||||||
import { dateNode } from "discourse/helpers/node";
|
|
||||||
import { createWidget } from "discourse/widgets/widget";
|
|
||||||
|
|
||||||
createWidget("large-notification-item", {
|
|
||||||
tagName: "li",
|
|
||||||
|
|
||||||
buildClasses(attrs) {
|
|
||||||
const result = ["item", "notification", "large-notification"];
|
|
||||||
if (!attrs.get("read")) {
|
|
||||||
result.push("unread");
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
},
|
|
||||||
|
|
||||||
html(attrs) {
|
|
||||||
const notificationName =
|
|
||||||
this.site.notificationLookup[attrs.notification_type];
|
|
||||||
|
|
||||||
return [
|
|
||||||
this.attach(
|
|
||||||
`${dasherize(notificationName)}-notification-item`,
|
|
||||||
attrs,
|
|
||||||
{},
|
|
||||||
{
|
|
||||||
fallbackWidgetName: "default-notification-item",
|
|
||||||
tagName: "div",
|
|
||||||
}
|
|
||||||
),
|
|
||||||
h("span.time", dateNode(attrs.created_at)),
|
|
||||||
];
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default createWidget("user-notifications-large", {
|
|
||||||
tagName: "ul.notifications.large-notifications",
|
|
||||||
|
|
||||||
html(attrs) {
|
|
||||||
const notifications = attrs.notifications;
|
|
||||||
const username = notifications.findArgs.username;
|
|
||||||
|
|
||||||
return notifications.map((n) => {
|
|
||||||
n.username = username;
|
|
||||||
return this.attach("large-notification-item", n);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,34 +0,0 @@
|
||||||
import { visit } from "@ember/test-helpers";
|
|
||||||
import { test } from "qunit";
|
|
||||||
import { acceptance, exists } from "discourse/tests/helpers/qunit-helpers";
|
|
||||||
import selectKit from "discourse/tests/helpers/select-kit-helper";
|
|
||||||
|
|
||||||
acceptance("Notifications filter", function (needs) {
|
|
||||||
needs.user();
|
|
||||||
|
|
||||||
test("Notifications filter true", async function (assert) {
|
|
||||||
await visit("/u/eviltrout/notifications");
|
|
||||||
|
|
||||||
assert.ok(exists(".large-notification"));
|
|
||||||
});
|
|
||||||
|
|
||||||
test("Notifications filter read", async function (assert) {
|
|
||||||
await visit("/u/eviltrout/notifications");
|
|
||||||
|
|
||||||
const dropdown = selectKit(".notifications-filter");
|
|
||||||
await dropdown.expand();
|
|
||||||
await dropdown.selectRowByValue("read");
|
|
||||||
|
|
||||||
assert.ok(exists(".large-notification"));
|
|
||||||
});
|
|
||||||
|
|
||||||
test("Notifications filter unread", async function (assert) {
|
|
||||||
await visit("/u/eviltrout/notifications");
|
|
||||||
|
|
||||||
const dropdown = selectKit(".notifications-filter");
|
|
||||||
await dropdown.expand();
|
|
||||||
await dropdown.selectRowByValue("unread");
|
|
||||||
|
|
||||||
assert.ok(exists(".large-notification"));
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -78,12 +78,10 @@ acceptance("User Routes", function (needs) {
|
||||||
"has the body class"
|
"has the body class"
|
||||||
);
|
);
|
||||||
|
|
||||||
const $links = queryAll(".item.notification a");
|
const $links = queryAll(".notification a");
|
||||||
|
|
||||||
assert.ok(
|
assert.ok(
|
||||||
$links[2].href.includes(
|
$links[2].href.includes("/u/eviltrout/notifications/likes-received")
|
||||||
"/u/eviltrout/notifications/likes-received?acting_username=aquaman"
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
updateCurrentUser({ moderator: true, admin: false });
|
updateCurrentUser({ moderator: true, admin: false });
|
||||||
|
|
|
@ -163,40 +163,6 @@
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
min-width: 0; // makes sure menu tabs don't go off screen
|
min-width: 0; // makes sure menu tabs don't go off screen
|
||||||
|
|
||||||
.double-user,
|
|
||||||
.multi-user {
|
|
||||||
white-space: unset;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-label {
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
color: var(--primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
background-color: var(--secondary);
|
|
||||||
|
|
||||||
&.unread,
|
|
||||||
&.pending {
|
|
||||||
background-color: var(--tertiary-low);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: var(--d-hover);
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:focus-within {
|
|
||||||
background: var(--d-hover);
|
|
||||||
a {
|
|
||||||
// we don't need the link focus because we're styling the parent
|
|
||||||
outline: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#quick-access-profile {
|
#quick-access-profile {
|
||||||
|
@ -380,8 +346,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-menu {
|
// Panel / user-notification-list styles. **not** menu panel sizing styles
|
||||||
.quick-access-panel {
|
.user-menu .quick-access-panel,
|
||||||
|
.user-notifications-list {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@ -390,9 +357,23 @@
|
||||||
border-top: 1px solid var(--primary-low);
|
border-top: 1px solid var(--primary-low);
|
||||||
padding-top: 0.75em;
|
padding-top: 0.75em;
|
||||||
margin-top: -1px;
|
margin-top: -1px;
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.double-user,
|
||||||
|
.multi-user {
|
||||||
|
white-space: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-label {
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
color: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
padding: 0 0.4em;
|
padding: 0 0.4em;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
@ -421,10 +402,27 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
li {
|
li {
|
||||||
background-color: var(--d-selected);
|
background-color: var(--secondary);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
|
|
||||||
|
&.unread,
|
||||||
|
&.pending {
|
||||||
|
background-color: var(--tertiary-low);
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--d-hover);
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus-within {
|
||||||
|
background: var(--d-hover);
|
||||||
|
a {
|
||||||
|
// we don't need the link focus because we're styling the parent
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This is until other languages remove the HTML from within
|
// This is until other languages remove the HTML from within
|
||||||
// notifications. It can then be removed
|
// notifications. It can then be removed
|
||||||
div .fa {
|
div .fa {
|
||||||
|
@ -555,10 +553,11 @@
|
||||||
}
|
}
|
||||||
/* as a big ol' click target, don't let text inside be selected */
|
/* as a big ol' click target, don't let text inside be selected */
|
||||||
@include unselectable;
|
@include unselectable;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-menu.show-avatars {
|
// Styles to have user avatar positioned and sized correctly
|
||||||
|
.user-menu.show-avatars,
|
||||||
|
.user-notifications-list.show-avatars {
|
||||||
li {
|
li {
|
||||||
a {
|
a {
|
||||||
.icon-avatar {
|
.icon-avatar {
|
||||||
|
|
|
@ -765,38 +765,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.large-notifications {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.large-notification {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
a {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.d-icon {
|
|
||||||
margin-right: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
span:first-child {
|
|
||||||
color: var(--primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Can remove this once other languages have removed html from i18n values
|
|
||||||
div {
|
|
||||||
.fa {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
p {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.second-factor {
|
.second-factor {
|
||||||
.second-factor-item {
|
.second-factor-item {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -46,7 +46,7 @@
|
||||||
color: var(--primary);
|
color: var(--primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.time,
|
.relative-date,
|
||||||
.delete-info,
|
.delete-info,
|
||||||
.draft-type {
|
.draft-type {
|
||||||
line-height: var(--line-height-small);
|
line-height: var(--line-height-small);
|
||||||
|
@ -69,10 +69,33 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.notification .time {
|
.user-notifications-list {
|
||||||
|
padding-top: 0;
|
||||||
|
|
||||||
|
li.notification {
|
||||||
|
padding: 0.25em 0;
|
||||||
|
border-bottom: 1px solid var(--primary-low);
|
||||||
|
|
||||||
|
a {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.relative-date {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
|
padding-top: 0;
|
||||||
float: none;
|
float: none;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
&:not(.show-avatars) {
|
||||||
|
li.notification {
|
||||||
|
padding: 0.75em 0;
|
||||||
|
|
||||||
|
.d-icon {
|
||||||
|
padding-top: 0;
|
||||||
|
font-size: var(--font-up-2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.expand-item,
|
.expand-item,
|
||||||
.collapse-item {
|
.collapse-item {
|
||||||
|
@ -102,27 +125,6 @@
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.notification {
|
|
||||||
li {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
display: inline-block;
|
|
||||||
|
|
||||||
span:first-child {
|
|
||||||
color: var(--primary);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// common/base/header.scss
|
|
||||||
.fa,
|
|
||||||
.icon {
|
|
||||||
color: var(--primary-med-or-secondary-med);
|
|
||||||
font-size: var(--font-up-4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.excerpt {
|
.excerpt {
|
||||||
margin: 1em 0 0 0;
|
margin: 1em 0 0 0;
|
||||||
font-size: var(--font-0);
|
font-size: var(--font-0);
|
||||||
|
|
|
@ -2,4 +2,3 @@
|
||||||
@import "sidebar/edit-navigation-menu/tags-modal";
|
@import "sidebar/edit-navigation-menu/tags-modal";
|
||||||
@import "user-card";
|
@import "user-card";
|
||||||
@import "user-info";
|
@import "user-info";
|
||||||
@import "user-stream-item";
|
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
// Desktop styles for "user-stream-item" component
|
|
||||||
.user-stream {
|
|
||||||
.notification {
|
|
||||||
&.unread {
|
|
||||||
background-color: var(--tertiary-low);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,12 +4,6 @@
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
.notification {
|
|
||||||
&.unread {
|
|
||||||
background-color: var(--tertiary-low);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.group-member-info {
|
.group-member-info {
|
||||||
.name {
|
.name {
|
||||||
vertical-align: inherit;
|
vertical-align: inherit;
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
import { h } from "virtual-dom";
|
|
||||||
import { formatUsername } from "discourse/lib/utilities";
|
|
||||||
import { DefaultNotificationItem } from "discourse/widgets/default-notification-item";
|
|
||||||
import RawHtml from "discourse/widgets/raw-html";
|
|
||||||
import { createWidgetFrom } from "discourse/widgets/widget";
|
|
||||||
import { iconNode } from "discourse-common/lib/icon-library";
|
|
||||||
import I18n from "discourse-i18n";
|
|
||||||
import slugifyChannel from "discourse/plugins/chat/discourse/lib/slugify-channel";
|
|
||||||
|
|
||||||
createWidgetFrom(DefaultNotificationItem, "chat-invitation-notification-item", {
|
|
||||||
services: ["chat", "router"],
|
|
||||||
|
|
||||||
text(data) {
|
|
||||||
const username = formatUsername(data.invited_by_username);
|
|
||||||
return I18n.t("notifications.chat_invitation_html", { username });
|
|
||||||
},
|
|
||||||
|
|
||||||
html(attrs) {
|
|
||||||
const notificationType = attrs.notification_type;
|
|
||||||
const lookup = this.site.get("notificationLookup");
|
|
||||||
const notificationName = lookup[notificationType];
|
|
||||||
const { data } = attrs;
|
|
||||||
const text = this.text(data);
|
|
||||||
const title = this.notificationTitle(notificationName, data);
|
|
||||||
const html = new RawHtml({ html: `<div>${text}</div>` });
|
|
||||||
const contents = [iconNode("link"), html];
|
|
||||||
const href = this.url(data);
|
|
||||||
|
|
||||||
return h(
|
|
||||||
"a",
|
|
||||||
{ attributes: { title, href, "data-auto-route": true } },
|
|
||||||
contents
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
url(data) {
|
|
||||||
const slug = slugifyChannel({
|
|
||||||
title: data.chat_channel_title,
|
|
||||||
slug: data.chat_channel_slug,
|
|
||||||
});
|
|
||||||
|
|
||||||
let url = `/chat/c/${slug || "-"}/${data.chat_channel_id}`;
|
|
||||||
|
|
||||||
if (data.chat_message_id) {
|
|
||||||
url += `/${data.chat_message_id}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return url;
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,72 +0,0 @@
|
||||||
import { h } from "virtual-dom";
|
|
||||||
import { formatUsername } from "discourse/lib/utilities";
|
|
||||||
import { DefaultNotificationItem } from "discourse/widgets/default-notification-item";
|
|
||||||
import RawHtml from "discourse/widgets/raw-html";
|
|
||||||
import { createWidgetFrom } from "discourse/widgets/widget";
|
|
||||||
import { iconNode } from "discourse-common/lib/icon-library";
|
|
||||||
import I18n from "discourse-i18n";
|
|
||||||
import slugifyChannel from "discourse/plugins/chat/discourse/lib/slugify-channel";
|
|
||||||
|
|
||||||
const chatNotificationItem = {
|
|
||||||
services: ["chat", "router"],
|
|
||||||
|
|
||||||
text(notificationName, data) {
|
|
||||||
const username = formatUsername(data.mentioned_by_username);
|
|
||||||
const identifier = data.identifier ? `@${data.identifier}` : null;
|
|
||||||
const i18nPrefix = data.is_direct_message_channel
|
|
||||||
? "notifications.popup.direct_message_chat_mention"
|
|
||||||
: "notifications.popup.chat_mention";
|
|
||||||
const i18nSuffix = identifier ? "other_html" : "direct_html";
|
|
||||||
|
|
||||||
return I18n.t(`${i18nPrefix}.${i18nSuffix}`, {
|
|
||||||
username,
|
|
||||||
identifier,
|
|
||||||
channel: data.chat_channel_title,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
html(attrs) {
|
|
||||||
const notificationType = attrs.notification_type;
|
|
||||||
const lookup = this.site.get("notificationLookup");
|
|
||||||
const notificationName = lookup[notificationType];
|
|
||||||
const { data } = attrs;
|
|
||||||
const title = this.notificationTitle(notificationName, data);
|
|
||||||
const text = this.text(notificationName, data);
|
|
||||||
const html = new RawHtml({ html: `<div>${text}</div>` });
|
|
||||||
const contents = [iconNode("d-chat"), html];
|
|
||||||
const href = this.url(data);
|
|
||||||
|
|
||||||
return h(
|
|
||||||
"a",
|
|
||||||
{ attributes: { title, href, "data-auto-route": true } },
|
|
||||||
contents
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
url(data) {
|
|
||||||
const slug = slugifyChannel({
|
|
||||||
title: data.chat_channel_title,
|
|
||||||
slug: data.chat_channel_slug,
|
|
||||||
});
|
|
||||||
|
|
||||||
let notificationRoute = `/chat/c/${slug || "-"}/${data.chat_channel_id}`;
|
|
||||||
if (data.chat_thread_id) {
|
|
||||||
notificationRoute += `/t/${data.chat_thread_id}`;
|
|
||||||
} else {
|
|
||||||
notificationRoute += `/${data.chat_message_id}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return notificationRoute;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
createWidgetFrom(
|
|
||||||
DefaultNotificationItem,
|
|
||||||
"chat-mention-notification-item",
|
|
||||||
chatNotificationItem
|
|
||||||
);
|
|
||||||
createWidgetFrom(
|
|
||||||
DefaultNotificationItem,
|
|
||||||
"chat-group-mention-notification-item",
|
|
||||||
chatNotificationItem
|
|
||||||
);
|
|
|
@ -1,52 +0,0 @@
|
||||||
import { render } from "@ember/test-helpers";
|
|
||||||
import hbs from "htmlbars-inline-precompile";
|
|
||||||
import { module, test } from "qunit";
|
|
||||||
import Notification from "discourse/models/notification";
|
|
||||||
import { NOTIFICATION_TYPES } from "discourse/tests/fixtures/concerns/notification-types";
|
|
||||||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
|
||||||
import { query } from "discourse/tests/helpers/qunit-helpers";
|
|
||||||
import { deepMerge } from "discourse-common/lib/object";
|
|
||||||
import slugifyChannel from "discourse/plugins/chat/discourse/lib/slugify-channel";
|
|
||||||
|
|
||||||
function getNotification(overrides = {}) {
|
|
||||||
return Notification.create(
|
|
||||||
deepMerge(
|
|
||||||
{
|
|
||||||
id: 11,
|
|
||||||
notification_type: NOTIFICATION_TYPES.chat_invitation,
|
|
||||||
read: false,
|
|
||||||
data: {
|
|
||||||
message: "chat.invitation_notification",
|
|
||||||
invited_by_username: "eviltrout",
|
|
||||||
chat_channel_id: 9,
|
|
||||||
chat_message_id: 2,
|
|
||||||
chat_channel_title: "Site",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
overrides
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
module(
|
|
||||||
"Discourse Chat | Widget | chat-invitation-notification-item",
|
|
||||||
function (hooks) {
|
|
||||||
setupRenderingTest(hooks);
|
|
||||||
|
|
||||||
test("notification url", async function (assert) {
|
|
||||||
this.set("args", getNotification());
|
|
||||||
|
|
||||||
await render(
|
|
||||||
hbs`<MountWidget @widget="chat-invitation-notification-item" @args={{this.args}} />`
|
|
||||||
);
|
|
||||||
|
|
||||||
const data = this.args.data;
|
|
||||||
assert.strictEqual(
|
|
||||||
query(".chat-invitation a").getAttribute("href"),
|
|
||||||
`/chat/c/${slugifyChannel({
|
|
||||||
title: data.chat_channel_title,
|
|
||||||
})}/${data.chat_channel_id}/${data.chat_message_id}`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
|
@ -1,139 +0,0 @@
|
||||||
import { render } from "@ember/test-helpers";
|
|
||||||
import hbs from "htmlbars-inline-precompile";
|
|
||||||
import { module, test } from "qunit";
|
|
||||||
import Notification from "discourse/models/notification";
|
|
||||||
import { NOTIFICATION_TYPES } from "discourse/tests/fixtures/concerns/notification-types";
|
|
||||||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
|
||||||
import { query } from "discourse/tests/helpers/qunit-helpers";
|
|
||||||
import { deepMerge } from "discourse-common/lib/object";
|
|
||||||
import I18n from "discourse-i18n";
|
|
||||||
import slugifyChannel from "discourse/plugins/chat/discourse/lib/slugify-channel";
|
|
||||||
|
|
||||||
function getNotification(overrides = {}) {
|
|
||||||
return Notification.create(
|
|
||||||
deepMerge(
|
|
||||||
{
|
|
||||||
id: 11,
|
|
||||||
notification_type: NOTIFICATION_TYPES.chat_invitation,
|
|
||||||
read: false,
|
|
||||||
data: {
|
|
||||||
message: "chat.mention_notification",
|
|
||||||
mentioned_by_username: "eviltrout",
|
|
||||||
chat_channel_id: 9,
|
|
||||||
chat_message_id: 2,
|
|
||||||
chat_channel_title: "Site",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
overrides
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
module(
|
|
||||||
"Discourse Chat | Widget | chat-mention-notification-item",
|
|
||||||
function (hooks) {
|
|
||||||
setupRenderingTest(hooks);
|
|
||||||
|
|
||||||
test("generated link", async function (assert) {
|
|
||||||
this.set("args", getNotification());
|
|
||||||
const data = this.args.data;
|
|
||||||
await render(
|
|
||||||
hbs`<MountWidget @widget="chat-mention-notification-item" @args={{this.args}} />`
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.strictEqual(
|
|
||||||
query(".chat-invitation a div").innerHTML.trim(),
|
|
||||||
I18n.t("notifications.popup.chat_mention.direct_html", {
|
|
||||||
username: "eviltrout",
|
|
||||||
identifier: null,
|
|
||||||
channel: "Site",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.strictEqual(
|
|
||||||
query(".chat-invitation a").getAttribute("href"),
|
|
||||||
`/chat/c/${slugifyChannel({
|
|
||||||
title: data.chat_channel_title,
|
|
||||||
})}/${data.chat_channel_id}/${data.chat_message_id}`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
module(
|
|
||||||
"Discourse Chat | Widget | chat-group-mention-notification-item",
|
|
||||||
function (hooks) {
|
|
||||||
setupRenderingTest(hooks);
|
|
||||||
|
|
||||||
test("generated link", async function (assert) {
|
|
||||||
this.set(
|
|
||||||
"args",
|
|
||||||
getNotification({
|
|
||||||
data: {
|
|
||||||
mentioned_by_username: "eviltrout",
|
|
||||||
identifier: "moderators",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
);
|
|
||||||
const data = this.args.data;
|
|
||||||
await render(
|
|
||||||
hbs`<MountWidget @widget="chat-group-mention-notification-item" @args={{this.args}} />`
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.strictEqual(
|
|
||||||
query(".chat-invitation a div").innerHTML.trim(),
|
|
||||||
I18n.t("notifications.popup.chat_mention.other_html", {
|
|
||||||
username: "eviltrout",
|
|
||||||
identifier: "@moderators",
|
|
||||||
channel: "Site",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.strictEqual(
|
|
||||||
query(".chat-invitation a").getAttribute("href"),
|
|
||||||
`/chat/c/${slugifyChannel({
|
|
||||||
title: data.chat_channel_title,
|
|
||||||
})}/${data.chat_channel_id}/${data.chat_message_id}`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
module(
|
|
||||||
"Discourse Chat | Widget | chat-group-mention-notification-item (@all)",
|
|
||||||
function (hooks) {
|
|
||||||
setupRenderingTest(hooks);
|
|
||||||
|
|
||||||
test("generated link", async function (assert) {
|
|
||||||
this.set(
|
|
||||||
"args",
|
|
||||||
getNotification({
|
|
||||||
data: {
|
|
||||||
mentioned_by_username: "eviltrout",
|
|
||||||
identifier: "all",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
);
|
|
||||||
const data = this.args.data;
|
|
||||||
await render(
|
|
||||||
hbs`<MountWidget @widget="chat-group-mention-notification-item" @args={{this.args}} />`
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.strictEqual(
|
|
||||||
query(".chat-invitation a div").innerHTML.trim(),
|
|
||||||
I18n.t("notifications.popup.chat_mention.other_html", {
|
|
||||||
username: "eviltrout",
|
|
||||||
identifier: "@all",
|
|
||||||
channel: "Site",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.strictEqual(
|
|
||||||
query(".chat-invitation a").getAttribute("href"),
|
|
||||||
`/chat/c/${slugifyChannel({
|
|
||||||
title: data.chat_channel_title,
|
|
||||||
})}/${data.chat_channel_id}/${data.chat_message_id}`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module PageObjects
|
||||||
|
module Pages
|
||||||
|
class UserNotifications < PageObjects::Pages::Base
|
||||||
|
def visit(user)
|
||||||
|
page.visit("/u/#{user.username}/notifications")
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def filter_dropdown
|
||||||
|
PageObjects::Components::SelectKit.new(".notifications-filter")
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_filter_value(value)
|
||||||
|
filter_dropdown.select_row_by_value(value)
|
||||||
|
end
|
||||||
|
|
||||||
|
def has_selected_filter_value?(value)
|
||||||
|
expect(filter_dropdown).to have_selected_value(value)
|
||||||
|
end
|
||||||
|
|
||||||
|
def has_notification?(notification)
|
||||||
|
page.has_css?(".notification a[href='#{notification.url}']")
|
||||||
|
end
|
||||||
|
|
||||||
|
def has_no_notification?(notification)
|
||||||
|
page.has_no_css?(".notification a[href='#{notification.url}']")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,31 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
describe "User notifications", type: :system do
|
||||||
|
fab!(:user)
|
||||||
|
let(:user_notifications_page) { PageObjects::Pages::UserNotifications.new }
|
||||||
|
|
||||||
|
fab!(:read_notification) { Fabricate(:notification, user: user, read: true) }
|
||||||
|
fab!(:unread_notification) { Fabricate(:notification, user: user, read: false) }
|
||||||
|
|
||||||
|
before { sign_in(user) }
|
||||||
|
|
||||||
|
describe "filtering" do
|
||||||
|
it "saves custom picture and system assigned pictures" do
|
||||||
|
user_notifications_page.visit(user)
|
||||||
|
user_notifications_page.filter_dropdown
|
||||||
|
expect(user_notifications_page).to have_selected_filter_value("all")
|
||||||
|
expect(user_notifications_page).to have_notification(read_notification)
|
||||||
|
expect(user_notifications_page).to have_notification(unread_notification)
|
||||||
|
|
||||||
|
user_notifications_page.set_filter_value("read")
|
||||||
|
|
||||||
|
expect(user_notifications_page).to have_notification(read_notification)
|
||||||
|
expect(user_notifications_page).to have_no_notification(unread_notification)
|
||||||
|
|
||||||
|
user_notifications_page.set_filter_value("unread")
|
||||||
|
|
||||||
|
expect(user_notifications_page).to have_no_notification(read_notification)
|
||||||
|
expect(user_notifications_page).to have_notification(unread_notification)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue