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