diff --git a/app/assets/javascripts/discourse/app/components/relative-date.gjs b/app/assets/javascripts/discourse/app/components/relative-date.gjs
new file mode 100644
index 00000000000..7f7770ea8c5
--- /dev/null
+++ b/app/assets/javascripts/discourse/app/components/relative-date.gjs
@@ -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();
+ }
+
+
+
+ {{relativeAge this.datetime}}
+
+
+}
diff --git a/app/assets/javascripts/discourse/app/components/user-menu/menu-item.hbs b/app/assets/javascripts/discourse/app/components/user-menu/menu-item.hbs
index bd3ca7038c4..5c7c25ac939 100644
--- a/app/assets/javascripts/discourse/app/components/user-menu/menu-item.hbs
+++ b/app/assets/javascripts/discourse/app/components/user-menu/menu-item.hbs
@@ -24,5 +24,11 @@
{{/if}}
+
+
+ {{#if this.endComponent}}
+
+ {{/if}}
+
\ No newline at end of file
diff --git a/app/assets/javascripts/discourse/app/components/user-menu/menu-item.js b/app/assets/javascripts/discourse/app/components/user-menu/menu-item.js
index b6a728fc70d..b8481864f7d 100644
--- a/app/assets/javascripts/discourse/app/components/user-menu/menu-item.js
+++ b/app/assets/javascripts/discourse/app/components/user-menu/menu-item.js
@@ -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;
}
diff --git a/app/assets/javascripts/discourse/app/components/user-notifications-large.js b/app/assets/javascripts/discourse/app/components/user-notifications-large.js
deleted file mode 100644
index a12f68bad67..00000000000
--- a/app/assets/javascripts/discourse/app/components/user-notifications-large.js
+++ /dev/null
@@ -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();
- },
-});
diff --git a/app/assets/javascripts/discourse/app/controllers/user-notifications.gjs b/app/assets/javascripts/discourse/app/controllers/user-notifications.gjs
new file mode 100644
index 00000000000..aef0a45b3eb
--- /dev/null
+++ b/app/assets/javascripts/discourse/app/controllers/user-notifications.gjs
@@ -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:
+
+ ,
+ };
+ 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();
+ }
+}
diff --git a/app/assets/javascripts/discourse/app/controllers/user-notifications.js b/app/assets/javascripts/discourse/app/controllers/user-notifications.js
deleted file mode 100644
index 13958a47a47..00000000000
--- a/app/assets/javascripts/discourse/app/controllers/user-notifications.js
+++ /dev/null
@@ -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();
- },
- },
-});
diff --git a/app/assets/javascripts/discourse/app/lib/user-menu/notification-item.js b/app/assets/javascripts/discourse/app/lib/user-menu/notification-item.js
index caec6036122..55b299778e4 100644
--- a/app/assets/javascripts/discourse/app/lib/user-menu/notification-item.js
+++ b/app/assets/javascripts/discourse/app/lib/user-menu/notification-item.js
@@ -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;
diff --git a/app/assets/javascripts/discourse/app/templates/user/notifications-index.hbs b/app/assets/javascripts/discourse/app/templates/user/notifications-index.hbs
index a342f041a09..a70cd4372fa 100644
--- a/app/assets/javascripts/discourse/app/templates/user/notifications-index.hbs
+++ b/app/assets/javascripts/discourse/app/templates/user/notifications-index.hbs
@@ -22,7 +22,11 @@
{{#if this.nothingFound}}
{{i18n "notifications.empty"}}
{{else}}
-
-
+
+ {{#each this.items as |item|}}
+
+ {{/each}}
+
+
{{/if}}
{{/if}}
\ No newline at end of file
diff --git a/app/assets/javascripts/discourse/app/widgets/admin-problems-notification-item.js b/app/assets/javascripts/discourse/app/widgets/admin-problems-notification-item.js
deleted file mode 100644
index 97b006fb6dc..00000000000
--- a/app/assets/javascripts/discourse/app/widgets/admin-problems-notification-item.js
+++ /dev/null
@@ -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");
- },
-});
diff --git a/app/assets/javascripts/discourse/app/widgets/bookmark-reminder-notification-item.js b/app/assets/javascripts/discourse/app/widgets/bookmark-reminder-notification-item.js
deleted file mode 100644
index 1d50d21b91b..00000000000
--- a/app/assets/javascripts/discourse/app/widgets/bookmark-reminder-notification-item.js
+++ /dev/null
@@ -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 "";
- }
- },
- }
-);
diff --git a/app/assets/javascripts/discourse/app/widgets/custom-notification-item.js b/app/assets/javascripts/discourse/app/widgets/custom-notification-item.js
deleted file mode 100644
index 531710d5977..00000000000
--- a/app/assets/javascripts/discourse/app/widgets/custom-notification-item.js
+++ /dev/null
@@ -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}`);
- },
-});
diff --git a/app/assets/javascripts/discourse/app/widgets/group-message-summary-notification-item.js b/app/assets/javascripts/discourse/app/widgets/group-message-summary-notification-item.js
deleted file mode 100644
index f02b0cc45f7..00000000000
--- a/app/assets/javascripts/discourse/app/widgets/group-message-summary-notification-item.js
+++ /dev/null
@@ -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,
- });
- },
- }
-);
diff --git a/app/assets/javascripts/discourse/app/widgets/invitee-accepted-notification-item.js b/app/assets/javascripts/discourse/app/widgets/invitee-accepted-notification-item.js
deleted file mode 100644
index d405a84c779..00000000000
--- a/app/assets/javascripts/discourse/app/widgets/invitee-accepted-notification-item.js
+++ /dev/null
@@ -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);
- },
- }
-);
diff --git a/app/assets/javascripts/discourse/app/widgets/liked-consolidated-notification-item.js b/app/assets/javascripts/discourse/app/widgets/liked-consolidated-notification-item.js
deleted file mode 100644
index 5d42987ea83..00000000000
--- a/app/assets/javascripts/discourse/app/widgets/liked-consolidated-notification-item.js
+++ /dev/null
@@ -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);
- },
- }
-);
diff --git a/app/assets/javascripts/discourse/app/widgets/liked-notification-item.js b/app/assets/javascripts/discourse/app/widgets/liked-notification-item.js
deleted file mode 100644
index fdcb2337493..00000000000
--- a/app/assets/javascripts/discourse/app/widgets/liked-notification-item.js
+++ /dev/null
@@ -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: `${username}`,
- username2: `${username2}`,
- });
- } else {
- return I18n.t("notifications.liked_many", {
- description,
- username: `${username}`,
- count,
- });
- }
- }
-
- return I18n.t("notifications.liked", { description, username });
- },
-});
diff --git a/app/assets/javascripts/discourse/app/widgets/membership-request-accepted-notification-item.js b/app/assets/javascripts/discourse/app/widgets/membership-request-accepted-notification-item.js
deleted file mode 100644
index 973db4bacf8..00000000000
--- a/app/assets/javascripts/discourse/app/widgets/membership-request-accepted-notification-item.js
+++ /dev/null
@@ -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,
- });
- },
- }
-);
diff --git a/app/assets/javascripts/discourse/app/widgets/membership-request-consolidated-notification-item.js b/app/assets/javascripts/discourse/app/widgets/membership-request-consolidated-notification-item.js
deleted file mode 100644
index 2af849fa98e..00000000000
--- a/app/assets/javascripts/discourse/app/widgets/membership-request-consolidated-notification-item.js
+++ /dev/null
@@ -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),
- });
- },
- }
-);
diff --git a/app/assets/javascripts/discourse/app/widgets/new-features-notification-item.js b/app/assets/javascripts/discourse/app/widgets/new-features-notification-item.js
deleted file mode 100644
index 2eb74e2b835..00000000000
--- a/app/assets/javascripts/discourse/app/widgets/new-features-notification-item.js
+++ /dev/null
@@ -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");
- },
-});
diff --git a/app/assets/javascripts/discourse/app/widgets/user-notifications-large.js b/app/assets/javascripts/discourse/app/widgets/user-notifications-large.js
deleted file mode 100644
index 115871c256c..00000000000
--- a/app/assets/javascripts/discourse/app/widgets/user-notifications-large.js
+++ /dev/null
@@ -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);
- });
- },
-});
diff --git a/app/assets/javascripts/discourse/tests/acceptance/notifications-filter-test.js b/app/assets/javascripts/discourse/tests/acceptance/notifications-filter-test.js
deleted file mode 100644
index a23aa200a51..00000000000
--- a/app/assets/javascripts/discourse/tests/acceptance/notifications-filter-test.js
+++ /dev/null
@@ -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"));
- });
-});
diff --git a/app/assets/javascripts/discourse/tests/acceptance/user-test.js b/app/assets/javascripts/discourse/tests/acceptance/user-test.js
index 4d28c77012b..cfb5bc63af9 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/user-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/user-test.js
@@ -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 });
diff --git a/app/assets/stylesheets/common/base/menu-panel.scss b/app/assets/stylesheets/common/base/menu-panel.scss
index ed90ad22c2e..debdc020969 100644
--- a/app/assets/stylesheets/common/base/menu-panel.scss
+++ b/app/assets/stylesheets/common/base/menu-panel.scss
@@ -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 {
diff --git a/app/assets/stylesheets/common/base/user.scss b/app/assets/stylesheets/common/base/user.scss
index ccd0be6f304..83e15c83eb1 100644
--- a/app/assets/stylesheets/common/base/user.scss
+++ b/app/assets/stylesheets/common/base/user.scss
@@ -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%;
diff --git a/app/assets/stylesheets/common/components/user-stream-item.scss b/app/assets/stylesheets/common/components/user-stream-item.scss
index a1fef7daa2c..461462cdc74 100644
--- a/app/assets/stylesheets/common/components/user-stream-item.scss
+++ b/app/assets/stylesheets/common/components/user-stream-item.scss
@@ -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);
diff --git a/app/assets/stylesheets/desktop/components/_index.scss b/app/assets/stylesheets/desktop/components/_index.scss
index 72479eca716..83b8bf56112 100644
--- a/app/assets/stylesheets/desktop/components/_index.scss
+++ b/app/assets/stylesheets/desktop/components/_index.scss
@@ -2,4 +2,3 @@
@import "sidebar/edit-navigation-menu/tags-modal";
@import "user-card";
@import "user-info";
-@import "user-stream-item";
diff --git a/app/assets/stylesheets/desktop/components/user-stream-item.scss b/app/assets/stylesheets/desktop/components/user-stream-item.scss
deleted file mode 100644
index d094f02a64b..00000000000
--- a/app/assets/stylesheets/desktop/components/user-stream-item.scss
+++ /dev/null
@@ -1,8 +0,0 @@
-// Desktop styles for "user-stream-item" component
-.user-stream {
- .notification {
- &.unread {
- background-color: var(--tertiary-low);
- }
- }
-}
diff --git a/app/assets/stylesheets/mobile/components/user-stream-item.scss b/app/assets/stylesheets/mobile/components/user-stream-item.scss
index 676c8258227..7a941286971 100644
--- a/app/assets/stylesheets/mobile/components/user-stream-item.scss
+++ b/app/assets/stylesheets/mobile/components/user-stream-item.scss
@@ -4,12 +4,6 @@
vertical-align: middle;
}
- .notification {
- &.unread {
- background-color: var(--tertiary-low);
- }
- }
-
.group-member-info {
.name {
vertical-align: inherit;
diff --git a/plugins/chat/assets/javascripts/discourse/widgets/chat-invitation-notification-item.js b/plugins/chat/assets/javascripts/discourse/widgets/chat-invitation-notification-item.js
deleted file mode 100644
index 74c71ea5bb4..00000000000
--- a/plugins/chat/assets/javascripts/discourse/widgets/chat-invitation-notification-item.js
+++ /dev/null
@@ -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: `${text}
` });
- 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;
- },
-});
diff --git a/plugins/chat/assets/javascripts/discourse/widgets/chat-mention-notification-item.js b/plugins/chat/assets/javascripts/discourse/widgets/chat-mention-notification-item.js
deleted file mode 100644
index f7d0db7dbfb..00000000000
--- a/plugins/chat/assets/javascripts/discourse/widgets/chat-mention-notification-item.js
+++ /dev/null
@@ -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: `${text}
` });
- 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
-);
diff --git a/plugins/chat/test/javascripts/widgets/chat-invitation-notification-item-test.js b/plugins/chat/test/javascripts/widgets/chat-invitation-notification-item-test.js
deleted file mode 100644
index 5d0948ab683..00000000000
--- a/plugins/chat/test/javascripts/widgets/chat-invitation-notification-item-test.js
+++ /dev/null
@@ -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``
- );
-
- 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}`
- );
- });
- }
-);
diff --git a/plugins/chat/test/javascripts/widgets/chat-mention-notification-item-test.js b/plugins/chat/test/javascripts/widgets/chat-mention-notification-item-test.js
deleted file mode 100644
index 0912cd0cac4..00000000000
--- a/plugins/chat/test/javascripts/widgets/chat-mention-notification-item-test.js
+++ /dev/null
@@ -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``
- );
-
- 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``
- );
-
- 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``
- );
-
- 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}`
- );
- });
- }
-);
diff --git a/spec/system/page_objects/pages/user_notifications.rb b/spec/system/page_objects/pages/user_notifications.rb
new file mode 100644
index 00000000000..4500c05ff0f
--- /dev/null
+++ b/spec/system/page_objects/pages/user_notifications.rb
@@ -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
diff --git a/spec/system/user_page/user_notifications_spec.rb b/spec/system/user_page/user_notifications_spec.rb
new file mode 100644
index 00000000000..4a2d5d59be0
--- /dev/null
+++ b/spec/system/user_page/user_notifications_spec.rb
@@ -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