FEATURE: show group mentions and topics in groups page

This commit is contained in:
Sam 2015-12-01 16:52:43 +11:00
parent 0cffbf87c3
commit d1a5d8ea62
25 changed files with 202 additions and 52 deletions

View File

@ -1,9 +1,49 @@
import { default as computed, observes } from 'ember-addons/ember-computed-decorators';
var Tab = Em.Object.extend({
@computed('name')
location(name) {
return 'group.' + name;
}
});
export default Ember.Controller.extend({
counts: null,
showing: null,
showing: 'posts',
// It would be nice if bootstrap marked action lists as selected when their links
// were 'active' not the `li` tags.
showingIndex: Em.computed.equal('showing', 'index'),
showingMembers: Em.computed.equal('showing', 'members')
@observes('counts')
countsChanged() {
const counts = this.get('counts');
this.get('tabs').forEach(tab => {
tab.set('count', counts.get(tab.get('name')));
});
},
@observes('showing')
showingChanged() {
const showing = this.get('showing');
this.get('tabs').forEach(tab => {
tab.set('active', showing === tab.get('name'));
});
},
tabs: [
Tab.create({
active: true,
name: 'posts',
'location': 'group.index'
}),
Tab.create({
name: 'topics'
}),
Tab.create({
name: 'mentions'
}),
Tab.create({
name: 'members'
})
]
});

View File

