FIX: Reply as new topic should select the correct category

This commit is contained in:
Robin Ward 2015-03-12 14:41:58 -04:00
parent 3ecb58980f
commit 091af27a31
4 changed files with 205 additions and 207 deletions

View File

@ -1,6 +1,7 @@
var MAX_SHOWN = 5; var MAX_SHOWN = 5;
import StringBuffer from 'discourse/mixins/string-buffer'; import StringBuffer from 'discourse/mixins/string-buffer';
import { iconHTML } from 'discourse/helpers/fa-icon';
export default Em.Component.extend(StringBuffer, { export default Em.Component.extend(StringBuffer, {
classNameBindings: [':gutter'], classNameBindings: [':gutter'],
@ -55,14 +56,14 @@ export default Em.Component.extend(StringBuffer, {
if (collapsed) { if (collapsed) {
var remaining = links.length - MAX_SHOWN; var remaining = links.length - MAX_SHOWN;
if (remaining > 0) { if (remaining > 0) {
buffer.push("<li><a href='#' class='toggle-more'>" + I18n.t('post.more_links', {count: remaining}) + "</a></li>"); buffer.push("<li><a href class='toggle-more'>" + I18n.t('post.more_links', {count: remaining}) + "</a></li>");
} }
} }
buffer.push('</ul>'); buffer.push('</ul>');
} }
if ((links.length <= MAX_SHOWN || !collapsed) && this.get('canReplyAsNewTopic')) { if ((links.length <= MAX_SHOWN || !collapsed) && this.get('canReplyAsNewTopic')) {
buffer.push("<a href='#' class='reply-new'><i class='fa fa-plus'></i>" + I18n.t('post.reply_as_new_topic') + "</a>"); buffer.push("<a href class='reply-new'>" + iconHTML('plus') + I18n.t('post.reply_as_new_topic') + "</a>");
} }
}, },

View File

@ -410,7 +410,7 @@ export default DiscourseController.extend({
// Given a potential instance and options, set the model for this composer. // Given a potential instance and options, set the model for this composer.
_setModel(composerModel, opts) { _setModel(composerModel, opts) {
if (opts.draft) { if (opts.draft) {
composerModel = Discourse.Composer.loadDraft(opts.draftKey, opts.draftSequence, opts.draft); composerModel = Discourse.Composer.loadDraft(opts);
if (composerModel) { if (composerModel) {
composerModel.set('topic', opts.topic); composerModel.set('topic', opts.topic);
} }

View File

@ -20,7 +20,7 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
}.observes('topic'), }.observes('topic'),
_titleChanged: function() { _titleChanged: function() {
var title = this.get('title'); const title = this.get('title');
if (!Ember.isEmpty(title)) { if (!Ember.isEmpty(title)) {
// Note normally you don't have to trigger this, but topic titles can be updated // Note normally you don't have to trigger this, but topic titles can be updated
@ -30,8 +30,8 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
}.observes('title', 'category'), }.observes('title', 'category'),
termChanged: function() { termChanged: function() {
var dropdown = this.get('controllers.header.visibleDropdown'); const dropdown = this.get('controllers.header.visibleDropdown');
var term = this.get('controllers.search.term'); const term = this.get('controllers.search.term');
if(dropdown === 'search-dropdown' && term){ if(dropdown === 'search-dropdown' && term){
this.set('searchHighlight', term); this.set('searchHighlight', term);
@ -43,13 +43,13 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
}.observes('controllers.search.term', 'controllers.header.visibleDropdown'), }.observes('controllers.search.term', 'controllers.header.visibleDropdown'),
postStreamLoadedAllPostsChanged: function(){ postStreamLoadedAllPostsChanged: function() {
// semantics of loaded all posts are slightly diff at topic level, // semantics of loaded all posts are slightly diff at topic level,
// it just means that we "once" loaded all posts, this means we don't // it just means that we "once" loaded all posts, this means we don't
// keep re-rendering the suggested topics when new posts zoom in // keep re-rendering the suggested topics when new posts zoom in
var loaded = this.get('postStream.loadedAllPosts'); let loaded = this.get('postStream.loadedAllPosts');
if(loaded) { if (loaded) {
this.set('loadedTopicId', this.get('model.id')); this.set('loadedTopicId', this.get('model.id'));
} else { } else {
loaded = this.get('loadedTopicId') === this.get('model.id'); loaded = this.get('loadedTopicId') === this.get('model.id');
@ -60,7 +60,7 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
}.observes('postStream', 'postStream.loadedAllPosts'), }.observes('postStream', 'postStream.loadedAllPosts'),
show_deleted: function(key, value) { show_deleted: function(key, value) {
var postStream = this.get('postStream'); const postStream = this.get('postStream');
if (!postStream) { return; } if (!postStream) { return; }
if (arguments.length > 1) { if (arguments.length > 1) {
@ -70,7 +70,7 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
}.property('postStream.summary'), }.property('postStream.summary'),
filter: function(key, value) { filter: function(key, value) {
var postStream = this.get('postStream'); const postStream = this.get('postStream');
if (!postStream) { return; } if (!postStream) { return; }
if (arguments.length > 1) { if (arguments.length > 1) {
@ -80,7 +80,7 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
}.property('postStream.summary'), }.property('postStream.summary'),
username_filters: function(key, value) { username_filters: function(key, value) {
var postStream = this.get('postStream'); const postStream = this.get('postStream');
if (!postStream) { return; } if (!postStream) { return; }
if (arguments.length > 1) { if (arguments.length > 1) {
@ -89,20 +89,19 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
return postStream.get('streamFilters.username_filters'); return postStream.get('streamFilters.username_filters');
}.property('postStream.streamFilters.username_filters'), }.property('postStream.streamFilters.username_filters'),
init: function() { _clearSelected: function() {
this._super();
this.set('selectedPosts', []); this.set('selectedPosts', []);
this.set('selectedReplies', []); this.set('selectedReplies', []);
}, }.on('init'),
actions: { actions: {
deleteTopic: function() { deleteTopic() {
this.deleteTopic(); this.deleteTopic();
}, },
// Post related methods // Post related methods
replyToPost: function(post) { replyToPost(post) {
var composerController = this.get('controllers.composer'), const composerController = this.get('controllers.composer'),
quoteController = this.get('controllers.quote-button'), quoteController = this.get('controllers.quote-button'),
quotedText = Discourse.Quote.build(quoteController.get('post'), quoteController.get('buffer')), quotedText = Discourse.Quote.build(quoteController.get('post'), quoteController.get('buffer')),
topic = post ? post.get('topic') : this.get('model'); topic = post ? post.get('topic') : this.get('model');
@ -116,7 +115,7 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
composerController.appendText(quotedText); composerController.appendText(quotedText);
} else { } else {
var opts = { const opts = {
action: Discourse.Composer.REPLY, action: Discourse.Composer.REPLY,
draftKey: topic.get('draft_key'), draftKey: topic.get('draft_key'),
draftSequence: topic.get('draft_sequence') draftSequence: topic.get('draft_sequence')
@ -135,14 +134,14 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
return false; return false;
}, },
toggleLike: function(post) { toggleLike(post) {
var likeAction = post.get('actionByName.like'); const likeAction = post.get('actionByName.like');
if (likeAction && likeAction.get('canToggle')) { if (likeAction && likeAction.get('canToggle')) {
likeAction.toggle(); likeAction.toggle();
} }
}, },
recoverPost: function(post) { recoverPost(post) {
// Recovering the first post recovers the topic instead // Recovering the first post recovers the topic instead
if (post.get('post_number') === 1) { if (post.get('post_number') === 1) {
this.recoverTopic(); this.recoverTopic();
@ -151,7 +150,7 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
post.recover(); post.recover();
}, },
deletePost: function(post) { deletePost(post) {
// Deleting the first post deletes the topic // Deleting the first post deletes the topic
if (post.get('post_number') === 1) { if (post.get('post_number') === 1) {
@ -159,7 +158,7 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
return; return;
} }
var user = Discourse.User.current(), const user = Discourse.User.current(),
replyCount = post.get('reply_count'), replyCount = post.get('reply_count'),
self = this; self = this;
@ -169,13 +168,13 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
{label: I18n.t("cancel"), {label: I18n.t("cancel"),
'class': 'btn-danger rightg'}, 'class': 'btn-danger rightg'},
{label: I18n.t("post.controls.delete_replies.no_value"), {label: I18n.t("post.controls.delete_replies.no_value"),
callback: function() { callback() {
post.destroy(user); post.destroy(user);
} }
}, },
{label: I18n.t("post.controls.delete_replies.yes_value"), {label: I18n.t("post.controls.delete_replies.yes_value"),
'class': 'btn-primary', 'class': 'btn-primary',
callback: function() { callback() {
Discourse.Post.deleteMany([post], [post]); Discourse.Post.deleteMany([post], [post]);
self.get('postStream.posts').forEach(function (p) { self.get('postStream.posts').forEach(function (p) {
if (p === post || p.get('reply_to_post_number') === post.get('post_number')) { if (p === post || p.get('reply_to_post_number') === post.get('post_number')) {
@ -188,7 +187,7 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
} else { } else {
post.destroy(user).then(null, function(e) { post.destroy(user).then(null, function(e) {
post.undoDeleteState(); post.undoDeleteState();
var response = $.parseJSON(e.responseText); const response = $.parseJSON(e.responseText);
if (response && response.errors) { if (response && response.errors) {
bootbox.alert(response.errors[0]); bootbox.alert(response.errors[0]);
} else { } else {
@ -198,7 +197,7 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
} }
}, },
editPost: function(post) { editPost(post) {
if (!Discourse.User.current()) { if (!Discourse.User.current()) {
return bootbox.alert(I18n.t('post.controls.edit_anonymous')); return bootbox.alert(I18n.t('post.controls.edit_anonymous'));
} }
@ -211,7 +210,7 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
}); });
}, },
toggleBookmark: function(post) { toggleBookmark(post) {
if (!Discourse.User.current()) { if (!Discourse.User.current()) {
alert(I18n.t("bookmarks.not_bookmarked")); alert(I18n.t("bookmarks.not_bookmarked"));
return; return;
@ -229,12 +228,12 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
} }
}, },
jumpTop: function() { jumpTop() {
this.get('controllers.topic-progress').send('jumpTop'); this.get('controllers.topic-progress').send('jumpTop');
}, },
selectAll: function() { selectAll() {
var posts = this.get('postStream.posts'), const posts = this.get('postStream.posts'),
selectedPosts = this.get('selectedPosts'); selectedPosts = this.get('selectedPosts');
if (posts) { if (posts) {
selectedPosts.addObjects(posts); selectedPosts.addObjects(posts);
@ -242,37 +241,37 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
this.set('allPostsSelected', true); this.set('allPostsSelected', true);
}, },
deselectAll: function() { deselectAll() {
this.get('selectedPosts').clear(); this.get('selectedPosts').clear();
this.get('selectedReplies').clear(); this.get('selectedReplies').clear();
this.set('allPostsSelected', false); this.set('allPostsSelected', false);
}, },
toggleParticipant: function(user) { toggleParticipant(user) {
this.get('postStream').toggleParticipant(Em.get(user, 'username')); this.get('postStream').toggleParticipant(Em.get(user, 'username'));
}, },
editTopic: function() { editTopic() {
if (!this.get('details.can_edit')) return false; if (!this.get('details.can_edit')) return false;
this.set('editingTopic', true); this.set('editingTopic', true);
return false; return false;
}, },
cancelEditingTopic: function() { cancelEditingTopic() {
this.set('editingTopic', false); this.set('editingTopic', false);
this.rollbackBuffer(); this.rollbackBuffer();
}, },
toggleMultiSelect: function() { toggleMultiSelect() {
this.toggleProperty('multiSelect'); this.toggleProperty('multiSelect');
}, },
finishedEditingTopic: function() { finishedEditingTopic() {
if (!this.get('editingTopic')) { return; } if (!this.get('editingTopic')) { return; }
// save the modifications // save the modifications
var self = this, const self = this,
props = this.get('buffered.buffer'); props = this.get('buffered.buffer');
Topic.update(this.get('model'), props).then(function() { Topic.update(this.get('model'), props).then(function() {
@ -289,12 +288,12 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
}); });
}, },
toggledSelectedPost: function(post) { toggledSelectedPost(post) {
this.performTogglePost(post); this.performTogglePost(post);
}, },
toggledSelectedPostReplies: function(post) { toggledSelectedPostReplies(post) {
var selectedReplies = this.get('selectedReplies'); const selectedReplies = this.get('selectedReplies');
if (this.performTogglePost(post)) { if (this.performTogglePost(post)) {
selectedReplies.addObject(post); selectedReplies.addObject(post);
} else { } else {
@ -302,8 +301,8 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
} }
}, },
deleteSelected: function() { deleteSelected() {
var self = this; const self = this;
bootbox.confirm(I18n.t("post.delete.confirm", { count: this.get('selectedPostsCount')}), function(result) { bootbox.confirm(I18n.t("post.delete.confirm", { count: this.get('selectedPostsCount')}), function(result) {
if (result) { if (result) {
@ -312,7 +311,7 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
return self.deleteTopic(); return self.deleteTopic();
} }
var selectedPosts = self.get('selectedPosts'), const selectedPosts = self.get('selectedPosts'),
selectedReplies = self.get('selectedReplies'), selectedReplies = self.get('selectedReplies'),
postStream = self.get('postStream'), postStream = self.get('postStream'),
toRemove = []; toRemove = [];
@ -328,59 +327,54 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
}); });
}, },
expandHidden: function(post) { expandHidden(post) {
post.expandHidden(); post.expandHidden();
}, },
toggleVisibility: function() { toggleVisibility() {
this.get('content').toggleStatus('visible'); this.get('content').toggleStatus('visible');
}, },
toggleClosed: function() { toggleClosed() {
this.get('content').toggleStatus('closed'); this.get('content').toggleStatus('closed');
}, },
recoverTopic: function() { recoverTopic() {
this.get('content').recover(); this.get('content').recover();
}, },
makeBanner: function() { makeBanner() {
this.get('content').makeBanner(); this.get('content').makeBanner();
}, },
removeBanner: function() { removeBanner() {
this.get('content').removeBanner(); this.get('content').removeBanner();
}, },
togglePinned: function() { togglePinned() {
// Note that this is different than clearPin // Note that this is different than clearPin
this.get('content').setStatus('pinned', this.get('pinned_at') ? false : true); this.get('content').setStatus('pinned', this.get('pinned_at') ? false : true);
}, },
togglePinnedGlobally: function() { togglePinnedGlobally() {
// Note that this is different than clearPin // Note that this is different than clearPin
this.get('content').setStatus('pinned_globally', this.get('pinned_at') ? false : true); this.get('content').setStatus('pinned_globally', this.get('pinned_at') ? false : true);
}, },
toggleArchived: function() { toggleArchived() {
this.get('content').toggleStatus('archived'); this.get('content').toggleStatus('archived');
}, },
// Toggle the star on the topic // Toggle the star on the topic
toggleStar: function() { toggleStar() {
this.get('content').toggleStar(); this.get('content').toggleStar();
}, },
/** clearPin() {
Clears the pin from a topic for the currently logged in user
@method clearPin
**/
clearPin: function() {
this.get('content').clearPin(); this.get('content').clearPin();
}, },
togglePinnedForUser: function() { togglePinnedForUser() {
if (this.get('pinned_at')) { if (this.get('pinned_at')) {
if (this.get('pinned')) { if (this.get('pinned')) {
this.get('content').clearPin(); this.get('content').clearPin();
@ -390,28 +384,29 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
} }
}, },
replyAsNewTopic: function(post) { replyAsNewTopic(post) {
var composerController = this.get('controllers.composer'), const composerController = this.get('controllers.composer'),
quoteController = this.get('controllers.quote-button'), quoteController = this.get('controllers.quote-button'),
quotedText = Discourse.Quote.build(quoteController.get('post'), quoteController.get('buffer')), quotedText = Discourse.Quote.build(quoteController.get('post'), quoteController.get('buffer')),
self = this; self = this;
quoteController.deselectText(); quoteController.deselectText();
composerController.open({ composerController.open({
action: Discourse.Composer.CREATE_TOPIC, action: Discourse.Composer.CREATE_TOPIC,
draftKey: Discourse.Composer.REPLY_AS_NEW_TOPIC_KEY draftKey: Discourse.Composer.REPLY_AS_NEW_TOPIC_KEY,
categoryId: this.get('category.id')
}).then(function() { }).then(function() {
return Em.isEmpty(quotedText) ? Discourse.Post.loadQuote(post.get('id')) : quotedText; return Em.isEmpty(quotedText) ? Discourse.Post.loadQuote(post.get('id')) : quotedText;
}).then(function(q) { }).then(function(q) {
var postUrl = "" + location.protocol + "//" + location.host + (post.get('url')), const postUrl = "" + location.protocol + "//" + location.host + (post.get('url')),
postLink = "[" + self.get('title') + "](" + postUrl + ")"; postLink = "[" + self.get('title') + "](" + postUrl + ")";
composerController.appendText(I18n.t("post.continue_discussion", { postLink: postLink }) + "\n\n" + q); composerController.appendText(I18n.t("post.continue_discussion", { postLink: postLink }) + "\n\n" + q);
}); });
}, },
expandFirstPost: function(post) { expandFirstPost(post) {
var self = this; const self = this;
this.set('loadingExpanded', true); this.set('loadingExpanded', true);
post.expand().then(function() { post.expand().then(function() {
self.set('firstPostExpanded', true); self.set('firstPostExpanded', true);
@ -422,8 +417,8 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
}); });
}, },
retryLoading: function() { retryLoading() {
var self = this; const self = this;
self.set('retrying', true); self.set('retrying', true);
this.get('postStream').refresh().then(function() { this.get('postStream').refresh().then(function() {
self.set('retrying', false); self.set('retrying', false);
@ -432,7 +427,7 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
}); });
}, },
toggleWiki: function(post) { toggleWiki(post) {
// the request to the server is made in an observer in the post class // the request to the server is made in an observer in the post class
post.toggleProperty('wiki'); post.toggleProperty('wiki');
}, },
@ -449,21 +444,21 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
} }
}, },
rebakePost: function (post) { rebakePost(post) {
post.rebake(); post.rebake();
}, },
unhidePost: function (post) { unhidePost(post) {
post.unhide(); post.unhide();
} }
}, },
togglePinnedState: function() { togglePinnedState() {
this.send('togglePinnedForUser'); this.send('togglePinnedForUser');
}, },
showExpandButton: function() { showExpandButton: function() {
var post = this.get('post'); const post = this.get('post');
return post.get('post_number') === 1 && post.get('topic.expandable_first_post'); return post.get('post_number') === 1 && post.get('topic.expandable_first_post');
}.property(), }.property(),
@ -495,12 +490,12 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
}.property('selectedPostsCount', 'allPostsSelected'), }.property('selectedPostsCount', 'allPostsSelected'),
canDeleteSelected: function() { canDeleteSelected: function() {
var selectedPosts = this.get('selectedPosts'); const selectedPosts = this.get('selectedPosts');
if (this.get('allPostsSelected')) return true; if (this.get('allPostsSelected')) return true;
if (this.get('selectedPostsCount') === 0) return false; if (this.get('selectedPostsCount') === 0) return false;
var canDelete = true; let canDelete = true;
selectedPosts.forEach(function(p) { selectedPosts.forEach(function(p) {
if (!p.get('can_delete')) { if (!p.get('can_delete')) {
canDelete = false; canDelete = false;
@ -520,19 +515,19 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
} }
}.observes('multiSelect'), }.observes('multiSelect'),
deselectPost: function(post) { deselectPost(post) {
this.get('selectedPosts').removeObject(post); this.get('selectedPosts').removeObject(post);
var selectedReplies = this.get('selectedReplies'); const selectedReplies = this.get('selectedReplies');
selectedReplies.removeObject(post); selectedReplies.removeObject(post);
var selectedReply = selectedReplies.findProperty('post_number', post.get('reply_to_post_number')); const selectedReply = selectedReplies.findProperty('post_number', post.get('reply_to_post_number'));
if (selectedReply) { selectedReplies.removeObject(selectedReply); } if (selectedReply) { selectedReplies.removeObject(selectedReply); }
this.set('allPostsSelected', false); this.set('allPostsSelected', false);
}, },
postSelected: function(post) { postSelected(post) {
if (this.get('allPostsSelected')) { return true; } if (this.get('allPostsSelected')) { return true; }
if (this.get('selectedPosts').contains(post)) { return true; } if (this.get('selectedPosts').contains(post)) { return true; }
if (this.get('selectedReplies').findProperty('post_number', post.get('reply_to_post_number'))) { return true; } if (this.get('selectedReplies').findProperty('post_number', post.get('reply_to_post_number'))) { return true; }
@ -548,11 +543,11 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
return spinnerHTML; return spinnerHTML;
}.property(), }.property(),
recoverTopic: function() { recoverTopic() {
this.get('content').recover(); this.get('content').recover();
}, },
deleteTopic: function() { deleteTopic() {
this.unsubscribe(); this.unsubscribe();
this.get('content').destroy(Discourse.User.current()); this.get('content').destroy(Discourse.User.current());
}, },
@ -600,8 +595,8 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
}); });
}, },
unsubscribe: function() { unsubscribe() {
var topicId = this.get('content.id'); const topicId = this.get('content.id');
if (!topicId) return; if (!topicId) return;
// there is a condition where the view never calls unsubscribe, navigate to a topic from a topic // there is a condition where the view never calls unsubscribe, navigate to a topic from a topic
@ -609,12 +604,12 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
}, },
// Topic related // Topic related
reply: function() { reply() {
this.replyToPost(); this.replyToPost();
}, },
performTogglePost: function(post) { performTogglePost(post) {
var selectedPosts = this.get('selectedPosts'); const selectedPosts = this.get('selectedPosts');
if (this.postSelected(post)) { if (this.postSelected(post)) {
this.deselectPost(post); this.deselectPost(post);
return false; return false;
@ -631,14 +626,14 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
// If our current post is changed, notify the router // If our current post is changed, notify the router
_currentPostChanged: function() { _currentPostChanged: function() {
var currentPost = this.get('currentPost'); const currentPost = this.get('currentPost');
if (currentPost) { if (currentPost) {
this.send('postChangedRoute', currentPost); this.send('postChangedRoute', currentPost);
} }
}.observes('currentPost'), }.observes('currentPost'),
readPosts: function(topicId, postNumbers) { readPosts(topicId, postNumbers) {
var postStream = this.get('postStream'); const postStream = this.get('postStream');
if(this.get('postStream.topic.id') === topicId){ if(this.get('postStream.topic.id') === topicId){
_.each(postStream.get('posts'), function(post){ _.each(postStream.get('posts'), function(post){
@ -649,7 +644,7 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
} }
}); });
var max = _.max(postNumbers); const max = _.max(postNumbers);
if(max > this.get('last_read_post_number')){ if(max > this.get('last_read_post_number')){
this.set('last_read_post_number', max); this.set('last_read_post_number', max);
} }
@ -657,10 +652,10 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
}, },
// Called the the topmost visible post on the page changes. // Called the the topmost visible post on the page changes.
topVisibleChanged: function(post) { topVisibleChanged(post) {
if (!post) { return; } if (!post) { return; }
var postStream = this.get('postStream'), const postStream = this.get('postStream'),
firstLoadedPost = postStream.get('firstLoadedPost'); firstLoadedPost = postStream.get('firstLoadedPost');
this.set('currentPost', post.get('post_number')); this.set('currentPost', post.get('post_number'));
@ -671,16 +666,16 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
// Note: jQuery shouldn't be done in a controller, but how else can we // Note: jQuery shouldn't be done in a controller, but how else can we
// trigger a scroll after a promise resolves in a controller? We need // trigger a scroll after a promise resolves in a controller? We need
// to do this to preserve upwards infinte scrolling. // to do this to preserve upwards infinte scrolling.
var $body = $('body'), const $body = $('body');
$elem = $('#post-cloak-' + post.get('post_number')), let $elem = $('#post-cloak-' + post.get('post_number'));
distToElement = $body.scrollTop() - $elem.position().top; const distToElement = $body.scrollTop() - $elem.position().top;
postStream.prependMore().then(function() { postStream.prependMore().then(function() {
Em.run.next(function () { Em.run.next(function () {
$elem = $('#post-cloak-' + post.get('post_number')); $elem = $('#post-cloak-' + post.get('post_number'));
// Quickly going back might mean the element is destroyed // Quickly going back might mean the element is destroyed
var position = $elem.position(); const position = $elem.position();
if (position && position.top) { if (position && position.top) {
$('html, body').scrollTop(position.top + distToElement); $('html, body').scrollTop(position.top + distToElement);
} }
@ -695,10 +690,10 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
@method bottomVisibleChanged @method bottomVisibleChanged
@params {Discourse.Post} post that is at the bottom @params {Discourse.Post} post that is at the bottom
**/ **/
bottomVisibleChanged: function(post) { bottomVisibleChanged(post) {
if (!post) { return; } if (!post) { return; }
var postStream = this.get('postStream'), const postStream = this.get('postStream'),
lastLoadedPost = postStream.get('lastLoadedPost'); lastLoadedPost = postStream.get('lastLoadedPost');
this.set('controllers.topic-progress.progressPosition', postStream.progressIndexOfPost(post)); this.set('controllers.topic-progress.progressPosition', postStream.progressIndexOfPost(post));

View File

@ -1,32 +1,32 @@
var CLOSED = 'closed', const CLOSED = 'closed',
SAVING = 'saving', SAVING = 'saving',
OPEN = 'open', OPEN = 'open',
DRAFT = 'draft', DRAFT = 'draft',
// The actions the composer can take // The actions the composer can take
CREATE_TOPIC = 'createTopic', CREATE_TOPIC = 'createTopic',
PRIVATE_MESSAGE = 'privateMessage', PRIVATE_MESSAGE = 'privateMessage',
REPLY = 'reply', REPLY = 'reply',
EDIT = 'edit', EDIT = 'edit',
REPLY_AS_NEW_TOPIC_KEY = "reply_as_new_topic", REPLY_AS_NEW_TOPIC_KEY = "reply_as_new_topic",
// When creating, these fields are moved into the post model from the composer model // When creating, these fields are moved into the post model from the composer model
_create_serializer = { _create_serializer = {
raw: 'reply', raw: 'reply',
title: 'title', title: 'title',
category: 'categoryId', category: 'categoryId',
topic_id: 'topic.id', topic_id: 'topic.id',
is_warning: 'isWarning', is_warning: 'isWarning',
archetype: 'archetypeId', archetype: 'archetypeId',
target_usernames: 'targetUsernames' target_usernames: 'targetUsernames'
}, },
_edit_topic_serializer = { _edit_topic_serializer = {
title: 'topic.title', title: 'topic.title',
categoryId: 'topic.category.id' categoryId: 'topic.category.id'
}; };
Discourse.Composer = Discourse.Model.extend({ const Composer = Discourse.Model.extend({
archetypes: function() { archetypes: function() {
return Discourse.Site.currentProp('archetypes'); return Discourse.Site.currentProp('archetypes');
@ -46,7 +46,6 @@ Discourse.Composer = Discourse.Model.extend({
viewOpen: Em.computed.equal('composeState', OPEN), viewOpen: Em.computed.equal('composeState', OPEN),
viewDraft: Em.computed.equal('composeState', DRAFT), viewDraft: Em.computed.equal('composeState', DRAFT),
archetype: function() { archetype: function() {
return this.get('archetypes').findProperty('id', this.get('archetypeId')); return this.get('archetypes').findProperty('id', this.get('archetypeId'));
}.property('archetypeId'), }.property('archetypeId'),
@ -61,18 +60,18 @@ Discourse.Composer = Discourse.Model.extend({
// Determine the appropriate title for this action // Determine the appropriate title for this action
actionTitle: function() { actionTitle: function() {
var topic = this.get('topic'); const topic = this.get('topic');
var postLink, topicLink; let postLink, topicLink;
if (topic) { if (topic) {
var postNumber = this.get('post.post_number'); const postNumber = this.get('post.post_number');
postLink = "<a href='" + (topic.get('url')) + "/" + postNumber + "'>" + postLink = "<a href='" + (topic.get('url')) + "/" + postNumber + "'>" +
I18n.t("post.post_number", { number: postNumber }) + "</a>"; I18n.t("post.post_number", { number: postNumber }) + "</a>";
topicLink = "<a href='" + (topic.get('url')) + "'> " + (Handlebars.Utils.escapeExpression(topic.get('title'))) + "</a>"; topicLink = "<a href='" + (topic.get('url')) + "'> " + (Handlebars.Utils.escapeExpression(topic.get('title'))) + "</a>";
} }
var postDescription, let postDescription;
post = this.get('post'); const post = this.get('post');
if (post) { if (post) {
postDescription = I18n.t('post.' + this.get('action'), { postDescription = I18n.t('post.' + this.get('action'), {
@ -82,8 +81,8 @@ Discourse.Composer = Discourse.Model.extend({
}); });
if (!Discourse.Mobile.mobileView) { if (!Discourse.Mobile.mobileView) {
var replyUsername = post.get('reply_to_user.username'); const replyUsername = post.get('reply_to_user.username');
var replyAvatarTemplate = post.get('reply_to_user.avatar_template'); const replyAvatarTemplate = post.get('reply_to_user.avatar_template');
if (replyUsername && replyAvatarTemplate && this.get('action') === EDIT) { if (replyUsername && replyAvatarTemplate && this.get('action') === EDIT) {
postDescription += " " + I18n.t("post.in_reply_to") + " " + Discourse.Utilities.tinyAvatar(replyAvatarTemplate) + " " + replyUsername; postDescription += " " + I18n.t("post.in_reply_to") + " " + Discourse.Utilities.tinyAvatar(replyAvatarTemplate) + " " + replyUsername;
} }
@ -164,7 +163,7 @@ Discourse.Composer = Discourse.Model.extend({
}.property('action'), }.property('action'),
hasMetaData: function() { hasMetaData: function() {
var metaData = this.get('metaData'); const metaData = this.get('metaData');
return metaData ? Em.isEmpty(Em.keys(this.get('metaData'))) : false; return metaData ? Em.isEmpty(Em.keys(this.get('metaData'))) : false;
}.property('metaData'), }.property('metaData'),
@ -227,7 +226,7 @@ Discourse.Composer = Discourse.Model.extend({
@property titleLength @property titleLength
**/ **/
titleLength: function() { titleLength: function() {
var title = this.get('title') || ""; const title = this.get('title') || "";
return title.replace(/\s+/img, " ").trim().length; return title.replace(/\s+/img, " ").trim().length;
}.property('title'), }.property('title'),
@ -237,17 +236,16 @@ Discourse.Composer = Discourse.Model.extend({
@property replyLength @property replyLength
**/ **/
replyLength: function() { replyLength: function() {
var reply = this.get('reply') || ""; let reply = this.get('reply') || "";
while (Discourse.Quote.REGEXP.test(reply)) { reply = reply.replace(Discourse.Quote.REGEXP, ""); } while (Discourse.Quote.REGEXP.test(reply)) { reply = reply.replace(Discourse.Quote.REGEXP, ""); }
return reply.replace(/\s+/img, " ").trim().length; return reply.replace(/\s+/img, " ").trim().length;
}.property('reply'), }.property('reply'),
init: function() { _setupComposer: function() {
this._super(); const val = (Discourse.Mobile.mobileView ? false : (Discourse.KeyValueStore.get('composer.showPreview') || 'true'));
var val = (Discourse.Mobile.mobileView ? false : (Discourse.KeyValueStore.get('composer.showPreview') || 'true'));
this.set('showPreview', val === 'true'); this.set('showPreview', val === 'true');
this.set('archetypeId', Discourse.Site.currentProp('default_archetype')); this.set('archetypeId', Discourse.Site.currentProp('default_archetype'));
}, }.on('init'),
/** /**
Append text to the current reply Append text to the current reply
@ -255,17 +253,16 @@ Discourse.Composer = Discourse.Model.extend({
@method appendText @method appendText
@param {String} text the text to append @param {String} text the text to append
**/ **/
appendText: function(text,position,opts) { appendText(text,position,opts) {
var reply = (this.get('reply') || ''); const reply = (this.get('reply') || '');
position = typeof(position) === "number" ? position : reply.length; position = typeof(position) === "number" ? position : reply.length;
var before = reply.slice(0, position) || ''; let before = reply.slice(0, position) || '';
var after = reply.slice(position) || ''; let after = reply.slice(position) || '';
var stripped, i; let stripped, i;
if (opts && opts.block){
if(opts && opts.block){ if (before.trim() !== ""){
if(before.trim() !== ""){
stripped = before.replace(/\r/g, ""); stripped = before.replace(/\r/g, "");
for(i=0; i<2; i++){ for(i=0; i<2; i++){
if(stripped[stripped.length - 1 - i] !== "\n"){ if(stripped[stripped.length - 1 - i] !== "\n"){
@ -298,7 +295,7 @@ Discourse.Composer = Discourse.Model.extend({
return before.length + text.length; return before.length + text.length;
}, },
togglePreview: function() { togglePreview() {
this.toggleProperty('showPreview'); this.toggleProperty('showPreview');
Discourse.KeyValueStore.set({ key: 'composer.showPreview', value: this.get('showPreview') }); Discourse.KeyValueStore.set({ key: 'composer.showPreview', value: this.get('showPreview') });
}, },
@ -312,13 +309,13 @@ Discourse.Composer = Discourse.Model.extend({
topic - The topic we're replying to, if present topic - The topic we're replying to, if present
quote - If we're opening a reply from a quote, the quote we're making quote - If we're opening a reply from a quote, the quote we're making
*/ */
open: function(opts) { open(opts) {
if (!opts) opts = {}; if (!opts) opts = {};
this.set('loading', false); this.set('loading', false);
var replyBlank = Em.isEmpty(this.get("reply")); const replyBlank = Em.isEmpty(this.get("reply"));
var composer = this; const composer = this;
if (!replyBlank && if (!replyBlank &&
(opts.action !== this.get('action') || ((opts.reply || opts.action === this.EDIT) && this.get('reply') !== this.get('originalText'))) && (opts.action !== this.get('action') || ((opts.reply || opts.action === this.EDIT) && this.get('reply') !== this.get('originalText'))) &&
!opts.tested) { !opts.tested) {
@ -363,7 +360,7 @@ Discourse.Composer = Discourse.Model.extend({
// If we are editing a post, load it. // If we are editing a post, load it.
if (opts.action === EDIT && opts.post) { if (opts.action === EDIT && opts.post) {
var topicProps = this.serialize(_edit_topic_serializer); const topicProps = this.serialize(_edit_topic_serializer);
topicProps.loading = true; topicProps.loading = true;
this.setProperties(topicProps); this.setProperties(topicProps);
@ -387,7 +384,7 @@ Discourse.Composer = Discourse.Model.extend({
return false; return false;
}, },
save: function(opts) { save(opts) {
if( !this.get('cantSubmitPost') ) { if( !this.get('cantSubmitPost') ) {
return this.get('editingPost') ? this.editPost(opts) : this.createPost(opts); return this.get('editingPost') ? this.editPost(opts) : this.createPost(opts);
} }
@ -398,7 +395,7 @@ Discourse.Composer = Discourse.Model.extend({
@method clearState @method clearState
**/ **/
clearState: function() { clearState() {
this.setProperties({ this.setProperties({
originalText: null, originalText: null,
reply: null, reply: null,
@ -410,11 +407,11 @@ Discourse.Composer = Discourse.Model.extend({
}, },
// When you edit a post // When you edit a post
editPost: function(opts) { editPost(opts) {
var post = this.get('post'), const post = this.get('post'),
oldCooked = post.get('cooked'), oldCooked = post.get('cooked'),
self = this, self = this;
promise; let promise;
// Update the title if we've changed it, otherwise consider it a // Update the title if we've changed it, otherwise consider it a
// successful resolved promise // successful resolved promise
@ -422,7 +419,7 @@ Discourse.Composer = Discourse.Model.extend({
post.get('post_number') === 1 && post.get('post_number') === 1 &&
this.get('topic.details.can_edit')) { this.get('topic.details.can_edit')) {
var topicProps = this.getProperties(Object.keys(_edit_topic_serializer)); const topicProps = this.getProperties(Object.keys(_edit_topic_serializer));
promise = Discourse.Topic.update(this.get('topic'), topicProps); promise = Discourse.Topic.update(this.get('topic'), topicProps);
} else { } else {
promise = Ember.RSVP.resolve(); promise = Ember.RSVP.resolve();
@ -441,7 +438,7 @@ Discourse.Composer = Discourse.Model.extend({
post.updateFromPost(result); post.updateFromPost(result);
self.clearState(); self.clearState();
}).catch(function(error) { }).catch(function(error) {
var response = $.parseJSON(error.responseText); const response = $.parseJSON(error.responseText);
if (response && response.errors) { if (response && response.errors) {
return(response.errors[0]); return(response.errors[0]);
} else { } else {
@ -453,14 +450,14 @@ Discourse.Composer = Discourse.Model.extend({
}); });
}, },
serialize: function(serializer, dest) { serialize(serializer, dest) {
if (!dest) { if (!dest) {
dest = {}; dest = {};
} }
var self = this; const self = this;
Object.keys(serializer).forEach(function(f) { Object.keys(serializer).forEach(function(f) {
var val = self.get(serializer[f]); const val = self.get(serializer[f]);
if (typeof val !== 'undefined') { if (typeof val !== 'undefined') {
Ember.set(dest, f, val); Ember.set(dest, f, val);
} }
@ -469,15 +466,15 @@ Discourse.Composer = Discourse.Model.extend({
}, },
// Create a new Post // Create a new Post
createPost: function(opts) { createPost(opts) {
var post = this.get('post'), const post = this.get('post'),
topic = this.get('topic'), topic = this.get('topic'),
currentUser = Discourse.User.current(), currentUser = Discourse.User.current(),
postStream = this.get('topic.postStream'), postStream = this.get('topic.postStream');
addedToStream = false; let addedToStream = false;
// Build the post object // Build the post object
var createdPost = Discourse.Post.create({ const createdPost = Discourse.Post.create({
imageSizes: opts.imageSizes, imageSizes: opts.imageSizes,
cooked: this.getCookedHtml(), cooked: this.getCookedHtml(),
reply_count: 0, reply_count: 0,
@ -509,7 +506,7 @@ Discourse.Composer = Discourse.Model.extend({
}); });
} }
var state = null; let state = null;
// If we're in a topic, we can append the post instantly. // If we're in a topic, we can append the post instantly.
if (postStream) { if (postStream) {
@ -533,12 +530,12 @@ Discourse.Composer = Discourse.Model.extend({
} }
} }
var composer = this; const composer = this;
var promise = new Ember.RSVP.Promise(function(resolve, reject) { const promise = new Ember.RSVP.Promise(function(resolve, reject) {
composer.set('composeState', SAVING); composer.set('composeState', SAVING);
createdPost.save(function(result) { createdPost.save(function(result) {
var saving = true; let saving = true;
createdPost.updateFromJson(result); createdPost.updateFromJson(result);
@ -555,7 +552,7 @@ Discourse.Composer = Discourse.Model.extend({
saving = false; saving = false;
// Update topic_count for the category // Update topic_count for the category
var category = Discourse.Site.currentProp('categories').find(function(x) { return x.get('id') === (parseInt(createdPost.get('category'),10) || 1); }); const category = Discourse.Site.currentProp('categories').find(function(x) { return x.get('id') === (parseInt(createdPost.get('category'),10) || 1); });
if (category) category.incrementProperty('topic_count'); if (category) category.incrementProperty('topic_count');
Discourse.notifyPropertyChange('globalNotice'); Discourse.notifyPropertyChange('globalNotice');
} }
@ -578,9 +575,9 @@ Discourse.Composer = Discourse.Model.extend({
composer.set('composeState', OPEN); composer.set('composeState', OPEN);
// TODO extract error handling code // TODO extract error handling code
var parsedError; let parsedError;
try { try {
var parsedJSON = $.parseJSON(error.responseText); const parsedJSON = $.parseJSON(error.responseText);
if (parsedJSON.errors) { if (parsedJSON.errors) {
parsedError = parsedJSON.errors[0]; parsedError = parsedJSON.errors[0];
} else if (parsedJSON.failed) { } else if (parsedJSON.failed) {
@ -599,11 +596,11 @@ Discourse.Composer = Discourse.Model.extend({
return promise; return promise;
}, },
getCookedHtml: function() { getCookedHtml() {
return $('#wmd-preview').html().replace(/<span class="marker"><\/span>/g, ''); return $('#wmd-preview').html().replace(/<span class="marker"><\/span>/g, '');
}, },
saveDraft: function() { saveDraft() {
// Do not save when drafts are disabled // Do not save when drafts are disabled
if (this.get('disableDrafts')) return; if (this.get('disableDrafts')) return;
// Do not save when there is no reply // Do not save when there is no reply
@ -611,7 +608,7 @@ Discourse.Composer = Discourse.Model.extend({
// Do not save when the reply's length is too small // Do not save when the reply's length is too small
if (this.get('replyLength') < Discourse.SiteSettings.min_post_length) return; if (this.get('replyLength') < Discourse.SiteSettings.min_post_length) return;
var data = { const data = {
reply: this.get('reply'), reply: this.get('reply'),
action: this.get('action'), action: this.get('action'),
title: this.get('title'), title: this.get('title'),
@ -624,7 +621,7 @@ Discourse.Composer = Discourse.Model.extend({
this.set('draftStatus', I18n.t('composer.saving_draft_tip')); this.set('draftStatus', I18n.t('composer.saving_draft_tip'));
var composer = this; const composer = this;
// try to save the draft // try to save the draft
return Discourse.Draft.save(this.get('draftKey'), this.get('draftSequence'), data) return Discourse.Draft.save(this.get('draftKey'), this.get('draftSequence'), data)
@ -637,16 +634,21 @@ Discourse.Composer = Discourse.Model.extend({
}); });
Discourse.Composer.reopenClass({ Composer.reopenClass({
open: function(opts) { open(opts) {
var composer = Discourse.Composer.create(); const composer = Composer.create();
composer.open(opts); composer.open(opts);
return composer; return composer;
}, },
loadDraft: function(draftKey, draftSequence, draft, topic) { loadDraft(opts) {
var composer; opts = opts || {};
let draft = opts.draft;
const draftKey = opts.draftKey;
const draftSequence = opts.draftSequence;
try { try {
if (draft && typeof draft === 'string') { if (draft && typeof draft === 'string') {
draft = JSON.parse(draft); draft = JSON.parse(draft);
@ -656,13 +658,12 @@ Discourse.Composer.reopenClass({
Discourse.Draft.clear(draftKey, draftSequence); Discourse.Draft.clear(draftKey, draftSequence);
} }
if (draft && ((draft.title && draft.title !== '') || (draft.reply && draft.reply !== ''))) { if (draft && ((draft.title && draft.title !== '') || (draft.reply && draft.reply !== ''))) {
composer = this.open({ return this.open({
draftKey: draftKey, draftKey,
draftSequence: draftSequence, draftSequence,
topic: topic,
action: draft.action, action: draft.action,
title: draft.title, title: draft.title,
categoryId: draft.categoryId, categoryId: draft.categoryId || opts.categoryId,
postId: draft.postId, postId: draft.postId,
archetypeId: draft.archetypeId, archetypeId: draft.archetypeId,
reply: draft.reply, reply: draft.reply,
@ -672,35 +673,36 @@ Discourse.Composer.reopenClass({
composerState: DRAFT composerState: DRAFT
}); });
} }
return composer;
}, },
serializeToTopic: function(fieldName, property) { serializeToTopic(fieldName, property) {
if (!property) { property = fieldName; } if (!property) { property = fieldName; }
_edit_topic_serializer[fieldName] = property; _edit_topic_serializer[fieldName] = property;
}, },
serializeOnCreate: function(fieldName, property) { serializeOnCreate(fieldName, property) {
if (!property) { property = fieldName; } if (!property) { property = fieldName; }
_create_serializer[fieldName] = property; _create_serializer[fieldName] = property;
}, },
serializedFieldsForCreate: function() { serializedFieldsForCreate() {
return Object.keys(_create_serializer); return Object.keys(_create_serializer);
}, },
// The status the compose view can have // The status the compose view can have
CLOSED: CLOSED, CLOSED,
SAVING: SAVING, SAVING,
OPEN: OPEN, OPEN,
DRAFT: DRAFT, DRAFT,
// The actions the composer can take // The actions the composer can take
CREATE_TOPIC: CREATE_TOPIC, CREATE_TOPIC,
PRIVATE_MESSAGE: PRIVATE_MESSAGE, PRIVATE_MESSAGE,
REPLY: REPLY, REPLY,
EDIT: EDIT, EDIT,
// Draft key // Draft key
REPLY_AS_NEW_TOPIC_KEY: REPLY_AS_NEW_TOPIC_KEY REPLY_AS_NEW_TOPIC_KEY
}); });
export default Composer;