diff --git a/src/modals/base.js b/src/modals/base.js index b6de20967..92d748f3a 100644 --- a/src/modals/base.js +++ b/src/modals/base.js @@ -83,7 +83,7 @@ const BaseModal = View.extend({ if (ev) { ev.preventDefault(); this.trigger_el = ev.target; - this.trigger_el.classList.add('selected'); + !u.hasClass('chat-image', this.trigger_el) && u.addClass('selected', this.trigger_el); } this.modal.show(); } diff --git a/src/shared/message/styling.js b/src/shared/message/styling.js index ed92f2dff..ac05ad766 100644 --- a/src/shared/message/styling.js +++ b/src/shared/message/styling.js @@ -23,12 +23,12 @@ const dont_escape = ['_', '>', '`', '~']; const styling_templates = { // m is the chatbox model // i is the offset of this directive relative to the start of the original message - 'emphasis': (txt, m, i) => html`_${renderStylingDirectiveBody(txt, m, i)}_`, + 'emphasis': (txt, i, mentions, options) => html`_${renderStylingDirectiveBody(txt, i, mentions, options)}_`, 'preformatted': txt => html`\`${txt}\``, 'preformatted_block': txt => html`
\`\`\`
${txt}
\`\`\`
`, - 'quote': (txt, m, i) => html`
${renderStylingDirectiveBody(txt, m, i)}
`, - 'strike': (txt, m, i) => html`~${renderStylingDirectiveBody(txt, m, i)}~`, - 'strong': (txt, m, i) => html`*${renderStylingDirectiveBody(txt, m, i)}*`, + 'quote': (txt, i, mentions, options) => html`
${renderStylingDirectiveBody(txt, i, mentions, options)}
`, + 'strike': (txt, i, mentions, options) => html`~${renderStylingDirectiveBody(txt, i, mentions, options)}~`, + 'strong': (txt, i, mentions, options) => html`*${renderStylingDirectiveBody(txt, i, mentions, options)}*`, }; @@ -145,15 +145,15 @@ export function getDirectiveAndLength (text, i) { export const isQuoteDirective = (d) => ['>', '>'].includes(d); -export function getDirectiveTemplate (d, text, model, offset) { +export function getDirectiveTemplate (d, text, offset, mentions, options) { const template = styling_templates[styling_map[d].name]; if (isQuoteDirective(d)) { const newtext = text .replace(/\n>/g, '\n') // Don't show the directive itself .replace(/\n$/, ''); // Trim line-break at the end - return template(newtext, model, offset); + return template(newtext, offset, mentions, options); } else { - return template(text, model, offset); + return template(text, offset, mentions, options); } } diff --git a/src/shared/message/text.js b/src/shared/message/text.js index bd77f5e24..3a78ec3f8 100644 --- a/src/shared/message/text.js +++ b/src/shared/message/text.js @@ -39,25 +39,30 @@ export class MessageText extends String { /** * Create a new {@link MessageText} instance. * @param { String } text - The text to be annotated - * @param { Message } model - The model representing the message to which - * this MessageText instance belongs * @param { Integer } offset - The offset of this particular piece of text * from the start of the original message text. This is necessary because * MessageText instances can be nested when templates call directives * which create new MessageText instances (as happens with XEP-393 styling directives). - * @param { Boolean } show_images - Whether image URLs should be rendered as tags. - * @param { Function } onImgLoad - Callback for when an inline rendered image has been loaded - * @param { Function } onImgClick - Callback for when an inline rendered image has been clicked + * @param { Array } mentions - An array of mention references + * @param { Object } options + * @param { Object } options.nick - The current user's nickname (only relevant if the message is in a XEP-0045 MUC) + * @param { Boolean } options.render_styling - Whether XEP-0393 message styling should be applied to the message + * @param { Boolean } options.show_images - Whether image URLs should be rendered as tags. + * @param { Function } options.onImgClick - Callback for when an inline rendered image has been clicked + * @param { Function } options.onImgLoad - Callback for when an inline rendered image has been loaded */ - constructor (text, model, offset=0, show_images, onImgLoad, onImgClick) { + constructor (text, offset=0, mentions=[], options={}) { super(text); - this.model = model; + this.mentions = mentions; + this.nick = options?.nick; this.offset = offset; - this.onImgClick = onImgClick; - this.onImgLoad = onImgLoad; - this.references = []; - this.show_images = show_images; + this.onImgClick = options?.onImgClick; + this.onImgLoad = options?.onImgLoad; + this.options = options; this.payload = []; + this.references = []; + this.render_styling = options?.render_styling; + this.show_images = options?.show_images; } /** @@ -136,21 +141,14 @@ export class MessageText extends String { */ addMentions (text, local_offset) { const full_offset = local_offset+this.offset; - if (!this.model.collection) { - // This model doesn't belong to a collection anymore, so it must be - // have been removed in the meantime and can be ignored. - log.debug('addMentions: ignoring dangling model'); - return; - } - const nick = this.model.collection.chatbox.get('nick'); - this.model.get('references')?.forEach(ref => { + this.mentions?.forEach(ref => { const begin = Number(ref.begin)-full_offset; if (begin < 0 || begin >= full_offset+text.length) { return; } const end = Number(ref.end)-full_offset; const mention = text.slice(begin, end); - if (mention === nick) { + if (mention === this.nick) { this.addTemplateResult( begin+local_offset, end+local_offset, @@ -171,9 +169,6 @@ export class MessageText extends String { * them. */ addStyling () { - if (this.model.get('is_unstyled') || !api.settings.get('allow_message_styling')) { - return; - } let i = 0; const references = []; if (containsDirectives(this)) { @@ -192,7 +187,7 @@ export class MessageText extends String { const text = this.slice(slice_begin, slice_end); references.push({ 'begin': i, - 'template': getDirectiveTemplate(d, text, this.model, offset), + 'template': getDirectiveTemplate(d, text, offset, this.mentions, this.options), end, }); i = end; @@ -254,7 +249,7 @@ export class MessageText extends String { */ await api.trigger('beforeMessageBodyTransformed', this, {'Synchronous': true}); - this.addStyling(); + this.render_styling && this.addStyling(); this.addAnnotations(this.addMentions); this.addAnnotations(this.addHyperlinks); this.addAnnotations(this.addMapURLs); diff --git a/src/templates/directives/body.js b/src/templates/directives/body.js index b9fea1389..907e1553b 100644 --- a/src/templates/directives/body.js +++ b/src/templates/directives/body.js @@ -18,14 +18,19 @@ class MessageBodyRenderer { async transform () { const show_images = api.settings.get('show_images_inline'); + const render_styling = !this.model.get('is_unstyled') && api.settings.get('allow_message_styling'); const offset = 0; const text = new MessageText( this.text, - this.model, offset, - show_images, - () => this.onImageLoaded(), - ev => this.component.showImageModal(ev) + this.model.get('references'), + { + 'nick': this.model.collection.chatbox.get('nick'), + 'onImgClick': () => this.onImageLoaded(), + 'onImgLoad': ev => this.component.showImageModal(ev), + render_styling, + show_images, + } ); await text.addTemplates(); return text.payload; diff --git a/src/templates/directives/styling.js b/src/templates/directives/styling.js index ec5c73f2b..3777325d2 100644 --- a/src/templates/directives/styling.js +++ b/src/templates/directives/styling.js @@ -1,16 +1,17 @@ import { MessageText } from '../../shared/message/text.js'; -import { directive, html } from "lit-html"; +import { directive, html } from 'lit-html'; import { until } from 'lit-html/directives/until.js'; - async function transform (t) { await t.addTemplates(); return t.payload; } -function renderer (text, model, offset) { - const t = new MessageText(text, model, offset, false); +function renderer (text, offset, mentions, options) { + const t = new MessageText(text, offset, mentions, Object.assign(options, { 'show_images': false })); return html`${until(transform(t), html`${t}`)}`; } -export const renderStylingDirectiveBody = directive((text, model, offset) => p => p.setValue(renderer(text, model, offset))); +export const renderStylingDirectiveBody = directive((txt, offset, mentions, options) => + p => p.setValue(renderer(txt, offset, mentions, options)) +);