@ -1,10 +1,5 @@
/**
Handles displaying posts within a group
@class GroupIndexController
@extends Ember.ArrayController
@namespace Discourse
@module Discourse
**/
export default Ember.ArrayController.extend({
needs: ['group'],
@ -21,7 +16,8 @@ export default Ember.ArrayController.extend({
var lastPostId = posts[posts.length-1].get('id'),
group = this.get('controllers.group.model');
group.findPosts({beforePostId: lastPostId}).then(function(newPosts) {
var opts = {beforePostId: lastPostId, type: this.get('type')};
group.findPosts(opts).then(function(newPosts) {
posts.addObjects(newPosts);
self.set('loading', false);
});

View File

@ -0,0 +1,3 @@
import IndexController from 'discourse/controllers/group/index';
export default IndexController.extend({type: 'mentions'});

View File

@ -0,0 +1,3 @@
import IndexController from 'discourse/controllers/group/index';
export default IndexController.extend({type: 'topics'});

View File

@ -121,10 +121,12 @@ const Group = Discourse.Model.extend({
findPosts(opts) {
opts = opts || {};
const type = opts['type'] || 'posts';
var data = {};
if (opts.beforePostId) { data.before_post_id = opts.beforePostId; }
return Discourse.ajax("/groups/" + this.get('name') + "/posts.json", { data: data }).then(function (posts) {
return Discourse.ajax(`/groups/${this.get('name')}/${type}.json`, { data: data }).then(function (posts) {
return posts.map(function (p) {
p.user = Discourse.User.create(p.user);
return Em.Object.create(p);

View File

@ -48,6 +48,8 @@ export default function() {
});
this.resource('group', { path: '/groups/:name' }, function() {
this.route('topics');
this.route('mentions');
this.route('members');
});

View File

@ -9,6 +9,6 @@ export default Discourse.Route.extend({
setupController(controller, model) {
controller.set("model", model);
this.controllerFor("group").set("showing", "index");
this.controllerFor("group").set("showing", "posts");
}
});

View File

@ -0,0 +1,11 @@
export default Discourse.Route.extend({
model() {
return this.modelFor("group").findPosts({type: 'mentions'});
},
setupController(controller, model) {
controller.set("model", model);
this.controllerFor("group").set("showing", "mentions");
}
});

View File

@ -0,0 +1,11 @@
export default Discourse.Route.extend({
model() {
return this.modelFor("group").findPosts({type: 'topics'});
},
setupController(controller, model) {
controller.set("model", model);
this.controllerFor("group").set("showing", "topics");
}
});

View File

@ -0,0 +1,18 @@
<div class='item'>
<div class='clearfix info'>
<a href="{{unbound post.user.userUrl}}" data-user-card="{{unbound post.user.username}}" class='avatar-link'><div class='avatar-wrapper'>{{avatar post.user imageSize="large" extraClasses="actor" ignoreTitle="true"}}</div></a>
<span class='time'>{{format-date post.created_at leaveAgo="true"}}</span>
<span class="title">
<a href="{{unbound post.url}}">{{unbound post.title}}</a>
</span>
<span class="category">{{category-link post.category}}</span>
<div class="group-member-info">
{{#if post.user_long_name}}
<span class="name">{{post.user_long_name}}</span>{{#if post.user_title}}<span class="title">, {{post.user_title}}</span>{{/if}}
{{/if}}
</div>
</div>
<p class='excerpt'>
{{{unbound post.cooked}}}
</p>
</div>

View File

@ -1,28 +1,26 @@
<div class="container">
<section class='user-navigation'>
<ul class='action-list nav-stacked'>
<li {{bind-attr class="showingIndex:active"}}>
{{#link-to 'group.index' model}}{{i18n 'groups.posts'}}
<span class='count'>({{counts.posts}})</span>
{{/link-to}}
</li>
<li {{bind-attr class="showingMembers:active"}}>
{{#link-to 'group.members' model}}{{i18n 'groups.members'}}
<span class='count'>({{counts.members}})</span>
<section class='user-navigation'>
<ul class='action-list nav-stacked'>
{{#each tabs as |tab|}}
<li class="{{if tab.active 'active'}}">
{{#link-to tab.location model}}{{tab.name}}
{{#if tab.count}}
<span class='count'>({{tab.count}})</span>
{{/if}}
{{/link-to}}
</li>
{{/each}}
</ul>
</section>
<section class='user-main'>
<section class='user-right groups'>
<section class='about group'>
<div class='details'>
<h1>{{model.name}}</h1>
</div>
</section>
{{outlet}}
<section class='user-main'>
<section class='user-right groups'>
<section class='about group'>
<div class='details'>
<h1>{{model.name}}</h1>
</div>
</section>
{{outlet}}
</section>
</section>
</div>

View File

@ -1,22 +1,5 @@
<div class='user-stream'>
{{#each p in controller}}
<div class='item'>
<div class='clearfix info'>
<a href="{{unbound p.user.userUrl}}" data-user-card="{{unbound p.user.username}}" class='avatar-link'><div class='avatar-wrapper'>{{avatar p.user imageSize="large" extraClasses="actor" ignoreTitle="true"}}</div></a>
<span class='time'>{{format-date p.created_at leaveAgo="true"}}</span>
<span class="title">
<a href="{{unbound p.url}}">{{unbound p.title}}</a>
</span>
<span class="category">{{category-link p.category}}</span>
<div class="group-member-info">
{{#if p.user_long_name}}
<span class="name">{{p.user_long_name}}</span>{{#if p.user_title}}<span class="title">, {{p.user_title}}</span>{{/if}}
{{/if}}
</div>
</div>
<p class='excerpt'>
{{{unbound p.cooked}}}
</p>
</div>
{{#each controller as |post|}}
{{group-post post=post}}
{{/each}}
</div>

View File

@ -20,7 +20,7 @@
<td class='avatar'>{{user-small user=m}}
{{#if m.owner}}
<span class='is-owner'>{{i18n "groups.owner"}}</span>
{{/if}}
{{/if}}
</td>
<td>
<span class="text">{{bound-date m.last_posted_at}}</span>

View File

@ -0,0 +1,6 @@
<div class='user-stream'>
{{#each controller as |post|}}
{{group-post post=post}}
{{/each}}
</div>

View File

@ -0,0 +1,5 @@
<div class='user-stream'>
{{#each controller as |post|}}
{{group-post post=post}}
{{/each}}
</div>

View File

@ -0,0 +1,6 @@
import ScrollTop from 'discourse/mixins/scroll-top';
import LoadMore from "discourse/mixins/load-more";
export default Ember.View.extend(ScrollTop, LoadMore, {
eyelineSelector: '.user-stream .item',
});

View File

@ -0,0 +1,6 @@
import ScrollTop from 'discourse/mixins/scroll-top';
import LoadMore from "discourse/mixins/load-more";
export default Ember.View.extend(ScrollTop, LoadMore, {
eyelineSelector: '.user-stream .item',
});

View File

@ -7,6 +7,8 @@ class GroupsController < ApplicationController
def counts
group = find_group(:group_id)
render json: {counts: { posts: group.posts_for(guardian).count,
topics: group.posts_for(guardian).where(post_number: 1).count,
mentions: group.mentioned_posts_for(guardian).count,
members: group.users.count } }
end
@ -16,6 +18,19 @@ class GroupsController < ApplicationController
render_serialized posts.to_a, GroupPostSerializer
end
def topics
group = find_group(:group_id)
posts = group.posts_for(guardian, params[:before_post_id]).where(post_number: 1).limit(20)
render_serialized posts.to_a, GroupPostSerializer
end
def mentions
group = find_group(:group_id)
posts = group.mentioned_posts_for(guardian, params[:before_post_id]).limit(20)
render_serialized posts.to_a, GroupPostSerializer
end
def members
group = find_group(:group_id)

View File

@ -3,6 +3,7 @@ class Group < ActiveRecord::Base
has_many :category_groups, dependent: :destroy
has_many :group_users, dependent: :destroy
has_many :group_mentions, dependent: :destroy
has_many :categories, through: :category_groups
has_many :users, through: :group_users
@ -80,6 +81,18 @@ class Group < ActiveRecord::Base
result.order('posts.created_at desc')
end
def mentioned_posts_for(guardian, before_post_id=nil)
result = Post.joins(:group_mentions)
.includes(:user, :topic, :topic => :category)
.references(:posts, :topics, :category)
.where('topics.archetype <> ?', Archetype.private_message)
.where(post_type: Post.types[:regular])
result = guardian.filter_allowed_categories(result)
result = result.where('posts.id < ?', before_post_id) if before_post_id
result.order('posts.created_at desc')
end
def self.trust_group_ids
(10..19).to_a
end

View File

@ -0,0 +1,4 @@
class GroupMention < ActiveRecord::Base
belongs_to :post
belongs_to :group
end

View File

@ -32,6 +32,7 @@ class Post < ActiveRecord::Base
has_many :replies, through: :post_replies
has_many :post_actions
has_many :topic_links
has_many :group_mentions, dependent: :destroy
has_many :post_uploads
has_many :uploads, through: :post_uploads

View File

@ -64,6 +64,16 @@ class PostAlerter
notify_post_users(post, notified)
end
sync_group_mentions(post, mentioned_groups)
end
def sync_group_mentions(post, mentioned_groups)
GroupMention.where(post_id: post.id).destroy_all
return if mentioned_groups.blank?
mentioned_groups.each do |group|
GroupMention.create(post_id: post.id, group_id: group.id)
end
end
def unread_posts(user, topic)

View File

@ -342,6 +342,8 @@ Discourse::Application.routes.draw do
resources :groups do
get 'members'
get 'posts'
get 'topics'
get 'mentions'
get 'counts'
member do

View File

@ -0,0 +1,12 @@
class AddGroupMentions < ActiveRecord::Migration
def change
create_table :group_mentions do |t|
t.integer :post_id
t.integer :group_id
t.timestamps
end
add_index :group_mentions, [:post_id, :group_id], unique: true
add_index :group_mentions, [:group_id, :post_id], unique: true
end
end

View File

@ -103,12 +103,15 @@ describe PostAlerter do
create_post_with_alerts(raw: "Hello @group how are you?")
}.to change(evil_trout.notifications, :count).by(1)
expect(GroupMention.count).to eq(1)
group.update_columns(alias_level: Group::ALIAS_LEVELS[:members_mods_and_admins])
expect {
create_post_with_alerts(raw: "Hello @group you are not mentionable")
}.to change(evil_trout.notifications, :count).by(0)
expect(GroupMention.count).to eq(2)
end
end