DEV: Modernization/cleanup (#480)

* Component template colocation
* Native classes
* glimmer plugin connector
* `this.` in templates
* `?.` operator
* Merged `withPluginApi` calls
* Updated deps
This commit is contained in:
Jarek Radosz 2023-06-22 10:06:37 +02:00 committed by GitHub
parent 3ca56e82d7
commit 2bff29e919
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
68 changed files with 1353 additions and 1356 deletions

View File

@ -3,34 +3,42 @@ GEM
specs:
ast (2.4.2)
json (2.6.3)
parallel (1.22.1)
parser (3.1.3.0)
parallel (1.23.0)
parser (3.2.2.3)
ast (~> 2.4.1)
prettier_print (1.2.0)
racc
prettier_print (1.2.1)
racc (1.7.1)
rainbow (3.1.1)
regexp_parser (2.6.1)
regexp_parser (2.8.1)
rexml (3.2.5)
rubocop (1.41.1)
rubocop (1.52.1)
json (~> 2.3)
parallel (~> 1.10)
parser (>= 3.1.2.1)
parser (>= 3.2.2.3)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0)
rexml (>= 3.2.5, < 4.0)
rubocop-ast (>= 1.23.0, < 2.0)
rubocop-ast (>= 1.28.0, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 3.0)
rubocop-ast (1.24.0)
parser (>= 3.1.1.0)
rubocop-discourse (3.0.1)
unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.29.0)
parser (>= 3.2.1.0)
rubocop-capybara (2.18.0)
rubocop (~> 1.41)
rubocop-discourse (3.2.0)
rubocop (>= 1.1.0)
rubocop-rspec (>= 2.0.0)
rubocop-rspec (2.16.0)
rubocop-factory_bot (2.23.1)
rubocop (~> 1.33)
ruby-progressbar (1.11.0)
syntax_tree (5.1.0)
rubocop-rspec (2.22.0)
rubocop (~> 1.33)
rubocop-capybara (~> 2.17)
rubocop-factory_bot (~> 2.22)
ruby-progressbar (1.13.0)
syntax_tree (6.1.1)
prettier_print (>= 1.2.0)
unicode-display_width (2.3.0)
unicode-display_width (2.4.2)
PLATFORMS
ruby

View File

@ -1,4 +1,4 @@
# Assign Plugin
# Assign Plugin
Allows you to assign topics and individual posts (both private and public) to a user or group.

View File

