FEATURE: Add review link to community section for logged in user (#18374)

When there are pending reviewables, the review section link is displayed
in the main section. When there are no pending reviewables, the review
section link is displayed under the more links drawer.

Internal ref: /t/74210
This commit is contained in:
Alan Guo Xiang Tan 2022-09-28 09:58:07 +08:00 committed by GitHub
parent bc97f3d1c1
commit 4b561277a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 192 additions and 28 deletions

View File

@ -151,11 +151,11 @@ export default Component.extend({
// "fast track" to update the current user's reviewable count before the message bus finds out. // "fast track" to update the current user's reviewable count before the message bus finds out.
if (performResult.reviewable_count !== undefined) { if (performResult.reviewable_count !== undefined) {
this.currentUser.set( this.currentUser.updateReviewableCount(
"reviewable_count",
performResult.reviewable_count performResult.reviewable_count
); );
} }
if (performResult.unseen_reviewable_count !== undefined) { if (performResult.unseen_reviewable_count !== undefined) {
this.currentUser.set( this.currentUser.set(
"unseen_reviewable_count", "unseen_reviewable_count",

View File

@ -1,5 +1,6 @@
import Component from "@glimmer/component"; import Component from "@glimmer/component";
import { inject as service } from "@ember/service"; import { inject as service } from "@ember/service";
import { tracked } from "@glimmer/tracking";
import { import {
customSectionLinks, customSectionLinks,
@ -13,29 +14,18 @@ export default class SidebarCommunitySection extends Component {
@service appEvents; @service appEvents;
@service siteSettings; @service siteSettings;
@tracked sectionLinks;
@tracked moreSectionLinks;
@tracked moreSecondarySectionLinks;
callbackId;
headerActionsIcon; headerActionsIcon;
headerActions; headerActions;
sectionLinks;
moreSectionLinks;
moreSecondarySectionLinks;
callbackId;
constructor() { constructor() {
super(...arguments); super(...arguments);
this.moreSectionLinks = this.#initializeSectionLinks([ this.refreshSectionLinks();
...this.defaultMoreSectionLinks,
...customSectionLinks,
]);
this.moreSecondarySectionLinks = this.#initializeSectionLinks([
...this.defaultMoreSecondarySectionLinks,
...secondaryCustomSectionLinks,
]);
this.sectionLinks = this.#initializeSectionLinks(
this.defaultMainSectionLinks
);
this.callbackId = this.topicTrackingState.onStateChange(() => { this.callbackId = this.topicTrackingState.onStateChange(() => {
this.sectionLinks.forEach((sectionLink) => { this.sectionLinks.forEach((sectionLink) => {
@ -64,6 +54,22 @@ export default class SidebarCommunitySection extends Component {
return []; return [];
} }
refreshSectionLinks() {
this.moreSectionLinks = this.#initializeSectionLinks([
...this.defaultMoreSectionLinks,
...customSectionLinks,
]);
this.moreSecondarySectionLinks = this.#initializeSectionLinks([
...this.defaultMoreSecondarySectionLinks,
...secondaryCustomSectionLinks,
]);
this.sectionLinks = this.#initializeSectionLinks(
this.defaultMainSectionLinks
);
}
#initializeSectionLinks(sectionLinkClasses) { #initializeSectionLinks(sectionLinkClasses) {
return sectionLinkClasses.reduce((links, sectionLinkClass) => { return sectionLinkClasses.reduce((links, sectionLinkClass) => {
const sectionLink = this.#initializeSectionLink(sectionLinkClass); const sectionLink = this.#initializeSectionLink(sectionLinkClass);

View File

@ -12,7 +12,7 @@
{{did-insert this.registerClickListener}} {{did-insert this.registerClickListener}}
{{will-destroy this.unregisterClickListener}} > {{will-destroy this.unregisterClickListener}} >
<div class="sidebar-more-section-link-details-content"> <div class="sidebar-more-section-links-details-content">
<div class="sidebar-more-section-links-details-content-main"> <div class="sidebar-more-section-links-details-content-main">
{{#each this.sectionLinks as |sectionLink|}} {{#each this.sectionLinks as |sectionLink|}}
<Sidebar::MoreSectionLink @sectionLink={{sectionLink}} /> <Sidebar::MoreSectionLink @sectionLink={{sectionLink}} />

View File

@ -1,5 +1,6 @@
import I18n from "I18n"; import I18n from "I18n";
import { bind } from "discourse-common/utils/decorators";
import Composer from "discourse/models/composer"; import Composer from "discourse/models/composer";
import { getOwner } from "discourse-common/lib/get-owner"; import { getOwner } from "discourse-common/lib/get-owner";
import PermissionType from "discourse/models/permission-type"; import PermissionType from "discourse/models/permission-type";
@ -12,6 +13,7 @@ import AboutSectionLink from "discourse/lib/sidebar/common/community-section/abo
import FAQSectionLink from "discourse/lib/sidebar/common/community-section/faq-section-link"; import FAQSectionLink from "discourse/lib/sidebar/common/community-section/faq-section-link";
import AdminSectionLink from "discourse/lib/sidebar/user/community-section/admin-section-link"; import AdminSectionLink from "discourse/lib/sidebar/user/community-section/admin-section-link";
import BadgesSectionLink from "discourse/lib/sidebar/common/community-section/badges-section-link"; import BadgesSectionLink from "discourse/lib/sidebar/common/community-section/badges-section-link";
import ReviewSectionLink from "discourse/lib/sidebar/user/community-section/review-section-link";
import SidebarCommonCommunitySection from "discourse/components/sidebar/common/community-section"; import SidebarCommonCommunitySection from "discourse/components/sidebar/common/community-section";
import { action } from "@ember/object"; import { action } from "@ember/object";
@ -29,19 +31,48 @@ export default class SidebarUserCommunitySection extends SidebarCommonCommunityS
title: I18n.t("sidebar.sections.community.header_action_title"), title: I18n.t("sidebar.sections.community.header_action_title"),
}, },
]; ];
this.appEvents.on(
"user-reviewable-count:changed",
this._refreshSectionLinks
);
}
willDestroy() {
this.appEvents.off(
"user-reviewable-count:changed",
this._refreshSectionLinks
);
}
@bind
_refreshSectionLinks() {
return this.refreshSectionLinks();
} }
get defaultMainSectionLinks() { get defaultMainSectionLinks() {
return [ const links = [
EverythingSectionLink, EverythingSectionLink,
TrackedSectionLink, TrackedSectionLink,
MyPostsSectionLink, MyPostsSectionLink,
AdminSectionLink, AdminSectionLink,
]; ];
if (this.currentUser.reviewable_count > 0) {
links.push(ReviewSectionLink);
}
return links;
} }
get defaultMoreSectionLinks() { get defaultMoreSectionLinks() {
return [GroupsSectionLink, UsersSectionLink, BadgesSectionLink]; const links = [GroupsSectionLink, UsersSectionLink, BadgesSectionLink];
if (this.currentUser.reviewable_count === 0) {
links.push(ReviewSectionLink);
}
return links;
} }
get defaultMoreSecondarySectionLinks() { get defaultMoreSecondarySectionLinks() {

View File

@ -28,10 +28,12 @@ export default {
const channel = user.enable_redesigned_user_menu const channel = user.enable_redesigned_user_menu
? `/reviewable_counts/${user.id}` ? `/reviewable_counts/${user.id}`
: "/reviewable_counts"; : "/reviewable_counts";
bus.subscribe(channel, (data) => { bus.subscribe(channel, (data) => {
if (data.reviewable_count >= 0) { if (data.reviewable_count >= 0) {
user.set("reviewable_count", data.reviewable_count); user.updateReviewableCount(data.reviewable_count);
} }
if (user.redesigned_user_menu_enabled) { if (user.redesigned_user_menu_enabled) {
user.set("unseen_reviewable_count", data.unseen_reviewable_count); user.set("unseen_reviewable_count", data.unseen_reviewable_count);
} }

View File

@ -0,0 +1,33 @@
import I18n from "I18n";
import BaseSectionLink from "discourse/lib/sidebar/base-community-section-link";
export default class ReviewSectionLink extends BaseSectionLink {
get name() {
return "review";
}
get route() {
return "review";
}
get title() {
return I18n.t("sidebar.sections.community.links.review.title");
}
get text() {
return I18n.t("sidebar.sections.community.links.review.content");
}
get shouldDisplay() {
return this.currentUser.can_review;
}
get badgeText() {
if (this.currentUser.reviewable_count > 0) {
return I18n.t("sidebar.sections.community.links.review.pending_count", {
count: this.currentUser.reviewable_count,
});
}
}
}

View File

@ -1059,6 +1059,11 @@ const User = RestModel.extend({
this.appEvents.trigger("user-drafts:changed"); this.appEvents.trigger("user-drafts:changed");
}, },
updateReviewableCount(count) {
this.set("reviewable_count", count);
this.appEvents.trigger("user-reviewable-count:changed", count);
},
isInDoNotDisturb() { isInDoNotDisturb() {
return ( return (
this.do_not_disturb_until && this.do_not_disturb_until &&

View File

@ -68,6 +68,7 @@ export default DiscourseRoute.extend({
const channel = this.currentUser.enable_redesigned_user_menu const channel = this.currentUser.enable_redesigned_user_menu
? `/reviewable_counts/${this.currentUser.id}` ? `/reviewable_counts/${this.currentUser.id}`
: "/reviewable_counts"; : "/reviewable_counts";
this.messageBus.subscribe(channel, (data) => { this.messageBus.subscribe(channel, (data) => {
if (data.updates) { if (data.updates) {
this.controller.reviewables.forEach((reviewable) => { this.controller.reviewables.forEach((reviewable) => {
@ -82,10 +83,6 @@ export default DiscourseRoute.extend({
deactivate() { deactivate() {
this.messageBus.unsubscribe("/reviewable_claimed"); this.messageBus.unsubscribe("/reviewable_claimed");
const channel = this.currentUser.enable_redesigned_user_menu
? `/reviewable_counts/${this.currentUser.id}`
: "/reviewable_counts";
this.messageBus.unsubscribe(channel);
}, },
@action @action

View File

@ -710,6 +710,86 @@ acceptance("Sidebar - Logged on user - Community Section", function (needs) {
); );
}); });
test("review link is not shown when user cannot review", async function (assert) {
updateCurrentUser({ can_review: false });
await visit("/");
assert.notOk(
exists(".sidebar-section-community .sidebar-section-link-review"),
"review link is not shown"
);
await click(
".sidebar-section-community .sidebar-more-section-links-details-summary"
);
assert.notOk(
exists(".sidebar-section-community .sidebar-section-link-review"),
"review link is not shown"
);
});
test("review link when user can review", async function (assert) {
updateCurrentUser({
can_review: true,
reviewable_count: 0,
});
await visit("/reivew");
assert.notOk(
exists(".sidebar-section-community .sidebar-section-link-review.active"),
"review link is shown as active when visiting the review route even if there are no pending reviewables"
);
await visit("/");
assert.notOk(
exists(".sidebar-section-community .sidebar-section-link-review"),
"review link is not shown as part of the main section links"
);
await click(
".sidebar-section-community .sidebar-more-section-links-details-summary"
);
assert.ok(
exists(
".sidebar-section-community .sidebar-more-section-links-details-content .sidebar-section-link-review"
),
"review link is displayed in the more drawer"
);
await publishToMessageBus("/reviewable_counts", {
reviewable_count: 34,
});
assert.ok(
exists(".sidebar-section-community .sidebar-section-link-review"),
"review link is shown as part of the main section links"
);
assert.strictEqual(
query(
".sidebar-section-community .sidebar-section-link-review .sidebar-section-link-content-badge"
).textContent.trim(),
"34 pending",
"displays the pending reviewable count"
);
await click(
".sidebar-section-community .sidebar-more-section-links-details-summary"
);
assert.notOk(
exists(
".sidebar-section-community .sidebar-more-section-links-details-content .sidebar-section-link-review"
),
"review link is not displayed in the more drawer"
);
});
test("new and unread count for tracked link", async function (assert) { test("new and unread count for tracked link", async function (assert) {
const categories = Site.current().categories; const categories = Site.current().categories;

View File

@ -29,7 +29,7 @@
background: transparent; background: transparent;
} }
.sidebar-more-section-link-details-content { .sidebar-more-section-links-details-content {
background-color: var(--secondary); background-color: var(--secondary);
box-shadow: shadow("dropdown"); box-shadow: shadow("dropdown");
margin: 0 calc(var(--d-sidebar-row-horizontal-padding) * 2 / 3); margin: 0 calc(var(--d-sidebar-row-horizontal-padding) * 2 / 3);

View File

@ -71,6 +71,12 @@
} }
} }
.sidebar-section-link-review {
.sidebar-section-link-content-badge {
color: var(--danger);
}
}
.sidebar-section-link-prefix { .sidebar-section-link-prefix {
flex-shrink: 0; flex-shrink: 0;

View File

@ -4196,6 +4196,10 @@ en:
draft_count: draft_count:
one: "%{count} draft" one: "%{count} draft"
other: "%{count} drafts" other: "%{count} drafts"
review:
content: "Review"
title: "review"
pending_count: "%{count} pending"
welcome_topic_banner: welcome_topic_banner:
title: "Create your Welcome Topic" title: "Create your Welcome Topic"