FEATURE: Add assigns tab to the experimental user menu (#366)
This commit adds a tab for assignments in the experimental user menu. The assignments tab behaves very similarly to the bookmarks and messages tab in core: it displays the user's unread assign notifications first and then fills the rest of available space in the menu with assignments (same content that the current user menu displays). More details of the experimental user menu can be found in https://github.com/discourse/discourse/pull/17379.
This commit is contained in:
parent
28bc8ab78a
commit
e6e222d8bc
|
@ -1,3 +1,4 @@
|
|||
2.9.0.beta8: 28bc8ab78a09551548c87f511ade3d64e1b04bc3
|
||||
2.9.0.beta3: 46f200935dc9e5750c3f2740abd993e27a9b3f6c
|
||||
2.8.2: 7bec9aeaf786defc9a133b8cb9ed24f1c2522400
|
||||
2.8.0.beta8: f901c5fe97c272c884c1e6cfdad1ad1cbe0b36be
|
||||
|
|
|
@ -174,6 +174,49 @@ module DiscourseAssign
|
|||
}
|
||||
end
|
||||
|
||||
def user_menu_assigns
|
||||
assign_notifications = Notification.unread_type(
|
||||
current_user,
|
||||
Notification.types[:assigned],
|
||||
user_menu_limit
|
||||
)
|
||||
|
||||
if assign_notifications.size < user_menu_limit
|
||||
opts = {}
|
||||
ignored_assignment_ids = assign_notifications.filter_map do |notification|
|
||||
notification.data_hash[:assignment_id]
|
||||
end
|
||||
opts[:ignored_assignment_ids] = ignored_assignment_ids if ignored_assignment_ids.present?
|
||||
|
||||
assigns_list = TopicQuery.new(
|
||||
current_user,
|
||||
per_page: user_menu_limit - assign_notifications.size
|
||||
).list_messages_assigned(current_user, ignored_assignment_ids: ignored_assignment_ids)
|
||||
end
|
||||
|
||||
if assign_notifications.present?
|
||||
serialized_notifications = ActiveModel::ArraySerializer.new(
|
||||
assign_notifications,
|
||||
each_serializer: NotificationSerializer,
|
||||
scope: guardian
|
||||
)
|
||||
end
|
||||
|
||||
if assigns_list
|
||||
serialized_assigns = serialize_data(
|
||||
assigns_list,
|
||||
TopicListSerializer,
|
||||
scope: guardian,
|
||||
root: false
|
||||
)[:topics]
|
||||
end
|
||||
|
||||
render json: {
|
||||
notifications: serialized_notifications || [],
|
||||
topics: serialized_assigns || [],
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def translate_failure(reason, assign_to)
|
||||
|
@ -238,5 +281,9 @@ module DiscourseAssign
|
|||
def ensure_assign_allowed
|
||||
raise Discourse::InvalidAccess.new unless current_user.can_assign?
|
||||
end
|
||||
|
||||
def user_menu_limit
|
||||
UsersController::USER_MENU_LIST_LIMIT
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
import UserMenuBaseItem from "discourse/lib/user-menu/base-item";
|
||||
import { postUrl } from "discourse/lib/utilities";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
import { emojiUnescape } from "discourse/lib/text";
|
||||
import I18n from "I18n";
|
||||
|
||||
const ICON = "user-plus";
|
||||
const GROUP_ICON = "group-plus";
|
||||
|
||||
export default class UserMenuAssignItem extends UserMenuBaseItem {
|
||||
constructor({ assign }) {
|
||||
super(...arguments);
|
||||
this.assign = assign;
|
||||
}
|
||||
|
||||
get className() {
|
||||
return "assign";
|
||||
}
|
||||
|
||||
get linkHref() {
|
||||
return postUrl(
|
||||
this.assign.slug,
|
||||
this.assign.id,
|
||||
(this.assign.last_read_post_number || 0) + 1
|
||||
);
|
||||
}
|
||||
|
||||
get linkTitle() {
|
||||
if (this.assign.assigned_to_group) {
|
||||
return I18n.t("user.assigned_to_group", {
|
||||
group_name:
|
||||
this.assign.assigned_to_group.full_name ||
|
||||
this.assign.assigned_to_group.name,
|
||||
});
|
||||
} else {
|
||||
return I18n.t("user.assigned_to_you");
|
||||
}
|
||||
}
|
||||
|
||||
get icon() {
|
||||
if (this.assign.assigned_to_group) {
|
||||
return GROUP_ICON;
|
||||
} else {
|
||||
return ICON;
|
||||
}
|
||||
}
|
||||
|
||||
get label() {
|
||||
return null;
|
||||
}
|
||||
|
||||
get description() {
|
||||
return htmlSafe(emojiUnescape(this.assign.fancy_title));
|
||||
}
|
||||
|
||||
get topicId() {
|
||||
return this.assign.id;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
import templateOnly from "@ember/component/template-only";
|
||||
// TODO: colocate this component's template
|
||||
export default templateOnly();
|
|
@ -0,0 +1,78 @@
|
|||
import UserMenuNotificationsList from "discourse/components/user-menu/notifications-list";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import UserMenuNotificationItem from "discourse/lib/user-menu/notification-item";
|
||||
import UserMenuAssignItem from "discourse/plugins/discourse-assign/discourse-assign/lib/user-menu/assign-item";
|
||||
import Notification from "discourse/models/notification";
|
||||
import I18n from "I18n";
|
||||
import showModal from "discourse/lib/show-modal";
|
||||
|
||||
export default class UserMenuAssignNotificationsList extends UserMenuNotificationsList {
|
||||
get dismissTypes() {
|
||||
return ["assigned"];
|
||||
}
|
||||
|
||||
get showDismiss() {
|
||||
return this._unreadAssignedNotificationsCount > 0;
|
||||
}
|
||||
|
||||
get dismissTitle() {
|
||||
return I18n.t("user.dismiss_assigned_tooltip");
|
||||
}
|
||||
|
||||
get showAllHref() {
|
||||
return `${this.currentUser.path}/activity/assigned`;
|
||||
}
|
||||
|
||||
get showAllTitle() {
|
||||
return I18n.t("user_menu.view_all_assigned");
|
||||
}
|
||||
|
||||
get itemsCacheKey() {
|
||||
return "user-menu-assigns-tab";
|
||||
}
|
||||
|
||||
get emptyStateComponent() {
|
||||
return "user-menu/assigns-list-empty-state";
|
||||
}
|
||||
|
||||
fetchItems() {
|
||||
return ajax("/assign/user-menu-assigns.json").then((data) => {
|
||||
const content = [];
|
||||
data.notifications.forEach((rawNotification) => {
|
||||
const notification = Notification.create(rawNotification);
|
||||
content.push(
|
||||
new UserMenuNotificationItem({
|
||||
notification,
|
||||
currentUser: this.currentUser,
|
||||
siteSettings: this.siteSettings,
|
||||
site: this.site,
|
||||
})
|
||||
);
|
||||
});
|
||||
content.push(
|
||||
...data.topics.map((assign) => new UserMenuAssignItem({ assign }))
|
||||
);
|
||||
return content;
|
||||
});
|
||||
}
|
||||
|
||||
dismissWarningModal() {
|
||||
const modalController = showModal("dismiss-notification-confirmation");
|
||||
modalController.set(
|
||||
"confirmationMessage",
|
||||
I18n.t("notifications.dismiss_confirmation.body.assigns", {
|
||||
count: this._unreadAssignedNotificationsCount,
|
||||
})
|
||||
);
|
||||
return modalController;
|
||||
}
|
||||
|
||||
get _unreadAssignedNotificationsCount() {
|
||||
const key = `grouped_unread_high_priority_notifications.${this.site.notification_types.assigned}`;
|
||||
// we're retrieving the value with get() so that Ember tracks the property
|
||||
// and re-renders the UI when it changes.
|
||||
// we can stop using `get()` when the User model is refactored into native
|
||||
// class with @tracked properties.
|
||||
return this.currentUser.get(key) || 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
import { withPluginApi } from "discourse/lib/plugin-api";
|
||||
|
||||
export default {
|
||||
name: "assign-user-menu",
|
||||
|
||||
initialize(container) {
|
||||
withPluginApi("1.2.0", (api) => {
|
||||
if (api.registerUserMenuTab) {
|
||||
const siteSettings = container.lookup("service:site-settings");
|
||||
if (!siteSettings.assign_enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentUser = api.getCurrentUser();
|
||||
if (!currentUser?.can_assign) {
|
||||
return;
|
||||
}
|
||||
api.registerUserMenuTab((UserMenuTab) => {
|
||||
return class extends UserMenuTab {
|
||||
get id() {
|
||||
return "assign-list";
|
||||
}
|
||||
|
||||
get panelComponent() {
|
||||
return "user-menu/assigns-list";
|
||||
}
|
||||
|
||||
get icon() {
|
||||
return "user-plus";
|
||||
}
|
||||
|
||||
get count() {
|
||||
return this.getUnreadCountForType("assigned");
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
|
@ -0,0 +1,14 @@
|
|||
<div class="empty-state">
|
||||
<span class="empty-state-title">
|
||||
{{i18n "user.no_assignments_title"}}
|
||||
</span>
|
||||
<div class="empty-state-body">
|
||||
<p>
|
||||
{{html-safe (i18n
|
||||
"user.no_assignments_body"
|
||||
icon=(d-icon "user-plus")
|
||||
preferencesUrl=(get-url "/my/preferences/notifications")
|
||||
)}}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
|
@ -93,6 +93,9 @@ en:
|
|||
Your assigned topics and messages will be listed here. You will also receive a periodic reminder notification of your assignments, which you can adjust in your <a href='%{preferencesUrl}'>user preferences</a>.
|
||||
<br><br>
|
||||
To assign a topic or message to yourself or to someone else, look for the %{icon} assign button at the bottom.
|
||||
dismiss_assigned_tooltip: "Mark all unread assign notifications as read"
|
||||
assigned_to_group: "assigned to %{group_name}"
|
||||
assigned_to_you: "assigned to you"
|
||||
admin:
|
||||
web_hooks:
|
||||
assign_event:
|
||||
|
@ -138,3 +141,10 @@ en:
|
|||
assigned: "%{username} assigned you"
|
||||
titles:
|
||||
assigned: "Assigned"
|
||||
dismiss_confirmation:
|
||||
body:
|
||||
assigns:
|
||||
one: "Are you sure? You have %{count} unread assign notification."
|
||||
other: "Are you sure? You have %{count} unread assign notifications."
|
||||
user_menu:
|
||||
view_all_assigned: "view all assigned"
|
||||
|
|
|
@ -7,4 +7,5 @@ DiscourseAssign::Engine.routes.draw do
|
|||
get "/suggestions" => "assign#suggestions"
|
||||
get "/assigned" => "assign#assigned"
|
||||
get "/members/:group_name" => "assign#group_members"
|
||||
get "/user-menu-assigns" => "assign#user_menu_assigns"
|
||||
end
|
||||
|
|
27
plugin.rb
27
plugin.rb
|
@ -380,23 +380,30 @@ after_initialize do
|
|||
next results
|
||||
end
|
||||
|
||||
add_to_class(:topic_query, :list_messages_assigned) do |user|
|
||||
add_to_class(:topic_query, :list_messages_assigned) do |user, ignored_assignment_ids: nil|
|
||||
list = default_results(include_pms: true)
|
||||
|
||||
where_clause = +"("
|
||||
where_clause << "(assigned_to_id = :user_id AND assigned_to_type = 'User' AND active)"
|
||||
if @options[:filter] != :direct
|
||||
where_clause << "OR (assigned_to_id IN (group_users.group_id) AND assigned_to_type = 'Group' AND active)"
|
||||
end
|
||||
where_clause << ")"
|
||||
|
||||
if ignored_assignment_ids.present?
|
||||
where_clause << "AND assignments.id NOT IN (:ignored_assignment_ids)"
|
||||
end
|
||||
topic_ids_sql = +<<~SQL
|
||||
SELECT topic_id FROM assignments
|
||||
LEFT JOIN group_users ON group_users.user_id = :user_id
|
||||
WHERE
|
||||
(assigned_to_id = :user_id AND assigned_to_type = 'User' AND active)
|
||||
WHERE #{where_clause}
|
||||
SQL
|
||||
|
||||
topic_ids_sql << <<~SQL if @options[:filter] != :direct
|
||||
OR (assigned_to_id IN (group_users.group_id) AND assigned_to_type = 'Group' AND active)
|
||||
SQL
|
||||
|
||||
sql = "topics.id IN (#{topic_ids_sql})"
|
||||
|
||||
list = list.where(sql, user_id: user.id).includes(:allowed_users)
|
||||
where_args = { user_id: user.id }
|
||||
if ignored_assignment_ids.present?
|
||||
where_args[:ignored_assignment_ids] = ignored_assignment_ids
|
||||
end
|
||||
list = list.where("topics.id IN (#{topic_ids_sql})", **where_args).includes(:allowed_users)
|
||||
|
||||
create_list(:assigned, { unordered: true }, list)
|
||||
end
|
||||
|
|
|
@ -392,4 +392,133 @@ RSpec.describe DiscourseAssign::AssignController do
|
|||
expect(response.status).to eq(200)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#user_menu_assigns" do
|
||||
fab!(:unread_assigned_topic) { Fabricate(:post).topic }
|
||||
fab!(:read_assigned_topic) { Fabricate(:post).topic }
|
||||
|
||||
fab!(:unread_assigned_post) { Fabricate(:post, topic: Fabricate(:post).topic) }
|
||||
fab!(:read_assigned_post) { Fabricate(:post, topic: Fabricate(:post).topic) }
|
||||
|
||||
fab!(:read_assigned_post_in_same_topic) { Fabricate(:post, topic: Fabricate(:post).topic) }
|
||||
fab!(:unread_assigned_post_in_same_topic) { Fabricate(:post, topic: read_assigned_post_in_same_topic.topic) }
|
||||
|
||||
fab!(:another_user_unread_assigned_topic) { Fabricate(:post).topic }
|
||||
fab!(:another_user_read_assigned_topic) { Fabricate(:post).topic }
|
||||
|
||||
before do
|
||||
Jobs.run_immediately!
|
||||
|
||||
[
|
||||
unread_assigned_topic,
|
||||
read_assigned_topic,
|
||||
unread_assigned_post,
|
||||
read_assigned_post,
|
||||
unread_assigned_post_in_same_topic,
|
||||
read_assigned_post_in_same_topic,
|
||||
].each do |target|
|
||||
Assigner.new(target, normal_admin).assign(user)
|
||||
end
|
||||
|
||||
Notification
|
||||
.where(
|
||||
notification_type: Notification.types[:assigned],
|
||||
read: false,
|
||||
user_id: user.id,
|
||||
topic_id: [
|
||||
read_assigned_topic.id,
|
||||
read_assigned_post.topic.id,
|
||||
read_assigned_post_in_same_topic.topic.id
|
||||
]
|
||||
)
|
||||
.where.not(
|
||||
topic_id: read_assigned_post_in_same_topic.topic.id,
|
||||
post_number: unread_assigned_post_in_same_topic.post_number
|
||||
)
|
||||
.update_all(read: true)
|
||||
|
||||
Assigner.new(another_user_read_assigned_topic, normal_admin).assign(user2)
|
||||
Assigner.new(another_user_unread_assigned_topic, normal_admin).assign(user2)
|
||||
Notification
|
||||
.where(
|
||||
notification_type: Notification.types[:assigned],
|
||||
read: false,
|
||||
user_id: user2.id,
|
||||
topic_id: another_user_read_assigned_topic,
|
||||
)
|
||||
.update_all(read: true)
|
||||
end
|
||||
|
||||
context "when logged out" do
|
||||
it "responds with 403" do
|
||||
get "/assign/user-menu-assigns.json"
|
||||
expect(response.status).to eq(403)
|
||||
end
|
||||
end
|
||||
|
||||
context "when logged in" do
|
||||
before do
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
it "responds with 403 if the current user can't assign" do
|
||||
user.update!(admin: false)
|
||||
user.group_users.where(group_id: default_allowed_group.id).destroy_all
|
||||
get "/assign/user-menu-assigns.json"
|
||||
expect(response.status).to eq(403)
|
||||
end
|
||||
|
||||
it "responds with 403 if the assign_enabled setting is disabled" do
|
||||
SiteSetting.assign_enabled = false
|
||||
get "/assign/user-menu-assigns.json"
|
||||
expect(response.status).to eq(403)
|
||||
end
|
||||
|
||||
it "sends an array of unread assigned notifications" do
|
||||
get "/assign/user-menu-assigns.json"
|
||||
expect(response.status).to eq(200)
|
||||
|
||||
notifications = response.parsed_body["notifications"]
|
||||
expect(notifications.map { |n| [n["topic_id"], n["post_number"]] }).to eq([
|
||||
[unread_assigned_topic.id, 1],
|
||||
[unread_assigned_post.topic.id, unread_assigned_post.post_number],
|
||||
[unread_assigned_post_in_same_topic.topic.id, unread_assigned_post_in_same_topic.post_number]
|
||||
])
|
||||
end
|
||||
|
||||
it "responds with an array of assigned topics that are not associated with any of the unread assigned notifications" do
|
||||
get "/assign/user-menu-assigns.json"
|
||||
expect(response.status).to eq(200)
|
||||
|
||||
topics = response.parsed_body["topics"]
|
||||
expect(topics.map { |t| t["id"] }).to eq([
|
||||
read_assigned_post_in_same_topic.topic.id,
|
||||
read_assigned_post.topic.id,
|
||||
read_assigned_topic.id,
|
||||
])
|
||||
end
|
||||
|
||||
it "fills up the remaining of the UsersController::USER_MENU_LIST_LIMIT limit with assigned topics" do
|
||||
stub_const(UsersController, "USER_MENU_LIST_LIMIT", 3) do
|
||||
get "/assign/user-menu-assigns.json"
|
||||
end
|
||||
expect(response.status).to eq(200)
|
||||
|
||||
notifications = response.parsed_body["notifications"]
|
||||
expect(notifications.size).to eq(3)
|
||||
topics = response.parsed_body["topics"]
|
||||
expect(topics.size).to eq(0)
|
||||
|
||||
stub_const(UsersController, "USER_MENU_LIST_LIMIT", 4) do
|
||||
get "/assign/user-menu-assigns.json"
|
||||
end
|
||||
expect(response.status).to eq(200)
|
||||
|
||||
notifications = response.parsed_body["notifications"]
|
||||
expect(notifications.size).to eq(3)
|
||||
topics = response.parsed_body["topics"]
|
||||
expect(topics.size).to eq(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,451 @@
|
|||
import {
|
||||
acceptance,
|
||||
exists,
|
||||
query,
|
||||
queryAll,
|
||||
updateCurrentUser,
|
||||
} from "discourse/tests/helpers/qunit-helpers";
|
||||
import { click, visit } from "@ember/test-helpers";
|
||||
import { test } from "qunit";
|
||||
import I18n from "I18n";
|
||||
|
||||
const USER_MENU_ASSIGN_RESPONSE = {
|
||||
notifications: [
|
||||
{
|
||||
id: 1716,
|
||||
user_id: 1,
|
||||
notification_type: 34,
|
||||
read: false,
|
||||
high_priority: true,
|
||||
created_at: "2022-08-11T21:32:32.404Z",
|
||||
post_number: 1,
|
||||
topic_id: 227,
|
||||
fancy_title: "Test poll topic please bear with me",
|
||||
slug: "test-poll-topic-please-bear-with-me",
|
||||
data: {
|
||||
message: "discourse_assign.assign_notification",
|
||||
display_username: "tony",
|
||||
topic_title: "Test poll topic please bear with me",
|
||||
assignment_id: 2,
|
||||
},
|
||||
},
|
||||
],
|
||||
topics: [
|
||||
{
|
||||
id: 209,
|
||||
title: "Howdy this a test topic!",
|
||||
fancy_title: "Howdy this <b>my test topic</b> with emoji :heart:!",
|
||||
slug: "howdy-this-a-test-topic",
|
||||
posts_count: 1,
|
||||
reply_count: 0,
|
||||
highest_post_number: 1,
|
||||
image_url: null,
|
||||
created_at: "2022-03-10T20:09:25.772Z",
|
||||
last_posted_at: "2022-03-10T20:09:25.959Z",
|
||||
bumped: true,
|
||||
bumped_at: "2022-03-10T20:09:25.959Z",
|
||||
archetype: "regular",
|
||||
unseen: false,
|
||||
last_read_post_number: 2,
|
||||
unread: 0,
|
||||
new_posts: 0,
|
||||
unread_posts: 0,
|
||||
pinned: false,
|
||||
unpinned: null,
|
||||
visible: true,
|
||||
closed: false,
|
||||
archived: false,
|
||||
notification_level: 3,
|
||||
bookmarked: false,
|
||||
liked: false,
|
||||
thumbnails: null,
|
||||
tags: [],
|
||||
tags_descriptions: {},
|
||||
views: 11,
|
||||
like_count: 7,
|
||||
has_summary: false,
|
||||
last_poster_username: "osama",
|
||||
category_id: 1,
|
||||
pinned_globally: false,
|
||||
featured_link: null,
|
||||
assigned_to_user: {
|
||||
id: 1,
|
||||
username: "osama",
|
||||
name: "Osama.OG",
|
||||
avatar_template: "/letter_avatar_proxy/v4/letter/o/f05b48/{size}.png",
|
||||
assign_icon: "user-plus",
|
||||
assign_path: "/u/osama/activity/assigned",
|
||||
},
|
||||
posters: [
|
||||
{
|
||||
extras: "latest single",
|
||||
description: "Original Poster, Most Recent Poster",
|
||||
user_id: 1,
|
||||
primary_group_id: 45,
|
||||
flair_group_id: 45,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 173,
|
||||
title: "Owners elegance entrance startled spirits losing",
|
||||
fancy_title:
|
||||
"Owners <i>elegance entrance :car: startled</i> spirits losing",
|
||||
slug: "owners-elegance-entrance-startled-spirits-losing",
|
||||
posts_count: 7,
|
||||
reply_count: 0,
|
||||
highest_post_number: 7,
|
||||
image_url: null,
|
||||
created_at: "2021-07-11T04:50:17.029Z",
|
||||
last_posted_at: "2021-12-24T17:21:03.418Z",
|
||||
bumped: true,
|
||||
bumped_at: "2021-12-24T17:21:03.418Z",
|
||||
archetype: "regular",
|
||||
unseen: false,
|
||||
last_read_post_number: 3,
|
||||
unread: 0,
|
||||
new_posts: 0,
|
||||
unread_posts: 0,
|
||||
pinned: false,
|
||||
unpinned: null,
|
||||
visible: true,
|
||||
closed: false,
|
||||
archived: false,
|
||||
notification_level: 1,
|
||||
bookmarked: false,
|
||||
liked: false,
|
||||
thumbnails: null,
|
||||
tags: ["music", "job-application"],
|
||||
tags_descriptions: {},
|
||||
views: 23,
|
||||
like_count: 24,
|
||||
has_summary: false,
|
||||
last_poster_username: "ambrose.bradtke",
|
||||
category_id: 1,
|
||||
pinned_globally: false,
|
||||
featured_link: null,
|
||||
assigned_to_group: {
|
||||
id: 45,
|
||||
automatic: false,
|
||||
name: "Team",
|
||||
user_count: 4,
|
||||
mentionable_level: 99,
|
||||
messageable_level: 99,
|
||||
visibility_level: 0,
|
||||
primary_group: true,
|
||||
title: "",
|
||||
grant_trust_level: null,
|
||||
incoming_email: null,
|
||||
has_messages: true,
|
||||
flair_url: null,
|
||||
flair_bg_color: "",
|
||||
flair_color: "",
|
||||
bio_raw: "",
|
||||
bio_cooked: null,
|
||||
bio_excerpt: null,
|
||||
public_admission: true,
|
||||
public_exit: true,
|
||||
allow_membership_requests: false,
|
||||
full_name: "",
|
||||
default_notification_level: 3,
|
||||
membership_request_template: "",
|
||||
members_visibility_level: 0,
|
||||
can_see_members: true,
|
||||
can_admin_group: true,
|
||||
publish_read_state: true,
|
||||
assign_icon: "group-plus",
|
||||
assign_path: "/g/Team/assigned/everyone",
|
||||
},
|
||||
posters: [
|
||||
{
|
||||
extras: null,
|
||||
description: "Original Poster",
|
||||
user_id: 26,
|
||||
primary_group_id: null,
|
||||
flair_group_id: null,
|
||||
},
|
||||
{
|
||||
extras: null,
|
||||
description: "Frequent Poster",
|
||||
user_id: 16,
|
||||
primary_group_id: null,
|
||||
flair_group_id: null,
|
||||
},
|
||||
{
|
||||
extras: null,
|
||||
description: "Frequent Poster",
|
||||
user_id: 22,
|
||||
primary_group_id: null,
|
||||
flair_group_id: null,
|
||||
},
|
||||
{
|
||||
extras: null,
|
||||
description: "Frequent Poster",
|
||||
user_id: 12,
|
||||
primary_group_id: null,
|
||||
flair_group_id: null,
|
||||
},
|
||||
{
|
||||
extras: "latest",
|
||||
description: "Most Recent Poster",
|
||||
user_id: 13,
|
||||
primary_group_id: null,
|
||||
flair_group_id: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
acceptance(
|
||||
"Discourse Assign | experimental user menu | user cannot assign",
|
||||
function (needs) {
|
||||
needs.user({ redesigned_user_menu_enabled: true, can_assign: false });
|
||||
needs.settings({
|
||||
assign_enabled: true,
|
||||
});
|
||||
|
||||
test("the assigns tab is not shown", async function (assert) {
|
||||
await visit("/");
|
||||
await click(".d-header-icons .current-user");
|
||||
assert.notOk(exists("#user-menu-button-assign-list"));
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
acceptance(
|
||||
"Discourse Assign | experimental user menu | assign_enabled setting is disabled",
|
||||
function (needs) {
|
||||
needs.user({ redesigned_user_menu_enabled: true, can_assign: true });
|
||||
needs.settings({
|
||||
assign_enabled: false,
|
||||
});
|
||||
|
||||
test("the assigns tab is not shown", async function (assert) {
|
||||
await visit("/");
|
||||
await click(".d-header-icons .current-user");
|
||||
assert.notOk(exists("#user-menu-button-assign-list"));
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
acceptance("Discourse Assign | experimental user menu", function (needs) {
|
||||
needs.user({
|
||||
redesigned_user_menu_enabled: true,
|
||||
can_assign: true,
|
||||
grouped_unread_high_priority_notifications: {
|
||||
34: 173, // assigned notification type
|
||||
},
|
||||
});
|
||||
|
||||
needs.settings({
|
||||
assign_enabled: true,
|
||||
});
|
||||
|
||||
let forceEmptyState = false;
|
||||
let markRead = false;
|
||||
let requestBody;
|
||||
|
||||
needs.pretender((server, helper) => {
|
||||
server.get("/assign/user-menu-assigns.json", () => {
|
||||
if (forceEmptyState) {
|
||||
return helper.response({ notifications: [], topics: [] });
|
||||
} else {
|
||||
return helper.response(USER_MENU_ASSIGN_RESPONSE);
|
||||
}
|
||||
});
|
||||
|
||||
server.put("/notifications/mark-read", (request) => {
|
||||
requestBody = request.requestBody;
|
||||
markRead = true;
|
||||
return helper.response({ success: true });
|
||||
});
|
||||
});
|
||||
|
||||
needs.hooks.afterEach(() => {
|
||||
forceEmptyState = false;
|
||||
markRead = false;
|
||||
requestBody = null;
|
||||
});
|
||||
|
||||
test("assigns tab", async function (assert) {
|
||||
await visit("/");
|
||||
await click(".d-header-icons .current-user");
|
||||
assert.ok(exists("#user-menu-button-assign-list"), "assigns tab exists");
|
||||
assert.ok(
|
||||
exists("#user-menu-button-assign-list .d-icon-user-plus"),
|
||||
"assigns tab has the user-plus icon"
|
||||
);
|
||||
assert.strictEqual(
|
||||
query(
|
||||
"#user-menu-button-assign-list .badge-notification"
|
||||
).textContent.trim(),
|
||||
"173",
|
||||
"assigns tab has a count badge"
|
||||
);
|
||||
|
||||
updateCurrentUser({
|
||||
grouped_unread_high_priority_notifications: {},
|
||||
});
|
||||
|
||||
assert.notOk(
|
||||
exists("#user-menu-button-assign-list .badge-notification"),
|
||||
"badge count disappears when it goes to zero"
|
||||
);
|
||||
assert.ok(
|
||||
exists("#user-menu-button-assign-list"),
|
||||
"assigns tab still exists"
|
||||
);
|
||||
});
|
||||
|
||||
test("displays unread assign notifications on top and fills the remaining space with read assigns", async function (assert) {
|
||||
await visit("/");
|
||||
await click(".d-header-icons .current-user");
|
||||
await click("#user-menu-button-assign-list");
|
||||
|
||||
const notifications = queryAll("#quick-access-assign-list .notification");
|
||||
assert.strictEqual(
|
||||
notifications.length,
|
||||
1,
|
||||
"there is one unread notification"
|
||||
);
|
||||
assert.ok(
|
||||
notifications[0].classList.contains("unread"),
|
||||
"the notification is unread"
|
||||
);
|
||||
assert.ok(
|
||||
notifications[0].classList.contains("assigned"),
|
||||
"the notification is of type assigned"
|
||||
);
|
||||
|
||||
const assigns = queryAll("#quick-access-assign-list .assign");
|
||||
assert.strictEqual(assigns.length, 2, "there are 2 assigns");
|
||||
|
||||
const userAssign = assigns[0];
|
||||
const groupAssign = assigns[1];
|
||||
assert.ok(
|
||||
userAssign.querySelector(".d-icon-user-plus"),
|
||||
"user assign has the right icon"
|
||||
);
|
||||
assert.ok(
|
||||
groupAssign.querySelector(".d-icon-group-plus"),
|
||||
"group assign has the right icon"
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
userAssign
|
||||
.querySelector("a")
|
||||
.href.endsWith("/t/howdy-this-a-test-topic/209/3"),
|
||||
"user assign links to the first unread post (last read post + 1)"
|
||||
);
|
||||
assert.ok(
|
||||
groupAssign
|
||||
.querySelector("a")
|
||||
.href.endsWith(
|
||||
"/t/owners-elegance-entrance-startled-spirits-losing/173/4"
|
||||
),
|
||||
"group assign links to the first unread post (last read post + 1)"
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
userAssign.textContent.trim(),
|
||||
"Howdy this my test topic with emoji !",
|
||||
"user assign contains the topic title"
|
||||
);
|
||||
assert.ok(
|
||||
userAssign.querySelector(".item-description img.emoji"),
|
||||
"emojis are rendered in user assign"
|
||||
);
|
||||
assert.ok(
|
||||
userAssign.querySelector(".item-description b").textContent.trim(),
|
||||
"my test topic",
|
||||
"user assign topic title is trusted"
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
groupAssign.textContent.trim().replaceAll(/\s+/g, " "),
|
||||
"Owners elegance entrance startled spirits losing",
|
||||
"group assign contains the topic title"
|
||||
);
|
||||
assert.ok(
|
||||
groupAssign.querySelector(".item-description i img.emoji"),
|
||||
"emojis are rendered in group assign"
|
||||
);
|
||||
assert.strictEqual(
|
||||
groupAssign
|
||||
.querySelector(".item-description i")
|
||||
.textContent.trim()
|
||||
.replaceAll(/\s+/g, " "),
|
||||
"elegance entrance startled",
|
||||
"group assign topic title is trusted"
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
userAssign.querySelector("a").title,
|
||||
I18n.t("user.assigned_to_you"),
|
||||
"user assign has the right title"
|
||||
);
|
||||
assert.strictEqual(
|
||||
groupAssign.querySelector("a").title,
|
||||
I18n.t("user.assigned_to_group", { group_name: "Team" }),
|
||||
"group assign has the right title"
|
||||
);
|
||||
});
|
||||
|
||||
test("dismiss button", async function (assert) {
|
||||
await visit("/");
|
||||
await click(".d-header-icons .current-user");
|
||||
await click("#user-menu-button-assign-list");
|
||||
|
||||
assert.ok(
|
||||
exists("#user-menu-button-assign-list .badge-notification"),
|
||||
"badge count is visible before dismissing"
|
||||
);
|
||||
|
||||
await click(".notifications-dismiss");
|
||||
assert.notOk(markRead, "mark-read request isn't sent");
|
||||
assert.strictEqual(
|
||||
query(".dismiss-notification-confirmation.modal-body").textContent.trim(),
|
||||
I18n.t("notifications.dismiss_confirmation.body.assigns", { count: 173 }),
|
||||
"dismiss confirmation modal is shown"
|
||||
);
|
||||
|
||||
await click(".modal-footer .btn-primary");
|
||||
assert.ok(markRead, "mark-read request is sent");
|
||||
assert.notOk(exists(".notifications-dismiss"), "dismiss button is gone");
|
||||
assert.notOk(
|
||||
exists("#user-menu-button-assign-list .badge-notification"),
|
||||
"badge count is gone after dismissing"
|
||||
);
|
||||
assert.strictEqual(
|
||||
requestBody,
|
||||
"dismiss_types=assigned",
|
||||
"mark-read request is sent with the right params"
|
||||
);
|
||||
});
|
||||
|
||||
test("empty state", async function (assert) {
|
||||
forceEmptyState = true;
|
||||
await visit("/");
|
||||
await click(".d-header-icons .current-user");
|
||||
await click("#user-menu-button-assign-list");
|
||||
|
||||
assert.strictEqual(
|
||||
query(".empty-state-title").textContent.trim(),
|
||||
I18n.t("user.no_assignments_title"),
|
||||
"empty state title is rendered"
|
||||
);
|
||||
const emptyStateBody = query(".empty-state-body");
|
||||
assert.ok(emptyStateBody, "empty state body exists");
|
||||
assert.ok(
|
||||
emptyStateBody.querySelector(".d-icon-user-plus"),
|
||||
"empty state body has user-plus icon"
|
||||
);
|
||||
assert.ok(
|
||||
emptyStateBody
|
||||
.querySelector("a")
|
||||
.href.endsWith("/my/preferences/notifications"),
|
||||
"empty state body has user-plus icon"
|
||||
);
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue