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>"); buffer.push("</span>");
// Are we showing full names? // Are we showing full names?
if (name && this.get('displayNameOnPosts') && (this.sanitizeName(name) !== this.sanitizeName(username))) { if (name && this.get('displayNameOnPosts') && (this.sanitizeName(name) !== this.sanitizeName(username))) {
name = Handlebars.Utils.escapeExpression(name); name = Handlebars.Utils.escapeExpression(name);

View File

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

View File

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

View File

@ -14,8 +14,15 @@ 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) { expandPostUser: function(post) {
this.controllerFor('poster-expansion').show(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) { composePrivateMessage: function(user) {

View File

@ -2,13 +2,13 @@
<div class='topic-avatar'> <div class='topic-avatar'>
<div class='contents'> <div class='contents'>
<div> <div>
{{poster-avatar action="showPosterExpansion" post=this classNames="main-avatar"}} {{poster-avatar action="expandPostUser" post=this classNames="main-avatar"}}
</div> </div>
</div> </div>
</div> </div>
<div class='topic-body'> <div class='topic-body'>
<div class="topic-meta-data"> <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}} {{#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 class='post-info post-date'>{{age-with-tooltip created_at}}</div>
</div> </div>

View File

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

View File

@ -1,13 +1,13 @@
{{#if model}} {{#if username}}
{{#link-to 'user' user}}{{bound-avatar model "huge"}}{{/link-to}} {{#link-to 'user' user}}{{bound-avatar avatar "huge"}}{{/link-to}}
<div class="names"> <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}} {{#if showName}}
<h2><a {{bind-attr href="usernameUrl"}}>{{name}}</a></h2> <h2>{{#link-to 'user' user}}{{name}}{{/link-to}}</h2>
{{/if}} {{/if}}
</div> </div>
{{#if showBadges}} {{#if showBadges}}
@ -22,6 +22,7 @@
{{/if}} {{/if}}
</div> </div>
{{/if}} {{/if}}
{{#if user}} {{#if user}}
<div class="metadata"> <div class="metadata">
{{#if user.location}}<h3><i class="fa fa-map-marker"></i> {{user.location}}</h3>{{/if}} {{#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}} {{groups-list groups=user.custom_groups}}
</div> </div>
<div class='bottom'> <div class='bottom'>
{{#if user.bio_cooked}}<div class='bio'>{{{user.bio_cooked}}}</div>{{/if}} {{#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, { Discourse.PostView = Discourse.GroupedView.extend(Ember.Evented, {
classNames: ['topic-post', 'clearfix'], classNames: ['topic-post', 'clearfix'],
templateName: 'post', templateName: 'post',
@ -36,7 +28,7 @@ Discourse.PostView = Discourse.GroupedView.extend(Ember.Evented, {
// If the cooked content changed, add the quote controls // If the cooked content changed, add the quote controls
cookedChanged: function() { cookedChanged: function() {
Em.run.scheduleOnce('afterRender', this, 'insertQuoteControls'); Em.run.scheduleOnce('afterRender', this, '_insertQuoteControls');
}.observes('post.cooked'), }.observes('post.cooked'),
mouseUp: function(e) { mouseUp: function(e) {
@ -66,7 +58,7 @@ Discourse.PostView = Discourse.GroupedView.extend(Ember.Evented, {
repliesShown: Em.computed.gt('post.replies.length', 0), repliesShown: Em.computed.gt('post.replies.length', 0),
updateQuoteElements: function($aside, desc) { _updateQuoteElements: function($aside, desc) {
var navLink = "", var navLink = "",
quoteTitle = I18n.t("post.follow_quote"), quoteTitle = I18n.t("post.follow_quote"),
postNumber = $aside.data('post'); postNumber = $aside.data('post');
@ -100,42 +92,39 @@ Discourse.PostView = Discourse.GroupedView.extend(Ember.Evented, {
$('.quote-controls', $aside).html(expandContract + navLink); $('.quote-controls', $aside).html(expandContract + navLink);
}, },
toggleQuote: function($aside) { _toggleQuote: function($aside) {
$aside.data('expanded',!$aside.data('expanded')); $aside.data('expanded', !$aside.data('expanded'));
if ($aside.data('expanded')) { if ($aside.data('expanded')) {
this.updateQuoteElements($aside, 'chevron-up'); this._updateQuoteElements($aside, 'chevron-up');
// Show expanded quote // Show expanded quote
var $blockQuote = $('blockquote', $aside); var $blockQuote = $('blockquote', $aside);
$aside.data('original-contents',$blockQuote.html()); $aside.data('original-contents',$blockQuote.html());
var originalText = $blockQuote.text().trim(); var originalText = $blockQuote.text().trim();
$blockQuote.html(I18n.t("loading")); $blockQuote.html(I18n.t("loading"));
var topic_id = this.get('post.topic_id'); var topicId = this.get('post.topic_id');
if ($aside.data('topic')) { 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); Discourse.ajax("/posts/by_number/" + topicId + "/" + postId).then(function (result) {
post_id = parseInt(post_id,10);
Discourse.ajax("/posts/by_number/" + topic_id + "/" + post_id).then(function (result) {
var parsed = $(result.cooked); var parsed = $(result.cooked);
parsed.replaceText(originalText, "<span class='highlighted'>" + originalText + "</span>"); parsed.replaceText(originalText, "<span class='highlighted'>" + originalText + "</span>");
$blockQuote.showHtml(parsed); $blockQuote.showHtml(parsed);
}); });
} else { } else {
// Hide expanded quote // Hide expanded quote
this.updateQuoteElements($aside, 'chevron-down'); this._updateQuoteElements($aside, 'chevron-down');
$('blockquote', $aside).showHtml($aside.data('original-contents')); $('blockquote', $aside).showHtml($aside.data('original-contents'));
} }
return false; return false;
}, },
// Show how many times links have been clicked on // Show how many times links have been clicked on
showLinkCounts: function() { _showLinkCounts: function() {
var self = this, var self = this,
link_counts = this.get('post.link_counts'); 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 // Add the quote controls to a post
insertQuoteControls: function() { _insertQuoteControls: function() {
var self = this, var self = this,
$quotes = this.$('aside.quote'); $quotes = this.$('aside.quote');
@ -204,14 +193,14 @@ Discourse.PostView = Discourse.GroupedView.extend(Ember.Evented, {
$quotes.each(function(i, e) { $quotes.each(function(i, e) {
var $aside = $(e); var $aside = $(e);
if ($aside.data('post')) { if ($aside.data('post')) {
self.updateQuoteElements($aside, 'chevron-down'); self._updateQuoteElements($aside, 'chevron-down');
var $title = $('.title', $aside); var $title = $('.title', $aside);
// Unless it's a full quote, allow click to expand // Unless it's a full quote, allow click to expand
if (!($aside.data('full') || $title.data('has-quote-controls'))) { if (!($aside.data('full') || $title.data('has-quote-controls'))) {
$title.on('click', function(e) { $title.on('click', function(e) {
if ($(e.target).is('a')) return true; if ($(e.target).is('a')) return true;
self.toggleQuote($aside); self._toggleQuote($aside);
}); });
$title.data('has-quote-controls', true); $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')); Discourse.ScreenTrack.current().stopTracking(this.get('elementId'));
}, this._unbindExpandMentions();
}.on('willDestroyElement'),
didInsertElement: function() { _postViewInserted: function() {
var $post = this.$(), var $post = this.$(),
post = this.get('post'), post = this.get('post'),
postNumber = post.get('post_number'); postNumber = post.get('post_number');
this.showLinkCounts(); this._showLinkCounts();
// Track this post // Track this post
Discourse.ScreenTrack.current().track(this.$().prop('id'), postNumber); Discourse.ScreenTrack.current().track(this.$().prop('id'), postNumber);
@ -245,12 +235,27 @@ Discourse.PostView = Discourse.GroupedView.extend(Ember.Evented, {
this.trigger('postViewInserted', $post); this.trigger('postViewInserted', $post);
// Find all the quotes // 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 highlight = this.get('controller.searchHighlight');
var cooked = this.$('.cooked'); var cooked = this.$('.cooked');

View File

@ -5,14 +5,22 @@ export default Discourse.View.extend({
classNameBindings: ['controller.visible::hidden', 'controller.showBadges'], classNameBindings: ['controller.visible::hidden', 'controller.showBadges'],
_setup: function() { _setup: function() {
var self = this; var self = this,
width = this.$().width();
this.appEvents.on('poster:expand', function(target) { this.appEvents.on('poster:expand', function(target) {
if (!target) { return; } if (!target) { return; }
Em.run.schedule('afterRender', function() { Em.run.schedule('afterRender', function() {
if (target) { if (target) {
var position = target.offset(); var position = target.offset();
if (position) { 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); self.$().css(position);
} }
} }