DEV: Convert core components to native class syntax (batch 8) (#28602)
Changes made using the ember-native-class-codemod, plus some manual tweaks
This commit is contained in:
parent
4150ec960e
commit
54b281c4a2
|
@ -1,23 +1,28 @@
|
|||
import Component from "@ember/component";
|
||||
import { schedule } from "@ember/runloop";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
import {
|
||||
attributeBindings,
|
||||
classNames,
|
||||
tagName,
|
||||
} from "@ember-decorators/component";
|
||||
import { iconHTML } from "discourse-common/lib/icon-library";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import I18n from "discourse-i18n";
|
||||
|
||||
export default Component.extend({
|
||||
tagName: "div",
|
||||
classNames: ["directory-table__column-header", "sortable"],
|
||||
attributeBindings: ["title", "colspan", "ariaSort:aria-sort", "role"],
|
||||
role: "columnheader",
|
||||
labelKey: null,
|
||||
chevronIcon: null,
|
||||
columnIcon: null,
|
||||
translated: false,
|
||||
automatic: false,
|
||||
onActiveRender: null,
|
||||
pressedState: null,
|
||||
ariaLabel: null,
|
||||
@tagName("div")
|
||||
@classNames("directory-table__column-header", "sortable")
|
||||
@attributeBindings("title", "colspan", "ariaSort:aria-sort", "role")
|
||||
export default class TableHeaderToggle extends Component {
|
||||
role = "columnheader";
|
||||
labelKey = null;
|
||||
chevronIcon = null;
|
||||
columnIcon = null;
|
||||
translated = false;
|
||||
automatic = false;
|
||||
onActiveRender = null;
|
||||
pressedState = null;
|
||||
ariaLabel = null;
|
||||
|
||||
@discourseComputed("order", "field", "asc")
|
||||
ariaSort() {
|
||||
|
@ -26,14 +31,16 @@ export default Component.extend({
|
|||
} else {
|
||||
return "none";
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
toggleProperties() {
|
||||
if (this.order === this.field) {
|
||||
this.set("asc", this.asc ? null : true);
|
||||
} else {
|
||||
this.setProperties({ order: this.field, asc: null });
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
toggleChevron() {
|
||||
if (this.order === this.field) {
|
||||
let chevron = iconHTML(this.asc ? "chevron-up" : "chevron-down");
|
||||
|
@ -41,32 +48,35 @@ export default Component.extend({
|
|||
} else {
|
||||
this.set("chevronIcon", null);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
click() {
|
||||
this.toggleProperties();
|
||||
},
|
||||
}
|
||||
|
||||
keyPress(e) {
|
||||
if (e.which === 13) {
|
||||
this.toggleProperties();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
didReceiveAttrs() {
|
||||
this._super(...arguments);
|
||||
super.didReceiveAttrs(...arguments);
|
||||
if (!this.automatic && !this.translated) {
|
||||
this.set("labelKey", this.field);
|
||||
}
|
||||
this.set("id", `table-header-toggle-${this.field.replace(/\s/g, "")}`);
|
||||
this.toggleChevron();
|
||||
this._updateA11yAttributes();
|
||||
},
|
||||
}
|
||||
|
||||
didRender() {
|
||||
this._super(...arguments);
|
||||
super.didRender(...arguments);
|
||||
|
||||
if (this.onActiveRender && this.chevronIcon) {
|
||||
this.onActiveRender(this.element);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_updateA11yAttributes() {
|
||||
let criteria = "";
|
||||
|
@ -99,10 +109,11 @@ export default Component.extend({
|
|||
} else {
|
||||
this.set("pressedState", "false");
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_focusHeader() {
|
||||
schedule("afterRender", () => {
|
||||
document.getElementById(this.id)?.focus();
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +1,22 @@
|
|||
import Component from "@ember/component";
|
||||
import {
|
||||
attributeBindings,
|
||||
classNameBindings,
|
||||
tagName,
|
||||
} from "@ember-decorators/component";
|
||||
import DiscourseURL from "discourse/lib/url";
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
|
||||
export default Component.extend({
|
||||
tagName: "a",
|
||||
classNameBindings: [
|
||||
":tag-badge-wrapper",
|
||||
":badge-wrapper",
|
||||
":bullet",
|
||||
"tagClass",
|
||||
],
|
||||
attributeBindings: ["href"],
|
||||
|
||||
@tagName("a")
|
||||
@classNameBindings(
|
||||
":tag-badge-wrapper",
|
||||
":badge-wrapper",
|
||||
":bullet",
|
||||
"tagClass"
|
||||
)
|
||||
@attributeBindings("href")
|
||||
export default class TagDropLink extends Component {
|
||||
@discourseComputed("tagId", "category")
|
||||
href(tagId, category) {
|
||||
let path;
|
||||
|
@ -24,16 +28,16 @@ export default Component.extend({
|
|||
}
|
||||
|
||||
return getURL(path);
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed("tagId")
|
||||
tagClass(tagId) {
|
||||
return "tag-" + tagId;
|
||||
},
|
||||
}
|
||||
|
||||
click(e) {
|
||||
e.preventDefault();
|
||||
DiscourseURL.routeTo(this.href);
|
||||
return true;
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -125,7 +125,7 @@
|
|||
/>
|
||||
|
||||
<DButton
|
||||
@action={{action "destroy"}}
|
||||
@action={{this.destroyTagGroup}}
|
||||
@disabled={{this.buffered.isNew}}
|
||||
@icon="far-trash-alt"
|
||||
@label="tagging.groups.delete"
|
||||
|
|
|
@ -1,28 +1,33 @@
|
|||
import Component from "@ember/component";
|
||||
import { action } from "@ember/object";
|
||||
import { service } from "@ember/service";
|
||||
import { isEmpty } from "@ember/utils";
|
||||
import { tagName } from "@ember-decorators/component";
|
||||
import { bufferedProperty } from "discourse/mixins/buffered-content";
|
||||
import Group from "discourse/models/group";
|
||||
import PermissionType from "discourse/models/permission-type";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import I18n from "discourse-i18n";
|
||||
|
||||
export default Component.extend(bufferedProperty("model"), {
|
||||
router: service(),
|
||||
dialog: service(),
|
||||
tagName: "",
|
||||
allGroups: null,
|
||||
@tagName("")
|
||||
export default class TagGroupsForm extends Component.extend(
|
||||
bufferedProperty("model")
|
||||
) {
|
||||
@service router;
|
||||
@service dialog;
|
||||
|
||||
allGroups = null;
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
super.init(...arguments);
|
||||
this.setGroupOptions();
|
||||
},
|
||||
}
|
||||
|
||||
setGroupOptions() {
|
||||
Group.findAll().then((groups) => {
|
||||
this.set("allGroups", groups);
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed(
|
||||
"buffered.name",
|
||||
|
@ -36,7 +41,7 @@ export default Component.extend(bufferedProperty("model"), {
|
|||
(!this.everyoneSelected(permissions) &&
|
||||
isEmpty(this.selectedGroupNames(permissions)))
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed("buffered.permissions", "allGroups")
|
||||
selectedGroupIds(permissions, allGroups) {
|
||||
|
@ -53,7 +58,7 @@ export default Component.extend(bufferedProperty("model"), {
|
|||
});
|
||||
|
||||
return groupIds;
|
||||
},
|
||||
}
|
||||
|
||||
everyoneSelected(permissions) {
|
||||
if (!permissions) {
|
||||
|
@ -61,7 +66,7 @@ export default Component.extend(bufferedProperty("model"), {
|
|||
}
|
||||
|
||||
return permissions.everyone === PermissionType.FULL;
|
||||
},
|
||||
}
|
||||
|
||||
selectedGroupNames(permissions) {
|
||||
if (!permissions) {
|
||||
|
@ -69,87 +74,89 @@ export default Component.extend(bufferedProperty("model"), {
|
|||
}
|
||||
|
||||
return Object.keys(permissions).filter((name) => name !== "everyone");
|
||||
},
|
||||
}
|
||||
|
||||
actions: {
|
||||
setPermissionsType(permissionName) {
|
||||
let updatedPermissions = Object.assign(
|
||||
{},
|
||||
this.buffered.get("permissions")
|
||||
);
|
||||
@action
|
||||
setPermissionsType(permissionName) {
|
||||
let updatedPermissions = Object.assign(
|
||||
{},
|
||||
this.buffered.get("permissions")
|
||||
);
|
||||
|
||||
if (permissionName === "private") {
|
||||
delete updatedPermissions.everyone;
|
||||
} else if (permissionName === "visible") {
|
||||
updatedPermissions.everyone = PermissionType.READONLY;
|
||||
if (permissionName === "private") {
|
||||
delete updatedPermissions.everyone;
|
||||
} else if (permissionName === "visible") {
|
||||
updatedPermissions.everyone = PermissionType.READONLY;
|
||||
} else {
|
||||
updatedPermissions.everyone = PermissionType.FULL;
|
||||
}
|
||||
|
||||
this.buffered.set("permissions", updatedPermissions);
|
||||
}
|
||||
|
||||
@action
|
||||
setPermissionsGroups(groupIds) {
|
||||
let updatedPermissions = Object.assign(
|
||||
{},
|
||||
this.buffered.get("permissions")
|
||||
);
|
||||
|
||||
this.allGroups.forEach((group) => {
|
||||
if (groupIds.includes(group.id)) {
|
||||
updatedPermissions[group.name] = PermissionType.FULL;
|
||||
} else {
|
||||
updatedPermissions.everyone = PermissionType.FULL;
|
||||
delete updatedPermissions[group.name];
|
||||
}
|
||||
});
|
||||
|
||||
this.buffered.set("permissions", updatedPermissions);
|
||||
},
|
||||
this.buffered.set("permissions", updatedPermissions);
|
||||
}
|
||||
|
||||
setPermissionsGroups(groupIds) {
|
||||
let updatedPermissions = Object.assign(
|
||||
{},
|
||||
this.buffered.get("permissions")
|
||||
);
|
||||
@action
|
||||
save() {
|
||||
if (this.cannotSave) {
|
||||
this.dialog.alert(I18n.t("tagging.groups.cannot_save"));
|
||||
return false;
|
||||
}
|
||||
|
||||
this.allGroups.forEach((group) => {
|
||||
if (groupIds.includes(group.id)) {
|
||||
updatedPermissions[group.name] = PermissionType.FULL;
|
||||
} else {
|
||||
delete updatedPermissions[group.name];
|
||||
}
|
||||
});
|
||||
const attrs = this.buffered.getProperties(
|
||||
"name",
|
||||
"tag_names",
|
||||
"parent_tag_name",
|
||||
"one_per_topic",
|
||||
"permissions"
|
||||
);
|
||||
|
||||
this.buffered.set("permissions", updatedPermissions);
|
||||
},
|
||||
// If 'everyone' is set to full, we can remove any groups.
|
||||
if (
|
||||
!attrs.permissions ||
|
||||
attrs.permissions.everyone === PermissionType.FULL
|
||||
) {
|
||||
attrs.permissions = { everyone: PermissionType.FULL };
|
||||
}
|
||||
|
||||
save() {
|
||||
if (this.cannotSave) {
|
||||
this.dialog.alert(I18n.t("tagging.groups.cannot_save"));
|
||||
return false;
|
||||
this.model.save(attrs).then(() => {
|
||||
this.commitBuffer();
|
||||
|
||||
if (this.onSave) {
|
||||
this.onSave();
|
||||
} else {
|
||||
this.router.transitionTo("tagGroups.index");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const attrs = this.buffered.getProperties(
|
||||
"name",
|
||||
"tag_names",
|
||||
"parent_tag_name",
|
||||
"one_per_topic",
|
||||
"permissions"
|
||||
);
|
||||
|
||||
// If 'everyone' is set to full, we can remove any groups.
|
||||
if (
|
||||
!attrs.permissions ||
|
||||
attrs.permissions.everyone === PermissionType.FULL
|
||||
) {
|
||||
attrs.permissions = { everyone: PermissionType.FULL };
|
||||
}
|
||||
|
||||
this.model.save(attrs).then(() => {
|
||||
this.commitBuffer();
|
||||
|
||||
if (this.onSave) {
|
||||
this.onSave();
|
||||
} else {
|
||||
this.router.transitionTo("tagGroups.index");
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
destroy() {
|
||||
return this.dialog.yesNoConfirm({
|
||||
message: I18n.t("tagging.groups.confirm_delete"),
|
||||
didConfirm: () => {
|
||||
this.model.destroyRecord().then(() => {
|
||||
if (this.onDestroy) {
|
||||
this.onDestroy();
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
@action
|
||||
destroyTagGroup() {
|
||||
return this.dialog.yesNoConfirm({
|
||||
message: I18n.t("tagging.groups.confirm_delete"),
|
||||
didConfirm: () => {
|
||||
this.model.destroyRecord().then(() => {
|
||||
if (this.onDestroy) {
|
||||
this.onDestroy();
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,24 +4,27 @@ import { and, reads } from "@ember/object/computed";
|
|||
import { service } from "@ember/service";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
import { isEmpty } from "@ember/utils";
|
||||
import { tagName } from "@ember-decorators/component";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import I18n from "discourse-i18n";
|
||||
|
||||
export default Component.extend({
|
||||
dialog: service(),
|
||||
tagName: "",
|
||||
loading: false,
|
||||
tagInfo: null,
|
||||
newSynonyms: null,
|
||||
showEditControls: false,
|
||||
canAdminTag: reads("currentUser.staff"),
|
||||
editSynonymsMode: and("canAdminTag", "showEditControls"),
|
||||
editing: false,
|
||||
newTagName: null,
|
||||
newTagDescription: null,
|
||||
router: service(),
|
||||
@tagName("")
|
||||
export default class TagInfo extends Component {
|
||||
@service dialog;
|
||||
@service router;
|
||||
|
||||
loading = false;
|
||||
tagInfo = null;
|
||||
newSynonyms = null;
|
||||
showEditControls = false;
|
||||
editing = false;
|
||||
newTagName = null;
|
||||
newTagDescription = null;
|
||||
|
||||
@reads("currentUser.staff") canAdminTag;
|
||||
@and("canAdminTag", "showEditControls") editSynonymsMode;
|
||||
|
||||
@discourseComputed("tagInfo.tag_group_names")
|
||||
tagGroupsInfo(tagGroupNames) {
|
||||
|
@ -29,14 +32,14 @@ export default Component.extend({
|
|||
count: tagGroupNames.length,
|
||||
tag_groups: tagGroupNames.join(", "),
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed("tagInfo.categories")
|
||||
categoriesInfo(categories) {
|
||||
return I18n.t("tagging.category_restrictions", {
|
||||
count: categories.length,
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed(
|
||||
"tagInfo.tag_group_names",
|
||||
|
@ -45,25 +48,25 @@ export default Component.extend({
|
|||
)
|
||||
nothingToShow(tagGroupNames, categories, synonyms) {
|
||||
return isEmpty(tagGroupNames) && isEmpty(categories) && isEmpty(synonyms);
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed("newTagName")
|
||||
updateDisabled(newTagName) {
|
||||
const filterRegexp = new RegExp(this.site.tags_filter_regexp, "g");
|
||||
newTagName = newTagName ? newTagName.replace(filterRegexp, "").trim() : "";
|
||||
return newTagName.length === 0;
|
||||
},
|
||||
}
|
||||
|
||||
didInsertElement() {
|
||||
this._super(...arguments);
|
||||
super.didInsertElement(...arguments);
|
||||
this.loadTagInfo();
|
||||
},
|
||||
}
|
||||
|
||||
didUpdateAttrs() {
|
||||
this._super(...arguments);
|
||||
super.didUpdateAttrs(...arguments);
|
||||
this.set("tagInfo", null);
|
||||
this.loadTagInfo();
|
||||
},
|
||||
}
|
||||
|
||||
loadTagInfo() {
|
||||
if (this.loading) {
|
||||
|
@ -81,7 +84,7 @@ export default Component.extend({
|
|||
})
|
||||
.finally(() => this.set("loading", false))
|
||||
.catch(popupAjaxError);
|
||||
},
|
||||
}
|
||||
|
||||
@action
|
||||
edit(event) {
|
||||
|
@ -95,7 +98,7 @@ export default Component.extend({
|
|||
newTagName: this.tag.id,
|
||||
newTagDescription: this.tagInfo.description,
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
@action
|
||||
unlinkSynonym(tag, event) {
|
||||
|
@ -105,7 +108,7 @@ export default Component.extend({
|
|||
})
|
||||
.then(() => this.tagInfo.synonyms.removeObject(tag))
|
||||
.catch(popupAjaxError);
|
||||
},
|
||||
}
|
||||
|
||||
@action
|
||||
deleteSynonym(tag, event) {
|
||||
|
@ -122,17 +125,17 @@ export default Component.extend({
|
|||
.catch(popupAjaxError);
|
||||
},
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
@action
|
||||
toggleEditControls() {
|
||||
this.toggleProperty("showEditControls");
|
||||
},
|
||||
}
|
||||
|
||||
@action
|
||||
cancelEditing() {
|
||||
this.set("editing", false);
|
||||
},
|
||||
}
|
||||
|
||||
@action
|
||||
finishedEditing() {
|
||||
|
@ -151,7 +154,7 @@ export default Component.extend({
|
|||
}
|
||||
})
|
||||
.catch(popupAjaxError);
|
||||
},
|
||||
}
|
||||
|
||||
@action
|
||||
deleteTag() {
|
||||
|
@ -182,7 +185,7 @@ export default Component.extend({
|
|||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
@action
|
||||
addSynonyms() {
|
||||
|
@ -217,5 +220,5 @@ export default Component.extend({
|
|||
.catch(popupAjaxError);
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,34 +1,35 @@
|
|||
import Component from "@ember/component";
|
||||
import { sort } from "@ember/object/computed";
|
||||
import { classNameBindings } from "@ember-decorators/component";
|
||||
import Category from "discourse/models/category";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import I18n from "discourse-i18n";
|
||||
|
||||
export default Component.extend({
|
||||
classNameBindings: [
|
||||
":tags-list",
|
||||
":tag-list",
|
||||
"categoryClass",
|
||||
"tagGroupNameClass",
|
||||
],
|
||||
@classNameBindings(
|
||||
":tags-list",
|
||||
":tag-list",
|
||||
"categoryClass",
|
||||
"tagGroupNameClass"
|
||||
)
|
||||
export default class TagList extends Component {
|
||||
isPrivateMessage = false;
|
||||
|
||||
isPrivateMessage: false,
|
||||
sortedTags: sort("tags", "sortProperties"),
|
||||
@sort("tags", "sortProperties") sortedTags;
|
||||
|
||||
@discourseComputed("titleKey")
|
||||
title(titleKey) {
|
||||
return titleKey && I18n.t(titleKey);
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed("categoryId")
|
||||
category(categoryId) {
|
||||
return categoryId && Category.findById(categoryId);
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed("category.fullSlug")
|
||||
categoryClass(slug) {
|
||||
return slug && `tag-list-${slug}`;
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed("tagGroupName")
|
||||
tagGroupNameClass(groupName) {
|
||||
|
@ -39,5 +40,5 @@ export default Component.extend({
|
|||
.toLowerCase();
|
||||
return groupName && `tag-group-${groupName}`;
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import Component from "@ember/component";
|
||||
import { classNames } from "@ember-decorators/component";
|
||||
|
||||
export default Component.extend({
|
||||
classNames: ["tap-tile-grid"],
|
||||
activeTile: null,
|
||||
});
|
||||
@classNames("tap-tile-grid")
|
||||
export default class TapTileGrid extends Component {
|
||||
activeTile = null;
|
||||
}
|
||||
|
|
|
@ -1,30 +1,35 @@
|
|||
import Component from "@ember/component";
|
||||
import { reads } from "@ember/object/computed";
|
||||
import {
|
||||
attributeBindings,
|
||||
classNameBindings,
|
||||
classNames,
|
||||
} from "@ember-decorators/component";
|
||||
import { propertyEqual } from "discourse/lib/computed";
|
||||
|
||||
export default Component.extend({
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
this.set("elementId", `tap_tile_${this.tileId}`);
|
||||
},
|
||||
@classNames("tap-tile")
|
||||
@classNameBindings("active")
|
||||
@attributeBindings("role", "ariaPressed", "tabIndex")
|
||||
export default class TapTile extends Component {
|
||||
role = "button";
|
||||
tabIndex = 0;
|
||||
|
||||
classNames: ["tap-tile"],
|
||||
classNameBindings: ["active"],
|
||||
attributeBindings: ["role", "ariaPressed", "tabIndex"],
|
||||
role: "button",
|
||||
tabIndex: 0,
|
||||
ariaPressed: reads("active"),
|
||||
@reads("active") ariaPressed;
|
||||
@propertyEqual("activeTile", "tileId") active;
|
||||
|
||||
init() {
|
||||
super.init(...arguments);
|
||||
this.set("elementId", `tap_tile_${this.tileId}`);
|
||||
}
|
||||
|
||||
click() {
|
||||
this.onChange(this.tileId);
|
||||
},
|
||||
}
|
||||
|
||||
keyDown(e) {
|
||||
if (e.key === "Enter") {
|
||||
e.stopPropagation();
|
||||
this.onChange(this.tileId);
|
||||
}
|
||||
},
|
||||
|
||||
active: propertyEqual("activeTile", "tileId"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,37 +1,33 @@
|
|||
import { TextField } from "@ember/legacy-built-in-components";
|
||||
import { computed } from "@ember/object";
|
||||
import { cancel, next } from "@ember/runloop";
|
||||
import { attributeBindings } from "@ember-decorators/component";
|
||||
import discourseDebounce from "discourse-common/lib/debounce";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import I18n from "discourse-i18n";
|
||||
|
||||
const DEBOUNCE_MS = 500;
|
||||
|
||||
export default TextField.extend({
|
||||
attributeBindings: [
|
||||
"autocorrect",
|
||||
"autocapitalize",
|
||||
"autofocus",
|
||||
"maxLength",
|
||||
"dir",
|
||||
"aria-label",
|
||||
"aria-controls",
|
||||
],
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
|
||||
this._prevValue = null;
|
||||
this._timer = null;
|
||||
},
|
||||
@attributeBindings(
|
||||
"autocorrect",
|
||||
"autocapitalize",
|
||||
"autofocus",
|
||||
"maxLength",
|
||||
"dir",
|
||||
"aria-label",
|
||||
"aria-controls"
|
||||
)
|
||||
export default class DiscourseTextField extends TextField {
|
||||
_prevValue = null;
|
||||
_timer = null;
|
||||
|
||||
didReceiveAttrs() {
|
||||
this._super(...arguments);
|
||||
super.didReceiveAttrs(...arguments);
|
||||
|
||||
this._prevValue = this.value;
|
||||
},
|
||||
}
|
||||
|
||||
didUpdateAttrs() {
|
||||
this._super(...arguments);
|
||||
super.didUpdateAttrs(...arguments);
|
||||
|
||||
if (this._prevValue !== this.value) {
|
||||
if (this.onChangeImmediate) {
|
||||
|
@ -46,33 +42,32 @@ export default TextField.extend({
|
|||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_debouncedChange() {
|
||||
next(() => this.onChange(this.value));
|
||||
},
|
||||
}
|
||||
|
||||
get dir() {
|
||||
if (this.siteSettings.support_mixed_text_direction) {
|
||||
return "auto";
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
super.willDestroyElement(...arguments);
|
||||
cancel(this._timer);
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed("placeholderKey")
|
||||
placeholder: {
|
||||
get() {
|
||||
if (this._placeholder) {
|
||||
return this._placeholder;
|
||||
}
|
||||
return this.placeholderKey ? I18n.t(this.placeholderKey) : "";
|
||||
},
|
||||
set(value) {
|
||||
return (this._placeholder = value);
|
||||
},
|
||||
},
|
||||
});
|
||||
@computed("placeholderKey", "_placeholder")
|
||||
get placeholder() {
|
||||
if (this._placeholder) {
|
||||
return this._placeholder;
|
||||
}
|
||||
return this.placeholderKey ? I18n.t(this.placeholderKey) : "";
|
||||
}
|
||||
|
||||
set placeholder(value) {
|
||||
this.set("_placeholder", value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import Component from "@ember/component";
|
|||
import { action, computed } from "@ember/object";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
import { isPresent } from "@ember/utils";
|
||||
import { classNames } from "@ember-decorators/component";
|
||||
|
||||
function convertMinutes(num) {
|
||||
return { hours: Math.floor(num / 60), minutes: num % 60 };
|
||||
|
@ -32,17 +33,14 @@ function convertMinutesToDurationString(n) {
|
|||
return output;
|
||||
}
|
||||
|
||||
export default Component.extend({
|
||||
classNames: ["d-time-input"],
|
||||
|
||||
hours: null,
|
||||
|
||||
minutes: null,
|
||||
|
||||
relativeDate: null,
|
||||
@classNames("d-time-input")
|
||||
export default class TimeInput extends Component {
|
||||
hours = null;
|
||||
minutes = null;
|
||||
relativeDate = null;
|
||||
|
||||
didReceiveAttrs() {
|
||||
this._super(...arguments);
|
||||
super.didReceiveAttrs(...arguments);
|
||||
|
||||
if (isPresent(this.date)) {
|
||||
this.setProperties({
|
||||
|
@ -61,9 +59,10 @@ export default Component.extend({
|
|||
minutes: null,
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
minimumTime: computed("relativeDate", "date", function () {
|
||||
@computed("relativeDate", "date")
|
||||
get minimumTime() {
|
||||
if (this.relativeDate) {
|
||||
if (this.date) {
|
||||
if (!this.date.isSame(this.relativeDate, "day")) {
|
||||
|
@ -75,9 +74,10 @@ export default Component.extend({
|
|||
return this.relativeDate.hours() * 60 + this.relativeDate.minutes();
|
||||
}
|
||||
}
|
||||
}),
|
||||
}
|
||||
|
||||
timeOptions: computed("minimumTime", "hours", "minutes", function () {
|
||||
@computed("minimumTime", "hours", "minutes")
|
||||
get timeOptions() {
|
||||
let options = [];
|
||||
|
||||
const start = this.minimumTime
|
||||
|
@ -136,20 +136,21 @@ export default Component.extend({
|
|||
title: name,
|
||||
};
|
||||
});
|
||||
}),
|
||||
}
|
||||
|
||||
time: computed("minimumTime", "hours", "minutes", function () {
|
||||
@computed("minimumTime", "hours", "minutes")
|
||||
get time() {
|
||||
if (isPresent(this.hours) && isPresent(this.minutes)) {
|
||||
return parseInt(this.hours, 10) * 60 + parseInt(this.minutes, 10);
|
||||
}
|
||||
}),
|
||||
}
|
||||
|
||||
@action
|
||||
onFocusIn(value, event) {
|
||||
if (value && event.target) {
|
||||
event.target.select();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@action
|
||||
onChangeTime(time) {
|
||||
|
@ -182,5 +183,5 @@ export default Component.extend({
|
|||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import Component from "@ember/component";
|
||||
import { action } from "@ember/object";
|
||||
import { and, equal } from "@ember/object/computed";
|
||||
import { tagName } from "@ember-decorators/component";
|
||||
import { observes, on } from "@ember-decorators/object";
|
||||
import {
|
||||
defaultTimeShortcuts,
|
||||
formatTime,
|
||||
|
@ -9,10 +11,7 @@ import {
|
|||
TIME_SHORTCUT_TYPES,
|
||||
} from "discourse/lib/time-shortcut";
|
||||
import { laterToday, now, parseCustomDatetime } from "discourse/lib/time-utils";
|
||||
import discourseComputed, {
|
||||
observes,
|
||||
on,
|
||||
} from "discourse-common/utils/decorators";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import I18n from "discourse-i18n";
|
||||
|
||||
const BINDINGS = {
|
||||
|
@ -40,30 +39,34 @@ const BINDINGS = {
|
|||
"n r": { handler: "selectShortcut", args: [TIME_SHORTCUT_TYPES.NONE] },
|
||||
};
|
||||
|
||||
export default Component.extend({
|
||||
tagName: "",
|
||||
@tagName("")
|
||||
export default class TimeShortcutPicker extends Component {
|
||||
@equal("selectedShortcut", TIME_SHORTCUT_TYPES.CUSTOM) customDatetimeSelected;
|
||||
@equal("selectedShortcut", TIME_SHORTCUT_TYPES.RELATIVE)
|
||||
relativeTimeSelected;
|
||||
@and("customDate", "customTime") customDatetimeFilled;
|
||||
|
||||
userTimezone: null,
|
||||
userTimezone = null;
|
||||
|
||||
onTimeSelected: null,
|
||||
onTimeSelected = null;
|
||||
|
||||
selectedShortcut: null,
|
||||
selectedTime: null,
|
||||
selectedDate: null,
|
||||
selectedDatetime: null,
|
||||
prefilledDatetime: null,
|
||||
selectedDurationMins: null,
|
||||
selectedShortcut = null;
|
||||
selectedTime = null;
|
||||
selectedDate = null;
|
||||
selectedDatetime = null;
|
||||
prefilledDatetime = null;
|
||||
selectedDurationMins = null;
|
||||
|
||||
hiddenOptions: null,
|
||||
customOptions: null,
|
||||
hiddenOptions = null;
|
||||
customOptions = null;
|
||||
|
||||
lastCustomDate: null,
|
||||
lastCustomTime: null,
|
||||
parsedLastCustomDatetime: null,
|
||||
customDate: null,
|
||||
customTime: null,
|
||||
lastCustomDate = null;
|
||||
lastCustomTime = null;
|
||||
parsedLastCustomDatetime = null;
|
||||
customDate = null;
|
||||
customTime = null;
|
||||
|
||||
_itsatrap: null,
|
||||
_itsatrap = null;
|
||||
|
||||
@on("init")
|
||||
_setupPicker() {
|
||||
|
@ -79,7 +82,7 @@ export default Component.extend({
|
|||
}
|
||||
|
||||
this._bindKeyboardShortcuts();
|
||||
},
|
||||
}
|
||||
|
||||
@observes("prefilledDatetime")
|
||||
prefilledDatetimeChanged() {
|
||||
|
@ -92,13 +95,13 @@ export default Component.extend({
|
|||
selectedShortcut: null,
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
super.willDestroyElement(...arguments);
|
||||
|
||||
this._itsatrap.unbind(Object.keys(BINDINGS));
|
||||
},
|
||||
}
|
||||
|
||||
parsePrefilledDatetime() {
|
||||
let parsedDatetime = parseCustomDatetime(
|
||||
|
@ -116,7 +119,7 @@ export default Component.extend({
|
|||
customTime: parsedDatetime.format("HH:mm"),
|
||||
selectedShortcut: TIME_SHORTCUT_TYPES.CUSTOM,
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
_loadLastUsedCustomDatetime() {
|
||||
const lastTime = this.keyValueStore.lastCustomTime;
|
||||
|
@ -135,7 +138,7 @@ export default Component.extend({
|
|||
parsedLastCustomDatetime: parsed,
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_bindKeyboardShortcuts() {
|
||||
Object.keys(BINDINGS).forEach((shortcut) => {
|
||||
|
@ -145,11 +148,7 @@ export default Component.extend({
|
|||
return false;
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
customDatetimeSelected: equal("selectedShortcut", TIME_SHORTCUT_TYPES.CUSTOM),
|
||||
relativeTimeSelected: equal("selectedShortcut", TIME_SHORTCUT_TYPES.RELATIVE),
|
||||
customDatetimeFilled: and("customDate", "customTime"),
|
||||
}
|
||||
|
||||
@observes("customDate", "customTime")
|
||||
customDatetimeChanged() {
|
||||
|
@ -157,7 +156,7 @@ export default Component.extend({
|
|||
return;
|
||||
}
|
||||
this.selectShortcut(TIME_SHORTCUT_TYPES.CUSTOM);
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed(
|
||||
"timeShortcuts",
|
||||
|
@ -203,7 +202,7 @@ export default Component.extend({
|
|||
this._applyCustomLabels(options, customLabels);
|
||||
options.forEach((o) => (o.timeFormatted = formatTime(o)));
|
||||
return options;
|
||||
},
|
||||
}
|
||||
|
||||
@action
|
||||
relativeTimeChanged(relativeTimeMins) {
|
||||
|
@ -215,7 +214,7 @@ export default Component.extend({
|
|||
});
|
||||
|
||||
this.onTimeSelected?.(TIME_SHORTCUT_TYPES.RELATIVE, dateTime);
|
||||
},
|
||||
}
|
||||
|
||||
@action
|
||||
selectShortcut(type) {
|
||||
|
@ -259,7 +258,7 @@ export default Component.extend({
|
|||
if (this.onTimeSelected) {
|
||||
this.onTimeSelected(type, dateTime);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_applyCustomLabels(options, customLabels) {
|
||||
options.forEach((option) => {
|
||||
|
@ -267,7 +266,7 @@ export default Component.extend({
|
|||
option.label = customLabels[option.id];
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
_formatTime(options) {
|
||||
options.forEach((option) => {
|
||||
|
@ -275,9 +274,9 @@ export default Component.extend({
|
|||
option.timeFormatted = option.time.format(I18n.t(option.timeFormatKey));
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
_defaultCustomDateTime() {
|
||||
return moment.tz(this.userTimezone).add(1, "hour");
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
import Component from "@ember/component";
|
||||
import { action } from "@ember/object";
|
||||
import { classNames } from "@ember-decorators/component";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
|
||||
export default Component.extend({
|
||||
classNames: ["top-title-buttons"],
|
||||
|
||||
@classNames("top-title-buttons")
|
||||
export default class TopPeriodButtons extends Component {
|
||||
@discourseComputed("period")
|
||||
periods(period) {
|
||||
return this.site.get("periods").filter((p) => p !== period);
|
||||
},
|
||||
}
|
||||
|
||||
@action
|
||||
changePeriod(p) {
|
||||
this.action(p);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import Component from "@ember/component";
|
||||
|
||||
// Injections don't occur without a class
|
||||
export default Component.extend();
|
||||
export default class TopicCategory extends Component {}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import Component from "@ember/component";
|
||||
import { action } from "@ember/object";
|
||||
import { scheduleOnce } from "@ember/runloop";
|
||||
import { service } from "@ember/service";
|
||||
import { classNameBindings } from "@ember-decorators/component";
|
||||
import $ from "jquery";
|
||||
import DiscourseURL from "discourse/lib/url";
|
||||
import CleansUp from "discourse/mixins/cleans-up";
|
||||
|
@ -30,41 +32,50 @@ function entranceDate(dt, showTime) {
|
|||
);
|
||||
}
|
||||
|
||||
export default Component.extend(CleansUp, {
|
||||
router: service(),
|
||||
session: service(),
|
||||
historyStore: service(),
|
||||
elementId: "topic-entrance",
|
||||
classNameBindings: ["visible::hidden"],
|
||||
topic: null,
|
||||
visible: null,
|
||||
_position: null,
|
||||
_originalActiveElement: null,
|
||||
_activeButton: null,
|
||||
@classNameBindings("visible::hidden")
|
||||
export default class TopicEntrance extends Component.extend(CleansUp) {
|
||||
@service router;
|
||||
@service session;
|
||||
@service historyStore;
|
||||
|
||||
elementId = "topic-entrance";
|
||||
topic = null;
|
||||
visible = null;
|
||||
_position = null;
|
||||
_originalActiveElement = null;
|
||||
_activeButton = null;
|
||||
|
||||
@discourseComputed("topic.created_at")
|
||||
createdDate: (createdAt) => new Date(createdAt),
|
||||
createdDate(createdAt) {
|
||||
return new Date(createdAt);
|
||||
}
|
||||
|
||||
@discourseComputed("topic.bumped_at")
|
||||
bumpedDate: (bumpedAt) => new Date(bumpedAt),
|
||||
bumpedDate(bumpedAt) {
|
||||
return new Date(bumpedAt);
|
||||
}
|
||||
|
||||
@discourseComputed("createdDate", "bumpedDate")
|
||||
showTime(createdDate, bumpedDate) {
|
||||
return (
|
||||
bumpedDate.getTime() - createdDate.getTime() < 1000 * 60 * 60 * 24 * 2
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed("createdDate", "showTime")
|
||||
topDate: (createdDate, showTime) => entranceDate(createdDate, showTime),
|
||||
topDate(createdDate, showTime) {
|
||||
return entranceDate(createdDate, showTime);
|
||||
}
|
||||
|
||||
@discourseComputed("bumpedDate", "showTime")
|
||||
bottomDate: (bumpedDate, showTime) => entranceDate(bumpedDate, showTime),
|
||||
bottomDate(bumpedDate, showTime) {
|
||||
return entranceDate(bumpedDate, showTime);
|
||||
}
|
||||
|
||||
didInsertElement() {
|
||||
this._super(...arguments);
|
||||
super.didInsertElement(...arguments);
|
||||
this.appEvents.on("topic-entrance:show", this, "_show");
|
||||
},
|
||||
}
|
||||
|
||||
_setCSS() {
|
||||
const pos = this._position;
|
||||
|
@ -79,7 +90,7 @@ export default Component.extend(CleansUp, {
|
|||
pos.left = windowWidth - width - 15;
|
||||
}
|
||||
$self.css(pos);
|
||||
},
|
||||
}
|
||||
|
||||
@bind
|
||||
_escListener(e) {
|
||||
|
@ -96,42 +107,42 @@ export default Component.extend(CleansUp, {
|
|||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_jumpTopButton() {
|
||||
return this.element.querySelector(".jump-top");
|
||||
},
|
||||
}
|
||||
|
||||
_jumpBottomButton() {
|
||||
return this.element.querySelector(".jump-bottom");
|
||||
},
|
||||
}
|
||||
|
||||
_setupEscListener() {
|
||||
document.body.addEventListener("keydown", this._escListener);
|
||||
},
|
||||
}
|
||||
|
||||
_removeEscListener() {
|
||||
document.body.removeEventListener("keydown", this._escListener);
|
||||
},
|
||||
}
|
||||
|
||||
_trapFocus() {
|
||||
this._originalActiveElement = document.activeElement;
|
||||
this._jumpTopButton().focus();
|
||||
this._activeButton = "top";
|
||||
},
|
||||
}
|
||||
|
||||
_releaseFocus() {
|
||||
if (this._originalActiveElement) {
|
||||
this._originalActiveElement.focus();
|
||||
this._originalActiveElement = null;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_applyDomChanges() {
|
||||
this._setCSS();
|
||||
this._setupEscListener();
|
||||
this._trapFocus();
|
||||
},
|
||||
}
|
||||
|
||||
_show(data) {
|
||||
this._position = data.position;
|
||||
|
@ -152,34 +163,34 @@ export default Component.extend(CleansUp, {
|
|||
}
|
||||
this.cleanUp();
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
cleanUp() {
|
||||
this.setProperties({ topic: null, visible: false });
|
||||
$("html").off("mousedown.topic-entrance");
|
||||
this._removeEscListener();
|
||||
this._releaseFocus();
|
||||
},
|
||||
}
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
super.willDestroyElement(...arguments);
|
||||
this.appEvents.off("topic-entrance:show", this, "_show");
|
||||
},
|
||||
}
|
||||
|
||||
_jumpTo(destination) {
|
||||
this.historyStore.set("lastTopicIdViewed", this.topic.id);
|
||||
|
||||
this.cleanUp();
|
||||
DiscourseURL.routeTo(destination);
|
||||
},
|
||||
}
|
||||
|
||||
actions: {
|
||||
enterTop() {
|
||||
this._jumpTo(this.get("topic.url"));
|
||||
},
|
||||
@action
|
||||
enterTop() {
|
||||
this._jumpTo(this.get("topic.url"));
|
||||
}
|
||||
|
||||
enterBottom() {
|
||||
this._jumpTo(this.get("topic.lastPostUrl"));
|
||||
},
|
||||
},
|
||||
});
|
||||
@action
|
||||
enterBottom() {
|
||||
this._jumpTo(this.get("topic.lastPostUrl"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,79 +2,81 @@ import Component from "@ember/component";
|
|||
import { computed } from "@ember/object";
|
||||
import { alias, or } from "@ember/object/computed";
|
||||
import { getOwner } from "@ember/owner";
|
||||
import { attributeBindings } from "@ember-decorators/component";
|
||||
import { NotificationLevels } from "discourse/lib/notification-levels";
|
||||
import { getTopicFooterButtons } from "discourse/lib/register-topic-footer-button";
|
||||
import { getTopicFooterDropdowns } from "discourse/lib/register-topic-footer-dropdown";
|
||||
import TopicBookmarkManager from "discourse/lib/topic-bookmark-manager";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
|
||||
export default Component.extend({
|
||||
elementId: "topic-footer-buttons",
|
||||
@attributeBindings("role")
|
||||
export default class TopicFooterButtons extends Component {
|
||||
elementId = "topic-footer-buttons";
|
||||
role = "region";
|
||||
|
||||
attributeBindings: ["role"],
|
||||
@getTopicFooterButtons() inlineButtons;
|
||||
@getTopicFooterDropdowns() inlineDropdowns;
|
||||
|
||||
role: "region",
|
||||
@alias("currentUser.can_send_private_messages") canSendPms;
|
||||
@alias("topic.details.can_invite_to") canInviteTo;
|
||||
@alias("currentUser.user_option.enable_defer") canDefer;
|
||||
@or("topic.archived", "topic.closed", "topic.deleted") inviteDisabled;
|
||||
|
||||
@discourseComputed("canSendPms", "topic.isPrivateMessage")
|
||||
canArchive(canSendPms, isPM) {
|
||||
return canSendPms && isPM;
|
||||
},
|
||||
}
|
||||
|
||||
inlineButtons: getTopicFooterButtons(),
|
||||
inlineDropdowns: getTopicFooterDropdowns(),
|
||||
@computed("inlineButtons.[]", "inlineDropdowns.[]")
|
||||
get inlineActionables() {
|
||||
return this.inlineButtons
|
||||
.filterBy("dropdown", false)
|
||||
.filterBy("anonymousOnly", false)
|
||||
.concat(this.inlineDropdowns)
|
||||
.sortBy("priority")
|
||||
.reverse();
|
||||
}
|
||||
|
||||
inlineActionables: computed(
|
||||
"inlineButtons.[]",
|
||||
"inlineDropdowns.[]",
|
||||
function () {
|
||||
return this.inlineButtons
|
||||
.filterBy("dropdown", false)
|
||||
.filterBy("anonymousOnly", false)
|
||||
.concat(this.inlineDropdowns)
|
||||
.sortBy("priority")
|
||||
.reverse();
|
||||
}
|
||||
),
|
||||
|
||||
topicBookmarkManager: computed("topic", function () {
|
||||
@computed("topic")
|
||||
get topicBookmarkManager() {
|
||||
return new TopicBookmarkManager(getOwner(this), this.topic);
|
||||
}),
|
||||
}
|
||||
|
||||
// topic.assigned_to_user is for backward plugin support
|
||||
@discourseComputed("inlineButtons.[]", "topic.assigned_to_user")
|
||||
dropdownButtons(inlineButtons) {
|
||||
return inlineButtons.filter((button) => button.dropdown);
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed("topic.isPrivateMessage")
|
||||
showNotificationsButton(isPM) {
|
||||
return !isPM || this.canSendPms;
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed("topic.details.notification_level")
|
||||
showNotificationUserTip(notificationLevel) {
|
||||
return notificationLevel >= NotificationLevels.TRACKING;
|
||||
},
|
||||
|
||||
canSendPms: alias("currentUser.can_send_private_messages"),
|
||||
|
||||
canInviteTo: alias("topic.details.can_invite_to"),
|
||||
|
||||
canDefer: alias("currentUser.user_option.enable_defer"),
|
||||
|
||||
inviteDisabled: or("topic.archived", "topic.closed", "topic.deleted"),
|
||||
}
|
||||
|
||||
@discourseComputed("topic.message_archived")
|
||||
archiveIcon: (archived) => (archived ? "envelope" : "folder"),
|
||||
archiveIcon(archived) {
|
||||
return archived ? "envelope" : "folder";
|
||||
}
|
||||
|
||||
@discourseComputed("topic.message_archived")
|
||||
archiveTitle: (archived) =>
|
||||
archived ? "topic.move_to_inbox.help" : "topic.archive_message.help",
|
||||
archiveTitle(archived) {
|
||||
return archived ? "topic.move_to_inbox.help" : "topic.archive_message.help";
|
||||
}
|
||||
|
||||
@discourseComputed("topic.message_archived")
|
||||
archiveLabel: (archived) =>
|
||||
archived ? "topic.move_to_inbox.title" : "topic.archive_message.title",
|
||||
archiveLabel(archived) {
|
||||
return archived
|
||||
? "topic.move_to_inbox.title"
|
||||
: "topic.archive_message.title";
|
||||
}
|
||||
|
||||
@discourseComputed("topic.isPrivateMessage")
|
||||
showBookmarkLabel: (isPM) => !isPM,
|
||||
});
|
||||
showBookmarkLabel(isPM) {
|
||||
return !isPM;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,20 +1,22 @@
|
|||
import Component from "@ember/component";
|
||||
import { alias } from "@ember/object/computed";
|
||||
import { on } from "@ember/object/evented";
|
||||
import { getOwner } from "@ember/owner";
|
||||
import { schedule } from "@ember/runloop";
|
||||
import { service } from "@ember/service";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
import {
|
||||
attributeBindings,
|
||||
classNameBindings,
|
||||
tagName,
|
||||
} from "@ember-decorators/component";
|
||||
import { observes, on } from "@ember-decorators/object";
|
||||
import $ from "jquery";
|
||||
import { topicTitleDecorators } from "discourse/components/topic-title";
|
||||
import { wantsNewWindow } from "discourse/lib/intercept-click";
|
||||
import DiscourseURL, { groupPath } from "discourse/lib/url";
|
||||
import { RUNTIME_OPTIONS } from "discourse-common/lib/raw-handlebars-helpers";
|
||||
import { findRawTemplate } from "discourse-common/lib/raw-templates";
|
||||
import discourseComputed, {
|
||||
bind,
|
||||
observes,
|
||||
} from "discourse-common/utils/decorators";
|
||||
import discourseComputed, { bind } from "discourse-common/utils/decorators";
|
||||
import I18n from "discourse-i18n";
|
||||
|
||||
export function showEntrance(e) {
|
||||
|
@ -44,18 +46,19 @@ export function navigateToTopic(topic, href) {
|
|||
return false;
|
||||
}
|
||||
|
||||
export default Component.extend({
|
||||
router: service(),
|
||||
historyStore: service(),
|
||||
tagName: "tr",
|
||||
classNameBindings: [":topic-list-item", "unboundClassNames", "topic.visited"],
|
||||
attributeBindings: ["data-topic-id", "role", "ariaLevel:aria-level"],
|
||||
"data-topic-id": alias("topic.id"),
|
||||
@tagName("tr")
|
||||
@classNameBindings(":topic-list-item", "unboundClassNames", "topic.visited")
|
||||
@attributeBindings("dataTopicId:data-topic-id", "role", "ariaLevel:aria-level")
|
||||
export default class TopicListItem extends Component {
|
||||
@service router;
|
||||
@service historyStore;
|
||||
|
||||
@alias("topic.id") dataTopicId;
|
||||
|
||||
didReceiveAttrs() {
|
||||
this._super(...arguments);
|
||||
super.didReceiveAttrs(...arguments);
|
||||
this.renderTopicListItem();
|
||||
},
|
||||
}
|
||||
|
||||
// Already-rendered topic is marked as highlighted
|
||||
// Ideally this should be a modifier... but we can't do that
|
||||
|
@ -65,7 +68,7 @@ export default Component.extend({
|
|||
if (this.topic.highlight) {
|
||||
this._highlightIfNeeded();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@observes("topic.pinned", "expandGloballyPinned", "expandAllPinned")
|
||||
renderTopicListItem() {
|
||||
|
@ -91,10 +94,10 @@ export default Component.extend({
|
|||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
didInsertElement() {
|
||||
this._super(...arguments);
|
||||
super.didInsertElement(...arguments);
|
||||
|
||||
if (this.includeUnreadIndicator) {
|
||||
this.messageBus.subscribe(this.unreadIndicatorChannel, this.onMessage);
|
||||
|
@ -110,10 +113,10 @@ export default Component.extend({
|
|||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
super.willDestroyElement(...arguments);
|
||||
|
||||
this.messageBus.unsubscribe(this.unreadIndicatorChannel, this.onMessage);
|
||||
|
||||
|
@ -124,7 +127,7 @@ export default Component.extend({
|
|||
title.removeEventListener("blur", this._onTitleBlur);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@bind
|
||||
onMessage(data) {
|
||||
|
@ -133,7 +136,7 @@ export default Component.extend({
|
|||
).classList;
|
||||
|
||||
nodeClassList.toggle("read", !data.show_indicator);
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed("topic.participant_groups")
|
||||
participantGroups(groupNames) {
|
||||
|
@ -144,29 +147,29 @@ export default Component.extend({
|
|||
return groupNames.map((name) => {
|
||||
return { name, url: groupPath(name) };
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed("topic.id")
|
||||
unreadIndicatorChannel(topicId) {
|
||||
return `/private-messages/unread-indicator/${topicId}`;
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed("topic.unread_by_group_member")
|
||||
unreadClass(unreadByGroupMember) {
|
||||
return unreadByGroupMember ? "" : "read";
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed("topic.unread_by_group_member")
|
||||
includeUnreadIndicator(unreadByGroupMember) {
|
||||
return typeof unreadByGroupMember !== "undefined";
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed
|
||||
newDotText() {
|
||||
return this.currentUser && this.currentUser.trust_level > 0
|
||||
? ""
|
||||
: I18n.t("filters.new.lower_title");
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed("topic", "lastVisitedTopic")
|
||||
unboundClassNames(topic, lastVisitedTopic) {
|
||||
|
@ -177,7 +180,7 @@ export default Component.extend({
|
|||
}
|
||||
|
||||
if (topic.get("tags")) {
|
||||
topic.get("tags").forEach((tagName) => classes.push("tag-" + tagName));
|
||||
topic.get("tags").forEach((tag) => classes.push("tag-" + tag));
|
||||
}
|
||||
|
||||
if (topic.get("hasExcerpt")) {
|
||||
|
@ -203,15 +206,15 @@ export default Component.extend({
|
|||
}
|
||||
|
||||
return classes.join(" ");
|
||||
},
|
||||
}
|
||||
|
||||
hasLikes() {
|
||||
return this.get("topic.like_count") > 0;
|
||||
},
|
||||
}
|
||||
|
||||
hasOpLikes() {
|
||||
return this.get("topic.op_like_count") > 0;
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed
|
||||
expandPinned() {
|
||||
|
@ -239,9 +242,11 @@ export default Component.extend({
|
|||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
}
|
||||
|
||||
showEntrance,
|
||||
showEntrance() {
|
||||
return showEntrance.call(this, ...arguments);
|
||||
}
|
||||
|
||||
click(e) {
|
||||
const result = this.showEntrance(e);
|
||||
|
@ -316,18 +321,20 @@ export default Component.extend({
|
|||
}
|
||||
|
||||
return this.unhandledRowClick(e, topic);
|
||||
},
|
||||
}
|
||||
|
||||
unhandledRowClick() {},
|
||||
unhandledRowClick() {}
|
||||
|
||||
keyDown(e) {
|
||||
if (e.key === "Enter" && e.target.classList.contains("post-activity")) {
|
||||
e.preventDefault();
|
||||
return this.navigateToTopic(this.topic, e.target.getAttribute("href"));
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
navigateToTopic,
|
||||
navigateToTopic() {
|
||||
return navigateToTopic.call(this, ...arguments);
|
||||
}
|
||||
|
||||
highlight(opts = { isLastViewedTopic: false }) {
|
||||
schedule("afterRender", () => {
|
||||
|
@ -347,9 +354,10 @@ export default Component.extend({
|
|||
this._titleElement()?.focus();
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
_highlightIfNeeded: on("didInsertElement", function () {
|
||||
@on("didInsertElement")
|
||||
_highlightIfNeeded() {
|
||||
// highlight the last topic viewed
|
||||
const lastViewedTopicId = this.historyStore.get("lastTopicIdViewed");
|
||||
const isLastViewedTopic = lastViewedTopicId === this.topic.id;
|
||||
|
@ -362,27 +370,27 @@ export default Component.extend({
|
|||
this.set("topic.highlight", false);
|
||||
this.highlight();
|
||||
}
|
||||
}),
|
||||
}
|
||||
|
||||
@bind
|
||||
_onTitleFocus() {
|
||||
if (this.element && !this.isDestroying && !this.isDestroyed) {
|
||||
this.element.classList.add("selected");
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@bind
|
||||
_onTitleBlur() {
|
||||
if (this.element && !this.isDestroying && !this.isDestroyed) {
|
||||
this.element.classList.remove("selected");
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_shouldFocusLastVisited() {
|
||||
return this.site.desktopView && this.focusLastVisitedTopic;
|
||||
},
|
||||
}
|
||||
|
||||
_titleElement() {
|
||||
return this.element.querySelector(".main-link .title");
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,68 +1,75 @@
|
|||
import Component from "@ember/component";
|
||||
import { dependentKeyCompat } from "@ember/object/compat";
|
||||
import { alias } from "@ember/object/computed";
|
||||
import { on } from "@ember/object/evented";
|
||||
import { service } from "@ember/service";
|
||||
import {
|
||||
classNameBindings,
|
||||
classNames,
|
||||
tagName,
|
||||
} from "@ember-decorators/component";
|
||||
import { observes, on } from "@ember-decorators/object";
|
||||
import LoadMore from "discourse/mixins/load-more";
|
||||
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
|
||||
export default Component.extend(LoadMore, {
|
||||
modal: service(),
|
||||
router: service(),
|
||||
siteSettings: service(),
|
||||
@tagName("table")
|
||||
@classNames("topic-list")
|
||||
@classNameBindings("bulkSelectEnabled:sticky-header")
|
||||
export default class TopicList extends Component.extend(LoadMore) {
|
||||
@service modal;
|
||||
@service router;
|
||||
@service siteSettings;
|
||||
|
||||
tagName: "table",
|
||||
classNames: ["topic-list"],
|
||||
classNameBindings: ["bulkSelectEnabled:sticky-header"],
|
||||
showTopicPostBadges: true,
|
||||
listTitle: "topic.title",
|
||||
lastCheckedElementId: null,
|
||||
showTopicPostBadges = true;
|
||||
listTitle = "topic.title";
|
||||
lastCheckedElementId = null;
|
||||
|
||||
// Overwrite this to perform client side filtering of topics, if desired
|
||||
@alias("topics") filteredTopics;
|
||||
|
||||
get canDoBulkActions() {
|
||||
return (
|
||||
this.currentUser?.canManageTopic && this.bulkSelectHelper?.selected.length
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
// Overwrite this to perform client side filtering of topics, if desired
|
||||
filteredTopics: alias("topics"),
|
||||
|
||||
_init: on("init", function () {
|
||||
@on("init")
|
||||
_init() {
|
||||
this.addObserver("hideCategory", this.rerender);
|
||||
this.addObserver("order", this.rerender);
|
||||
this.addObserver("ascending", this.rerender);
|
||||
this.refreshLastVisited();
|
||||
}),
|
||||
}
|
||||
|
||||
get selected() {
|
||||
return this.bulkSelectHelper?.selected;
|
||||
},
|
||||
}
|
||||
|
||||
@dependentKeyCompat // for the classNameBindings
|
||||
// for the classNameBindings
|
||||
@dependentKeyCompat
|
||||
get bulkSelectEnabled() {
|
||||
return this.bulkSelectHelper?.bulkSelectEnabled;
|
||||
},
|
||||
}
|
||||
|
||||
get toggleInTitle() {
|
||||
return (
|
||||
!this.bulkSelectHelper?.bulkSelectEnabled && this.get("canBulkSelect")
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed
|
||||
sortable() {
|
||||
return !!this.changeSort;
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed("order")
|
||||
showLikes(order) {
|
||||
return order === "likes";
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed("order")
|
||||
showOpLikes(order) {
|
||||
return order === "op_likes";
|
||||
},
|
||||
}
|
||||
|
||||
@observes("topics.[]")
|
||||
topicsAdded() {
|
||||
|
@ -70,22 +77,22 @@ export default Component.extend(LoadMore, {
|
|||
if (!this.lastVisitedTopic) {
|
||||
this.refreshLastVisited();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@observes("topics", "order", "ascending", "category", "top", "hot")
|
||||
lastVisitedTopicChanged() {
|
||||
this.refreshLastVisited();
|
||||
},
|
||||
}
|
||||
|
||||
scrolled() {
|
||||
this._super(...arguments);
|
||||
super.scrolled(...arguments);
|
||||
let onScroll = this.onScroll;
|
||||
if (!onScroll) {
|
||||
return;
|
||||
}
|
||||
|
||||
onScroll.call(this);
|
||||
},
|
||||
}
|
||||
|
||||
_updateLastVisitedTopic(topics, order, ascending, top, hot) {
|
||||
this.set("lastVisitedTopic", null);
|
||||
|
@ -145,7 +152,7 @@ export default Component.extend(LoadMore, {
|
|||
}
|
||||
|
||||
this.set("lastVisitedTopic", lastVisitedTopic);
|
||||
},
|
||||
}
|
||||
|
||||
refreshLastVisited() {
|
||||
this._updateLastVisitedTopic(
|
||||
|
@ -155,7 +162,7 @@ export default Component.extend(LoadMore, {
|
|||
this.top,
|
||||
this.hot
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
click(e) {
|
||||
const onClick = (sel, callback) => {
|
||||
|
@ -200,7 +207,7 @@ export default Component.extend(LoadMore, {
|
|||
}
|
||||
this.rerender();
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
keyDown(e) {
|
||||
if (e.key === "Enter" || e.key === " ") {
|
||||
|
@ -217,5 +224,5 @@ export default Component.extend(LoadMore, {
|
|||
this.rerender();
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
import Component from "@ember/component";
|
||||
import { action } from "@ember/object";
|
||||
import { tagName } from "@ember-decorators/component";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
|
||||
export default Component.extend({
|
||||
tagName: "",
|
||||
popupId: null,
|
||||
hidden: false,
|
||||
@tagName("")
|
||||
export default class TopicNavigationPopup extends Component {
|
||||
popupId = null;
|
||||
hidden = false;
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
super.init(...arguments);
|
||||
|
||||
if (this.popupKey) {
|
||||
const value = this.keyValueStore.getItem(this.popupKey);
|
||||
|
@ -18,14 +19,14 @@ export default Component.extend({
|
|||
this.keyValueStore.removeItem(this.popupKey);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed("popupId")
|
||||
popupKey(popupId) {
|
||||
if (popupId) {
|
||||
return `dismiss_topic_nav_popup_${popupId}`;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@action
|
||||
close() {
|
||||
|
@ -39,5 +40,5 @@ export default Component.extend({
|
|||
this.keyValueStore.setItem(this.popupKey, true);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,43 +2,40 @@ import Component from "@ember/component";
|
|||
import EmberObject from "@ember/object";
|
||||
import { next } from "@ember/runloop";
|
||||
import { service } from "@ember/service";
|
||||
import { classNameBindings } from "@ember-decorators/component";
|
||||
import { observes } from "@ember-decorators/object";
|
||||
import $ from "jquery";
|
||||
import { headerOffset } from "discourse/lib/offset-calculator";
|
||||
import SwipeEvents from "discourse/lib/swipe-events";
|
||||
import discourseDebounce from "discourse-common/lib/debounce";
|
||||
import discourseLater from "discourse-common/lib/later";
|
||||
import { bind, observes } from "discourse-common/utils/decorators";
|
||||
import { bind } from "discourse-common/utils/decorators";
|
||||
import JumpToPost from "./modal/jump-to-post";
|
||||
|
||||
const MIN_WIDTH_TIMELINE = 925;
|
||||
const MIN_HEIGHT_TIMELINE = 325;
|
||||
|
||||
export default Component.extend({
|
||||
modal: service(),
|
||||
@classNameBindings(
|
||||
"info.topicProgressExpanded:topic-progress-expanded",
|
||||
"info.renderTimeline:with-timeline:with-topic-progress"
|
||||
)
|
||||
export default class TopicNavigation extends Component {
|
||||
@service modal;
|
||||
|
||||
classNameBindings: [
|
||||
"info.topicProgressExpanded:topic-progress-expanded",
|
||||
"info.renderTimeline:with-timeline:with-topic-progress",
|
||||
],
|
||||
composerOpen: null,
|
||||
info: null,
|
||||
canRender: true,
|
||||
_lastTopicId: null,
|
||||
_swipeEvents: null,
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
this.set("info", EmberObject.create());
|
||||
},
|
||||
composerOpen = null;
|
||||
info = EmberObject.create();
|
||||
canRender = true;
|
||||
_lastTopicId = null;
|
||||
_swipeEvents = null;
|
||||
|
||||
didUpdateAttrs() {
|
||||
this._super(...arguments);
|
||||
super.didUpdateAttrs(...arguments);
|
||||
if (this._lastTopicId !== this.topic.id) {
|
||||
this._lastTopicId = this.topic.id;
|
||||
this.set("canRender", false);
|
||||
next(() => this.set("canRender", true));
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_performCheckSize() {
|
||||
if (!this.element || this.isDestroying || this.isDestroyed) {
|
||||
|
@ -60,17 +57,17 @@ export default Component.extend({
|
|||
this.mediaQuery.matches && verticalSpace > MIN_HEIGHT_TIMELINE
|
||||
);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@bind
|
||||
_checkSize() {
|
||||
discourseDebounce(this, this._performCheckSize, 200, true);
|
||||
},
|
||||
}
|
||||
|
||||
// we need to store this so topic progress has something to init with
|
||||
_topicScrolled(event) {
|
||||
this.set("info.prevEvent", event);
|
||||
},
|
||||
}
|
||||
|
||||
@observes("info.topicProgressExpanded")
|
||||
_expanded() {
|
||||
|
@ -95,17 +92,17 @@ export default Component.extend({
|
|||
$(window).off("click.hide-fullscreen");
|
||||
}
|
||||
this._checkSize();
|
||||
},
|
||||
}
|
||||
|
||||
composerOpened() {
|
||||
this.set("composerOpen", true);
|
||||
this._checkSize();
|
||||
},
|
||||
}
|
||||
|
||||
composerClosed() {
|
||||
this.set("composerOpen", false);
|
||||
this._checkSize();
|
||||
},
|
||||
}
|
||||
|
||||
_collapseFullscreen(delay = 500) {
|
||||
if (this.get("info.topicProgressExpanded")) {
|
||||
|
@ -119,7 +116,7 @@ export default Component.extend({
|
|||
this._checkSize();
|
||||
}, delay);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
keyboardTrigger(e) {
|
||||
if (e.type === "jump") {
|
||||
|
@ -131,7 +128,7 @@ export default Component.extend({
|
|||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@bind
|
||||
onSwipeStart(event) {
|
||||
|
@ -153,7 +150,7 @@ export default Component.extend({
|
|||
} else if (e.direction === "up" || e.direction === "down") {
|
||||
this.movingElement = document.querySelector(".timeline-container");
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@bind
|
||||
onSwipeCancel() {
|
||||
|
@ -164,7 +161,7 @@ export default Component.extend({
|
|||
fill: "forwards",
|
||||
easing: "ease-out",
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
@bind
|
||||
onSwipeEnd(event) {
|
||||
|
@ -195,7 +192,7 @@ export default Component.extend({
|
|||
easing: "ease-out",
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@bind
|
||||
onSwipe(event) {
|
||||
|
@ -207,10 +204,10 @@ export default Component.extend({
|
|||
[{ transform: `translate3d(0, ${this.pxClosed}px, 0)` }],
|
||||
{ fill: "forwards" }
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
didInsertElement() {
|
||||
this._super(...arguments);
|
||||
super.didInsertElement(...arguments);
|
||||
|
||||
this._lastTopicId = this.topic.id;
|
||||
|
||||
|
@ -237,10 +234,10 @@ export default Component.extend({
|
|||
this.element.addEventListener("swipecancel", this.onSwipeCancel);
|
||||
this.element.addEventListener("swipe", this.onSwipe);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
super.willDestroyElement(...arguments);
|
||||
|
||||
this.appEvents
|
||||
.off("topic:current-post-scrolled", this, this._topicScrolled)
|
||||
|
@ -263,5 +260,5 @@ export default Component.extend({
|
|||
this.element.removeEventListener("swipe", this.onSwipe);
|
||||
this._swipeEvents.removeTouchListeners();
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,20 +2,23 @@ import Component from "@ember/component";
|
|||
import { action } from "@ember/object";
|
||||
import { alias } from "@ember/object/computed";
|
||||
import { scheduleOnce } from "@ember/runloop";
|
||||
import { classNameBindings } from "@ember-decorators/component";
|
||||
import { isTesting } from "discourse-common/config/environment";
|
||||
import discourseLater from "discourse-common/lib/later";
|
||||
import discourseComputed, { bind } from "discourse-common/utils/decorators";
|
||||
|
||||
const CSS_TRANSITION_DELAY = isTesting() ? 0 : 500;
|
||||
|
||||
export default Component.extend({
|
||||
elementId: "topic-progress-wrapper",
|
||||
classNameBindings: ["docked", "withTransitions"],
|
||||
docked: false,
|
||||
withTransitions: null,
|
||||
progressPosition: null,
|
||||
postStream: alias("topic.postStream"),
|
||||
_streamPercentage: null,
|
||||
@classNameBindings("docked", "withTransitions")
|
||||
export default class TopicProgress extends Component {
|
||||
elementId = "topic-progress-wrapper";
|
||||
docked = false;
|
||||
withTransitions = null;
|
||||
progressPosition = null;
|
||||
|
||||
@alias("topic.postStream") postStream;
|
||||
|
||||
_streamPercentage = null;
|
||||
|
||||
@discourseComputed(
|
||||
"postStream.loaded",
|
||||
|
@ -25,14 +28,14 @@ export default Component.extend({
|
|||
hideProgress(loaded, currentPost, filteredPostsCount) {
|
||||
const hideOnShortStream = this.site.desktopView && filteredPostsCount < 2;
|
||||
return !loaded || !currentPost || hideOnShortStream;
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed("postStream.filteredPostsCount")
|
||||
hugeNumberOfPosts(filteredPostsCount) {
|
||||
return (
|
||||
filteredPostsCount >= this.siteSettings.short_progress_text_threshold
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed("progressPosition", "topic.last_read_post_id")
|
||||
showBackButton(position, lastReadId) {
|
||||
|
@ -43,7 +46,7 @@ export default Component.extend({
|
|||
const stream = this.get("postStream.stream");
|
||||
const readPos = stream.indexOf(lastReadId) || 0;
|
||||
return readPos < stream.length - 1 && readPos > position;
|
||||
},
|
||||
}
|
||||
|
||||
_topicScrolled(event) {
|
||||
if (this.docked) {
|
||||
|
@ -57,15 +60,15 @@ export default Component.extend({
|
|||
_streamPercentage: (event.percent * 100).toFixed(2),
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed("_streamPercentage")
|
||||
progressStyle(_streamPercentage) {
|
||||
return `--progress-bg-width: ${_streamPercentage || 0}%`;
|
||||
},
|
||||
}
|
||||
|
||||
didInsertElement() {
|
||||
this._super(...arguments);
|
||||
super.didInsertElement(...arguments);
|
||||
|
||||
this.appEvents
|
||||
.on("composer:resized", this, this._composerEvent)
|
||||
|
@ -79,15 +82,15 @@ export default Component.extend({
|
|||
// start CSS transitions a tiny bit later
|
||||
// to avoid jumpiness on initial topic load
|
||||
discourseLater(this._addCssTransitions, CSS_TRANSITION_DELAY);
|
||||
},
|
||||
}
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
super.willDestroyElement(...arguments);
|
||||
this._topicBottomObserver?.disconnect();
|
||||
this.appEvents
|
||||
.off("composer:resized", this, this._composerEvent)
|
||||
.off("topic:current-post-scrolled", this, this._topicScrolled);
|
||||
},
|
||||
}
|
||||
|
||||
@bind
|
||||
_addCssTransitions() {
|
||||
|
@ -95,7 +98,7 @@ export default Component.extend({
|
|||
return;
|
||||
}
|
||||
this.set("withTransitions", true);
|
||||
},
|
||||
}
|
||||
|
||||
_startObserver() {
|
||||
if ("IntersectionObserver" in window) {
|
||||
|
@ -104,7 +107,7 @@ export default Component.extend({
|
|||
document.querySelector("#topic-bottom")
|
||||
);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_setupObserver() {
|
||||
// minimum 50px here ensures element is not docked when
|
||||
|
@ -117,7 +120,7 @@ export default Component.extend({
|
|||
threshold: 1,
|
||||
rootMargin: `0px 0px -${bottomIntersectionMargin}px 0px`,
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
_composerEvent() {
|
||||
// reinitializing needed to account for composer height
|
||||
|
@ -127,7 +130,7 @@ export default Component.extend({
|
|||
this._topicBottomObserver?.disconnect();
|
||||
this._startObserver();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@bind
|
||||
_intersectionHandler(entries) {
|
||||
|
@ -167,16 +170,16 @@ export default Component.extend({
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
click(e) {
|
||||
if (e.target.closest("#topic-progress")) {
|
||||
this.toggleProperty("expanded");
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@action
|
||||
goBack() {
|
||||
this.jumpToPost(this.get("topic.last_read_post_number"));
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,55 +1,58 @@
|
|||
import Component from "@ember/component";
|
||||
import { cancel, next } from "@ember/runloop";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
import { classNames } from "@ember-decorators/component";
|
||||
import { on } from "@ember-decorators/object";
|
||||
import { DELETE_REPLIES_TYPE } from "discourse/components/modal/edit-topic-timer";
|
||||
import Category from "discourse/models/category";
|
||||
import { isTesting } from "discourse-common/config/environment";
|
||||
import { iconHTML } from "discourse-common/lib/icon-library";
|
||||
import discourseLater from "discourse-common/lib/later";
|
||||
import discourseComputed, { on } from "discourse-common/utils/decorators";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import I18n from "discourse-i18n";
|
||||
|
||||
export default Component.extend({
|
||||
classNames: ["topic-timer-info"],
|
||||
_delayedRerender: null,
|
||||
clockIcon: htmlSafe(`${iconHTML("far-clock")}`),
|
||||
trashLabel: I18n.t("post.controls.remove_timer"),
|
||||
title: null,
|
||||
notice: null,
|
||||
showTopicTimer: null,
|
||||
showTopicTimerModal: null,
|
||||
removeTopicTimer: null,
|
||||
@classNames("topic-timer-info")
|
||||
export default class TopicTimerInfo extends Component {
|
||||
clockIcon = htmlSafe(`${iconHTML("far-clock")}`);
|
||||
trashLabel = I18n.t("post.controls.remove_timer");
|
||||
|
||||
title = null;
|
||||
notice = null;
|
||||
showTopicTimer = null;
|
||||
showTopicTimerModal = null;
|
||||
removeTopicTimer = null;
|
||||
_delayedRerender = null;
|
||||
|
||||
@on("didReceiveAttrs")
|
||||
setupRenderer() {
|
||||
this.renderTopicTimer();
|
||||
},
|
||||
}
|
||||
|
||||
@on("willDestroyElement")
|
||||
cancelDelayedRenderer() {
|
||||
if (this._delayedRerender) {
|
||||
cancel(this._delayedRerender);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed
|
||||
canModifyTimer() {
|
||||
return this.currentUser && this.currentUser.get("canManageTopic");
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed("canModifyTimer", "removeTopicTimer")
|
||||
showTrashCan(canModifyTimer, removeTopicTimer) {
|
||||
return canModifyTimer && removeTopicTimer;
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed("canModifyTimer", "showTopicTimerModal")
|
||||
showEdit(canModifyTimer, showTopicTimerModal) {
|
||||
return canModifyTimer && showTopicTimerModal;
|
||||
},
|
||||
}
|
||||
|
||||
additionalOpts() {
|
||||
return {};
|
||||
},
|
||||
}
|
||||
|
||||
renderTopicTimer() {
|
||||
const isDeleteRepliesType = this.statusType === DELETE_REPLIES_TYPE;
|
||||
|
@ -130,7 +133,7 @@ export default Component.extend({
|
|||
} else {
|
||||
this.set("showTopicTimer", null);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
rerenderDelay(minutesLeft) {
|
||||
if (minutesLeft > 2160) {
|
||||
|
@ -144,7 +147,7 @@ export default Component.extend({
|
|||
}
|
||||
|
||||
return 1000;
|
||||
},
|
||||
}
|
||||
|
||||
_noticeKey() {
|
||||
let statusType = this.statusType;
|
||||
|
@ -156,5 +159,5 @@ export default Component.extend({
|
|||
}
|
||||
|
||||
return `topic.status_update_notice.auto_${statusType}`;
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue