From 4b940dc8bd8e3b4077c12faa6f3a2ec23e870275 Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Wed, 14 Dec 2016 17:26:16 +0800 Subject: [PATCH] FEATURE: Add groups page. --- .../discourse/controllers/groups.js.es6 | 16 +++++++ .../javascripts/discourse/models/group.js.es6 | 3 +- .../discourse/routes/app-route-map.js.es6 | 2 + .../discourse/routes/groups.js.es6 | 13 ++++++ .../discourse/templates/groups.hbs | 39 ++++++++++++++++ .../discourse/widgets/hamburger-menu.js.es6 | 2 + app/assets/stylesheets/common/base/group.scss | 2 - .../stylesheets/common/base/groups.scss | 45 +++++++++++++++++++ app/controllers/groups_controller.rb | 16 +++++++ config/locales/client.en.yml | 1 + spec/fabricators/group_fabricator.rb | 2 +- spec/integration/groups_spec.rb | 18 ++++++++ .../javascripts/acceptance/groups-test.js.es6 | 16 ++++++- .../fixtures/groups-fixtures.js.es6 | 3 ++ .../helpers/create-pretender.js.es6 | 4 ++ 15 files changed, 177 insertions(+), 5 deletions(-) create mode 100644 app/assets/javascripts/discourse/controllers/groups.js.es6 create mode 100644 app/assets/javascripts/discourse/routes/groups.js.es6 create mode 100644 app/assets/javascripts/discourse/templates/groups.hbs create mode 100644 app/assets/stylesheets/common/base/groups.scss create mode 100644 test/javascripts/fixtures/groups-fixtures.js.es6 diff --git a/app/assets/javascripts/discourse/controllers/groups.js.es6 b/app/assets/javascripts/discourse/controllers/groups.js.es6 new file mode 100644 index 00000000000..d8ce08f3227 --- /dev/null +++ b/app/assets/javascripts/discourse/controllers/groups.js.es6 @@ -0,0 +1,16 @@ +import { observes } from 'ember-addons/ember-computed-decorators'; + +export default Ember.Controller.extend({ + application: Ember.inject.controller(), + + @observes("groups.canLoadMore") + _showFooter() { + this.set("application.showFooter", !this.get("groups.canLoadMore")); + }, + + actions: { + loadMore() { + this.get('groups').loadMore(); + } + } +}); diff --git a/app/assets/javascripts/discourse/models/group.js.es6 b/app/assets/javascripts/discourse/models/group.js.es6 index d3ca40baef7..38d4fb82ee2 100644 --- a/app/assets/javascripts/discourse/models/group.js.es6 +++ b/app/assets/javascripts/discourse/models/group.js.es6 @@ -1,8 +1,9 @@ import { ajax } from 'discourse/lib/ajax'; import { default as computed, observes } from "ember-addons/ember-computed-decorators"; import GroupHistory from 'discourse/models/group-history'; +import RestModel from 'discourse/models/rest'; -const Group = Discourse.Model.extend({ +const Group = RestModel.extend({ limit: 50, offset: 0, user_count: 0, diff --git a/app/assets/javascripts/discourse/routes/app-route-map.js.es6 b/app/assets/javascripts/discourse/routes/app-route-map.js.es6 index 4b23587a899..04da387763c 100644 --- a/app/assets/javascripts/discourse/routes/app-route-map.js.es6 +++ b/app/assets/javascripts/discourse/routes/app-route-map.js.es6 @@ -50,6 +50,8 @@ export default function() { this.route(defaultHomepage(), { path: '/' }); }); + this.route('groups', { resetNamespace: true }); + this.route('group', { path: '/groups/:name', resetNamespace: true }, function() { this.route('members'); this.route('posts'); diff --git a/app/assets/javascripts/discourse/routes/groups.js.es6 b/app/assets/javascripts/discourse/routes/groups.js.es6 new file mode 100644 index 00000000000..b567f7f72cf --- /dev/null +++ b/app/assets/javascripts/discourse/routes/groups.js.es6 @@ -0,0 +1,13 @@ +export default Discourse.Route.extend({ + titleToken() { + return I18n.t('groups.index'); + }, + + model(params) { + return this.store.findAll('group', params); + }, + + setupController(controller, model) { + controller.set('groups', model); + } +}); diff --git a/app/assets/javascripts/discourse/templates/groups.hbs b/app/assets/javascripts/discourse/templates/groups.hbs new file mode 100644 index 00000000000..b4563aef8dd --- /dev/null +++ b/app/assets/javascripts/discourse/templates/groups.hbs @@ -0,0 +1,39 @@ +{{#d-section pageClass="groups"}} + {{#load-more selector=".groups-table .groups-table-row" action="loadMore"}} +

{{i18n "groups.index"}}

+ +
+ + + + + + + + {{#each groups as |group|}} + + + + + {{/each}} + +
{{i18n "groups.name"}}{{i18n "groups.user_count"}}
+ {{#link-to "group.members" group.name}} + {{#if group.flair_url}} + + {{avatar-flair + flairURL=group.flair_url + flairBgColor=group.flair_bg_color + flairColor=group.flair_color + groupName=group.name}} + + {{/if}} + +

@{{group.name}}

+ {{/link-to}} +
{{group.user_count}}
+
+ {{/load-more}} + + {{conditional-loading-spinner condition=groups.loadingMore}} +{{/d-section}} diff --git a/app/assets/javascripts/discourse/widgets/hamburger-menu.js.es6 b/app/assets/javascripts/discourse/widgets/hamburger-menu.js.es6 index d670a89c6fa..452cc069891 100644 --- a/app/assets/javascripts/discourse/widgets/hamburger-menu.js.es6 +++ b/app/assets/javascripts/discourse/widgets/hamburger-menu.js.es6 @@ -103,6 +103,8 @@ export default createWidget('hamburger-menu', { links.push({ route: 'users', className: 'user-directory-link', label: 'directory.title' }); } + links.push({ route: 'groups', className: 'groups-link', label: 'groups.index' }); + if (this.siteSettings.tagging_enabled) { links.push({ route: 'tags', label: 'tagging.tags' }); } diff --git a/app/assets/stylesheets/common/base/group.scss b/app/assets/stylesheets/common/base/group.scss index 09c4eb28991..ef205ef343d 100644 --- a/app/assets/stylesheets/common/base/group.scss +++ b/app/assets/stylesheets/common/base/group.scss @@ -111,9 +111,7 @@ table.group-members { .group-details { width: 100%; -} -.group-details { span { display: inline-block; vertical-align: middle; diff --git a/app/assets/stylesheets/common/base/groups.scss b/app/assets/stylesheets/common/base/groups.scss new file mode 100644 index 00000000000..cb9e69f01ef --- /dev/null +++ b/app/assets/stylesheets/common/base/groups.scss @@ -0,0 +1,45 @@ +.groups-page { + h1 { + margin: 20px 0px; + } +} + +.groups-table { + width: 100%; + + .groups-name { + span { + display: inline-block; + vertical-align: middle; + } + + .avatar-flair { + $size: 30px; + + background-size: $size; + height: $size; + width: $size; + + i { + font-size: $size !important; + } + } + } + + th { + border-bottom: 3px solid dark-light-diff($primary, $secondary, 90%, -60%); + padding: 5px 0px 5px 5px; + color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + font-weight: normal; + } + + tr { + border-bottom: 1px solid dark-light-diff($primary, $secondary, 90%, -60%); + + td { + text-align: center; + color: dark-light-diff($primary, $secondary, 50%, -50%); + padding: 0.8em 0; + } + } +} diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index e4b5fc14331..dd0265ad5ff 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -10,6 +10,22 @@ class GroupsController < ApplicationController skip_before_filter :preload_json, :check_xhr, only: [:posts_feed, :mentions_feed] + def index + page_size = 30 + page = params[:page]&.to_i || 0 + + groups = Group.order(user_count: :desc, name: :asc) + .where(visible: true) + .offset(page * page_size) + .limit(page_size) + + render json: { + groups: serialize_data(groups, BasicGroupSerializer), + total_rows_groups: Group.count, + load_more_groups: groups_path(page: page + 1) + } + end + def show render_serialized(find_group(:id), GroupShowSerializer, root: 'basic_group') end diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index ba337b8443e..257ab7fbe22 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -417,6 +417,7 @@ en: request: "Request to Join Group" allow_membership_requests: "Allow users to send membership requests to group owners (Requires everyone to be able to mention the group)" name: "Name" + user_count: "Number of Members" bio: "About Group" selector_placeholder: "Add members" owner: "owner" diff --git a/spec/fabricators/group_fabricator.rb b/spec/fabricators/group_fabricator.rb index 0ec42da4c5e..d190d520e69 100644 --- a/spec/fabricators/group_fabricator.rb +++ b/spec/fabricators/group_fabricator.rb @@ -1,3 +1,3 @@ Fabricator(:group) do - name 'my_group' + name { sequence(:name) { |n| "my_group_#{n}" } } end diff --git a/spec/integration/groups_spec.rb b/spec/integration/groups_spec.rb index fbf620c4a38..cf2f0547777 100644 --- a/spec/integration/groups_spec.rb +++ b/spec/integration/groups_spec.rb @@ -12,6 +12,24 @@ describe "Groups" do expect(response).to be_success end + describe 'viewing groups' do + it 'should return the right response' do + group.update_attributes!(visible: true) + other_group = Fabricate(:group, name: '0000', visible: true) + + get "/groups.json" + + expect(response).to be_success + + response_body = JSON.parse(response.body) + + group_ids = response_body["groups"].map { |g| g["id"] } + + expect(group_ids).to include(group.id, other_group.id) + expect(response_body["load_more_groups"]).to eq("/groups?page=1") + end + end + describe "checking if a group can be mentioned" do it "should return the right response" do sign_in(user) diff --git a/test/javascripts/acceptance/groups-test.js.es6 b/test/javascripts/acceptance/groups-test.js.es6 index 6152326f52c..04c8e6f5bff 100644 --- a/test/javascripts/acceptance/groups-test.js.es6 +++ b/test/javascripts/acceptance/groups-test.js.es6 @@ -3,6 +3,20 @@ import { acceptance, logIn } from "helpers/qunit-helpers"; acceptance("Groups"); test("Browsing Groups", () => { + visit("/groups"); + + andThen(() => { + equal(count('.groups-table-row'), 18, 'it displays visible groups'); + }); + + click("a[href='/groups/discourse/members']"); + + andThen(() => { + equal(find('.group-header').text().trim(), 'Awesome Team', "it displays the group page"); + }); +}); + +test("Viewing Group", () => { visit("/groups/discourse"); andThen(() => { @@ -34,7 +48,7 @@ test("Browsing Groups", () => { }); }); -test("Admin Browsing Groups", () => { +test("Admin Viewing Group", () => { logIn(); Discourse.reset(); diff --git a/test/javascripts/fixtures/groups-fixtures.js.es6 b/test/javascripts/fixtures/groups-fixtures.js.es6 new file mode 100644 index 00000000000..078ffbd8578 --- /dev/null +++ b/test/javascripts/fixtures/groups-fixtures.js.es6 @@ -0,0 +1,3 @@ +export default { + "/groups.json": {"groups":[{"id":1,"automatic":true,"name":"admins","user_count":2,"alias_level":0,"visible":true,"automatic_membership_email_domains":null,"automatic_membership_retroactive":false,"primary_group":false,"title":null,"grant_trust_level":null,"incoming_email":null,"has_messages":false,"flair_url":null,"flair_bg_color":null,"flair_color":null,"bio_raw":null,"bio_cooked":null,"public":false,"allow_membership_requests":false,"full_name":null},{"id":3,"automatic":true,"name":"staff","user_count":2,"alias_level":0,"visible":true,"automatic_membership_email_domains":null,"automatic_membership_retroactive":false,"primary_group":false,"title":null,"grant_trust_level":null,"incoming_email":null,"has_messages":false,"flair_url":null,"flair_bg_color":null,"flair_color":null,"bio_raw":null,"bio_cooked":null,"public":false,"allow_membership_requests":false,"full_name":null},{"id":10,"automatic":true,"name":"trust_level_0","user_count":2,"alias_level":0,"visible":true,"automatic_membership_email_domains":null,"automatic_membership_retroactive":false,"primary_group":false,"title":null,"grant_trust_level":null,"incoming_email":null,"has_messages":false,"flair_url":null,"flair_bg_color":null,"flair_color":null,"bio_raw":null,"bio_cooked":null,"public":false,"allow_membership_requests":false,"full_name":null},{"id":11,"automatic":true,"name":"trust_level_1","user_count":2,"alias_level":0,"visible":true,"automatic_membership_email_domains":null,"automatic_membership_retroactive":false,"primary_group":false,"title":null,"grant_trust_level":null,"incoming_email":null,"has_messages":false,"flair_url":null,"flair_bg_color":null,"flair_color":null,"bio_raw":null,"bio_cooked":null,"public":false,"allow_membership_requests":false,"full_name":null},{"id":12,"automatic":true,"name":"trust_level_2","user_count":2,"alias_level":0,"visible":true,"automatic_membership_email_domains":null,"automatic_membership_retroactive":false,"primary_group":false,"title":null,"grant_trust_level":null,"incoming_email":null,"has_messages":false,"flair_url":null,"flair_bg_color":null,"flair_color":null,"bio_raw":null,"bio_cooked":null,"public":false,"allow_membership_requests":false,"full_name":null},{"id":13,"automatic":true,"name":"trust_level_3","user_count":2,"alias_level":0,"visible":true,"automatic_membership_email_domains":null,"automatic_membership_retroactive":false,"primary_group":false,"title":null,"grant_trust_level":null,"incoming_email":null,"has_messages":false,"flair_url":null,"flair_bg_color":null,"flair_color":null,"bio_raw":null,"bio_cooked":null,"public":false,"allow_membership_requests":false,"full_name":null},{"id":14,"automatic":true,"name":"trust_level_4","user_count":2,"alias_level":0,"visible":true,"automatic_membership_email_domains":null,"automatic_membership_retroactive":false,"primary_group":false,"title":null,"grant_trust_level":null,"incoming_email":null,"has_messages":false,"flair_url":null,"flair_bg_color":null,"flair_color":null,"bio_raw":null,"bio_cooked":null,"public":false,"allow_membership_requests":false,"full_name":null},{"id":2,"automatic":true,"name":"moderators","user_count":1,"alias_level":0,"visible":true,"automatic_membership_email_domains":null,"automatic_membership_retroactive":false,"primary_group":false,"title":null,"grant_trust_level":null,"incoming_email":null,"has_messages":false,"flair_url":null,"flair_bg_color":null,"flair_color":null,"bio_raw":null,"bio_cooked":null,"public":false,"allow_membership_requests":false,"full_name":null},{"id":41,"automatic":false,"name":"my_group_0","user_count":0,"alias_level":0,"visible":true,"automatic_membership_email_domains":null,"automatic_membership_retroactive":false,"primary_group":false,"title":null,"grant_trust_level":null,"incoming_email":null,"has_messages":false,"flair_url":null,"flair_bg_color":null,"flair_color":null,"bio_raw":null,"bio_cooked":null,"public":false,"allow_membership_requests":false,"full_name":null},{"id":42,"automatic":false,"name":"my_group_1","user_count":0,"alias_level":0,"visible":true,"automatic_membership_email_domains":null,"automatic_membership_retroactive":false,"primary_group":false,"title":null,"grant_trust_level":null,"incoming_email":null,"has_messages":false,"flair_url":null,"flair_bg_color":null,"flair_color":null,"bio_raw":null,"bio_cooked":null,"public":false,"allow_membership_requests":false,"full_name":null},{"id":43,"automatic":false,"name":"my_group_2","user_count":0,"alias_level":0,"visible":true,"automatic_membership_email_domains":null,"automatic_membership_retroactive":false,"primary_group":false,"title":null,"grant_trust_level":null,"incoming_email":null,"has_messages":false,"flair_url":null,"flair_bg_color":null,"flair_color":null,"bio_raw":null,"bio_cooked":null,"public":false,"allow_membership_requests":false,"full_name":null},{"id":44,"automatic":false,"name":"my_group_3","user_count":0,"alias_level":0,"visible":true,"automatic_membership_email_domains":null,"automatic_membership_retroactive":false,"primary_group":false,"title":null,"grant_trust_level":null,"incoming_email":null,"has_messages":false,"flair_url":null,"flair_bg_color":null,"flair_color":null,"bio_raw":null,"bio_cooked":null,"public":false,"allow_membership_requests":false,"full_name":null},{"id":45,"automatic":false,"name":"my_group_4","user_count":0,"alias_level":0,"visible":true,"automatic_membership_email_domains":null,"automatic_membership_retroactive":false,"primary_group":false,"title":null,"grant_trust_level":null,"incoming_email":null,"has_messages":false,"flair_url":null,"flair_bg_color":null,"flair_color":null,"bio_raw":null,"bio_cooked":null,"public":false,"allow_membership_requests":false,"full_name":null},{"id":46,"automatic":false,"name":"my_group_5","user_count":0,"alias_level":0,"visible":true,"automatic_membership_email_domains":null,"automatic_membership_retroactive":false,"primary_group":false,"title":null,"grant_trust_level":null,"incoming_email":null,"has_messages":false,"flair_url":null,"flair_bg_color":null,"flair_color":null,"bio_raw":null,"bio_cooked":null,"public":false,"allow_membership_requests":false,"full_name":null},{"id":47,"automatic":false,"name":"my_group_6","user_count":0,"alias_level":0,"visible":true,"automatic_membership_email_domains":null,"automatic_membership_retroactive":false,"primary_group":false,"title":null,"grant_trust_level":null,"incoming_email":null,"has_messages":false,"flair_url":null,"flair_bg_color":null,"flair_color":null,"bio_raw":null,"bio_cooked":null,"public":false,"allow_membership_requests":false,"full_name":null},{"id":48,"automatic":false,"name":"my_group_7","user_count":0,"alias_level":0,"visible":true,"automatic_membership_email_domains":null,"automatic_membership_retroactive":false,"primary_group":false,"title":null,"grant_trust_level":null,"incoming_email":null,"has_messages":false,"flair_url":null,"flair_bg_color":null,"flair_color":null,"bio_raw":null,"bio_cooked":null,"public":false,"allow_membership_requests":false,"full_name":null},{"id":49,"automatic":false,"name":"my_group_8","user_count":0,"alias_level":0,"visible":true,"automatic_membership_email_domains":null,"automatic_membership_retroactive":false,"primary_group":false,"title":null,"grant_trust_level":null,"incoming_email":null,"has_messages":false,"flair_url":null,"flair_bg_color":null,"flair_color":null,"bio_raw":null,"bio_cooked":null,"public":false,"allow_membership_requests":false,"full_name":null},{"id":50,"automatic":false,"name":"discourse","user_count":0,"alias_level":0,"visible":true,"automatic_membership_email_domains":null,"automatic_membership_retroactive":false,"primary_group":false,"title":null,"grant_trust_level":null,"incoming_email":null,"has_messages":false,"flair_url":null,"flair_bg_color":null,"flair_color":null,"bio_raw":null,"bio_cooked":null,"public":false,"allow_membership_requests":false,"full_name":null}],"total_rows_groups":19,"load_more_groups":"/groups?page=1"} +} diff --git a/test/javascripts/helpers/create-pretender.js.es6 b/test/javascripts/helpers/create-pretender.js.es6 index fe1b4d1a294..c7f2f8cbceb 100644 --- a/test/javascripts/helpers/create-pretender.js.es6 +++ b/test/javascripts/helpers/create-pretender.js.es6 @@ -242,6 +242,10 @@ export default function() { slug: request.params.slug } }); }); + this.get("groups", () => { + return response(200, fixturesByUrl['/groups.json']); + }); + this.get("/groups/discourse/topics.json", () => { return response(200, fixturesByUrl['/groups/discourse/posts.json']); });