FIX: Quoting an avatar when `default_avatars` was set was broken.
This commit is contained in:
parent
091af27a31
commit
893c1aa067
|
@ -8,9 +8,7 @@ export default DiscourseController.extend({
|
|||
loadScript('defer/html-sanitizer-bundle');
|
||||
}.on('init'),
|
||||
|
||||
/**
|
||||
If the buffer is cleared, clear out other state (post)
|
||||
**/
|
||||
// If the buffer is cleared, clear out other state (post)
|
||||
bufferChanged: function() {
|
||||
if (this.blank('buffer')) this.set('post', null);
|
||||
}.observes('buffer'),
|
||||
|
@ -18,40 +16,38 @@ export default DiscourseController.extend({
|
|||
/**
|
||||
Save the currently selected text and displays the
|
||||
"quote reply" button
|
||||
|
||||
@method selectText
|
||||
**/
|
||||
selectText: function(postId) {
|
||||
selectText(postId) {
|
||||
// anonymous users cannot "quote-reply"
|
||||
if (!Discourse.User.current()) return;
|
||||
|
||||
// don't display the "quote-reply" button if we can't create a post
|
||||
if (!this.get('controllers.topic.model.details.can_create_post')) return;
|
||||
|
||||
var selection = window.getSelection();
|
||||
const selection = window.getSelection();
|
||||
// no selections
|
||||
if (selection.rangeCount === 0) return;
|
||||
|
||||
// retrieve the selected range
|
||||
var range = selection.getRangeAt(0),
|
||||
cloned = range.cloneRange(),
|
||||
$ancestor = $(range.commonAncestorContainer);
|
||||
const range = selection.getRangeAt(0),
|
||||
cloned = range.cloneRange(),
|
||||
$ancestor = $(range.commonAncestorContainer);
|
||||
|
||||
if ($ancestor.closest('.cooked').length === 0) {
|
||||
this.set('buffer', '');
|
||||
return;
|
||||
}
|
||||
|
||||
var selectedText = Discourse.Utilities.selectedText();
|
||||
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
|
||||
var postStream = this.get('controllers.topic.postStream');
|
||||
const postStream = this.get('controllers.topic.postStream');
|
||||
this.set('post', postStream.findLoadedPost(postId));
|
||||
this.set('buffer', selectedText);
|
||||
|
||||
// create a marker element
|
||||
var markerElement = document.createElement("span");
|
||||
const markerElement = document.createElement("span");
|
||||
// containing a single invisible character
|
||||
markerElement.appendChild(document.createTextNode("\u{feff}"));
|
||||
|
||||
|
@ -61,42 +57,37 @@ export default DiscourseController.extend({
|
|||
range.insertNode(markerElement);
|
||||
|
||||
// retrieve the position of the market
|
||||
var markerOffset = $(markerElement).offset(),
|
||||
$quoteButton = $('.quote-button');
|
||||
const markerOffset = $(markerElement).offset(),
|
||||
$quoteButton = $('.quote-button');
|
||||
|
||||
// remove the marker
|
||||
markerElement.parentNode.removeChild(markerElement);
|
||||
|
||||
// work around Chrome that would sometimes lose the selection
|
||||
var sel = window.getSelection();
|
||||
const sel = window.getSelection();
|
||||
sel.removeAllRanges();
|
||||
sel.addRange(cloned);
|
||||
|
||||
// move the quote button above the marker
|
||||
Em.run.schedule('afterRender', function() {
|
||||
var top = markerOffset.top;
|
||||
var left = markerOffset.left;
|
||||
let topOff = markerOffset.top;
|
||||
let leftOff = markerOffset.left;
|
||||
|
||||
if (Discourse.Mobile.isMobileDevice) {
|
||||
top = top + 20;
|
||||
left = Math.min(left + 10, $(window).width() - $quoteButton.outerWidth());
|
||||
topOff = topOff + 20;
|
||||
leftOff = Math.min(leftOff + 10, $(window).width() - $quoteButton.outerWidth());
|
||||
} else {
|
||||
top = top - $quoteButton.outerHeight() - 5;
|
||||
topOff = topOff - $quoteButton.outerHeight() - 5;
|
||||
}
|
||||
|
||||
$quoteButton.offset({ top: top, left: left });
|
||||
$quoteButton.offset({ top: topOff, left: leftOff });
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
Quote the currently selected text
|
||||
|
||||
@method quoteText
|
||||
**/
|
||||
quoteText: function() {
|
||||
var post = this.get('post');
|
||||
var composerController = this.get('controllers.composer');
|
||||
var composerOpts = {
|
||||
quoteText() {
|
||||
const post = this.get('post');
|
||||
const composerController = this.get('controllers.composer');
|
||||
const composerOpts = {
|
||||
action: Discourse.Composer.REPLY,
|
||||
draftKey: this.get('post.topic.draft_key')
|
||||
};
|
||||
|
@ -108,13 +99,13 @@ export default DiscourseController.extend({
|
|||
}
|
||||
|
||||
// If the composer is associated with a different post, we don't change it.
|
||||
var composerPost = composerController.get('content.post');
|
||||
const composerPost = composerController.get('content.post');
|
||||
if (composerPost && (composerPost.get('id') !== this.get('post.id'))) {
|
||||
composerOpts.post = composerPost;
|
||||
}
|
||||
|
||||
var buffer = this.get('buffer');
|
||||
var quotedText = Discourse.Quote.build(post, buffer);
|
||||
const buffer = this.get('buffer');
|
||||
const quotedText = Discourse.Quote.build(post, buffer);
|
||||
composerOpts.quote = quotedText;
|
||||
if (composerController.get('content.viewOpen') || composerController.get('content.viewDraft')) {
|
||||
composerController.appendBlockAtCursor(quotedText.trim());
|
||||
|
@ -125,12 +116,7 @@ export default DiscourseController.extend({
|
|||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
Deselect the currently selected text
|
||||
|
||||
@method deselectText
|
||||
**/
|
||||
deselectText: function() {
|
||||
deselectText() {
|
||||
// clear selected text
|
||||
window.getSelection().removeAllRanges();
|
||||
// clean up the buffer
|
||||
|
|
|
@ -1,21 +1,8 @@
|
|||
/**
|
||||
Build the BBCode for a Quote
|
||||
|
||||
@class BBCode
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.Quote = {
|
||||
|
||||
REGEXP: /\[quote=([^\]]*)\]((?:[\s\S](?!\[quote=[^\]]*\]))*?)\[\/quote\]/im,
|
||||
|
||||
/**
|
||||
Build the BBCode quote around the selected text
|
||||
|
||||
@method buildQuote
|
||||
@param {Discourse.Post} post The post we are quoting
|
||||
@param {String} contents The text selected
|
||||
**/
|
||||
// Build the BBCode quote around the selected text
|
||||
build: function(post, contents) {
|
||||
var contents_hashed, result, sansQuotes, stripped, stripped_hashed, tmp;
|
||||
if (!contents) contents = "";
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import userSearch from 'discourse/lib/user-search';
|
||||
import afterTransition from 'discourse/lib/after-transition';
|
||||
import loadScript from 'discourse/lib/load-script';
|
||||
import avatarTemplate from 'discourse/lib/avatar-template';
|
||||
|
||||
const ComposerView = Discourse.View.extend(Ember.Evented, {
|
||||
_lastKeyTimeout: null,
|
||||
|
@ -244,7 +245,10 @@ const ComposerView = Discourse.View.extend(Ember.Evented, {
|
|||
if (posts) {
|
||||
const quotedPost = posts.findProperty("post_number", postNumber);
|
||||
if (quotedPost) {
|
||||
return Discourse.Utilities.tinyAvatar(quotedPost.get("avatar_template"));
|
||||
const username = quotedPost.get('username'),
|
||||
uploadId = quotedPost.get('uploaded_avatar_id');
|
||||
|
||||
return Discourse.Utilities.tinyAvatar(avatarTemplate(username, uploadId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,3 @@
|
|||
/**
|
||||
This view is used for rendering the pop-up quote button
|
||||
|
||||
@class QuoteButtonView
|
||||
@extends Discourse.View
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
export default Discourse.View.extend({
|
||||
classNames: ['quote-button'],
|
||||
classNameBindings: ['visible'],
|
||||
|
@ -13,20 +5,12 @@ export default Discourse.View.extend({
|
|||
isTouchInProgress: false,
|
||||
|
||||
/**
|
||||
Determines whether the pop-up quote button should be visible.
|
||||
The button is visible whenever there is something in the buffer
|
||||
(ie. something has been selected)
|
||||
|
||||
@property visible
|
||||
**/
|
||||
visible: Em.computed.notEmpty('controller.buffer'),
|
||||
|
||||
/**
|
||||
Renders the pop-up quote button.
|
||||
|
||||
@method render
|
||||
**/
|
||||
render: function(buffer) {
|
||||
render(buffer) {
|
||||
buffer.push(I18n.t("post.quote_reply"));
|
||||
},
|
||||
|
||||
|
@ -38,15 +22,15 @@ export default Discourse.View.extend({
|
|||
|
||||
@method didInsertElement
|
||||
**/
|
||||
didInsertElement: function() {
|
||||
var controller = this.get('controller'),
|
||||
view = this;
|
||||
didInsertElement() {
|
||||
const controller = this.get('controller'),
|
||||
view = this;
|
||||
|
||||
$(document)
|
||||
.on("mousedown.quote-button", function(e) {
|
||||
view.set('isMouseDown', true);
|
||||
|
||||
var $target = $(e.target);
|
||||
const $target = $(e.target);
|
||||
// we don't want to deselect when we click on buttons that use it
|
||||
if ($target.hasClass('quote-button') ||
|
||||
$target.closest('.create').length ||
|
||||
|
@ -75,27 +59,17 @@ export default Discourse.View.extend({
|
|||
});
|
||||
},
|
||||
|
||||
/**
|
||||
Selects the text
|
||||
|
||||
@method selectText
|
||||
**/
|
||||
selectText: function(target, controller) {
|
||||
var $target = $(target);
|
||||
selectText(target, controller) {
|
||||
const $target = $(target);
|
||||
// breaks if quoting has been disabled by the user
|
||||
if (!Discourse.User.currentProp('enable_quoting')) return;
|
||||
// retrieve the post id from the DOM
|
||||
var postId = $target.closest('.boxed').data('post-id');
|
||||
const postId = $target.closest('.boxed').data('post-id');
|
||||
// select the text
|
||||
if (postId) controller.selectText(postId);
|
||||
},
|
||||
|
||||
/**
|
||||
Unbinds from global `mouseup, mousedown, selectionchange` events
|
||||
|
||||
@method willDestroyElement
|
||||
**/
|
||||
willDestroyElement: function() {
|
||||
willDestroyElement() {
|
||||
$(document)
|
||||
.off("mousedown.quote-button")
|
||||
.off("mouseup.quote-button")
|
||||
|
@ -104,12 +78,7 @@ export default Discourse.View.extend({
|
|||
.off("selectionchange");
|
||||
},
|
||||
|
||||
/**
|
||||
Quote the selected text when clicking on the quote button.
|
||||
|
||||
@method click
|
||||
**/
|
||||
click: function(e) {
|
||||
click(e) {
|
||||
e.stopPropagation();
|
||||
return this.get('controller').quoteText(e);
|
||||
}
|
||||
|
|
|
@ -20,12 +20,28 @@ module PrettyText
|
|||
end
|
||||
end
|
||||
|
||||
# function here are available to v8
|
||||
# functions here are available to v8
|
||||
def avatar_template(username)
|
||||
return "" unless username
|
||||
user = User.find_by(username_lower: username.downcase)
|
||||
return "" unless user.present?
|
||||
schemaless absolute user.avatar_template
|
||||
|
||||
|
||||
# TODO: Add support for ES6 and call `avatar-template` directly
|
||||
if !user.uploaded_avatar_id && SiteSetting.default_avatars.present?
|
||||
split_avatars = SiteSetting.default_avatars.split("\n")
|
||||
if split_avatars.present?
|
||||
hash = username.each_char.reduce(0) do |result, char|
|
||||
[((result << 5) - result) + char.ord].pack('L').unpack('l').first
|
||||
end
|
||||
|
||||
avatar_template = split_avatars[hash.abs % split_avatars.size]
|
||||
end
|
||||
else
|
||||
avatar_template = user.avatar_template
|
||||
end
|
||||
|
||||
schemaless absolute avatar_template
|
||||
end
|
||||
|
||||
def is_username_valid(username)
|
||||
|
@ -65,6 +81,8 @@ module PrettyText
|
|||
ctx.eval("var window = {}; window.devicePixelRatio = 2;") # hack to make code think stuff is retina
|
||||
ctx.eval("var I18n = {}; I18n.t = function(a,b){ return helpers.t(a,b); }");
|
||||
|
||||
ctx.eval("var modules = {};")
|
||||
|
||||
decorate_context(ctx)
|
||||
|
||||
ctx_load(ctx,
|
||||
|
@ -74,7 +92,7 @@ module PrettyText
|
|||
"app/assets/javascripts/discourse/dialects/dialect.js",
|
||||
"app/assets/javascripts/discourse/lib/utilities.js",
|
||||
"app/assets/javascripts/discourse/lib/html.js",
|
||||
"app/assets/javascripts/discourse/lib/markdown.js"
|
||||
"app/assets/javascripts/discourse/lib/markdown.js",
|
||||
)
|
||||
|
||||
Dir["#{app_root}/app/assets/javascripts/discourse/dialects/**.js"].sort.each do |dialect|
|
||||
|
|
Loading…
Reference in New Issue