mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
283 lines
6.6 KiB
JavaScript
283 lines
6.6 KiB
JavaScript
import discourseComputed from "discourse-common/utils/decorators";
|
|
import { emojiUnescape } from "discourse/lib/text";
|
|
import Category from "discourse/models/category";
|
|
import EmberObject from "@ember/object";
|
|
import { reads } from "@ember/object/computed";
|
|
import deprecated from "discourse-common/lib/deprecated";
|
|
import Site from "discourse/models/site";
|
|
import User from "discourse/models/user";
|
|
|
|
const NavItem = EmberObject.extend({
|
|
@discourseComputed("name")
|
|
title(name) {
|
|
const extra = {};
|
|
|
|
return I18n.t("filters." + name.replace("/", ".") + ".help", extra);
|
|
},
|
|
|
|
@discourseComputed("name", "count")
|
|
displayName(name, count) {
|
|
count = count || 0;
|
|
|
|
if (
|
|
name === "latest" &&
|
|
(!Site.currentProp("mobileView") || this.tagId !== undefined)
|
|
) {
|
|
count = 0;
|
|
}
|
|
|
|
let extra = { count: count };
|
|
const titleKey = count === 0 ? ".title" : ".title_with_count";
|
|
|
|
return emojiUnescape(
|
|
I18n.t(`filters.${name.replace("/", ".") + titleKey}`, extra)
|
|
);
|
|
},
|
|
|
|
@discourseComputed("filterType", "category", "noSubcategories", "tagId")
|
|
href(filterType, category, noSubcategories, tagId) {
|
|
let customHref = null;
|
|
|
|
NavItem.customNavItemHrefs.forEach(function(cb) {
|
|
customHref = cb.call(this, this);
|
|
if (customHref) {
|
|
return false;
|
|
}
|
|
}, this);
|
|
|
|
if (customHref) {
|
|
return customHref;
|
|
}
|
|
|
|
const context = { category, noSubcategories, tagId };
|
|
return NavItem.pathFor(filterType, context);
|
|
},
|
|
|
|
filterType: reads("name"),
|
|
|
|
@discourseComputed("name", "category", "noSubcategories")
|
|
filterMode(name, category, noSubcategories) {
|
|
let mode = "";
|
|
if (category) {
|
|
mode += "c/";
|
|
mode += Category.slugFor(category);
|
|
if (noSubcategories) {
|
|
mode += "/none";
|
|
}
|
|
mode += "/l/";
|
|
}
|
|
return mode + name.replace(" ", "-");
|
|
},
|
|
|
|
@discourseComputed("name", "category", "topicTrackingState.messageCount")
|
|
count(name, category) {
|
|
const state = this.topicTrackingState;
|
|
if (state) {
|
|
return state.lookupCount(name, category);
|
|
}
|
|
}
|
|
});
|
|
|
|
const ExtraNavItem = NavItem.extend({
|
|
href: discourseComputed("href", {
|
|
get() {
|
|
if (this._href) {
|
|
return this._href;
|
|
}
|
|
|
|
return this.href;
|
|
},
|
|
|
|
set(key, value) {
|
|
return (this._href = value);
|
|
}
|
|
}),
|
|
|
|
count: 0,
|
|
|
|
customFilter: null
|
|
});
|
|
|
|
NavItem.reopenClass({
|
|
extraArgsCallbacks: [],
|
|
customNavItemHrefs: [],
|
|
extraNavItemDescriptors: [],
|
|
|
|
pathFor(filterType, context) {
|
|
let path = Discourse.getURL("");
|
|
let includesCategoryContext = false;
|
|
let includesTagContext = false;
|
|
|
|
if (filterType === "categories") {
|
|
path += "/categories";
|
|
return path;
|
|
}
|
|
|
|
if (context.tagId && Site.currentProp("filters").includes(filterType)) {
|
|
includesTagContext = true;
|
|
path += "/tags";
|
|
}
|
|
|
|
if (context.category) {
|
|
includesCategoryContext = true;
|
|
path += `/c/${Category.slugFor(context.category)}`;
|
|
|
|
if (context.noSubcategories) {
|
|
path += "/none";
|
|
}
|
|
}
|
|
|
|
if (includesTagContext) {
|
|
path += `/${context.tagId}`;
|
|
}
|
|
|
|
if (includesTagContext || includesCategoryContext) {
|
|
path += "/l";
|
|
}
|
|
|
|
path += `/${filterType}`;
|
|
|
|
// In the case of top, the nav item doesn't include a period because the
|
|
// period has its own selector just below
|
|
|
|
return path;
|
|
},
|
|
|
|
// Create a nav item given a filterType. It returns null if there is not
|
|
// valid nav item. The name is a historical artifact.
|
|
fromText(filterType, opts) {
|
|
const anonymous = !User.current();
|
|
|
|
opts = opts || {};
|
|
|
|
if (
|
|
anonymous &&
|
|
!Site.currentProp("anonymous_top_menu_items").includes(filterType)
|
|
)
|
|
return null;
|
|
|
|
if (!Category.list() && filterType === "categories") return null;
|
|
if (!Site.currentProp("top_menu_items").includes(filterType)) return null;
|
|
|
|
var args = { name: filterType, hasIcon: filterType === "unread" };
|
|
if (opts.category) {
|
|
args.category = opts.category;
|
|
}
|
|
if (opts.tagId) {
|
|
args.tagId = opts.tagId;
|
|
}
|
|
if (opts.persistedQueryParams) {
|
|
args.persistedQueryParams = opts.persistedQueryParams;
|
|
}
|
|
if (opts.noSubcategories) {
|
|
args.noSubcategories = true;
|
|
}
|
|
NavItem.extraArgsCallbacks.forEach(cb =>
|
|
_.merge(args, cb.call(this, filterType, opts))
|
|
);
|
|
|
|
const store = Discourse.__container__.lookup("service:store");
|
|
return store.createRecord("nav-item", args);
|
|
},
|
|
|
|
buildList(category, args) {
|
|
args = args || {};
|
|
|
|
if (category) {
|
|
args.category = category;
|
|
}
|
|
|
|
let items = Discourse.SiteSettings.top_menu.split("|");
|
|
|
|
const filterType = (args.filterMode || "").split("/").pop();
|
|
|
|
if (!items.some(i => filterType === i)) {
|
|
items.push(filterType);
|
|
}
|
|
|
|
items = items
|
|
.map(i => NavItem.fromText(i, args))
|
|
.filter(
|
|
i => i !== null && !(category && i.get("name").indexOf("categor") === 0)
|
|
);
|
|
|
|
const context = {
|
|
category: args.category,
|
|
tagId: args.tagId,
|
|
noSubcategories: args.noSubcategories
|
|
};
|
|
|
|
const extraItems = NavItem.extraNavItemDescriptors
|
|
.map(descriptor => ExtraNavItem.create(_.merge({}, context, descriptor)))
|
|
.filter(item => {
|
|
if (!item.customFilter) return true;
|
|
return item.customFilter(category, args);
|
|
});
|
|
|
|
let forceActive = false;
|
|
|
|
extraItems.forEach(item => {
|
|
if (item.init) {
|
|
item.init(item, category, args);
|
|
}
|
|
|
|
const before = item.before;
|
|
if (before) {
|
|
let i = 0;
|
|
for (i = 0; i < items.length; i++) {
|
|
if (items[i].name === before) {
|
|
break;
|
|
}
|
|
}
|
|
items.splice(i, 0, item);
|
|
} else {
|
|
items.push(item);
|
|
}
|
|
|
|
if (item.customHref) {
|
|
item.set("href", item.customHref(category, args));
|
|
}
|
|
|
|
if (item.forceActive && item.forceActive(category, args)) {
|
|
item.active = true;
|
|
forceActive = true;
|
|
} else {
|
|
item.active = undefined;
|
|
}
|
|
});
|
|
|
|
if (forceActive) {
|
|
items.forEach(i => {
|
|
if (i.active === undefined) {
|
|
i.active = false;
|
|
}
|
|
});
|
|
}
|
|
return items;
|
|
}
|
|
});
|
|
|
|
export default NavItem;
|
|
|
|
export function extraNavItemProperties(cb) {
|
|
NavItem.extraArgsCallbacks.push(cb);
|
|
}
|
|
|
|
export function customNavItemHref(cb) {
|
|
NavItem.customNavItemHrefs.push(cb);
|
|
}
|
|
|
|
export function addNavItem(item) {
|
|
NavItem.extraNavItemDescriptors.push(item);
|
|
}
|
|
|
|
Object.defineProperty(Discourse, "NavItem", {
|
|
get() {
|
|
deprecated("Import the NavItem class instead of using Discourse.NavItem", {
|
|
since: "2.4.0",
|
|
dropFrom: "2.5.0"
|
|
});
|
|
return NavItem;
|
|
}
|
|
});
|