Render audio from URLs in messages
This commit is contained in:
parent
33b426c79e
commit
095d9b60cd
@ -793,6 +793,17 @@ domain_placeholder
|
||||
The placeholder text shown in the domain input on the registration form.
|
||||
|
||||
|
||||
embed_audio
|
||||
-----------
|
||||
|
||||
* Default: ``true``
|
||||
|
||||
If set to ``false``, audio files won't be embedded in chats, instead only their links will be shown.
|
||||
|
||||
It also accepts an array strings of whitelisted domain names to only render videos that belong to those domains.
|
||||
E.g. ``['conversejs.org']``
|
||||
|
||||
|
||||
embed_videos
|
||||
------------
|
||||
|
||||
|
@ -47,9 +47,11 @@ module.exports = function(config) {
|
||||
{ pattern: "src/plugins/chatview/tests/http-file-upload.js", type: 'module' },
|
||||
{ pattern: "src/plugins/chatview/tests/markers.js", type: 'module' },
|
||||
{ pattern: "src/plugins/chatview/tests/me-messages.js", type: 'module' },
|
||||
{ pattern: "src/plugins/chatview/tests/message-audio.js", type: 'module' },
|
||||
{ pattern: "src/plugins/chatview/tests/message-images.js", type: 'module' },
|
||||
{ pattern: "src/plugins/chatview/tests/message-videos.js", type: 'module' },
|
||||
{ pattern: "src/plugins/chatview/tests/messages.js", type: 'module' },
|
||||
{ pattern: "src/plugins/chatview/tests/oob.js", type: 'module' },
|
||||
{ pattern: "src/plugins/chatview/tests/receipts.js", type: 'module' },
|
||||
{ pattern: "src/plugins/chatview/tests/spoilers.js", type: 'module' },
|
||||
{ pattern: "src/plugins/chatview/tests/xss.js", type: 'module' },
|
||||
|
@ -37,6 +37,7 @@ converse.plugins.add('converse-chatview', {
|
||||
'auto_focus': true,
|
||||
'debounced_content_rendering': true,
|
||||
'embed_videos': true,
|
||||
'embed_audio': true,
|
||||
'filter_url_query_params': null,
|
||||
'image_urls_regex': null,
|
||||
'message_limit': 0,
|
||||
|
25
src/plugins/chatview/tests/message-audio.js
Normal file
25
src/plugins/chatview/tests/message-audio.js
Normal file
@ -0,0 +1,25 @@
|
||||
/*global mock, converse */
|
||||
|
||||
const { sizzle, u } = converse.env;
|
||||
|
||||
describe("A Chat Message", function () {
|
||||
|
||||
it("will render audio files from their URLs",
|
||||
mock.initConverse(['chatBoxesFetched'], {},
|
||||
async function (done, _converse) {
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
const base_url = 'https://conversejs.org';
|
||||
const message = base_url+"/logo/audio.mp3";
|
||||
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
await mock.openChatBoxFor(_converse, contact_jid);
|
||||
const view = _converse.chatboxviews.get(contact_jid);
|
||||
await mock.sendMessage(view, message);
|
||||
await u.waitUntil(() => view.querySelectorAll('.chat-content audio').length, 1000)
|
||||
const msg = sizzle('.chat-content .chat-msg:last .chat-msg__text').pop();
|
||||
expect(msg.innerHTML.replace(/<!-.*?->/g, '').replace(/(\r\n|\n|\r)/gm, "").trim()).toEqual(
|
||||
`<audio controls="" src="${message}"></audio>`+
|
||||
`<a target="_blank" rel="noopener" href="${message}">${message}</a>`);
|
||||
done();
|
||||
}));
|
||||
});
|
@ -1267,167 +1267,4 @@ describe("A Chat Message", function () {
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
describe("which contains an OOB URL", function () {
|
||||
|
||||
it("will render audio from oob mp3 URLs",
|
||||
mock.initConverse(
|
||||
['chatBoxesFetched'], {},
|
||||
async function (done, _converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
await mock.openChatBoxFor(_converse, contact_jid);
|
||||
const view = _converse.chatboxviews.get(contact_jid);
|
||||
spyOn(view.model, 'sendMessage').and.callThrough();
|
||||
|
||||
let stanza = u.toStanza(`
|
||||
<message from="${contact_jid}"
|
||||
type="chat"
|
||||
to="romeo@montague.lit/orchard">
|
||||
<body>Have you heard this funny audio?</body>
|
||||
<x xmlns="jabber:x:oob"><url>https://montague.lit/audio.mp3</url></x>
|
||||
</message>`)
|
||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||
await new Promise(resolve => view.model.messages.once('rendered', resolve));
|
||||
await u.waitUntil(() => view.querySelectorAll('.chat-content .chat-msg audio').length, 1000);
|
||||
let msg = view.querySelector('.chat-msg .chat-msg__text');
|
||||
expect(msg.classList.length).toEqual(1);
|
||||
expect(u.hasClass('chat-msg__text', msg)).toBe(true);
|
||||
expect(msg.textContent).toEqual('Have you heard this funny audio?');
|
||||
let media = view.querySelector('.chat-msg .chat-msg__media');
|
||||
expect(media.innerHTML.replace(/<!-.*?->/g, '').replace(/(\r\n|\n|\r)/gm, "").trim()).toEqual(
|
||||
`<audio controls="" src="https://montague.lit/audio.mp3"></audio> `+
|
||||
`<a target="_blank" rel="noopener" href="https://montague.lit/audio.mp3">Download audio file "audio.mp3"</a>`);
|
||||
|
||||
// If the <url> and <body> contents is the same, don't duplicate.
|
||||
stanza = u.toStanza(`
|
||||
<message from="${contact_jid}"
|
||||
type="chat"
|
||||
to="romeo@montague.lit/orchard">
|
||||
<body>https://montague.lit/audio.mp3</body>
|
||||
<x xmlns="jabber:x:oob"><url>https://montague.lit/audio.mp3</url></x>
|
||||
</message>`);
|
||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||
await new Promise(resolve => view.model.messages.once('rendered', resolve));
|
||||
msg = view.querySelector('.chat-msg:last-child .chat-msg__text');
|
||||
expect(msg.innerHTML.replace(/<!-.*?->/g, '')).toEqual('Have you heard this funny audio?'); // Emtpy
|
||||
media = view.querySelector('.chat-msg:last-child .chat-msg__media');
|
||||
expect(media.innerHTML.replace(/<!-.*?->/g, '').replace(/(\r\n|\n|\r)/gm, "").trim()).toEqual(
|
||||
`<audio controls="" src="https://montague.lit/audio.mp3"></audio> `+
|
||||
`<a target="_blank" rel="noopener" href="https://montague.lit/audio.mp3">`+
|
||||
`Download audio file "audio.mp3"</a>`);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will render video from oob mp4 URLs",
|
||||
mock.initConverse(
|
||||
['chatBoxesFetched'], {},
|
||||
async function (done, _converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
await mock.openChatBoxFor(_converse, contact_jid)
|
||||
const view = _converse.chatboxviews.get(contact_jid);
|
||||
spyOn(view.model, 'sendMessage').and.callThrough();
|
||||
|
||||
let stanza = u.toStanza(`
|
||||
<message from="${contact_jid}"
|
||||
type="chat"
|
||||
to="romeo@montague.lit/orchard">
|
||||
<body>Have you seen this funny video?</body>
|
||||
<x xmlns="jabber:x:oob"><url>https://montague.lit/video.mp4</url></x>
|
||||
</message>`);
|
||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||
await u.waitUntil(() => view.querySelectorAll('.chat-content .chat-msg video').length, 2000)
|
||||
let msg = view.querySelector('.chat-msg .chat-msg__text');
|
||||
expect(msg.classList.length).toBe(1);
|
||||
expect(msg.textContent).toEqual('Have you seen this funny video?');
|
||||
let media = view.querySelector('.chat-msg .chat-msg__media');
|
||||
expect(media.innerHTML.replace(/(\r\n|\n|\r)/gm, "").replace(/<!-.*?->/g, '')).toEqual(
|
||||
`<video controls="" preload="metadata" style="max-height: 50vh" src="https://montague.lit/video.mp4"></video>`);
|
||||
|
||||
|
||||
// If the <url> and <body> contents is the same, don't duplicate.
|
||||
stanza = u.toStanza(`
|
||||
<message from="${contact_jid}"
|
||||
type="chat"
|
||||
to="romeo@montague.lit/orchard">
|
||||
<body>https://montague.lit/video.mp4</body>
|
||||
<x xmlns="jabber:x:oob"><url>https://montague.lit/video.mp4</url></x>
|
||||
</message>`);
|
||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||
await new Promise(resolve => view.model.messages.once('rendered', resolve));
|
||||
msg = view.querySelector('.chat-msg:last-child .chat-msg__text');
|
||||
expect(msg.innerHTML.replace(/<!-.*?->/g, '')).toEqual('Have you seen this funny video?');
|
||||
media = view.querySelector('.chat-msg:last-child .chat-msg__media');
|
||||
expect(media.innerHTML.replace(/(\r\n|\n|\r)/gm, "").replace(/<!-.*?->/g, '')).toEqual(
|
||||
`<video controls="" preload="metadata" style="max-height: 50vh" src="https://montague.lit/video.mp4"></video>`);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will render download links for files from oob URLs",
|
||||
mock.initConverse(
|
||||
['chatBoxesFetched'], {},
|
||||
async function (done, _converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
await mock.openChatBoxFor(_converse, contact_jid);
|
||||
const view = _converse.chatboxviews.get(contact_jid);
|
||||
spyOn(view.model, 'sendMessage').and.callThrough();
|
||||
const stanza = u.toStanza(`
|
||||
<message from="${contact_jid}"
|
||||
type="chat"
|
||||
to="romeo@montague.lit/orchard">
|
||||
<body>Have you downloaded this funny file?</body>
|
||||
<x xmlns="jabber:x:oob"><url>https://montague.lit/funny.pdf</url></x>
|
||||
</message>`);
|
||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||
await new Promise(resolve => view.model.messages.once('rendered', resolve));
|
||||
await u.waitUntil(() => view.querySelectorAll('.chat-content .chat-msg a').length, 1000);
|
||||
const msg = view.querySelector('.chat-msg .chat-msg__text');
|
||||
expect(u.hasClass('chat-msg__text', msg)).toBe(true);
|
||||
expect(msg.textContent).toEqual('Have you downloaded this funny file?');
|
||||
const media = view.querySelector('.chat-msg .chat-msg__media');
|
||||
expect(media.innerHTML.replace(/(\r\n|\n|\r)/gm, "").replace(/<!-.*?->/g, '')).toEqual(
|
||||
`<a target="_blank" rel="noopener" href="https://montague.lit/funny.pdf">Download file "funny.pdf"</a>`);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will render images from oob URLs",
|
||||
mock.initConverse(
|
||||
['chatBoxesFetched'], {},
|
||||
async function (done, _converse) {
|
||||
|
||||
const base_url = 'https://conversejs.org';
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
await mock.openChatBoxFor(_converse, contact_jid)
|
||||
const view = _converse.chatboxviews.get(contact_jid);
|
||||
spyOn(view.model, 'sendMessage').and.callThrough();
|
||||
const url = base_url+"/logo/conversejs-filled.svg";
|
||||
|
||||
const stanza = u.toStanza(`
|
||||
<message from="${contact_jid}"
|
||||
type="chat"
|
||||
to="romeo@montague.lit/orchard">
|
||||
<body>Have you seen this funny image?</body>
|
||||
<x xmlns="jabber:x:oob"><url>${url}</url></x>
|
||||
</message>`);
|
||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||
await new Promise(resolve => view.model.messages.once('rendered', resolve));
|
||||
await u.waitUntil(() => view.querySelectorAll('.chat-content .chat-msg a').length, 1000);
|
||||
const msg = view.querySelector('.chat-msg .chat-msg__text');
|
||||
expect(u.hasClass('chat-msg__text', msg)).toBe(true);
|
||||
expect(msg.textContent).toEqual('Have you seen this funny image?');
|
||||
const media = view.querySelector('.chat-msg .chat-msg__media');
|
||||
expect(media.innerHTML.replace(/<!-.*?->/g, '').replace(/(\r\n|\n|\r)/gm, "")).toEqual(
|
||||
`<a target="_blank" rel="noopener" href="${base_url}/logo/conversejs-filled.svg">`+
|
||||
`Download image file "conversejs-filled.svg"</a>`);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
167
src/plugins/chatview/tests/oob.js
Normal file
167
src/plugins/chatview/tests/oob.js
Normal file
@ -0,0 +1,167 @@
|
||||
/*global mock, converse */
|
||||
|
||||
const { Promise, u } = converse.env;
|
||||
|
||||
describe("A Chat Message", function () {
|
||||
describe("which contains an OOB URL", function () {
|
||||
|
||||
it("will render audio from oob mp3 URLs",
|
||||
mock.initConverse(
|
||||
['chatBoxesFetched'], {},
|
||||
async function (done, _converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
await mock.openChatBoxFor(_converse, contact_jid);
|
||||
const view = _converse.chatboxviews.get(contact_jid);
|
||||
spyOn(view.model, 'sendMessage').and.callThrough();
|
||||
|
||||
const url = 'https://montague.lit/audio.mp3';
|
||||
let stanza = u.toStanza(`
|
||||
<message from="${contact_jid}"
|
||||
type="chat"
|
||||
to="romeo@montague.lit/orchard">
|
||||
<body>Have you heard this funny audio?</body>
|
||||
<x xmlns="jabber:x:oob"><url>${url}</url></x>
|
||||
</message>`)
|
||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||
await new Promise(resolve => view.model.messages.once('rendered', resolve));
|
||||
await u.waitUntil(() => view.querySelectorAll('.chat-content .chat-msg audio').length, 1000);
|
||||
let msg = view.querySelector('.chat-msg .chat-msg__text');
|
||||
expect(msg.classList.length).toEqual(1);
|
||||
expect(u.hasClass('chat-msg__text', msg)).toBe(true);
|
||||
expect(msg.textContent).toEqual('Have you heard this funny audio?');
|
||||
let media = view.querySelector('.chat-msg .chat-msg__media');
|
||||
expect(media.innerHTML.replace(/<!-.*?->/g, '').replace(/(\r\n|\n|\r)/gm, "").trim()).toEqual(
|
||||
`<audio controls="" src="https://montague.lit/audio.mp3"></audio>`+
|
||||
`<a target="_blank" rel="noopener" href="https://montague.lit/audio.mp3">${url}</a>`);
|
||||
|
||||
// If the <url> and <body> contents is the same, don't duplicate.
|
||||
stanza = u.toStanza(`
|
||||
<message from="${contact_jid}"
|
||||
type="chat"
|
||||
to="romeo@montague.lit/orchard">
|
||||
<body>https://montague.lit/audio.mp3</body>
|
||||
<x xmlns="jabber:x:oob"><url>https://montague.lit/audio.mp3</url></x>
|
||||
</message>`);
|
||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||
await new Promise(resolve => view.model.messages.once('rendered', resolve));
|
||||
msg = view.querySelector('.chat-msg:last-child .chat-msg__text');
|
||||
expect(msg.innerHTML.replace(/<!-.*?->/g, '')).toEqual('Have you heard this funny audio?'); // Emtpy
|
||||
media = view.querySelector('.chat-msg:last-child .chat-msg__media');
|
||||
expect(media.innerHTML.replace(/<!-.*?->/g, '').replace(/(\r\n|\n|\r)/gm, "").trim()).toEqual(
|
||||
`<audio controls="" src="https://montague.lit/audio.mp3"></audio>`+
|
||||
`<a target="_blank" rel="noopener" href="${url}">${url}</a>`);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will render video from oob mp4 URLs",
|
||||
mock.initConverse(
|
||||
['chatBoxesFetched'], {},
|
||||
async function (done, _converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
await mock.openChatBoxFor(_converse, contact_jid)
|
||||
const view = _converse.chatboxviews.get(contact_jid);
|
||||
spyOn(view.model, 'sendMessage').and.callThrough();
|
||||
|
||||
let stanza = u.toStanza(`
|
||||
<message from="${contact_jid}"
|
||||
type="chat"
|
||||
to="romeo@montague.lit/orchard">
|
||||
<body>Have you seen this funny video?</body>
|
||||
<x xmlns="jabber:x:oob"><url>https://montague.lit/video.mp4</url></x>
|
||||
</message>`);
|
||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||
await u.waitUntil(() => view.querySelectorAll('.chat-content .chat-msg video').length, 2000)
|
||||
let msg = view.querySelector('.chat-msg .chat-msg__text');
|
||||
expect(msg.classList.length).toBe(1);
|
||||
expect(msg.textContent).toEqual('Have you seen this funny video?');
|
||||
let media = view.querySelector('.chat-msg .chat-msg__media');
|
||||
expect(media.innerHTML.replace(/(\r\n|\n|\r)/gm, "").replace(/<!-.*?->/g, '')).toEqual(
|
||||
`<video controls="" preload="metadata" style="max-height: 50vh" src="https://montague.lit/video.mp4"></video>`);
|
||||
|
||||
|
||||
// If the <url> and <body> contents is the same, don't duplicate.
|
||||
stanza = u.toStanza(`
|
||||
<message from="${contact_jid}"
|
||||
type="chat"
|
||||
to="romeo@montague.lit/orchard">
|
||||
<body>https://montague.lit/video.mp4</body>
|
||||
<x xmlns="jabber:x:oob"><url>https://montague.lit/video.mp4</url></x>
|
||||
</message>`);
|
||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||
await new Promise(resolve => view.model.messages.once('rendered', resolve));
|
||||
msg = view.querySelector('.chat-msg:last-child .chat-msg__text');
|
||||
expect(msg.innerHTML.replace(/<!-.*?->/g, '')).toEqual('Have you seen this funny video?');
|
||||
media = view.querySelector('.chat-msg:last-child .chat-msg__media');
|
||||
expect(media.innerHTML.replace(/(\r\n|\n|\r)/gm, "").replace(/<!-.*?->/g, '')).toEqual(
|
||||
`<video controls="" preload="metadata" style="max-height: 50vh" src="https://montague.lit/video.mp4"></video>`);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will render download links for files from oob URLs",
|
||||
mock.initConverse(
|
||||
['chatBoxesFetched'], {},
|
||||
async function (done, _converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
await mock.openChatBoxFor(_converse, contact_jid);
|
||||
const view = _converse.chatboxviews.get(contact_jid);
|
||||
spyOn(view.model, 'sendMessage').and.callThrough();
|
||||
const stanza = u.toStanza(`
|
||||
<message from="${contact_jid}"
|
||||
type="chat"
|
||||
to="romeo@montague.lit/orchard">
|
||||
<body>Have you downloaded this funny file?</body>
|
||||
<x xmlns="jabber:x:oob"><url>https://montague.lit/funny.pdf</url></x>
|
||||
</message>`);
|
||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||
await new Promise(resolve => view.model.messages.once('rendered', resolve));
|
||||
await u.waitUntil(() => view.querySelectorAll('.chat-content .chat-msg a').length, 1000);
|
||||
const msg = view.querySelector('.chat-msg .chat-msg__text');
|
||||
expect(u.hasClass('chat-msg__text', msg)).toBe(true);
|
||||
expect(msg.textContent).toEqual('Have you downloaded this funny file?');
|
||||
const media = view.querySelector('.chat-msg .chat-msg__media');
|
||||
expect(media.innerHTML.replace(/(\r\n|\n|\r)/gm, "").replace(/<!-.*?->/g, '')).toEqual(
|
||||
`<a target="_blank" rel="noopener" href="https://montague.lit/funny.pdf">Download file "funny.pdf"</a>`);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will render images from oob URLs",
|
||||
mock.initConverse(
|
||||
['chatBoxesFetched'], {},
|
||||
async function (done, _converse) {
|
||||
|
||||
const base_url = 'https://conversejs.org';
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
await mock.openChatBoxFor(_converse, contact_jid)
|
||||
const view = _converse.chatboxviews.get(contact_jid);
|
||||
spyOn(view.model, 'sendMessage').and.callThrough();
|
||||
const url = base_url+"/logo/conversejs-filled.svg";
|
||||
|
||||
const stanza = u.toStanza(`
|
||||
<message from="${contact_jid}"
|
||||
type="chat"
|
||||
to="romeo@montague.lit/orchard">
|
||||
<body>Have you seen this funny image?</body>
|
||||
<x xmlns="jabber:x:oob"><url>${url}</url></x>
|
||||
</message>`);
|
||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||
await new Promise(resolve => view.model.messages.once('rendered', resolve));
|
||||
await u.waitUntil(() => view.querySelectorAll('.chat-content .chat-msg a').length, 1000);
|
||||
const msg = view.querySelector('.chat-msg .chat-msg__text');
|
||||
expect(u.hasClass('chat-msg__text', msg)).toBe(true);
|
||||
expect(msg.textContent).toEqual('Have you seen this funny image?');
|
||||
const media = view.querySelector('.chat-msg .chat-msg__media');
|
||||
expect(media.innerHTML.replace(/<!-.*?->/g, '').replace(/(\r\n|\n|\r)/gm, "")).toEqual(
|
||||
`<a target="_blank" rel="noopener" href="${base_url}/logo/conversejs-filled.svg">`+
|
||||
`Download image file "conversejs-filled.svg"</a>`);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
});
|
@ -4,6 +4,8 @@ import renderRichText from 'shared/directives/rich-text.js';
|
||||
import { CustomElement } from 'shared/components/element.js';
|
||||
import { api } from "@converse/headless/core";
|
||||
|
||||
import './styles/message-body.scss';
|
||||
|
||||
|
||||
export default class MessageBody extends CustomElement {
|
||||
|
||||
@ -13,6 +15,7 @@ export default class MessageBody extends CustomElement {
|
||||
is_me_message: { type: Boolean },
|
||||
show_images: { type: Boolean },
|
||||
embed_videos: { type: Boolean },
|
||||
embed_audio: { type: Boolean },
|
||||
text: { type: String },
|
||||
}
|
||||
}
|
||||
@ -31,12 +34,13 @@ export default class MessageBody extends CustomElement {
|
||||
const offset = 0;
|
||||
const mentions = this.model.get('references');
|
||||
const options = {
|
||||
'embed_audio': this.embed_audio,
|
||||
'embed_videos': this.embed_videos,
|
||||
'nick': this.model.collection.chatbox.get('nick'),
|
||||
'onImgClick': this.onImgClick,
|
||||
'onImgLoad': () => this.onImgLoad(),
|
||||
'render_styling': !this.model.get('is_unstyled') && api.settings.get('allow_message_styling'),
|
||||
'show_images': this.show_images,
|
||||
'embed_videos': this.embed_videos,
|
||||
'show_me_message': true
|
||||
}
|
||||
return renderRichText(this.text, offset, mentions, options, callback);
|
||||
|
@ -240,6 +240,7 @@ export default class Message extends CustomElement {
|
||||
?is_me_message="${this.model.isMeCommand()}"
|
||||
?show_images="${api.settings.get('show_images_inline')}"
|
||||
?embed_videos="${api.settings.get('embed_videos')}"
|
||||
?embed_audio="${api.settings.get('embed_audio')}"
|
||||
text="${text}"></converse-chat-message-body>
|
||||
${ (this.model.get('received') && !this.model.isMeCommand() && !is_groupchat_message) ? html`<span class="fa fa-check chat-msg__receipt"></span>` : '' }
|
||||
${ (this.model.get('edited')) ? html`<i title="${ i18n_edited }" class="fa fa-edit chat-msg__edit-modal" @click=${this.showMessageVersionsModal}></i>` : '' }
|
||||
|
5
src/shared/chat/styles/message-body.scss
Normal file
5
src/shared/chat/styles/message-body.scss
Normal file
@ -0,0 +1,5 @@
|
||||
converse-chat-message-body {
|
||||
audio {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
@ -6,6 +6,8 @@ export default class RichText extends CustomElement {
|
||||
|
||||
static get properties () {
|
||||
return {
|
||||
embed_audio: { type: Boolean },
|
||||
embed_videos: { type: Boolean },
|
||||
mentions: { type: Array },
|
||||
nick: { type: String },
|
||||
offset: { type: Number },
|
||||
@ -13,7 +15,6 @@ export default class RichText extends CustomElement {
|
||||
onImgLoad: { type: Function },
|
||||
render_styling: { type: Boolean },
|
||||
show_images: { type: Boolean },
|
||||
embed_videos: { type: Boolean },
|
||||
show_me_message: { type: Boolean },
|
||||
text: { type: String },
|
||||
}
|
||||
@ -21,22 +22,24 @@ export default class RichText extends CustomElement {
|
||||
|
||||
constructor () {
|
||||
super();
|
||||
this.offset = 0;
|
||||
this.embed_audio = false;
|
||||
this.embed_videos = false;
|
||||
this.mentions = [];
|
||||
this.offset = 0;
|
||||
this.render_styling = false;
|
||||
this.show_images = false;
|
||||
this.embed_videos = false;
|
||||
this.show_me_message = false;
|
||||
}
|
||||
|
||||
render () {
|
||||
const options = {
|
||||
embed_audio: this.embed_audio,
|
||||
embed_videos: this.embed_videos,
|
||||
nick: this.nick,
|
||||
onImgClick: this.onImgClick,
|
||||
onImgLoad: this.onImgLoad,
|
||||
render_styling: this.render_styling,
|
||||
show_images: this.show_images,
|
||||
embed_videos: this.embed_videos,
|
||||
show_me_message: this.show_me_message,
|
||||
}
|
||||
return renderRichText(this.text, this.offset, this.mentions, options);
|
||||
|
@ -14,7 +14,7 @@ class StylingDirective extends Directive {
|
||||
txt,
|
||||
offset,
|
||||
mentions,
|
||||
Object.assign(options, { 'show_images': false, 'embed_videos': false })
|
||||
Object.assign(options, { 'show_images': false, 'embed_videos': false, 'embed_audio': false })
|
||||
);
|
||||
return html`${until(transform(t), html`${t}`)}`;
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
import URI from 'urijs';
|
||||
import log from '@converse/headless/log';
|
||||
import tpl_audio from 'templates/audio.js';
|
||||
import tpl_image from 'templates/image.js';
|
||||
import tpl_video from '../templates/video.js';
|
||||
import tpl_video from 'templates/video.js';
|
||||
import { _converse, api } from '@converse/headless/core';
|
||||
import { containsDirectives, getDirectiveAndLength, getDirectiveTemplate, isQuoteDirective } from './styling.js';
|
||||
import {
|
||||
@ -13,8 +14,11 @@ import {
|
||||
import {
|
||||
filterQueryParamsFromURL,
|
||||
getHyperlinkTemplate,
|
||||
isImageURL,
|
||||
getURI,
|
||||
isAudioDomainAllowed,
|
||||
isAudioURL,
|
||||
isImageDomainAllowed,
|
||||
isImageURL,
|
||||
isVideoDomainAllowed,
|
||||
isVideoURL
|
||||
} from 'utils/html';
|
||||
@ -66,6 +70,8 @@ export class RichText extends String {
|
||||
*/
|
||||
constructor (text, offset = 0, mentions = [], options = {}) {
|
||||
super(text);
|
||||
this.embed_audio = options?.embed_audio;
|
||||
this.embed_videos = options?.embed_videos;
|
||||
this.mentions = mentions;
|
||||
this.nick = options?.nick;
|
||||
this.offset = offset;
|
||||
@ -76,7 +82,6 @@ export class RichText extends String {
|
||||
this.references = [];
|
||||
this.render_styling = options?.render_styling;
|
||||
this.show_images = options?.show_images;
|
||||
this.embed_videos = options?.embed_videos;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -114,6 +119,8 @@ export class RichText extends String {
|
||||
});
|
||||
} else if (this.embed_videos && isVideoURL(url_text) && isVideoDomainAllowed(url_text)) {
|
||||
template = tpl_video({ 'url': filtered_url });
|
||||
} else if (this.embed_audio && isAudioURL(url_text) && isAudioDomainAllowed(url_text)) {
|
||||
template = tpl_audio(filtered_url);
|
||||
} else {
|
||||
template = getHyperlinkTemplate(filtered_url);
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
import { html } from "lit";
|
||||
import { html } from 'lit';
|
||||
|
||||
|
||||
export default (o) => html`
|
||||
<audio controls src="${o.url}"></audio>
|
||||
<a target="_blank" rel="noopener" href="${o.url}">${o.label_download}</a>
|
||||
`;
|
||||
export default (url) => {
|
||||
return html`<audio controls src="${url}"></audio><a target="_blank" rel="noopener" href="${url}">${url}</a>`;
|
||||
}
|
||||
|
@ -94,6 +94,20 @@ export function isImageURL (url) {
|
||||
return regex?.test(url) || isURLWithImageExtension(url);
|
||||
}
|
||||
|
||||
export function isAudioDomainAllowed (url) {
|
||||
const embed_audio = api.settings.get('embed_audio');
|
||||
if (!Array.isArray(embed_audio)) {
|
||||
return embed_audio;
|
||||
}
|
||||
try {
|
||||
const audio_domain = getURI(url).domain();
|
||||
return embed_audio.includes(audio_domain);
|
||||
} catch (error) {
|
||||
log.debug(error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function isVideoDomainAllowed (url) {
|
||||
const embed_videos = api.settings.get('embed_videos');
|
||||
if (!Array.isArray(embed_videos)) {
|
||||
@ -122,7 +136,7 @@ export function isImageDomainAllowed (url) {
|
||||
}
|
||||
}
|
||||
|
||||
function getFileName (uri) {
|
||||
export function getFileName (uri) {
|
||||
try {
|
||||
return decodeURI(uri.filename());
|
||||
} catch (error) {
|
||||
@ -131,12 +145,8 @@ function getFileName (uri) {
|
||||
}
|
||||
}
|
||||
|
||||
function renderAudioURL (_converse, uri) {
|
||||
const { __ } = _converse;
|
||||
return tpl_audio({
|
||||
'url': uri.toString(),
|
||||
'label_download': __('Download audio file "%1$s"', getFileName(uri))
|
||||
});
|
||||
function renderAudioURL (url) {
|
||||
return tpl_audio(url);
|
||||
}
|
||||
|
||||
function renderImageURL (_converse, uri) {
|
||||
@ -170,7 +180,7 @@ u.getOOBURLMarkup = function (_converse, url) {
|
||||
if (u.isVideoURL(uri)) {
|
||||
return tpl_video({ url });
|
||||
} else if (u.isAudioURL(uri)) {
|
||||
return renderAudioURL(_converse, uri);
|
||||
return renderAudioURL(url);
|
||||
} else if (u.isImageURL(uri)) {
|
||||
return renderImageURL(_converse, uri);
|
||||
} else {
|
||||
|
Loading…
Reference in New Issue
Block a user