Pop up with user information when clicking avatar on topic page
This commit is contained in:
parent
578ef2098b
commit
fc00269b7f
|
@ -105,19 +105,20 @@ Discourse = Ember.Application.createWithMixins(Discourse.Ajax, {
|
||||||
$('#main').on('click.discourse', 'a', function(e) {
|
$('#main').on('click.discourse', 'a', function(e) {
|
||||||
if (e.isDefaultPrevented() || e.shiftKey || e.metaKey || e.ctrlKey) { return; }
|
if (e.isDefaultPrevented() || e.shiftKey || e.metaKey || e.ctrlKey) { return; }
|
||||||
|
|
||||||
var $currentTarget = $(e.currentTarget);
|
var $currentTarget = $(e.currentTarget),
|
||||||
var href = $currentTarget.attr('href');
|
href = $currentTarget.attr('href');
|
||||||
if (!href) { return; }
|
|
||||||
if (href === '#') { return; }
|
|
||||||
if ($currentTarget.attr('target')) { return; }
|
|
||||||
if ($currentTarget.data('auto-route')) { return; }
|
|
||||||
|
|
||||||
// If it's an ember #link-to skip it
|
if (!href ||
|
||||||
if ($currentTarget.hasClass('ember-view')) { return; }
|
href === '#' ||
|
||||||
|
$currentTarget.attr('target') ||
|
||||||
if ($currentTarget.hasClass('lightbox')) { return; }
|
$currentTarget.data('ember-action') ||
|
||||||
if (href.indexOf("mailto:") === 0) { return; }
|
$currentTarget.data('auto-route') ||
|
||||||
if (href.match(/^http[s]?:\/\//i) && !href.match(new RegExp("^http:\\/\\/" + window.location.hostname, "i"))) { return; }
|
$currentTarget.hasClass('ember-view') ||
|
||||||
|
$currentTarget.hasClass('lightbox') ||
|
||||||
|
href.indexOf("mailto:") === 0 ||
|
||||||
|
(href.match(/^http[s]?:\/\//i) && !href.match(new RegExp("^http:\\/\\/" + window.location.hostname, "i")))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
Discourse.URL.routeTo(href);
|
Discourse.URL.routeTo(href);
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
/**
|
||||||
|
A controller for expanding information about a poster.
|
||||||
|
|
||||||
|
@class PosterExpansion
|
||||||
|
@extends Discourse.ObjectController
|
||||||
|
@namespace Discourse
|
||||||
|
@module Discourse
|
||||||
|
**/
|
||||||
|
Discourse.PosterExpansionController = Discourse.ObjectController.extend({
|
||||||
|
needs: ['topic'],
|
||||||
|
|
||||||
|
show: function(user, post) {
|
||||||
|
this.setProperties({model: user, post: post});
|
||||||
|
},
|
||||||
|
|
||||||
|
close: function() {
|
||||||
|
this.set('model', null);
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
togglePosts: function(user) {
|
||||||
|
var postStream = this.get('controllers.topic.postStream');
|
||||||
|
postStream.toggleParticipant(user.get('username'));
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
|
@ -133,7 +133,6 @@ Discourse.PostStream = Em.Object.extend({
|
||||||
|
|
||||||
hasNoFilters: Em.computed.empty('filterDesc'),
|
hasNoFilters: Em.computed.empty('filterDesc'),
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Returns the window of posts above the current set in the stream, bound to the top of the stream.
|
Returns the window of posts above the current set in the stream, bound to the top of the stream.
|
||||||
This is the collection we'll ask for when scrolling upwards.
|
This is the collection we'll ask for when scrolling upwards.
|
||||||
|
|
|
@ -259,7 +259,6 @@ Discourse.User = Discourse.Model.extend({
|
||||||
json.user.invited_by = Discourse.User.create(json.user.invited_by);
|
json.user.invited_by = Discourse.User.create(json.user.invited_by);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
user.setProperties(json.user);
|
user.setProperties(json.user);
|
||||||
return user;
|
return user;
|
||||||
});
|
});
|
||||||
|
@ -297,6 +296,17 @@ Discourse.User = Discourse.Model.extend({
|
||||||
Discourse.User.reopenClass(Discourse.Singleton, {
|
Discourse.User.reopenClass(Discourse.Singleton, {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Find a `Discourse.User` for a given username.
|
||||||
|
|
||||||
|
@method findByUsername
|
||||||
|
@returns {Promise} a promise that resolves to a `Discourse.User`
|
||||||
|
**/
|
||||||
|
findByUsername: function(username) {
|
||||||
|
var user = Discourse.User.create({username: username});
|
||||||
|
return user.findDetails();
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The current singleton will retrieve its attributes from the `PreloadStore`
|
The current singleton will retrieve its attributes from the `PreloadStore`
|
||||||
if it exists. Otherwise, no instance is created.
|
if it exists. Otherwise, no instance is created.
|
||||||
|
@ -325,7 +335,6 @@ Discourse.User.reopenClass(Discourse.Singleton, {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Checks if given username is valid for this email address
|
Checks if given username is valid for this email address
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,14 @@ Discourse.TopicRoute = Discourse.Route.extend({
|
||||||
actions: {
|
actions: {
|
||||||
// Modals that can pop up within a topic
|
// Modals that can pop up within a topic
|
||||||
|
|
||||||
|
showPosterExpansion: function(post) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
Discourse.User.findByUsername(post.get('username')).then(function (user) {
|
||||||
|
self.controllerFor('posterExpansion').show(user, post);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
showFlags: function(post) {
|
showFlags: function(post) {
|
||||||
Discourse.Route.showModal(this, 'flag', post);
|
Discourse.Route.showModal(this, 'flag', post);
|
||||||
this.controllerFor('flag').setProperties({ selected: null });
|
this.controllerFor('flag').setProperties({ selected: null });
|
||||||
|
@ -80,9 +88,10 @@ Discourse.TopicRoute = Discourse.Route.extend({
|
||||||
|
|
||||||
// Clear the search context
|
// Clear the search context
|
||||||
this.controllerFor('search').set('searchContext', null);
|
this.controllerFor('search').set('searchContext', null);
|
||||||
|
this.controllerFor('posterExpansion').set('model', null);
|
||||||
|
|
||||||
var topicController = this.controllerFor('topic');
|
var topicController = this.controllerFor('topic'),
|
||||||
var postStream = topicController.get('postStream');
|
postStream = topicController.get('postStream');
|
||||||
postStream.cancelFilter();
|
postStream.cancelFilter();
|
||||||
|
|
||||||
topicController.set('multiSelect', false);
|
topicController.set('multiSelect', false);
|
||||||
|
|
|
@ -19,9 +19,9 @@
|
||||||
<div class='topic-meta-data span2'>
|
<div class='topic-meta-data span2'>
|
||||||
{{#unless userDeleted}}
|
{{#unless userDeleted}}
|
||||||
<div {{bindAttr class=":contents byTopicCreator:topic-creator"}}>
|
<div {{bindAttr class=":contents byTopicCreator:topic-creator"}}>
|
||||||
<a href='{{unbound usernameUrl}}'>{{avatar this imageSize="large"}}</a>
|
<a href='{{unbound usernameUrl}}' {{action showPosterExpansion this}}>{{avatar this imageSize="large"}}</a>
|
||||||
<h3 {{bindAttr class="staff new_user"}}><a href='{{unbound usernameUrl}}'>{{breakUp username}}</a></h3>
|
<h3 {{bindAttr class="staff new_user"}}><a href='{{unbound usernameUrl}}' {{action showPosterExpansion this}}>{{breakUp username}}</a></h3>
|
||||||
{{#if user_title}}<div class="user-title">{{user_title}}</div>{{/if}}
|
{{#if user_title}}<div class="user-title" {{action showPosterExpansion this}}>{{user_title}}</div>{{/if}}
|
||||||
</div>
|
</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="contents">
|
<div class="contents">
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
{{#if model}}
|
||||||
|
{{avatar model imageSize="huge"}}
|
||||||
|
|
||||||
|
<h1>{{username}}</h1>
|
||||||
|
<h2>{{name}}</h2>
|
||||||
|
<h3>{{i18n last_post}}: {{unboundDate last_posted_at}}</h3>
|
||||||
|
|
||||||
|
<div class='bottom'>
|
||||||
|
{{#if bio_cooked}}<div class='bio'>{{{bio_cooked}}}</div>{{/if}}
|
||||||
|
|
||||||
|
<button class='btn'><i class='icon icon-envelope'></i>{{i18n user.private_message}}</button>
|
||||||
|
|
||||||
|
{{#link-to 'user' model class="btn"}}<i class='icon icon-user'></i>{{i18n user.profile}}{{/link-to}}
|
||||||
|
|
||||||
|
<button class='btn' {{action togglePosts this}}><i class='icon icon-filter'></i>{{i18n topic.filter_to username="username"}}</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{/if}}
|
|
@ -125,6 +125,8 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{render share}}
|
{{render share}}
|
||||||
|
{{render posterExpansion}}
|
||||||
|
|
||||||
{{#if currentUser.enable_quoting}}
|
{{#if currentUser.enable_quoting}}
|
||||||
{{render quoteButton}}
|
{{render quoteButton}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
@ -132,3 +134,4 @@
|
||||||
{{#if currentUser.staff}}
|
{{#if currentUser.staff}}
|
||||||
{{render topicAdminMenu content}}
|
{{render topicAdminMenu content}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
/**
|
||||||
|
Shows expanded details for a poster
|
||||||
|
|
||||||
|
@class PosterExpansionView
|
||||||
|
@namespace Discourse
|
||||||
|
@module Discourse
|
||||||
|
**/
|
||||||
|
Discourse.PosterExpansionView = Discourse.View.extend({
|
||||||
|
elementId: 'poster-expansion',
|
||||||
|
classNameBindings: ['controller.model::hidden'],
|
||||||
|
|
||||||
|
// Position the expansion when the model changes
|
||||||
|
_modelChanged: function() {
|
||||||
|
var post = this.get('controller.post'),
|
||||||
|
self = this;
|
||||||
|
|
||||||
|
Em.run.schedule('afterRender', function() {
|
||||||
|
if (post) {
|
||||||
|
var $post = $('#' + post.get('postElementId')),
|
||||||
|
$avatar = $('.topic-meta-data img.avatar', $post),
|
||||||
|
position = $avatar.offset();
|
||||||
|
|
||||||
|
position.left += $avatar.width() + 5;
|
||||||
|
self.$().css(position);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}.observes('controller.model'),
|
||||||
|
|
||||||
|
didInsertElement: function() {
|
||||||
|
var self = this;
|
||||||
|
$('html').on('mousedown.outside-poster-expansion', function(e) {
|
||||||
|
if (self.$().has(e.target).length !== 0) { return; }
|
||||||
|
self.get('controller').set('model', null);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
willDestroyElement: function() {
|
||||||
|
$('html').off('mousedown.outside-poster-expansion');
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
|
@ -0,0 +1,51 @@
|
||||||
|
// styles that apply to the "share" popup when sharing a link to a post or topic
|
||||||
|
|
||||||
|
@import "common/foundation/variables";
|
||||||
|
@import "common/foundation/mixins";
|
||||||
|
|
||||||
|
#poster-expansion {
|
||||||
|
position: absolute;
|
||||||
|
left: 20px;
|
||||||
|
z-index: 990;
|
||||||
|
@include border-radius-all(3px);
|
||||||
|
@include box-shadow(1px 1px 5px $darkish_gray);
|
||||||
|
background-color: $white;
|
||||||
|
padding: 7px 7px 6px 7px;
|
||||||
|
width: 400px;
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 30px;
|
||||||
|
line-height: 33px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 22px;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: normal;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom {
|
||||||
|
clear: both;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
img.avatar {
|
||||||
|
float: left;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0 0 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin: 0 0 7px 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,6 +30,8 @@ class TopicsController < ApplicationController
|
||||||
|
|
||||||
opts = params.slice(:username_filters, :filter, :page, :post_number)
|
opts = params.slice(:username_filters, :filter, :page, :post_number)
|
||||||
|
|
||||||
|
opts[:username_filters] = [opts[:username_filters]] if opts[:username_filters].is_a?(String)
|
||||||
|
|
||||||
begin
|
begin
|
||||||
@topic_view = TopicView.new(params[:id] || params[:topic_id], current_user, opts)
|
@topic_view = TopicView.new(params[:id] || params[:topic_id], current_user, opts)
|
||||||
rescue Discourse::NotFound
|
rescue Discourse::NotFound
|
||||||
|
|
|
@ -581,6 +581,7 @@ en:
|
||||||
title: Topic Rank Details
|
title: Topic Rank Details
|
||||||
|
|
||||||
topic:
|
topic:
|
||||||
|
filter_to: "Toggle only posts by {{username}} in this topic"
|
||||||
create_in: 'Create {{categoryName}} Topic'
|
create_in: 'Create {{categoryName}} Topic'
|
||||||
create: 'Create Topic'
|
create: 'Create Topic'
|
||||||
create_long: 'Create a new Topic'
|
create_long: 'Create a new Topic'
|
||||||
|
|
|
@ -25,3 +25,15 @@ test("isAllowedToUploadAFile", function() {
|
||||||
user.setProperties({ admin: false, moderator: true });
|
user.setProperties({ admin: false, moderator: true });
|
||||||
ok(user.isAllowedToUploadAFile("image"), "moderator can always upload a file");
|
ok(user.isAllowedToUploadAFile("image"), "moderator can always upload a file");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
asyncTestDiscourse("findByUsername", function() {
|
||||||
|
expect(3);
|
||||||
|
|
||||||
|
Discourse.User.findByUsername('eviltrout').then(function (user) {
|
||||||
|
present(user);
|
||||||
|
equal(user.get('username'), 'eviltrout', 'it has the correct username');
|
||||||
|
equal(user.get('name'), 'Robin Ward', 'it has the full name since it has details');
|
||||||
|
start();
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue