mirror of
https://github.com/discourse/discourse.git
synced 2025-03-23 08:25:43 +08:00
DEV: Add admins and moderators sections to the redesigned /about page (#28273)
This commit continues on work laid out by 6039b513fe
to redesign the /about page. In this commit, we add sections for showing the site admins and moderators.
The lists of admins and moderators display the 10 most recently seen admins/moderators, with a button to display the rest of admins or moderators. Admins or moderators that have not logged in to the site in the last year will not be shown. Clicking on an admin's or moderator's name/avatar will show their user card.
This commit is contained in:
parent
34de336afb
commit
1d6e54e54c
@ -0,0 +1,47 @@
|
||||
import avatar from "discourse/helpers/avatar";
|
||||
import { prioritizeNameInUx } from "discourse/lib/settings";
|
||||
import { userPath } from "discourse/lib/url";
|
||||
import i18n from "discourse-common/helpers/i18n";
|
||||
|
||||
const AboutPageUser = <template>
|
||||
<div data-username={{@user.username}} class="user-info small">
|
||||
<div class="user-image">
|
||||
<div class="user-image-inner">
|
||||
<a
|
||||
href={{userPath @user.username}}
|
||||
data-user-card={{@user.username}}
|
||||
aria-hidden="true"
|
||||
>
|
||||
{{avatar @user imageSize="large"}}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="user-detail">
|
||||
<div class="name-line">
|
||||
<a
|
||||
href={{userPath @user.username}}
|
||||
data-user-card={{@user.username}}
|
||||
aria-label={{i18n "user.profile_possessive" username=@user.username}}
|
||||
>
|
||||
<span class="username">
|
||||
{{#if (prioritizeNameInUx @user.name)}}
|
||||
{{@user.name}}
|
||||
{{else}}
|
||||
{{@user.username}}
|
||||
{{/if}}
|
||||
</span>
|
||||
<span class="name">
|
||||
{{#if (prioritizeNameInUx @user.name)}}
|
||||
{{@user.username}}
|
||||
{{else}}
|
||||
{{@user.name}}
|
||||
{{/if}}
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="title">{{@user.title}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>;
|
||||
|
||||
export default AboutPageUser;
|
@ -1,71 +1,49 @@
|
||||
import Component from "@glimmer/component";
|
||||
import { service } from "@ember/service";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
import { renderAvatar } from "discourse/helpers/user-avatar";
|
||||
import { prioritizeNameInUx } from "discourse/lib/settings";
|
||||
import { userPath } from "discourse/lib/url";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { action } from "@ember/object";
|
||||
import AboutPageUser from "discourse/components/about-page-user";
|
||||
import DButton from "discourse/components/d-button";
|
||||
import i18n from "discourse-common/helpers/i18n";
|
||||
|
||||
export default class AboutPageUsers extends Component {
|
||||
@service siteSettings;
|
||||
@tracked expanded = false;
|
||||
|
||||
get usersTemplates() {
|
||||
return (this.args.users || []).map((user) => ({
|
||||
name: user.name,
|
||||
username: user.username,
|
||||
userPath: userPath(user.username),
|
||||
avatar: renderAvatar(user, {
|
||||
imageSize: "large",
|
||||
siteSettings: this.siteSettings,
|
||||
}),
|
||||
title: user.title || "",
|
||||
prioritizeName: prioritizeNameInUx(user.name),
|
||||
}));
|
||||
get users() {
|
||||
let users = this.args.users;
|
||||
if (this.showViewMoreButton && !this.expanded) {
|
||||
users = users.slice(0, this.args.truncateAt);
|
||||
}
|
||||
return users;
|
||||
}
|
||||
|
||||
get showViewMoreButton() {
|
||||
return (
|
||||
this.args.truncateAt > 0 && this.args.users.length > this.args.truncateAt
|
||||
);
|
||||
}
|
||||
|
||||
@action
|
||||
toggleExpanded() {
|
||||
this.expanded = !this.expanded;
|
||||
}
|
||||
|
||||
<template>
|
||||
{{#each this.usersTemplates as |template|}}
|
||||
<div data-username={{template.username}} class="user-info small">
|
||||
<div class="user-image">
|
||||
<div class="user-image-inner">
|
||||
<a
|
||||
href={{template.userPath}}
|
||||
data-user-card={{template.username}}
|
||||
aria-hidden="true"
|
||||
>
|
||||
{{htmlSafe template.avatar}}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="user-detail">
|
||||
<div class="name-line">
|
||||
<a
|
||||
href={{template.userPath}}
|
||||
data-user-card={{template.username}}
|
||||
aria-label={{i18n
|
||||
"user.profile_possessive"
|
||||
username=template.username
|
||||
}}
|
||||
>
|
||||
<span class="username">
|
||||
{{#if template.prioritizeName}}
|
||||
{{template.name}}
|
||||
{{else}}
|
||||
{{template.username}}
|
||||
{{/if}}
|
||||
</span>
|
||||
<span class="name">
|
||||
{{#if template.prioritizeName}}
|
||||
{{template.username}}
|
||||
{{else}}
|
||||
{{template.name}}
|
||||
{{/if}}
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="title">{{template.title}}</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
<div class="about-page-users-list">
|
||||
{{#each this.users as |user|}}
|
||||
<AboutPageUser @user={{user}} />
|
||||
{{/each}}
|
||||
</div>
|
||||
{{#if this.showViewMoreButton}}
|
||||
<DButton
|
||||
class="btn-flat about-page-users-list__expand-button"
|
||||
@action={{this.toggleExpanded}}
|
||||
@icon={{if this.expanded "chevron-up" "chevron-down"}}
|
||||
@translatedLabel={{if
|
||||
this.expanded
|
||||
(i18n "about.view_less")
|
||||
(i18n "about.view_more")
|
||||
}}
|
||||
/>
|
||||
{{/if}}
|
||||
</template>
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import Component from "@glimmer/component";
|
||||
import { hash } from "@ember/helper";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
import AboutPageUsers from "discourse/components/about-page-users";
|
||||
import PluginOutlet from "discourse/components/plugin-outlet";
|
||||
import { number } from "discourse/lib/formatter";
|
||||
import dIcon from "discourse-common/helpers/d-icon";
|
||||
@ -161,6 +162,20 @@ export default class AboutPage extends Component {
|
||||
</div>
|
||||
<h3>{{i18n "about.simple_title"}}</h3>
|
||||
<div>{{htmlSafe @model.extended_site_description}}</div>
|
||||
|
||||
{{#if @model.admins.length}}
|
||||
<section class="about__admins">
|
||||
<h3>{{dIcon "users"}} {{i18n "about.our_admins"}}</h3>
|
||||
<AboutPageUsers @users={{@model.admins}} @truncateAt={{10}} />
|
||||
</section>
|
||||
{{/if}}
|
||||
|
||||
{{#if @model.moderators.length}}
|
||||
<section class="about__moderators">
|
||||
<h3>{{dIcon "users"}} {{i18n "about.our_moderators"}}</h3>
|
||||
<AboutPageUsers @users={{@model.moderators}} @truncateAt={{10}} />
|
||||
</section>
|
||||
{{/if}}
|
||||
</section>
|
||||
<section class="about__right-side">
|
||||
<h3>{{i18n "about.contact"}}</h3>
|
||||
|
@ -0,0 +1,14 @@
|
||||
import Component from "@glimmer/component";
|
||||
import AboutPageUser from "discourse/components/about-page-user";
|
||||
|
||||
export default class LegacyAboutPageUsers extends Component {
|
||||
get users() {
|
||||
return this.args.users || [];
|
||||
}
|
||||
|
||||
<template>
|
||||
{{#each this.users as |user|}}
|
||||
<AboutPageUser @user={{user}} />
|
||||
{{/each}}
|
||||
</template>
|
||||
}
|
@ -53,7 +53,7 @@
|
||||
<section class="about admins">
|
||||
<h3>{{d-icon "users"}} {{i18n "about.our_admins"}}</h3>
|
||||
<div class="users">
|
||||
<AboutPageUsers @users={{this.model.admins}} />
|
||||
<LegacyAboutPageUsers @users={{this.model.admins}} />
|
||||
</div>
|
||||
</section>
|
||||
{{/if}}
|
||||
@ -70,7 +70,7 @@
|
||||
<section class="about moderators">
|
||||
<h3>{{d-icon "users"}} {{i18n "about.our_moderators"}}</h3>
|
||||
<div class="users">
|
||||
<AboutPageUsers @users={{this.model.moderators}} />
|
||||
<LegacyAboutPageUsers @users={{this.model.moderators}} />
|
||||
</div>
|
||||
</section>
|
||||
{{/if}}
|
||||
@ -90,7 +90,7 @@
|
||||
>
|
||||
<h3>{{category-link cm.category}}{{i18n "about.moderators"}}</h3>
|
||||
<div class="users">
|
||||
<AboutPageUsers @users={{cm.moderators}} />
|
||||
<LegacyAboutPageUsers @users={{cm.moderators}} />
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</section>
|
||||
|
@ -37,6 +37,25 @@
|
||||
&__activities-item-period {
|
||||
font-size: var(--font-down-2);
|
||||
}
|
||||
|
||||
&__admins,
|
||||
&__moderators {
|
||||
margin-top: 3em;
|
||||
|
||||
h3 {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.about-page-users-list {
|
||||
display: grid;
|
||||
gap: 1em;
|
||||
grid-template-columns: repeat(auto-fit, minmax(20em, 1fr));
|
||||
|
||||
&__expand-button {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
section.about {
|
||||
|
@ -346,6 +346,8 @@ en:
|
||||
contact: "Contact us"
|
||||
contact_info: "In the event of a critical issue or urgent matter affecting this site, please contact us at %{contact_info}."
|
||||
site_activity: "Site activity"
|
||||
view_more: "View more"
|
||||
view_less: "View less"
|
||||
activities:
|
||||
topics:
|
||||
one: "%{formatted_number} topic"
|
||||
|
@ -171,5 +171,187 @@ describe "About page", type: :system do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "our admins section" do
|
||||
before { User.update_all(last_seen_at: 1.month.ago) }
|
||||
|
||||
fab!(:admins) { Fabricate.times(12, :admin) }
|
||||
|
||||
it "displays only the 10 most recently seen admins when there are more than 10 admins" do
|
||||
admins[0].update!(last_seen_at: 4.minutes.ago)
|
||||
admins[1].update!(last_seen_at: 1.minutes.ago)
|
||||
admins[2].update!(last_seen_at: 10.minutes.ago)
|
||||
|
||||
about_page.visit
|
||||
expect(about_page.admins_list).to have_expand_button
|
||||
|
||||
displayed_admins = about_page.admins_list.users
|
||||
expect(displayed_admins.size).to eq(10)
|
||||
expect(displayed_admins.map { |u| u[:username] }.first(3)).to eq(
|
||||
[admins[1].username, admins[0].username, admins[2].username],
|
||||
)
|
||||
end
|
||||
|
||||
it "allows expanding and collapsing the list of admins" do
|
||||
about_page.visit
|
||||
|
||||
displayed_admins = about_page.admins_list.users
|
||||
expect(displayed_admins.size).to eq(10)
|
||||
|
||||
expect(about_page.admins_list).to be_expandable
|
||||
|
||||
about_page.admins_list.expand
|
||||
|
||||
expect(about_page.admins_list).to be_collapsible
|
||||
|
||||
displayed_admins = about_page.admins_list.users
|
||||
expect(displayed_admins.size).to eq(13) # 12 fabricated for this spec group and 1 global
|
||||
|
||||
about_page.admins_list.collapse
|
||||
|
||||
expect(about_page.admins_list).to be_expandable
|
||||
|
||||
displayed_admins = about_page.admins_list.users
|
||||
expect(displayed_admins.size).to eq(10)
|
||||
end
|
||||
|
||||
it "doesn't show an expand/collapse button when there are fewer than 10 admins" do
|
||||
User.where(id: admins.first(7).map(&:id)).destroy_all
|
||||
|
||||
about_page.visit
|
||||
|
||||
displayed_admins = about_page.admins_list.users
|
||||
expect(displayed_admins.size).to eq(6)
|
||||
expect(about_page.admins_list).to have_no_expand_button
|
||||
end
|
||||
|
||||
it "prioritizes names when prioritize_username_in_ux is false" do
|
||||
SiteSetting.prioritize_username_in_ux = false
|
||||
|
||||
about_page.visit
|
||||
|
||||
displayed_admins = about_page.admins_list.users
|
||||
admins = User.where(username: displayed_admins.map { |u| u[:username] })
|
||||
expect(displayed_admins.map { |u| u[:displayed_username] }).to contain_exactly(
|
||||
*admins.pluck(:name),
|
||||
)
|
||||
expect(displayed_admins.map { |u| u[:displayed_name] }).to contain_exactly(
|
||||
*admins.pluck(:username),
|
||||
)
|
||||
end
|
||||
|
||||
it "prioritizes usernames when prioritize_username_in_ux is true" do
|
||||
SiteSetting.prioritize_username_in_ux = true
|
||||
|
||||
about_page.visit
|
||||
|
||||
displayed_admins = about_page.admins_list.users
|
||||
admins = User.where(username: displayed_admins.map { |u| u[:username] })
|
||||
expect(displayed_admins.map { |u| u[:displayed_username] }).to contain_exactly(
|
||||
*admins.pluck(:username),
|
||||
)
|
||||
expect(displayed_admins.map { |u| u[:displayed_name] }).to contain_exactly(
|
||||
*admins.pluck(:name),
|
||||
)
|
||||
end
|
||||
|
||||
it "opens the user card when a user is clicked" do
|
||||
about_page.visit
|
||||
|
||||
about_page.admins_list.users.first[:node].click
|
||||
expect(about_page).to have_css("#user-card")
|
||||
end
|
||||
end
|
||||
|
||||
describe "our moderators section" do
|
||||
before { User.update_all(last_seen_at: 1.month.ago) }
|
||||
|
||||
fab!(:moderators) { Fabricate.times(13, :moderator) }
|
||||
|
||||
it "displays only the 10 most recently seen moderators when there are more than 10 moderators" do
|
||||
moderators[10].update!(last_seen_at: 5.hours.ago)
|
||||
moderators[3].update!(last_seen_at: 2.hours.ago)
|
||||
moderators[5].update!(last_seen_at: 13.hours.ago)
|
||||
|
||||
about_page.visit
|
||||
expect(about_page.moderators_list).to have_expand_button
|
||||
|
||||
displayed_mods = about_page.moderators_list.users
|
||||
expect(displayed_mods.size).to eq(10)
|
||||
expect(displayed_mods.map { |u| u[:username] }.first(3)).to eq(
|
||||
[moderators[3].username, moderators[10].username, moderators[5].username],
|
||||
)
|
||||
end
|
||||
|
||||
it "allows expanding and collapsing the list of moderators" do
|
||||
about_page.visit
|
||||
|
||||
displayed_mods = about_page.moderators_list.users
|
||||
expect(displayed_mods.size).to eq(10)
|
||||
|
||||
expect(about_page.moderators_list).to be_expandable
|
||||
|
||||
about_page.moderators_list.expand
|
||||
|
||||
expect(about_page.moderators_list).to be_collapsible
|
||||
|
||||
displayed_mods = about_page.moderators_list.users
|
||||
expect(displayed_mods.size).to eq(14) # 13 fabricated for this spec group and 1 global
|
||||
|
||||
about_page.moderators_list.collapse
|
||||
|
||||
expect(about_page.moderators_list).to be_expandable
|
||||
|
||||
displayed_mods = about_page.moderators_list.users
|
||||
expect(displayed_mods.size).to eq(10)
|
||||
end
|
||||
|
||||
it "doesn't show an expand/collapse button when there are fewer than 10 moderators" do
|
||||
User.where(id: moderators.first(10).map(&:id)).destroy_all
|
||||
|
||||
about_page.visit
|
||||
|
||||
displayed_mods = about_page.moderators_list.users
|
||||
expect(displayed_mods.size).to eq(4)
|
||||
expect(about_page.moderators_list).to have_no_expand_button
|
||||
end
|
||||
|
||||
it "prioritizes names when prioritize_username_in_ux is false" do
|
||||
SiteSetting.prioritize_username_in_ux = false
|
||||
|
||||
about_page.visit
|
||||
|
||||
displayed_mods = about_page.moderators_list.users
|
||||
moderators = User.where(username: displayed_mods.map { |u| u[:username] })
|
||||
expect(displayed_mods.map { |u| u[:displayed_username] }).to contain_exactly(
|
||||
*moderators.pluck(:name),
|
||||
)
|
||||
expect(displayed_mods.map { |u| u[:displayed_name] }).to contain_exactly(
|
||||
*moderators.pluck(:username),
|
||||
)
|
||||
end
|
||||
|
||||
it "prioritizes usernames when prioritize_username_in_ux is true" do
|
||||
SiteSetting.prioritize_username_in_ux = true
|
||||
|
||||
about_page.visit
|
||||
|
||||
displayed_mods = about_page.moderators_list.users
|
||||
moderators = User.where(username: displayed_mods.map { |u| u[:username] })
|
||||
expect(displayed_mods.map { |u| u[:displayed_username] }).to contain_exactly(
|
||||
*moderators.pluck(:username),
|
||||
)
|
||||
expect(displayed_mods.map { |u| u[:displayed_name] }).to contain_exactly(
|
||||
*moderators.pluck(:name),
|
||||
)
|
||||
end
|
||||
|
||||
it "opens the user card when a user is clicked" do
|
||||
about_page.visit
|
||||
|
||||
about_page.moderators_list.users.last[:node].click
|
||||
expect(about_page).to have_css("#user-card")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
60
spec/system/page_objects/components/about_page_users_list.rb
Normal file
60
spec/system/page_objects/components/about_page_users_list.rb
Normal file
@ -0,0 +1,60 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module PageObjects
|
||||
module Components
|
||||
class AboutPageUsersList < PageObjects::Components::Base
|
||||
attr_reader :container
|
||||
|
||||
def initialize(container)
|
||||
@container = container
|
||||
end
|
||||
|
||||
def has_expand_button?
|
||||
container.has_css?(".about-page-users-list__expand-button")
|
||||
end
|
||||
|
||||
def has_no_expand_button?
|
||||
container.has_no_css?(".about-page-users-list__expand-button")
|
||||
end
|
||||
|
||||
def expandable?
|
||||
container.find(".about-page-users-list__expand-button").has_text?(
|
||||
I18n.t("js.about.view_more"),
|
||||
)
|
||||
end
|
||||
|
||||
def collapsible?
|
||||
container.find(".about-page-users-list__expand-button").has_text?(
|
||||
I18n.t("js.about.view_less"),
|
||||
)
|
||||
end
|
||||
|
||||
def expand
|
||||
container.find(
|
||||
".about-page-users-list__expand-button",
|
||||
text: I18n.t("js.about.view_more"),
|
||||
).click
|
||||
end
|
||||
|
||||
def collapse
|
||||
container.find(
|
||||
".about-page-users-list__expand-button",
|
||||
text: I18n.t("js.about.view_less"),
|
||||
).click
|
||||
end
|
||||
|
||||
def users
|
||||
container
|
||||
.all(".user-info")
|
||||
.map do |node|
|
||||
{
|
||||
username: node["data-username"],
|
||||
displayed_username: node.find(".name-line .username").text,
|
||||
displayed_name: node.find(".name-line .name").text,
|
||||
node:,
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -50,6 +50,14 @@ module PageObjects
|
||||
PageObjects::Components::AboutPageSiteActivity.new(find(".about__activities"))
|
||||
end
|
||||
|
||||
def admins_list
|
||||
PageObjects::Components::AboutPageUsersList.new(find(".about__admins"))
|
||||
end
|
||||
|
||||
def moderators_list
|
||||
PageObjects::Components::AboutPageUsersList.new(find(".about__moderators"))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def site_age_stat_element
|
||||
|
Loading…
x
Reference in New Issue
Block a user