FEATURE: "Hot" replacing "Top" as default in the top menu (#28252)

This change only applies to instances that have not modified the
`top_menu` site setting.
This commit is contained in:
Penar Musaraj 2024-08-08 13:57:42 -04:00 committed by GitHub
parent 5b1d9d602f
commit 7c5e3eacda
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 88 additions and 34 deletions

View File

@ -16,6 +16,7 @@ import { AUTO_DELETE_PREFERENCES } from "discourse/models/bookmark";
import discourseComputed from "discourse-common/utils/decorators"; import discourseComputed from "discourse-common/utils/decorators";
import I18n from "discourse-i18n"; import I18n from "discourse-i18n";
// same as UserOption::HOMEPAGES
const USER_HOMES = { const USER_HOMES = {
1: "latest", 1: "latest",
2: "categories", 2: "categories",
@ -24,6 +25,7 @@ const USER_HOMES = {
5: "top", 5: "top",
6: "bookmarks", 6: "bookmarks",
7: "unseen", 7: "unseen",
8: "hot",
}; };
const TEXT_SIZES = ["smallest", "smaller", "normal", "larger", "largest"]; const TEXT_SIZES = ["smallest", "smaller", "normal", "larger", "largest"];
@ -44,10 +46,6 @@ export default Controller.extend({
this._super(...arguments); this._super(...arguments);
this.set("selectedDarkColorSchemeId", this.session.userDarkSchemeId); this.set("selectedDarkColorSchemeId", this.session.userDarkSchemeId);
if (this.siteSettings.top_menu.split("|").includes("hot")) {
USER_HOMES[8] = "hot";
}
}, },
@discourseComputed("makeThemeDefault") @discourseComputed("makeThemeDefault")
@ -213,12 +211,20 @@ export default Controller.extend({
}); });
} }
this.siteSettings.top_menu.split("|").forEach((m) => { const availableIds = this.siteSettings.top_menu.split("|");
const userHome = USER_HOMES[this.get("model.user_option.homepage_id")];
if (userHome && !availableIds.includes(userHome)) {
availableIds.push(USER_HOMES[this.homepageId]);
}
availableIds.forEach((m) => {
let id = homeValues[m]; let id = homeValues[m];
if (id) { if (id) {
result.push({ name: I18n.t(`filters.${m}.title`), value: Number(id) }); result.push({ name: I18n.t(`filters.${m}.title`), value: Number(id) });
} }
}); });
return result; return result;
}, },

View File

