mirror of
https://github.com/discourse/discourse.git
synced 2025-03-25 00:47:25 +08:00
UX: Allow users to filter by different group types on groups page.
This commit is contained in:
parent
63a1e9b60a
commit
15bcfcd182
@ -1,13 +1,24 @@
|
||||
import { observes } from 'ember-addons/ember-computed-decorators';
|
||||
import debounce from 'discourse/lib/debounce';
|
||||
import { default as computed, observes } from 'ember-addons/ember-computed-decorators';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
application: Ember.inject.controller(),
|
||||
queryParams: ["order", "asc", "filter"],
|
||||
queryParams: ["order", "asc", "filter", "type"],
|
||||
order: null,
|
||||
asc: null,
|
||||
filter: "",
|
||||
filterInput: "",
|
||||
type: "",
|
||||
|
||||
@computed("model.extras.type_filters")
|
||||
types(typeFilters) {
|
||||
const types = [];
|
||||
|
||||
typeFilters.forEach(type => {
|
||||
types.push({ id: type, name: I18n.t(`groups.index.${type}_groups`) });
|
||||
});
|
||||
|
||||
return types;
|
||||
},
|
||||
|
||||
@observes("filterInput")
|
||||
_setFilter: debounce(function() {
|
||||
|
@ -1,21 +1,26 @@
|
||||
export default Discourse.Route.extend({
|
||||
queryParams: {
|
||||
order: { refreshModel: true, replace: true },
|
||||
asc: { refreshModel: true, replace: true },
|
||||
filter: { refreshModel: true }
|
||||
},
|
||||
|
||||
refreshQueryWithoutTransition: true,
|
||||
|
||||
titleToken() {
|
||||
return I18n.t('groups.index.title');
|
||||
},
|
||||
|
||||
queryParams: {
|
||||
order: { refreshModel: true, replace: true },
|
||||
asc: { refreshModel: true, replace: true },
|
||||
filter: { refreshModel: true },
|
||||
type: { refreshModel: true, replace: true },
|
||||
},
|
||||
|
||||
refreshQueryWithoutTransition: true,
|
||||
|
||||
model(params) {
|
||||
return this.store.findAll('group', params);
|
||||
this._params = params;
|
||||
return this.store.findAll("group", params);
|
||||
},
|
||||
|
||||
setupController(controller, model) {
|
||||
controller.set('model', model);
|
||||
}
|
||||
controller.setProperties({
|
||||
model,
|
||||
filterInput: this._params.filter
|
||||
});
|
||||
},
|
||||
});
|
||||
|
@ -3,7 +3,13 @@
|
||||
|
||||
{{text-field value=filterInput
|
||||
placeholderKey="groups.filter_name"
|
||||
class="group-filter-name no-blur"}}
|
||||
class="groups-name-filter no-blur"}}
|
||||
|
||||
{{combo-box value=type
|
||||
content=types
|
||||
clearable=true
|
||||
none="groups.index.all_groups"
|
||||
class="groups-type-filter"}}
|
||||
|
||||
{{#if model}}
|
||||
{{#conditional-loading-spinner condition=model.loading}}
|
||||
@ -56,7 +62,7 @@
|
||||
showLogin='showLogin'}}
|
||||
|
||||
{{d-button icon="ban"
|
||||
label=(if group.automatic 'groups.automatic_group' 'groups.closed_group')
|
||||
label=(if group.automatic 'groups.automatic_group' 'groups.close_group')
|
||||
disabled=true}}
|
||||
{{/group-membership-button}}
|
||||
</td>
|
||||
|
@ -5,8 +5,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.group-filter-name {
|
||||
display: inline-block;
|
||||
.groups-name-filter, .groups-type-filter {
|
||||
float: right;
|
||||
}
|
||||
|
||||
@ -34,6 +33,10 @@
|
||||
padding: 0.8em;
|
||||
}
|
||||
|
||||
td.groups-info {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
td.group-user-status {
|
||||
i {
|
||||
color: $primary;
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
.group-nav {
|
||||
li {
|
||||
float: left;
|
||||
|
@ -3,7 +3,7 @@
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.group-filter-name {
|
||||
.groups-name-filter {
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,29 @@ class GroupsController < ApplicationController
|
||||
skip_before_action :preload_json, :check_xhr, only: [:posts_feed, :mentions_feed]
|
||||
skip_before_action :check_xhr, only: [:show]
|
||||
|
||||
TYPE_FILTERS = {
|
||||
my: Proc.new { |groups, current_user|
|
||||
raise Discourse::NotFound unless current_user
|
||||
Group.member_of(groups, current_user)
|
||||
},
|
||||
owner: Proc.new { |groups, current_user|
|
||||
raise Discourse::NotFound unless current_user
|
||||
Group.owner_of(groups, current_user)
|
||||
},
|
||||
public: Proc.new { |groups|
|
||||
groups.where(public_admission: true, automatic: false)
|
||||
},
|
||||
close: Proc.new { |groups|
|
||||
groups.where(
|
||||
public_admission: false,
|
||||
automatic: false
|
||||
)
|
||||
},
|
||||
automatic: Proc.new { |groups|
|
||||
groups.where(automatic: true)
|
||||
}
|
||||
}
|
||||
|
||||
def index
|
||||
unless SiteSetting.enable_group_directory?
|
||||
raise Discourse::InvalidAccess.new(:enable_group_directory)
|
||||
@ -28,9 +51,12 @@ class GroupsController < ApplicationController
|
||||
groups = Group.search_groups(filter, groups: groups)
|
||||
end
|
||||
|
||||
type_filters = TYPE_FILTERS.keys
|
||||
|
||||
unless guardian.is_staff?
|
||||
# hide automatic groups from all non stuff to de-clutter page
|
||||
groups = groups.where(automatic: false)
|
||||
type_filters.delete(:automatic)
|
||||
end
|
||||
|
||||
count = groups.count
|
||||
@ -40,6 +66,10 @@ class GroupsController < ApplicationController
|
||||
Group.preload_custom_fields(groups, Group.preloaded_custom_field_names)
|
||||
end
|
||||
|
||||
if type = params[:type]&.to_sym
|
||||
groups = TYPE_FILTERS[type].call(groups, current_user)
|
||||
end
|
||||
|
||||
if current_user
|
||||
group_users = GroupUser.where(group: groups, user: current_user)
|
||||
user_group_ids = group_users.pluck(:group_id)
|
||||
@ -52,8 +82,11 @@ class GroupsController < ApplicationController
|
||||
user_group_ids: user_group_ids || [],
|
||||
owner_group_ids: owner_group_ids || []
|
||||
),
|
||||
extras: {
|
||||
type_filters: current_user ? type_filters : type_filters - [:my, :owner]
|
||||
},
|
||||
total_rows_groups: count,
|
||||
load_more_groups: groups_path(page: page + 1)
|
||||
load_more_groups: groups_path(page: page + 1, type: type),
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -561,6 +561,16 @@ class Group < ActiveRecord::Base
|
||||
STAFF_GROUPS.include?(self.name.to_sym)
|
||||
end
|
||||
|
||||
def self.member_of(groups, user)
|
||||
groups.joins(
|
||||
"LEFT JOIN group_users gu ON gu.group_id = groups.id
|
||||
").where("gu.user_id = ?", user.id)
|
||||
end
|
||||
|
||||
def self.owner_of(groups, user)
|
||||
self.member_of(groups, user).where("gu.owner")
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def name_format_validator
|
||||
|
@ -435,7 +435,7 @@ en:
|
||||
filter_name: "filter by group name"
|
||||
message: "Message"
|
||||
automatic_group: Automatic Group
|
||||
closed_group: Closed Group
|
||||
close_group: Close Group
|
||||
is_group_user: "You are a member of this group"
|
||||
is_group_owner: "You are an owner of this group"
|
||||
allow_membership_requests: "Allow users to send membership requests to group owners"
|
||||
@ -454,6 +454,13 @@ en:
|
||||
index:
|
||||
title: "Groups"
|
||||
empty: "There are no visible groups."
|
||||
all_groups: "All Groups"
|
||||
owner_groups: "Groups I am an owner of"
|
||||
close_groups: "Close Groups"
|
||||
public_groups: "Public Groups"
|
||||
automatic_groups: "Automatic Groups"
|
||||
public_groups: "Public Groups"
|
||||
my_groups: "My Groups"
|
||||
title:
|
||||
one: "Group"
|
||||
other: "Groups"
|
||||
|
@ -5,7 +5,7 @@ describe GroupsController do
|
||||
let(:group) { Fabricate(:group, users: [user]) }
|
||||
|
||||
describe '#index' do
|
||||
let!(:staff_group) do
|
||||
let(:staff_group) do
|
||||
Fabricate(:group, name: '0000', visibility_level: Group.visibility_levels[:staff])
|
||||
end
|
||||
|
||||
@ -69,6 +69,7 @@ describe GroupsController do
|
||||
|
||||
it 'should return the right response' do
|
||||
group
|
||||
staff_group
|
||||
get "/groups.json"
|
||||
|
||||
expect(response).to be_success
|
||||
@ -81,15 +82,23 @@ describe GroupsController do
|
||||
expect(group_ids).to_not include(staff_group.id)
|
||||
expect(response_body["load_more_groups"]).to eq("/groups?page=1")
|
||||
expect(response_body["total_rows_groups"]).to eq(1)
|
||||
|
||||
expect(response_body["extras"]["type_filters"].map(&:to_sym)).to eq(
|
||||
described_class::TYPE_FILTERS.keys - [:my, :owner, :automatic]
|
||||
)
|
||||
end
|
||||
|
||||
context 'viewing as an admin' do
|
||||
it 'should display automatic groups' do
|
||||
admin = Fabricate(:admin)
|
||||
let(:admin) { Fabricate(:admin) }
|
||||
|
||||
before do
|
||||
sign_in(admin)
|
||||
group.add(admin)
|
||||
group.add_owner(admin)
|
||||
end
|
||||
|
||||
it 'should return the right response' do
|
||||
staff_group
|
||||
get "/groups.json"
|
||||
|
||||
expect(response).to be_success
|
||||
@ -104,6 +113,65 @@ describe GroupsController do
|
||||
expect(group_ids).to include(group.id, staff_group.id)
|
||||
expect(response_body["load_more_groups"]).to eq("/groups?page=1")
|
||||
expect(response_body["total_rows_groups"]).to eq(10)
|
||||
|
||||
expect(response_body["extras"]["type_filters"].map(&:to_sym)).to eq(
|
||||
described_class::TYPE_FILTERS.keys
|
||||
)
|
||||
end
|
||||
|
||||
context 'filterable by type' do
|
||||
def expect_type_to_return_right_groups(type, expected_group_ids)
|
||||
get "/groups.json", params: { type: type }
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
|
||||
response_body = JSON.parse(response.body)
|
||||
group_ids = response_body["groups"].map { |g| g["id"] }
|
||||
|
||||
expect(group_ids).to contain_exactly(*expected_group_ids)
|
||||
end
|
||||
|
||||
describe 'my groups' do
|
||||
it 'should return the right response' do
|
||||
expect_type_to_return_right_groups('my', [group.id])
|
||||
end
|
||||
end
|
||||
|
||||
describe 'owner groups' do
|
||||
it 'should return the right response' do
|
||||
group2 = Fabricate(:group)
|
||||
group3 = Fabricate(:group)
|
||||
group2.add_owner(admin)
|
||||
|
||||
expect_type_to_return_right_groups('owner', [group.id, group2.id])
|
||||
end
|
||||
end
|
||||
|
||||
describe 'automatic groups' do
|
||||
it 'should return the right response' do
|
||||
expect_type_to_return_right_groups(
|
||||
'automatic',
|
||||
Group::AUTO_GROUP_IDS.keys - [0]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'public groups' do
|
||||
it 'should return the right response' do
|
||||
group2 = Fabricate(:group, public_admission: true)
|
||||
|
||||
expect_type_to_return_right_groups('public', [group2.id])
|
||||
end
|
||||
end
|
||||
|
||||
describe 'close groups' do
|
||||
it 'should return the right response' do
|
||||
group2 = Fabricate(:group, public_admission: false)
|
||||
group3 = Fabricate(:group, public_admission: true)
|
||||
|
||||
expect_type_to_return_right_groups('close', [group.id, group2.id])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
Loading…
x
Reference in New Issue
Block a user