DEV: Group and nest routes in `userPrivateMessages` into child routes (#19190)

Currently this is how the navigation structure looks like on the messages page: 

#### When personal inbox route is active

```
Inbox
  sent
  new
  unread
  archive
Group 1 Inbox
Group 2 Inbox
Tags
<Plugin Outlet>
```

#### When group inbox route is active

```
Inbox
Group 1 Inbox
  sent
  new
  unread
  archive
Group 2 Inbox
Tags
<Plugin Outlet>
```

With the existing structure, it is very easy for plugins to add additional navigation links by using the plugin outlet. In the redesigned user page navigation, the navigation structure on the messages page has been changed to look like this: 

#### When personal inbox route is active

```
---dropdown-------
| Inbox          |   Latest | Sent | New | Unread | Archive
------------------
```

#### When group inbox route is active 

```
---dropdown------
| Group 1 Inbox |   Latest | New | Unread | Archive
-----------------
```

With the new navigation structure, we can no longer rely on a simple plugin outlet to extend the navigation structure. Instead, we will need to introduce a plugin API for plugins to extend the navigation structure. The API needs to allow two things to happen: 

1. The plugin API needs to allow the plugin to register an item in the drop down and for the registered item to be "selected" whenever the plugin's routes are active. 

1. The plugin API needs to allow the plugin to register items into the secondary horizontal navigation menu beside the drop down. 

While trying to design the API, I struggle with trying to determine the "context" of the current route. In order words, it was hard to figure out if the current user is viewing the personal inbox, group inbox or tags. This is attributed to the fact that our current routing structure looks like this:

```
this.route(
  "userPrivateMessages",
  { path: "/messages", resetNamespace: true },
  function () {
    this.route("new");
    this.route("unread");
    this.route("archive");
    this.route("sent");
    this.route("warnings");
    this.route("group", { path: "group/:name" });
    this.route("groupArchive", { path: "group/:name/archive" });
    this.route("groupNew", { path: "group/:name/new" });
    this.route("groupUnread", { path: "group/:name/unread" });
    this.route("tags");
    this.route("tagsShow", { path: "tags/:id" });
  }
);
```

In order to provide context of the current route, we currently require all child routes under the `userPrivateMessages` route to set a `pmView` property on the `userPrivateMessages` controller. If the route requires additional context like the group currently active on the group inbox routes, the child routes would then have to set the `group` property on the `userPrivateMessages` controller. The problems with this approach is that we end up with many permutations of state on the `userPrivateMessages` controller and have to always clean up the state when navigating between the child routes. Basically, data is flowing upwards from the child routes into the parent controller which is not an ideal approach because we cannot easily determine where the "data" setup happens. Instead, we want to follow something similar to the "Data down, actions up" pattern where data flows downwards. In this commit, the `userPrivateMessages` routes have been changed to look like this: 

```
this.route(
  "userPrivateMessages",
  { path: "/messages", resetNamespace: true },
  function () {
    this.route("user", { path: "/" }, function () {
      this.route("new");
      this.route("unread");
      this.route("archive");
      this.route("sent");
      this.route("warnings");
    });

    this.route("group", { path: "group/:name" }, function () {
      this.route("archive");
      this.route("new");
      this.route("unread");
    });

    this.route("tags", { path: "/tags" }, function () {
      this.route("show", { path: ":id" });
    });
  }
);
```

Basically, we group the child routes based on the purpose each route servers. User inbox routes are grouped together while group inbox routes are grouped together. A big benefit of this is that now have a different Ember router and controller for each grouping of child routes. The context of the current route is then tied directly to the route name instead of requiring each child route to set an attribute on the parent controller. 

The second reason for why we needed to group the child routes together is because it allows us to pass the responsibility of rendering the secondary navigation links to the child routes. In this commit, we use the `{{in-element}}` modifier in the child route to render the secondary navigation links.

```
---dropdown--------
| Group 1 Inbox   |     Latest | New | Unread | Archive
------------------------
<parent template>    <horizontal secondary navigation links element>
```

This means that each child route with its own model and context can then handle the responsibility of rendering the secondary navigation links without having to pass its context up to the `userPrivateMessages` controller. While this should have simplified by the `userPrivateMessages` controller, we can't do that in this commit because our current navigation structure requires all links for all message inboxes to remain on screen at all times. Once we fully transition to the redesigned user menu navigation, we will be able to greatly simplify things around the routes and controllers for `userPrivateMessages`. 

In an ideal world, we would deprecate the old routes but I have done a quick search through all known plugins and no plugins are currently relying on those routes. There is a chance we could break plugins here but I'll like to see some smoke first before committing to the effort of deprecating client side routes.
This commit is contained in:
Alan Guo Xiang Tan 2022-12-01 09:21:12 +08:00 committed by GitHub
parent d516c575fd
commit 4da2e3fef4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 396 additions and 254 deletions

View File

@ -27,6 +27,7 @@ export default class SidebarUserMessagesSection extends Component {
@service appEvents;
@service pmTopicTrackingState;
@service currentUser;
@service router;
constructor() {
super(...arguments);
@ -69,13 +70,13 @@ export default class SidebarUserMessagesSection extends Component {
);
}
_refreshSectionLinksDisplayState({
currentRouteName,
currentRouteParentName,
currentRouteParams,
}) {
_refreshSectionLinksDisplayState() {
const currentRouteName = this.router.currentRoute.name;
const currentRouteParentName = this.router.currentRoute.parent.name;
const currentRouteParentParams = this.router.currentRoute.parent.params;
if (
currentRouteParentName !== "userPrivateMessages" &&
!currentRouteParentName.includes("userPrivateMessages") &&
currentRouteParentName !== "topic"
) {
for (const sectionLink of this.allSectionLinks) {
@ -84,7 +85,7 @@ export default class SidebarUserMessagesSection extends Component {
} else {
const attrs = {
currentRouteName,
currentRouteParams,
currentRouteParentParams,
};
if (currentRouteParentName === "topic") {

View File

@ -7,97 +7,7 @@
</ol>
{{/if}}
<HorizontalOverflowNav @className="messages-nav">
{{#if @isPersonal}}
<li class="messages-latest">
<LinkTo @route="userPrivateMessages.index" @model={{@user}}>
{{d-icon "envelope"}}
<span>{{i18n "categories.latest"}}</span>
</LinkTo>
</li>
<li class="messages-sent">
<LinkTo @route="userPrivateMessages.sent" @model={{@user}}>
{{d-icon "reply"}}
<span>{{i18n "user.messages.sent"}}</span>
</LinkTo>
</li>
{{#if @viewingSelf}}
<li class="messages-new">
<LinkTo @route="userPrivateMessages.new" @model={{@user}} class="new">
{{d-icon "exclamation-circle"}}
<span>{{@newLinkText}}</span>
</LinkTo>
</li>
<li class="messages-unread">
<LinkTo @route="userPrivateMessages.unread" @model={{@user}} class="unread">
{{d-icon "plus-circle"}}
<span>{{@unreadLinkText}}</span>
</LinkTo>
</li>
{{/if}}
<li class="messages-archive">
<LinkTo @route="userPrivateMessages.archive" @model={{@user}}>
{{d-icon "archive"}}
<span>{{i18n "user.messages.archive"}}</span>
</LinkTo>
</li>
{{/if}}
{{#if @isGroup}}
<li class="messages-group-latest">
<LinkTo @route="userPrivateMessages.group" @model={{@group.name}}>
{{d-icon "envelope"}}
<span>{{i18n "categories.latest"}}</span>
</LinkTo>
</li>
{{#if @viewingSelf}}
<li class="messages-group-new">
<LinkTo @route="userPrivateMessages.groupNew" @model={{@group.name}} class="new">
{{d-icon "exclamation-circle"}}
<span>{{@newLinkText}}</span>
</LinkTo>
</li>
<li class="messages-group-unread">
<LinkTo @route="userPrivateMessages.groupUnread" @model={{@group.name}} class="unread">
{{d-icon "plus-circle"}}
<span>{{@unreadLinkText}}</span>
</LinkTo>
</li>
<li class="messages-group-archive">
<LinkTo @route="userPrivateMessages.groupArchive" @model={{@group.name}}>
{{d-icon "archive"}}
<span>{{i18n "user.messages.archive"}}</span>
</LinkTo>
</li>
{{/if}}
{{/if}}
{{#if this.displayTags}}
<li class="tags">
<LinkTo @route="userPrivateMessages.tags" @model={{@user}}>
{{d-icon "tag"}}
<span>{{i18n "user.messages.all_tags"}}</span>
</LinkTo>
</li>
{{#if @tagId}}
<li class="archive">
<LinkTo @route="userPrivateMessages.tagsShow" @model={{@tagId}}>
{{@tagId}}
</LinkTo>
</li>
{{/if}}
{{/if}}
<PluginOutlet @name="user-messages-nav" @connectorTagName="li" @args={{hash model=@user}} />
</HorizontalOverflowNav>
<HorizontalOverflowNav @className="messages-nav" id="user-navigation-secondary__horizontal-nav" />
{{#if this.site.desktopView}}
<div class="navigation-controls">

View File

@ -7,7 +7,8 @@ export default class UserNavMessagesNav extends Component {
get messagesDropdownvalue() {
switch (this.args.currentRouteName) {
case "userPrivateMessages.tags":
case "userPrivateMessages.tagsShow":
case "userPrivateMessages.tags.index":
case "userPrivateMessages.tags.show":
return "tags";
default:
if (this.args.groupFilter) {

View File

@ -0,0 +1,5 @@
{{#if this.currentUser.redesigned_user_page_nav_enabled}}
{{#in-element this.messagesNav}}
{{yield}}
{{/in-element}}
{{/if}}

View File

@ -0,0 +1,10 @@
import Component from "@glimmer/component";
import { inject as service } from "@ember/service";
export default class extends Component {
@service currentUser;
get messagesNav() {
return document.getElementById("user-navigation-secondary__horizontal-nav");
}
}

View File

@ -0,0 +1,40 @@
import I18n from "I18n";
import Controller, { inject as controller } from "@ember/controller";
import { computed } from "@ember/object";
export default class extends Controller {
@controller user;
get viewingSelf() {
return this.user.viewingSelf;
}
@computed(
"pmTopicTrackingState.newIncoming.[]",
"pmTopicTrackingState.statesModificationCounter"
)
get newLinkText() {
return this.#linkText("new");
}
@computed(
"pmTopicTrackingState.newIncoming.[]",
"pmTopicTrackingState.statesModificationCounter"
)
get unreadLinkText() {
return this.#linkText("unread");
}
#linkText(type) {
const count = this.pmTopicTrackingState?.lookupCount(type, {
inboxFilter: "group",
groupName: this.groupName,
});
if (count === 0) {
return I18n.t(`user.messages.${type}`);
} else {
return I18n.t(`user.messages.${type}_with_count`, { count });
}
}
}

View File

@ -0,0 +1,28 @@
import Controller from "@ember/controller";
import { action } from "@ember/object";
import { tracked } from "@glimmer/tracking";
export default class extends Controller {
@tracked tagsForUser = null;
@tracked sortedByCount = true;
@tracked sortedByName = false;
@tracked sortProperties = ["count:desc", "id"];
@action
sortByCount(event) {
event?.preventDefault();
this.sortProperties = ["count:desc", "id"];
this.sortedByCount = true;
this.sortedByName = false;
}
@action
sortById(event) {
event?.preventDefault();
this.sortProperties = ["id"];
this.sortedByCount = false;
this.sortedByName = true;
}
}

View File

@ -1,29 +1,6 @@
import Controller from "@ember/controller";
import { action } from "@ember/object";
import { tracked } from "@glimmer/tracking";
export default Controller.extend({
sortProperties: ["count:desc", "id"],
tagsForUser: null,
sortedByCount: true,
sortedByName: false,
@action
sortByCount(event) {
event?.preventDefault();
this.setProperties({
sortProperties: ["count:desc", "id"],
sortedByCount: true,
sortedByName: false,
});
},
@action
sortById(event) {
event?.preventDefault();
this.setProperties({
sortProperties: ["id"],
sortedByCount: false,
sortedByName: true,
});
},
});
export default class extends Controller {
@tracked tagName = null;
}

View File

@ -0,0 +1,50 @@
import I18n from "I18n";
import Controller, { inject as controller } from "@ember/controller";
import { computed } from "@ember/object";
import { inject as service } from "@ember/service";
export default class extends Controller {
@service router;
@controller user;
get viewingSelf() {
return this.user.viewingSelf;
}
@computed("viewingSelf", "router.currentRoute.name", "currentUser.admin")
get showWarningsWarning() {
return (
this.router.currentRoute.name === "userPrivateMessages.user.warnings" &&
!this.viewingSelf &&
!this.currentUser.isAdmin
);
}
@computed(
"pmTopicTrackingState.newIncoming.[]",
"pmTopicTrackingState.statesModificationCounter"
)
get newLinkText() {
return this.#linkText("new");
}
@computed(
"pmTopicTrackingState.newIncoming.[]",
"pmTopicTrackingState.statesModificationCounter"
)
get unreadLinkText() {
return this.#linkText("unread");
}
#linkText(type) {
const count = this.pmTopicTrackingState?.lookupCount(type, {
inboxFilter: "user",
});
if (count === 0) {
return I18n.t(`user.messages.${type}`);
} else {
return I18n.t(`user.messages.${type}_with_count`, { count });
}
}
}

View File

@ -3,7 +3,6 @@ import { action } from "@ember/object";
import { inject as service } from "@ember/service";
import { alias, and, equal, readOnly } from "@ember/object/computed";
import discourseComputed from "discourse-common/utils/decorators";
import { VIEW_NAME_WARNINGS } from "discourse/routes/user-private-messages-warnings";
import I18n from "I18n";
export const PERSONAL_INBOX = "__personal_inbox__";
@ -12,23 +11,18 @@ export default Controller.extend({
user: controller(),
router: service(),
pmView: false,
viewingSelf: alias("user.viewingSelf"),
isGroup: equal("pmView", "group"),
isPersonal: equal("pmView", "user"),
isGroup: equal("currentParentRouteName", "userPrivateMessages.group"),
isPersonal: equal("currentParentRouteName", "userPrivateMessages.user"),
group: null,
groupFilter: alias("group.name"),
currentRouteName: readOnly("router.currentRouteName"),
currentParentRouteName: readOnly("router.currentRoute.parent.name"),
pmTaggingEnabled: alias("site.can_tag_pms"),
tagId: null,
showNewPM: and("user.viewingSelf", "currentUser.can_send_private_messages"),
@discourseComputed("viewingSelf", "pmView", "currentUser.admin")
showWarningsWarning(viewingSelf, pmView, isAdmin) {
return pmView === VIEW_NAME_WARNINGS && !viewingSelf && !isAdmin;
},
@discourseComputed(
"pmTopicTrackingState.newIncoming.[]",
"pmTopicTrackingState.statesModificationCounter",

View File

@ -42,8 +42,6 @@ export function startPageTracking(router, appEvents, documentTitle) {
url,
title: documentTitle.getTitle(),
currentRouteName: router.currentRouteName,
currentRouteParams: router.currentRoute.params,
currentRouteParentName: router.currentRoute.parent?.name,
replacedOnlyQueryParams,
});
});

View File

@ -1,14 +1,14 @@
import I18n from "I18n";
import { capitalize } from "@ember/string";
import MessageSectionLink from "discourse/lib/sidebar/user/messages-section/message-section-link";
export default class GroupMessageSectionLink extends MessageSectionLink {
routeNames = new Set([
"userPrivateMessages.group",
"userPrivateMessages.groupUnread",
"userPrivateMessages.groupNew",
"userPrivateMessages.groupArchive",
"userPrivateMessages.group.index",
"userPrivateMessages.group.unread",
"userPrivateMessages.group.new",
"userPrivateMessages.group.archive",
]);
get name() {
@ -23,7 +23,7 @@ export default class GroupMessageSectionLink extends MessageSectionLink {
if (this._isInbox) {
return "userPrivateMessages.group";
} else {
return `userPrivateMessages.group${capitalize(this.type)}`;
return `userPrivateMessages.group.${this.type}`;
}
}
@ -49,7 +49,11 @@ export default class GroupMessageSectionLink extends MessageSectionLink {
}
}
pageChanged({ currentRouteName, currentRouteParams, privateMessageTopic }) {
pageChanged({
currentRouteName,
currentRouteParentParams,
privateMessageTopic,
}) {
if (this._isInbox) {
return;
}
@ -65,6 +69,7 @@ export default class GroupMessageSectionLink extends MessageSectionLink {
this.setDisplayState =
this.routeNames.has(currentRouteName) &&
currentRouteParams.name.toLowerCase() === this.group.name.toLowerCase();
currentRouteParentParams.name.toLowerCase() ===
this.group.name.toLowerCase();
}
}

View File

@ -4,11 +4,12 @@ import MessageSectionLink from "discourse/lib/sidebar/user/messages-section/mess
export default class PersonalMessageSectionLink extends MessageSectionLink {
routeNames = new Set([
"userPrivateMessages.index",
"userPrivateMessages.unread",
"userPrivateMessages.sent",
"userPrivateMessages.new",
"userPrivateMessages.archive",
"userPrivateMessages.user",
"userPrivateMessages.user.index",
"userPrivateMessages.user.unread",
"userPrivateMessages.user.sent",
"userPrivateMessages.user.new",
"userPrivateMessages.user.archive",
]);
get name() {
@ -21,9 +22,9 @@ export default class PersonalMessageSectionLink extends MessageSectionLink {
get route() {
if (this._isInbox) {
return "userPrivateMessages.index";
return "userPrivateMessages.user.index";
} else {
return `userPrivateMessages.${this.type}`;
return `userPrivateMessages.user.${this.type}`;
}
}

View File

@ -11,6 +11,6 @@ export default class PMTagSectionLink extends BaseTagSectionLink {
}
get route() {
return "userPrivateMessages.tagsShow";
return "userPrivateMessages.tags.show";
}
}

View File

@ -141,17 +141,23 @@ export default function () {
"userPrivateMessages",
{ path: "/messages", resetNamespace: true },
function () {
this.route("new");
this.route("unread");
this.route("archive");
this.route("sent");
this.route("warnings");
this.route("group", { path: "group/:name" });
this.route("groupArchive", { path: "group/:name/archive" });
this.route("groupNew", { path: "group/:name/new" });
this.route("groupUnread", { path: "group/:name/unread" });
this.route("tags");
this.route("tagsShow", { path: "tags/:id" });
this.route("user", { path: "/" }, function () {
this.route("new");
this.route("unread");
this.route("archive");
this.route("sent");
this.route("warnings");
});
this.route("group", { path: "group/:name" }, function () {
this.route("archive");
this.route("new");
this.route("unread");
});
this.route("tags", { path: "/tags" }, function () {
this.route("show", { path: ":id" });
});
}
);

View File

@ -21,9 +21,11 @@ export default (inboxType, filter) => {
}
},
model(params) {
model() {
const username = this.modelFor("user").get("username_lower");
let topicListFilter = `topics/private-messages-group/${username}/${params.name}`;
const groupName = this.modelFor("userPrivateMessages.group");
let topicListFilter = `topics/private-messages-group/${username}/${groupName}`;
if (filter !== "inbox") {
topicListFilter = `${topicListFilter}/${filter}`;

View File

@ -77,7 +77,6 @@ export default (inboxType, path, filter) => {
userPrivateMessagesController.setProperties({
archive: false,
pmView: inboxType,
group: null,
});

View File

@ -0,0 +1,4 @@
import createPMRoute from "discourse/routes/build-private-messages-group-route";
import { INBOX_FILTER } from "discourse/routes/build-private-messages-route";
export default createPMRoute("group", INBOX_FILTER);

View File

@ -1,4 +1,11 @@
import createPMRoute from "discourse/routes/build-private-messages-group-route";
import { INBOX_FILTER } from "discourse/routes/build-private-messages-route";
import DiscourseRoute from "discourse/routes/discourse";
export default createPMRoute("group", INBOX_FILTER);
export default class extends DiscourseRoute {
model(params) {
return params.name;
}
setupController(controller, model) {
controller.set("groupName", model);
}
}

View File

@ -0,0 +1,36 @@
import DiscourseRoute from "discourse/routes/discourse";
import EmberObject from "@ember/object";
import I18n from "I18n";
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
export default DiscourseRoute.extend({
model() {
const username = this.modelFor("user").get("username_lower");
return ajax(`/tags/personal_messages/${username}`)
.then((result) => {
return result.tags.map((tag) => EmberObject.create(tag));
})
.catch(popupAjaxError);
},
titleToken() {
return [I18n.t("tagging.tags"), I18n.t("user.private_messages")];
},
setupController(controller, model) {
controller.setProperties({
model,
sortProperties: this.siteSettings.tags_sort_alphabetically
? ["id"]
: ["count:desc", "id"],
tagsForUser: this.modelFor("user").get("username_lower"),
});
this.controllerFor("user-topics-list").setProperties({
showToggleBulkSelect: false,
selected: [],
});
},
});

View File

@ -12,6 +12,8 @@ export default createPMRoute("tags", "private-messages-tags").extend({
model(params) {
this.controllerFor("user-private-messages").set("tagId", params.id);
this.controllerFor("user-private-messages-tags").set("tagName", params.id);
const username = this.modelFor("user").get("username_lower");
this.set("tagId", params.id);

View File

@ -1,39 +1,3 @@
import DiscourseRoute from "discourse/routes/discourse";
import EmberObject from "@ember/object";
import I18n from "I18n";
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
export default DiscourseRoute.extend({
model() {
const username = this.modelFor("user").get("username_lower");
return ajax(`/tags/personal_messages/${username}`)
.then((result) => {
return result.tags.map((tag) => EmberObject.create(tag));
})
.catch(popupAjaxError);
},
titleToken() {
return [I18n.t("tagging.tags"), I18n.t("user.private_messages")];
},
setupController(controller, model) {
this.controllerFor("user-private-messages-tags").setProperties({
model,
sortProperties: this.siteSettings.tags_sort_alphabetically
? ["id"]
: ["count:desc", "id"],
tagsForUser: this.modelFor("user").get("username_lower"),
});
this.controllerFor("user-topics-list").setProperties({
showToggleBulkSelect: false,
selected: [],
});
this.controllerFor("user-private-messages").setProperties({
pmView: "tags",
});
},
});
export default DiscourseRoute.extend({});

View File

@ -1,9 +1,7 @@
import createPMRoute from "discourse/routes/build-private-messages-route";
export const VIEW_NAME_WARNINGS = "warnings";
export default createPMRoute(
VIEW_NAME_WARNINGS,
"warnings",
"private-messages-warnings",
null /* no message bus notifications */
);

View File

@ -0,0 +1,11 @@
import DiscourseRoute from "discourse/routes/discourse";
export default class extends DiscourseRoute {
model() {
return this.modelFor("user");
}
setupController(controller, model) {
controller.set("model", model);
}
}

View File

@ -43,7 +43,6 @@ export default DiscourseRoute.extend({
@action
willTransition() {
this._super(...arguments);
this.controllerFor("user").set("pmView", null);
return true;
},
});

View File

@ -21,6 +21,7 @@
{{did-insert this.scrollToActive}}
{{on "mousedown" this.scrollDrag}}
class="nav-pills action-list {{@className}}"
...attributes
>
{{yield}}
</ul>

View File

@ -0,0 +1,33 @@
<UserNav::MessagesSecondaryNav>
<li class="messages-group-latest">
<LinkTo @route="userPrivateMessages.group.index" @model={{this.groupName}}>
{{d-icon "envelope"}}
<span>{{i18n "categories.latest"}}</span>
</LinkTo>
</li>
{{#if this.viewingSelf}}
<li class="messages-group-new">
<LinkTo @route="userPrivateMessages.group.new" @model={{this.groupName}} class="new">
{{d-icon "exclamation-circle"}}
<span>{{this.newLinkText}}</span>
</LinkTo>
</li>
<li class="messages-group-unread">
<LinkTo @route="userPrivateMessages.group.unread" @model={{this.groupName}} class="unread">
{{d-icon "plus-circle"}}
<span>{{this.unreadLinkText}}</span>
</LinkTo>
</li>
<li class="messages-group-archive">
<LinkTo @route="userPrivateMessages.group.archive" @model={{this.groupName}}>
{{d-icon "archive"}}
<span>{{i18n "user.messages.archive"}}</span>
</LinkTo>
</li>
{{/if}}
</UserNav::MessagesSecondaryNav>
{{outlet}}

View File

@ -0,0 +1,25 @@
<div class="list-controls">
<div class="container">
<h2>{{i18n "tagging.tags"}}</h2>
</div>
</div>
<div class="tag-sort-options">
{{i18n "tagging.sort_by"}}
<span class="tag-sort-count {{if this.sortedByCount "active"}}">
<a href {{on "click" this.sortByCount}}>
{{i18n "tagging.sort_by_count"}}
</a>
</span>
<span class="tag-sort-name {{if this.sortedByName "active"}}">
<a href {{on "click" this.sortById}}>
{{i18n "tagging.sort_by_name"}}
</a>
</span>
</div>
<hr>
{{#if this.model}}
<TagList @tags={{this.model}} @sortProperties={{this.sortProperties}} @titleKey="tagging.all_tags" @isPrivateMessage={{true}} @tagsForUser={{this.tagsForUser}} />
{{/if}}

View File

@ -1,25 +1,18 @@
<div class="list-controls">
<div class="container">
<h2>{{i18n "tagging.tags"}}</h2>
</div>
</div>
<UserNav::MessagesSecondaryNav>
<li class="tags">
<LinkTo @route="userPrivateMessages.tags.index">
{{d-icon "tag"}}
<span>{{i18n "user.messages.all_tags"}}</span>
</LinkTo>
</li>
<div class="tag-sort-options">
{{i18n "tagging.sort_by"}}
<span class="tag-sort-count {{if this.sortedByCount "active"}}">
<a href {{on "click" this.sortByCount}}>
{{i18n "tagging.sort_by_count"}}
</a>
</span>
<span class="tag-sort-name {{if this.sortedByName "active"}}">
<a href {{on "click" this.sortById}}>
{{i18n "tagging.sort_by_name"}}
</a>
</span>
</div>
{{#if this.tagName}}
<li class="archive">
<LinkTo @route="userPrivateMessages.tags.show" @model={{this.tagName}}>
{{this.tagName}}
</LinkTo>
</li>
{{/if}}
</UserNav::MessagesSecondaryNav>
<hr>
{{#if this.model}}
<TagList @tags={{this.model}} @sortProperties={{this.sortProperties}} @titleKey="tagging.all_tags" @isPrivateMessage={{true}} @tagsForUser={{this.tagsForUser}} />
{{/if}}
{{outlet}}

View File

@ -0,0 +1,46 @@
{{#if this.showWarningsWarning}}
<div class="alert alert-info">{{html-safe (i18n "admin.user.warnings_list_warning")}}</div>
{{/if}}
<UserNav::MessagesSecondaryNav>
<li class="messages-latest">
<LinkTo @route="userPrivateMessages.user.index" @model={{this.model}}>
{{d-icon "envelope"}}
<span>{{i18n "categories.latest"}}</span>
</LinkTo>
</li>
<li class="messages-sent">
<LinkTo @route="userPrivateMessages.user.sent" @model={{this.model}}>
{{d-icon "reply"}}
<span>{{i18n "user.messages.sent"}}</span>
</LinkTo>
</li>
{{#if this.viewingSelf}}
<li class="messages-new">
<LinkTo @route="userPrivateMessages.user.new" @model={{this.model}} class="new">
{{d-icon "exclamation-circle"}}
<span>{{this.newLinkText}}</span>
</LinkTo>
</li>
<li class="messages-unread">
<LinkTo @route="userPrivateMessages.user.unread" @model={{this.model}} class="unread">
{{d-icon "plus-circle"}}
<span>{{this.unreadLinkText}}</span>
</LinkTo>
</li>
{{/if}}
<li class="messages-archive">
<LinkTo @route="userPrivateMessages.user.archive" @model={{this.model}}>
{{d-icon "archive"}}
<span>{{i18n "user.messages.archive"}}</span>
</LinkTo>
</li>
<PluginOutlet @name="user-messages-nav" @connectorTagName="li" @args={{hash model=this.model}} />
</UserNav::MessagesSecondaryNav>
{{outlet}}

View File

@ -21,34 +21,34 @@
<DSection @class="user-secondary-navigation" @pageClass="user-messages">
<MobileNav @class="messages-nav" @desktopClass="nav-stacked action-list">
<li>
<LinkTo @route="userPrivateMessages.index" @model={{this.model}}>
<LinkTo @route="userPrivateMessages.user.index" @model={{this.model}}>
{{i18n "user.messages.inbox"}}
</LinkTo>
</li>
{{#if this.isPersonal}}
<li class="archive">
<LinkTo @route="userPrivateMessages.sent" @model={{this.model}}>
<LinkTo @route="userPrivateMessages.user.sent" @model={{this.model}}>
{{i18n "user.messages.sent"}}
</LinkTo>
</li>
{{#if this.viewingSelf}}
<li class="archive">
<LinkTo @route="userPrivateMessages.new" @model={{this.model}} class="new">
<LinkTo @route="userPrivateMessages.user.new" @model={{this.model}} class="new">
{{this.newLinkText}}
</LinkTo>
</li>
<li class="archive">
<LinkTo @route="userPrivateMessages.unread" @model={{this.model}} class="unread">
<LinkTo @route="userPrivateMessages.user.unread" @model={{this.model}} class="unread">
{{this.unreadLinkText}}
</LinkTo>
</li>
{{/if}}
<li class="archive">
<LinkTo @route="userPrivateMessages.archive" @model={{this.model}}>
<LinkTo @route="userPrivateMessages.user.archive" @model={{this.model}}>
{{i18n "user.messages.archive"}}
</LinkTo>
</li>
@ -65,20 +65,20 @@
{{#if (and this.isGroup (eq this.groupFilter group.name))}}
{{#if this.viewingSelf}}
<li class="archive">
<LinkTo @route="userPrivateMessages.groupNew" @model={{group.name}} class="new">
<LinkTo @route="userPrivateMessages.group.new" @model={{group.name}} class="new">
{{this.newLinkText}}
</LinkTo>
</li>
<li class="archive">
<LinkTo @route="userPrivateMessages.groupUnread" @model={{group.name}} class="unread">
<LinkTo @route="userPrivateMessages.group.unread" @model={{group.name}} class="unread">
{{this.unreadLinkText}}
</LinkTo>
</li>
{{/if}}
<li class="archive">
<LinkTo @route="userPrivateMessages.groupArchive" @model={{group.name}}>
<LinkTo @route="userPrivateMessages.group.archive" @model={{group.name}}>
{{i18n "user.messages.archive"}}
</LinkTo>
</li>
@ -87,18 +87,18 @@
{{#if this.pmTaggingEnabled}}
<li class="tags">
<LinkTo @route="userPrivateMessages.tags" @model={{this.model}}>
<LinkTo @route="userPrivateMessages.tags.index" @model={{this.model}}>
{{i18n "user.messages.tags"}}
</LinkTo>
{{#if this.tagId}}
<li class="archive">
<LinkTo @route="userPrivateMessages.tagsShow" @model={{this.tagId}}>
{{this.tagId}}
</LinkTo>
</li>
{{/if}}
</li>
{{#if this.tagId}}
<li class="archive">
<LinkTo @route="userPrivateMessages.tags.show" @model={{this.tagId}}>
{{this.tagId}}
</LinkTo>
</li>
{{/if}}
{{/if}}
<PluginOutlet @name="user-messages-nav" @tagName="span" @connectorTagName="li" @args={{hash model=this.model}} />
@ -130,9 +130,5 @@
{{/if}}
</div>
{{#if this.showWarningsWarning}}
<div class="alert alert-info">{{html-safe (i18n "admin.user.warnings_list_warning")}}</div>
{{/if}}
{{outlet}}
</section>