@ -2,19 +2,27 @@ import { click, visit } from "@ember/test-helpers";
import { test } from "qunit"; import { test } from "qunit";
import Sinon from "sinon"; import Sinon from "sinon";
import DiscourseURL from "discourse/lib/url"; import DiscourseURL from "discourse/lib/url";
import discoveryFixtures from "discourse/tests/fixtures/discovery-fixtures";
import { import {
acceptance, acceptance,
publishToMessageBus, publishToMessageBus,
} from "discourse/tests/helpers/qunit-helpers"; } from "discourse/tests/helpers/qunit-helpers";
import { cloneJSON } from "discourse-common/lib/object";
acceptance("Software update refresh", function (needs) { acceptance("Software update refresh", function (needs) {
needs.user(); needs.user();
needs.pretender((server, helper) => {
server.get("/hot.json", () => {
return helper.response(cloneJSON(discoveryFixtures["/latest.json"]));
});
});
test("Refreshes page on next navigation", async function (assert) { test("Refreshes page on next navigation", async function (assert) {
const redirectStub = Sinon.stub(DiscourseURL, "redirectTo"); const redirectStub = Sinon.stub(DiscourseURL, "redirectTo");
await visit("/"); await visit("/");
await click(".nav-item_top a"); await click(".nav-item_hot a");
assert.true( assert.true(
redirectStub.notCalled, redirectStub.notCalled,
"redirect was not triggered by default" "redirect was not triggered by default"
@ -24,16 +32,16 @@ acceptance("Software update refresh", function (needs) {
redirectStub.resetHistory(); redirectStub.resetHistory();
await visit("/"); await visit("/");
await click(".nav-item_top a"); await click(".nav-item_hot a");
assert.true( assert.true(
redirectStub.calledWith("/top"), redirectStub.calledWith("/hot"),
"redirect was triggered after asset change" "redirect was triggered after asset change"
); );
redirectStub.resetHistory(); redirectStub.resetHistory();
await visit("/"); await visit("/");
await click("#create-topic"); await click("#create-topic");
await click(".nav-item_top a"); await click(".nav-item_hot a");
assert.true( assert.true(
redirectStub.notCalled, redirectStub.notCalled,
"redirect is not triggered while composer is open" "redirect is not triggered while composer is open"
@ -42,9 +50,9 @@ acceptance("Software update refresh", function (needs) {
redirectStub.resetHistory(); redirectStub.resetHistory();
await visit("/"); await visit("/");
await click(".save-or-cancel .cancel"); await click(".save-or-cancel .cancel");
await click(".nav-item_top a"); await click(".nav-item_hot a");
assert.true( assert.true(
redirectStub.calledWith("/top"), redirectStub.calledWith("/hot"),
"redirect is triggered on next navigation after composer closed" "redirect is triggered on next navigation after composer closed"
); );
}); });

View File

@ -285,7 +285,7 @@ acceptance("Tag info", function (needs) {
[ [
"/tags/c/faq/4/planters/l/latest.json", "/tags/c/faq/4/planters/l/latest.json",
"/tags/c/feature/2/planters/l/latest.json", "/tags/c/feature/2/planters/l/latest.json",
"/tags/c/feature/2/planters/l/top.json", "/tags/c/feature/2/planters/l/hot.json",
"/tags/c/feature/2/none/planters/l/latest.json", "/tags/c/feature/2/none/planters/l/latest.json",
].forEach((url) => { ].forEach((url) => {
server.get(url, () => { server.get(url, () => {
@ -565,8 +565,8 @@ acceptance("Tag info", function (needs) {
await click(".nav-item_latest a[href]"); await click(".nav-item_latest a[href]");
assert.strictEqual(currentURL(), "/tags/c/feature/2/planters/l/latest"); assert.strictEqual(currentURL(), "/tags/c/feature/2/planters/l/latest");
await click(".nav-item_top a[href]"); await click(".nav-item_hot a[href]");
assert.strictEqual(currentURL(), "/tags/c/feature/2/planters/l/top"); assert.strictEqual(currentURL(), "/tags/c/feature/2/planters/l/hot");
}); });
test("admin can manage tags", async function (assert) { test("admin can manage tags", async function (assert) {

View File

@ -3,6 +3,7 @@ import { skip, test } from "qunit";
import { configureEyeline } from "discourse/lib/eyeline"; import { configureEyeline } from "discourse/lib/eyeline";
import { ScrollingDOMMethods } from "discourse/mixins/scrolling"; import { ScrollingDOMMethods } from "discourse/mixins/scrolling";
import discoveryFixtures from "discourse/tests/fixtures/discovery-fixtures"; import discoveryFixtures from "discourse/tests/fixtures/discovery-fixtures";
import topFixtures from "discourse/tests/fixtures/top-fixtures";
import { import {
acceptance, acceptance,
exists, exists,
@ -17,6 +18,12 @@ acceptance("Topic Discovery", function (needs) {
show_pinned_excerpt_desktop: true, show_pinned_excerpt_desktop: true,
}); });
needs.pretender((server, helper) => {
server.get("/hot.json", () => {
return helper.response(cloneJSON(topFixtures["/top.json"]));
});
});
test("Visit Discovery Pages", async function (assert) { test("Visit Discovery Pages", async function (assert) {
await visit("/"); await visit("/");
assert.ok( assert.ok(
@ -143,13 +150,13 @@ acceptance("Topic Discovery", function (needs) {
"shows the correct latest topics" "shows the correct latest topics"
); );
await click(".navigation-container a[href='/top']"); await click(".navigation-container a[href='/hot']");
assert.strictEqual(currentURL(), "/top", "switches to top"); assert.strictEqual(currentURL(), "/hot", "switches to hot");
assert.deepEqual( assert.deepEqual(
query(".topic-list-body .topic-list-item:first-of-type").dataset.topicId, query(".topic-list-body .topic-list-item:first-of-type").dataset.topicId,
"13088", "13088",
"shows the correct top topics" "shows the correct hot topics"
); );
await click(".navigation-container a[href='/categories']"); await click(".navigation-container a[href='/categories']");

View File

@ -94,8 +94,8 @@ acceptance("Topic Discovery Tracked", function (needs) {
); );
assert.ok( assert.ok(
query("#navigation-bar li.top a").href.endsWith("/top?f=tracked"), query("#navigation-bar li.hot a").href.endsWith("/hot?f=tracked"),
"top link has tracked filter" "hot link has tracked filter"
); );
assert.ok( assert.ok(

View File

@ -41,6 +41,7 @@ export default {
"posted", "posted",
"search", "search",
"bookmarks", "bookmarks",
"hot",
], ],
periods: ["all", "yearly", "quarterly", "monthly", "weekly", "daily"], periods: ["all", "yearly", "quarterly", "monthly", "weekly", "daily"],
top_menu_items: [ top_menu_items: [
@ -51,10 +52,10 @@ export default {
"read", "read",
"posted", "posted",
"categories", "categories",
"top", "hot",
"bookmarks", "bookmarks",
], ],
anonymous_top_menu_items: ["latest", "top", "categories"], anonymous_top_menu_items: ["latest", "hot", "categories"],
uncategorized_category_id: 17, uncategorized_category_id: 17,
is_readonly: false, is_readonly: false,
categories: [ categories: [

View File

@ -54,9 +54,9 @@ PreloadStore.store("site", {
"read", "read",
"posted", "posted",
"categories", "categories",
"top", "hot",
], ],
anonymous_top_menu_items: ["latest", "categories", "top"], anonymous_top_menu_items: ["latest", "categories", "hot"],
uncategorized_category_id: 17, uncategorized_category_id: 17,
categories: [ categories: [
{ {

View File

@ -10,7 +10,7 @@ class UserOption < ActiveRecord::Base
5 => "top", 5 => "top",
6 => "bookmarks", 6 => "bookmarks",
7 => "unseen", 7 => "unseen",
# 8 => reserved for "hot" 8 => "hot",
} }
self.ignored_columns = [ self.ignored_columns = [

View File

@ -1881,7 +1881,7 @@ en:
max_reply_history: "Maximum number of replies to expand when expanding in-reply-to" max_reply_history: "Maximum number of replies to expand when expanding in-reply-to"
topics_per_period_in_top_summary: "Number of top topics shown in the default top topics summary." topics_per_period_in_top_summary: "Number of top topics shown in the default top topics summary."
topics_per_period_in_top_page: "Number of top topics shown on the expanded 'Show More' top topics." topics_per_period_in_top_page: "Number of top topics shown on the expanded 'Show More' top topics."
redirect_users_to_top_page: "Automatically redirect new and long absent users to the top page." redirect_users_to_top_page: "Automatically redirect new and long absent users to the top page. Only applies when 'top' is present in the 'top menu' site setting."
top_page_default_timeframe: "Default top page time period for anonymous users (automatically adjusts for logged in users based on their last visit)." top_page_default_timeframe: "Default top page time period for anonymous users (automatically adjusts for logged in users based on their last visit)."
moderators_view_emails: "Allow moderators to view user email addresses." moderators_view_emails: "Allow moderators to view user email addresses."
prioritize_username_in_ux: "Show username first on user page, user card and posts (when disabled name is shown first)" prioritize_username_in_ux: "Show username first on user page, user card and posts (when disabled name is shown first)"

View File

@ -190,7 +190,7 @@ basic:
refresh: true refresh: true
type: list type: list
list_type: simple list_type: simple
default: "latest|new|unread|top|categories" default: "latest|new|unread|hot|categories"
regex: "latest" regex: "latest"
regex_error: "site_settings.errors.must_include_latest" regex_error: "site_settings.errors.must_include_latest"
validator: RegexPresenceValidator validator: RegexPresenceValidator

View File

@ -29,7 +29,7 @@ describe "Homepage", type: :system do
homepage_picker = PageObjects::Components::SelectKit.new("#home-selector") homepage_picker = PageObjects::Components::SelectKit.new("#home-selector")
homepage_picker.expand homepage_picker.expand
homepage_picker.select_row_by_name("Top") homepage_picker.select_row_by_name("Hot")
page.find(".btn-primary.save-changes").click page.find(".btn-primary.save-changes").click
# Wait for the save to complete # Wait for the save to complete
@ -37,8 +37,7 @@ describe "Homepage", type: :system do
visit "/" visit "/"
expect(page).to have_css(".navigation-container .top.active", text: "Top") expect(page).to have_css(".navigation-container .hot.active", text: "Hot")
expect(page).to have_css(".top-lists")
end end
it "defaults to first top_menu item as anonymous homepage" do it "defaults to first top_menu item as anonymous homepage" do
@ -86,16 +85,15 @@ describe "Homepage", type: :system do
homepage_picker = PageObjects::Components::SelectKit.new("#home-selector") homepage_picker = PageObjects::Components::SelectKit.new("#home-selector")
homepage_picker.expand homepage_picker.expand
# user overrides theme custom homepage # user overrides theme custom homepage
homepage_picker.select_row_by_name("Top") homepage_picker.select_row_by_name("Hot")
page.find(".btn-primary.save-changes").click page.find(".btn-primary.save-changes").click
# Wait for the save to complete # Wait for the save to complete
find(".btn-primary.save-changes:not([disabled])", wait: 5) find(".btn-primary.save-changes:not([disabled])", wait: 5)
expect(user.user_option.homepage_id).to eq(UserOption::HOMEPAGES.key("top")) expect(user.user_option.homepage_id).to eq(UserOption::HOMEPAGES.key("hot"))
click_logo click_logo
expect(page).to have_css(".navigation-container .top.active", text: "Top") expect(page).to have_css(".navigation-container .hot.active", text: "Hot")
expect(page).to have_css(".top-lists")
visit "/u/#{user.username}/preferences/interface" visit "/u/#{user.username}/preferences/interface"
@ -107,7 +105,7 @@ describe "Homepage", type: :system do
# Wait for the save to complete # Wait for the save to complete
find(".btn-primary.save-changes:not([disabled])", wait: 5) find(".btn-primary.save-changes:not([disabled])", wait: 5)
expect(user.reload.user_option.homepage_id).to_not eq(UserOption::HOMEPAGES.key("top")) expect(user.reload.user_option.homepage_id).to_not eq(UserOption::HOMEPAGES.key("hot"))
click_logo click_logo

View File

@ -70,6 +70,10 @@ module PageObjects
component.find(".select-kit-collection li[data-value='#{value}']") component.find(".select-kit-collection li[data-value='#{value}']")
end end
def has_no_option_value?(value)
component.has_no_css?(".select-kit-collection li[data-value='#{value}']")
end
def expand def expand
collapsed_component.find(":not(.is-expanded) .select-kit-header", visible: :all).click collapsed_component.find(":not(.is-expanded) .select-kit-header", visible: :all).click
expanded_component expanded_component

View File

@ -31,4 +31,34 @@ describe "User preferences | Interface", type: :system do
end end
end end
end end
describe "Default Home Page" do
context "when a user has picked a home page that is no longer available in top_menu" do
it "shows the selected homepage" do
SiteSetting.top_menu = "latest|hot"
user.user_option.update!(homepage_id: UserOption::HOMEPAGES.key("unread"))
user_preferences_page.visit(user)
click_link(I18n.t("js.user.preferences_nav.interface"))
dropdown = PageObjects::Components::SelectKit.new("#home-selector")
expect(dropdown).to have_selected_name("Unread")
end
end
it "shows only the available home pages from top_menu" do
SiteSetting.top_menu = "latest|hot"
user_preferences_page.visit(user)
click_link(I18n.t("js.user.preferences_nav.interface"))
dropdown = PageObjects::Components::SelectKit.new("#home-selector")
dropdown.expand
expect(dropdown).to have_option_value(UserOption::HOMEPAGES.key("latest"))
expect(dropdown).to have_option_value(UserOption::HOMEPAGES.key("hot"))
expect(dropdown).to have_no_option_value(UserOption::HOMEPAGES.key("top"))
expect(dropdown).to have_no_option_value(UserOption::HOMEPAGES.key("new"))
end
end
end end