Autolink URLs in messages
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Cadence Ember 2020-11-08 00:49:12 +13:00
parent d6be694d3b
commit 327290e971
Signed by: cadence
GPG key ID: BC1C2C61CF521B17
3 changed files with 33 additions and 13 deletions

View file

@ -19,12 +19,12 @@ const qa = s => document.querySelectorAll(s);
*/ */
class ElemJS { class ElemJS {
constructor(type) { constructor(type) {
if (type instanceof HTMLElement) { if (typeof type === "string") {
// If passed an existing element, bind to it // Passed a tag name; create an element to bind to
this.bind(type);
} else {
// Otherwise, create a new detached element to bind to
this.bind(document.createElement(type)); this.bind(document.createElement(type));
} else {
// Passed an existing element; bind to it
this.bind(type);
} }
this.children = []; this.children = [];
} }

View file

@ -68,9 +68,7 @@ function cleanHTML(html) {
} }
// Here we put all the processing of the messages that isn't as likely to potentially lead to security issues // Here we put all the processing of the messages that isn't as likely to potentially lead to security issues
function postProcessElements(rootNode) { function postProcessElements(element) {
const element = rootNode.element
element.querySelectorAll("pre").forEach(n => { element.querySelectorAll("pre").forEach(n => {
new HighlightedCode(n) new HighlightedCode(n)
}) })
@ -95,13 +93,11 @@ class HTMLMessage extends MatrixEvent {
this.clearChildren() this.clearChildren()
let html = this.data.content.formatted_body let html = this.data.content.formatted_body
const content = ejs("div")
const fragment = cleanHTML(html) const fragment = cleanHTML(html)
content.element.appendChild(fragment) postProcessElements(fragment)
postProcessElements(content)
this.child(content) this.child(ejs(fragment))
super.render() super.render()
} }
@ -121,10 +117,33 @@ class HTMLMessage extends MatrixEvent {
} }
} }
function autoLinkText(text) {
const fragment = ejs(new DocumentFragment())
let lastIndex = 0
text.replace(/https?:\/\/(?:[A-Za-z-]+\.)+[A-Za-z]{1,10}(?::[0-9]{1,6})?(?:\/[^ ]*)?/g, (url, index) => {
// add text before URL
fragment.addText(text.slice(lastIndex, index))
// add URL
fragment.child(
ejs("a")
.attribute("target", "_blank")
.attribute("noopener", "")
.attribute("href", url)
.addText(url)
)
// update state
lastIndex = index + url.length
})
// add final text
fragment.addText(text.slice(lastIndex))
return fragment
}
class TextMessage extends MatrixEvent { class TextMessage extends MatrixEvent {
render() { render() {
this.clearChildren() this.clearChildren()
this.text(this.data.content.body) const fragment = autoLinkText(this.data.content.body)
this.child(fragment)
super.render() super.render()
} }

View file

@ -49,6 +49,7 @@
overflow-wrap: anywhere overflow-wrap: anywhere
opacity: 1 opacity: 1
transition: opacity 0.2s ease-out transition: opacity 0.2s ease-out
white-space: pre-wrap
&--pending &--pending
opacity: 0.5 opacity: 0.5