Update the MesageText class to not require a Model object

This allows us to use it to transform any piece of text and not just
text from a chat message.
This commit is contained in:
JC Brand 2021-03-24 09:59:26 +01:00
parent 16e7133e31
commit 1fd3e3676a
5 changed files with 43 additions and 42 deletions

View File

@ -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();
}

View File

@ -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`<span class="styling-directive">_</span><i>${renderStylingDirectiveBody(txt, m, i)}</i><span class="styling-directive">_</span>`,
'emphasis': (txt, i, mentions, options) => html`<span class="styling-directive">_</span><i>${renderStylingDirectiveBody(txt, i, mentions, options)}</i><span class="styling-directive">_</span>`,
'preformatted': txt => html`<span class="styling-directive">\`</span><code>${txt}</code><span class="styling-directive">\`</span>`,
'preformatted_block': txt => html`<div class="styling-directive">\`\`\`</div><code class="block">${txt}</code><div class="styling-directive">\`\`\`</div>`,
'quote': (txt, m, i) => html`<blockquote>${renderStylingDirectiveBody(txt, m, i)}</blockquote>`,
'strike': (txt, m, i) => html`<span class="styling-directive">~</span><del>${renderStylingDirectiveBody(txt, m, i)}</del><span class="styling-directive">~</span>`,
'strong': (txt, m, i) => html`<span class="styling-directive">*</span><b>${renderStylingDirectiveBody(txt, m, i)}</b><span class="styling-directive">*</span>`,
'quote': (txt, i, mentions, options) => html`<blockquote>${renderStylingDirectiveBody(txt, i, mentions, options)}</blockquote>`,
'strike': (txt, i, mentions, options) => html`<span class="styling-directive">~</span><del>${renderStylingDirectiveBody(txt, i, mentions, options)}</del><span class="styling-directive">~</span>`,
'strong': (txt, i, mentions, options) => html`<span class="styling-directive">*</span><b>${renderStylingDirectiveBody(txt, i, mentions, options)}</b><span class="styling-directive">*</span>`,
};
@ -145,15 +145,15 @@ export function getDirectiveAndLength (text, i) {
export const isQuoteDirective = (d) => ['>', '&gt;'].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);
}
}

View File

@ -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 <img> 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 <img> 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);

View File

@ -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;

View File

@ -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))
);