@ -1,5 +1,6 @@
export default {
resource: "group",
map() {
this.route("assigned", function () {
this.route("show", { path: "/:filter" });

View File

@ -1,5 +1,6 @@
export default {
resource: "user.userPrivateMessages",
map() {
this.route("assigned", { path: "/assigned" }, function () {
this.route("index", { path: "/" });

View File

@ -1,5 +1,6 @@
export default {
resource: "user.userActivity",
map() {
this.route("assigned");
},

View File

@ -1,15 +1,15 @@
<div class="reviewable-filter discourse-assign-assign-to-filter">
<label class="filter-label">{{i18n "discourse_assign.assigned_to"}}</label>
<EmailGroupUserChooser
@value={{additionalFilters.assigned_to}}
@value={{this.additionalFilters.assigned_to}}
@onChange={{action "updateAssignedTo"}}
autocomplete="off"
@options={{hash
maximum=1
fullWidthWrap=true
filterPlaceholder=placeholderKey
filterPlaceholder=this.placeholderKey
includeGroups=false
groupMembersOf=allowedGroups
groupMembersOf=this.allowedGroups
}}
/>
</div>

View File

@ -4,7 +4,7 @@
}}</label>
<div class="controls">
<EmailGroupUserChooser
@value={{searchedTerms.assigned}}
@value={{this.searchedTerms.assigned}}
@onChange={{action "onChangeAssigned"}}
@options={{hash
maximum=1

View File

@ -4,7 +4,7 @@
<label class="checkbox-label">
<Input
@type="checkbox"
@checked={{readonly category.enable_unassigned_filter}}
@checked={{readonly this.category.enable_unassigned_filter}}
{{on "change" (action "onChangeSetting" value="target.checked")}}
/>
{{i18n "discourse_assign.add_unassigned_filter"}}

View File

@ -1 +1 @@
<GroupAssignedMenuItem @group={{group}} />
<GroupAssignedMenuItem @group={{this.group}} />

View File

@ -1,8 +1,7 @@
export default {
shouldRender(args, component) {
return (
component.currentUser &&
component.currentUser.can_assign &&
component.currentUser?.can_assign &&
args.group.can_show_assigned_tab &&
args.group.assignment_count > 0
);

View File

@ -1,17 +1,20 @@
<div class="control-group">
<label class="control-label">{{i18n
"discourse_assign.admin.groups.manage.interaction.assign"
}}</label>
<label for="visiblity">{{i18n
<label class="control-label">
{{i18n "discourse_assign.admin.groups.manage.interaction.assign"}}
</label>
<label for="visiblity">
{{i18n
"discourse_assign.admin.groups.manage.interaction.assignable_levels.title"
}}</label>
}}
</label>
<ComboBox
@name="alias"
@valueProperty="value"
@value={{assignableLevel}}
@content={{assignableLevelOptions}}
@value={{this.assignableLevel}}
@content={{this.assignableLevelOptions}}
@class="groups-form-assignable-level"
@onChange={{action (mut model.assignable_level)}}
@onChange={{action (mut @outletArgs.model.assignable_level)}}
/>
</div>

View File

@ -1,24 +1,17 @@
import Component from "@glimmer/component";
import I18n from "I18n";
import { or } from "@ember/object/computed";
import { defineProperty } from "@ember/object";
export default {
name: "assignable-interaction-fields",
export default class AssignableInteractionFields extends Component {
assignableLevelOptions = [
{ name: I18n.t("groups.alias_levels.nobody"), value: 0 },
{ name: I18n.t("groups.alias_levels.only_admins"), value: 1 },
{ name: I18n.t("groups.alias_levels.mods_and_admins"), value: 2 },
{ name: I18n.t("groups.alias_levels.members_mods_and_admins"), value: 3 },
{ name: I18n.t("groups.alias_levels.owners_mods_and_admins"), value: 4 },
{ name: I18n.t("groups.alias_levels.everyone"), value: 99 },
];
setupComponent(args, component) {
this.assignableLevelOptions = [
{ name: I18n.t("groups.alias_levels.nobody"), value: 0 },
{ name: I18n.t("groups.alias_levels.only_admins"), value: 1 },
{ name: I18n.t("groups.alias_levels.mods_and_admins"), value: 2 },
{ name: I18n.t("groups.alias_levels.members_mods_and_admins"), value: 3 },
{ name: I18n.t("groups.alias_levels.owners_mods_and_admins"), value: 4 },
{ name: I18n.t("groups.alias_levels.everyone"), value: 99 },
];
defineProperty(
component,
"assignableLevel",
or("model.assignable_level", "assignableLevelOptions.firstObject.value")
);
},
};
get assignableLevel() {
return this.args.outletArgs.model.assignable_level || 0;
}
}

View File

@ -1,4 +1,4 @@
{{#if currentUser.can_assign}}
{{#if this.currentUser.can_assign}}
<LinkTo @route="userActivity.assigned">
{{d-icon "user-plus"}}
{{i18n "discourse_assign.assigned"}}

View File

@ -1,4 +1,4 @@
<LinkTo @route="userPrivateMessages.assigned" @model={{model}}>
<LinkTo @route="userPrivateMessages.assigned" @model={{this.model}}>
{{d-icon "user-plus" class="glyph"}}
{{i18n "discourse_assign.assigned"}}
</LinkTo>

View File

@ -1,9 +1,7 @@
export function shouldShowAssigned(args, component) {
const needsButton =
component.currentUser && component.currentUser.get("can_assign");
const needsButton = component.currentUser?.can_assign;
return (
needsButton &&
(!component.get("site.mobileView") || args.model.get("isPrivateMessage"))
needsButton && (!component.site.mobileView || args.model.isPrivateMessage)
);
}

View File

@ -1 +1 @@
<RemindAssignsFrequency @user={{model}} />
<RemindAssignsFrequency @user={{this.model}} />

View File

@ -1,5 +1,5 @@
export default {
shouldRender(args, component) {
return component.currentUser && component.currentUser.get("can_assign");
return component.currentUser?.can_assign;
},
};

View File

@ -8,17 +8,19 @@ import { isEmpty } from "@ember/utils";
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
export default Controller.extend(ModalFunctionality, {
topicBulkActions: controller(),
assignSuggestions: null,
allowedGroups: null,
taskActions: service(),
autofocus: not("capabilities.touch"),
assigneeName: or("model.username", "model.group_name"),
assigneeError: false,
export default class AssignUser extends Controller.extend(ModalFunctionality) {
@service taskActions;
@controller topicBulkActions;
init() {
this._super(...arguments);
assignSuggestions = null;
allowedGroups = null;
assigneeError = false;
@not("capabilities.touch") autofocus;
@or("model.username", "model.group_name") assigneeName;
constructor() {
super(...arguments);
this.set("allowedGroups", []);
this.set("assigneeError", false);
@ -31,17 +33,17 @@ export default Controller.extend(ModalFunctionality, {
this.set("allowedGroups", data.assign_allowed_on_groups);
this.set("allowedGroupsForAssignment", data.assign_allowed_for_groups);
});
},
}
onShow() {
this.set("assigneeError", false);
},
}
onClose() {
if (this.get("model.onClose") && this.get("model.username")) {
this.get("model.onClose")(this.get("model.username"));
if (this.model.onClose && this.model.username) {
this.model.onClose(this.model.username);
}
},
}
bulkAction(username, note) {
return this.topicBulkActions.performAndRefresh({
@ -49,19 +51,19 @@ export default Controller.extend(ModalFunctionality, {
username,
note,
});
},
}
@discourseComputed("siteSettings.enable_assign_status")
statusEnabled() {
return this.siteSettings.enable_assign_status;
},
}
@discourseComputed("siteSettings.assign_statuses")
availableStatuses() {
return this.siteSettings.assign_statuses.split("|").map((status) => {
return { id: status, name: status };
});
},
}
@discourseComputed("siteSettings.assign_statuses", "model.status")
status() {
@ -70,22 +72,19 @@ export default Controller.extend(ModalFunctionality, {
this.model.target.assignment_status ||
this.siteSettings.assign_statuses.split("|")[0]
);
},
}
@action
handleTextAreaKeydown(event) {
if ((event.ctrlKey || event.metaKey) && event.key === "Enter") {
this.assign();
}
},
}
@action
assign() {
if (this.isBulkAction) {
return this.bulkAction(
this.get("model.username"),
this.get("model.note")
);
return this.bulkAction(this.model.username, this.model.note);
}
if (!this.assigneeName) {
@ -95,18 +94,15 @@ export default Controller.extend(ModalFunctionality, {
let path = "/assign/assign";
if (isEmpty(this.get("model.username"))) {
if (isEmpty(this.model.username)) {
this.model.target.set("assigned_to_user", null);
}
if (isEmpty(this.get("model.group_name"))) {
if (isEmpty(this.model.group_name)) {
this.model.target.set("assigned_to_group", null);
}
if (
isEmpty(this.get("model.username")) &&
isEmpty(this.get("model.group_name"))
) {
if (isEmpty(this.model.username) && isEmpty(this.model.group_name)) {
path = "/assign/unassign";
}
@ -115,19 +111,19 @@ export default Controller.extend(ModalFunctionality, {
return ajax(path, {
type: "PUT",
data: {
username: this.get("model.username"),
group_name: this.get("model.group_name"),
target_id: this.get("model.target.id"),
target_type: this.get("model.targetType"),
note: this.get("model.note"),
status: this.get("model.status"),
username: this.model.username,
group_name: this.model.group_name,
target_id: this.model.target.id,
target_type: this.model.targetType,
note: this.model.note,
status: this.model.status,
},
})
.then(() => {
this.get("model.onSuccess")?.();
this.model.onSuccess?.();
})
.catch(popupAjaxError);
},
}
@action
assignUser(name) {
@ -136,27 +132,27 @@ export default Controller.extend(ModalFunctionality, {
if (name) {
return this.assign();
}
},
}
@action
assignUsername(selected) {
this.setGroupOrUser(selected.firstObject);
},
}
setGroupOrUser(name) {
this.set("assigneeError", false);
this.set("model.allowedGroups", this.taskActions.allowedGroups);
if (this.allowedGroupsForAssignment.includes(name)) {
this.setProperties({
"model.username": null,
"model.group_name": name,
"model.allowedGroups": this.taskActions.allowedGroups,
});
} else {
this.setProperties({
"model.username": name,
"model.group_name": null,
"model.allowedGroups": this.taskActions.allowedGroups,
});
}
},
});
}
}

View File

@ -6,22 +6,23 @@ import { inject as controller } from "@ember/controller";
import { inject as service } from "@ember/service";
import { action } from "@ember/object";
export default UserTopicsList.extend({
user: controller(),
taskActions: service(),
order: "",
ascending: false,
search: "",
bulkSelectEnabled: false,
selected: [],
canBulkSelect: alias("currentUser.staff"),
export default class GroupAssignedShow extends UserTopicsList {
@service taskActions;
@controller user;
queryParams: ["order", "ascending", "search"],
queryParams = ["order", "ascending", "search"];
order = "";
ascending = false;
search = "";
bulkSelectEnabled = false;
selected = [];
@alias("currentUser.staff") canBulkSelect;
_setSearchTerm(searchTerm) {
this.set("search", searchTerm);
this.refreshModel();
},
}
refreshModel() {
this.set("loading", true);
@ -39,21 +40,21 @@ export default UserTopicsList.extend({
.finally(() => {
this.set("loading", false);
});
},
}
@action
unassign(targetId, targetType = "Topic") {
this.taskActions
.unassign(targetId, targetType)
.then(() => this.send("changeAssigned"));
},
}
@action
reassign(topic) {
this.taskActions
.assign(topic)
.set("model.onSuccess", () => this.send("changeAssigned"));
},
}
@action
changeSort(sortBy) {
@ -64,20 +65,20 @@ export default UserTopicsList.extend({
this.setProperties({ order: sortBy, ascending: false });
this.refreshModel();
}
},
}
@action
onChangeFilter(value) {
discourseDebounce(this, this._setSearchTerm, value, INPUT_DELAY * 2);
},
}
@action
toggleBulkSelect() {
this.toggleProperty("bulkSelectEnabled");
},
}
@action
refresh() {
this.refreshModel();
},
});
}
}

View File

@ -6,33 +6,34 @@ import discourseComputed from "discourse-common/utils/decorators";
import discourseDebounce from "discourse-common/lib/debounce";
import { INPUT_DELAY } from "discourse-common/config/environment";
export default Controller.extend({
router: service(),
application: controller(),
loading: false,
offset: 0,
filterName: "",
filter: "",
export default class GroupAssigned extends Controller {
@service router;
@controller application;
loading = false;
offset = 0;
filterName = "";
filter = "";
@discourseComputed("router.currentRoute.queryParams.order")
order(order) {
return order || "";
},
}
@discourseComputed("router.currentRoute.queryParams.ascending")
ascending(ascending) {
return ascending || false;
},
}
@discourseComputed("router.currentRoute.queryParams.search")
search(search) {
return search || "";
},
}
@discourseComputed("site.mobileView")
isDesktop(mobileView) {
return !mobileView;
},
}
_setFilter(filter) {
this.set("loading", true);
@ -53,7 +54,7 @@ export default Controller.extend({
.finally(() => {
this.set("loading", false);
});
},
}
findMembers(refresh) {
if (refresh) {
@ -77,15 +78,15 @@ export default Controller.extend({
})
.finally(() => this.set("loading", false));
}
},
}
@action
loadMore() {
this.findMembers();
},
}
@action
onChangeFilterName(value) {
discourseDebounce(this, this._setFilter, value, INPUT_DELAY * 2);
},
});
}
}

View File

@ -10,23 +10,19 @@ import { iconHTML } from "discourse-common/lib/icon-library";
import I18n from "I18n";
import { htmlSafe } from "@ember/template";
export default UserTopicsList.extend({
user: controller(),
taskActions: service(),
queryParams: ["order", "ascending", "search"],
order: "",
ascending: false,
search: "",
export default class UserActivityAssigned extends UserTopicsList {
@service taskActions;
@controller user;
_setSearchTerm(searchTerm) {
this.set("search", searchTerm);
this.refreshModel();
},
queryParams = ["order", "ascending", "search"];
order = "";
ascending = false;
search = "";
@discourseComputed("model.topics.length", "search")
doesntHaveAssignments(topicsLength, search) {
return !search && !topicsLength;
},
}
@discourseComputed
emptyStateBody() {
@ -36,7 +32,12 @@ export default UserTopicsList.extend({
icon: iconHTML("user-plus"),
})
);
},
}
_setSearchTerm(searchTerm) {
this.set("search", searchTerm);
this.refreshModel();
}
refreshModel() {
this.set("loading", true);
@ -53,21 +54,21 @@ export default UserTopicsList.extend({
.finally(() => {
this.set("loading", false);
});
},
}
@action
unassign(targetId, targetType = "Topic") {
this.taskActions
.unassign(targetId, targetType)
.then(() => this.send("changeAssigned"));
},
}
@action
reassign(topic) {
this.taskActions
.assign(topic)
.set("model.onSuccess", () => this.send("changeAssigned"));
},
}
@action
changeSort(sortBy) {
@ -78,10 +79,10 @@ export default UserTopicsList.extend({
this.setProperties({ order: sortBy, ascending: false });
this.refreshModel();
}
},
}
@action
onChangeFilter(value) {
discourseDebounce(this, this._setSearchTerm, value, INPUT_DELAY * 2);
},
});
}
}

View File

@ -26,23 +26,17 @@ const DEPENDENT_KEYS = [
"topic.assigned_to_user.username",
];
function titleForState(name) {
if (name) {
return I18n.t("discourse_assign.unassign.help", {
username: name,
});
function defaultTitle(topic) {
const username =
topic.assigned_to_user?.username || topic.assigned_to_group?.name;
if (username) {
return I18n.t("discourse_assign.unassign.help", { username });
} else {
return I18n.t("discourse_assign.assign.help");
}
}
function defaultTitle(topic) {
return titleForState(
topic.get("topic.assigned_to_user.username") ||
topic.get("topic.assigned_to_group.name")
);
}
function includeIsAssignedOnTopic(api) {
api.modifyClass("model:topic", {
pluginId: PLUGIN_ID,
@ -57,7 +51,7 @@ function registerTopicFooterButtons(api) {
id: "reassign",
action(id) {
if (!this.get("currentUser.can_assign")) {
if (!this.currentUser?.can_assign) {
return;
}
@ -103,8 +97,8 @@ function registerTopicFooterButtons(api) {
},
noneItem() {
const user = this.get("topic.assigned_to_user");
const group = this.get("topic.assigned_to_group");
const user = this.topic.assigned_to_user;
const group = this.topic.assigned_to_group;
const label = I18n.t("discourse_assign.unassign.title_w_ellipsis");
const groupLabel = I18n.t("discourse_assign.unassign.title");
@ -149,8 +143,7 @@ function registerTopicFooterButtons(api) {
];
if (
this.topic.isAssigned() &&
this.get("topic.assigned_to_user")?.username !==
this.currentUser.username
this.topic.assigned_to_user?.username !== this.currentUser.username
) {
content.push({
id: "reassign-self",
@ -176,7 +169,7 @@ function registerTopicFooterButtons(api) {
displayed() {
return (
this.get("currentUser.can_assign") &&
this.currentUser?.can_assign &&
!this.site.mobileView &&
this.topic.isAssigned()
);
@ -194,16 +187,16 @@ function registerTopicFooterButtons(api) {
},
priority: 250,
translatedTitle() {
return defaultTitle(this);
return defaultTitle(this.topic);
},
translatedAriaLabel() {
return defaultTitle(this);
return defaultTitle(this.topic);
},
translatedLabel() {
return I18n.t("discourse_assign.assign.title");
},
action() {
if (!this.get("currentUser.can_assign")) {
if (!this.currentUser?.can_assign) {
return;
}
@ -238,14 +231,14 @@ function registerTopicFooterButtons(api) {
api.registerTopicFooterButton({
id: "unassign-mobile-header",
translatedTitle() {
return defaultTitle(this);
return defaultTitle(this.topic);
},
translatedAriaLabel() {
return defaultTitle(this);
return defaultTitle(this.topic);
},
translatedLabel() {
const user = this.get("topic.assigned_to_user");
const group = this.get("topic.assigned_to_group");
const user = this.topic.assigned_to_user;
const group = this.topic.assigned_to_group;
const label = I18n.t("discourse_assign.assigned_to_w_ellipsis");
if (user) {
@ -280,10 +273,10 @@ function registerTopicFooterButtons(api) {
return "user-times";
},
translatedTitle() {
return defaultTitle(this);
return defaultTitle(this.topic);
},
translatedAriaLabel() {
return defaultTitle(this);
return defaultTitle(this.topic);
},
translatedLabel() {
const label = I18n.t("discourse_assign.unassign.title");
@ -293,7 +286,7 @@ function registerTopicFooterButtons(api) {
);
},
action() {
if (!this.get("currentUser.can_assign")) {
if (!this.currentUser?.can_assign) {
return;
}
@ -341,7 +334,7 @@ function registerTopicFooterButtons(api) {
);
},
action() {
if (!this.get("currentUser.can_assign")) {
if (!this.currentUser?.can_assign) {
return;
}
@ -366,8 +359,7 @@ function registerTopicFooterButtons(api) {
this.site.mobileView &&
this.currentUser?.can_assign &&
this.topic.isAssigned() &&
this.get("topic.assigned_to_user")?.username !==
this.currentUser.username
this.topic.assigned_to_user?.username !== this.currentUser.username
);
},
});
@ -391,7 +383,7 @@ function registerTopicFooterButtons(api) {
);
},
action() {
if (!this.get("currentUser.can_assign")) {
if (!this.currentUser?.can_assign) {
return;
}
@ -611,13 +603,13 @@ function initialize(api) {
};
let assignedToIndirectly;
if (topic.get("indirectly_assigned_to")) {
assignedToIndirectly = Object.entries(
topic.get("indirectly_assigned_to")
).map(([key, value]) => {
value.assigned_to.assignedToPostId = key;
return value;
});
if (topic.indirectly_assigned_to) {
assignedToIndirectly = Object.entries(topic.indirectly_assigned_to).map(
([key, value]) => {
value.assigned_to.assignedToPostId = key;
return value;
}
);
} else {
assignedToIndirectly = [];
}
@ -821,7 +813,7 @@ function initialize(api) {
unsubscribe() {
this._super(...arguments);
if (!this.get("model.id")) {
if (!this.model?.id) {
return;
}
@ -899,7 +891,7 @@ export default {
SearchAdvancedOptions.reopen({
updateSearchTermForAssignedUsername() {
const match = this.filterBlocks(REGEXP_USERNAME_PREFIX);
const userFilter = this.get("searchedTerms.assigned");
const userFilter = this.searchedTerms?.assigned;
let searchTerm = this.searchTerm || "";
let keyword = "assigned";
@ -944,23 +936,20 @@ export default {
});
}
withPluginApi("0.13.0", (api) => includeIsAssignedOnTopic(api));
withPluginApi("0.11.0", (api) => initialize(api));
withPluginApi("0.8.28", (api) => registerTopicFooterButtons(api));
withPluginApi("0.13.0", (api) => {
includeIsAssignedOnTopic(api);
initialize(api);
registerTopicFooterButtons(api);
withPluginApi("0.11.7", (api) => {
api.addSearchSuggestion("in:assigned");
api.addSearchSuggestion("in:unassigned");
});
withPluginApi("0.12.2", (api) => {
api.addGroupPostSmallActionCode("assigned_group");
api.addGroupPostSmallActionCode("reassigned_group");
api.addGroupPostSmallActionCode("unassigned_group");
api.addGroupPostSmallActionCode("assigned_group_to_post");
api.addGroupPostSmallActionCode("unassigned_group_from_post");
});
withPluginApi("0.12.3", (api) => {
api.addUserSearchOption("assignableGroups");
});
},

View File

@ -1,49 +1,43 @@
import DiscourseRoute from "discourse/routes/discourse";
import { findOrResetCachedTopicList } from "discourse/lib/cached-topic-list";
export default DiscourseRoute.extend({
export default class GroupAssignedShow extends DiscourseRoute {
beforeModel(transition) {
if (!(transition.hasOwnProperty("from") && transition.from)) {
return;
}
if (transition.from.localName === "show") {
if (transition.from?.localName === "show") {
this.session.set("topicListScrollPosition", 1);
}
},
}
model(params) {
let filter = null;
if (
["everyone", this.modelFor("group").get("name")].includes(params.filter)
) {
filter = `topics/group-topics-assigned/${this.modelFor("group").get(
"name"
)}`;
let filter;
if (["everyone", this.modelFor("group").name].includes(params.filter)) {
filter = `topics/group-topics-assigned/${this.modelFor("group").name}`;
} else {
filter = `topics/messages-assigned/${params.filter}`;
}
const lastTopicList = findOrResetCachedTopicList(this.session, filter);
return lastTopicList
? lastTopicList
: this.store.findFiltered("topicList", {
filter,
params: {
order: params.order,
ascending: params.ascending,
search: params.search,
direct: params.filter !== "everyone",
},
});
},
return (
findOrResetCachedTopicList(this.session, filter) ||
this.store.findFiltered("topicList", {
filter,
params: {
order: params.order,
ascending: params.ascending,
search: params.search,
direct: params.filter !== "everyone",
},
})
);
}
setupController(controller, model) {
controller.setProperties({
model,
search: this.currentModel.params.search,
});
},
}
renderTemplate() {
this.render("group-topics-list");
},
});
}
}

View File

@ -2,10 +2,10 @@ import DiscourseRoute from "discourse/routes/discourse";
import { ajax } from "discourse/lib/ajax";
import { action } from "@ember/object";
export default DiscourseRoute.extend({
export default class GroupAssigned extends DiscourseRoute {
model() {
return ajax(`/assign/members/${this.modelFor("group").get("name")}`);
},
return ajax(`/assign/members/${this.modelFor("group").name}`);
}
setupController(controller, model) {
controller.setProperties({
@ -19,7 +19,7 @@ export default DiscourseRoute.extend({
});
controller.findMembers(true);
},
}
redirect(model, transition) {
if (transition.to.params.hasOwnProperty("filter")) {
@ -27,10 +27,10 @@ export default DiscourseRoute.extend({
} else {
this.transitionTo("group.assigned.show", "everyone");
}
},
}
@action
changeAssigned() {
this.refresh();
},
});
}
}

View File

@ -3,19 +3,19 @@ import UserTopicListRoute from "discourse/routes/user-topic-list";
import cookie from "discourse/lib/cookie";
import { action } from "@ember/object";
export default UserTopicListRoute.extend({
templateName: "user-activity-assigned",
controllerName: "user-activity-assigned",
export default class UserActivityAssigned extends UserTopicListRoute {
templateName = "user-activity-assigned";
controllerName = "user-activity-assigned";
userActionType: 16,
noContentHelpKey: "discourse_assigns.no_assigns",
userActionType = 16;
noContentHelpKey = "discourse_assigns.no_assigns";
beforeModel() {
if (!this.currentUser) {
cookie("destination_url", window.location.href);
this.transitionTo("login");
}
},
}
model(params) {
return this.store.findFiltered("topicList", {
@ -29,14 +29,14 @@ export default UserTopicListRoute.extend({
search: params.search,
},
});
},
}
titleToken() {
return I18n.t("discourse_assign.assigned");
},
}
@action
changeAssigned() {
this.refresh();
},
});
}
}

View File

@ -0,0 +1,9 @@
<div class="assigned-to-user">
{{avatar @user imageSize="small"}}
<span class="assigned-username">
{{@user.username}}
</span>
{{yield}}
</div>

View File

@ -1,5 +0,0 @@
import Component from "@ember/component";
export default Component.extend({
classNames: ["assigned-to-user"],
});

View File

@ -0,0 +1,86 @@
{{!
The `~` syntax strip spaces between the elements, making it produce
`<a class=topic-post-badges>Some text</a><span class=topic-post-badges>`,
with no space between them.
This causes the topic-post-badge to be considered the same word as "text"
at the end of the link, preventing it from line wrapping onto its own line.
}}
{{#if this.bulkSelectEnabled}}
<td class="bulk-select topic-list-data">
<input type="checkbox" class="bulk-select" />
</td>
{{/if}}
<td class="main-link clearfix topic-list-data" colspan="1">
<span class="link-top-line">
{{~raw "topic-status" topic=this.topic}}
{{~#if this.isPrivateMessage}}
{{~d-icon "envelope" class="private-message-icon"}}
{{~/if}}
{{~topic-link this.topic class="raw-link raw-topic-link"}}
{{~#if this.topic.featured_link}}
{{~topic-featured-link this.topic}}
{{~/if}}
{{~#if this.showTopicPostBadges}}
{{~raw
"topic-post-badges"
unread=this.topic.unread
unseen=this.topic.unseen
url=this.topic.lastUnreadUrl
newDotText=this.newDotText
}}
{{~/if}}
</span>
<div class="link-bottom-line">
{{#if (or (not this.hideCategory) (not this.topic.isPinnedUncategorized))}}
{{category-link this.topic.category}}
{{/if}}
{{discourse-tags this.topic mode="list" tagsForUser=this.tagsForUser}}
{{raw
"list/action-list"
topic=this.topic
postNumbers=this.topic.liked_post_numbers
className="likes"
icon="heart"
}}
</div>
{{#if this.expandPinned}}
{{raw "list/topic-excerpt" topic=this.topic}}
{{/if}}
</td>
{{#if this.showPosters}}
{{raw "list/posters-column" posters=this.topic.featuredUsers}}
{{/if}}
{{raw "list/posts-count-column" topic=this.topic}}
<td class="num views {{this.topic.viewsHeat}} topic-list-data">{{number
this.topic.views
numberKey="views_long"
}}</td>
{{raw
"list/activity-column"
topic=this.topic
class="num topic-list-data"
tagName="td"
}}
<td class="topic-list-data">
{{#if this.topic.assigned_to_user}}
<AssignActionsDropdown
@topic={{this.topic}}
@assignee={{this.topic.assigned_to_user.username}}
@unassign={{this.unassign}}
@reassign={{this.reassign}}
/>
{{else if this.topic.assigned_to_group}}
<AssignActionsDropdown
@topic={{this.topic}}
@assignee={{this.topic.assigned_to_group.name}}
@group={{true}}
@unassign={{this.unassign}}
@reassign={{this.reassign}}
/>
{{else}}
<AssignActionsDropdown @topic={{this.topic}} @unassign={{this.unassign}} />
{{/if}}
</td>

View File

@ -1,7 +1,8 @@
import TopicListItem from "discourse/components/topic-list-item";
import { equal } from "@ember/object/computed";
export default TopicListItem.extend({
classNames: ["assigned-topic-list-item"],
isPrivateMessage: equal("topic.archetype", "private_message"),
});
export default class AssignedTopicListItem extends TopicListItem {
classNames = ["assigned-topic-list-item"];
@equal("topic.archetype", "private_message") isPrivateMessage;
}

View File

@ -0,0 +1,46 @@
{{#unless this.skipHeader}}
<thead class="topic-list-header assigned-topic-list-header">
{{raw
"topic-list-header"
canBulkSelect=this.canBulkSelect
canDoBulkActions=this.canDoBulkActions
toggleInTitle=this.toggleInTitle
hideCategory=this.hideCategory
showPosters=true
showLikes=this.showLikes
showOpLikes=this.showOpLikes
order=this.order
ascending=this.ascending
sortable=this.sortable
listTitle=this.listTitle
bulkSelectEnabled=this.bulkSelectEnabled
}}
</thead>
{{/unless}}
<tbody class="topic-list-body assigned-topic-list-body">
{{#each this.filteredTopics as |topic|}}
<AssignedTopicListItem
@topic={{topic}}
@bulkSelectEnabled={{this.bulkSelectEnabled}}
@showTopicPostBadges={{this.showTopicPostBadges}}
@hideCategory={{this.hideCategory}}
@showPosters={{true}}
@showLikes={{this.showLikes}}
@showOpLikes={{this.showOpLikes}}
@expandGloballyPinned={{this.expandGloballyPinned}}
@expandAllPinned={{this.expandAllPinned}}
@lastVisitedTopic={{this.lastVisitedTopic}}
@selected={{this.selected}}
@tagsForUser={{this.tagsForUser}}
@unassign={{this.unassign}}
@reassign={{this.reassign}}
/>
{{raw
"list/visited-line"
lastVisitedTopic=this.lastVisitedTopic
topic=topic
}}
{{/each}}
</tbody>

View File

@ -1,3 +1,3 @@
import EmailGroupUserChooserRow from "select-kit/components/email-group-user-chooser-row";
export default EmailGroupUserChooserRow.extend();
export default class AssigneeChooserRow extends EmailGroupUserChooserRow {}

View File

@ -1,7 +1,7 @@
import EmailGroupUserChooser from "select-kit/components/email-group-user-chooser";
export default EmailGroupUserChooser.extend({
export default class AssigneeChooser extends EmailGroupUserChooser {
modifyComponentForRow() {
return "assignee-chooser-row";
},
});
}
}

View File

@ -0,0 +1,40 @@
<ConditionalLoadingSpinner @condition={{this.loading}}>
{{#if this.hasIncoming}}
<div class="show-mores">
<a href class="alert alert-info clickable" {{action this.showInserted}}>
<CountI18n
@key="topic_count_"
@suffix="latest"
@count={{this.incomingCount}}
/>
</a>
</div>
{{/if}}
{{#if this.topics}}
<AssignedTopicList
@showPosters={{this.showPosters}}
@hideCategory={{this.hideCategory}}
@topics={{this.topics}}
@expandExcerpts={{this.expandExcerpts}}
@bulkSelectEnabled={{this.bulkSelectEnabled}}
@canBulkSelect={{this.canBulkSelect}}
@bulkSelectAction={{this.bulkSelectAction}}
@selected={{this.selected}}
@skipHeader={{this.skipHeader}}
@tagsForUser={{this.tagsForUser}}
@changeSort={{this.changeSort}}
@toggleBulkSelect={{this.toggleBulkSelect}}
@unassign={{this.unassign}}
@reassign={{this.reassign}}
@onScroll={{this.onScroll}}
@scrollOnLoad={{this.scrollOnLoad}}
/>
{{else}}
{{#unless this.loadingMore}}
<div class="alert alert-info">
{{i18n "choose_topic.none_found"}}
</div>
{{/unless}}
{{/if}}
</ConditionalLoadingSpinner>

View File

@ -7,21 +7,21 @@ function assignIfEqual(topic, data) {
}
}
export default Component.extend({
export default class FlaggedTopicListener extends Component {
didInsertElement() {
this._super();
super.didInsertElement(...arguments);
this.messageBus.subscribe("/staff/topic-assignment", (data) => {
let flaggedTopics = this.flaggedTopics;
if (flaggedTopics) {
flaggedTopics.forEach((ft) => assignIfEqual(ft.topic, data));
if (this.flaggedTopics) {
this.flaggedTopics.forEach((ft) => assignIfEqual(ft.topic, data));
} else {
assignIfEqual(this.topic, data);
}
});
},
}
willDestroyElement() {
this._super();
super.willDestroyElement(...arguments);
this.messageBus.unsubscribe("/staff/topic-assignment");
},
});
}
}

View File

@ -0,0 +1,55 @@
<li>
{{#if @showAvatar}}
<LinkTo
@route="group.assigned.show"
@model={{@filter.username_lower}}
@query={{hash order=@order ascending=@ascending search=@search}}
>
<div class="assign-image">
<a
href={{@filter.userPath}}
data-user-card={{@filter.username}}
>{{avatar filter imageSize="large"}}</a>
</div>
<div class="assign-names">
<div class="assign-username">{{format-username @filter.username}}</div>
<div class="assign-name">{{@filter.name}}</div>
</div>
<div class="assign-count">
{{@filter.assignments_count}}
</div>
</LinkTo>
{{else if @groupName}}
<LinkTo
@route="group.assigned.show"
@model={{@filter}}
@query={{hash order=@order ascending=@ascending search=@search}}
>
<div class="assign-image">
{{d-icon "group-plus"}}
</div>
<div class="assign-names">
<div class="assign-username">{{@groupName}}</div>
</div>
<div class="assign-count">
{{@assignmentCount}}
</div>
</LinkTo>
{{else}}
<LinkTo
@route="group.assigned.show"
@model={{@filter}}
@query={{hash order=@order ascending=@ascending search=@search}}
>
<div class="assign-everyone">
{{i18n "discourse_assign.group_everyone"}}
</div>
<div class="assign-count">
{{@assignmentCount}}
</div>
</LinkTo>
{{/if}}
</li>

View File

@ -1,5 +0,0 @@
import Component from "@ember/component";
export default Component.extend({
tagName: "li",
});

View File

@ -1,4 +1,4 @@
<LinkTo @route="group.assigned">
{{d-icon "group-plus" class="glyph"}}{{i18n "discourse_assign.assigned"}}
({{group.assignment_count}})
({{@group.assignment_count}})
</LinkTo>

View File

@ -1,5 +0,0 @@
import Component from "@ember/component";
export default Component.extend({
tagName: "",
});

View File

@ -0,0 +1,14 @@
{{#if this.siteSettings.assign_enabled}}
<div class="controls controls-dropdown">
<label>{{i18n "discourse_assign.reminders_frequency.description"}}</label>
<ComboBox
@id="remind-assigns-frequency"
@valueProperty="value"
@content={{this.availableFrequencies}}
@value={{this.selectedFrequency}}
@onChange={{action
(mut this.user.custom_fields.remind_assigns_frequency)
}}
/>
</div>
{{/if}}

View File

@ -2,7 +2,7 @@ import Component from "@ember/component";
import I18n from "I18n";
import discourseComputed from "discourse-common/utils/decorators";
export default Component.extend({
export default class RemindAssignsFrequency extends Component {
@discourseComputed(
"user.custom_fields.remind_assigns_frequency",
"siteSettings.remind_assigns_frequency"
@ -17,16 +17,14 @@ export default Component.extend({
}
return siteDefaultAssignsFrequency;
},
}
@discourseComputed("user.reminders_frequency")
availableFrequencies(userRemindersFrequency) {
return userRemindersFrequency.map((freq) => {
return {
name: I18n.t(freq.name),
value: freq.value,
selected: false,
};
});
},
});
return userRemindersFrequency.map((freq) => ({
name: I18n.t(freq.name),
value: freq.value,
selected: false,
}));
}
}

View File

@ -2,7 +2,7 @@ import Service from "@ember/service";
import { ajax } from "discourse/lib/ajax";
import showModal from "discourse/lib/show-modal";
export default Service.extend({
export default class TaskActions extends Service {
i18nSuffix(targetType) {
switch (targetType) {
case "Post":
@ -10,7 +10,7 @@ export default Service.extend({
case "Topic":
return "_modal";
}
},
}
unassign(targetId, targetType = "Topic") {
return ajax("/assign/unassign", {
@ -20,7 +20,7 @@ export default Service.extend({
target_type: targetType,
},
});
},
}
assign(target, options = { isAssigned: false, targetType: "Topic" }) {
return showModal("assign-user", {
@ -37,7 +37,7 @@ export default Service.extend({
status: target.assignment_status,
},
});
},
}
reassignUserToTopic(user, target, targetType = "Topic") {
return ajax("/assign/assign", {
@ -49,5 +49,5 @@ export default Service.extend({
status: target.assignment_status,
},
});
},
});
}
}

View File

@ -1,5 +0,0 @@
{{avatar user imageSize="small"}}
<span class="assigned-username">
{{user.username}}
</span>
{{yield}}

View File

@ -1,88 +0,0 @@
{{!
The `~` syntax strip spaces between the elements, making it produce
`<a class=topic-post-badges>Some text</a><span class=topic-post-badges>`,
with no space between them.
This causes the topic-post-badge to be considered the same word as "text"
at the end of the link, preventing it from line wrapping onto its own line.
}}
{{#if bulkSelectEnabled}}
<td class="bulk-select topic-list-data">
<input type="checkbox" class="bulk-select" />
</td>
{{/if}}
<td class="main-link clearfix topic-list-data" colspan="1">
<span class="link-top-line">
{{~raw "topic-status" topic=topic}}
{{~#if isPrivateMessage}}
{{~d-icon "envelope" class="private-message-icon"}}
{{~/if}}
{{~topic-link topic class="raw-link raw-topic-link"}}
{{~#if topic.featured_link}}
{{~topic-featured-link topic}}
{{~/if}}
{{~#if showTopicPostBadges}}
{{~raw
"topic-post-badges"
unread=topic.unread
unseen=topic.unseen
url=topic.lastUnreadUrl
newDotText=newDotText
}}
{{~/if}}
</span>
<div class="link-bottom-line">
{{#unless hideCategory}}
{{#unless topic.isPinnedUncategorized}}
{{category-link topic.category}}
{{/unless}}
{{/unless}}
{{discourse-tags topic mode="list" tagsForUser=tagsForUser}}
{{raw
"list/action-list"
topic=topic
postNumbers=topic.liked_post_numbers
className="likes"
icon="heart"
}}
</div>
{{#if expandPinned}}
{{raw "list/topic-excerpt" topic=topic}}
{{/if}}
</td>
{{#if showPosters}}
{{raw "list/posters-column" posters=topic.featuredUsers}}
{{/if}}
{{raw "list/posts-count-column" topic=topic}}
<td class="num views {{topic.viewsHeat}} topic-list-data">{{number
topic.views
numberKey="views_long"
}}</td>
{{raw
"list/activity-column"
topic=topic
class="num topic-list-data"
tagName="td"
}}
<td class="topic-list-data">
{{#if topic.assigned_to_user}}
<AssignActionsDropdown
@topic={{topic}}
@assignee={{topic.assigned_to_user.username}}
@unassign={{unassign}}
@reassign={{reassign}}
/>
{{else if topic.assigned_to_group}}
<AssignActionsDropdown
@topic={{topic}}
@assignee={{topic.assigned_to_group.name}}
@group={{true}}
@unassign={{unassign}}
@reassign={{reassign}}
/>
{{else}}
<AssignActionsDropdown @topic={{topic}} @unassign={{unassign}} />
{{/if}}
</td>

View File

@ -1,42 +0,0 @@
{{#unless skipHeader}}
<thead class="topic-list-header assigned-topic-list-header">
{{raw
"topic-list-header"
canBulkSelect=canBulkSelect
canDoBulkActions=canDoBulkActions
toggleInTitle=toggleInTitle
hideCategory=hideCategory
showPosters=true
showLikes=showLikes
showOpLikes=showOpLikes
order=order
ascending=ascending
sortable=sortable
listTitle=listTitle
bulkSelectEnabled=bulkSelectEnabled
}}
</thead>
{{/unless}}
<tbody class="topic-list-body assigned-topic-list-body">
{{#each filteredTopics as |topic|}}
<AssignedTopicListItem
@topic={{topic}}
@bulkSelectEnabled={{bulkSelectEnabled}}
@showTopicPostBadges={{showTopicPostBadges}}
@hideCategory={{hideCategory}}
@showPosters={{true}}
@showLikes={{showLikes}}
@showOpLikes={{showOpLikes}}
@expandGloballyPinned={{expandGloballyPinned}}
@expandAllPinned={{expandAllPinned}}
@lastVisitedTopic={{lastVisitedTopic}}
@selected={{selected}}
@tagsForUser={{tagsForUser}}
@unassign={{unassign}}
@reassign={{reassign}}
/>
{{raw "list/visited-line" lastVisitedTopic=lastVisitedTopic topic=topic}}
{{/each}}
</tbody>

View File

@ -1,40 +0,0 @@
<ConditionalLoadingSpinner @condition={{loading}}>
{{#if hasIncoming}}
<div class="show-mores">
<a href class="alert alert-info clickable" {{action showInserted}}>
<CountI18n
@key="topic_count_"
@suffix="latest"
@count={{incomingCount}}
/>
</a>
</div>
{{/if}}
{{#if topics}}
<AssignedTopicList
@showPosters={{showPosters}}
@hideCategory={{hideCategory}}
@topics={{topics}}
@expandExcerpts={{expandExcerpts}}
@bulkSelectEnabled={{bulkSelectEnabled}}
@canBulkSelect={{canBulkSelect}}
@bulkSelectAction={{bulkSelectAction}}
@selected={{selected}}
@skipHeader={{skipHeader}}
@tagsForUser={{tagsForUser}}
@changeSort={{changeSort}}
@toggleBulkSelect={{toggleBulkSelect}}
@unassign={{unassign}}
@reassign={{reassign}}
@onScroll={{onScroll}}
@scrollOnLoad={{scrollOnLoad}}
/>
{{else}}
{{#unless loadingMore}}
<div class="alert alert-info">
{{i18n "choose_topic.none_found"}}
</div>
{{/unless}}
{{/if}}
</ConditionalLoadingSpinner>

View File

@ -1,53 +0,0 @@
{{#if showAvatar}}
<LinkTo
@route="group.assigned.show"
@model={{filter.username_lower}}
@query={{hash order=order ascending=ascending search=search}}
>
<div class="assign-image">
<a href={{filter.userPath}} data-user-card={{filter.username}}>{{avatar
filter
imageSize="large"
}}</a>
</div>
<div class="assign-names">
<div class="assign-username">{{format-username filter.username}}</div>
<div class="assign-name">{{filter.name}}</div>
</div>
<div class="assign-count">
{{filter.assignments_count}}
</div>
</LinkTo>
{{else if groupName}}
<LinkTo
@route="group.assigned.show"
@model={{filter}}
@query={{hash order=order ascending=ascending search=search}}
>
<div class="assign-image">
{{d-icon "group-plus"}}
</div>
<div class="assign-names">
<div class="assign-username">{{groupName}}</div>
</div>
<div class="assign-count">
{{assignmentCount}}
</div>
</LinkTo>
{{else}}
<LinkTo
@route="group.assigned.show"
@model={{filter}}
@query={{hash order=order ascending=ascending search=search}}
>
<div class="assign-everyone">
{{i18n "discourse_assign.group_everyone"}}
</div>
<div class="assign-count">
{{assignmentCount}}
</div>
</LinkTo>
{{/if}}

View File

@ -1,12 +0,0 @@
{{#if siteSettings.assign_enabled}}
<div class="controls controls-dropdown">
<label>{{i18n "discourse_assign.reminders_frequency.description"}}</label>
<ComboBox
@id="remind-assigns-frequency"
@valueProperty="value"
@content={{availableFrequencies}}
@value={{selectedFrequency}}
@onChange={{action (mut user.custom_fields.remind_assigns_frequency)}}
/>
</div>
{{/if}}

View File

@ -2,7 +2,7 @@
<div class="inline-form full-width">
<Input
class="no-blur"
@value={{readonly search}}
@value={{readonly this.search}}
placeholder={{i18n "discourse_assign.topic_search_placeholder"}}
autocomplete="off"
@type="search"
@ -10,31 +10,31 @@
/>
</div>
</div>
<LoadMore
@class="paginated-topics-list"
@selector=".paginated-topics-list .topic-list tr"
@action={{action "loadMore"}}
>
<BasicAssignedTopicList
@topicList={{model}}
@hideCategory={{hideCategory}}
@showPosters={{showPosters}}
@bulkSelectEnabled={{bulkSelectEnabled}}
@canBulkSelect={{canBulkSelect}}
@selected={{selected}}
@hasIncoming={{hasIncoming}}
@incomingCount={{incomingCount}}
@topicList={{this.model}}
@hideCategory={{this.hideCategory}}
@showPosters={{this.showPosters}}
@bulkSelectEnabled={{this.bulkSelectEnabled}}
@canBulkSelect={{this.canBulkSelect}}
@selected={{this.selected}}
@hasIncoming={{this.hasIncoming}}
@incomingCount={{this.incomingCount}}
@showInserted={{action "showInserted"}}
@tagsForUser={{tagsForUser}}
@tagsForUser={{this.tagsForUser}}
@changeSort={{action "changeSort"}}
@toggleBulkSelect={{action "toggleBulkSelect"}}
@bulkSelectAction={{action "refresh"}}
@unassign={{action "unassign"}}
@reassign={{action "reassign"}}
@onScroll={{saveScrollPosition}}
@onScroll={{this.saveScrollPosition}}
@scrollOnLoad={{true}}
/>
<ConditionalLoadingSpinner @condition={{model.loadingMore}} />
<ConditionalLoadingSpinner @condition={{this.model.loadingMore}} />
</LoadMore>

View File

@ -3,53 +3,58 @@
@class="activity-nav"
@desktopClass="action-list activity-list nav-stacked"
>
{{#if isDesktop}}
{{#if this.isDesktop}}
<div class="search-div">
<Input
@type="text"
placeholder={{i18n
"discourse_assign.sidebar_name_filter_placeholder"
}}
@value={{readonly filterName}}
@value={{readonly this.filterName}}
class="search"
{{on "input" (action "onChangeFilterName" value="target.value")}}
{{on "input" (action this.onChangeFilterName value="target.value")}}
/>
</div>
{{/if}}
<LoadMore @selector=".activity-nav li" @action={{action "loadMore"}}>
<GroupAssignedFilter
@showAvatar={{false}}
@filter="everyone"
@routeType={{route_type}}
@assignmentCount={{group.assignment_count}}
@search={{search}}
@ascending={{ascending}}
@order={{order}}
@routeType={{this.route_type}}
@assignmentCount={{this.group.assignment_count}}
@search={{this.search}}
@ascending={{this.ascending}}
@order={{this.order}}
/>
<GroupAssignedFilter
@showAvatar={{false}}
@groupName={{group.name}}
@filter={{group.name}}
@routeType={{route_type}}
@assignmentCount={{group.group_assignment_count}}
@search={{search}}
@ascending={{ascending}}
@order={{order}}
@groupName={{this.group.name}}
@filter={{this.group.name}}
@routeType={{this.route_type}}
@assignmentCount={{this.group.group_assignment_count}}
@search={{this.search}}
@ascending={{this.ascending}}
@order={{this.order}}
/>
{{#each members as |member|}}
{{#each this.members as |member|}}
<GroupAssignedFilter
@showAvatar={{true}}
@filter={{member}}
@routeType={{route_type}}
@search={{search}}
@ascending={{ascending}}
@order={{order}}
@routeType={{this.route_type}}
@search={{this.search}}
@ascending={{this.ascending}}
@order={{this.order}}
/>
{{/each}}
<ConditionalLoadingSpinner @condition={{loading}} />
<ConditionalLoadingSpinner @condition={{this.loading}} />
</LoadMore>
</MobileNav>
</section>
<section class="user-content">
{{outlet}}
</section>

View File

@ -1,25 +1,25 @@
<td class="topic-list-data">
<div class="main-link">
<TopicStatus @topic={{topic}} />
{{~#if isPrivateMessage}}
<TopicStatus @topic={{this.topic}} />
{{~#if this.isPrivateMessage}}
{{~d-icon "envelope" class="private-message-icon"}}
{{~/if}}
{{~topic-link topic}}
{{#if topic.unseen}}
{{~topic-link this.topic}}
{{#if this.topic.unseen}}
<span class="badge-notification new-topic"></span>
{{/if}}
{{#if topic.hasExcerpt}}
{{#if this.topic.hasExcerpt}}
<div class="topic-excerpt">
{{html-safe topic.excerpt}}
{{#if topic.excerptTruncated}}
{{#unless topic.canClearPin}}<a href={{topic.url}}>{{i18n
{{html-safe this.topic.excerpt}}
{{#if this.topic.excerptTruncated}}
{{#unless this.topic.canClearPin}}<a href={{this.topic.url}}>{{i18n
"read_more"
}}</a>{{/unless}}
{{/if}}
{{#if topic.canClearPin}}
{{#if this.topic.canClearPin}}
<a
href
{{action "clearPin" topic}}
{{action "clearPin" this.topic}}
title={{i18n "topic.clear_pin.help"}}
>{{i18n "topic.clear_pin.title"}}</a>
{{/if}}
@ -27,43 +27,49 @@
{{/if}}
</div>
<div class="pull-right topic-list-num">
{{#if topic.assigned_to_user}}
{{#if this.topic.assigned_to_user}}
<AssignActionsDropdown
@topic={{topic}}
@assignee={{topic.assigned_to_user.username}}
@unassign={{unassign}}
@reassign={{reassign}}
@topic={{this.topic}}
@assignee={{this.topic.assigned_to_user.username}}
@unassign={{this.unassign}}
@reassign={{this.reassign}}
/>
{{else if topic.assigned_to_group}}
{{else if this.topic.assigned_to_group}}
<AssignActionsDropdown
@topic={{topic}}
@assignee={{topic.assigned_to_group.name}}
@topic={{this.topic}}
@assignee={{this.topic.assigned_to_group.name}}
@group={{true}}
@unassign={{unassign}}
@reassign={{reassign}}
@unassign={{this.unassign}}
@reassign={{this.reassign}}
/>
{{else}}
<AssignActionsDropdown @topic={{topic}} @unassign={{unassign}} />
<AssignActionsDropdown
@topic={{this.topic}}
@unassign={{this.unassign}}
/>
{{/if}}
</div>
<div class="clearfix"></div>
<div class="topic-item-stats clearfix">
{{discourse-tags topic mode="list" tagsForUser=tagsForUser}}
{{discourse-tags this.topic mode="list" tagsForUser=this.tagsForUser}}
<div class="pull-right topic-list-num">
{{raw
"list/activity-column"
topic=topic
topic=this.topic
tagName="div"
class="num activity last"
}}
<a
href={{topic.lastPostUrl}}
title="{{i18n 'last_post'}}: {{html-safe raw-date topic.bumped_at}}"
>{{topic.last_poster_username}}</a>
href={{this.topic.lastPostUrl}}
title="{{i18n 'last_post'}}: {{html-safe
raw-date
this.topic.bumped_at
}}"
>{{this.topic.last_poster_username}}</a>
</div>
{{#unless hideCategory}}
{{#unless this.hideCategory}}
<div class="category">
{{category-link topic.category}}
{{category-link this.topic.category}}
</div>
{{/unless}}
<div class="clearfix"></div>

View File

@ -1,29 +1,33 @@
<ConditionalLoadingSpinner @condition={{loading}}>
{{#if hasIncoming}}
<ConditionalLoadingSpinner @condition={{this.loading}}>
{{#if this.hasIncoming}}
<div class="show-mores">
<a href class="alert alert-info clickable" {{action showInserted}}>
<a
href
class="alert alert-info clickable"
{{on "click" this.showInserted}}
>
<CountI18n
@key="topic_count_"
@suffix="latest"
@count={{incomingCount}}
@count={{this.incomingCount}}
/>
</a>
</div>
{{/if}}
{{#if topics}}
{{#if this.topics}}
<table class="topic-list assigned-topic-list">
<tbody class="topic-list-body assigned-topic-list-body">
{{#each topics as |t|}}
{{#each this.topics as |topic|}}
<AssignedTopicListItem
@topic={{t}}
@expandGloballyPinned={{expandGloballyPinned}}
@expandAllPinned={{expandAllPinned}}
@lastVisitedTopic={{lastVisitedTopic}}
@selected={{selected}}
@tagsForUser={{tagsForUser}}
@unassign={{unassign}}
@reassign={{reassign}}
@topic={{topic}}
@expandGloballyPinned={{this.expandGloballyPinned}}
@expandAllPinned={{this.expandAllPinned}}
@lastVisitedTopic={{this.lastVisitedTopic}}
@selected={{this.selected}}
@tagsForUser={{this.tagsForUser}}
@unassign={{this.unassign}}
@reassign={{this.reassign}}
/>
{{/each}}
</tbody>

View File

@ -4,22 +4,22 @@
<label>{{i18n "discourse_assign.assign_modal.assignee_label"}}</label>
<AssigneeChooser
autocomplete="off"
@value={{assigneeName}}
@value={{this.assigneeName}}
@onChange={{action "assignUsername"}}
autofocus="autofocus"
@showUserStatus={{true}}
@options={{hash
mobilePlacementStrategy="absolute"
filterPlaceholder=placeholderKey
filterPlaceholder=this.placeholderKey
includeGroups=true
customSearchOptions=(hash
assignableGroups=true defaultSearchResults=this.assignSuggestions
)
groupMembersOf=allowedGroups
groupMembersOf=this.allowedGroups
maximum=1
autofocus=autofocus
autofocus=this.autofocus
tabindex=1
expandedOnInsert=(not assigneeName)
expandedOnInsert=(not this.assigneeName)
caretUpIcon="search"
caretDownIcon="search"
}}
@ -37,9 +37,9 @@
<label>{{i18n "discourse_assign.assign_modal.status_label"}}</label>
<ComboBox
@id="assign-status"
@content={{availableStatuses}}
@value={{status}}
@onChange={{action (mut model.status)}}
@content={{this.availableStatuses}}
@value={{this.status}}
@onChange={{action (mut this.model.status)}}
/>
</div>
{{/if}}
@ -51,8 +51,8 @@
>{{i18n "discourse_assign.assign_modal.optional_label"}}</span>
</label>
<Textarea
id={{"assign-modal-note"}}
@value={{model.note}}
id="assign-modal-note"
@value={{this.model.note}}
{{! template-lint-disable no-down-event-binding }}
{{on "keydown" (action "handleTextAreaKeydown")}}
/>
@ -63,14 +63,14 @@
<div class="modal-footer">
<DButton
@label={{if
model.reassign
this.model.reassign
"discourse_assign.reassign.title"
"discourse_assign.assign_modal.assign"
}}
@icon={{inviteIcon}}
@icon={{this.inviteIcon}}
@action={{this.assign}}
class="btn-primary"
@disabled={{disabled}}
@disabled={{this.disabled}}
/>
<DModalCancel @close={{route-action "closeModal"}} />
</div>

View File

@ -1,18 +1,18 @@
{{#if doesntHaveAssignments}}
{{#if this.doesntHaveAssignments}}
<EmptyState
@title={{i18n "user.no_assignments_title"}}
@body={{emptyStateBody}}
@body={{this.emptyStateBody}}
/>
{{else}}
<div class="topic-search-div">
<div class="inline-form full-width">
<Input
class="no-blur"
@value={{readonly search}}
placeholder={{i18n "discourse_assign.topic_search_placeholder"}}
autocomplete="off"
@value={{readonly this.search}}
@type="search"
{{on "input" (action "onChangeFilter" value="target.value")}}
class="no-blur"
placeholder={{i18n "discourse_assign.topic_search_placeholder"}}
autocomplete="off"
/>
</div>
</div>
@ -23,22 +23,22 @@
@action={{action "loadMore"}}
>
<BasicAssignedTopicList
@topicList={{model}}
@hideCategory={{hideCategory}}
@topicList={{this.model}}
@hideCategory={{this.hideCategory}}
@showPosters={{true}}
@bulkSelectEnabled={{bulkSelectEnabled}}
@selected={{selected}}
@hasIncoming={{hasIncoming}}
@incomingCount={{incomingCount}}
@showInserted={{action "showInserted"}}
@tagsForUser={{tagsForUser}}
@changeSort={{action "changeSort"}}
@unassign={{action "unassign"}}
@reassign={{action "reassign"}}
@onScroll={{saveScrollPosition}}
@bulkSelectEnabled={{this.bulkSelectEnabled}}
@selected={{this.selected}}
@hasIncoming={{this.hasIncoming}}
@incomingCount={{this.incomingCount}}
@showInserted={{this.showInserted}}
@tagsForUser={{this.tagsForUser}}
@changeSort={{this.changeSort}}
@unassign={{this.unassign}}
@reassign={{this.reassign}}
@onScroll={{this.saveScrollPosition}}
@scrollOnLoad={{true}}
/>
<ConditionalLoadingSpinner @condition={{model.loadingMore}} />
<ConditionalLoadingSpinner @condition={{this.model.loadingMore}} />
</LoadMore>
{{/if}}

View File

@ -1,7 +1,7 @@
{
"name": "discourse",
"version": "1.0.0",
"repository": "git@github.com:discourse/discourse-assign.git",
"version": "1.0.1",
"repository": "https://github.com/discourse/discourse-assign",
"author": "Discourse",
"license": "MIT",
"devDependencies": {

View File

@ -5,7 +5,6 @@
# version: 1.0.1
# authors: Sam Saffron
# url: https://github.com/discourse/discourse-assign
# transpile_js: true
enabled_site_setting :assign_enabled

View File

@ -14,6 +14,6 @@ acceptance("Discourse Assign | Assign disabled mobile", function (needs) {
const menu = selectKit(".topic-footer-mobile-dropdown");
await menu.expand();
assert.notOk(menu.rowByValue("assign").exists());
assert.false(menu.rowByValue("assign").exists());
});
});

View File

@ -3,7 +3,6 @@ import { cloneJSON } from "discourse-common/lib/object";
import userFixtures from "discourse/tests/fixtures/user-fixtures";
import {
acceptance,
exists,
updateCurrentUser,
} from "discourse/tests/helpers/qunit-helpers";
import { click, visit } from "@ember/test-helpers";
@ -43,9 +42,9 @@ acceptance("Discourse Assign | Assign mobile", function (needs) {
const menu = selectKit(".topic-footer-mobile-dropdown");
await menu.expand();
assert.ok(menu.rowByValue("assign").exists());
assert.true(menu.rowByValue("assign").exists());
await menu.selectRowByValue("assign");
assert.ok(exists(".assign.modal-body"), "assign modal opens");
assert.dom(".assign.modal-body").exists("assign modal opens");
});
});
@ -81,25 +80,23 @@ acceptance("Discourse Assign | Assign desktop", function (needs) {
test("Post contains hidden assign button", async function (assert) {
await visit("/t/internationalization-localization/280");
assert.ok(
!exists("#post_2 .extra-buttons .d-icon-user-plus"),
"assign to post button is hidden"
);
assert
.dom("#post_2 .extra-buttons .d-icon-user-plus")
.doesNotExist("assign to post button is hidden");
await click("#post_2 button.show-more-actions");
assert.ok(
exists("#post_2 .extra-buttons .d-icon-user-plus"),
"assign to post button exists"
);
assert
.dom("#post_2 .extra-buttons .d-icon-user-plus")
.exists("assign to post button exists");
await click("#post_2 .extra-buttons .d-icon-user-plus");
assert.ok(exists(".assign.modal-body"), "assign modal opens");
assert.dom(".assign.modal-body").exists("assign modal opens");
});
test("Footer dropdown contains button", async function (assert) {
await visit("/t/internationalization-localization/280");
await click("#topic-footer-button-assign");
assert.ok(exists(".assign.modal-body"), "assign modal opens");
assert.dom(".assign.modal-body").exists("assign modal opens");
});
});
@ -140,10 +137,9 @@ acceptance("Discourse Assign | Assign Status enabled", function (needs) {
await visit("/t/internationalization-localization/280");
await click("#topic-footer-button-assign");
assert.ok(
exists(".assign.modal-body #assign-status"),
"assign status dropdown exists"
);
assert
.dom(".assign.modal-body #assign-status")
.exists("assign status dropdown exists");
});
});
@ -180,10 +176,9 @@ acceptance("Discourse Assign | Assign Status disabled", function (needs) {
await visit("/t/internationalization-localization/280");
await click("#topic-footer-button-assign");
assert.notOk(
exists(".assign.modal-body #assign-status"),
"assign status dropdown doesn't exists"
);
assert
.dom(".assign.modal-body #assign-status")
.doesNotExist("assign status dropdown doesn't exists");
});
});

View File

@ -1,7 +1,6 @@
import { test } from "qunit";
import {
acceptance,
exists,
query,
updateCurrentUser,
} from "discourse/tests/helpers/qunit-helpers";
@ -99,62 +98,59 @@ acceptance("Discourse Assign | Assigned topic", function (needs) {
updateCurrentUser({ can_assign: true });
await visit("/t/assignment-topic/44");
assert.strictEqual(
query("#topic-title .assigned-to").innerText.trim(),
"eviltrout",
"shows assignment in the header"
);
assert.strictEqual(
query("#post_1 .assigned-to").innerText,
"Assigned topic to eviltrout#2 to Developers",
"shows assignment and indirect assignments in the first post"
);
assert.ok(exists("#post_1 .assigned-to svg.d-icon-user-plus"));
assert.strictEqual(
query(".discourse-tags .assigned-to[href='/t/28830'] span").title,
"Shark Doododooo",
"shows topic assign notes"
);
assert.strictEqual(
query(".discourse-tags .assigned-to[href='/p/2'] span").title,
'<script>alert("xss")</script>',
"shows indirect assign notes"
);
assert.ok(
exists("#topic-footer-dropdown-reassign"),
"shows reassign dropdown at the bottom of the topic"
);
assert
.dom("#topic-title .assigned-to")
.hasText("eviltrout", "shows assignment in the header");
assert
.dom("#post_1 .assigned-to")
.hasText(
"Assigned topic to eviltrout#2 to Developers",
"shows assignment and indirect assignments in the first post"
);
assert.dom("#post_1 .assigned-to svg.d-icon-user-plus").exists();
assert
.dom(".discourse-tags .assigned-to[href='/t/28830'] span")
.hasAttribute("title", "Shark Doododooo", "shows topic assign notes");
assert
.dom(".discourse-tags .assigned-to[href='/p/2'] span")
.hasAttribute(
"title",
'<script>alert("xss")</script>',
"shows indirect assign notes"
);
assert
.dom("#topic-footer-dropdown-reassign")
.exists("shows reassign dropdown at the bottom of the topic");
});
test("Shows group assignment info", async function (assert) {
updateCurrentUser({ can_assign: true });
await visit("/t/assignment-topic/45");
assert.strictEqual(
query("#topic-title .assigned-to").innerText.trim(),
"Developers",
"shows assignment in the header"
);
assert.strictEqual(
query("#post_1 .assigned-to--group").innerText.trim(),
"Assigned topic to Developers",
"shows assignment in the first post"
);
assert.ok(exists("#post_1 .assigned-to svg.d-icon-group-plus"));
assert.ok(
exists("#topic-footer-dropdown-reassign"),
"shows reassign dropdown at the bottom of the topic"
);
assert
.dom("#topic-title .assigned-to")
.hasText("Developers", "shows assignment in the header");
assert
.dom("#post_1 .assigned-to--group")
.hasText(
"Assigned topic to Developers",
"shows assignment in the first post"
);
assert.dom("#post_1 .assigned-to svg.d-icon-group-plus").exists();
assert
.dom("#topic-footer-dropdown-reassign")
.exists("shows reassign dropdown at the bottom of the topic");
});
test("User without assign ability cannot see footer button", async function (assert) {
updateCurrentUser({ can_assign: false, admin: false, moderator: false });
await visit("/t/assignment-topic/45");
assert.notOk(
exists("#topic-footer-dropdown-reassign"),
"does not show reassign dropdown at the bottom of the topic"
);
assert
.dom("#topic-footer-dropdown-reassign")
.doesNotExist(
"does not show reassign dropdown at the bottom of the topic"
);
});
test("Shows assignment notification", async function (assert) {
@ -166,7 +162,7 @@ acceptance("Discourse Assign | Assigned topic", function (needs) {
"section.user-content ul.notifications li.item.notification"
);
assert.ok(
assert.true(
notification.children[0].classList.contains("assigned"),
"with correct assigned class"
);
@ -201,9 +197,9 @@ acceptance("Discourse Assign | Reassign topic", function (needs) {
await visit("/t/assignment-topic/44");
await menu.expand();
assert.ok(menu.rowByValue("unassign").exists());
assert.ok(menu.rowByValue("reassign").exists());
assert.ok(menu.rowByValue("reassign-self").exists());
assert.true(menu.rowByValue("unassign").exists());
assert.true(menu.rowByValue("reassign").exists());
assert.true(menu.rowByValue("reassign-self").exists());
});
});
@ -225,9 +221,9 @@ acceptance("Discourse Assign | Reassign topic | mobile", function (needs) {
await visit("/t/assignment-topic/44");
await menu.expand();
assert.ok(menu.rowByValue("unassign-mobile").exists());
assert.ok(menu.rowByValue("reassign-mobile").exists());
assert.ok(menu.rowByValue("reassign-self-mobile").exists());
assert.true(menu.rowByValue("unassign-mobile").exists());
assert.true(menu.rowByValue("reassign-mobile").exists());
assert.true(menu.rowByValue("reassign-self-mobile").exists());
});
});
@ -248,6 +244,6 @@ acceptance("Discourse Assign | Reassign topic conditionals", function (needs) {
await visit("/t/assignment-topic/44");
await menu.expand();
assert.notOk(menu.rowByValue("reassign-self").exists());
assert.false(menu.rowByValue("reassign-self").exists());
});
});

View File

@ -1,6 +1,5 @@
import {
acceptance,
exists,
query,
queryAll,
updateCurrentUser,
@ -211,7 +210,7 @@ acceptance(
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"));
assert.dom("#user-menu-button-assign-list").doesNotExist();
});
}
);
@ -229,7 +228,7 @@ acceptance(
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"));
assert.dom("#user-menu-button-assign-list").doesNotExist();
});
}
);
@ -284,31 +283,24 @@ acceptance("Discourse Assign | user menu", function (needs) {
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"
);
assert.dom("#user-menu-button-assign-list").exists("assigns tab exists");
assert
.dom("#user-menu-button-assign-list .d-icon-user-plus")
.exists("assigns tab has the user-plus icon");
assert
.dom("#user-menu-button-assign-list .badge-notification")
.hasText("173", "assigns tab has a count badge");
updateCurrentUser({
grouped_unread_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"
);
assert
.dom("#user-menu-button-assign-list .badge-notification")
.doesNotExist("badge count disappears when it goes to zero");
assert
.dom("#user-menu-button-assign-list")
.exists("assigns tab still exists");
});
test("clicking on the assign tab when it's already selected navigates to the user's assignments page", async function (assert) {
@ -335,11 +327,11 @@ acceptance("Discourse Assign | user menu", function (needs) {
1,
"there is one unread notification"
);
assert.ok(
assert.true(
notifications[0].classList.contains("unread"),
"the notification is unread"
);
assert.ok(
assert.true(
notifications[0].classList.contains("assigned"),
"the notification is of type assigned"
);
@ -358,13 +350,13 @@ acceptance("Discourse Assign | user menu", function (needs) {
"group assign has the right icon"
);
assert.ok(
assert.true(
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(
assert.true(
groupAssign
.querySelector("a")
.href.endsWith(
@ -382,7 +374,7 @@ acceptance("Discourse Assign | user menu", function (needs) {
userAssign.querySelector(".item-description img.emoji"),
"emojis are rendered in user assign"
);
assert.ok(
assert.strictEqual(
userAssign.querySelector(".item-description b").textContent.trim(),
"my test topic",
"user assign topic title is trusted"
@ -423,13 +415,12 @@ acceptance("Discourse Assign | user menu", function (needs) {
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"
);
assert
.dom("#user-menu-button-assign-list .badge-notification")
.exists("badge count is visible before dismissing");
await click(".notifications-dismiss");
assert.notOk(markRead, "mark-read request isn't sent");
assert.false(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 }),
@ -437,12 +428,11 @@ acceptance("Discourse Assign | user menu", function (needs) {
);
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.true(markRead, "mark-read request is sent");
assert.dom(".notifications-dismiss").doesNotExist("dismiss button is gone");
assert
.dom("#user-menu-button-assign-list .badge-notification")
.doesNotExist("badge count is gone after dismissing");
assert.strictEqual(
requestBody,
"dismiss_types=assigned",
@ -456,21 +446,20 @@ acceptance("Discourse Assign | user menu", function (needs) {
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"),
assert
.dom(".empty-state-title")
.hasText(
I18n.t("user.no_assignments_title"),
"empty state title is rendered"
);
assert.dom(".empty-state-body").exists("empty state body exists");
assert
.dom(".empty-state-body .d-icon-user-plus")
.exists("empty state body has user-plus icon");
assert.true(
query(".empty-state-body a").href.endsWith(
"/my/preferences/notifications"
),
"empty state body has user-plus icon"
);
});

View File

@ -1,4 +1,4 @@
import { acceptance, exists } from "discourse/tests/helpers/qunit-helpers";
import { acceptance } from "discourse/tests/helpers/qunit-helpers";
import { visit } from "@ember/test-helpers";
import { test } from "qunit";
import I18n from "I18n";
@ -40,7 +40,7 @@ acceptance(
await visit("/c/test");
const title = I18n.t("filters.unassigned.help");
assert.ok(exists(`#navigation-bar li[title='${title}']`));
assert.dom(`#navigation-bar li[title='${title}']`).exists();
});
}
);
@ -60,7 +60,7 @@ acceptance(
await visit("/c/test");
const title = I18n.t("filters.unassigned.help");
assert.ok(!exists(`#navigation-bar li[title='${title}']`));
assert.dom(`#navigation-bar li[title='${title}']`).doesNotExist();
});
}
);
@ -80,7 +80,7 @@ acceptance(
await visit("/c/test");
const title = I18n.t("filters.unassigned.help");
assert.ok(exists(`#navigation-bar li[title='${title}']`));
assert.dom(`#navigation-bar li[title='${title}']`).exists();
});
}
);
@ -100,7 +100,7 @@ acceptance(
await visit("/c/test");
const title = I18n.t("filters.unassigned.help");
assert.ok(!exists(`#navigation-bar li[title='${title}']`));
assert.dom(`#navigation-bar li[title='${title}']`).doesNotExist();
});
}
);

View File

@ -1,4 +1,4 @@
import { acceptance, count } from "discourse/tests/helpers/qunit-helpers";
import { acceptance } from "discourse/tests/helpers/qunit-helpers";
import { visit } from "@ember/test-helpers";
import AssignedTopics from "../fixtures/assigned-group-assignments-fixtures";
import GroupMembers from "../fixtures/group-members-fixtures";
@ -21,11 +21,11 @@ acceptance("Discourse Assign | GroupAssignments", function (needs) {
test("Group Assignments Everyone", async function (assert) {
await visit("/g/discourse/assigned");
assert.strictEqual(count(".topic-list-item"), 1);
assert.dom(".topic-list-item").exists({ count: 1 });
});
test("Group Assignments Ahmedgagan", async function (assert) {
await visit("/g/discourse/assigned/ahmedgagan6");
assert.strictEqual(count(".topic-list-item"), 1);
assert.dom(".topic-list-item").exists({ count: 1 });
});
});

View File

@ -1,7 +1,6 @@
import { test } from "qunit";
import {
acceptance,
query,
updateCurrentUser,
} from "discourse/tests/helpers/qunit-helpers";
import { visit } from "@ember/test-helpers";
@ -48,10 +47,11 @@ acceptance(
await visit("/t/assignment-topic/44");
assert.strictEqual(
query(".topic-notifications-button .reason span.text").innerText,
"You will receive notifications because you are watching this topic."
);
assert
.dom(".topic-notifications-button .reason span.text")
.hasText(
"You will receive notifications because you are watching this topic."
);
});
test("Show user assign reason when user never tracks topics", async function (assert) {
@ -61,10 +61,11 @@ acceptance(
await visit("/t/assignment-topic/45");
assert.strictEqual(
query(".topic-notifications-button .reason span.text").innerText,
"You will see a count of new replies because this topic was assigned to you."
);
assert
.dom(".topic-notifications-button .reason span.text")
.hasText(
"You will see a count of new replies because this topic was assigned to you."
);
});
}
);

View File

@ -1,5 +1,5 @@
import selectKit from "discourse/tests/helpers/select-kit-helper";
import { acceptance, query } from "discourse/tests/helpers/qunit-helpers";
import { acceptance } from "discourse/tests/helpers/qunit-helpers";
import { fillIn, visit } from "@ember/test-helpers";
import { test } from "qunit";
@ -28,11 +28,12 @@ acceptance("Discourse Assign | Search - Full Page", function (needs) {
await fillIn(".search-query", "none");
await inSelector.expand();
await inSelector.selectRowByValue("assigned");
assert.strictEqual(
query(".search-query").value,
"none in:assigned",
'has updated search term to "none in:assigned"'
);
assert
.dom(".search-query")
.hasValue(
"none in:assigned",
'has updated search term to "none in:assigned"'
);
});
test("update in:unassigned filter through advanced search ui", async function (assert) {
@ -43,11 +44,12 @@ acceptance("Discourse Assign | Search - Full Page", function (needs) {
await fillIn(".search-query", "none");
await inSelector.expand();
await inSelector.selectRowByValue("unassigned");
assert.strictEqual(
query(".search-query").value,
"none in:unassigned",
'has updated search term to "none in:unassigned"'
);
assert
.dom(".search-query")
.hasValue(
"none in:unassigned",
'has updated search term to "none in:unassigned"'
);
});
test("update assigned to through advanced search ui", async function (assert) {
@ -67,10 +69,11 @@ acceptance("Discourse Assign | Search - Full Page", function (needs) {
'has "admin" filled in'
);
assert.strictEqual(
query(".search-query").value,
"none assigned:admin",
'has updated search term to "none assigned:admin"'
);
assert
.dom(".search-query")
.hasValue(
"none assigned:admin",
'has updated search term to "none assigned:admin"'
);
});
});

View File

@ -1,9 +1,5 @@
import selectKit from "discourse/tests/helpers/select-kit-helper";
import {
acceptance,
count,
exists,
} from "discourse/tests/helpers/qunit-helpers";
import { acceptance } from "discourse/tests/helpers/qunit-helpers";
import { visit } from "@ember/test-helpers";
import AssignedTopics from "../fixtures/assigned-topics-fixtures";
import { cloneJSON } from "discourse-common/lib/object";
@ -26,8 +22,8 @@ acceptance(
await visit("/u/eviltrout/activity/assigned");
await options.expand();
assert.strictEqual(count("li[data-value='unassign']"), 1);
assert.strictEqual(count("li[data-value='reassign']"), 1);
assert.dom("li[data-value='unassign']").exists({ count: 1 });
assert.dom("li[data-value='reassign']").exists({ count: 1 });
});
}
);
@ -49,12 +45,12 @@ acceptance(
test("It renders the empty state panel", async function (assert) {
await visit("/u/eviltrout/activity/assigned");
assert.ok(exists("div.empty-state"));
assert.dom("div.empty-state").exists();
});
test("It does not render the search form", async function (assert) {
await visit("/u/eviltrout/activity/assigned");
assert.notOk(exists("div.topic-search-div"));
assert.dom("div.topic-search-div").doesNotExist();
});
}
);

View File

@ -1,9 +1,12 @@
import { module, test } from "qunit";
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import EmberObject from "@ember/object";
import pretender, { response } from "discourse/tests/helpers/create-pretender";
import { discourseModule } from "discourse/tests/helpers/qunit-helpers";
import { test } from "qunit";
import { getOwner } from "discourse-common/lib/get-owner";
module("Unit | Controller | assign-user", function (hooks) {
setupRenderingTest(hooks);
discourseModule("Unit | Controller | assign-user", function () {
test("assigning a user via suggestions makes API call and closes the modal", async function (assert) {
pretender.get("/assign/suggestions", () =>
response({
@ -16,7 +19,8 @@ discourseModule("Unit | Controller | assign-user", function () {
pretender.put("/assign/assign", () => response({}));
let modalClosed = false;
const controller = this.getController("assign-user", {
const controller = getOwner(this).lookup("controller:assign-user");
controller.setProperties({
model: {
target: EmberObject.create({}),
},
@ -29,7 +33,7 @@ discourseModule("Unit | Controller | assign-user", function () {
await controller.assignUser("nat");
assert.strictEqual(modalClosed, true);
assert.true(modalClosed);
});
test("assigning a user by selector does not close the modal", async function (assert) {
@ -42,7 +46,8 @@ discourseModule("Unit | Controller | assign-user", function () {
);
let modalClosed = false;
const controller = this.getController("assign-user", {
const controller = getOwner(this).lookup("controller:assign-user");
controller.setProperties({
model: {
target: EmberObject.create({}),
},
@ -55,6 +60,6 @@ discourseModule("Unit | Controller | assign-user", function () {
await controller.assignUsername("nat");
assert.strictEqual(modalClosed, false);
assert.false(modalClosed);
});
});

View File

@ -1,13 +1,16 @@
import { discourseModule } from "discourse/tests/helpers/qunit-helpers";
import { test } from "qunit";
import { module, test } from "qunit";
import { setupTest } from "ember-qunit";
import sinon from "sinon";
import * as showModal from "discourse/lib/show-modal";
import pretender, { response } from "discourse/tests/helpers/create-pretender";
import { getOwner } from "discourse-common/lib/get-owner";
module("Unit | Service | task-actions", function (hooks) {
setupTest(hooks);
discourseModule("Unit | Service | task-actions", function () {
test("assign", function (assert) {
const stub = sinon.stub(showModal, "default").returns("the modal");
const service = this.container.lookup("service:task-actions");
const service = getOwner(this).lookup("service:task-actions");
const target = {
assigned_to_user: { username: "tomtom" },
assigned_to_group: { name: "cats" },
@ -17,7 +20,7 @@ discourseModule("Unit | Service | task-actions", function () {
const modalCall = stub.getCall(0).args;
assert.strictEqual(modal, "the modal");
assert.deepEqual(modalCall[0], "assign-user");
assert.strictEqual(modalCall[0], "assign-user");
assert.deepEqual(modalCall[1], {
title: "discourse_assign.assign_modal.title",
model: {
@ -32,7 +35,7 @@ discourseModule("Unit | Service | task-actions", function () {
});
test("reassignUserToTopic", async function (assert) {
const service = this.container.lookup("service:task-actions");
const service = getOwner(this).lookup("service:task-actions");
const target = { id: 1 };
const user = { username: "tomtom" };
let assignRequest;

1049
yarn.lock

File diff suppressed because it is too large Load Diff