UX: new layout for groups page, allow changes to automatic group bio (#7350)

This commit is contained in:
Maja Komel 2019-04-18 04:44:30 +02:00 committed by Kris
parent bf712a8598
commit 1f17d52f55
11 changed files with 198 additions and 285 deletions

View File

@ -21,6 +21,8 @@ export default Ember.Component.extend({
} }
}, },
canEdit: Ember.computed.not("model.automatic"),
@computed("basicNameValidation", "uniqueNameValidation") @computed("basicNameValidation", "uniqueNameValidation")
nameValidation(basicNameValidation, uniqueNameValidation) { nameValidation(basicNameValidation, uniqueNameValidation) {
return uniqueNameValidation ? uniqueNameValidation : basicNameValidation; return uniqueNameValidation ? uniqueNameValidation : basicNameValidation;

View File

@ -6,19 +6,16 @@ export default Ember.Controller.extend({
@computed("model.automatic") @computed("model.automatic")
tabs(automatic) { tabs(automatic) {
const defaultTabs = [ const defaultTabs = [
{ route: "group.manage.profile", title: "groups.manage.profile.title" },
{ {
route: "group.manage.interaction", route: "group.manage.interaction",
title: "groups.manage.interaction.title" title: "groups.manage.interaction.title"
}, },
{ route: "group.manage.logs", title: "groups.manage.logs.title" } { route: "group.manage.logs", title: "groups.manage.logs.title" }
]; ];
if (!automatic) { if (!automatic) {
defaultTabs.splice(0, 0, {
route: "group.manage.profile",
title: "groups.manage.profile.title"
});
defaultTabs.splice(1, 0, { defaultTabs.splice(1, 0, {
route: "group.manage.membership", route: "group.manage.membership",
title: "groups.manage.membership.title" title: "groups.manage.membership.title"

View File

@ -3,11 +3,5 @@ export default Discourse.Route.extend({
titleToken() { titleToken() {
return I18n.t("groups.manage.profile.title"); return I18n.t("groups.manage.profile.title");
},
afterModel(group) {
if (group.get("automatic")) {
this.replaceWith("group.manage.interaction", group);
}
} }
}); });

View File

@ -16,4 +16,6 @@
disabled=loading disabled=loading
icon="user-plus" icon="user-plus"
label="groups.request"}} label="groups.request"}}
{{else}}
{{yield}}
{{/if}} {{/if}}

View File

@ -1,36 +1,38 @@
{{#if this.currentUser.admin}} {{#if canEdit}}
<div class="control-group"> {{#if this.currentUser.admin}}
<label class="control-label" for="name">{{i18n 'groups.name'}}</label> <div class="control-group">
<label class="control-label" for="name">{{i18n 'groups.name'}}</label>
{{text-field name="name" {{text-field name="name"
class="input-xxlarge group-form-name" class="input-xxlarge group-form-name"
value=nameInput value=nameInput
placeholderKey="admin.groups.name_placeholder"}} placeholderKey="admin.groups.name_placeholder"}}
{{input-tip validation=nameValidation}} {{input-tip validation=nameValidation}}
</div>
{{/if}}
<div class="control-group">
<label class="control-label" for='full_name'>{{i18n 'groups.manage.full_name'}}</label>
{{text-field name='full_name'
class="input-xxlarge group-form-full-name"
value=model.full_name}}
</div>
{{#if this.currentUser.admin}}
<div class="control-group">
<label class="control-label" for="title">
{{i18n 'admin.groups.default_title'}}
</label>
{{input value=model.title name="title" class="input-xxlarge"}}
<div class="control-instructions">
{{i18n 'admin.groups.default_title_description'}}
</div> </div>
{{/if}}
<div class="control-group">
<label class="control-label" for='full_name'>{{i18n 'groups.manage.full_name'}}</label>
{{text-field name='full_name'
class="input-xxlarge group-form-full-name"
value=model.full_name}}
</div> </div>
{{#if this.currentUser.admin}}
<div class="control-group">
<label class="control-label" for="title">
{{i18n 'admin.groups.default_title'}}
</label>
{{input value=model.title name="title" class="input-xxlarge"}}
<div class="control-instructions">
{{i18n 'admin.groups.default_title_description'}}
</div>
</div>
{{/if}}
{{/if}} {{/if}}
<div class="control-group"> <div class="control-group">
@ -38,10 +40,12 @@
{{d-editor value=model.bio_raw class="group-form-bio input-xxlarge"}} {{d-editor value=model.bio_raw class="group-form-bio input-xxlarge"}}
</div> </div>
{{yield}} {{#if canEdit}}
{{yield}}
<div class="control-group"> <div class="control-group">
{{group-flair-inputs model=model}} {{group-flair-inputs model=model}}
</div> </div>
{{plugin-outlet name="group-edit" args=(hash group=model)}} {{plugin-outlet name="group-edit" args=(hash group=model)}}
{{/if}}

View File

@ -1,11 +1,5 @@
<span class='groups-info-name'>{{group.displayName}}</span>
{{#if showFullName}} {{#if showFullName}}
<span class='groups-info-full-name'>{{group.full_name}}</span> <span class='groups-info-name'>{{group.full_name}}</span>
{{/if}} {{else}}
<span class='groups-info-name'>{{group.displayName}}</span>
{{#if group.title}}
<div>
<span class='groups-info-title'>{{group.title}}</span>
</div>
{{/if}} {{/if}}

View File

@ -25,69 +25,61 @@
{{#conditional-loading-spinner condition=model.loading}} {{#conditional-loading-spinner condition=model.loading}}
{{#load-more selector=".groups-table .groups-table-row" action=(action "loadMore")}} {{#load-more selector=".groups-table .groups-table-row" action=(action "loadMore")}}
<div class='container'> <div class='container'>
<table class="groups-table"> <div class="groups-boxes">
<thead> {{#each model as |group|}}
<tr> {{#link-to "group.members" group.name class="group-box"}}
{{directory-toggle field="name" labelKey="groups.group_name" order=order asc=asc}} <div class="group-box-inner">
{{directory-toggle field="user_count" labelKey="groups.user_count" order=order asc=asc}} <div class="group-info-wrapper">
<th>{{i18n "groups.index.group_type"}}</th> {{#if group.flair_url}}
<th>{{i18n "groups.membership"}}</th> <span class='group-avatar-flair'>
</tr> {{avatar-flair
</thead> flairURL=group.flair_url
flairBgColor=group.flair_bg_color
<tbody> flairColor=group.flair_color
{{#each model as |group|}} groupName=group.name}}
<tr class="groups-table-row"> </span>
<td class="groups-info">
{{#link-to "group.members" group.name}}
{{#if group.flair_url}}
<span class='group-avatar-flair'>
{{avatar-flair
flairURL=group.flair_url
flairBgColor=group.flair_bg_color
flairColor=group.flair_color
groupName=group.name}}
</span>
{{/if}}
{{groups-info group=group}}
{{/link-to}}
</td>
<td class="groups-user-count">{{d-icon "group"}}{{group.user_count}}</td>
<td class="groups-table-type">
{{#if group.public_admission}}
{{i18n 'groups.index.public'}}
{{else if group.isPrivate}}
{{d-icon "far-eye-slash"}}
{{i18n 'groups.index.private'}}
{{else}}
{{#if group.automatic}}
{{i18n 'groups.index.automatic'}}
{{else}}
{{i18n 'groups.index.closed'}}
{{/if}}
{{/if}} {{/if}}
</td> <span class="group-info">
{{groups-info group=group}}
<td class="groups-table-membership"> <div class="group-user-count">{{d-icon "user"}}{{group.user_count}}</div>
{{#if group.is_group_owner}} </span>
<span> </div>
<div class="group-description">{{{group.bio_excerpt}}}</div>
<div class="group-membership">
{{#group-membership-button tagName='' model=group showLogin=(route-action "showLogin")}}
{{#if group.is_group_owner}}
<span class="is-group-owner">
{{d-icon "shield"}}
{{i18n "groups.index.is_group_owner"}} {{i18n "groups.index.is_group_owner"}}
</span> </span>
{{else if group.is_group_user}} {{else if group.is_group_user}}
<span> <span class="is-group-member">
{{d-icon "check"}}
{{i18n "groups.index.is_group_user"}} {{i18n "groups.index.is_group_user"}}
</span> </span>
{{/if}} {{else if group.public_admission}}
{{i18n 'groups.index.public'}}
{{group-membership-button tagName='' model=group showLogin=(route-action "showLogin")}} {{else if group.isPrivate}}
</td> {{d-icon "far-eye-slash"}}
</tr> {{i18n 'groups.index.private'}}
{{/each}} {{else}}
</tbody> {{#if group.automatic}}
</table> {{i18n 'groups.index.automatic'}}
{{else}}
{{d-icon "ban"}}
{{i18n 'groups.index.closed'}}
{{/if}}
{{/if}}
{{/group-membership-button}}
</div>
</div>
{{/link-to}}
{{/each}}
</div>
</div> </div>
{{/load-more}} {{/load-more}}

View File

@ -1,7 +1,7 @@
.groups-header { .groups-header {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
margin-bottom: 30px; margin-bottom: 1em;
.groups-header-new { .groups-header-new {
order: 2; order: 2;
margin-bottom: 0.5em; margin-bottom: 0.5em;
@ -23,206 +23,128 @@
} }
} }
.groups-table { .groups-boxes {
margin: 1em 0;
width: 100%; width: 100%;
// IE11 falls back to flexbox
display: flex;
flex-wrap: wrap;
justify-content: space-between;
// Everything that's not IE11 uses grid
@supports (display: grid) {
display: grid;
grid-template-columns: repeat(4, 24%);
grid-column-gap: 1.333%;
grid-row-gap: 1em;
@include breakpoint("tablet") {
grid-template-columns: repeat(3, 32%);
grid-column-gap: 2%;
}
@include breakpoint("mobile") {
grid-template-columns: 100%;
}
}
tr { .group-box {
td { // Flex and margin are for IE11
padding: 0.8em; flex: 1 1 24%;
margin: 1%;
@include breakpoint("mobile") {
margin: 0;
}
display: flex;
box-sizing: border-box;
cursor: pointer;
border: 1px solid $primary-low;
color: $primary;
.discourse-no-touch & {
transition: all 0.25s;
&:hover {
box-shadow: shadow("card");
}
}
.group-membership {
color: $primary-medium; color: $primary-medium;
} margin-top: auto;
padding-top: 1em;
td.groups-info { .is-group-owner,
width: 50%; .is-group-member {
} color: $success;
td.group-user-status {
.d-icon {
color: $primary;
} }
} }
.group-box-inner {
td.groups-user-count { padding: 1em;
width: 17%; width: 100%;
font-size: $font-up-2; display: flex;
.d-icon { flex-direction: column;
display: none; box-sizing: border-box;
} .group-info-wrapper {
} display: flex;
overflow: hidden;
td.groups-table-type { flex: 0 1 auto;
width: 17%;
font-size: $font-up-1;
}
td.groups-table-membership {
font-size: $font-up-1;
.group-membership-button {
font-size: $font-down-1;
display: inline-block;
margin-left: 5px;
&:empty {
display: none;
}
}
}
@include breakpoint(mobile) {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
padding: 10px 0;
td.groups-info,
td.groups-user-count,
td.groups-table-type,
td.groups-table-membership {
width: 100%;
padding: 0;
text-align: right;
font-size: $font-0;
}
td.groups-info {
grid-column-start: 1;
grid-column-end: 4;
grid-row-start: 1;
grid-row-end: 3;
text-align: left;
line-height: $line-height-medium;
margin-bottom: 0.5em;
a {
display: flex;
}
.group-avatar-flair { .group-avatar-flair {
margin-right: 0.5em; margin-top: 0.3em;
margin-right: 8px;
flex: 0 0 auto;
} }
.groups-info-name { .group-info {
word-break: break-all; flex: 1 0 auto;
max-width: 45vw; margin-bottom: 1em;
} width: 70%;
.groups-info-full-name {
font-size: $font-down-1; span {
width: 100%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
} }
} }
td.groups-user-count { .group-user-count {
display: flex; display: flex;
align-items: center; align-items: center;
grid-column-start: 4; color: $primary-high;
grid-row-start: 1;
.d-icon {
display: inline-block;
margin-right: 0.25em;
color: $primary-low-mid;
}
}
td.groups-table-type {
grid-row-start: 3;
grid-column-start: 1;
grid-column-end: 3;
text-align: left;
align-self: center;
.d-icon { .d-icon {
margin-right: 0.25em; margin-right: 0.25em;
font-size: 0.8em;
color: $primary-medium;
} }
} }
td.groups-table-membership { .group-description {
grid-row-start: 3; color: $primary-high;
grid-column-start: 3; word-wrap: break-word;
grid-column-end: 5;
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-end;
button {
margin-left: 5px;
display: flex;
}
&:empty {
display: none;
}
} }
// IE11 grid support
display: -ms-grid;
-ms-grid-columns: 1fr 1fr;
-ms-grid-rows: 1fr 1fr;
td.groups-info {
display: -ms-grid;
-ms-grid-row: 1;
-ms-grid-column: 1;
}
td.groups-user-count {
justify-content: flex-end;
-ms-grid-row: 1;
-ms-grid-column: 2;
}
td.groups-table-type {
display: flex;
align-items: center;
-ms-grid-row: 2;
-ms-grid-column: 1;
}
td.groups-table-membership {
display: flex;
-ms-grid-row: 2;
-ms-grid-column: 2;
}
}
}
@include breakpoint(mobile) {
thead {
display: none;
}
}
.groups-info {
.group-info-details {
vertical-align: middle;
} }
.groups-info-name { .groups-info-name {
font-size: $font-up-1;
font-weight: bold; font-weight: bold;
color: $primary; color: $primary;
color: dark-light-choose($primary-high, $secondary-low);
}
.groups-info-full-name {
color: dark-light-choose($primary-high, $secondary-low);
}
.groups-info-title {
font-size: $font-down-1;
color: dark-light-choose($primary-medium, $secondary-medium);
}
.group-avatar-flair {
vertical-align: middle;
color: $primary;
}
span {
display: inline-block;
} }
$size: 40px; $size: 40px;
$icon-size: $size / 1.8; $icon-size: $size / 1.8;
.avatar-flair { .group-avatar-flair {
background-size: $size; display: inline-block;
height: $size; color: $primary;
width: $size;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
background-repeat: no-repeat;
.d-icon { .avatar-flair {
height: $icon-size; background-size: $size;
width: $icon-size; height: $size;
width: $size;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
background-repeat: no-repeat;
.d-icon {
height: $icon-size;
width: $icon-size;
}
} }
} }
.avatar-flair-image { .avatar-flair-image {
width: $size; width: $size;
} }

View File

@ -507,6 +507,7 @@ class GroupsController < ApplicationController
mentionable_level mentionable_level
messageable_level messageable_level
default_notification_level default_notification_level
bio_raw
} }
else else
default_params = %i{ default_params = %i{

View File

@ -19,6 +19,7 @@ class BasicGroupSerializer < ApplicationSerializer
:flair_color, :flair_color,
:bio_raw, :bio_raw,
:bio_cooked, :bio_cooked,
:bio_excerpt,
:public_admission, :public_admission,
:public_exit, :public_exit,
:allow_membership_requests, :allow_membership_requests,
@ -38,6 +39,10 @@ class BasicGroupSerializer < ApplicationSerializer
end end
end end
def bio_excerpt
PrettyText.excerpt(object.bio_cooked, 110) if object.bio_cooked.present?
end
def include_incoming_email? def include_incoming_email?
staff? staff?
end end

View File

@ -5,11 +5,11 @@ acceptance("Groups");
QUnit.test("Browsing Groups", async assert => { QUnit.test("Browsing Groups", async assert => {
await visit("/g?username=eviltrout"); await visit("/g?username=eviltrout");
assert.equal(count(".groups-table-row"), 1, "it displays user's groups"); assert.equal(count(".group-box"), 1, "it displays user's groups");
await visit("/g"); await visit("/g");
assert.equal(count(".groups-table-row"), 2, "it displays visible groups"); assert.equal(count(".group-box"), 2, "it displays visible groups");
assert.equal( assert.equal(
find(".group-index-join").length, find(".group-index-join").length,
1, 1,