REFACTOR: Cleaner `quoteButton` object, and some bug fixes

This commit is contained in:
Robin Ward 2016-12-30 11:46:09 -05:00
parent 477b237e45
commit da76dd3d6b
5 changed files with 50 additions and 34 deletions

View File

@ -1,4 +1,3 @@
import computed from 'ember-addons/ember-computed-decorators';
import { selectedText } from 'discourse/lib/utilities'; import { selectedText } from 'discourse/lib/utilities';
// we don't want to deselect when we click on buttons that use it // we don't want to deselect when we click on buttons that use it
@ -10,17 +9,22 @@ function willQuote(e) {
export default Ember.Component.extend({ export default Ember.Component.extend({
classNames: ['quote-button'], classNames: ['quote-button'],
classNameBindings: ['visible'], classNameBindings: ['visible'],
visible: false,
@computed('quoteState.buffer')
visible: buffer => buffer && buffer.length > 0,
_isMouseDown: false, _isMouseDown: false,
_reselected: false, _reselected: false,
_hideButton() {
this.get('quoteState').clear();
this.set('visible', false);
},
_selectionChanged() { _selectionChanged() {
const quoteState = this.get('quoteState');
const selection = window.getSelection(); const selection = window.getSelection();
if (selection.isCollapsed) { if (selection.isCollapsed) {
if (this.get("visible")) this.sendAction("deselectText"); if (this.get("visible")) { this._hideButton(); }
return; return;
} }
@ -34,12 +38,13 @@ export default Ember.Component.extend({
postId = postId || $ancestor.closest('.boxed, .reply').data('post-id'); postId = postId || $ancestor.closest('.boxed, .reply').data('post-id');
if ($ancestor.closest(".contents").length === 0 || !postId) { if ($ancestor.closest(".contents").length === 0 || !postId) {
if (this.get("visible")) this.sendAction("deselectText"); if (this.get("visible")) { this._hideButton(); }
return; return;
} }
} }
this.get("quoteState").setProperties({ postId, buffer: selectedText() }); quoteState.selected(postId, selectedText());
this.set('visible', quoteState.buffer.length > 0);
// on Desktop, shows the button at the beginning of the selection // on Desktop, shows the button at the beginning of the selection
// on Mobile, shows the button at the end of the selection // on Mobile, shows the button at the end of the selection
@ -97,11 +102,11 @@ export default Ember.Component.extend({
const wait = (isWinphone || isAndroid) ? 250 : 25; const wait = (isWinphone || isAndroid) ? 250 : 25;
const onSelectionChanged = _.debounce(() => this._selectionChanged(), wait); const onSelectionChanged = _.debounce(() => this._selectionChanged(), wait);
$(document).on("mousedown.quote-button", (e) => { $(document).on("mousedown.quote-button", e => {
this._isMouseDown = true; this._isMouseDown = true;
this._reselected = false; this._reselected = false;
if (!willQuote(e)) { if (!willQuote(e)) {
this.sendAction("deselectText"); this._hideButton();
} }
}).on("mouseup.quote-button", () => { }).on("mouseup.quote-button", () => {
this._isMouseDown = false; this._isMouseDown = false;
@ -120,7 +125,8 @@ export default Ember.Component.extend({
}, },
click() { click() {
this.sendAction("selectText"); const { postId, buffer } = this.get('quoteState');
this.attrs.selectText(postId, buffer).then(() => this._hideButton());
return false; return false;
} }
}); });

View File

@ -11,6 +11,7 @@ import { categoryBadgeHTML } from 'discourse/helpers/category-link';
import Post from 'discourse/models/post'; import Post from 'discourse/models/post';
import debounce from 'discourse/lib/debounce'; import debounce from 'discourse/lib/debounce';
import isElementInViewport from "discourse/lib/is-element-in-viewport"; import isElementInViewport from "discourse/lib/is-element-in-viewport";
import QuoteState from 'discourse/lib/quote-state';
export default Ember.Controller.extend(SelectedPostsCount, BufferedContent, { export default Ember.Controller.extend(SelectedPostsCount, BufferedContent, {
composer: Ember.inject.controller(), composer: Ember.inject.controller(),
@ -119,7 +120,7 @@ export default Ember.Controller.extend(SelectedPostsCount, BufferedContent, {
this._super(); this._super();
this.set('selectedPosts', []); this.set('selectedPosts', []);
this.set('selectedReplies', []); this.set('selectedReplies', []);
this.set('quoteState', Ember.Object.create({ buffer: null, postId: null })); this.set('quoteState', new QuoteState());
}, },
showCategoryChooser: Ember.computed.not("model.isPrivateMessage"), showCategoryChooser: Ember.computed.not("model.isPrivateMessage"),
@ -167,16 +168,8 @@ export default Ember.Controller.extend(SelectedPostsCount, BufferedContent, {
this.send('showFeatureTopic'); this.send('showFeatureTopic');
}, },
deselectText() { selectText(postId, buffer) {
this.get('quoteState').setProperties({ buffer: null, postId: null }); return this.get('model.postStream').loadPost(postId).then(post => {
},
selectText() {
const { postId, buffer } = this.get('quoteState');
this.send('deselectText');
this.get('model.postStream').loadPost(postId).then(post => {
// If we can't create a post, delegate to reply as new topic // If we can't create a post, delegate to reply as new topic
if (!this.get('model.details.can_create_post')) { if (!this.get('model.details.can_create_post')) {
this.send('replyAsNewTopic', post); this.send('replyAsNewTopic', post);
@ -196,15 +189,19 @@ export default Ember.Controller.extend(SelectedPostsCount, BufferedContent, {
// If the composer is associated with a different post, we don't change it. // If the composer is associated with a different post, we don't change it.
const composer = this.get('composer'); const composer = this.get('composer');
const composerPost = composer.get('content.post'); const composerPost = composer.get('model.post');
if (composerPost && (composerPost.get('id') !== this.get('post.id'))) { if (composerPost && (composerPost.get('id') !== this.get('post.id'))) {
composerOpts.post = composerPost; composerOpts.post = composerPost;
} }
const quotedText = Quote.build(post, buffer); const quotedText = Quote.build(post, buffer);
composerOpts.quote = quotedText; composerOpts.quote = quotedText;
if (composer.get('content.viewOpen') || composer.get('content.viewDraft')) { if (composer.get('model.viewOpen')) {
this.appEvents.trigger('composer:insert-text', quotedText); this.appEvents.trigger('composer:insert-text', quotedText);
} else if (composer.get('model.viewDraft')) {
const model = composer.get('model');
model.set('reply', model.get('reply') + quotedText);
composer.send('openIfDraft');
} else { } else {
composer.open(composerOpts); composer.open(composerOpts);
} }
@ -314,9 +311,10 @@ export default Ember.Controller.extend(SelectedPostsCount, BufferedContent, {
const quoteState = this.get('quoteState'); const quoteState = this.get('quoteState');
const postStream = this.get('model.postStream'); const postStream = this.get('model.postStream');
const quotedPost = postStream.findLoadedPost(quoteState.get('postId')); const quotedPost = postStream.findLoadedPost(quoteState.postId);
const quotedText = Quote.build(quotedPost, quoteState.get('buffer')); const quotedText = Quote.build(quotedPost, quoteState.buffer);
this.send('deselectText');
quoteState.clear();
if (composerController.get('content.topic.id') === topic.get('id') && if (composerController.get('content.topic.id') === topic.get('id') &&
composerController.get('content.action') === Composer.REPLY) { composerController.get('content.action') === Composer.REPLY) {
@ -652,10 +650,9 @@ export default Ember.Controller.extend(SelectedPostsCount, BufferedContent, {
replyAsNewTopic(post) { replyAsNewTopic(post) {
const composerController = this.get('composer'); const composerController = this.get('composer');
const quoteState = this.get('quoteState'); const { quoteState } = this;
post = post || quoteState.get('post'); const quotedText = Quote.build(post, quoteState.buffer);
const quotedText = Quote.build(post, quoteState.get('buffer')); quoteState.clear();
this.send('deselectText');
composerController.open({ composerController.open({
action: Composer.CREATE_TOPIC, action: Composer.CREATE_TOPIC,

View File

@ -0,0 +1,15 @@
export default class QuoteState {
constructor() {
this.clear();
}
selected(postId, buffer) {
this.postId = postId;
this.buffer = buffer;
}
clear() {
this.buffer = '';
this.postId = null;
}
}

View File

@ -117,7 +117,6 @@ const TopicRoute = Discourse.Route.extend({
willTransition() { willTransition() {
this._super(); this._super();
this.controllerFor("topic").send('deselectText');
Em.run.cancel(scheduledReplace); Em.run.cancel(scheduledReplace);
isTransitioning = true; isTransitioning = true;
return true; return true;
@ -207,6 +206,7 @@ const TopicRoute = Discourse.Route.extend({
// close the multi select when switching topics // close the multi select when switching topics
controller.set('multiSelect', false); controller.set('multiSelect', false);
controller.get('quoteState').clear();
this.controllerFor('composer').set('topic', model); this.controllerFor('composer').set('topic', model);
this.topicTrackingState.trackIncoming('all'); this.topicTrackingState.trackIncoming('all');

View File

@ -273,8 +273,6 @@
{{share-popup topic=model replyAsNewTopic="replyAsNewTopic"}} {{share-popup topic=model replyAsNewTopic="replyAsNewTopic"}}
{{#if currentUser.enable_quoting}} {{#if currentUser.enable_quoting}}
{{quote-button quoteState=quoteState {{quote-button quoteState=quoteState selectText=(action "selectText")}}
selectText="selectText"
deselectText="deselectText"}}
{{/if}} {{/if}}
{{/discourse-topic}} {{/discourse-topic}}