yosup/js/ui/fmt.js
Thomas Mathews 077bf49fdb New Feature: Direct Messages
This feature involved a lot of refactoring in order to get working
correctly. I wanted to continue using the timeline view for chats thus I
used alternative styling & structure for DM event kinds. This worked
create since the elements map does not care.

There is some queing that has to be done to decrypt message content thus
I allow viewing messages even if they haven't been decrypted yet. I
think this is good for transparency since you understand what is and is
not decrypted. I do think that the UX could improve, because even tho it
is fast, it's flashes on new messages.

I did not implement saving of latest messages. I will do this later, but
this feature is big enough to merge as is: an alpha state that works.

I further abstracted profile & name updating to work in a more global
manner. Additionally I rewrote code that had attribute scripts to use
addEventListener instead. This is needed to happen anyways for security
and made the codebase easier to manage.
2023-01-06 14:55:33 -08:00

105 lines
2.6 KiB
JavaScript

function linkify(text="", show_media=false) {
return text.replace(URL_REGEX, function(match, p1, p2, p3) {
const url = p2+p3;
let parsed;
try {
parsed = new URL(url)
} catch (err) {
return match;
}
let markup;
if (show_media && is_img_url(parsed.pathname)) {
markup = html`
<img class="inline-img clickable" src="${url}"
onclick="open_media_preview('${url}', 'image')"/>`;
} else if (show_media && is_video_url(parsed.pathname)) {
markup = html`
<video controls class="inline-img" />
<source src="${url}">
</video>`;
} else {
markup = html`<a target="_blank" rel="noopener noreferrer" href="${url}">${url}</a>`;
}
return p1+markup;
})
}
function format_content(ev, show_media) {
if (ev.kind === KIND_REACTION) {
if (ev.content === "" || ev.content === "+")
return "❤️"
return html`${ev.content.trim()}`;
}
const content = (ev.kind == KIND_DM ? ev.decrypted || ev.content : ev.content)
.trim();
const body = fmt_body(content, show_media);
let cw = get_content_warning(ev.tags)
if (cw !== null) {
let cwHTML = "Content Warning"
if (cw === "") {
cwHTML += "."
} else {
cwHTML += html`: "<span>${cw}</span>".`
}
return `
<details class="cw">
<summary class="event-message">${cwHTML}</summary>
${body}
</details>`;
}
return body;
}
/* fmt_body will parse images, blockquotes, and sanitize the content.
*/
function fmt_body(content, show_media) {
const split = content.split("\n")
let blockin = false
return split.reduce((str, line) => {
if (line !== "" && line[0] === '>') {
if (!blockin) {
str += "<span class='quote'>"
blockin = true
}
str += linkify(html`${line.slice(1)}`, show_media)
} else {
if (blockin) {
blockin = false
str += "</span>"
}
str += linkify(html`${line}`, show_media)
}
return str + "<br/>"
}, "")
}
/* DEPRECATED: use fmt_name
* format_profile_name takes in a profile and tries it's best to
* return a string that is best suited for the profile.
*/
function fmt_profile_name(profile={}, fallback="Anonymous") {
const name = profile.display_name || profile.user || profile.name ||
fallback
return html`${name}`;
}
function fmt_name(profile={data:{}}) {
const { data } = profile;
const name = data.display_name || data.user || data.name ||
fmt_pubkey(profile.pubkey);
return html`${name}`;
}
function fmt_pubkey(pk) {
if (!pk)
return "Unknown";
return pk.slice(-8)
}
function fmt_datetime(d) {
return d.getFullYear() +
"/" + ("0" + (d.getMonth()+1)).slice(-2) +
"/" + ("0" + d.getDate()).slice(-2) +
" " + ("0" + d.getHours()).slice(-2) +
":" + ("0" + d.getMinutes()).slice(-2);
}