FEATURE: User page refactor
Re-organise user page so it is easier to find interesting info split it into tabs - Introduce notifications and messages tabs - Stop couting stuff for the user page to speed up rendering - Suppress more information when viewing your own profile
This commit is contained in:
parent
c3f08145b8
commit
a8b5192efd
|
@ -0,0 +1,25 @@
|
|||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
|
||||
pmView: false,
|
||||
|
||||
privateMessagesActive: Em.computed.equal('pmView', 'index'),
|
||||
privateMessagesMineActive: Em.computed.equal('pmView', 'mine'),
|
||||
privateMessagesUnreadActive: Em.computed.equal('pmView', 'unread'),
|
||||
isGroup: Em.computed.equal('pmView', 'groups'),
|
||||
|
||||
|
||||
@computed('model.groups', 'groupFilter', 'pmView')
|
||||
groupPMStats(groups, filter, pmView) {
|
||||
if (groups) {
|
||||
return groups.filter(group => group.has_messages)
|
||||
.map(g => {
|
||||
return {
|
||||
name: g.name,
|
||||
active: (g.name === filter && pmView === "groups")
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
|
@ -6,7 +6,6 @@ import User from 'discourse/models/user';
|
|||
|
||||
export default Ember.Controller.extend(CanCheckEmails, {
|
||||
indexStream: false,
|
||||
pmView: false,
|
||||
userActionType: null,
|
||||
needs: ['user-notifications', 'user-topics-list'],
|
||||
|
||||
|
@ -28,11 +27,14 @@ export default Ember.Controller.extend(CanCheckEmails, {
|
|||
},
|
||||
|
||||
@computed('viewingSelf', 'currentUser.admin')
|
||||
canSeePrivateMessages(viewingSelf, isAdmin) {
|
||||
showPrivateMessages(viewingSelf, isAdmin) {
|
||||
return this.siteSettings.enable_private_messages && (viewingSelf || isAdmin);
|
||||
},
|
||||
|
||||
canSeeNotificationHistory: Em.computed.alias('canSeePrivateMessages'),
|
||||
@computed('viewingSelf', 'currentUser.admin')
|
||||
canSeeNotificationHistory(viewingSelf, isAdmin) {
|
||||
return viewingSelf || isAdmin;
|
||||
},
|
||||
|
||||
@computed("content.badge_count")
|
||||
showBadges(badgeCount) {
|
||||
|
@ -45,6 +47,12 @@ export default Ember.Controller.extend(CanCheckEmails, {
|
|||
(userActionType === UserAction.TYPES.messages_received);
|
||||
},
|
||||
|
||||
@computed("indexStream", "userActionType")
|
||||
showActionTypeSummary(indexStream,userActionType, showPMs) {
|
||||
return (indexStream || userActionType) && !showPMs;
|
||||
},
|
||||
|
||||
|
||||
@computed()
|
||||
canInviteToForum() {
|
||||
return User.currentProp('can_invite_to_forum');
|
||||
|
@ -64,23 +72,6 @@ export default Ember.Controller.extend(CanCheckEmails, {
|
|||
}
|
||||
},
|
||||
|
||||
privateMessagesActive: Em.computed.equal('pmView', 'index'),
|
||||
privateMessagesMineActive: Em.computed.equal('pmView', 'mine'),
|
||||
privateMessagesUnreadActive: Em.computed.equal('pmView', 'unread'),
|
||||
|
||||
@computed('model.private_messages_stats.groups', 'groupFilter', 'pmView')
|
||||
groupPMStats(stats,filter,pmView) {
|
||||
if (stats) {
|
||||
return stats.map(g => {
|
||||
return {
|
||||
name: g.name,
|
||||
count: g.count,
|
||||
active: (g.name === filter && pmView === 'groups')
|
||||
};
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
expandProfile() {
|
||||
this.set('forceExpand', true);
|
||||
|
|
|
@ -3,4 +3,5 @@ export default {
|
|||
this.controllerFor('user').set('userActionType', userActionType);
|
||||
this.controllerFor('user-activity').set('userActionType', userActionType);
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -199,6 +199,15 @@ const User = RestModel.extend({
|
|||
ua.action_type === UserAction.TYPES.topics;
|
||||
},
|
||||
|
||||
@computed("groups.@each")
|
||||
displayGroups() {
|
||||
const groups = this.get('groups');
|
||||
const filtered = groups.filter(group => {
|
||||
return !group.automatic || group.name === "moderators";
|
||||
});
|
||||
return filtered.length === 0 ? null : filtered;
|
||||
},
|
||||
|
||||
// The user's stat count, excluding PMs.
|
||||
@computed("statsExcludingPms.@each.count")
|
||||
statsCountNonPM() {
|
||||
|
@ -233,8 +242,8 @@ const User = RestModel.extend({
|
|||
}));
|
||||
}
|
||||
|
||||
if (!Em.isEmpty(json.user.custom_groups)) {
|
||||
json.user.custom_groups = json.user.custom_groups.map(g => Group.create(g));
|
||||
if (!Em.isEmpty(json.user.groups)) {
|
||||
json.user.groups = json.user.groups.map(g => Group.create(g));
|
||||
}
|
||||
|
||||
if (json.user.invited_by) {
|
||||
|
|
|
@ -58,13 +58,20 @@ export default function() {
|
|||
this.resource('users');
|
||||
this.resource('user', { path: '/users/:username' }, function() {
|
||||
this.resource('userActivity', { path: '/activity' }, function() {
|
||||
_.map(Discourse.UserAction.TYPES, (id, userAction) => {
|
||||
this.route(userAction, { path: userAction.replace('_', '-') });
|
||||
this.route('topics');
|
||||
this.route('replies');
|
||||
this.route('likesGiven', {path: 'likes-given'});
|
||||
this.route('bookmarks');
|
||||
});
|
||||
|
||||
this.resource('userNotifications', {path: '/notifications'}, function(){
|
||||
this.route('responses');
|
||||
this.route('likesReceived', { path: 'likes-received'});
|
||||
this.route('mentions');
|
||||
this.route('edits');
|
||||
});
|
||||
|
||||
this.route('badges');
|
||||
this.route('notifications');
|
||||
this.route('flaggedPosts', { path: '/flagged-posts' });
|
||||
this.route('deletedPosts', { path: '/deleted-posts' });
|
||||
|
||||
|
@ -85,6 +92,7 @@ export default function() {
|
|||
this.resource('userInvited', { path: '/invited' }, function() {
|
||||
this.route('show', { path: '/:filter' });
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
this.route('signup', {path: '/signup'});
|
||||
|
|
|
@ -24,7 +24,7 @@ export default (viewName, path) => {
|
|||
showParticipants: true
|
||||
});
|
||||
|
||||
this.controllerFor("user").set("pmView", viewName);
|
||||
this.controllerFor("userPrivateMessages").set("pmView", viewName);
|
||||
this.searchService.set('contextType', 'private_messages');
|
||||
},
|
||||
|
|
@ -2,5 +2,5 @@ import UserActivityStreamRoute from "discourse/routes/user-activity-stream";
|
|||
import UserAction from "discourse/models/user-action";
|
||||
|
||||
export default UserActivityStreamRoute.extend({
|
||||
userActionType: UserAction.TYPES["replies"]
|
||||
userActionType: UserAction.TYPES["posts"]
|
||||
});
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import Draft from 'discourse/models/draft';
|
||||
|
||||
export default Discourse.Route.extend({
|
||||
model() {
|
||||
return this.modelFor("user");
|
||||
|
@ -7,21 +5,5 @@ export default Discourse.Route.extend({
|
|||
|
||||
setupController(controller, user) {
|
||||
this.controllerFor("user-activity").set("model", user);
|
||||
|
||||
// Bring up a draft
|
||||
const composerController = this.controllerFor("composer");
|
||||
controller.set("model", user);
|
||||
if (this.currentUser) {
|
||||
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
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
export default Discourse.Route.extend({
|
||||
renderTemplate() {
|
||||
this.render("user/notifications-index");
|
||||
}
|
||||
});
|
|
@ -2,5 +2,5 @@ import UserActivityStreamRoute from "discourse/routes/user-activity-stream";
|
|||
import UserAction from "discourse/models/user-action";
|
||||
|
||||
export default UserActivityStreamRoute.extend({
|
||||
userActionType: UserAction.TYPES["likes_received"]
|
||||
userActionType: UserAction.TYPES["likes_received"],
|
||||
});
|
|
@ -2,5 +2,5 @@ import UserActivityStreamRoute from "discourse/routes/user-activity-stream";
|
|||
import UserAction from "discourse/models/user-action";
|
||||
|
||||
export default UserActivityStreamRoute.extend({
|
||||
userActionType: UserAction.TYPES["posts"]
|
||||
userActionType: UserAction.TYPES["replies"]
|
||||
});
|
|
@ -1,6 +1,11 @@
|
|||
import ViewingActionType from "discourse/mixins/viewing-action-type";
|
||||
|
||||
export default Discourse.Route.extend(ViewingActionType, {
|
||||
|
||||
renderTemplate() {
|
||||
this.render('user/notifications');
|
||||
},
|
||||
|
||||
actions: {
|
||||
didTransition() {
|
||||
this.controllerFor("user-notifications")._showFooter();
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import Group from 'discourse/models/group';
|
||||
import createPMRoute from "discourse/routes/build-user-topic-list-route";
|
||||
import createPMRoute from "discourse/routes/build-private-messages-route";
|
||||
|
||||
export default createPMRoute('groups', 'private-messages-groups').extend({
|
||||
model(params) {
|
||||
|
@ -13,13 +13,13 @@ export default createPMRoute('groups', 'private-messages-groups').extend({
|
|||
const groupName = _.last(model.get("filter").split('/'));
|
||||
Group.findAll().then(groups => {
|
||||
const group = _.first(groups.filterBy("name", groupName));
|
||||
this.controllerFor("user-topics-list").set("group", group);
|
||||
this.controllerFor("user-private-messages").set("group", group);
|
||||
});
|
||||
},
|
||||
|
||||
setupController(controller, model) {
|
||||
this._super.apply(this, arguments);
|
||||
const group = _.last(model.get("filter").split('/'));
|
||||
this.controllerFor("user").set("groupFilter", group);
|
||||
this.controllerFor("userPrivateMessages").set("groupFilter", group);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
import createPMRoute from "discourse/routes/build-user-topic-list-route";
|
||||
import createPMRoute from "discourse/routes/build-private-messages-route";
|
||||
|
||||
export default createPMRoute('index', 'private-messages');
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
import createPMRoute from "discourse/routes/build-user-topic-list-route";
|
||||
import createPMRoute from "discourse/routes/build-private-messages-route";
|
||||
|
||||
export default createPMRoute('mine', 'private-messages-sent');
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
import createPMRoute from "discourse/routes/build-user-topic-list-route";
|
||||
import createPMRoute from "discourse/routes/build-private-messages-route";
|
||||
|
||||
export default createPMRoute('unread', 'private-messages-unread');
|
||||
|
|
|
@ -1,6 +1,35 @@
|
|||
import UserActivityRoute from 'discourse/routes/user-activity';
|
||||
import Draft from 'discourse/models/draft';
|
||||
|
||||
export default Discourse.Route.extend({
|
||||
|
||||
renderTemplate() {
|
||||
this.render('user/messages');
|
||||
},
|
||||
|
||||
|
||||
model() {
|
||||
return this.modelFor("user");
|
||||
},
|
||||
|
||||
setupController(controller, user) {
|
||||
this._super();
|
||||
// Bring up a draft
|
||||
const composerController = this.controllerFor("composer");
|
||||
controller.set("model", user);
|
||||
if (this.currentUser) {
|
||||
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
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
export default UserActivityRoute.extend({
|
||||
actions: {
|
||||
willTransition: function() {
|
||||
this._super();
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
export default Discourse.Route.extend({
|
||||
});
|
|
@ -1,11 +1,3 @@
|
|||
<div class="clearfix">
|
||||
{{#if group}}
|
||||
{{group-notifications-button group=group}}
|
||||
{{/if}}
|
||||
{{#if showNewPM}}
|
||||
{{d-button class="btn-primary pull-right new-private-message" action="composePrivateMessage" icon="envelope" label="user.new_private_message"}}
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{basic-topic-list topicList=model
|
||||
hideCategory=hideCategory
|
||||
|
|
|
@ -1 +1,42 @@
|
|||
<section class='user-navigation'>
|
||||
<ul class='action-list nav-stacked'>
|
||||
|
||||
<li class='no-glyph'>
|
||||
{{#link-to 'userActivity.index'}}{{i18n 'user.filters.all'}}{{/link-to}}
|
||||
</li>
|
||||
|
||||
<li class='no-glyph'>
|
||||
{{#link-to 'userActivity.topics'}}{{i18n 'user_action_groups.4'}}{{/link-to}}
|
||||
</li>
|
||||
|
||||
<li>
|
||||
{{#link-to 'userActivity.replies'}}
|
||||
<i class="glyph fa fa-reply"></i>{{i18n 'user_action_groups.5'}}
|
||||
{{/link-to}}
|
||||
</li>
|
||||
|
||||
<li>
|
||||
{{#link-to 'userActivity.likesGiven'}}
|
||||
<i class="glyph fa fa-heart"></i>{{i18n 'user_action_groups.1'}}
|
||||
{{/link-to}}
|
||||
</li>
|
||||
|
||||
<li>
|
||||
{{#link-to 'userActivity.bookmarks'}}
|
||||
<i class="glyph fa fa-bookmark"></i>{{i18n 'user_action_groups.3'}}
|
||||
{{/link-to}}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
{{#if viewingSelf}}
|
||||
<div class='user-archive'>
|
||||
{{d-button action="exportUserArchive" label="user.download_archive" icon="download"}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
</section>
|
||||
|
||||
<section class='user-right'>
|
||||
{{outlet}}
|
||||
</section>
|
||||
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
<section class='user-navigation'>
|
||||
<ul class='action-list nav-stacked'>
|
||||
<li {{bind-attr class=":noGlyph privateMessagesActive:active"}}>
|
||||
{{#link-to 'userPrivateMessages.index' model}}
|
||||
{{i18n 'user.messages.all'}}
|
||||
{{#if model.hasPMs}}<span class='count'>({{model.private_messages_stats.all}})</span>{{/if}}
|
||||
{{/link-to}}
|
||||
</li>
|
||||
<li {{bind-attr class=":noGlyph privateMessagesMineActive:active"}}>
|
||||
{{#link-to 'userPrivateMessages.mine' model}}
|
||||
{{i18n 'user.messages.mine'}}
|
||||
{{#if model.hasStartedPMs}}<span class='count'>({{model.private_messages_stats.mine}})</span>{{/if}}
|
||||
{{/link-to}}
|
||||
</li>
|
||||
<li {{bind-attr class=":noGlyph privateMessagesUnreadActive:active"}}>
|
||||
{{#link-to 'userPrivateMessages.unread' model}}
|
||||
{{i18n 'user.messages.unread'}}
|
||||
{{#if model.hasUnreadPMs}}<span class='badge-notification unread-private-messages'>{{model.private_messages_stats.unread}}</span>{{/if}}
|
||||
{{/link-to}}
|
||||
</li>
|
||||
{{#each groupPMStats as |group|}}
|
||||
<li class="{{if group.active "active"}}">
|
||||
{{#link-to 'userPrivateMessages.group' group.name}}
|
||||
<i class='glyph fa fa-group'></i>
|
||||
{{group.name}}
|
||||
{{/link-to}}
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
|
||||
{{d-button class="btn-primary new-private-message" action="composePrivateMessage" icon="envelope" label="user.new_private_message"}}
|
||||
</section>
|
||||
|
||||
<section class='user-right messages'>
|
||||
|
||||
{{#if isGroup}}
|
||||
<div class="clearfix">
|
||||
{{group-notifications-button group=group}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{outlet}}
|
||||
</section>
|
|
@ -0,0 +1,34 @@
|
|||
{{#if model.error}}
|
||||
<div class="item error">
|
||||
{{#if model.forbidden}}
|
||||
{{i18n 'errors.reasons.forbidden'}}
|
||||
{{else}}
|
||||
{{i18n 'errors.desc.unknown'}}
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if showDismissButton}}
|
||||
<div class='notification-buttons'>
|
||||
<button title="{{i18n 'user.dismiss_notifications_tooltip'}}" id='dismiss-notifications-top' class='btn notifications-read' {{action "resetNew"}}>{{i18n 'user.dismiss_notifications'}}</button>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#each n in model}}
|
||||
<div {{bind-attr class=":item :notification n.read::unread"}}>
|
||||
{{notification-item notification=n}}
|
||||
<span class="time">
|
||||
{{format-date n.created_at leaveAgo="true"}}
|
||||
</span>
|
||||
</div>
|
||||
{{/each}}
|
||||
|
||||
{{#conditional-loading-spinner condition=loading}}
|
||||
{{#unless model.canLoadMore}}
|
||||
{{#if showDismissButton}}
|
||||
<div class='notification-buttons'>
|
||||
<button title="{{i18n 'user.dismiss_notifications_tooltip'}}" id='dismiss-notifications' class='btn notifications-read' {{action "resetNew"}}>{{i18n 'user.dismiss_notifications'}}</button>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
{{/conditional-loading-spinner}}
|
|
@ -1,34 +1,25 @@
|
|||
{{#if model.error}}
|
||||
<div class="item error">
|
||||
{{#if model.forbidden}}
|
||||
{{i18n 'errors.reasons.forbidden'}}
|
||||
{{else}}
|
||||
{{i18n 'errors.desc.unknown'}}
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if showDismissButton}}
|
||||
<div class='notification-buttons'>
|
||||
<button title="{{i18n 'user.dismiss_notifications_tooltip'}}" id='dismiss-notifications-top' class='btn notifications-read' {{action "resetNew"}}>{{i18n 'user.dismiss_notifications'}}</button>
|
||||
</div>
|
||||
{{/if}}
|
||||
<section class='user-navigation'>
|
||||
<ul class='action-list nav-stacked'>
|
||||
<li class='no-glyph'>
|
||||
{{#link-to 'userNotifications.index'}}{{i18n 'user.filters.all'}}{{/link-to}}
|
||||
</li>
|
||||
<li>
|
||||
{{#link-to 'userNotifications.responses'}}
|
||||
<i class="glyph fa fa-reply"></i>
|
||||
{{i18n 'user_action_groups.6'}}
|
||||
{{/link-to}}
|
||||
</li>
|
||||
<li>
|
||||
{{#link-to 'userNotifications.likesReceived'}}
|
||||
<i class="glyph fa fa-heart"></i>{{i18n 'user_action_groups.2'}}
|
||||
{{/link-to}}
|
||||
</li>
|
||||
<li>{{#link-to 'userNotifications.mentions'}}<i class="glyph fa fa-at"></i>{{i18n 'user_action_groups.7'}}{{/link-to}}</li>
|
||||
<li>{{#link-to 'userNotifications.edits'}}<i class="glyph fa fa-pencil"></i>{{i18n 'user_action_groups.11'}}{{/link-to}}</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
{{#each n in model}}
|
||||
<div {{bind-attr class=":item :notification n.read::unread"}}>
|
||||
{{notification-item notification=n}}
|
||||
<span class="time">
|
||||
{{format-date n.created_at leaveAgo="true"}}
|
||||
</span>
|
||||
</div>
|
||||
{{/each}}
|
||||
|
||||
{{#conditional-loading-spinner condition=loading}}
|
||||
{{#unless model.canLoadMore}}
|
||||
{{#if showDismissButton}}
|
||||
<div class='notification-buttons'>
|
||||
<button title="{{i18n 'user.dismiss_notifications_tooltip'}}" id='dismiss-notifications' class='btn notifications-read' {{action "resetNew"}}>{{i18n 'user.dismiss_notifications'}}</button>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
{{/conditional-loading-spinner}}
|
||||
<section class='user-right'>
|
||||
{{outlet}}
|
||||
</section>
|
||||
|
|
|
@ -237,14 +237,7 @@
|
|||
{{category-group categories=model.mutedCategories blacklist=selectedCategories}}
|
||||
</div>
|
||||
<div class="instructions">{{i18n 'user.muted_categories_instructions'}}</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group topics">
|
||||
<label class="control-label">{{i18n 'categories.topics'}}</label>
|
||||
{{#if siteSettings.automatically_unpin_topics}}
|
||||
{{preference-checkbox labelKey="user.automatically_unpin_topics" checked=model.automatically_unpin_topics}}
|
||||
{{/if}}
|
||||
<div class="controls topic-controls">
|
||||
<div class="controls category-controls">
|
||||
<a href="{{unbound model.mutedTopicsPath}}">{{i18n 'user.muted_topics_link'}}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -258,6 +251,13 @@
|
|||
<div class="instructions">{{i18n 'user.muted_users_instructions'}}</div>
|
||||
</div>
|
||||
|
||||
{{#if siteSettings.automatically_unpin_topics}}
|
||||
<div class="control-group topics">
|
||||
<label class="control-label">{{i18n 'categories.topics'}}</label>
|
||||
{{preference-checkbox labelKey="user.automatically_unpin_topics" checked=model.automatically_unpin_topics}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{plugin-outlet "user-custom-controls"}}
|
||||
|
||||
<div class="control-group">
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<div class="container{{if viewingSelf ' viewing-self'}}">
|
||||
<section class='user-main'>
|
||||
<section {{bind-attr class="collapsedInfo :about model.profileBackground:has-background:no-background"}} style={{model.profileBackground}}>
|
||||
{{#unless collapsedInfo}}
|
||||
<div class='staff-counters'>
|
||||
{{#if model.number_of_flags_given}}
|
||||
<div><span class="helpful-flags">{{model.number_of_flags_given}}</span> {{i18n 'user.staff_counters.flags_given'}}</div>
|
||||
|
@ -26,6 +27,8 @@
|
|||
<div><span class="warnings-received">{{model.number_of_warnings}}</span> {{i18n 'user.staff_counters.warnings_received'}}</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/unless}}
|
||||
|
||||
<div class='profile-image'></div>
|
||||
<div class='details'>
|
||||
<div class='primary'>
|
||||
|
@ -40,18 +43,9 @@
|
|||
</a>
|
||||
</li>
|
||||
{{/if}}
|
||||
{{#if viewingSelf}}
|
||||
<li><a {{action "logout"}} href class='btn btn-danger'>{{fa-icon "sign-out"}}{{i18n 'user.log_out'}}</a></li>
|
||||
{{/if}}
|
||||
{{#if currentUser.staff}}
|
||||
<li><a href={{model.adminPath}} class="btn">{{fa-icon "wrench"}}{{i18n 'admin.user.show_admin_profile'}}</a></li>
|
||||
{{/if}}
|
||||
{{#if model.can_edit}}
|
||||
<li>{{#link-to 'preferences' class="btn"}}{{fa-icon "cog"}}{{i18n 'user.preferences'}}{{/link-to}}</li>
|
||||
{{/if}}
|
||||
{{#if canInviteToForum}}
|
||||
<li>{{#link-to 'userInvited' class="btn"}}{{fa-icon "user-plus"}}{{i18n 'user.invited.title'}}{{/link-to}}</li>
|
||||
{{/if}}
|
||||
{{#if collapsedInfo}}
|
||||
{{#if viewingSelf}}
|
||||
<li><a {{action "expandProfile"}} href class="btn">{{fa-icon "angle-double-down"}}{{i18n 'user.expand_profile'}}</a></li>
|
||||
|
@ -109,6 +103,8 @@
|
|||
<div style='clear: both'></div>
|
||||
</div>
|
||||
|
||||
|
||||
{{#unless collapsedInfo}}
|
||||
<div class='secondary'>
|
||||
<dl>
|
||||
{{#if model.created_at}}
|
||||
|
@ -135,10 +131,10 @@
|
|||
{{/if}}
|
||||
</dd>
|
||||
{{/if}}
|
||||
{{#if model.custom_groups}}
|
||||
<dt>{{i18n 'groups.title' count=model.custom_groups.length}}</dt>
|
||||
{{#if model.displayGroups}}
|
||||
<dt>{{i18n 'groups.title' count=model.displayGroups.length}}</dt>
|
||||
<dd class='groups'>
|
||||
{{#each group in model.custom_groups}}
|
||||
{{#each group in model.displayGroups}}
|
||||
<span>{{#link-to 'group' group class="group-link"}}{{group.name}}{{/link-to}}</span>
|
||||
{{/each}}
|
||||
</dd>
|
||||
|
@ -149,76 +145,34 @@
|
|||
</dl>
|
||||
{{plugin-outlet "user-profile-secondary"}}
|
||||
</div>
|
||||
{{/unless}}
|
||||
</section>
|
||||
|
||||
<section class='user-navigation'>
|
||||
<ul class='action-list nav-stacked'>
|
||||
{{activity-filter count=model.statsCountNonPM user=model userActionType=userActionType indexStream=indexStream}}
|
||||
{{#each stat in model.statsExcludingPms}}
|
||||
{{activity-filter content=stat user=model userActionType=userActionType indexStream=indexStream}}
|
||||
{{/each}}
|
||||
{{#if showBadges}}
|
||||
{{#link-to 'user.badges' tagName="li"}}
|
||||
{{#link-to 'user.badges'}}
|
||||
<i class='glyph fa fa-certificate'></i>
|
||||
{{i18n 'badges.title'}}
|
||||
<span class='count'>({{model.badge_count}})</span>
|
||||
{{/link-to}}
|
||||
{{/link-to}}
|
||||
{{/if}}
|
||||
<ul class="user-nav">
|
||||
<li class='selected'>{{#link-to 'userActivity'}}{{i18n 'user.activity_stream'}}{{/link-to}}</li>
|
||||
{{#if canSeeNotificationHistory}}
|
||||
{{#link-to 'user.notifications' tagName="li"}}
|
||||
{{#link-to 'user.notifications'}}
|
||||
<li>
|
||||
{{#link-to 'userNotifications'}}
|
||||
{{fa-icon "comment" class="glyph"}}
|
||||
{{i18n 'user.notifications'}}
|
||||
{{/link-to}}
|
||||
{{/link-to}}
|
||||
</li>
|
||||
{{/if}}
|
||||
{{#if showPrivateMessages}}
|
||||
<li>{{#link-to 'userPrivateMessages'}}{{fa-icon "envelope-o"}}{{i18n 'user.private_messages'}}{{/link-to}}</li>
|
||||
{{/if}}
|
||||
{{#if canInviteToForum}}
|
||||
<li>{{#link-to 'userInvited'}}{{fa-icon "user-plus"}}{{i18n 'user.invited.title'}}{{/link-to}}</li>
|
||||
{{/if}}
|
||||
{{#if showBadges}}
|
||||
<li>{{#link-to 'user.badges'}}{{fa-icon "certificate"}}{{i18n 'badges.title'}}{{/link-to}}</li>
|
||||
{{/if}}
|
||||
{{#if model.can_edit}}
|
||||
<li>{{#link-to 'preferences'}}{{fa-icon "cog"}}{{i18n 'user.preferences'}}{{/link-to}}</li>
|
||||
{{/if}}
|
||||
</ul>
|
||||
|
||||
{{#if canSeePrivateMessages}}
|
||||
<h3>{{fa-icon "envelope"}} {{i18n 'user.private_messages'}}</h3>
|
||||
<ul class='action-list nav-stacked'>
|
||||
<li {{bind-attr class=":noGlyph privateMessagesActive:active"}}>
|
||||
{{#link-to 'userPrivateMessages.index' model}}
|
||||
{{i18n 'user.messages.all'}}
|
||||
{{#if model.hasPMs}}<span class='count'>({{model.private_messages_stats.all}})</span>{{/if}}
|
||||
{{/link-to}}
|
||||
</li>
|
||||
<li {{bind-attr class=":noGlyph privateMessagesMineActive:active"}}>
|
||||
{{#link-to 'userPrivateMessages.mine' model}}
|
||||
{{i18n 'user.messages.mine'}}
|
||||
{{#if model.hasStartedPMs}}<span class='count'>({{model.private_messages_stats.mine}})</span>{{/if}}
|
||||
{{/link-to}}
|
||||
</li>
|
||||
<li {{bind-attr class=":noGlyph privateMessagesUnreadActive:active"}}>
|
||||
{{#link-to 'userPrivateMessages.unread' model}}
|
||||
{{i18n 'user.messages.unread'}}
|
||||
{{#if model.hasUnreadPMs}}<span class='badge-notification unread-private-messages'>{{model.private_messages_stats.unread}}</span>{{/if}}
|
||||
{{/link-to}}
|
||||
</li>
|
||||
{{#each groupPMStats as |group|}}
|
||||
<li class="{{if group.active "active"}}">
|
||||
{{#link-to 'userPrivateMessages.group' group.name}}
|
||||
<i class='glyph fa fa-group'></i>
|
||||
{{group.name}}
|
||||
<span class='count'>({{group.count}})</span>
|
||||
{{/link-to}}
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{/if}}
|
||||
|
||||
{{#if viewingSelf}}
|
||||
<div class='user-archive'>
|
||||
{{d-button action="exportUserArchive" label="user.download_archive" icon="download"}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</section>
|
||||
|
||||
<section class='user-right'>
|
||||
{{outlet}}
|
||||
</section>
|
||||
|
||||
</section>
|
||||
</div>
|
||||
|
|
|
@ -131,3 +131,34 @@
|
|||
|
||||
}
|
||||
}
|
||||
|
||||
.user-nav {
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
li {
|
||||
padding: 0;
|
||||
a {
|
||||
color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 40%));
|
||||
padding: 5px;
|
||||
min-width: 90px;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
border-bottom: 3px solid transparent;
|
||||
}
|
||||
a.active, a:hover {
|
||||
color: $primary;
|
||||
border-bottom: 3px solid dark-light-choose(scale-color($primary, $lightness: 20%), scale-color($secondary, $lightness: 20%));
|
||||
}
|
||||
display: inline-block;
|
||||
text-decoration: none;
|
||||
margin: 5px 0px;
|
||||
.fa {
|
||||
margin-right: 5px;
|
||||
}
|
||||
.fa.fa-comment {
|
||||
margin-right: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -65,13 +65,13 @@
|
|||
color: $primary;
|
||||
}
|
||||
}
|
||||
.active > a,
|
||||
.active > a, & li > a.active
|
||||
{
|
||||
color: $secondary;
|
||||
background-color: $quaternary;
|
||||
}
|
||||
|
||||
.active > a::after,
|
||||
.active > a::after, & li > a.active::after
|
||||
{
|
||||
left: 90%;
|
||||
top: 33%;
|
||||
|
|
|
@ -231,7 +231,6 @@
|
|||
background-position: center center;
|
||||
background-size: cover;
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
overflow: hidden;
|
||||
|
||||
&.group {
|
||||
|
@ -642,3 +641,15 @@
|
|||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.user-right .group-notification-menu {
|
||||
float: right;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.user-right.messages .topic-list {
|
||||
thead, th.views, td.views {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,9 +33,10 @@ class UsersController < ApplicationController
|
|||
|
||||
@user = fetch_user_from_params(include_inactive: current_user.try(:staff?))
|
||||
user_serializer = UserSerializer.new(@user, scope: guardian, root: 'user')
|
||||
if params[:stats].to_s == "false"
|
||||
|
||||
# TODO remove this options from serializer
|
||||
user_serializer.omit_stats = true
|
||||
end
|
||||
|
||||
topic_id = params[:include_post_count_for].to_i
|
||||
if topic_id != 0
|
||||
user_serializer.topic_post_count = {topic_id => Post.where(topic_id: topic_id, user_id: @user.id).count }
|
||||
|
|
|
@ -141,10 +141,6 @@ class User < ActiveRecord::Base
|
|||
SiteSetting.min_username_length.to_i..SiteSetting.max_username_length.to_i
|
||||
end
|
||||
|
||||
def custom_groups
|
||||
groups.where(automatic: false, visible: true)
|
||||
end
|
||||
|
||||
def self.username_available?(username)
|
||||
lower = username.downcase
|
||||
User.where(username_lower: lower).blank? && !SiteSetting.reserved_usernames.split("|").include?(username)
|
||||
|
|
|
@ -11,7 +11,8 @@ class BasicGroupSerializer < ApplicationSerializer
|
|||
:title,
|
||||
:grant_trust_level,
|
||||
:incoming_email,
|
||||
:notification_level
|
||||
:notification_level,
|
||||
:has_messages
|
||||
|
||||
def include_incoming_email?
|
||||
scope.is_staff?
|
||||
|
|
|
@ -70,7 +70,7 @@ class UserSerializer < BasicUserSerializer
|
|||
:automatically_unpin_topics
|
||||
|
||||
has_one :invited_by, embed: :object, serializer: BasicUserSerializer
|
||||
has_many :custom_groups, embed: :object, serializer: BasicGroupSerializer
|
||||
has_many :groups, embed: :object, serializer: BasicGroupSerializer
|
||||
has_many :featured_user_badges, embed: :ids, serializer: UserBadgeSerializer, root: :user_badges
|
||||
has_one :card_badge, embed: :object, serializer: BadgeSerializer
|
||||
|
||||
|
@ -118,6 +118,14 @@ class UserSerializer < BasicUserSerializer
|
|||
### ATTRIBUTES
|
||||
###
|
||||
|
||||
def groups
|
||||
if scope.is_admin? || object.id == scope.user.try(:id)
|
||||
object.groups
|
||||
else
|
||||
object.groups.where(visible: true)
|
||||
end
|
||||
end
|
||||
|
||||
def include_email?
|
||||
object.id && object.id == scope.user.try(:id)
|
||||
end
|
||||
|
|
|
@ -378,7 +378,6 @@ en:
|
|||
"6": "Responses"
|
||||
"7": "Mentions"
|
||||
"9": "Quotes"
|
||||
"10": "Starred"
|
||||
"11": "Edits"
|
||||
"12": "Sent Items"
|
||||
"13": "Inbox"
|
||||
|
@ -448,6 +447,7 @@ en:
|
|||
invited_by: "Invited By"
|
||||
trust_level: "Trust Level"
|
||||
notifications: "Notifications"
|
||||
statistics: "Stats"
|
||||
desktop_notifications:
|
||||
label: "Desktop Notifications"
|
||||
not_supported: "Notifications are not supported on this browser. Sorry."
|
||||
|
|
|
@ -305,6 +305,7 @@ Discourse::Application.routes.draw do
|
|||
get "users/:username/activity/:filter" => "users#show", constraints: {username: USERNAME_ROUTE_FORMAT}
|
||||
get "users/:username/badges" => "users#show", constraints: {username: USERNAME_ROUTE_FORMAT}
|
||||
get "users/:username/notifications" => "users#show", constraints: {username: USERNAME_ROUTE_FORMAT}
|
||||
get "users/:username/notifications/:filter" => "users#show", constraints: {username: USERNAME_ROUTE_FORMAT}
|
||||
get "users/:username/pending" => "users#show", constraints: {username: USERNAME_ROUTE_FORMAT}
|
||||
delete "users/:username" => "users#destroy", constraints: {username: USERNAME_ROUTE_FORMAT}
|
||||
# The external_id constraint is to allow periods to be used in the value without becoming part of the format. ie: foo.bar.json
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
class AddHasMessagesToGroups < ActiveRecord::Migration
|
||||
def up
|
||||
add_column :groups, :has_messages, :boolean, default: false, null: false
|
||||
|
||||
execute <<SQL
|
||||
UPDATE groups g SET has_messages = true
|
||||
WHERE exists(SELECT group_id FROM topic_allowed_groups WHERE group_id = g.id)
|
||||
SQL
|
||||
|
||||
end
|
||||
|
||||
def down
|
||||
remove_column :groups, :has_messages
|
||||
end
|
||||
end
|
|
@ -186,6 +186,7 @@ class TopicCreator
|
|||
check_can_send_permission!(topic, group)
|
||||
topic.topic_allowed_groups.build(group_id: group.id)
|
||||
len += 1
|
||||
group.update_columns(:has_messages, true) unless group.has_messages
|
||||
end
|
||||
|
||||
rollback_with!(topic, :target_group_not_found) unless len == names.length
|
||||
|
|
Loading…
Reference in New Issue