UX: Topic recommendations tweaks. (#22880)

This PR updates how we display related and suggested topics on mobile and desktop. It adds a new `PluginOutlet` specifically designed for adding new topic lists, which automatically work if following the same conventions as the ones inside `<MoreTopics />`.

While we display lists side by side on desktop, we only display one in mobile. You can switch to another one by clicking on the nav pills, and we'll automatically save your preference for next time.
This commit is contained in:
Roman Rizzi 2023-07-31 18:33:21 -03:00 committed by GitHub
parent 318bdbdb46
commit e7fb4be23e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 511 additions and 349 deletions

View File

@ -0,0 +1,40 @@
<div
class="more-content-wrapper
{{if this.showTitleOnMobile 'mobile-single-list'}}"
{{did-insert this.buildListPills}}
>
{{#if this.showTopicListsNav}}
<div class="row">
<ul class="nav nav-pills" {{did-insert this.buildListPills}}>
{{#each this.availablePills as |pill|}}
<li>
<DButton
@translatedTitle={{pill.name}}
@translatedLabel={{pill.name}}
@class={{if pill.selected "active"}}
@action={{action "rememberTopicListPreference" pill.id}}
/>
</li>
{{/each}}
</ul>
</div>
{{/if}}
{{#if @topic.relatedMessages.length}}
<RelatedMessages @topic={{@topic}} />
{{/if}}
{{#if @topic.suggestedTopics.length}}
<SuggestedTopics @topic={{@topic}} />
<span>
<PluginOutlet
@name="below-suggested-topics"
@connectorTagName="div"
@outletArgs={{hash topic=@topic}}
/>
</span>
{{/if}}
<PluginOutlet @name="topic-more-content" @outletArgs={{hash model=@topic}} />
</div>

View File

@ -0,0 +1,64 @@
import Component from "@glimmer/component";
import { inject as service } from "@ember/service";
import { next } from "@ember/runloop";
import { action } from "@ember/object";
import { tracked } from "@glimmer/tracking";
export default class MoreTopics extends Component {
@service site;
@service moreTopicsPreferenceTracking;
@tracked availablePills = [];
@tracked singleList = false;
get showTopicListsNav() {
return this.site.mobileView && !this.singleList;
}
get showTitleOnMobile() {
return this.site.mobileView && this.singleList;
}
@action
rememberTopicListPreference(value) {
this.moreTopicsPreferenceTracking.updatePreference(value);
this.buildListPills();
}
@action
buildListPills() {
if (!this.site.mobileView) {
return;
}
next(() => {
const pills = Array.from(
document.querySelectorAll(".more-content-topics")
).map((topicList) => {
return {
name: topicList.dataset.mobileTitle,
id: topicList.dataset.listId,
};
});
if (pills.length <= 1) {
this.singleList = true;
return;
}
let preference = this.moreTopicsPreferenceTracking.preference;
if (!preference) {
this.moreTopicsPreferenceTracking.updatePreference(pills[0].id);
preference = pills[0].id;
}
pills.forEach((pill) => {
pill.selected = pill.id === preference;
});
this.availablePills = pills;
});
}
}

View File

@ -1,10 +1,12 @@
<div <div
id="related-messages" id="related-messages"
class="suggested-topics" class="more-content-topics {{if this.hidden 'hidden'}}"
role="complementary" role="complementary"
aria-labelledby="related-messages-title" aria-labelledby="related-messages-title"
data-mobile-title={{i18n "related_messages.pill"}}
data-list-id={{this.listId}}
> >
<h3 class="suggested-topics-title" id="related-messages-title"> <h3 id="related-messages-title" class="more-topics-title">
{{i18n "related_messages.title"}} {{i18n "related_messages.title"}}
</h3> </h3>

View File

@ -1,9 +1,17 @@
import Component from "@ember/component"; import Component from "@ember/component";
import discourseComputed from "discourse-common/utils/decorators"; import discourseComputed from "discourse-common/utils/decorators";
import getURL from "discourse-common/lib/get-url"; import getURL from "discourse-common/lib/get-url";
import { inject as service } from "@ember/service";
export default Component.extend({ export default Component.extend({
tagName: "", tagName: "",
moreTopicsPreferenceTracking: service(),
listId: "related-Messages",
@discourseComputed("moreTopicsPreferenceTracking.preference")
hidden(preference) {
return this.site.mobileView && preference !== this.listId;
},
@discourseComputed("topic") @discourseComputed("topic")
targetUser(topic) { targetUser(topic) {

View File

@ -1,12 +1,14 @@
<div <div
id="suggested-topics" id="suggested-topics"
class="suggested-topics" class="more-content-topics {{if this.hidden 'hidden'}}"
role="complementary" role="complementary"
aria-labelledby="suggested-topics-title" aria-labelledby="suggested-topics-title"
data-mobile-title={{i18n "suggested_topics.pill"}}
data-list-id={{this.listId}}
> >
<UserTip @id="suggested_topics" @selector=".user-tip-reference" /> <UserTip @id="suggested_topics" @selector=".user-tip-reference" />
<h3 id="suggested-topics-title" class="suggested-topics-title"> <h3 id="suggested-topics-title" class="more-topics-title">
{{i18n this.suggestedTitleLabel}} {{i18n this.suggestedTitleLabel}}
</h3> </h3>
@ -25,12 +27,4 @@
<h3 class="suggested-topics-message"> <h3 class="suggested-topics-message">
{{html-safe this.browseMoreMessage}} {{html-safe this.browseMoreMessage}}
</h3> </h3>
</div> </div>
<span>
<PluginOutlet
@name="below-suggested-topics"
@connectorTagName="div"
@outletArgs={{hash topic=this.topic}}
/>
</span>

View File

@ -5,9 +5,12 @@ import { categoryBadgeHTML } from "discourse/helpers/category-link";
import discourseComputed from "discourse-common/utils/decorators"; import discourseComputed from "discourse-common/utils/decorators";
import getURL from "discourse-common/lib/get-url"; import getURL from "discourse-common/lib/get-url";
import { iconHTML } from "discourse-common/lib/icon-library"; import { iconHTML } from "discourse-common/lib/icon-library";
import { inject as service } from "@ember/service";
export default Component.extend({ export default Component.extend({
tagName: "", tagName: "",
moreTopicsPreferenceTracking: service(),
listId: "suggested-topics",
suggestedTitleLabel: computed("topic", function () { suggestedTitleLabel: computed("topic", function () {
const href = this.currentUser && this.currentUser.pmPath(this.topic); const href = this.currentUser && this.currentUser.pmPath(this.topic);
@ -18,6 +21,11 @@ export default Component.extend({
} }
}), }),
@discourseComputed("moreTopicsPreferenceTracking.preference")
hidden(preference) {
return this.site.mobileView && preference !== this.listId;
},
@discourseComputed( @discourseComputed(
"topic", "topic",
"pmTopicTrackingState.isTracking", "pmTopicTrackingState.isTracking",

View File

@ -360,7 +360,7 @@ export default {
}, },
goToFirstSuggestedTopic() { goToFirstSuggestedTopic() {
const el = document.querySelector(".suggested-topics a.raw-topic-link"); const el = document.querySelector("#suggested-topics a.raw-topic-link");
if (el) { if (el) {
el.click(); el.click();
} else { } else {

View File

@ -0,0 +1,20 @@
import Service, { inject as service } from "@ember/service";
import { tracked } from "@glimmer/tracking";
const TOPIC_LIST_PREFERENCE_KEY = "more-topics-list-preference";
export default class MoreTopicsPreferenceTracking extends Service {
@service keyValueStore;
@tracked preference;
init() {
super.init(...arguments);
this.preference = this.keyValueStore.get(TOPIC_LIST_PREFERENCE_KEY);
}
updatePreference(value) {
this.keyValueStore.set({ key: TOPIC_LIST_PREFERENCE_KEY, value });
this.preference = value;
}
}

View File

@ -543,20 +543,8 @@
@outletArgs={{hash model=this.model}} @outletArgs={{hash model=this.model}}
/> />
</span> </span>
<div
class="{{if <MoreTopics @topic={{this.model}} />
this.model.relatedMessages.length
'related-messages-wrapper'
}}
{{if this.model.suggestedTopics.length 'suggested-topics-wrapper'}}"
>
{{#if this.model.relatedMessages.length}}
<RelatedMessages @topic={{this.model}} />
{{/if}}
{{#if this.model.suggestedTopics.length}}
<SuggestedTopics @topic={{this.model}} />
{{/if}}
</div>
{{/if}} {{/if}}
{{else}} {{else}}
<div class="container"> <div class="container">

View File

@ -21,7 +21,7 @@ acceptance("Personal Message", function (needs) {
await visit("/t/pm-for-testing/12"); await visit("/t/pm-for-testing/12");
assert.strictEqual( assert.strictEqual(
query("#suggested-topics .suggested-topics-title").innerText.trim(), query("#suggested-topics-title").innerText.trim(),
I18n.t("suggested_topics.pm_title") I18n.t("suggested_topics.pm_title")
); );
}); });

View File

@ -204,7 +204,7 @@ acceptance("Topic", function (needs) {
await visit("/t/internationalization-localization/280"); await visit("/t/internationalization-localization/280");
assert.strictEqual( assert.strictEqual(
query("#suggested-topics .suggested-topics-title").innerText.trim(), query("#suggested-topics-title").innerText.trim(),
I18n.t("suggested_topics.title") I18n.t("suggested_topics.title")
); );
}); });

View File

@ -83,242 +83,246 @@ acceptance(
} }
); );
acceptance( let fetchedNew;
"User Private Messages - user with group messages", let fetchUserNew;
function (needs) { let fetchedGroupNew;
let fetchedNew;
let fetchUserNew;
let fetchedGroupNew;
needs.user({ function withGroupMessagesSetup(needs) {
id: 5, needs.user({
username: "charlie", id: 5,
groups: [{ id: 14, name: "awesome_group", has_messages: true }], username: "charlie",
groups: [{ id: 14, name: "awesome_group", has_messages: true }],
});
needs.site({
can_tag_pms: true,
});
needs.hooks.afterEach(() => {
fetchedNew = false;
fetchedGroupNew = false;
fetchUserNew = false;
});
needs.pretender((server, helper) => {
server.get("/tags/personal_messages/:username.json", () => {
return helper.response({ tags: [{ id: "tag1" }] });
}); });
needs.site({ server.get("/t/13.json", () => {
can_tag_pms: true, const response = cloneJSON(fixturesByUrl["/t/12/1.json"]);
response.suggested_group_name = "awesome_group";
return helper.response(response);
}); });
needs.hooks.afterEach(() => { server.get("/topics/private-messages/:username.json", () => {
fetchedNew = false; return helper.response({
fetchedGroupNew = false; topic_list: {
fetchUserNew = false; topics: [
}); {
id: 1,
needs.pretender((server, helper) => { posters: [],
server.get("/tags/personal_messages/:username.json", () => { notification_level: NotificationLevels.TRACKING,
return helper.response({ tags: [{ id: "tag1" }] }); unread_posts: 1,
last_read_post_number: 1,
highest_post_number: 2,
},
{
id: 2,
posters: [],
},
{
id: 3,
posters: [],
},
],
},
}); });
});
server.get("/t/13.json", () => { [
const response = cloneJSON(fixturesByUrl["/t/12/1.json"]); "/topics/private-messages-new/:username.json",
response.suggested_group_name = "awesome_group"; "/topics/private-messages-unread/:username.json",
return helper.response(response); "/topics/private-messages-archive/:username.json",
"/topics/private-messages-group/:username/:group_name/new.json",
"/topics/private-messages-group/:username/:group_name/unread.json",
"/topics/private-messages-group/:username/:group_name/archive.json",
"/topics/private-messages-tags/:username/:tag_name",
].forEach((url) => {
server.get(url, () => {
let topics;
if (fetchedNew || fetchedGroupNew || fetchUserNew) {
topics = [];
} else {
topics = [
{ id: 1, posters: [] },
{ id: 2, posters: [] },
{ id: 3, posters: [] },
];
}
return helper.response({
topic_list: {
topics,
},
});
}); });
});
server.get("/topics/private-messages/:username.json", () => { server.get(
"/topics/private-messages-group/:username/:group_name.json",
() => {
return helper.response({ return helper.response({
topic_list: { topic_list: {
topics: [ topics: [
{ { id: 1, posters: [] },
id: 1, { id: 2, posters: [] },
posters: [],
notification_level: NotificationLevels.TRACKING,
unread_posts: 1,
last_read_post_number: 1,
highest_post_number: 2,
},
{
id: 2,
posters: [],
},
{
id: 3,
posters: [],
},
], ],
}, },
}); });
}); }
);
[ server.put("/topics/pm-reset-new", (request) => {
"/topics/private-messages-new/:username.json", const requestBody = request.requestBody;
"/topics/private-messages-unread/:username.json", // No easy way to do this https://github.com/pretenderjs/pretender/issues/159
"/topics/private-messages-archive/:username.json", if (requestBody === "inbox=group&group_name=awesome_group") {
"/topics/private-messages-group/:username/:group_name/new.json", fetchedGroupNew = true;
"/topics/private-messages-group/:username/:group_name/unread.json", }
"/topics/private-messages-group/:username/:group_name/archive.json",
"/topics/private-messages-tags/:username/:tag_name",
].forEach((url) => {
server.get(url, () => {
let topics;
if (fetchedNew || fetchedGroupNew || fetchUserNew) { if (requestBody === "inbox=user") {
topics = []; fetchUserNew = true;
} else { }
topics = [
{ id: 1, posters: [] },
{ id: 2, posters: [] },
{ id: 3, posters: [] },
];
}
return helper.response({ if (requestBody === "inbox=all") {
topic_list: { fetchedNew = true;
topics, }
},
});
});
});
server.get( return helper.response({ topic_ids: [1, 2, 3] });
"/topics/private-messages-group/:username/:group_name.json",
() => {
return helper.response({
topic_list: {
topics: [
{ id: 1, posters: [] },
{ id: 2, posters: [] },
],
},
});
}
);
server.put("/topics/pm-reset-new", (request) => {
const requestBody = request.requestBody;
// No easy way to do this https://github.com/pretenderjs/pretender/issues/159
if (requestBody === "inbox=group&group_name=awesome_group") {
fetchedGroupNew = true;
}
if (requestBody === "inbox=user") {
fetchUserNew = true;
}
if (requestBody === "inbox=all") {
fetchedNew = true;
}
return helper.response({ topic_ids: [1, 2, 3] });
});
server.put("/topics/bulk", (request) => {
const requestBody = request.requestBody;
if (requestBody.includes("private_message_inbox=all")) {
fetchedNew = true;
}
if (
requestBody.includes(
"private_message_inbox=group&group_name=awesome_group"
)
) {
fetchedGroupNew = true;
}
if (requestBody.includes("private_message_inbox=user")) {
fetchUserNew = true;
}
return helper.response({
topic_ids: [1, 2, 3],
});
});
}); });
const publishReadToMessageBus = function (opts = {}) { server.put("/topics/bulk", (request) => {
return publishToMessageBus( const requestBody = request.requestBody;
`/private-message-topic-tracking-state/user/${opts.userId || 5}`,
{
topic_id: opts.topicId,
message_type: "read",
payload: {
last_read_post_number: 2,
highest_post_number: 2,
notification_level: 2,
},
}
);
};
const publishUnreadToMessageBus = function (opts = {}) { if (requestBody.includes("private_message_inbox=all")) {
return publishToMessageBus( fetchedNew = true;
`/private-message-topic-tracking-state/user/${opts.userId || 5}`, }
{
topic_id: opts.topicId,
message_type: "unread",
payload: {
last_read_post_number: 1,
highest_post_number: 2,
notification_level: 2,
group_ids: opts.groupIds || [],
},
}
);
};
const publishNewToMessageBus = function (opts = {}) { if (
return publishToMessageBus( requestBody.includes(
`/private-message-topic-tracking-state/user/${opts.userId || 5}`, "private_message_inbox=group&group_name=awesome_group"
{ )
topic_id: opts.topicId, ) {
message_type: "new_topic", fetchedGroupNew = true;
payload: { }
last_read_post_number: null,
highest_post_number: 1,
group_ids: opts.groupIds || [],
},
}
);
};
const publishGroupArchiveToMessageBus = function (opts) { if (requestBody.includes("private_message_inbox=user")) {
return publishToMessageBus( fetchUserNew = true;
`/private-message-topic-tracking-state/group/${opts.groupIds[0]}`, }
{
topic_id: opts.topicId,
message_type: "group_archive",
payload: {
group_ids: opts.groupIds,
acting_user_id: opts.actingUserId,
},
}
);
};
const publishGroupUnreadToMessageBus = function (opts) { return helper.response({
return publishToMessageBus( topic_ids: [1, 2, 3],
`/private-message-topic-tracking-state/group/${opts.groupIds[0]}`, });
{ });
topic_id: opts.topicId, });
message_type: "unread", }
payload: {
last_read_post_number: 1,
highest_post_number: 2,
notification_level: 2,
group_ids: opts.groupIds || [],
},
}
);
};
const publishGroupNewToMessageBus = function (opts) { const publishReadToMessageBus = function (opts = {}) {
return publishToMessageBus( return publishToMessageBus(
`/private-message-topic-tracking-state/group/${opts.groupIds[0]}`, `/private-message-topic-tracking-state/user/${opts.userId || 5}`,
{ {
topic_id: opts.topicId, topic_id: opts.topicId,
message_type: "new_topic", message_type: "read",
payload: { payload: {
last_read_post_number: null, last_read_post_number: 2,
highest_post_number: 1, highest_post_number: 2,
group_ids: opts.groupIds || [], notification_level: 2,
}, },
} }
); );
}; };
const publishUnreadToMessageBus = function (opts = {}) {
return publishToMessageBus(
`/private-message-topic-tracking-state/user/${opts.userId || 5}`,
{
topic_id: opts.topicId,
message_type: "unread",
payload: {
last_read_post_number: 1,
highest_post_number: 2,
notification_level: 2,
group_ids: opts.groupIds || [],
},
}
);
};
const publishNewToMessageBus = function (opts = {}) {
return publishToMessageBus(
`/private-message-topic-tracking-state/user/${opts.userId || 5}`,
{
topic_id: opts.topicId,
message_type: "new_topic",
payload: {
last_read_post_number: null,
highest_post_number: 1,
group_ids: opts.groupIds || [],
},
}
);
};
const publishGroupArchiveToMessageBus = function (opts) {
return publishToMessageBus(
`/private-message-topic-tracking-state/group/${opts.groupIds[0]}`,
{
topic_id: opts.topicId,
message_type: "group_archive",
payload: {
group_ids: opts.groupIds,
acting_user_id: opts.actingUserId,
},
}
);
};
const publishGroupUnreadToMessageBus = function (opts) {
return publishToMessageBus(
`/private-message-topic-tracking-state/group/${opts.groupIds[0]}`,
{
topic_id: opts.topicId,
message_type: "unread",
payload: {
last_read_post_number: 1,
highest_post_number: 2,
notification_level: 2,
group_ids: opts.groupIds || [],
},
}
);
};
const publishGroupNewToMessageBus = function (opts) {
return publishToMessageBus(
`/private-message-topic-tracking-state/group/${opts.groupIds[0]}`,
{
topic_id: opts.topicId,
message_type: "new_topic",
payload: {
last_read_post_number: null,
highest_post_number: 1,
group_ids: opts.groupIds || [],
},
}
);
};
acceptance(
"User Private Messages - user with group messages",
function (needs) {
withGroupMessagesSetup(needs);
test("incoming group archive message acted by current user", async function (assert) { test("incoming group archive message acted by current user", async function (assert) {
await visit("/u/charlie/messages"); await visit("/u/charlie/messages");
@ -639,83 +643,6 @@ acceptance(
); );
}); });
test("suggested messages without new or unread", async function (assert) {
await visit("/t/12");
assert.strictEqual(
query(".suggested-topics-message").innerText.trim(),
"Want to read more? Browse other messages in personal messages.",
"displays the right browse more message"
);
});
test("suggested messages with new and unread", async function (assert) {
await visit("/t/12");
await publishNewToMessageBus({ userId: 5, topicId: 1 });
assert.strictEqual(
query(".suggested-topics-message").innerText.trim(),
"There is 1 new message remaining, or browse other personal messages",
"displays the right browse more message"
);
await publishUnreadToMessageBus({ userId: 5, topicId: 2 });
assert.strictEqual(
query(".suggested-topics-message").innerText.trim(),
"There is 1 unread and 1 new message remaining, or browse other personal messages",
"displays the right browse more message"
);
await publishReadToMessageBus({ userId: 5, topicId: 2 });
assert.strictEqual(
query(".suggested-topics-message").innerText.trim(),
"There is 1 new message remaining, or browse other personal messages",
"displays the right browse more message"
);
});
test("suggested messages for group messages without new or unread", async function (assert) {
await visit("/t/13");
assert.ok(
query(".suggested-topics-message")
.innerText.trim()
.match(
/Want to read more\? Browse other messages in\s+awesome_group\./
),
"displays the right browse more message"
);
});
test("suggested messages for group messages with new and unread", async function (assert) {
await visit("/t/13");
await publishGroupNewToMessageBus({ groupIds: [14], topicId: 1 });
assert.ok(
query(".suggested-topics-message")
.innerText.trim()
.match(
/There is 1 new message remaining, or browse other messages in\s+awesome_group/
),
"displays the right browse more message"
);
await publishGroupUnreadToMessageBus({ groupIds: [14], topicId: 2 });
assert.ok(
query(".suggested-topics-message")
.innerText.trim()
.match(
/There is 1 unread and 1 new message remaining, or browse other messages in\s+awesome_group/
),
"displays the right browse more message"
);
});
test("navigating between user messages route with dropdown", async function (assert) { test("navigating between user messages route with dropdown", async function (assert) {
await visit("/u/Charlie/messages"); await visit("/u/Charlie/messages");
@ -808,6 +735,95 @@ acceptance(
} }
); );
acceptance(
"User Private Messages - user with group messages - Mobile",
function (needs) {
withGroupMessagesSetup(needs);
needs.mobileView();
test("suggested messages without new or unread", async function (assert) {
await visit("/t/12");
assert.strictEqual(
query(".suggested-topics-message").innerText.trim(),
"Want to read more? Browse other messages in personal messages.",
"displays the right browse more message"
);
});
test("suggested messages with new and unread", async function (assert) {
await visit("/t/12");
await publishNewToMessageBus({ userId: 5, topicId: 1 });
assert.strictEqual(
query(".suggested-topics-message").innerText.trim(),
"There is 1 new message remaining, or browse other personal messages",
"displays the right browse more message"
);
await publishUnreadToMessageBus({ userId: 5, topicId: 2 });
assert.ok(
query(".suggested-topics-message")
.innerText.trim()
.match(
/There is 1 unread\s+ and 1 new message remaining, or browse other personal messages/
),
"displays the right browse more message"
);
await publishReadToMessageBus({ userId: 5, topicId: 2 });
assert.strictEqual(
query(".suggested-topics-message").innerText.trim(),
"There is 1 new message remaining, or browse other personal messages",
"displays the right browse more message"
);
});
test("suggested messages for group messages without new or unread", async function (assert) {
await visit("/t/13");
assert.ok(
query(".suggested-topics-message")
.innerText.trim()
.match(
/Want to read more\? Browse other messages in\s+awesome_group\./
),
"displays the right browse more message"
);
});
test("suggested messages for group messages with new and unread", async function (assert) {
needs.mobileView();
await visit("/t/13");
await publishGroupNewToMessageBus({ groupIds: [14], topicId: 1 });
assert.ok(
query(".suggested-topics-message")
.innerText.trim()
.match(
/There is 1 new message remaining, or browse other messages in\s+awesome_group/
),
"displays the right browse more message"
);
await publishGroupUnreadToMessageBus({ groupIds: [14], topicId: 2 });
assert.ok(
query(".suggested-topics-message")
.innerText.trim()
.match(
/There is 1 unread\s+ and 1 new message remaining, or browse other messages in\s+awesome_group/
),
"displays the right browse more message"
);
});
}
);
acceptance("User Private Messages - user with no messages", function (needs) { acceptance("User Private Messages - user with no messages", function (needs) {
needs.user(); needs.user();

View File

@ -368,42 +368,43 @@ a.badge-category {
max-width: 757px; max-width: 757px;
} }
.suggested-topics-wrapper.related-messages-wrapper { .more-content-wrapper .topic-list-body .topic-list-data:first-of-type {
.suggested-topics:nth-of-type(n + 2) { padding-left: 0;
thead {
display: none;
}
}
} }
// Target the .badge-category text, the bullet icon needs to maintain `display: block` // Target the .badge-category text, the bullet icon needs to maintain `display: block`
.suggested-topics h3 .badge-wrapper.bullet span.badge-category, .more-content-topics h3 .badge-wrapper.bullet span.badge-category,
.suggested-topics h3 .badge-wrapper.box span, .more-content-topics h3 .badge-wrapper.box span,
.suggested-topics h3 .badge-wrapper.bar span { .more-content-topics h3 .badge-wrapper.bar span {
display: inline; display: inline;
} }
.suggested-topics h3 .badge-wrapper.bullet span.badge-category { .more-content-topicss h3 .badge-wrapper.bullet span.badge-category {
// Override vertical-align: text-top from `badges.css.scss` // Override vertical-align: text-top from `badges.css.scss`
vertical-align: baseline; vertical-align: baseline;
line-height: var(--line-height-medium); line-height: var(--line-height-medium);
} }
.suggested-topics h3 .badge-wrapper.bullet, .more-content-topics h3 .badge-wrapper.bullet,
.suggested-topics h3 .badge-wrapper.bullet span.badge-category-parent-bg, .more-content-topics h3 .badge-wrapper.bullet span.badge-category-parent-bg,
.suggested-topics h3 .badge-wrapper.bullet span.badge-category-bg { .more-content-topics h3 .badge-wrapper.bullet span.badge-category-bg {
// Top of bullet aligns with top of line - adjust line height to vertically align bullet. // Top of bullet aligns with top of line - adjust line height to vertically align bullet.
line-height: 0.8; line-height: 0.8;
} }
.suggested-topics .badge-wrapper.bullet span.badge-category, .more-content-topics .badge-wrapper.bullet span.badge-category,
.suggested-topics .badge-wrapper.bar span.badge-category { .more-content-topics .badge-wrapper.bar span.badge-category {
max-width: 150px; max-width: 150px;
} }
.suggested-topics .suggested-topics-title { .more-content-topics {
display: flex; .topic-list-body {
align-items: center; border-top: none;
.topic-list-item:last-of-type {
border-bottom: none;
}
}
} }
.post-links-container { .post-links-container {

View File

@ -362,11 +362,11 @@ pre.codeblock-buttons:hover {
} }
} }
.suggested-topics { .more-content-topics {
margin: 4.5em 0 1em; margin-top: 2em;
table { .suggested-topics-message {
margin-top: 10px; display: none;
} }
} }

View File

@ -135,3 +135,21 @@
max-width: 100%; max-width: 100%;
} }
} }
.more-content-wrapper {
display: flex;
.topic-list-header,
.posts-map,
.views {
display: none;
}
.topic-list-body {
border-top: none;
.topic-list-item:last-of-type {
border-bottom: none;
}
}
}

View File

@ -252,17 +252,18 @@ a.reply-to-tab {
} }
} }
.suggested-topics { .more-content-wrapper {
&:not(.mobile-single-list) {
.more-topics-title {
display: none;
}
}
}
.more-content-topics {
clear: left; clear: left;
padding: 20px 0 15px 0; padding: 20px 0 15px 0;
th.views,
td.views,
td.activity,
th.activity,
th.likes,
td.likes {
display: none;
}
a.badge-category, a.badge-category,
a.badge-category-parent { a.badge-category-parent {
font-size: var(--font-down-1); font-size: var(--font-down-1);

View File

@ -301,10 +301,12 @@ en:
related_messages: related_messages:
title: "Related Messages" title: "Related Messages"
pill: "Related Messages"
see_all: 'See <a href="%{path}">all messages</a> from @%{username}...' see_all: 'See <a href="%{path}">all messages</a> from @%{username}...'
suggested_topics: suggested_topics:
title: "Suggested Topics" title: "New & Unread Topics"
pill: "Suggested"
pm_title: "Suggested Messages" pm_title: "Suggested Messages"
about: about: