FEATURE: Show user popup when clicking on an @mention in a post.

This commit is contained in:
Robin Ward 2014-07-23 16:10:01 -04:00
parent 04c6733ae7
commit 27f786e06a
9 changed files with 83 additions and 59 deletions

View File

@ -34,8 +34,6 @@ var PosterNameComponent = Em.Component.extend({
}
buffer.push("</span>");
// Are we showing full names?
if (name && this.get('displayNameOnPosts') && (this.sanitizeName(name) !== this.sanitizeName(username))) {
name = Handlebars.Utils.escapeExpression(name);

View File

@ -2,7 +2,9 @@ export default Discourse.ObjectController.extend({
needs: ['topic'],
visible: false,
user: null,
username: null,
participant: null,
avatar: null,
postStream: Em.computed.alias('controllers.topic.postStream'),
enoughPostsForFiltering: Em.computed.gte('participant.post_count', 2),
@ -20,22 +22,25 @@ export default Discourse.ObjectController.extend({
showMoreBadges: Em.computed.gt('moreBadgesCount', 0),
show: function(post) {
show: function(username, uploadedAvatarId) {
var url = "/users/" + username;
// Don't show on mobile
if (Discourse.Mobile.mobileView) {
Discourse.URL.routeTo(post.get('usernameUrl'));
Discourse.URL.routeTo(url);
return;
}
var currentPostId = this.get('id'),
var currentUsername = this.get('username'),
wasVisible = this.get('visible');
this.setProperties({model: post, visible: true});
this.set('avatar', {username: username, uploaded_avatar_id: uploadedAvatarId});
this.setProperties({visible: true, username: username});
// If we click the avatar again, close it.
if (post.get('id') === currentPostId && wasVisible) {
this.setProperties({ visible: false, model: null });
if (username === currentUsername && wasVisible) {
this.setProperties({ visible: false, username: null, avatar: null });
return;
}
@ -44,13 +49,14 @@ export default Discourse.ObjectController.extend({
// Retrieve their participants info
var participants = this.get('topic.details.participants');
if (participants) {
this.set('participant', participants.findBy('username', post.get('username')));
this.set('participant', participants.findBy('username', username));
}
var self = this;
self.set('user', null);
Discourse.User.findByUsername(post.get('username')).then(function (user) {
Discourse.User.findByUsername(username).then(function (user) {
self.set('user', user);
self.set('avatar', user);
});
},

View File

@ -189,7 +189,7 @@ Handlebars.registerHelper('avatar', function(user, options) {
@for Handlebars
**/
Em.Handlebars.helper('bound-avatar', function(user, size, uploadId) {
if (Em.isEmpty(user)) { return; }
var username = Em.get(user, 'username');
if(arguments.length < 4){

View File

@ -14,8 +14,15 @@ Discourse.TopicRoute = Discourse.Route.extend({
actions: {
// Modals that can pop up within a topic
showPosterExpansion: function(post) {
this.controllerFor('poster-expansion').show(post);
expandPostUser: function(post) {
this.controllerFor('poster-expansion').show(post.get('username'), post.get('uploaded_avatar_id'));
},
expandPostUsername: function(username) {
username = username.replace(/^@/, '');
if (!Em.isEmpty(username)) {
this.controllerFor('poster-expansion').show(username);
}
},
composePrivateMessage: function(user) {

View File

@ -2,13 +2,13 @@
<div class='topic-avatar'>
<div class='contents'>
<div>
{{poster-avatar action="showPosterExpansion" post=this classNames="main-avatar"}}
{{poster-avatar action="expandPostUser" post=this classNames="main-avatar"}}
</div>
</div>
</div>
<div class='topic-body'>
<div class="topic-meta-data">
{{poster-name post=this expandAction="showPosterExpansion"}}
{{poster-name post=this expandAction="expandPostUser"}}
{{#if view.parentView.previousPost}}<a href='{{unbound url}}' class="post-info arrow" title="{{i18n topic.jump_reply_up}}"><i class='fa fa-arrow-up'></i></a>{{/if}}
<div class='post-info post-date'>{{age-with-tooltip created_at}}</div>
</div>

View File

@ -20,7 +20,7 @@
<div class='topic-avatar'>
{{#unless userDeleted}}
<div {{bind-attr class=":contents byTopicCreator:topic-creator"}}>
{{poster-avatar action="showPosterExpansion" post=this classNames="main-avatar"}}
{{poster-avatar action="expandPostUser" post=this classNames="main-avatar"}}
</div>
{{else}}
<div class="contents">
@ -34,7 +34,7 @@
<div class='topic-body'>
<div class='topic-meta-data'>
{{poster-name post=this expandAction="showPosterExpansion"}}
{{poster-name post=this expandAction="expandPostUser"}}
<div class='post-info'>
<a class='post-date' {{bind-attr href="shareUrl" data-share-url="shareUrl" data-post-number="post_number"}}>{{age-with-tooltip created_at}}</a>
</div>

View File

@ -1,13 +1,13 @@
{{#if model}}
{{#link-to 'user' user}}{{bound-avatar model "huge"}}{{/link-to}}
{{#if username}}
{{#link-to 'user' user}}{{bound-avatar avatar "huge"}}{{/link-to}}
<div class="names">
<h1 {{bind-attr class="staff new_user"}}><a {{bind-attr href="usernameUrl"}}>{{username}}</a></h1>
<h1 {{bind-attr class="staff new_user"}}>
{{#link-to 'user' user}}{{username}}{{/link-to}}
</h1>
{{#if showName}}
<h2><a {{bind-attr href="usernameUrl"}}>{{name}}</a></h2>
<h2>{{#link-to 'user' user}}{{name}}{{/link-to}}</h2>
{{/if}}
</div>
{{#if showBadges}}
@ -22,6 +22,7 @@
{{/if}}
</div>
{{/if}}
{{#if user}}
<div class="metadata">
{{#if user.location}}<h3><i class="fa fa-map-marker"></i> {{user.location}}</h3>{{/if}}
@ -30,7 +31,6 @@
{{groups-list groups=user.custom_groups}}
</div>
<div class='bottom'>
{{#if user.bio_cooked}}<div class='bio'>{{{user.bio_cooked}}}</div>{{/if}}

View File

@ -1,11 +1,3 @@
/**
This view renders a post.
@class PostView
@extends Discourse.GroupedView
@namespace Discourse
@module Discourse
**/
Discourse.PostView = Discourse.GroupedView.extend(Ember.Evented, {
classNames: ['topic-post', 'clearfix'],
templateName: 'post',
@ -36,7 +28,7 @@ Discourse.PostView = Discourse.GroupedView.extend(Ember.Evented, {
// If the cooked content changed, add the quote controls
cookedChanged: function() {
Em.run.scheduleOnce('afterRender', this, 'insertQuoteControls');
Em.run.scheduleOnce('afterRender', this, '_insertQuoteControls');
}.observes('post.cooked'),
mouseUp: function(e) {
@ -66,7 +58,7 @@ Discourse.PostView = Discourse.GroupedView.extend(Ember.Evented, {
repliesShown: Em.computed.gt('post.replies.length', 0),
updateQuoteElements: function($aside, desc) {
_updateQuoteElements: function($aside, desc) {
var navLink = "",
quoteTitle = I18n.t("post.follow_quote"),
postNumber = $aside.data('post');
@ -100,42 +92,39 @@ Discourse.PostView = Discourse.GroupedView.extend(Ember.Evented, {
$('.quote-controls', $aside).html(expandContract + navLink);
},
toggleQuote: function($aside) {
$aside.data('expanded',!$aside.data('expanded'));
_toggleQuote: function($aside) {
$aside.data('expanded', !$aside.data('expanded'));
if ($aside.data('expanded')) {
this.updateQuoteElements($aside, 'chevron-up');
this._updateQuoteElements($aside, 'chevron-up');
// Show expanded quote
var $blockQuote = $('blockquote', $aside);
$aside.data('original-contents',$blockQuote.html());
var originalText = $blockQuote.text().trim();
$blockQuote.html(I18n.t("loading"));
var topic_id = this.get('post.topic_id');
var topicId = this.get('post.topic_id');
if ($aside.data('topic')) {
topic_id = $aside.data('topic');
topicId = $aside.data('topic');
}
var post_id = $aside.data('post');
var postId = parseInt($aside.data('post'), 10);
topicId = parseInt(topicId, 10);
topic_id = parseInt(topic_id,10);
post_id = parseInt(post_id,10);
Discourse.ajax("/posts/by_number/" + topic_id + "/" + post_id).then(function (result) {
Discourse.ajax("/posts/by_number/" + topicId + "/" + postId).then(function (result) {
var parsed = $(result.cooked);
parsed.replaceText(originalText, "<span class='highlighted'>" + originalText + "</span>");
$blockQuote.showHtml(parsed);
});
} else {
// Hide expanded quote
this.updateQuoteElements($aside, 'chevron-down');
this._updateQuoteElements($aside, 'chevron-down');
$('blockquote', $aside).showHtml($aside.data('original-contents'));
}
return false;
},
// Show how many times links have been clicked on
showLinkCounts: function() {
_showLinkCounts: function() {
var self = this,
link_counts = this.get('post.link_counts');
@ -194,7 +183,7 @@ Discourse.PostView = Discourse.GroupedView.extend(Ember.Evented, {
},
// Add the quote controls to a post
insertQuoteControls: function() {
_insertQuoteControls: function() {
var self = this,
$quotes = this.$('aside.quote');
@ -204,14 +193,14 @@ Discourse.PostView = Discourse.GroupedView.extend(Ember.Evented, {
$quotes.each(function(i, e) {
var $aside = $(e);
if ($aside.data('post')) {
self.updateQuoteElements($aside, 'chevron-down');
self._updateQuoteElements($aside, 'chevron-down');
var $title = $('.title', $aside);
// Unless it's a full quote, allow click to expand
if (!($aside.data('full') || $title.data('has-quote-controls'))) {
$title.on('click', function(e) {
if ($(e.target).is('a')) return true;
self.toggleQuote($aside);
self._toggleQuote($aside);
});
$title.data('has-quote-controls', true);
}
@ -219,16 +208,17 @@ Discourse.PostView = Discourse.GroupedView.extend(Ember.Evented, {
});
},
willDestroyElement: function() {
_destroyedPostView: function() {
Discourse.ScreenTrack.current().stopTracking(this.get('elementId'));
},
this._unbindExpandMentions();
}.on('willDestroyElement'),
didInsertElement: function() {
_postViewInserted: function() {
var $post = this.$(),
post = this.get('post'),
postNumber = post.get('post_number');
this.showLinkCounts();
this._showLinkCounts();
// Track this post
Discourse.ScreenTrack.current().track(this.$().prop('id'), postNumber);
@ -245,12 +235,27 @@ Discourse.PostView = Discourse.GroupedView.extend(Ember.Evented, {
this.trigger('postViewInserted', $post);
// Find all the quotes
Em.run.scheduleOnce('afterRender', this, 'insertQuoteControls');
Em.run.scheduleOnce('afterRender', this, '_insertQuoteControls');
this.applySearchHighlight();
this._applySearchHighlight();
this._bindExpandMentions();
}.on('didInsertElement'),
_bindExpandMentions: function() {
var self = this;
this.$('.cooked').on('click.discourse-mention', 'a.mention', function(e) {
var $target = $(e.target);
self.appEvents.trigger('poster:expand', $target);
self.get('controller').send('expandPostUsername', $target.text());
return false;
});
},
applySearchHighlight: function(){
_unbindExpandMentions: function() {
this.$('.cooked').off('click.discourse-mention');
},
_applySearchHighlight: function() {
var highlight = this.get('controller.searchHighlight');
var cooked = this.$('.cooked');

View File

@ -5,14 +5,22 @@ export default Discourse.View.extend({
classNameBindings: ['controller.visible::hidden', 'controller.showBadges'],
_setup: function() {
var self = this;
var self = this,
width = this.$().width();
this.appEvents.on('poster:expand', function(target) {
if (!target) { return; }
Em.run.schedule('afterRender', function() {
if (target) {
var position = target.offset();
if (position) {
position.left += target.width() + 5;
position.left += target.width() + 10;
var overage = ($(window).width() - 50) - (position.left + width);
if (overage < 0) {
position.left += overage;
position.top += target.height() + 5;
}
self.$().css(position);
}
}