mirror of
https://github.com/discourse/discourse.git
synced 2025-01-18 16:42:46 +08:00
DEV: First pass at side topics section (#16697)
* Implements everything, tracked and bookmarked links * Implements unread/new count for everything link
This commit is contained in:
parent
19677ce3f6
commit
9b420eb6e3
|
@ -0,0 +1,51 @@
|
|||
import Ember from "ember";
|
||||
|
||||
export default class SidebarSectionLinkTo extends Ember.LinkComponent {
|
||||
// Overriding the private function here because the behavior of the component when used with the `current-when`
|
||||
// attribute does not seem to follow what was mentioned in the docs: "A link will be active if current-when is true or
|
||||
// the current route is the route this link would transition to". When the `current-when` attribute is used, the
|
||||
// `route` and `query` attributes are ignored which is not what we want. In addition, we're stuck on Ember 3.15 at
|
||||
// the moment and are awaiting the upgrade to the latest supported Ember version before I can determine if this is a
|
||||
// bug and report it as such.
|
||||
_isActive(routerState) {
|
||||
if (this.loading) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let currentWhen = this["current-when"];
|
||||
|
||||
if (typeof currentWhen === "boolean") {
|
||||
return currentWhen;
|
||||
}
|
||||
|
||||
let isCurrentWhenSpecified = Boolean(currentWhen);
|
||||
|
||||
if (isCurrentWhenSpecified) {
|
||||
currentWhen = currentWhen.split(" ");
|
||||
} else {
|
||||
currentWhen = [this._route];
|
||||
}
|
||||
|
||||
let { _models: models, _query: query, _routing: routing } = this;
|
||||
|
||||
for (let i = 0; i < currentWhen.length; i++) {
|
||||
if (
|
||||
routing.isActiveForRoute(
|
||||
models,
|
||||
query,
|
||||
currentWhen[i],
|
||||
routerState,
|
||||
// **custom code override start**
|
||||
// we always want query params to be considered
|
||||
false
|
||||
// isCurrentWhenSpecified
|
||||
// **custom code override end**
|
||||
)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
import GlimmerComponent from "discourse/components/glimmer";
|
||||
|
||||
import { action } from "@ember/object";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
|
||||
export default class SidebarSection extends GlimmerComponent {
|
||||
@tracked displaySection = true;
|
||||
|
||||
@action
|
||||
toggleSectionDisplay() {
|
||||
this.displaySection = !this.displaySection;
|
||||
}
|
||||
|
||||
get headerCaretIcon() {
|
||||
return this.displaySection ? "angle-down" : "angle-up";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
import I18n from "I18n";
|
||||
|
||||
import GlimmerComponent from "discourse/components/glimmer";
|
||||
import Composer from "discourse/models/composer";
|
||||
import { getOwner } from "discourse-common/lib/get-owner";
|
||||
import PermissionType from "discourse/models/permission-type";
|
||||
import discourseDebounce from "discourse-common/lib/debounce";
|
||||
|
||||
import { action } from "@ember/object";
|
||||
import { next } from "@ember/runloop";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
|
||||
export default class SidebarTopicsSection extends GlimmerComponent {
|
||||
@tracked totalUnread = 0;
|
||||
@tracked totalNew = 0;
|
||||
|
||||
constructor(owner, args) {
|
||||
super(owner, args);
|
||||
this._refreshSectionCounts();
|
||||
|
||||
this.topicTrackingState.onStateChange(
|
||||
this._topicTrackingStateUpdated.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
_topicTrackingStateUpdated() {
|
||||
// refreshing section counts by looping through the states in topicTrackingState is an expensive operation so
|
||||
// we debounce this.
|
||||
discourseDebounce(this, this._refreshSectionCounts, 100);
|
||||
}
|
||||
|
||||
_refreshSectionCounts() {
|
||||
let totalUnread = 0;
|
||||
let totalNew = 0;
|
||||
|
||||
this.topicTrackingState.forEachTracked((topic, isNew, isUnread) => {
|
||||
if (isNew) {
|
||||
totalNew += 1;
|
||||
} else if (isUnread) {
|
||||
totalUnread += 1;
|
||||
}
|
||||
});
|
||||
|
||||
this.totalUnread = totalUnread;
|
||||
this.totalNew = totalNew;
|
||||
}
|
||||
|
||||
get everythingSectionLinkBadgeText() {
|
||||
if (this.totalUnread > 0) {
|
||||
return I18n.t("sidebar.sections.links.badge.unread_count", {
|
||||
count: this.totalUnread,
|
||||
});
|
||||
} else if (this.totalNew > 0) {
|
||||
return I18n.t("sidebar.sections.links.badge.new_count", {
|
||||
count: this.totalNew,
|
||||
});
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
get everythingSectionLinkRoute() {
|
||||
if (this.totalUnread > 0) {
|
||||
return "discovery.unread";
|
||||
} else if (this.totalNew > 0) {
|
||||
return "discovery.new";
|
||||
} else {
|
||||
return "discovery.latest";
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
composeTopic() {
|
||||
const composerArgs = {
|
||||
action: Composer.CREATE_TOPIC,
|
||||
draftKey: Composer.NEW_TOPIC_KEY,
|
||||
};
|
||||
|
||||
const controller = getOwner(this).lookup("controller:navigation/category");
|
||||
const category = controller.category;
|
||||
|
||||
if (category && category.permission === PermissionType.FULL) {
|
||||
composerArgs.categoryId = category.id;
|
||||
}
|
||||
|
||||
next(() => {
|
||||
getOwner(this).lookup("controller:composer").open(composerArgs);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -22,7 +22,7 @@ const controllerOpts = {
|
|||
queryParams: Object.keys(queryParams),
|
||||
};
|
||||
|
||||
// Default to `null`
|
||||
// Default to `undefined`
|
||||
controllerOpts.queryParams.forEach((p) => {
|
||||
controllerOpts[p] = queryParams[p].default;
|
||||
});
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
{{#if @shouldDisplay}}
|
||||
<div class="sidebar-wrapper">
|
||||
<div class="sidebar-container">
|
||||
<Sidebar::TopicsSection />
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
<div class="sidebar-section-link-wrapper">
|
||||
<Sidebar::SectionLinkTo
|
||||
@class={{concat "sidebar-section-link sidebar-section-link-" @linkName}}
|
||||
@route={{@route}}
|
||||
@query={{@query}}
|
||||
@models={{if @model (array @model) (if @models @models (array))}}
|
||||
@current-when={{@current-when}}
|
||||
@title={{i18n (concat "sidebar.sections.links." @linkName ".title")}}
|
||||
>
|
||||
{{i18n (concat "sidebar.sections.links." @linkName ".content")}}
|
||||
|
||||
{{#if @badgeText}}
|
||||
<span class="sidebar-section-link-content-badge">
|
||||
{{@badgeText}}
|
||||
</span>
|
||||
{{/if}}
|
||||
</Sidebar::SectionLinkTo>
|
||||
</div>
|
|
@ -0,0 +1,23 @@
|
|||
<div class={{concat "sidebar-section-wrapper sidebar-section-" @sectionName}}>
|
||||
<div class="sidebar-section-header">
|
||||
<button type="button" class="sidebar-section-header-caret" title="toggle section" {{on "click" this.toggleSectionDisplay}}>
|
||||
{{d-icon this.headerCaretIcon}}
|
||||
</button>
|
||||
|
||||
<LinkTo @route={{@headerRoute}} @query={{@headerQuery}} @class="sidebar-section-header-link">
|
||||
{{@headerTitle}}
|
||||
</LinkTo>
|
||||
|
||||
{{#if @headerAction}}
|
||||
<button type="button" class="sidebar-section-header-button" {{on "click" @headerAction}}>
|
||||
{{d-icon @headerActionIcon}}
|
||||
</button>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{#if this.displaySection}}
|
||||
<div class="sidebar-section-content">
|
||||
{{yield}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
|
@ -0,0 +1,18 @@
|
|||
<Sidebar::Section
|
||||
@sectionName="topics"
|
||||
@headerRoute="discovery.latest"
|
||||
@headerQuery={{hash f=undefined}}
|
||||
@headerTitle="topics"
|
||||
@headerActionIcon="plus"
|
||||
@headerAction={{this.composeTopic}}>
|
||||
|
||||
<Sidebar::SectionLink
|
||||
@linkName="everything"
|
||||
@route={{this.everythingSectionLinkRoute}}
|
||||
@query={{hash f=undefined}}
|
||||
@current-when={{"discovery.latest discovery.new discovery.unread discovery.top"}}
|
||||
@badgeText={{this.everythingSectionLinkBadgeText}} />
|
||||
|
||||
<Sidebar::SectionLink @linkName="tracked" @route="discovery.latest" @query={{hash f="tracked"}} />
|
||||
<Sidebar::SectionLink @linkName="bookmarked" @route="userActivity.bookmarks" @model={{this.currentUser}} />
|
||||
</Sidebar::Section>
|
|
@ -0,0 +1,401 @@
|
|||
import { click, currentURL, settled, visit } from "@ember/test-helpers";
|
||||
|
||||
import {
|
||||
acceptance,
|
||||
conditionalTest,
|
||||
exists,
|
||||
loggedInUser,
|
||||
publishToMessageBus,
|
||||
query,
|
||||
queryAll,
|
||||
} from "discourse/tests/helpers/qunit-helpers";
|
||||
import { isLegacyEmber } from "discourse-common/config/environment";
|
||||
import topicFixtures from "discourse/tests/fixtures/discovery-fixtures";
|
||||
import { cloneJSON } from "discourse-common/lib/object";
|
||||
|
||||
acceptance("Sidebar - Topics Section", function (needs) {
|
||||
needs.user({ experimental_sidebar_enabled: true });
|
||||
|
||||
needs.pretender((server, helper) => {
|
||||
server.get("/new.json", () => {
|
||||
return helper.response(cloneJSON(topicFixtures["/latest.json"]));
|
||||
});
|
||||
|
||||
server.get("/unread.json", () => {
|
||||
return helper.response(cloneJSON(topicFixtures["/latest.json"]));
|
||||
});
|
||||
|
||||
server.get("/top.json", () => {
|
||||
return helper.response(cloneJSON(topicFixtures["/latest.json"]));
|
||||
});
|
||||
});
|
||||
|
||||
conditionalTest(
|
||||
"clicking on section header button",
|
||||
!isLegacyEmber(),
|
||||
async function (assert) {
|
||||
await visit("/");
|
||||
await click(".sidebar-section-topics .sidebar-section-header-button");
|
||||
|
||||
assert.ok(exists("#reply-control"), "it opens the composer");
|
||||
}
|
||||
);
|
||||
|
||||
conditionalTest(
|
||||
"clicking on section header button while viewing a category",
|
||||
!isLegacyEmber(),
|
||||
async function (assert) {
|
||||
await visit("/c/bug");
|
||||
await click(".sidebar-section-topics .sidebar-section-header-button");
|
||||
|
||||
assert.ok(exists("#reply-control"), "it opens the composer");
|
||||
|
||||
assert.strictEqual(
|
||||
query(".category-input .selected-name .category-name").textContent,
|
||||
"bug",
|
||||
"the current category is prefilled in the composer input"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
conditionalTest(
|
||||
"clicking on section caret button",
|
||||
!isLegacyEmber(),
|
||||
async function (assert) {
|
||||
await visit("/");
|
||||
|
||||
assert.ok(exists(".sidebar-section-content"), "shows content section");
|
||||
|
||||
await click(".sidebar-section-topics .sidebar-section-header-caret");
|
||||
|
||||
assert.ok(!exists(".sidebar-section-content"), "hides content section");
|
||||
|
||||
await click(".sidebar-section-topics .sidebar-section-header-caret");
|
||||
|
||||
assert.ok(exists(".sidebar-section-content"), "shows content section");
|
||||
}
|
||||
);
|
||||
|
||||
conditionalTest(
|
||||
"clicking on section header link",
|
||||
!isLegacyEmber(),
|
||||
async function (assert) {
|
||||
await visit("/t/280");
|
||||
await click(".sidebar-section-topics .sidebar-section-header-link");
|
||||
|
||||
assert.strictEqual(
|
||||
currentURL(),
|
||||
"/latest",
|
||||
"it should transistion to the homepage"
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
queryAll(".sidebar-section-topics .sidebar-section-link.active").length,
|
||||
1,
|
||||
"only one link is marked as active"
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
exists(
|
||||
".sidebar-section-topics .sidebar-section-link-everything.active"
|
||||
),
|
||||
"the everything link is marked as active"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
conditionalTest(
|
||||
"clicking on everything link",
|
||||
!isLegacyEmber(),
|
||||
async function (assert) {
|
||||
await visit("/t/280");
|
||||
await click(".sidebar-section-topics .sidebar-section-link-everything");
|
||||
|
||||
assert.strictEqual(
|
||||
currentURL(),
|
||||
"/latest",
|
||||
"it should transistion to the latest page"
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
queryAll(".sidebar-section-topics .sidebar-section-link.active").length,
|
||||
1,
|
||||
"only one link is marked as active"
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
exists(
|
||||
".sidebar-section-topics .sidebar-section-link-everything.active"
|
||||
),
|
||||
"the everything link is marked as active"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
conditionalTest(
|
||||
"clicking on tracked link",
|
||||
!isLegacyEmber(),
|
||||
async function (assert) {
|
||||
await visit("/t/280");
|
||||
await click(".sidebar-section-topics .sidebar-section-link-tracked");
|
||||
|
||||
assert.strictEqual(
|
||||
currentURL(),
|
||||
"/latest?f=tracked",
|
||||
"it should transistion to the tracked url"
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
queryAll(".sidebar-section-topics .sidebar-section-link.active").length,
|
||||
1,
|
||||
"only one link is marked as active"
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
exists(".sidebar-section-topics .sidebar-section-link-tracked.active"),
|
||||
"the tracked link is marked as active"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
conditionalTest(
|
||||
"clicking on bookmarked link",
|
||||
!isLegacyEmber(),
|
||||
async function (assert) {
|
||||
await visit("/t/280");
|
||||
await click(".sidebar-section-topics .sidebar-section-link-bookmarked");
|
||||
|
||||
assert.strictEqual(
|
||||
currentURL(),
|
||||
`/u/${loggedInUser().username}/activity/bookmarks`,
|
||||
"it should transistion to the bookmarked url"
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
queryAll(".sidebar-section-topics .sidebar-section-link.active").length,
|
||||
1,
|
||||
"only one link is marked as active"
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
exists(
|
||||
".sidebar-section-topics .sidebar-section-link-bookmarked.active"
|
||||
),
|
||||
"the bookmarked link is marked as active"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
conditionalTest(
|
||||
"visiting top route",
|
||||
!isLegacyEmber(),
|
||||
async function (assert) {
|
||||
await visit("/top");
|
||||
|
||||
assert.strictEqual(
|
||||
queryAll(".sidebar-section-topics .sidebar-section-link.active").length,
|
||||
1,
|
||||
"only one link is marked as active"
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
exists(
|
||||
".sidebar-section-topics .sidebar-section-link-everything.active"
|
||||
),
|
||||
"the everything link is marked as active"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
conditionalTest(
|
||||
"visiting unread route",
|
||||
!isLegacyEmber(),
|
||||
async function (assert) {
|
||||
await visit("/unread");
|
||||
|
||||
assert.strictEqual(
|
||||
queryAll(".sidebar-section-topics .sidebar-section-link.active").length,
|
||||
1,
|
||||
"only one link is marked as active"
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
exists(
|
||||
".sidebar-section-topics .sidebar-section-link-everything.active"
|
||||
),
|
||||
"the everything link is marked as active"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
conditionalTest(
|
||||
"visiting new route",
|
||||
!isLegacyEmber(),
|
||||
async function (assert) {
|
||||
await visit("/new");
|
||||
|
||||
assert.strictEqual(
|
||||
queryAll(".sidebar-section-topics .sidebar-section-link.active").length,
|
||||
1,
|
||||
"only one link is marked as active"
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
exists(
|
||||
".sidebar-section-topics .sidebar-section-link-everything.active"
|
||||
),
|
||||
"the everything link is marked as active"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
conditionalTest(
|
||||
"new and unread count for everything link",
|
||||
!isLegacyEmber(),
|
||||
async function (assert) {
|
||||
this.container.lookup("topic-tracking-state:main").loadStates([
|
||||
{
|
||||
topic_id: 1,
|
||||
highest_post_number: 1,
|
||||
last_read_post_number: null,
|
||||
created_at: "2022-05-11T03:09:31.959Z",
|
||||
category_id: 1,
|
||||
notification_level: null,
|
||||
created_in_new_period: true,
|
||||
unread_not_too_old: true,
|
||||
treat_as_new_topic_start_date: "2022-05-09T03:17:34.286Z",
|
||||
},
|
||||
{
|
||||
topic_id: 2,
|
||||
highest_post_number: 12,
|
||||
last_read_post_number: 11,
|
||||
created_at: "2020-02-09T09:40:02.672Z",
|
||||
category_id: 2,
|
||||
notification_level: 2,
|
||||
created_in_new_period: false,
|
||||
unread_not_too_old: true,
|
||||
treat_as_new_topic_start_date: "2022-05-09T03:17:34.286Z",
|
||||
},
|
||||
{
|
||||
topic_id: 3,
|
||||
highest_post_number: 15,
|
||||
last_read_post_number: 14,
|
||||
created_at: "2021-06-14T12:41:02.477Z",
|
||||
category_id: 3,
|
||||
notification_level: 2,
|
||||
created_in_new_period: false,
|
||||
unread_not_too_old: true,
|
||||
treat_as_new_topic_start_date: "2022-05-09T03:17:34.286Z",
|
||||
},
|
||||
{
|
||||
topic_id: 4,
|
||||
highest_post_number: 17,
|
||||
last_read_post_number: 16,
|
||||
created_at: "2020-10-31T03:41:42.257Z",
|
||||
category_id: 4,
|
||||
notification_level: 2,
|
||||
created_in_new_period: false,
|
||||
unread_not_too_old: true,
|
||||
treat_as_new_topic_start_date: "2022-05-09T03:17:34.286Z",
|
||||
},
|
||||
]);
|
||||
|
||||
await visit("/");
|
||||
|
||||
assert.strictEqual(
|
||||
query(
|
||||
".sidebar-section-link-everything .sidebar-section-link-content-badge"
|
||||
).textContent.trim(),
|
||||
"3 unread",
|
||||
"it displays the right unread count"
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
query(".sidebar-section-link-everything").href.endsWith("/unread"),
|
||||
"is links to unread filter"
|
||||
);
|
||||
|
||||
// simulate reading topic 2
|
||||
publishToMessageBus("/unread", {
|
||||
topic_id: 2,
|
||||
message_type: "read",
|
||||
payload: {
|
||||
last_read_post_number: 12,
|
||||
highest_post_number: 12,
|
||||
notification_level: 2,
|
||||
},
|
||||
});
|
||||
|
||||
await settled();
|
||||
|
||||
assert.strictEqual(
|
||||
query(
|
||||
".sidebar-section-link-everything .sidebar-section-link-content-badge"
|
||||
).textContent.trim(),
|
||||
"2 unread",
|
||||
"it updates the unread count"
|
||||
);
|
||||
|
||||
// simulate reading topic 3
|
||||
publishToMessageBus("/unread", {
|
||||
topic_id: 3,
|
||||
message_type: "read",
|
||||
payload: {
|
||||
last_read_post_number: 15,
|
||||
highest_post_number: 15,
|
||||
notification_level: 2,
|
||||
},
|
||||
});
|
||||
|
||||
// simulate reading topic 4
|
||||
publishToMessageBus("/unread", {
|
||||
topic_id: 4,
|
||||
message_type: "read",
|
||||
payload: {
|
||||
last_read_post_number: 17,
|
||||
highest_post_number: 17,
|
||||
notification_level: 2,
|
||||
},
|
||||
});
|
||||
|
||||
await settled();
|
||||
|
||||
assert.strictEqual(
|
||||
query(
|
||||
".sidebar-section-link-everything .sidebar-section-link-content-badge"
|
||||
).textContent.trim(),
|
||||
"1 new",
|
||||
"it displays the new count once there are no unread topics"
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
query(".sidebar-section-link-everything").href.endsWith("/new"),
|
||||
"is links to new filter"
|
||||
);
|
||||
|
||||
publishToMessageBus("/unread", {
|
||||
topic_id: 1,
|
||||
message_type: "read",
|
||||
payload: {
|
||||
last_read_post_number: 1,
|
||||
highest_post_number: 1,
|
||||
notification_level: 2,
|
||||
},
|
||||
});
|
||||
|
||||
await settled();
|
||||
|
||||
assert.ok(
|
||||
!exists(
|
||||
".sidebar-section-link-everything .sidebar-section-link-content-badge"
|
||||
),
|
||||
"it removes new count once there are no new topics"
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
query(".sidebar-section-link-everything").href.endsWith("/latest"),
|
||||
"is links to latest filter"
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
|
@ -32,11 +32,98 @@
|
|||
box-sizing: border-box;
|
||||
height: 100%;
|
||||
width: 240px;
|
||||
padding: 1em;
|
||||
padding: 1em 0.5em 1em 0;
|
||||
}
|
||||
|
||||
.sidebar-toggle {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.sidebar-section-header {
|
||||
display: flex;
|
||||
text-transform: uppercase;
|
||||
font-size: 1em;
|
||||
font-weight: bold;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.sidebar-section-header-link {
|
||||
&:visited {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: var(--primary-low);
|
||||
}
|
||||
|
||||
flex: 1 1 auto;
|
||||
color: var(--primary);
|
||||
font-size: var(--font-down-1);
|
||||
padding: 0.25em 0.5em;
|
||||
}
|
||||
|
||||
.sidebar-section-header-button {
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0.25em 0.5em;
|
||||
|
||||
.d-icon {
|
||||
font-size: $font-down-1;
|
||||
color: var(--primary-medium);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: var(--primary-low);
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-section-link-wrapper {
|
||||
margin-left: 1.5em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.sidebar-section-link {
|
||||
flex: 1 1 0;
|
||||
display: flex;
|
||||
padding: 0.25em 0.5em;
|
||||
color: var(--primary-high);
|
||||
font-size: var(--font-down-1);
|
||||
|
||||
&:hover {
|
||||
background: var(--primary-low);
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: var(--primary);
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-section-link-content-badge {
|
||||
color: var(--tertiary);
|
||||
font-size: var(--font-down-1);
|
||||
font-weight: normal;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.sidebar-section-header-caret {
|
||||
width: 1.5em;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
|
||||
&:hover {
|
||||
opacity: 100;
|
||||
}
|
||||
|
||||
opacity: 0;
|
||||
|
||||
.d-icon {
|
||||
font-size: $font-down-1;
|
||||
color: var(--primary-medium);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4030,6 +4030,23 @@ en:
|
|||
|
||||
second_factor_auth:
|
||||
redirect_after_success: "Second factor authentication is successful. Redirecting to the previous page…"
|
||||
|
||||
sidebar:
|
||||
sections:
|
||||
links:
|
||||
badge:
|
||||
unread_count: "%{count} unread"
|
||||
new_count: "%{count} new"
|
||||
everything:
|
||||
content: "Everything"
|
||||
title: "All topics"
|
||||
tracked:
|
||||
content: "Tracked"
|
||||
title: "All tracked topics"
|
||||
bookmarked:
|
||||
content: "Bookmarked"
|
||||
title: "All bookmarked topics"
|
||||
|
||||
# This section is exported to the javascript for i18n in the admin section
|
||||
admin_js:
|
||||
type_to_filter: "type to filter..."
|
||||
|
|
Loading…
Reference in New Issue
Block a user