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>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<PluginOutlet @name="menu-item-end" @outletArgs={{hash item=this}}>
|
||||
{{#if this.endComponent}}
|
||||
<this.endComponent />
|
||||
{{/if}}
|
||||
</PluginOutlet>
|
||||
</a>
|
||||
</li>
|
|
@ -57,6 +57,10 @@ export default class UserMenuItem extends Component {
|
|||
return this.#item.iconComponentArgs;
|
||||
}
|
||||
|
||||
get endComponent() {
|
||||
return this.#item.endComponent;
|
||||
}
|
||||
|
||||
get #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";
|
||||
|
||||
export default class UserMenuNotificationItem extends UserMenuBaseItem {
|
||||
constructor({ notification, appEvents, currentUser, siteSettings, site }) {
|
||||
constructor({
|
||||
notification,
|
||||
endComponent,
|
||||
appEvents,
|
||||
currentUser,
|
||||
siteSettings,
|
||||
site,
|
||||
}) {
|
||||
super(...arguments);
|
||||
this.appEvents = appEvents;
|
||||
this.notification = notification;
|
||||
this.currentUser = currentUser;
|
||||
this.endComponent = endComponent;
|
||||
this.notification = notification;
|
||||
this.siteSettings = siteSettings;
|
||||
this.site = site;
|
||||
|
||||
|
|
|
@ -22,7 +22,11 @@
|
|||
{{#if this.nothingFound}}
|
||||
<div class="alert alert-info">{{i18n "notifications.empty"}}</div>
|
||||
{{else}}
|
||||
<UserNotificationsLarge @notifications={{this.model}} />
|
||||
<ConditionalLoadingSpinner @condition={{this.loading}} />
|
||||
<div class={{this.listContainerClassNames}}>
|
||||
{{#each this.items as |item|}}
|
||||
<UserMenu::MenuItem @item={{item}} />
|
||||
{{/each}}
|
||||
<ConditionalLoadingSpinner @condition={{this.loading}} />
|
||||
</div>
|
||||
{{/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"
|
||||
);
|
||||
|
||||
const $links = queryAll(".item.notification a");
|
||||
const $links = queryAll(".notification a");
|
||||
|
||||
assert.ok(
|
||||
$links[2].href.includes(
|
||||
"/u/eviltrout/notifications/likes-received?acting_username=aquaman"
|
||||
)
|
||||
$links[2].href.includes("/u/eviltrout/notifications/likes-received")
|
||||
);
|
||||
|
||||
updateCurrentUser({ moderator: true, admin: false });
|
||||
|
|
|
@ -163,40 +163,6 @@
|
|||
justify-content: space-between;
|
||||
box-sizing: border-box;
|
||||
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 {
|
||||
|
@ -380,185 +346,218 @@
|
|||
}
|
||||
}
|
||||
|
||||
.user-menu {
|
||||
.quick-access-panel {
|
||||
width: 100%;
|
||||
// Panel / user-notification-list styles. **not** menu panel sizing styles
|
||||
.user-menu .quick-access-panel,
|
||||
.user-notifications-list {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
max-height: 100%;
|
||||
border-top: 1px solid var(--primary-low);
|
||||
padding-top: 0.75em;
|
||||
margin-top: -1px;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.double-user,
|
||||
.multi-user {
|
||||
white-space: unset;
|
||||
}
|
||||
|
||||
.item-label {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
h3 {
|
||||
padding: 0 0.4em;
|
||||
font-weight: bold;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
|
||||
.d-icon,
|
||||
&:hover .d-icon {
|
||||
color: var(--primary-medium);
|
||||
}
|
||||
.icon {
|
||||
color: var(--primary-high);
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
.d-icon {
|
||||
color: var(--secondary);
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
flex-flow: column wrap;
|
||||
overflow: hidden;
|
||||
max-height: 100%;
|
||||
border-top: 1px solid var(--primary-low);
|
||||
padding-top: 0.75em;
|
||||
margin-top: -1px;
|
||||
&:focus {
|
||||
}
|
||||
|
||||
li {
|
||||
background-color: var(--secondary);
|
||||
box-sizing: border-box;
|
||||
list-style-type: none;
|
||||
|
||||
&.unread,
|
||||
&.pending {
|
||||
background-color: var(--tertiary-low);
|
||||
}
|
||||
&:hover {
|
||||
background-color: var(--d-hover);
|
||||
outline: none;
|
||||
}
|
||||
h3 {
|
||||
padding: 0 0.4em;
|
||||
font-weight: bold;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
|
||||
.d-icon,
|
||||
&:hover .d-icon {
|
||||
color: var(--primary-medium);
|
||||
}
|
||||
.icon {
|
||||
color: var(--primary-high);
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
.d-icon {
|
||||
color: var(--secondary);
|
||||
&:focus-within {
|
||||
background: var(--d-hover);
|
||||
a {
|
||||
// we don't need the link focus because we're styling the parent
|
||||
outline: 0;
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
// This is until other languages remove the HTML from within
|
||||
// notifications. It can then be removed
|
||||
div .fa {
|
||||
display: none;
|
||||
}
|
||||
|
||||
span.double-user,
|
||||
// e.g., "username, username2"
|
||||
span.multi-user
|
||||
// e.g., "username and n others"
|
||||
{
|
||||
display: inline;
|
||||
max-width: 100%;
|
||||
align-items: baseline;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
span.multi-user
|
||||
// e.g., "username, username2, and n others"
|
||||
{
|
||||
span.multi-username:nth-of-type(2) {
|
||||
// margin between username2 and "and n others"
|
||||
margin-right: 0.25em;
|
||||
}
|
||||
}
|
||||
|
||||
// truncate when usernames are very long
|
||||
span.multi-username {
|
||||
@include ellipsis;
|
||||
flex: 0 1 auto;
|
||||
min-width: 1.2em;
|
||||
max-width: 10em;
|
||||
&:nth-of-type(2) {
|
||||
// margin for comma between username and username2
|
||||
margin-left: 0.25em;
|
||||
}
|
||||
}
|
||||
|
||||
&: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;
|
||||
}
|
||||
.btn-flat:focus {
|
||||
// undo default btn-flat style
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
a,
|
||||
.profile-tab-btn {
|
||||
display: flex;
|
||||
flex-flow: column wrap;
|
||||
margin: 0.25em;
|
||||
padding: 0em 0.25em;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 0.25em 0.5em;
|
||||
}
|
||||
|
||||
a,
|
||||
button {
|
||||
> div {
|
||||
overflow: hidden; // clears the text from wrapping below icons
|
||||
overflow-wrap: anywhere;
|
||||
@supports not (overflow-wrap: anywhere) {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
// Truncate items with more than 2 lines.
|
||||
@include line-clamp(2);
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
li {
|
||||
background-color: var(--d-selected);
|
||||
box-sizing: border-box;
|
||||
list-style-type: none;
|
||||
|
||||
// This is until other languages remove the HTML from within
|
||||
// notifications. It can then be removed
|
||||
div .fa {
|
||||
display: none;
|
||||
}
|
||||
|
||||
span.double-user,
|
||||
// e.g., "username, username2"
|
||||
span.multi-user
|
||||
// e.g., "username and n others"
|
||||
{
|
||||
display: inline;
|
||||
max-width: 100%;
|
||||
align-items: baseline;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
span.multi-user
|
||||
// e.g., "username, username2, and n others"
|
||||
{
|
||||
span.multi-username:nth-of-type(2) {
|
||||
// margin between username2 and "and n others"
|
||||
margin-right: 0.25em;
|
||||
}
|
||||
}
|
||||
|
||||
// truncate when usernames are very long
|
||||
span.multi-username {
|
||||
@include ellipsis;
|
||||
flex: 0 1 auto;
|
||||
min-width: 1.2em;
|
||||
max-width: 10em;
|
||||
&:nth-of-type(2) {
|
||||
// margin for comma between username and username2
|
||||
margin-left: 0.25em;
|
||||
}
|
||||
}
|
||||
|
||||
&: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;
|
||||
}
|
||||
.btn-flat:focus {
|
||||
// undo default btn-flat style
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
a,
|
||||
.profile-tab-btn {
|
||||
display: flex;
|
||||
margin: 0.25em;
|
||||
padding: 0em 0.25em;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 0.25em 0.5em;
|
||||
}
|
||||
|
||||
a,
|
||||
button {
|
||||
> div {
|
||||
overflow: hidden; // clears the text from wrapping below icons
|
||||
overflow-wrap: anywhere;
|
||||
@supports not (overflow-wrap: anywhere) {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
// Truncate items with more than 2 lines.
|
||||
@include line-clamp(2);
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
li:not(.show-all) {
|
||||
padding: 0;
|
||||
align-self: flex-start;
|
||||
width: 100%;
|
||||
|
||||
.d-icon {
|
||||
padding-top: 0.2em;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
}
|
||||
.is-warning {
|
||||
.d-icon-envelope {
|
||||
color: var(--danger);
|
||||
}
|
||||
}
|
||||
.read {
|
||||
background-color: var(--secondary);
|
||||
}
|
||||
.none {
|
||||
padding-top: 5px;
|
||||
}
|
||||
.spinner-container {
|
||||
min-height: 2em;
|
||||
}
|
||||
.spinner {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-width: 2px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.show-all a {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 30px;
|
||||
color: var(--primary-med-or-secondary-high);
|
||||
background: var(--blend-primary-secondary-5);
|
||||
&:hover {
|
||||
color: var(--primary);
|
||||
background: var(--primary-low);
|
||||
}
|
||||
}
|
||||
/* as a big ol' click target, don't let text inside be selected */
|
||||
@include unselectable;
|
||||
}
|
||||
li:not(.show-all) {
|
||||
padding: 0;
|
||||
align-self: flex-start;
|
||||
width: 100%;
|
||||
|
||||
.d-icon {
|
||||
padding-top: 0.2em;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
}
|
||||
.is-warning {
|
||||
.d-icon-envelope {
|
||||
color: var(--danger);
|
||||
}
|
||||
}
|
||||
.read {
|
||||
background-color: var(--secondary);
|
||||
}
|
||||
.none {
|
||||
padding-top: 5px;
|
||||
}
|
||||
.spinner-container {
|
||||
min-height: 2em;
|
||||
}
|
||||
.spinner {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-width: 2px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.show-all a {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 30px;
|
||||
color: var(--primary-med-or-secondary-high);
|
||||
background: var(--blend-primary-secondary-5);
|
||||
&:hover {
|
||||
color: var(--primary);
|
||||
background: var(--primary-low);
|
||||
}
|
||||
}
|
||||
/* as a big ol' click target, don't let text inside be selected */
|
||||
@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 {
|
||||
a {
|
||||
.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-item {
|
||||
width: 100%;
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
color: var(--primary);
|
||||
}
|
||||
|
||||
.time,
|
||||
.relative-date,
|
||||
.delete-info,
|
||||
.draft-type {
|
||||
line-height: var(--line-height-small);
|
||||
|
@ -69,9 +69,32 @@
|
|||
}
|
||||
}
|
||||
|
||||
.notification .time {
|
||||
margin-left: auto;
|
||||
float: none;
|
||||
.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;
|
||||
padding-top: 0;
|
||||
float: none;
|
||||
}
|
||||
}
|
||||
&:not(.show-avatars) {
|
||||
li.notification {
|
||||
padding: 0.75em 0;
|
||||
|
||||
.d-icon {
|
||||
padding-top: 0;
|
||||
font-size: var(--font-up-2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.expand-item,
|
||||
|
@ -102,27 +125,6 @@
|
|||
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 {
|
||||
margin: 1em 0 0 0;
|
||||
font-size: var(--font-0);
|
||||
|
|
|
@ -2,4 +2,3 @@
|
|||
@import "sidebar/edit-navigation-menu/tags-modal";
|
||||
@import "user-card";
|
||||
@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;
|
||||
}
|
||||
|
||||
.notification {
|
||||
&.unread {
|
||||
background-color: var(--tertiary-low);
|
||||
}
|
||||
}
|
||||
|
||||
.group-member-info {
|
||||
.name {
|
||||
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