DEV: Add group messages and group_message_summary notifications in the messages tab in the user menu (#18390)

This commit adds non-archived group messages and `group_message_summary` notifications in the messages tab in the user menu. With this change, the messages tab in the user menu now includes 3 types of items:

1. Unread `private_message` notifications (notifications when you receive a reply in a PM)
2. Unread and read `group_message_summary` notifications (notifications when there's a new message in a group inbox that you track)
3. Non-archived personal and group messages

Unread `private_message` notifications are always shown first, followed by unread `group_message_summary` notifications, and then everything else (messages and read `group_message_summary` notifications) sorted by recency (most recent first).

Internal topic: t/72976.
This commit is contained in:
Osama Sayegh 2022-09-30 08:44:04 +03:00 committed by GitHub
parent 6ebd2cecda
commit 5a5625460b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 554 additions and 140 deletions

View File

@ -170,7 +170,7 @@ const CORE_TOP_TABS = [
}
get notificationTypes() {
return ["private_message"];
return ["private_message", "group_message_summary"];
}
get linkWhenActive() {

View File

@ -7,9 +7,21 @@ import UserMenuNotificationItem from "discourse/lib/user-menu/notification-item"
import UserMenuMessageItem from "discourse/lib/user-menu/message-item";
import Topic from "discourse/models/topic";
function parseDateString(date) {
if (date) {
return new Date(date);
}
}
async function initializeNotifications(rawList) {
const notifications = rawList.map((n) => Notification.create(n));
await Notification.applyTransformations(notifications);
return notifications;
}
export default class UserMenuMessagesList extends UserMenuNotificationsList {
get dismissTypes() {
return ["private_message"];
return this.filterByTypes;
}
get showAllHref() {
@ -51,9 +63,10 @@ export default class UserMenuMessagesList extends UserMenuNotificationsList {
);
const content = [];
const notifications = data.notifications.map((n) => Notification.create(n));
await Notification.applyTransformations(notifications);
notifications.forEach((notification) => {
const unreadNotifications = await initializeNotifications(
data.unread_notifications
);
unreadNotifications.forEach((notification) => {
content.push(
new UserMenuNotificationItem({
notification,
@ -66,12 +79,39 @@ export default class UserMenuMessagesList extends UserMenuNotificationsList {
const topics = data.topics.map((t) => Topic.create(t));
await Topic.applyTransformations(topics);
content.push(
...topics.map((topic) => {
return new UserMenuMessageItem({ message: topic });
})
const readNotifications = await initializeNotifications(
data.read_notifications
);
let latestReadNotificationDate = parseDateString(
readNotifications[0]?.created_at
);
let latestMessageDate = parseDateString(topics[0]?.bumped_at);
while (latestReadNotificationDate || latestMessageDate) {
if (
!latestReadNotificationDate ||
(latestMessageDate && latestReadNotificationDate < latestMessageDate)
) {
content.push(new UserMenuMessageItem({ message: topics[0] }));
topics.shift();
latestMessageDate = parseDateString(topics[0]?.bumped_at);
} else {
content.push(
new UserMenuNotificationItem({
notification: readNotifications[0],
currentUser: this.currentUser,
siteSettings: this.siteSettings,
site: this.site,
})
);
readNotifications.shift();
latestReadNotificationDate = parseDateString(
readNotifications[0]?.created_at
);
}
}
return content;
}

View File

@ -805,7 +805,7 @@ acceptance("User menu - Dismiss button", function (needs) {
const copy = cloneJSON(
UserMenuFixtures["/u/:username/user-menu-private-messages"]
);
copy.notifications = [];
copy.unread_notifications = [];
return helper.response(copy);
} else {
return helper.response(
@ -941,7 +941,7 @@ acceptance("User menu - Dismiss button", function (needs) {
assert.ok(markRead, "mark-read request is sent");
assert.strictEqual(
markReadRequestBody,
"dismiss_types=private_message",
"dismiss_types=private_message%2Cgroup_message_summary",
"mark-read request specifies private_message types"
);
assert.notOk(exists(".user-menu .notifications-dismiss"));

View File

@ -64,79 +64,80 @@ export default {
],
},
"/u/:username/user-menu-private-messages": {
notifications: [
{
id: 8315,
user_id: 1,
notification_type: 6,
read: false,
high_priority: true,
created_at: "2022-08-05T17:27:24.873Z",
post_number: 1,
topic_id: 249,
fancy_title: "Very secret message!",
slug: "very-secret-message",
data: {
topic_title: "very secret message!",
original_post_id: 1043,
original_post_type: 1,
original_username: "osama",
revision_number: null,
display_username: "osama"
},
unread_notifications: [
{
id: 8315,
user_id: 1,
notification_type: 6,
read: false,
high_priority: true,
created_at: "2022-08-05T17:27:24.873Z",
post_number: 1,
topic_id: 249,
fancy_title: "Very secret message!",
slug: "very-secret-message",
data: {
topic_title: "very secret message!",
original_post_id: 1043,
original_post_type: 1,
original_username: "osama",
revision_number: null,
display_username: "osama"
},
],
topics: [
{
id: 8092,
title: "BUG: Can not render emoji properly :/",
fancy_title: "BUG: Can not render emoji properly :confused:",
slug: "bug-can-not-render-emoji-properly",
posts_count: 1,
reply_count: 0,
highest_post_number: 2,
image_url: null,
created_at: "2019-07-26T01:29:24.008Z",
last_posted_at: "2019-07-26T01:29:24.177Z",
bumped: true,
bumped_at: "2019-07-26T01:29:24.177Z",
unseen: false,
last_read_post_number: 2,
unread_posts: 0,
pinned: false,
unpinned: null,
visible: true,
closed: false,
archived: false,
notification_level: 3,
bookmarked: false,
bookmarks: [],
liked: false,
views: 5,
like_count: 0,
has_summary: false,
archetype: "private_message",
last_poster_username: "mixtape",
category_id: null,
pinned_globally: false,
featured_link: null,
posters: [
{
extras: "latest single",
description: "Original Poster, Most Recent Poster",
user_id: 13,
primary_group_id: null,
},
],
participants: [
{
extras: "latest",
description: null,
user_id: 13,
primary_group_id: null,
},
],
}
],
},
],
topics: [
{
id: 8092,
title: "BUG: Can not render emoji properly :/",
fancy_title: "BUG: Can not render emoji properly :confused:",
slug: "bug-can-not-render-emoji-properly",
posts_count: 1,
reply_count: 0,
highest_post_number: 2,
image_url: null,
created_at: "2019-07-26T01:29:24.008Z",
last_posted_at: "2019-07-26T01:29:24.177Z",
bumped: true,
bumped_at: "2019-07-26T01:29:24.177Z",
unseen: false,
last_read_post_number: 2,
unread_posts: 0,
pinned: false,
unpinned: null,
visible: true,
closed: false,
archived: false,
notification_level: 3,
bookmarked: false,
bookmarks: [],
liked: false,
views: 5,
like_count: 0,
has_summary: false,
archetype: "private_message",
last_poster_username: "mixtape",
category_id: null,
pinned_globally: false,
featured_link: null,
posters: [
{
extras: "latest single",
description: "Original Poster, Most Recent Poster",
user_id: 13,
primary_group_id: null,
},
],
participants: [
{
extras: "latest",
description: null,
user_id: 13,
primary_group_id: null,
},
],
}
],
read_notifications: [],
}
}

View File

@ -5,14 +5,128 @@ import { render, settled } from "@ember/test-helpers";
import { NOTIFICATION_TYPES } from "discourse/tests/fixtures/concerns/notification-types";
import { hbs } from "ember-cli-htmlbars";
import pretender, { response } from "discourse/tests/helpers/create-pretender";
import { cloneJSON, deepMerge } from "discourse-common/lib/object";
import UserMenuFixtures from "discourse/tests/fixtures/user-menu";
import I18n from "I18n";
function getMessage(overrides = {}) {
return deepMerge(
{
id: 8092,
title: "Test ToPic 4422",
fancy_title: "Test topic 4422",
slug: "test-topic-4422",
posts_count: 1,
reply_count: 0,
highest_post_number: 2,
image_url: null,
created_at: "2019-07-26T01:29:24.008Z",
last_posted_at: "2019-07-26T01:29:24.177Z",
bumped: true,
bumped_at: "2019-07-26T01:29:24.177Z",
unseen: false,
last_read_post_number: 2,
unread_posts: 0,
pinned: false,
unpinned: null,
visible: true,
closed: false,
archived: false,
notification_level: 3,
bookmarked: false,
bookmarks: [],
liked: false,
views: 5,
like_count: 0,
has_summary: false,
archetype: "private_message",
last_poster_username: "mixtape",
category_id: null,
pinned_globally: false,
featured_link: null,
posters: [
{
extras: "latest single",
description: "Original Poster, Most Recent Poster",
user_id: 13,
primary_group_id: null,
},
],
participants: [
{
extras: "latest",
description: null,
user_id: 13,
primary_group_id: null,
},
],
},
overrides
);
}
function getGroupMessageSummaryNotification(overrides = {}) {
return deepMerge(
{
id: 9492,
user_id: 1,
notification_type: 16,
read: true,
high_priority: false,
created_at: "2022-08-05T17:27:24.873Z",
post_number: null,
topic_id: null,
fancy_title: null,
slug: null,
data: {
group_id: 1,
group_name: "jokers",
inbox_count: 4,
username: "joker.leader",
},
},
overrides
);
}
module("Integration | Component | user-menu | messages-list", function (hooks) {
setupRenderingTest(hooks);
const template = hbs`<UserMenu::MessagesList/>`;
test("renders notifications on top and messages on bottom", async function (assert) {
test("renders unread PM notifications first followed by messages and read group_message_summary notifications", async function (assert) {
pretender.get("/u/eviltrout/user-menu-private-messages", () => {
const copy = cloneJSON(
UserMenuFixtures["/u/:username/user-menu-private-messages"]
);
copy.read_notifications = [getGroupMessageSummaryNotification()];
return response(copy);
});
await render(template);
const items = queryAll("ul li");
assert.strictEqual(items.length, 3);
assert.ok(items[0].classList.contains("notification"));
assert.ok(items[0].classList.contains("unread"));
assert.ok(items[0].classList.contains("private-message"));
assert.ok(items[1].classList.contains("notification"));
assert.ok(items[1].classList.contains("read"));
assert.ok(items[1].classList.contains("group-message-summary"));
assert.ok(items[2].classList.contains("message"));
});
test("does not error when there are no group_message_summary notifications", async function (assert) {
pretender.get("/u/eviltrout/user-menu-private-messages", () => {
const copy = cloneJSON(
UserMenuFixtures["/u/:username/user-menu-private-messages"]
);
copy.read_notifications = [];
return response(copy);
});
await render(template);
const items = queryAll("ul li");
@ -25,6 +139,117 @@ module("Integration | Component | user-menu | messages-list", function (hooks) {
assert.ok(items[1].classList.contains("message"));
});
test("does not error when there are no messages", async function (assert) {
pretender.get("/u/eviltrout/user-menu-private-messages", () => {
const copy = cloneJSON(
UserMenuFixtures["/u/:username/user-menu-private-messages"]
);
copy.topics = [];
copy.read_notifications = [getGroupMessageSummaryNotification()];
return response(copy);
});
await render(template);
const items = queryAll("ul li");
assert.strictEqual(items.length, 2);
assert.ok(items[0].classList.contains("notification"));
assert.ok(items[0].classList.contains("unread"));
assert.ok(items[0].classList.contains("private-message"));
assert.ok(items[1].classList.contains("notification"));
assert.ok(items[1].classList.contains("read"));
assert.ok(items[1].classList.contains("group-message-summary"));
});
test("merge-sorts group_message_summary notifications and messages", async function (assert) {
pretender.get("/u/eviltrout/user-menu-private-messages", () => {
const copy = cloneJSON(
UserMenuFixtures["/u/:username/user-menu-private-messages"]
);
copy.unread_notifications = [];
copy.topics = [
getMessage({
bumped_at: "2014-07-26T01:29:24.177Z",
fancy_title: "Test Topic 0003",
}),
getMessage({
bumped_at: "2012-07-26T01:29:24.177Z",
fancy_title: "Test Topic 0002",
}),
getMessage({
bumped_at: "2010-07-26T01:29:24.177Z",
fancy_title: "Test Topic 0001",
}),
];
copy.read_notifications = [
getGroupMessageSummaryNotification({
created_at: "2015-07-26T01:29:24.177Z",
data: {
inbox_count: 13,
},
}),
getGroupMessageSummaryNotification({
created_at: "2013-07-26T01:29:24.177Z",
data: {
inbox_count: 12,
},
}),
getGroupMessageSummaryNotification({
created_at: "2011-07-26T01:29:24.177Z",
data: {
inbox_count: 11,
},
}),
];
return response(copy);
});
await render(template);
const items = queryAll("ul li");
assert.strictEqual(items.length, 6);
assert.strictEqual(
items[0].textContent.trim(),
I18n.t("notifications.group_message_summary", {
count: 13,
group_name: "jokers",
})
);
assert.strictEqual(
items[1].textContent.trim().replaceAll(/\s+/g, " "),
"mixtape Test Topic 0003"
);
assert.strictEqual(
items[2].textContent.trim(),
I18n.t("notifications.group_message_summary", {
count: 12,
group_name: "jokers",
})
);
assert.strictEqual(
items[3].textContent.trim().replaceAll(/\s+/g, " "),
"mixtape Test Topic 0002"
);
assert.strictEqual(
items[4].textContent.trim(),
I18n.t("notifications.group_message_summary", {
count: 11,
group_name: "jokers",
})
);
assert.strictEqual(
items[5].textContent.trim().replaceAll(/\s+/g, " "),
"mixtape Test Topic 0001"
);
});
test("show all link", async function (assert) {
await render(template);
const link = query(".panel-body-bottom .show-all");
@ -66,7 +291,11 @@ module("Integration | Component | user-menu | messages-list", function (hooks) {
test("empty state (aka blank page syndrome)", async function (assert) {
pretender.get("/u/eviltrout/user-menu-private-messages", () => {
return response({ notifications: [], topics: [] });
return response({
unread_notifications: [],
topics: [],
read_notifications: [],
});
});
await render(template);
assert.strictEqual(

View File

@ -1746,11 +1746,10 @@ class UsersController < ApplicationController
raise Discourse::InvalidAccess.new("username doesn't match current_user's username")
end
reminder_notifications = Notification.unread_type(
current_user,
Notification.types[:bookmark_reminder],
USER_MENU_LIST_LIMIT
)
reminder_notifications = Notification
.for_user_menu(current_user.id, limit: USER_MENU_LIST_LIMIT)
.unread
.where(notification_type: Notification.types[:bookmark_reminder])
if reminder_notifications.size < USER_MENU_LIST_LIMIT
exclude_bookmark_ids = reminder_notifications
@ -1803,29 +1802,37 @@ class UsersController < ApplicationController
raise Discourse::InvalidAccess.new("personal messages are disabled.")
end
message_notifications = Notification.unread_type(
current_user,
Notification.types[:private_message],
USER_MENU_LIST_LIMIT
)
unread_notifications = Notification
.for_user_menu(current_user.id, limit: USER_MENU_LIST_LIMIT)
.unread
.where(notification_type: [Notification.types[:private_message], Notification.types[:group_message_summary]])
.to_a
if message_notifications.size < USER_MENU_LIST_LIMIT
exclude_topic_ids = message_notifications.map(&:topic_id).uniq
if unread_notifications.size < USER_MENU_LIST_LIMIT
exclude_topic_ids = unread_notifications.filter_map(&:topic_id).uniq
limit = USER_MENU_LIST_LIMIT - unread_notifications.size
messages_list = TopicQuery.new(
current_user,
per_page: USER_MENU_LIST_LIMIT - message_notifications.size
).list_private_messages(current_user) do |query|
per_page: limit
).list_private_messages_direct_and_groups(current_user) do |query|
if exclude_topic_ids.present?
query.where("topics.id NOT IN (?)", exclude_topic_ids)
else
query
end
end
read_notifications = Notification
.for_user_menu(current_user.id, limit: limit)
.where(
read: true,
notification_type: Notification.types[:group_message_summary],
)
.to_a
end
if message_notifications.present?
serialized_notifications = ActiveModel::ArraySerializer.new(
message_notifications,
if unread_notifications.present?
serialized_unread_notifications = ActiveModel::ArraySerializer.new(
unread_notifications,
each_serializer: NotificationSerializer,
scope: guardian
)
@ -1840,8 +1847,17 @@ class UsersController < ApplicationController
)[:topics]
end
if read_notifications.present?
serialized_read_notifications = ActiveModel::ArraySerializer.new(
read_notifications,
each_serializer: NotificationSerializer,
scope: guardian
)
end
render json: {
notifications: serialized_notifications || [],
unread_notifications: serialized_unread_notifications || [],
read_notifications: serialized_read_notifications || [],
topics: serialized_messages || []
}
end
@ -2027,13 +2043,4 @@ class UsersController < ApplicationController
{ users: ActiveModel::ArraySerializer.new(users, each_serializer: each_serializer).as_json }
end
def find_unread_notifications_of_type(type, limit)
current_user
.notifications
.visible
.includes(:topic)
.where(read: false, notification_type: type)
.limit(limit)
end
end

View File

@ -15,14 +15,26 @@ class Notification < ActiveRecord::Base
scope :recent, lambda { |n = nil| n ||= 10; order('notifications.created_at desc').limit(n) }
scope :visible , lambda { joins('LEFT JOIN topics ON notifications.topic_id = topics.id')
.where('topics.id IS NULL OR topics.deleted_at IS NULL') }
scope :unread_type, ->(user, type, limit = 20) do
where(user_id: user.id, read: false, notification_type: type).visible.includes(:topic).limit(limit)
scope :unread_type, ->(user, type, limit = 30) do
unread_types(user, [type], limit)
end
scope :prioritized, ->(limit = nil) do
scope :unread_types, ->(user, types, limit = 30) do
where(user_id: user.id, read: false, notification_type: types)
.visible
.includes(:topic)
.limit(limit)
end
scope :prioritized, ->() do
order("notifications.high_priority AND NOT notifications.read DESC")
.order("NOT notifications.read DESC")
.order("notifications.created_at DESC")
.limit(limit || 30)
end
scope :for_user_menu, ->(user_id, limit: 30) do
where(user_id: user_id)
.visible
.prioritized
.includes(:topic)
.limit(limit)
end
attr_accessor :skip_send_email
@ -226,7 +238,8 @@ class Notification < ActiveRecord::Base
notifications = user.notifications
.includes(:topic)
.visible
.prioritized(count)
.prioritized
.limit(count)
if types.present?
notifications = notifications.where(notification_type: types)

View File

@ -5,14 +5,16 @@ class TopicQuery
def list_private_messages(user, &blk)
list = private_messages_for(user, :user)
list = not_archived(list, user)
list = have_posts_from_others(list, user)
list = list.where(<<~SQL)
NOT (
topics.participant_count = 1
AND topics.user_id = #{user.id.to_i}
AND topics.moderator_posts_count = 0
)
SQL
create_list(:private_messages, {}, list, &blk)
end
def list_private_messages_direct_and_groups(user, &blk)
list = private_messages_for(user, :all)
list = not_archived(list, user)
list = not_archived_in_groups(list)
list = have_posts_from_others(list, user)
create_list(:private_messages, {}, list, &blk)
end
@ -252,6 +254,20 @@ class TopicQuery
.where('um.user_id IS NULL')
end
def not_archived_in_groups(list)
list.left_joins(:group_archived_messages).where(group_archived_messages: { id: nil })
end
def have_posts_from_others(list, user)
list.where(<<~SQL)
NOT (
topics.participant_count = 1
AND topics.user_id = #{user.id.to_i}
AND topics.moderator_posts_count = 0
)
SQL
end
def group
@group ||= begin
Group

View File

@ -143,6 +143,25 @@ Fabricator(:private_message_post, from: :post) do
raw "Ssshh! This is our secret conversation!"
end
Fabricator(:group_private_message_post, from: :post) do
transient :recipients
user
topic do |attrs|
Fabricate(:private_message_topic,
user: attrs[:user],
created_at: attrs[:created_at],
subtype: TopicSubtype.user_to_user,
topic_allowed_users: [
Fabricate.build(:topic_allowed_user, user: attrs[:user]),
],
topic_allowed_groups: [
Fabricate.build(:topic_allowed_group, group: attrs[:recipients] || Fabricate(:group))
]
)
end
raw "Ssshh! This is our group secret conversation!"
end
Fabricator(:private_message_post_one_user, from: :post) do
user
topic do |attrs|

View File

@ -5797,9 +5797,21 @@ RSpec.describe UsersController do
end
describe "#user_menu_messages" do
fab!(:group1) { Fabricate(:group, has_messages: true, users: [user]) }
fab!(:group2) { Fabricate(:group, has_messages: true, users: [user, user1]) }
fab!(:group3) { Fabricate(:group, has_messages: true, users: [user1]) }
fab!(:message_without_notification) { Fabricate(:private_message_post, recipient: user).topic }
fab!(:message_with_read_notification) { Fabricate(:private_message_post, recipient: user).topic }
fab!(:message_with_unread_notification) { Fabricate(:private_message_post, recipient: user).topic }
fab!(:archived_message) { Fabricate(:private_message_post, recipient: user).topic }
fab!(:group_message1) { Fabricate(:group_private_message_post, recipients: group1).topic }
fab!(:group_message2) { Fabricate(:group_private_message_post, recipients: group2).topic }
fab!(:group_message3) { Fabricate(:group_private_message_post, recipients: group3).topic }
fab!(:archived_group_message1) { Fabricate(:group_private_message_post, recipients: group1).topic }
fab!(:archived_group_message2) { Fabricate(:group_private_message_post, recipients: group2).topic }
fab!(:user1_message_without_notification) do
Fabricate(:private_message_post, recipient: user1).topic
@ -5810,16 +5822,18 @@ RSpec.describe UsersController do
fab!(:user1_message_with_unread_notification) do
Fabricate(:private_message_post, recipient: user1).topic
end
fab!(:user1_archived_message) { Fabricate(:private_message_post, recipient: user1).topic }
fab!(:unread_notification) do
fab!(:unread_pm_notification) do
Fabricate(
:private_message_notification,
read: false,
user: user,
topic: message_with_unread_notification
topic: message_with_unread_notification,
created_at: 4.minutes.ago
)
end
fab!(:read_notification) do
fab!(:read_pm_notification) do
Fabricate(
:private_message_notification,
read: true,
@ -5828,7 +5842,27 @@ RSpec.describe UsersController do
)
end
fab!(:user1_unread_notification) do
fab!(:unread_group_message_summary_notification) do
Fabricate(
:notification,
read: false,
user: user,
notification_type: Notification.types[:group_message_summary],
created_at: 2.minutes.ago
)
end
fab!(:read_group_message_summary_notification) do
Fabricate(
:notification,
read: true,
user: user,
notification_type: Notification.types[:group_message_summary],
created_at: 1.minutes.ago
)
end
fab!(:user1_unread_pm_notification) do
Fabricate(
:private_message_notification,
read: false,
@ -5836,7 +5870,7 @@ RSpec.describe UsersController do
topic: user1_message_with_unread_notification
)
end
fab!(:user1_read_notification) do
fab!(:user1_read_pm_notification) do
Fabricate(
:private_message_notification,
read: true,
@ -5845,6 +5879,30 @@ RSpec.describe UsersController do
)
end
fab!(:user1_unread_group_message_summary_notification) do
Fabricate(
:notification,
read: false,
user: user1,
notification_type: Notification.types[:group_message_summary],
)
end
fab!(:user1_read_group_message_summary_notification) do
Fabricate(
:notification,
read: true,
user: user1,
notification_type: Notification.types[:group_message_summary],
)
end
before do
UserArchivedMessage.archive!(user.id, archived_message)
UserArchivedMessage.archive!(user1.id, user1_archived_message)
GroupArchivedMessage.archive!(group1.id, archived_group_message1)
GroupArchivedMessage.archive!(group2.id, archived_group_message2)
end
context "when logged out" do
it "responds with 404" do
get "/u/#{user.username}/user-menu-private-messages"
@ -5873,33 +5931,62 @@ RSpec.describe UsersController do
get "/u/#{user.username}/user-menu-private-messages"
expect(response.status).to eq(200)
notifications = response.parsed_body["notifications"]
expect(notifications.map { |notification| notification["id"] }).to contain_exactly(
unread_notification.id
unread_notifications = response.parsed_body["unread_notifications"]
expect(unread_notifications.map { |notification| notification["id"] }).to eq([
unread_pm_notification.id,
unread_group_message_summary_notification.id
])
end
it "sends an array of read group_message_summary notifications" do
read_group_message_summary_notification2 = Fabricate(
:notification,
read: true,
user: user,
notification_type: Notification.types[:group_message_summary],
created_at: 5.minutes.ago
)
get "/u/#{user.username}/user-menu-private-messages"
expect(response.status).to eq(200)
read_notifications = response.parsed_body["read_notifications"]
expect(read_notifications.map { |notification| notification["id"] }).to eq([
read_group_message_summary_notification.id,
read_group_message_summary_notification2.id
])
end
it "responds with an array of PM topics that are not associated with any of the unread private_message notifications" do
group_message1.update!(bumped_at: 1.minutes.ago)
message_without_notification.update!(bumped_at: 3.minutes.ago)
group_message2.update!(bumped_at: 6.minutes.ago)
message_with_read_notification.update!(bumped_at: 10.minutes.ago)
read_group_message_summary_notification.destroy!
get "/u/#{user.username}/user-menu-private-messages"
expect(response.status).to eq(200)
topics = response.parsed_body["topics"]
expect(topics.map { |topic| topic["id"] }).to contain_exactly(
expect(topics.map { |topic| topic["id"] }).to eq([
group_message1.id,
message_without_notification.id,
group_message2.id,
message_with_read_notification.id
)
])
end
it "fills up the remaining of the USER_MENU_LIST_LIMIT limit with PM topics" do
stub_const(UsersController, "USER_MENU_LIST_LIMIT", 2) do
stub_const(UsersController, "USER_MENU_LIST_LIMIT", 3) do
get "/u/#{user.username}/user-menu-private-messages"
end
expect(response.status).to eq(200)
notifications = response.parsed_body["notifications"]
expect(notifications.size).to eq(1)
unread_notifications = response.parsed_body["unread_notifications"]
expect(unread_notifications.size).to eq(2)
topics = response.parsed_body["topics"]
read_notifications = response.parsed_body["read_notifications"]
expect(topics.size).to eq(1)
expect(read_notifications.size).to eq(1)
message2 = Fabricate(:private_message_post, recipient: user).topic
Fabricate(
@ -5913,11 +6000,13 @@ RSpec.describe UsersController do
get "/u/#{user.username}/user-menu-private-messages"
end
expect(response.status).to eq(200)
notifications = response.parsed_body["notifications"]
expect(notifications.size).to eq(2)
unread_notifications = response.parsed_body["unread_notifications"]
expect(unread_notifications.size).to eq(2)
topics = response.parsed_body["topics"]
read_notifications = response.parsed_body["read_notifications"]
expect(topics.size).to eq(0)
expect(read_notifications.size).to eq(0)
end
end
end