diff --git a/app/assets/javascripts/discourse.js b/app/assets/javascripts/discourse.js index 4b845c4c8..d05de2e48 100644 --- a/app/assets/javascripts/discourse.js +++ b/app/assets/javascripts/discourse.js @@ -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); diff --git a/app/assets/javascripts/discourse/controllers/poster_expansion_controller.js b/app/assets/javascripts/discourse/controllers/poster_expansion_controller.js new file mode 100644 index 000000000..61cab9438 --- /dev/null +++ b/app/assets/javascripts/discourse/controllers/poster_expansion_controller.js @@ -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(); + } + } + +}); + + diff --git a/app/assets/javascripts/discourse/models/post_stream.js b/app/assets/javascripts/discourse/models/post_stream.js index c8c6636fd..c4c0217d5 100644 --- a/app/assets/javascripts/discourse/models/post_stream.js +++ b/app/assets/javascripts/discourse/models/post_stream.js @@ -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. diff --git a/app/assets/javascripts/discourse/models/user.js b/app/assets/javascripts/discourse/models/user.js index 627918c61..b3fee4385 100644 --- a/app/assets/javascripts/discourse/models/user.js +++ b/app/assets/javascripts/discourse/models/user.js @@ -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 diff --git a/app/assets/javascripts/discourse/routes/topic_route.js b/app/assets/javascripts/discourse/routes/topic_route.js index 6865e340d..375a33cf4 100644 --- a/app/assets/javascripts/discourse/routes/topic_route.js +++ b/app/assets/javascripts/discourse/routes/topic_route.js @@ -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); diff --git a/app/assets/javascripts/discourse/templates/post.js.handlebars b/app/assets/javascripts/discourse/templates/post.js.handlebars index 516556941..534080598 100644 --- a/app/assets/javascripts/discourse/templates/post.js.handlebars +++ b/app/assets/javascripts/discourse/templates/post.js.handlebars @@ -19,9 +19,9 @@
{{#unless userDeleted}}
- {{avatar this imageSize="large"}} -

{{breakUp username}}

- {{#if user_title}}
{{user_title}}
{{/if}} + {{avatar this imageSize="large"}} +

{{breakUp username}}

+ {{#if user_title}}
{{user_title}}
{{/if}}
{{else}}
diff --git a/app/assets/javascripts/discourse/templates/poster_expansion.handlebars b/app/assets/javascripts/discourse/templates/poster_expansion.handlebars new file mode 100644 index 000000000..185af36de --- /dev/null +++ b/app/assets/javascripts/discourse/templates/poster_expansion.handlebars @@ -0,0 +1,18 @@ +{{#if model}} + {{avatar model imageSize="huge"}} + +

{{username}}

+

{{name}}

+

{{i18n last_post}}: {{unboundDate last_posted_at}}

+ +
+ {{#if bio_cooked}}
{{{bio_cooked}}}
{{/if}} + + + + {{#link-to 'user' model class="btn"}}{{i18n user.profile}}{{/link-to}} + + +
+ +{{/if}} diff --git a/app/assets/javascripts/discourse/templates/topic.js.handlebars b/app/assets/javascripts/discourse/templates/topic.js.handlebars index c6c17ffde..c81775e2f 100644 --- a/app/assets/javascripts/discourse/templates/topic.js.handlebars +++ b/app/assets/javascripts/discourse/templates/topic.js.handlebars @@ -125,6 +125,8 @@
{{render share}} +{{render posterExpansion}} + {{#if currentUser.enable_quoting}} {{render quoteButton}} {{/if}} @@ -132,3 +134,4 @@ {{#if currentUser.staff}} {{render topicAdminMenu content}} {{/if}} + diff --git a/app/assets/javascripts/discourse/views/poster_expansion_view.js b/app/assets/javascripts/discourse/views/poster_expansion_view.js new file mode 100644 index 000000000..36fb39d28 --- /dev/null +++ b/app/assets/javascripts/discourse/views/poster_expansion_view.js @@ -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'); + } + +}); \ No newline at end of file diff --git a/app/assets/stylesheets/desktop/poster_expansion.scss b/app/assets/stylesheets/desktop/poster_expansion.scss new file mode 100644 index 000000000..8a3a62891 --- /dev/null +++ b/app/assets/stylesheets/desktop/poster_expansion.scss @@ -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; + } +} diff --git a/app/controllers/topics_controller.rb b/app/controllers/topics_controller.rb index 1872e1ea5..8f4a8996e 100644 --- a/app/controllers/topics_controller.rb +++ b/app/controllers/topics_controller.rb @@ -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 diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 7891f01c0..370a4b994 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -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' diff --git a/test/javascripts/models/user_test.js b/test/javascripts/models/user_test.js index 0414ef44e..c25ab80fb 100644 --- a/test/javascripts/models/user_test.js +++ b/test/javascripts/models/user_test.js @@ -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(); + }); +}); \ No newline at end of file