Each user activity filter has its own URL now.

This commit is contained in:
Robin Ward 2013-07-16 16:16:37 -04:00
parent 3238e0caa4
commit 19f3a8d640
28 changed files with 388 additions and 277 deletions

View File

@ -4,7 +4,7 @@
<div class='field'>{{i18n user.username.title}}</div>
<div class='value'>{{username}}</div>
<div class='controls'>
{{#linkTo 'user.activity' content class="btn"}}
{{#linkTo 'userActivity' class="btn"}}
<i class='icon icon-user'></i>
{{i18n admin.user.show_public_profile}}
{{/linkTo}}

View File

@ -28,6 +28,26 @@ Discourse.computed = {
}).property(p1, p2);
},
/**
Returns i18n version of a string based on a property.
@method i18n
@params {String} properties* to format
@params {String} format the i18n format string
@return {Function} computedProperty function
**/
i18n: function() {
var args = Array.prototype.slice.call(arguments, 0);
var format = args.pop();
var computed = Ember.computed(function() {
var context = this;
return I18n.t(format.fmt.apply(format, args.map(function (a) {
return context.get(a);
})));
});
return computed.property.apply(computed, args);
},
/**
Uses an Ember String `fmt` call to format a string. See:
http://emberjs.com/api/classes/Ember.String.html#method_fmt
@ -71,4 +91,5 @@ Discourse.computed = {
}
};

View File

@ -1,3 +1,37 @@
Discourse.UserActivityRoute = Discourse.Route.extend({
renderTemplate: function() {
this.render('user_activity', {into: 'user', outlet: 'userOutlet' });
},
model: function() {
return this.modelFor('user');
}
});
Discourse.UserActivityIndexRoute = Discourse.Route.extend({
model: function() {
return this.modelFor('user').findStream();
},
setupController: function(controller, model) {
this.controllerFor('userActivity').set('stream', model);
}
});
Object.keys(Discourse.UserAction.TYPES).forEach(function (userAction) {
Discourse["UserActivity" + userAction.classify() + "Route"] = Discourse.UserActivityIndexRoute.extend({
model: function() {
return this.modelFor('user').findStream(Discourse.UserAction.TYPES[userAction]);
}
});
});
Discourse.UserIndexRoute = Discourse.Route.extend({
redirect: function() {
this.transitionTo('userActivity.index');
}
});
/**
This controller supports all actions on a user's activity stream
@ -6,7 +40,7 @@
@namespace Discourse
@module Discourse
**/
Discourse.UserActivityController = Discourse.ObjectController.extend({
Discourse.UserActivityController = Discourse.Controller.extend({
needs: ['composer'],
kickOffPrivateMessage: (function() {

View File

@ -1,3 +1,68 @@
/**
Base route for showing private messages
@class UserPrivateMessagesRoute
@extends Discourse.Route
@namespace Discourse
@module Discourse
**/
Discourse.UserPrivateMessagesRoute = Discourse.Route.extend({
renderTemplate: function() {
this.render('user_private_messages', {into: 'user', outlet: 'userOutlet' });
},
model: function() {
return this.modelFor('user');
},
setupController: function(controller, user) {
var composerController = this.controllerFor('composer');
controller.set('model', user);
Discourse.Draft.get('new_private_message').then(function(data) {
if (data.draft) {
composerController.open({
draft: data.draft,
draftKey: 'new_private_message',
ignoreIfChanged: true,
draftSequence: data.draft_sequence
});
}
});
}
});
/**
Default private messages route
@class UserPrivateMessagesIndexRoute
@extends Discourse.Route
@namespace Discourse
@module Discourse
**/
Discourse.UserPrivateMessagesIndexRoute = Discourse.Route.extend({
model: function() {
return this.modelFor('user').findStream(Discourse.UserAction.TYPES.messages_received);
},
setupController: function(controller, model) {
this.controllerFor('userPrivateMessages').set('stream', model);
}
});
/**
Private messages sent route
@class UserPrivateMessagesSentRoute
@extends Discourse.Route
@namespace Discourse
@module Discourse
**/
Discourse.UserPrivateMessagesSentRoute = Discourse.UserPrivateMessagesIndexRoute.extend({
model: function() {
return this.modelFor('user').findStream(Discourse.UserAction.TYPES.messages_sent);
}
});
/**
This controller handles actions related to a user's private messages.

View File

@ -270,8 +270,12 @@ Discourse.User = Discourse.Model.extend({
},
findStream: function(filter) {
if (Discourse.UserAction.statGroups[filter]) {
filter = Discourse.UserAction.statGroups[filter].join(",");
// When filtering for replies, include mentions and quotes too
if (filter === Discourse.UserAction.TYPES.replies) {
filter = [Discourse.UserAction.TYPES.replies,
Discourse.UserAction.TYPES.mentions,
Discourse.UserAction.TYPES.quotes].join(",");
}
var stream = Discourse.UserStream.create({
@ -351,7 +355,7 @@ Discourse.User.reopenClass({
groupStats: function(stats) {
var responses = Discourse.UserActionStat.create({
count: 0,
action_type: Discourse.UserAction.RESPONSE
action_type: Discourse.UserAction.TYPES.replies
});
stats.filterProperty('isResponse').forEach(function (stat) {
@ -363,7 +367,7 @@ Discourse.User.reopenClass({
var insertAt = 1;
result.forEach(function(item, index){
if(item.action_type === Discourse.UserAction.NEW_TOPIC || item.action_type === Discourse.UserAction.POST){
if(item.action_type === Discourse.UserAction.TYPES.topics || item.action_type === Discourse.UserAction.TYPES.posts){
insertAt = index + 1;
}
});

View File

@ -6,82 +6,100 @@
@namespace Discourse
@module Discourse
**/
var UserActionTypes = {
likes_given: 1,
likes_received: 2,
bookmarks: 3,
topics: 4,
posts: 5,
replies: 6,
mentions: 7,
quotes: 9,
favorites: 10,
edits: 11,
messages_sent: 12,
messages_received: 13
};
var InvertedActionTypes = {};
_.each(UserActionTypes, function (k, v) {
InvertedActionTypes[k] = v;
});
Discourse.UserAction = Discourse.Model.extend({
descriptionHtml: function() {
var action = this.get('action_type');
var ua = Discourse.UserAction;
var actions = [ua.LIKE, ua.WAS_LIKED, ua.STAR, ua.EDIT, ua.BOOKMARK, ua.GOT_PRIVATE_MESSAGE, ua.NEW_PRIVATE_MESSAGE];
var icon = "";
var sentence = "";
var sameUser = (this.get('username') === Discourse.User.current('username'));
/**
Return an i18n key we will use for the description text of a user action.
if (action === null || actions.indexOf(action) >= 0) {
@property descriptionKey
**/
descriptionKey: function() {
var action = this.get('action_type');
if (action === null || Discourse.UserAction.TO_SHOW.indexOf(action) >= 0) {
if (this.get('isPM')) {
icon = '<i class="icon icon-envelope" title="{{i18n user.stream.private_message}}"></i>';
if (sameUser) {
sentence = I18n.t('user_action.sent_by_you', { userUrl: this.get('userUrl') });
} else {
sentence = I18n.t('user_action.sent_by_user', { user: this.get('name'), userUrl: this.get('userUrl') });
}
return this.get('sameUser') ? 'sent_by_you' : 'sent_by_user';
} else {
if (sameUser) {
sentence = I18n.t('user_action.posted_by_you', { userUrl: this.get('userUrl') });
} else {
sentence = I18n.t('user_action.posted_by_user', { user: this.get('name'), userUrl: this.get('userUrl') });
}
return this.get('sameUser') ? 'posted_by_you' : 'posted_by_user';
}
} else if (action === ua.NEW_TOPIC) {
if (sameUser) {
sentence = I18n.t('user_action.you_posted_topic', { userUrl: this.get('userUrl'), topicUrl: this.get('replyUrl') });
} else {
sentence = I18n.t('user_action.user_posted_topic', { user: this.get('name'), userUrl: this.get('userUrl'), topicUrl: this.get('replyUrl') });
}
} else if (action === ua.POST || action === ua.RESPONSE) {
if (this.get('reply_to_post_number')) {
if (sameUser) {
sentence = I18n.t('user_action.you_replied_to_post', { post_number: '#' + this.get('reply_to_post_number'),
userUrl: this.get('userUrl'), postUrl: this.get('postUrl') });
} else {
sentence = I18n.t('user_action.user_replied_to_post', { user: this.get('name'),
post_number: '#' + this.get('reply_to_post_number'), userUrl: this.get('userUrl'), postUrl: this.get('postUrl') });
}
} else {
if (sameUser) {
sentence = I18n.t('user_action.you_replied_to_topic', { userUrl: this.get('userUrl'),
topicUrl: this.get('replyUrl') });
} else {
sentence = I18n.t('user_action.user_replied_to_topic', { user: this.get('name'),
userUrl: this.get('userUrl'), topicUrl: this.get('replyUrl') });
}
}
} else if (action === ua.MENTION) {
if (sameUser) {
sentence = I18n.t('user_action.you_mentioned_user', { user: this.get('target_name'),
user1Url: this.get('userUrl'), user2Url: this.get('targetUserUrl') });
} else {
if (this.get('target_username') === Discourse.User.current('username')) {
sentence = I18n.t('user_action.user_mentioned_you', { user: this.get('name'),
user1Url: this.get('userUrl'), user2Url: this.get('targetUserUrl') });
} else {
sentence = I18n.t('user_action.user_mentioned_user', { user: this.get('name'),
another_user: this.get('target_name'), user1Url: this.get('userUrl'), user2Url: this.get('targetUserUrl') });
}
}
} else {
return "";
}
return new Handlebars.SafeString(icon + " " + sentence);
}.property(),
if (this.get('topicType')) {
return this.get('sameUser') ? 'you_posted_topic' : 'user_posted_topic';
}
targetUserUrl: function() {
return Discourse.Utilities.userUrl(this.get('target_username'));
}.property(),
if (this.get('postReplyType')) {
if (this.get('reply_to_post_number')) {
return this.get('sameUser') ? 'you_replied_to_post' : 'user_replied_to_post';
} else {
return this.get('sameUser') ? 'you_replied_to_topic' : 'user_replied_to_topic';
}
}
userUrl: function() {
return Discourse.Utilities.userUrl(this.get('username'));
}.property(),
if (this.get('mentionType')) {
if (this.get('sameUser')) {
return 'you_mentioned_user';
} else {
return this.get('targetUser') ? 'user_mentioned_you' : 'user_mentioned_user';
}
}
}.property('action_type'),
/**
Returns the HTML representation of a user action's description, complete with icon.
@property descriptionHtml
**/
descriptionHtml: function() {
var descriptionKey = this.get('descriptionKey');
if (!descriptionKey) { return; }
var icon = this.get('isPM') ? '<i class="icon icon-envelope" title="{{i18n user.stream.private_message}}"></i>' : '';
return new Handlebars.SafeString(icon + " " + I18n.t("user_action." + descriptionKey, {
userUrl: this.get('userUrl'),
replyUrl: this.get('replyUrl'),
postUrl: this.get('postUrl'),
topicUrl: this.get('replyUrl'),
user: this.get('name'),
post_number: '#' + this.get('reply_to_post_number'),
user1Url: this.get('userUrl'),
user2Url: this.get('targetUserUrl'),
another_user: this.get('target_name')
}));
}.property('descriptionKey'),
sameUser: function() {
return this.get('username') === Discourse.User.current('username');
}.property('username'),
targetUser: function() {
return this.get('target_username') === Discourse.User.current('username');
}.property('target_username'),
targetUserUrl: Discourse.computed.url('target_username', '/users/%@'),
userUrl: Discourse.computed.url('username', '/users/%@'),
postUrl: function() {
return Discourse.Utilities.postUrl(this.get('slug'), this.get('topic_id'), this.get('post_number'));
@ -91,15 +109,14 @@ Discourse.UserAction = Discourse.Model.extend({
return Discourse.Utilities.postUrl(this.get('slug'), this.get('topic_id'), this.get('reply_to_post_number'));
}.property(),
isPM: function() {
var a = this.get('action_type');
return a === Discourse.UserAction.NEW_PRIVATE_MESSAGE || a === Discourse.UserAction.GOT_PRIVATE_MESSAGE;
}.property(),
isPostAction: function() {
var a = this.get('action_type');
return a === Discourse.UserAction.RESPONSE || a === Discourse.UserAction.POST || a === Discourse.UserAction.NEW_TOPIC;
}.property(),
replyType: Em.computed.equal('action_type', UserActionTypes.replies),
postType: Em.computed.equal('action_type', UserActionTypes.posts),
topicType: Em.computed.equal('action_type', UserActionTypes.topics),
messageSentType: Em.computed.equal('action_type', UserActionTypes.messages_sent),
messageReceivedType: Em.computed.equal('action_type', UserActionTypes.messages_received),
mentionType: Em.computed.equal('action_type', UserActionTypes.mentions),
isPM: Em.computed.or('messageSentType', 'messageReceivedType'),
postReplyType: Em.computed.or('postType', 'replyType'),
addChild: function(action) {
var groups = this.get("childGroups");
@ -113,17 +130,16 @@ Discourse.UserAction = Discourse.Model.extend({
}
this.set("childGroups", groups);
var ua = Discourse.UserAction;
var bucket = (function() {
switch (action.action_type) {
case ua.LIKE:
case ua.WAS_LIKED:
case UserActionTypes.likes_given:
case UserActionTypes.likes_received:
return "likes";
case ua.STAR:
case UserActionTypes.favorites:
return "stars";
case ua.EDIT:
case UserActionTypes.edits:
return "edits";
case ua.BOOKMARK:
case UserActionTypes.bookmarks:
return "bookmarks";
}
})();
@ -145,28 +161,29 @@ Discourse.UserAction = Discourse.Model.extend({
}.property("childGroups"),
switchToActing: function() {
this.set('username', this.get('acting_username'));
this.set('avatar_template', this.get('acting_avatar_template'));
this.set('name', this.get('acting_name'));
this.setProperties({
username: this.get('acting_username'),
avatar_template: this.get('acting_avatar_template'),
name: this.get('acting_name')
});
}
});
Discourse.UserAction.reopenClass({
collapseStream: function(stream) {
var collapse, collapsed, pos, uniq;
collapse = [this.LIKE, this.WAS_LIKED, this.STAR, this.EDIT, this.BOOKMARK];
uniq = {};
collapsed = Em.A();
pos = 0;
_.each(stream, function(item) {
var current, found, key;
key = "" + item.topic_id + "-" + item.post_number;
found = uniq[key];
var uniq = {},
collapsed = Em.A(),
pos = 0;
stream.forEach(function(item) {
var key = "" + item.topic_id + "-" + item.post_number;
var found = uniq[key];
if (found === void 0) {
if (collapse.indexOf(item.action_type) >= 0) {
var current;
if (Discourse.UserAction.TO_COLLAPSE.indexOf(item.action_type) >= 0) {
current = Discourse.UserAction.create(item);
current.set('action_type', null);
current.set('description', null);
current.setProperties({action_type: null, description: null});
item.switchToActing();
current.addChild(item);
} else {
@ -176,39 +193,37 @@ Discourse.UserAction.reopenClass({
collapsed[pos] = current;
pos += 1;
} else {
if (collapse.indexOf(item.action_type) >= 0) {
if (Discourse.UserAction.TO_COLLAPSE.indexOf(item.action_type) >= 0) {
item.switchToActing();
return collapsed[found].addChild(item);
collapsed[found].addChild(item);
} else {
collapsed[found].set('action_type', item.get('action_type'));
return collapsed[found].set('description', item.get('description'));
collapsed[found].setProperties(item.getProperties('action_type', 'description'));
}
}
});
return collapsed;
},
// in future we should be sending this through from the server
LIKE: 1,
WAS_LIKED: 2,
BOOKMARK: 3,
NEW_TOPIC: 4,
POST: 5,
RESPONSE: 6,
MENTION: 7,
QUOTE: 9,
STAR: 10,
EDIT: 11,
NEW_PRIVATE_MESSAGE: 12,
GOT_PRIVATE_MESSAGE: 13
});
TYPES: UserActionTypes,
TYPES_INVERTED: InvertedActionTypes,
TO_COLLAPSE: [UserActionTypes.likes_given,
UserActionTypes.likes_received,
UserActionTypes.favorites,
UserActionTypes.edits,
UserActionTypes.bookmarks],
TO_SHOW: [
UserActionTypes.likes_given,
UserActionTypes.likes_received,
UserActionTypes.favorites,
UserActionTypes.edits,
UserActionTypes.bookmarks,
UserActionTypes.messages_sent,
UserActionTypes.messages_received
]
Discourse.UserAction.reopenClass({
statGroups: (function() {
var g = {};
g[Discourse.UserAction.RESPONSE] = [Discourse.UserAction.RESPONSE, Discourse.UserAction.MENTION, Discourse.UserAction.QUOTE];
return g;
})()
});

View File

@ -10,19 +10,17 @@ Discourse.UserActionStat = Discourse.Model.extend({
isPM: function() {
var actionType = this.get('action_type');
return actionType === Discourse.UserAction.NEW_PRIVATE_MESSAGE ||
actionType === Discourse.UserAction.GOT_PRIVATE_MESSAGE;
return actionType === Discourse.UserAction.TYPES.messages_sent ||
actionType === Discourse.UserAction.TYPES.messages_received;
}.property('action_type'),
description: function() {
return I18n.t('user_action_groups.' + this.get('action_type'));
}.property('description'),
description: Discourse.computed.i18n('action_type', 'user_action_groups.%@'),
isResponse: function() {
var actionType = this.get('action_type');
return actionType === Discourse.UserAction.RESPONSE ||
actionType === Discourse.UserAction.MENTION ||
actionType === Discourse.UserAction.QUOTE;
return actionType === Discourse.UserAction.TYPES.replies ||
actionType === Discourse.UserAction.TYPES.mentions ||
actionType === Discourse.UserAction.TYPES.quotes;
}.property('action_type')
});

View File

@ -40,12 +40,24 @@ Discourse.Route.buildRoutes(function() {
// User routes
this.resource('user', { path: '/users/:username' }, function() {
this.route('activity', { path: '/' });
this.route('index', { path: '/'} );
this.resource('userActivity', { path: '/activity' }, function() {
var resource = this;
Object.keys(Discourse.UserAction.TYPES).forEach(function (userAction) {
resource.route(userAction, { path: userAction.replace("_", "-") });
});
});
this.resource('userPrivateMessages', { path: '/private-messages' }, function() {
this.route('sent', {path: '/messages-sent'});
});
this.resource('preferences', { path: '/preferences' }, function() {
this.route('username', { path: '/username' });
this.route('email', { path: '/email' });
});
this.route('privateMessages', { path: '/private-messages' });
this.route('invited', { path: 'invited' });
});
});

View File

@ -11,7 +11,7 @@ Discourse.RestrictedUserRoute = Discourse.Route.extend({
afterModel: function() {
var user = this.modelFor('user');
if (!user.get('can_edit')) {
this.transitionTo('user.activity', user);
this.transitionTo('userActivity');
}
}

View File

@ -1,21 +0,0 @@
/**
This route handles shows a user's activity
@class UserActivityRoute
@extends Discourse.Route
@namespace Discourse
@module Discourse
**/
Discourse.UserActivityRoute = Discourse.Route.extend({
model: function() {
return this.modelFor('user').findStream();
},
renderTemplate: function() {
this.render({ into: 'user', outlet: 'userOutlet' });
}
});

View File

@ -1,37 +0,0 @@
/**
This route displays a user's private messages.
@class UserPrivateMessagesRoute
@extends Discourse.RestrictedUserRoute
@namespace Discourse
@module Discourse
**/
Discourse.UserPrivateMessagesRoute = Discourse.RestrictedUserRoute.extend({
model: function() {
return this.modelFor('user').findStream(Discourse.UserAction.GOT_PRIVATE_MESSAGE);
},
renderTemplate: function() {
this.render({ into: 'user', outlet: 'userOutlet' });
},
setupController: function(controller, stream) {
var composerController = this.controllerFor('composer');
controller.set('model', stream);
Discourse.Draft.get('new_private_message').then(function(data) {
if (data.draft) {
composerController.open({
draft: data.draft,
draftKey: 'new_private_message',
ignoreIfChanged: true,
draftSequence: data.draft_sequence
});
}
});
}
});

View File

@ -85,7 +85,7 @@
</li>
<li class='current-user'>
{{#if currentUser}}
{{#titledLinkTo 'user.activity' currentUser titleKey="current_user" class="icon"}}{{avatar currentUser imageSize="medium" }}{{/titledLinkTo}}
{{#titledLinkTo 'user' currentUser titleKey="current_user" class="icon"}}{{avatar currentUser imageSize="medium" }}{{/titledLinkTo}}
{{else}}
<div class="icon not-logged-in-avatar" {{action showLogin}}><i class='icon-user'></i></div>
{{/if}}

View File

@ -1,4 +1,4 @@
{{#with user}}
{{#with model}}
<div id='user-info'>
<nav class='buttons'>
{{#if can_edit}}
@ -15,9 +15,9 @@
<div class='clearfix'></div>
<ul class='action-list nav-stacked side-nav'>
{{activityFilter count=statsCountNonPM}}
{{#each statsExcludingPms}}
{{activityFilter content=this}}
{{activityFilter count=statsCountNonPM user=this}}
{{#each stat in statsExcludingPms}}
{{activityFilter content=stat user=model}}
{{/each}}
</ul>
<div class='show'>
@ -35,7 +35,7 @@
<dt>{{i18n user.last_seen}}:</dt><dd>{{date last_seen_at}}</dd>
{{/if}}
{{#if invited_by}}
<dt>{{i18n user.invited_by}}:</dt><dd>{{#linkTo 'user.activity' invited_by}}{{invited_by.username}}{{/linkTo}}</dd>
<dt>{{i18n user.invited_by}}:</dt><dd>{{#linkTo 'userActivity' invited_by}}{{invited_by.username}}{{/linkTo}}</dd>
{{/if}}
{{#if email}}
<dt>{{i18n user.email.title}}:</dt><dd {{bindAttr title="email"}}>{{email}}</dd>
@ -53,4 +53,4 @@
</div>
{{/with}}
{{userStream stream=model}}
{{userStream stream=stream}}

View File

@ -1,5 +1,5 @@
<div id='user-info'>
{{#with user}}
{{#with model}}
<nav class='buttons'>
{{#if can_edit}}
{{#linkTo "preferences" class="btn"}}{{i18n user.edit}}{{/linkTo}}
@ -15,11 +15,12 @@
<div class='clearfix'></div>
<ul class='action-list nav-stacked side-nav'>
{{#each statsPmsOnly}}
{{activityFilter content=this}}
{{#each stat in statsPmsOnly}}
{{activityFilter content=stat user=model}}
{{/each}}
</ul>
{{/with}}
</div>
{{userStream stream=model}}
{{userStream stream=stream}}

View File

@ -1,6 +1,6 @@
<div id='user-stream'>
{{#each view.stream.content}}
<div {{bindAttr class=":item hidden:hidden deleted:deleted"}}>
<div {{bindAttr class=":item hidden deleted"}}>
<div class='clearfix info'>
<a href="{{unbound userUrl}}" class='avatar-link'><div class='avatar-wrapper'>{{avatar this imageSize="large" extraClasses="actor" ignoreTitle="true"}}</div></a>
<span class='time'>{{date path="created_at" leaveAgo="true"}}</span>

View File

@ -12,11 +12,11 @@
{{/if}}
<ul class="nav nav-pills">
<li>
{{#linkTo 'user.activity'}}{{i18n user.activity_stream}}{{/linkTo}}
{{#linkTo 'userActivity'}}{{i18n user.activity_stream}}{{/linkTo}}
</li>
{{#if canSeePrivateMessages}}
<li>
{{#linkTo "user.privateMessages"}}{{i18n user.private_messages}}{{/linkTo}}
{{#linkTo 'userPrivateMessages'}}{{i18n user.private_messages}}{{/linkTo}}
</li>
{{/if}}
<li>

View File

@ -30,7 +30,6 @@ Discourse.ActionsHistoryView = Discourse.View.extend({
if (c.get('usersExpanded')) {
var postUrl;
c.get('users').forEach(function(u) {
console.log(u);
iconsHtml += "<a href=\"" + Discourse.getURL("/users/") + (u.get('username_lower')) + "\">";
if (u.post_url) {
postUrl = postUrl || u.post_url;

View File

@ -10,9 +10,8 @@ Discourse.ActivityFilterView = Discourse.View.extend({
tagName: 'li',
classNameBindings: ['active', 'noGlyph'],
stream: Em.computed.alias('controller.content'),
stream: Em.computed.alias('controller.stream'),
shouldRerender: Discourse.View.renderIfChanged('count'),
noGlyph: Em.computed.empty('icon'),
active: function() {
@ -24,62 +23,57 @@ Discourse.ActivityFilterView = Discourse.View.extend({
}
}.property('stream.filter', 'content.action_type'),
activityCount: function() {
return this.get('content.count') || this.get('count');
}.property('content.count', 'count'),
typeKey: function() {
var actionType = this.get('content.action_type');
if (actionType === Discourse.UserAction.TYPES.messages_received) { return ""; }
var result = Discourse.UserAction.TYPES_INVERTED[actionType];
if (!result) { return ""; }
// We like our URLS to have hyphens, not underscores
return "/" + result.replace("_", "-");
}.property('content.action_type'),
url: function() {
var section = this.get('content.isPM') ? "/private-messages" : "/activity";
return "/users/" + this.get('user.username_lower') + section + this.get('typeKey');
}.property('typeKey'),
description: function() {
return this.get('content.description') || I18n.t("user.filters.all");
}.property('content.description'),
render: function(buffer) {
var content = this.get("content");
var count, description;
if (content) {
count = Em.get(content, "count");
description = Em.get(content, "description");
} else {
count = this.get("count");
description = I18n.t("user.filters.all");
}
buffer.push("<a href='" + this.get('url') + "'>");
var icon = this.get('icon');
buffer.push("<a href='#'>");
if(icon) {
if (icon) {
buffer.push("<i class='glyph icon icon-" + icon + "'></i> ");
}
buffer.push(description +
" <span class='count'>(" + count + ")</span>");
buffer.push(this.get('description') + " <span class='count'>(" + this.get('activityCount') + ")</span>");
buffer.push("<span class='icon-chevron-right'></span></a>");
},
icon: function(){
var action_type = parseInt(this.get("content.action_type"),10);
var icon;
switch(action_type){
case Discourse.UserAction.WAS_LIKED:
icon = "heart";
break;
case Discourse.UserAction.BOOKMARK:
icon = "bookmark";
break;
case Discourse.UserAction.EDIT:
icon = "pencil";
break;
case Discourse.UserAction.RESPONSE:
icon = "reply";
break;
case Discourse.UserAction.STAR:
icon = "star";
break;
switch(parseInt(this.get('content.action_type'),10)) {
case Discourse.UserAction.TYPES.likes_received:
return "heart";
case Discourse.UserAction.TYPES.bookmarks:
return "bookmark";
case Discourse.UserAction.TYPES.edits:
return "pencil";
case Discourse.UserAction.TYPES.replies:
return "reply";
case Discourse.UserAction.TYPES.favorites:
return "star";
}
}.property("content.action_type")
return icon;
}.property("content.action_type"),
click: function() {
this.set('stream.filter', this.get('content.action_type'));
return false;
}
});
Discourse.View.registerHelper('activityFilter', Discourse.ActivityFilterView);

View File

@ -18,12 +18,12 @@ Discourse.UserPrivateMessagesView = Discourse.View.extend({
inbox: function(evt) {
this.selectCurrent(evt);
return this.set('controller.filter', Discourse.UserAction.GOT_PRIVATE_MESSAGE);
return this.set('controller.filter', Discourse.UserAction.TYPES.messages_received);
},
sentMessages: function(evt) {
this.selectCurrent(evt);
return this.set('controller.filter', Discourse.UserAction.NEW_PRIVATE_MESSAGE);
return this.set('controller.filter', Discourse.UserAction.TYPES.messages_sent);
}
});

View File

@ -11,15 +11,17 @@
// Stuff we need to load first
//= require_tree ./discourse/mixins
//= require ./discourse/components/computed
//= require ./discourse/views/view
//= require ./discourse/components/debounce
//= require ./discourse/models/model
//= require ./discourse/models/user_action
//= require ./discourse/controllers/controller
//= require ./discourse/controllers/object_controller
//= require ./discourse/views/modal/modal_body_view
//= require ./discourse/views/combobox_view
//= require ./discourse/views/buttons/button_view
//= require ./discourse/views/buttons/dropdown_button_view
//= require ./discourse/models/model
//= require ./discourse/routes/discourse_route
//= require ./discourse/routes/discourse_restricted_user_route

View File

@ -119,6 +119,7 @@ Discourse::Application.routes.draw do
get 'user_preferences' => 'users#user_preferences_redirect'
get 'users/:username/private-messages' => 'user_actions#private_messages', constraints: {username: USERNAME_ROUTE_FORMAT}
get 'users/:username/private-messages/:filter' => 'user_actions#private_messages', constraints: {username: USERNAME_ROUTE_FORMAT}
get 'users/:username' => 'users#show', constraints: {username: USERNAME_ROUTE_FORMAT}
put 'users/:username' => 'users#update', constraints: {username: USERNAME_ROUTE_FORMAT}
get 'users/:username/preferences' => 'users#preferences', constraints: {username: USERNAME_ROUTE_FORMAT}, as: :email_preferences
@ -129,6 +130,8 @@ Discourse::Application.routes.draw do
get 'users/:username/avatar(/:size)' => 'users#avatar', constraints: {username: USERNAME_ROUTE_FORMAT}
get 'users/:username/invited' => 'users#invited', constraints: {username: USERNAME_ROUTE_FORMAT}
post 'users/:username/send_activation_email' => 'users#send_activation_email', constraints: {username: USERNAME_ROUTE_FORMAT}
get 'users/:username/activity' => 'users#show', constraints: {username: USERNAME_ROUTE_FORMAT}
get 'users/:username/activity/:filter' => 'users#show', constraints: {username: USERNAME_ROUTE_FORMAT}
resources :uploads

View File

@ -6,7 +6,14 @@ task 'integration:create_fixtures' => :environment do
fixtures = {
list: ["/latest.json", "/categories.json", "/category/bug.json"],
topic: ["/t/280.json"],
user: ["/users/eviltrout.json", "/user_actions.json?offset=0&username=eviltrout"],
user: ["/users/eviltrout.json",
"/user_actions.json?offset=0&username=eviltrout",
"/user_actions.json?offset=0&username=eviltrout&filter=4",
"/user_actions.json?offset=0&username=eviltrout&filter=5",
"/user_actions.json?offset=0&username=eviltrout&filter=6,7,9",
"/user_actions.json?offset=0&username=eviltrout&filter=1",
"/user_actions.json?offset=0&username=eviltrout&filter=2",
"/user_actions.json?offset=0&username=eviltrout&filter=11"],
static: ["/faq", '/tos', '/privacy']
}

View File

@ -50,7 +50,6 @@ test("fmt", function() {
test("url without a prefix", function() {
var t = testClass.create({ username: 'eviltrout' });
equal(t.get('userUrl'), "/users/eviltrout");
});
test("url with a prefix", function() {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,12 +1,21 @@
integration("User");
test("Profile", function() {
test("Activity Streams", function() {
expect(14);
visit("/users/eviltrout").then(function() {
expect(2);
var streamTest = function(url) {
visit(url).then(function() {
ok(exists(".user-heading"), "The heading is rendered");
ok(exists("#user-stream"), "The stream is rendered");
});
};
ok(exists(".user-heading"), "The heading is rendered");
ok(exists("#user-stream"), "The stream is rendered");
});
streamTest("/users/eviltrout");
streamTest("/users/eviltrout/activity/topics");
streamTest("/users/eviltrout/activity/posts");
streamTest("/users/eviltrout/activity/replies");
streamTest("/users/eviltrout/activity/likes-given");
streamTest("/users/eviltrout/activity/likes-received");
streamTest("/users/eviltrout/activity/edits");
});

View File

@ -3,17 +3,17 @@ module("Discourse.UserAction");
test("collapsing likes", function () {
var actions = Discourse.UserAction.collapseStream([
Discourse.UserAction.create({
action_type: Discourse.UserAction.LIKE,
action_type: Discourse.UserAction.TYPES.likes_given,
topic_id: 1,
user_id: 1,
post_number: 1
}), Discourse.UserAction.create({
action_type: Discourse.UserAction.EDIT,
action_type: Discourse.UserAction.TYPES.edits,
topic_id: 2,
user_id: 1,
post_number: 1
}), Discourse.UserAction.create({
action_type: Discourse.UserAction.LIKE,
action_type: Discourse.UserAction.TYPES.likes_given,
topic_id: 1,
user_id: 2,
post_number: 1