mirror of
https://github.com/discourse/discourse.git
synced 2026-08-14 06:55:03 -05:00
FEATURE: add period filter in top topics route for tags. (#13415)
And also move all the "top topics by period" routes to query string param. /top/monthly => /top?period=monthly /c/:slug/:id/l/top/monthly => /c/:slug/:id/l/top?period=monthly /tag/:slug/l/top/daily => /tag/:slug/l/top?period=daily (new)
This commit is contained in:
@@ -13,6 +13,7 @@ export const queryParams = {
|
||||
before: { replace: true, refreshModel: true },
|
||||
bumped_before: { replace: true, refreshModel: true },
|
||||
f: { replace: true, refreshModel: true },
|
||||
period: { replace: true, refreshModel: true },
|
||||
};
|
||||
|
||||
// Basic controller options
|
||||
|
||||
@@ -37,9 +37,10 @@ export default Controller.extend({
|
||||
}/l`;
|
||||
}
|
||||
|
||||
url += "/top/" + period;
|
||||
url += "/top";
|
||||
|
||||
const queryParams = this.router.currentRoute.queryParams;
|
||||
let queryParams = this.router.currentRoute.queryParams;
|
||||
queryParams.period = period;
|
||||
if (Object.keys(queryParams).length) {
|
||||
url =
|
||||
`${url}?` +
|
||||
|
||||
@@ -1,12 +1,4 @@
|
||||
import {
|
||||
alias,
|
||||
empty,
|
||||
equal,
|
||||
gt,
|
||||
not,
|
||||
notEmpty,
|
||||
readOnly,
|
||||
} from "@ember/object/computed";
|
||||
import { alias, empty, equal, gt, not, readOnly } from "@ember/object/computed";
|
||||
import BulkTopicSelection from "discourse/mixins/bulk-topic-selection";
|
||||
import DiscoveryController from "discourse/controllers/discovery";
|
||||
import I18n from "I18n";
|
||||
@@ -140,7 +132,7 @@ const controllerOpts = {
|
||||
allLoaded: empty("model.more_topics_url"),
|
||||
latest: endWith("model.filter", "latest"),
|
||||
new: endWith("model.filter", "new"),
|
||||
top: notEmpty("period"),
|
||||
top: endWith("model.filter", "top"),
|
||||
yearly: equal("period", "yearly"),
|
||||
quarterly: equal("period", "quarterly"),
|
||||
monthly: equal("period", "monthly"),
|
||||
|
||||
@@ -8,6 +8,7 @@ import Topic from "discourse/models/topic";
|
||||
import { alias } from "@ember/object/computed";
|
||||
import bootbox from "bootbox";
|
||||
import { queryParams } from "discourse/controllers/discovery-sortable";
|
||||
import { endWith } from "discourse/lib/computed";
|
||||
|
||||
export default Controller.extend(BulkTopicSelection, FilterModeMixin, {
|
||||
application: controller(),
|
||||
@@ -27,6 +28,8 @@ export default Controller.extend(BulkTopicSelection, FilterModeMixin, {
|
||||
max_posts: null,
|
||||
q: null,
|
||||
showInfo: false,
|
||||
top: endWith("list.filter", "top"),
|
||||
period: alias("list.for_period"),
|
||||
|
||||
@discourseComputed(
|
||||
"canCreateTopic",
|
||||
@@ -131,8 +134,30 @@ export default Controller.extend(BulkTopicSelection, FilterModeMixin, {
|
||||
this.setProperties({ order, ascending: false });
|
||||
}
|
||||
|
||||
let params = { order, ascending: this.ascending };
|
||||
if (this.period) {
|
||||
params.period = this.period;
|
||||
}
|
||||
|
||||
this.transitionToRoute({
|
||||
queryParams: { order, ascending: this.ascending },
|
||||
queryParams: params,
|
||||
});
|
||||
},
|
||||
|
||||
changePeriod(p) {
|
||||
this.set("period", p);
|
||||
|
||||
let params = { period: this.period };
|
||||
|
||||
if (!(this.order === "default" && this.ascending === false)) {
|
||||
params = Object.assign(params, {
|
||||
order: this.order,
|
||||
ascending: this.ascending,
|
||||
});
|
||||
}
|
||||
|
||||
this.transitionToRoute({
|
||||
queryParams: params,
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@@ -36,13 +36,7 @@ export default Mixin.create({
|
||||
|
||||
set(key, value) {
|
||||
this.set("rawFilterMode", value);
|
||||
const parts = value.split("/");
|
||||
|
||||
if (parts.length >= 2 && parts[parts.length - 2] === "top") {
|
||||
this.set("filterType", "top");
|
||||
} else {
|
||||
this.set("filterType", parts.pop());
|
||||
}
|
||||
this.set("filterType", value.split("/").pop());
|
||||
|
||||
return value;
|
||||
},
|
||||
|
||||
@@ -34,7 +34,18 @@ export default {
|
||||
app[
|
||||
`Discovery${filterCapitalized}CategoryNoneController`
|
||||
] = DiscoverySortableController.extend();
|
||||
if (filter !== "top") {
|
||||
|
||||
if (filter === "top") {
|
||||
app.DiscoveryTopRoute = buildTopicRoute("top", {
|
||||
actions: {
|
||||
willTransition() {
|
||||
User.currentProp("should_be_redirected_to_top", false);
|
||||
User.currentProp("redirected_to_top.reason", null);
|
||||
return this._super(...arguments);
|
||||
},
|
||||
},
|
||||
});
|
||||
} else {
|
||||
app[`Discovery${filterCapitalized}Route`] = buildTopicRoute(filter);
|
||||
}
|
||||
|
||||
@@ -46,38 +57,6 @@ export default {
|
||||
] = buildCategoryRoute(filter, { no_subcategories: true });
|
||||
});
|
||||
|
||||
app.DiscoveryTopRoute = buildTopicRoute("top", {
|
||||
actions: {
|
||||
willTransition() {
|
||||
User.currentProp("should_be_redirected_to_top", false);
|
||||
User.currentProp("redirected_to_top.reason", null);
|
||||
return this._super(...arguments);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
site.get("periods").forEach((period) => {
|
||||
const periodCapitalized = period.capitalize();
|
||||
app[
|
||||
`DiscoveryTop${periodCapitalized}Controller`
|
||||
] = DiscoverySortableController.extend();
|
||||
app[
|
||||
`DiscoveryTop${periodCapitalized}CategoryController`
|
||||
] = DiscoverySortableController.extend();
|
||||
app[
|
||||
`DiscoveryTop${periodCapitalized}CategoryNoneController`
|
||||
] = DiscoverySortableController.extend();
|
||||
app[`DiscoveryTop${periodCapitalized}Route`] = buildTopicRoute(
|
||||
"top/" + period
|
||||
);
|
||||
app[`DiscoveryTop${periodCapitalized}CategoryRoute`] = buildCategoryRoute(
|
||||
"top/" + period
|
||||
);
|
||||
app[
|
||||
`DiscoveryTop${periodCapitalized}CategoryNoneRoute`
|
||||
] = buildCategoryRoute("top/" + period, { no_subcategories: true });
|
||||
});
|
||||
|
||||
app["TagsShowCategoryRoute"] = TagShowRoute.extend();
|
||||
app["TagsShowCategoryNoneRoute"] = TagShowRoute.extend({
|
||||
noSubcategories: true,
|
||||
|
||||
@@ -27,14 +27,7 @@ export default function () {
|
||||
});
|
||||
|
||||
this.route("discovery", { path: "/", resetNamespace: true }, function () {
|
||||
// top
|
||||
this.route("top");
|
||||
this.route("topCategoryNone", {
|
||||
path: "/c/*category_slug_path_with_id/none/l/top",
|
||||
});
|
||||
this.route("topCategory", { path: "/c/*category_slug_path_with_id/l/top" });
|
||||
|
||||
// top by periods
|
||||
// top by periods - legacy route
|
||||
Site.currentProp("periods").forEach((period) => {
|
||||
const top = "top" + period.capitalize();
|
||||
|
||||
@@ -47,7 +40,7 @@ export default function () {
|
||||
});
|
||||
});
|
||||
|
||||
// filters (e.g. bookmarks, posted, read, unread, latest)
|
||||
// filters (e.g. bookmarks, posted, read, unread, latest, top)
|
||||
Site.currentProp("filters").forEach((filter) => {
|
||||
this.route(filter, { path: "/" + filter });
|
||||
this.route(filter + "CategoryNone", {
|
||||
|
||||
@@ -148,8 +148,7 @@ export default (filterArg, params) => {
|
||||
category = model.category,
|
||||
canCreateTopic = topics.get("can_create_topic"),
|
||||
canCreateTopicOnCategory =
|
||||
canCreateTopic && category.get("permission") === PermissionType.FULL,
|
||||
filter = this.filter(category);
|
||||
canCreateTopic && category.get("permission") === PermissionType.FULL;
|
||||
|
||||
this.controllerFor("navigation/category").setProperties({
|
||||
canCreateTopicOnCategory: canCreateTopicOnCategory,
|
||||
@@ -162,7 +161,7 @@ export default (filterArg, params) => {
|
||||
category,
|
||||
period:
|
||||
topics.get("for_period") ||
|
||||
(filter.indexOf("/") > 0 ? filter.split("/")[1] : ""),
|
||||
(model.modelParams && model.modelParams.period),
|
||||
selected: [],
|
||||
noSubcategories: params && !!params.no_subcategories,
|
||||
expandAllPinned: true,
|
||||
|
||||
@@ -130,9 +130,7 @@ export default function (filter, extras) {
|
||||
const topicOpts = {
|
||||
model,
|
||||
category: null,
|
||||
period:
|
||||
model.get("for_period") ||
|
||||
(filter.indexOf("top/") >= 0 ? filter.split("/")[1] : ""),
|
||||
period: model.get("for_period") || model.get("params.period"),
|
||||
selected: [],
|
||||
expandAllPinned: false,
|
||||
expandGloballyPinned: true,
|
||||
|
||||
@@ -7,6 +7,7 @@ import OpenComposer from "discourse/mixins/open-composer";
|
||||
import User from "discourse/models/user";
|
||||
import { scrollTop } from "discourse/mixins/scroll-top";
|
||||
import { setTopicList } from "discourse/lib/topic-list-tracker";
|
||||
import Site from "discourse/models/site";
|
||||
|
||||
export default DiscourseRoute.extend(OpenComposer, {
|
||||
queryParams: {
|
||||
@@ -19,6 +20,7 @@ export default DiscourseRoute.extend(OpenComposer, {
|
||||
|
||||
beforeModel(transition) {
|
||||
const url = transition.intent.url;
|
||||
let matches;
|
||||
if (
|
||||
(url === "/" || url === "/latest" || url === "/categories") &&
|
||||
transition.targetName.indexOf("discovery.top") === -1 &&
|
||||
@@ -26,7 +28,19 @@ export default DiscourseRoute.extend(OpenComposer, {
|
||||
) {
|
||||
User.currentProp("should_be_redirected_to_top", false);
|
||||
const period = User.currentProp("redirected_to_top.period") || "all";
|
||||
this.replaceWith(`discovery.top${period.capitalize()}`);
|
||||
this.replaceWith("discovery.top", {
|
||||
queryParams: {
|
||||
period: period,
|
||||
},
|
||||
});
|
||||
} else if (url && (matches = url.match(/top\/(.*)$/))) {
|
||||
if (Site.currentProp("periods").includes(matches[1])) {
|
||||
this.replaceWith("discovery.top", {
|
||||
queryParams: {
|
||||
period: matches[1],
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -45,21 +45,26 @@
|
||||
<div class="full-width">
|
||||
<div id="list-area">
|
||||
{{#unless loading}}
|
||||
{{#if list.topics}}
|
||||
{{#discovery-topics-list
|
||||
model=list
|
||||
refresh=(action "refresh")
|
||||
autoAddTopicsToBulkSelect=autoAddTopicsToBulkSelect
|
||||
bulkSelectEnabled=bulkSelectEnabled
|
||||
addTopicsToBulkSelect=(action "addTopicsToBulkSelect")
|
||||
as |discoveryTopicList|
|
||||
{{#discovery-topics-list
|
||||
model=list
|
||||
refresh=(action "refresh")
|
||||
autoAddTopicsToBulkSelect=autoAddTopicsToBulkSelect
|
||||
bulkSelectEnabled=bulkSelectEnabled
|
||||
addTopicsToBulkSelect=(action "addTopicsToBulkSelect")
|
||||
as |discoveryTopicList|
|
||||
}}
|
||||
{{#if top}}
|
||||
<div class="top-lists">
|
||||
{{period-chooser period=period action=(action "changePeriod") fullDay=false}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{bulk-select-button
|
||||
selected=selected
|
||||
action=(action "refresh")
|
||||
category=category
|
||||
}}
|
||||
{{bulk-select-button
|
||||
selected=selected
|
||||
action=(action "refresh")
|
||||
category=category
|
||||
}}
|
||||
|
||||
{{#if list.topics}}
|
||||
{{topic-list
|
||||
topics=list.topics
|
||||
canBulkSelect=canBulkSelect
|
||||
@@ -74,8 +79,8 @@
|
||||
onScroll=discoveryTopicList.saveScrollPosition
|
||||
scrollOnLoad=true
|
||||
}}
|
||||
{{/discovery-topics-list}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{/discovery-topics-list}}
|
||||
|
||||
<footer class="topic-list-bottom">
|
||||
{{topic-dismiss-buttons position="bottom" selectedTopics=selected
|
||||
|
||||
@@ -2,13 +2,13 @@ import {
|
||||
acceptance,
|
||||
updateCurrentUser,
|
||||
} from "discourse/tests/helpers/qunit-helpers";
|
||||
import { currentRouteName, visit } from "@ember/test-helpers";
|
||||
import { currentURL, visit } from "@ember/test-helpers";
|
||||
import DiscoveryFixtures from "discourse/tests/fixtures/discovery-fixtures";
|
||||
import { test } from "qunit";
|
||||
|
||||
acceptance("Redirect to Top", function (needs) {
|
||||
needs.pretender((server, helper) => {
|
||||
server.get("/top/weekly.json", () => {
|
||||
server.get("/top.json?period=weekly", () => {
|
||||
return helper.response(DiscoveryFixtures["/latest.json"]);
|
||||
});
|
||||
server.get("/top/monthly.json", () => {
|
||||
@@ -30,11 +30,7 @@ acceptance("Redirect to Top", function (needs) {
|
||||
});
|
||||
|
||||
await visit("/categories");
|
||||
assert.equal(
|
||||
currentRouteName(),
|
||||
"discovery.topWeekly",
|
||||
"it works for categories"
|
||||
);
|
||||
assert.equal(currentURL(), "/top?period=weekly", "it works for categories");
|
||||
});
|
||||
|
||||
test("redirects latest to monthly top", async function (assert) {
|
||||
@@ -47,11 +43,7 @@ acceptance("Redirect to Top", function (needs) {
|
||||
});
|
||||
|
||||
await visit("/latest");
|
||||
assert.equal(
|
||||
currentRouteName(),
|
||||
"discovery.topMonthly",
|
||||
"it works for latest"
|
||||
);
|
||||
assert.equal(currentURL(), "/top?period=monthly", "it works for latest");
|
||||
});
|
||||
|
||||
test("redirects root to All top", async function (assert) {
|
||||
@@ -64,6 +56,6 @@ acceptance("Redirect to Top", function (needs) {
|
||||
});
|
||||
|
||||
await visit("/");
|
||||
assert.equal(currentRouteName(), "discovery.topAll", "it works for root");
|
||||
assert.equal(currentURL(), "/top?period=all", "it works for root");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -124,7 +124,7 @@ acceptance("Topic Discovery", function (needs) {
|
||||
await periodChooser.selectRowByValue("yearly");
|
||||
|
||||
assert.ok(
|
||||
DiscourseURL.routeTo.calledWith("/top/yearly?f=foo&d=bar"),
|
||||
DiscourseURL.routeTo.calledWith("/top?f=foo&d=bar&period=yearly"),
|
||||
"it keeps the query params"
|
||||
);
|
||||
});
|
||||
|
||||
@@ -35,6 +35,7 @@ export default {
|
||||
"latest",
|
||||
"unread",
|
||||
"new",
|
||||
"top",
|
||||
"starred",
|
||||
"read",
|
||||
"posted",
|
||||
|
||||
@@ -40,6 +40,7 @@ PreloadStore.store("site", {
|
||||
"latest",
|
||||
"unread",
|
||||
"new",
|
||||
"top",
|
||||
"starred",
|
||||
"read",
|
||||
"posted",
|
||||
|
||||
Reference in New Issue
Block a user