mirror of
https://github.com/discourse/discourse.git
synced 2025-02-04 16:33:01 +08:00
160 lines
4.9 KiB
JavaScript
160 lines
4.9 KiB
JavaScript
import loadScript from 'discourse/lib/load-script';
|
|
import Quote from 'discourse/lib/quote';
|
|
import computed from 'ember-addons/ember-computed-decorators';
|
|
|
|
export default Ember.Controller.extend({
|
|
needs: ['topic', 'composer'],
|
|
|
|
_loadSanitizer: function() {
|
|
loadScript('defer/html-sanitizer-bundle');
|
|
}.on('init'),
|
|
|
|
@computed('buffer', 'postId')
|
|
post(buffer, postId) {
|
|
if (!postId || Ember.isEmpty(buffer)) { return null; }
|
|
|
|
const postStream = this.get('controllers.topic.model.postStream');
|
|
const post = postStream.findLoadedPost(postId);
|
|
|
|
return post;
|
|
},
|
|
|
|
// Save the currently selected text and displays the
|
|
// "quote reply" button
|
|
selectText(postId) {
|
|
// anonymous users cannot "quote-reply"
|
|
if (!this.currentUser) return;
|
|
|
|
// don't display the "quote-reply" button if we can't reply
|
|
const topicDetails = this.get('controllers.topic.model.details');
|
|
if (!(topicDetails.get('can_reply_as_new_topic') || topicDetails.get('can_create_post'))) {
|
|
return;
|
|
}
|
|
|
|
const selection = window.getSelection();
|
|
|
|
// no selections
|
|
if (selection.isCollapsed) {
|
|
this.set('buffer', '');
|
|
return;
|
|
}
|
|
|
|
// retrieve the selected range
|
|
const range = selection.getRangeAt(0),
|
|
cloned = range.cloneRange(),
|
|
$ancestor = $(range.commonAncestorContainer);
|
|
|
|
if ($ancestor.closest('.cooked').length === 0) {
|
|
this.set('buffer', '');
|
|
return;
|
|
}
|
|
|
|
const selectedText = Discourse.Utilities.selectedText();
|
|
if (this.get('buffer') === selectedText) return;
|
|
|
|
// we need to retrieve the post data from the posts collection in the topic controller
|
|
this.set('postId', postId);
|
|
this.set('buffer', selectedText);
|
|
|
|
// create a marker element
|
|
const markerElement = document.createElement("span");
|
|
// containing a single invisible character
|
|
markerElement.appendChild(document.createTextNode("\ufeff"));
|
|
|
|
const isMobileDevice = this.site.isMobileDevice;
|
|
const capabilities = this.capabilities,
|
|
isIOS = capabilities.isIOS,
|
|
isAndroid = capabilities.isAndroid;
|
|
|
|
// collapse the range at the beginning/end of the selection
|
|
range.collapse(!isMobileDevice);
|
|
// and insert it at the start of our selection range
|
|
range.insertNode(markerElement);
|
|
|
|
// retrieve the position of the marker
|
|
const markerOffset = $(markerElement).offset(),
|
|
$quoteButton = $('.quote-button');
|
|
|
|
// remove the marker
|
|
markerElement.parentNode.removeChild(markerElement);
|
|
|
|
// work around Chrome that would sometimes lose the selection
|
|
const sel = window.getSelection();
|
|
sel.removeAllRanges();
|
|
sel.addRange(cloned);
|
|
|
|
// move the quote button above the marker
|
|
Em.run.schedule('afterRender', function() {
|
|
let topOff = markerOffset.top;
|
|
let leftOff = markerOffset.left;
|
|
|
|
if (isMobileDevice || isIOS || isAndroid) {
|
|
topOff = topOff + 20;
|
|
leftOff = Math.min(leftOff + 10, $(window).width() - $quoteButton.outerWidth());
|
|
} else {
|
|
topOff = topOff - $quoteButton.outerHeight() - 5;
|
|
}
|
|
|
|
$quoteButton.offset({ top: topOff, left: leftOff });
|
|
});
|
|
},
|
|
|
|
quoteText() {
|
|
const Composer = require('discourse/models/composer').default;
|
|
const postId = this.get('postId');
|
|
const post = this.get('post');
|
|
|
|
// defer load if needed, if in an expanded replies section
|
|
if (!post) {
|
|
const postStream = this.get('controllers.topic.model.postStream');
|
|
return postStream.loadPost(postId).then(p => {
|
|
this.set('post', p);
|
|
return this.quoteText();
|
|
});
|
|
}
|
|
|
|
// If we can't create a post, delegate to reply as new topic
|
|
if (!this.get('controllers.topic.model.details.can_create_post')) {
|
|
this.get('controllers.topic').send('replyAsNewTopic', post);
|
|
return;
|
|
}
|
|
|
|
const composerController = this.get('controllers.composer');
|
|
const composerOpts = {
|
|
action: Composer.REPLY,
|
|
draftKey: post.get('topic.draft_key')
|
|
};
|
|
|
|
if (post.get('post_number') === 1) {
|
|
composerOpts.topic = post.get("topic");
|
|
} else {
|
|
composerOpts.post = post;
|
|
}
|
|
|
|
// If the composer is associated with a different post, we don't change it.
|
|
const composerPost = composerController.get('content.post');
|
|
if (composerPost && (composerPost.get('id') !== this.get('post.id'))) {
|
|
composerOpts.post = composerPost;
|
|
}
|
|
|
|
const buffer = this.get('buffer');
|
|
const quotedText = Quote.build(post, buffer);
|
|
composerOpts.quote = quotedText;
|
|
if (composerController.get('content.viewOpen') || composerController.get('content.viewDraft')) {
|
|
this.appEvents.trigger('composer:insert-text', quotedText);
|
|
} else {
|
|
composerController.open(composerOpts);
|
|
}
|
|
this.set('buffer', '');
|
|
return false;
|
|
},
|
|
|
|
deselectText() {
|
|
// clear selected text
|
|
window.getSelection().removeAllRanges();
|
|
// clean up the buffer
|
|
this.set('buffer', '');
|
|
}
|
|
|
|
});
|