Adds support for dynamic composer messages.

This commit is contained in:
Robin Ward 2013-09-09 16:54:33 -04:00
parent 9c6c0f2a3d
commit 0de96a6059
10 changed files with 110 additions and 80 deletions

View File

@ -7,10 +7,16 @@
@module Discourse @module Discourse
**/ **/
Discourse.ComposerController = Discourse.Controller.extend({ Discourse.ComposerController = Discourse.Controller.extend({
needs: ['modal', 'topic'], needs: ['modal', 'topic', 'composerMessages'],
replyAsNewTopicDraft: Em.computed.equal('model.draftKey', Discourse.Composer.REPLY_AS_NEW_TOPIC_KEY), replyAsNewTopicDraft: Em.computed.equal('model.draftKey', Discourse.Composer.REPLY_AS_NEW_TOPIC_KEY),
init: function() {
this._super();
this.set('similarTopics', Em.A());
},
togglePreview: function() { togglePreview: function() {
this.get('model').togglePreview(); this.get('model').togglePreview();
}, },
@ -94,7 +100,6 @@ Discourse.ComposerController = Discourse.Controller.extend({
composerController.destroyDraft(); composerController.destroyDraft();
} }
opts = opts || {}; opts = opts || {};
composerController.close(); composerController.close();
@ -112,51 +117,27 @@ Discourse.ComposerController = Discourse.Controller.extend({
}); });
}, },
closeEducation: function() { _considerNewUserEducation: function() {
this.set('educationClosed', true);
},
closeSimilar: function() {
this.set('similarClosed', true);
},
similarVisible: function() {
if (this.get('similarClosed')) return false;
if (this.get('model.composeState') !== Discourse.Composer.OPEN) return false;
return (this.get('similarTopics.length') || 0) > 0;
}.property('similarTopics.length', 'similarClosed', 'model.composeState'),
newUserEducationVisible: function() {
if (!this.get('educationContents')) return false;
if (this.get('model.composeState') !== Discourse.Composer.OPEN) return false;
if (!this.present('model.reply')) return false;
if (this.get('educationClosed')) return false;
return true;
}.property('model.composeState', 'model.reply', 'educationClosed', 'educationContents'),
fetchNewUserEducation: function() {
// We don't show education when editing a post. // We don't show education when editing a post.
if (this.get('model.editingPost')) return; if (this.get('model.editingPost')) return;
// If creating a topic, use topic_count, otherwise post_count // If creating a topic, use topic_count, otherwise post_count
var count = this.get('model.creatingTopic') ? Discourse.User.currentProp('topic_count') : Discourse.User.currentProp('reply_count'); var count = this.get('model.creatingTopic') ? Discourse.User.currentProp('topic_count') : Discourse.User.currentProp('reply_count');
if (count >= Discourse.SiteSettings.educate_until_posts) { if (count >= Discourse.SiteSettings.educate_until_posts) { return; }
this.set('educationClosed', true);
this.set('educationContents', '');
return;
}
// The user must have typed a reply // The user must have typed a reply
if (!this.get('typedReply')) return; if (!this.get('typedReply')) return;
this.set('educationClosed', false);
// If visible update the text // If visible update the text
var educationKey = this.get('model.creatingTopic') ? 'new-topic' : 'new-reply'; var educationKey = this.get('model.creatingTopic') ? 'new-topic' : 'new-reply',
var composerController = this; messageController = this.get('controllers.composerMessages');
Discourse.ajax("/education/" + educationKey, {dataType: 'html'}).then(function(result) { Discourse.ajax("/education/" + educationKey, {dataType: 'html'}).then(function(result) {
composerController.set('educationContents', result); messageController.popup({
templateName: 'composer/education',
body: result
});
}); });
}.observes('typedReply', 'model.creatingTopic', 'currentUser.reply_count'), }.observes('typedReply', 'model.creatingTopic', 'currentUser.reply_count'),
@ -176,16 +157,25 @@ Discourse.ComposerController = Discourse.Controller.extend({
// We don't care about similar topics unless creating a topic // We don't care about similar topics unless creating a topic
if (!this.get('model.creatingTopic')) return; if (!this.get('model.creatingTopic')) return;
var body = this.get('model.reply'); var body = this.get('model.reply'),
var title = this.get('model.title'); title = this.get('model.title');
// Ensure the fields are of the minimum length // Ensure the fields are of the minimum length
if (body.length < Discourse.SiteSettings.min_body_similar_length) return; if (body.length < Discourse.SiteSettings.min_body_similar_length) return;
if (title.length < Discourse.SiteSettings.min_title_similar_length) return; if (title.length < Discourse.SiteSettings.min_title_similar_length) return;
var composerController = this; var messageController = this.get('controllers.composerMessages'),
Discourse.Topic.findSimilarTo(title, body).then(function (topics) { similarTopics = this.get('similarTopics');
composerController.set('similarTopics', topics);
Discourse.Topic.findSimilarTo(title, body).then(function (newTopics) {
similarTopics.clear();
similarTopics.pushObjects(newTopics);
messageController.popup({
templateName: 'composer/similar_topics',
similarTopics: similarTopics,
extraClass: 'similar-topics'
});
}); });
}, },
@ -211,8 +201,6 @@ Discourse.ComposerController = Discourse.Controller.extend({
var promise = opts.promise || Ember.Deferred.create(); var promise = opts.promise || Ember.Deferred.create();
opts.promise = promise; opts.promise = promise;
this.set('typedReply', false); this.set('typedReply', false);
this.set('similarTopics', null);
this.set('similarClosed', false);
if (!opts.draftKey) { if (!opts.draftKey) {
alert("composer was opened without a draft key"); alert("composer was opened without a draft key");

View File

@ -0,0 +1,31 @@
/**
A controller for displaying messages as the user composes a message.
@class ComposerMessagesController
@extends Ember.ArrayController
@namespace Discourse
@module Discourse
**/
Discourse.ComposerMessagesController = Ember.ArrayController.extend({
needs: ['composer'],
init: function() {
this._super();
this.set('messagesByTemplate', {});
},
popup: function(msg) {
var messagesByTemplate = this.get('messagesByTemplate'),
existing = messagesByTemplate[msg.templateName];
if (!existing) {
this.pushObject(msg);
messagesByTemplate[msg.templateName] = msg;
}
},
closeMessage: function(message) {
this.removeObject(message);
}
});

View File

@ -2,23 +2,7 @@
<div class='contents'> <div class='contents'>
<div class='composer-popup-container'> {{render composerMessages}}
<div id='new-user-education' class='composer-popup' style='display: none'>
<a href='#' {{action closeEducation}} class='close'><i class='icon icon-remove-sign'></i></a>
{{{educationContents}}}
</div>
<div id='similar-topics' class='composer-popup' style='display: none'>
<a href='#' {{action closeSimilar}} class='close'><i class='icon icon-remove-sign'></i></a>
<h3>{{i18n composer.similar_topics}}<h3>
<ul class='topics'>
{{#each similarTopics}}
<li>{{{topicLink this}}} <span class='posts-count'>({{{i18n topic.filters.n_posts count="posts_count"}}})</span></li>
{{/each}}
</ul>
</div>
</div>
<div class='control'> <div class='control'>
<a href='#' class='toggler' {{action toggle bubbles=false}} title='{{i18n composer.toggler}}'></a> <a href='#' class='toggler' {{action toggle bubbles=false}} title='{{i18n composer.toggler}}'></a>

View File

@ -0,0 +1,2 @@
<a href='#' {{action closeMessage this}} class='close'><i class='icon icon-remove-sign'></i></a>
{{{body}}}

View File

@ -0,0 +1,8 @@
<a href='#' {{action closeMessage this}} class='close'><i class='icon icon-remove-sign'></i></a>
<h3>{{i18n composer.similar_topics}}<h3>
<ul class='topics'>
{{#each similarTopics}}
<li>{{{topicLink this}}} <span class='posts-count'>({{{i18n topic.filters.n_posts count="posts_count"}}})</span></li>
{{/each}}
</ul>

View File

@ -0,0 +1,34 @@
/**
Renders a popup messages on the composer
@class ComposerMessagesView
@extends Discourse.View
@namespace Discourse
@module Discourse
**/
Discourse.ComposerMessagesView = Ember.CollectionView.extend({
classNameBindings: [':composer-popup-container', 'hidden'],
content: Em.computed.alias('controller.content'),
hidden: Em.computed.not('controller.controllers.composer.model.viewOpen'),
itemViewClass: Discourse.View.extend({
classNames: ['composer-popup', 'hidden'],
templateName: Em.computed.alias('content.templateName'),
init: function() {
this._super();
this.set('context', this.get('content'));
if (this.get('content.extraClass')) {
this.get('classNames').pushObject(this.get('content.extraClass'));
}
},
didInsertElement: function() {
var replyControl = $('#reply-control');
this.$().css('bottom', (replyControl.height() || 0) + "px").slideDown('fast');
}
}),
});

View File

@ -67,24 +67,6 @@ Discourse.ComposerView = Discourse.View.extend({
}); });
}.observes('model.reply', 'model.hidePreview'), }.observes('model.reply', 'model.hidePreview'),
newUserEducationVisibilityChanged: function() {
var $panel = $('#new-user-education');
if (this.get('controller.newUserEducationVisible')) {
$panel.slideDown('fast');
} else {
$panel.slideUp('fast');
}
}.observes('controller.newUserEducationVisible'),
similarVisibilityChanged: function() {
var $panel = $('#similar-topics');
if (this.get('controller.similarVisible')) {
$panel.slideDown('fast');
} else {
$panel.slideUp('fast');
}
}.observes('controller.similarVisible'),
movePanels: function(sizePx) { movePanels: function(sizePx) {
$('.composer-popup').css('bottom', sizePx); $('.composer-popup').css('bottom', sizePx);
}, },

View File

@ -15,6 +15,7 @@
//= require_tree ./discourse/mixins //= require_tree ./discourse/mixins
//= require ./discourse/components/computed //= require ./discourse/components/computed
//= require ./discourse/views/view //= require ./discourse/views/view
//= require ./discourse/views/container_view
//= require ./discourse/components/debounce //= require ./discourse/components/debounce
//= require ./discourse/models/model //= require ./discourse/models/model
//= require ./discourse/models/user_action //= require ./discourse/models/user_action

View File

@ -51,7 +51,7 @@
} }
} }
#similar-topics { .similar-topics {
background-color: #b5e8fd; background-color: #b5e8fd;
border: 1px solid darken(#b5e8fd, 10%); border: 1px solid darken(#b5e8fd, 10%);

View File

@ -12,7 +12,7 @@
display: none; display: none;
} }
.composer-popup, #similar-topics { .composer-popup {
display: none; display: none;
} }
@ -99,7 +99,7 @@ display: none;
a.cancel { a.cancel {
text-decoration: underline; text-decoration: underline;
padding-left: 7px; padding-left: 7px;
float: left; float: left;
margin-top: 6px; margin-top: 6px;
} }
.control-row { .control-row {