Pop up with user information when clicking avatar on topic page

This commit is contained in:
Robin Ward 2013-10-03 12:51:30 -04:00
parent 578ef2098b
commit fc00269b7f
13 changed files with 198 additions and 20 deletions

@ -105,19 +105,20 @@ Discourse = Ember.Application.createWithMixins(Discourse.Ajax, {
$('#main').on('click.discourse', 'a', function(e) {
if (e.isDefaultPrevented() || e.shiftKey || e.metaKey || e.ctrlKey) { return; }
var $currentTarget = $(e.currentTarget);
var href = $currentTarget.attr('href');
if (!href) { return; }
if (href === '#') { return; }
if ($currentTarget.attr('target')) { return; }
if ($currentTarget.data('auto-route')) { return; }
var $currentTarget = $(e.currentTarget),
href = $currentTarget.attr('href');
// If it's an ember #link-to skip it
if ($currentTarget.hasClass('ember-view')) { return; }
if ($currentTarget.hasClass('lightbox')) { return; }
if (href.indexOf("mailto:") === 0) { return; }
if (href.match(/^http[s]?:\/\//i) && !href.match(new RegExp("^http:\\/\\/" + window.location.hostname, "i"))) { return; }
if (!href ||
href === '#' ||
$currentTarget.attr('target') ||
$currentTarget.data('ember-action') ||
$currentTarget.data('auto-route') ||
$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();
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'),
/**
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.

@ -259,7 +259,6 @@ Discourse.User = Discourse.Model.extend({
json.user.invited_by = Discourse.User.create(json.user.invited_by);
}
user.setProperties(json.user);
return user;
});
@ -297,6 +296,17 @@ Discourse.User = Discourse.Model.extend({
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`
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

@ -13,6 +13,14 @@ Discourse.TopicRoute = Discourse.Route.extend({
actions: {
// 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) {
Discourse.Route.showModal(this, 'flag', post);
this.controllerFor('flag').setProperties({ selected: null });
@ -80,9 +88,10 @@ Discourse.TopicRoute = Discourse.Route.extend({
// Clear the search context
this.controllerFor('search').set('searchContext', null);
this.controllerFor('posterExpansion').set('model', null);
var topicController = this.controllerFor('topic');
var postStream = topicController.get('postStream');
var topicController = this.controllerFor('topic'),
postStream = topicController.get('postStream');
postStream.cancelFilter();
topicController.set('multiSelect', false);

@ -19,9 +19,9 @@
<div class='topic-meta-data span2'>
{{#unless userDeleted}}
<div {{bindAttr class=":contents byTopicCreator:topic-creator"}}>
<a href='{{unbound usernameUrl}}'>{{avatar this imageSize="large"}}</a>
<h3 {{bindAttr class="staff new_user"}}><a href='{{unbound usernameUrl}}'>{{breakUp username}}</a></h3>
{{#if user_title}}<div class="user-title">{{user_title}}</div>{{/if}}
<a href='{{unbound usernameUrl}}' {{action showPosterExpansion this}}>{{avatar this imageSize="large"}}</a>
<h3 {{bindAttr class="staff new_user"}}><a href='{{unbound usernameUrl}}' {{action showPosterExpansion this}}>{{breakUp username}}</a></h3>
{{#if user_title}}<div class="user-title" {{action showPosterExpansion this}}>{{user_title}}</div>{{/if}}
</div>
{{else}}
<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>
{{render share}}
{{render posterExpansion}}
{{#if currentUser.enable_quoting}}
{{render quoteButton}}
{{/if}}
@ -132,3 +134,4 @@
{{#if currentUser.staff}}
{{render topicAdminMenu content}}
{{/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[:username_filters] = [opts[:username_filters]] if opts[:username_filters].is_a?(String)
begin
@topic_view = TopicView.new(params[:id] || params[:topic_id], current_user, opts)
rescue Discourse::NotFound

@ -581,6 +581,7 @@ en:
title: Topic Rank Details
topic:
filter_to: "Toggle only posts by {{username}} in this topic"
create_in: 'Create {{categoryName}} Topic'
create: 'Create Topic'
create_long: 'Create a new Topic'

@ -25,3 +25,15 @@ test("isAllowedToUploadAFile", function() {
user.setProperties({ admin: false, moderator: true });
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();
});
});