forked from cory/tildefriends
848 lines
22 KiB
JavaScript
848 lines
22 KiB
JavaScript
import {LitElement, html, render, unsafeHTML} from './lit-all.min.js';
|
|
import * as tfrpc from '/static/tfrpc.js';
|
|
import * as tfutils from './tf-utils.js';
|
|
import * as emojis from './emojis.js';
|
|
import {styles} from './tf-styles.js';
|
|
|
|
class TfMessageElement extends LitElement {
|
|
static get properties() {
|
|
return {
|
|
whoami: {type: String},
|
|
message: {type: Object},
|
|
users: {type: Object},
|
|
drafts: {type: Object},
|
|
format: {type: String},
|
|
blog_data: {type: String},
|
|
expanded: {type: Object},
|
|
channel: {type: String},
|
|
channel_unread: {type: Number},
|
|
};
|
|
}
|
|
|
|
static styles = styles;
|
|
|
|
constructor() {
|
|
super();
|
|
let self = this;
|
|
this.whoami = null;
|
|
this.message = {};
|
|
this.users = {};
|
|
this.drafts = {};
|
|
this.format = 'message';
|
|
this.expanded = {};
|
|
this.channel_unread = -1;
|
|
}
|
|
|
|
show_reply() {
|
|
let event = new CustomEvent('tf-draft', {
|
|
bubbles: true,
|
|
composed: true,
|
|
detail: {
|
|
id: this.message?.id,
|
|
draft: {
|
|
encrypt_to: this.message?.decrypted?.recps,
|
|
},
|
|
},
|
|
});
|
|
this.dispatchEvent(event);
|
|
}
|
|
|
|
discard_reply() {
|
|
this.dispatchEvent(
|
|
new CustomEvent('tf-draft', {
|
|
bubbles: true,
|
|
composed: true,
|
|
detail: {id: this.id, draft: undefined},
|
|
})
|
|
);
|
|
}
|
|
|
|
show_reactions() {
|
|
let modal = document.getElementById('reactions_modal');
|
|
modal.users = this.users;
|
|
modal.votes = this.message?.votes || [];
|
|
}
|
|
|
|
render_votes() {
|
|
function normalize_expression(expression) {
|
|
if (expression === 'Like' || !expression) {
|
|
return '👍';
|
|
} else if (expression === 'Unlike') {
|
|
return '👎';
|
|
} else if (expression === 'heart') {
|
|
return '❤️';
|
|
} else {
|
|
return expression;
|
|
}
|
|
}
|
|
if (this.message?.votes?.length) {
|
|
return html` <div class="w3-container">
|
|
<div
|
|
class="w3-button w3-bar w3-padding-small"
|
|
@click=${this.show_reactions}
|
|
>
|
|
${(this.message.votes || []).map(
|
|
(vote) => html`
|
|
<span
|
|
class="w3-bar-item w3-padding-small"
|
|
title="${this.users[vote.author]?.name ??
|
|
vote.author} ${new Date(vote.timestamp)}"
|
|
>
|
|
${normalize_expression(vote.content.vote.expression)}
|
|
</span>
|
|
`
|
|
)}
|
|
</div>
|
|
</div>`;
|
|
}
|
|
}
|
|
|
|
render_raw() {
|
|
let raw = {
|
|
id: this.message?.id,
|
|
previous: this.message?.previous,
|
|
author: this.message?.author,
|
|
sequence: this.message?.sequence,
|
|
timestamp: this.message?.timestamp,
|
|
hash: this.message?.hash,
|
|
content: this.message?.content,
|
|
signature: this.message?.signature,
|
|
};
|
|
return html`<div style="white-space: pre-wrap">
|
|
${JSON.stringify(raw, null, 2)}
|
|
</div>`;
|
|
}
|
|
|
|
vote(emoji) {
|
|
let reaction = emoji;
|
|
let message = this.message.id;
|
|
if (
|
|
confirm(
|
|
'Are you sure you want to react with ' +
|
|
reaction +
|
|
' to ' +
|
|
message +
|
|
'?'
|
|
)
|
|
) {
|
|
tfrpc.rpc
|
|
.appendMessage(this.whoami, {
|
|
type: 'vote',
|
|
vote: {
|
|
link: message,
|
|
value: 1,
|
|
expression: reaction,
|
|
},
|
|
})
|
|
.catch(function (error) {
|
|
alert(error?.message);
|
|
});
|
|
}
|
|
}
|
|
|
|
react(event) {
|
|
emojis.picker((x) => this.vote(x), null, this.whoami);
|
|
}
|
|
|
|
show_image(link) {
|
|
let div = document.createElement('div');
|
|
div.style.left = 0;
|
|
div.style.top = 0;
|
|
div.style.width = '100%';
|
|
div.style.height = '100%';
|
|
div.style.position = 'fixed';
|
|
div.style.background = '#000';
|
|
div.style.zIndex = 100;
|
|
div.style.display = 'grid';
|
|
let img = document.createElement('img');
|
|
img.src = link;
|
|
img.style.maxWidth = '100%';
|
|
img.style.maxHeight = '100%';
|
|
img.style.display = 'block';
|
|
img.style.margin = 'auto';
|
|
img.style.objectFit = 'contain';
|
|
img.style.width = '100%';
|
|
div.appendChild(img);
|
|
function image_close(event) {
|
|
document.body.removeChild(div);
|
|
window.removeEventListener('keydown', image_close);
|
|
}
|
|
div.onclick = image_close;
|
|
window.addEventListener('keydown', image_close);
|
|
document.body.appendChild(div);
|
|
}
|
|
|
|
body_click(event) {
|
|
if (event.srcElement.tagName == 'IMG') {
|
|
this.show_image(event.srcElement.src);
|
|
} else if (
|
|
event.srcElement.tagName == 'DIV' &&
|
|
event.srcElement.classList.contains('img_caption')
|
|
) {
|
|
let next = event.srcElement.nextSibling;
|
|
if (next.style.display != 'none') {
|
|
next.style.display = 'none';
|
|
} else {
|
|
next.style.display = 'block';
|
|
}
|
|
}
|
|
}
|
|
|
|
render_mention(mention) {
|
|
if (!mention?.link || typeof mention.link != 'string') {
|
|
return html` <pre>${JSON.stringify(mention)}</pre>`;
|
|
} else if (
|
|
mention?.link?.startsWith('&') &&
|
|
mention?.type?.startsWith('image/')
|
|
) {
|
|
return html`
|
|
<img
|
|
src=${'/' + mention.link + '/view'}
|
|
style="max-width: 128px; max-height: 128px"
|
|
title=${mention.name}
|
|
@click=${() => this.show_image('/' + mention.link + '/view')}
|
|
/>
|
|
`;
|
|
} else if (
|
|
mention.link?.startsWith('&') &&
|
|
mention.name?.startsWith('audio:')
|
|
) {
|
|
return html`
|
|
<audio controls style="height: 32px">
|
|
<source src=${'/' + mention.link + '/view'}></source>
|
|
</audio>
|
|
`;
|
|
} else if (
|
|
mention.link?.startsWith('&') &&
|
|
mention.name?.startsWith('video:')
|
|
) {
|
|
return html`
|
|
<video controls style="max-height: 240px; max-width: 128px">
|
|
<source src=${'/' + mention.link + '/view'}></source>
|
|
</video>
|
|
`;
|
|
} else if (
|
|
mention.link?.startsWith('&') &&
|
|
mention?.type === 'application/tildefriends'
|
|
) {
|
|
return html` <a href=${`/${mention.link}/`}>😎 ${mention.name}</a>`;
|
|
} else if (mention.link?.startsWith('%') || mention.link?.startsWith('@')) {
|
|
return html` <a href=${'#' + encodeURIComponent(mention.link)}
|
|
>${mention.name}</a
|
|
>`;
|
|
} else if (mention.link?.startsWith('#')) {
|
|
return html` <a href=${'#q=' + encodeURIComponent(mention.link)}
|
|
>${mention.link}</a
|
|
>`;
|
|
} else if (
|
|
Object.keys(mention).length == 2 &&
|
|
mention.link &&
|
|
mention.name
|
|
) {
|
|
return html` <a href=${`/${mention.link}/view`}>${mention.name}</a>`;
|
|
} else {
|
|
return html` <pre style="white-space: pre-wrap">
|
|
${JSON.stringify(mention, null, 2)}</pre
|
|
>`;
|
|
}
|
|
}
|
|
|
|
render_mentions() {
|
|
let mentions = this.message?.content?.mentions || [];
|
|
mentions = mentions.filter(
|
|
(x) => this.message?.content?.text?.indexOf(x.link) === -1
|
|
);
|
|
if (mentions.length) {
|
|
let self = this;
|
|
return html`
|
|
<fieldset style="padding: 0.5em; border: 1px solid black">
|
|
<legend>Mentions</legend>
|
|
${mentions.map((x) => self.render_mention(x))}
|
|
</fieldset>
|
|
`;
|
|
}
|
|
}
|
|
|
|
total_child_messages(message) {
|
|
if (!message.child_messages) {
|
|
return 0;
|
|
}
|
|
let total = message.child_messages.length;
|
|
for (let m of message.child_messages) {
|
|
total += this.total_child_messages(m);
|
|
}
|
|
return total;
|
|
}
|
|
|
|
set_expanded(expanded, tag) {
|
|
this.dispatchEvent(
|
|
new CustomEvent('tf-expand', {
|
|
bubbles: true,
|
|
composed: true,
|
|
detail: {id: (this.message.id || '') + (tag || ''), expanded: expanded},
|
|
})
|
|
);
|
|
}
|
|
|
|
toggle_expanded(tag) {
|
|
this.set_expanded(
|
|
!this.expanded[(this.message.id || '') + (tag || '')],
|
|
tag
|
|
);
|
|
}
|
|
|
|
render_children() {
|
|
let self = this;
|
|
if (this.message.child_messages?.length) {
|
|
if (!this.expanded[this.message.id]) {
|
|
return html`<button
|
|
class="w3-button w3-theme-d1"
|
|
@click=${() => self.set_expanded(true)}
|
|
>
|
|
+ ${this.total_child_messages(this.message) + ' More'}
|
|
</button>`;
|
|
} else {
|
|
return html`<button
|
|
class="w3-button w3-theme-d1"
|
|
@click=${() => self.set_expanded(false)}
|
|
>
|
|
Collapse</button
|
|
>${(this.message.child_messages || []).map(
|
|
(x) =>
|
|
html`<tf-message
|
|
.message=${x}
|
|
whoami=${this.whoami}
|
|
.users=${this.users}
|
|
.drafts=${this.drafts}
|
|
.expanded=${this.expanded}
|
|
channel=${this.channel}
|
|
channel_unread=${this.channel_unread}
|
|
></tf-message>`
|
|
)}`;
|
|
}
|
|
}
|
|
}
|
|
|
|
mark_unread() {
|
|
this.dispatchEvent(new CustomEvent('channelsetunread', {
|
|
bubbles: true,
|
|
composed: true,
|
|
detail: {
|
|
channel: this.channel,
|
|
unread: this.message.rowid,
|
|
},
|
|
}));
|
|
}
|
|
|
|
render_channels() {
|
|
let content = this.message?.content;
|
|
if (this?.messsage?.decrypted?.type == 'post') {
|
|
content = this.message.decrypted;
|
|
}
|
|
let channels = [];
|
|
if (typeof content.channel === 'string') {
|
|
channels.push(`#${content.channel}`);
|
|
}
|
|
if (Array.isArray(content.mentions)) {
|
|
for (let mention of content.mentions) {
|
|
if (typeof mention?.link === 'string' && mention.link.startsWith('#')) {
|
|
channels.push(mention.link);
|
|
}
|
|
}
|
|
}
|
|
return channels.map((x) => html`<tf-tag tag=${x}></tf-tag>`);
|
|
}
|
|
|
|
render() {
|
|
let content = this.message?.content;
|
|
if (this.message?.decrypted?.type == 'post') {
|
|
content = this.message.decrypted;
|
|
}
|
|
let class_background = this.message?.decrypted
|
|
? 'w3-pale-red'
|
|
: (this.message?.rowid >= this.channel_unread ? 'w3-theme-d2' : 'w3-theme-d4');
|
|
let self = this;
|
|
let raw_button;
|
|
switch (this.format) {
|
|
case 'raw':
|
|
if (content?.type == 'post' || content?.type == 'blog') {
|
|
raw_button = html`<button
|
|
class="w3-button w3-theme-d1"
|
|
@click=${() => (self.format = 'md')}
|
|
>
|
|
Markdown
|
|
</button>`;
|
|
} else {
|
|
raw_button = html`<button
|
|
class="w3-button w3-theme-d1"
|
|
@click=${() => (self.format = 'message')}
|
|
>
|
|
Message
|
|
</button>`;
|
|
}
|
|
break;
|
|
case 'md':
|
|
raw_button = html`<button
|
|
class="w3-button w3-theme-d1"
|
|
@click=${() => (self.format = 'message')}
|
|
>
|
|
Message
|
|
</button>`;
|
|
break;
|
|
case 'decrypted':
|
|
raw_button = html`<button
|
|
class="w3-button w3-theme-d1"
|
|
@click=${() => (self.format = 'raw')}
|
|
>
|
|
Raw
|
|
</button>`;
|
|
break;
|
|
default:
|
|
if (this.message.decrypted) {
|
|
raw_button = html`<button
|
|
class="w3-button w3-theme-d1"
|
|
@click=${() => (self.format = 'decrypted')}
|
|
>
|
|
Decrypted
|
|
</button>`;
|
|
} else {
|
|
raw_button = html`<button
|
|
class="w3-button w3-theme-d1"
|
|
@click=${() => (self.format = 'raw')}
|
|
>
|
|
Raw
|
|
</button>`;
|
|
}
|
|
break;
|
|
}
|
|
function small_frame(inner) {
|
|
let body;
|
|
return html`
|
|
<div
|
|
class="w3-card-4 w3-theme-d4 w3-border-theme"
|
|
style="margin-top: 8px; padding: 16px; display: inline-block; overflow-wrap: anywhere"
|
|
>
|
|
<tf-user id=${self.message.author} .users=${self.users}></tf-user>
|
|
<span style="padding-right: 8px"
|
|
><a tfarget="_top" href=${'#' + encodeURIComponent(self.message.id)}
|
|
>%</a
|
|
>
|
|
${new Date(self.message.timestamp).toLocaleString()}</span
|
|
>
|
|
${raw_button} ${self.format == 'raw' ? self.render_raw() : inner}
|
|
${self.render_votes()}
|
|
${(self.message.child_messages || []).map(
|
|
(x) => html`
|
|
<tf-message
|
|
.message=${x}
|
|
whoami=${self.whoami}
|
|
.users=${self.users}
|
|
.drafts=${self.drafts}
|
|
.expanded=${self.expanded}
|
|
channel=${this.channel}
|
|
channel_unread=${this.channel_unread}
|
|
></tf-message>
|
|
`
|
|
)}
|
|
</div>
|
|
`;
|
|
}
|
|
if (this.message?.type === 'contact_group') {
|
|
return html` <div
|
|
class="w3-card-4 w3-theme-d4 w3-border-theme"
|
|
style="margin-top: 8px; padding: 16px; overflow-wrap: anywhere"
|
|
>
|
|
${this.message.messages.map(
|
|
(x) =>
|
|
html`<tf-message
|
|
.message=${x}
|
|
whoami=${this.whoami}
|
|
.users=${this.users}
|
|
.drafts=${this.drafts}
|
|
.expanded=${this.expanded}
|
|
channel=${this.channel}
|
|
channel_unread=${this.channel_unread}
|
|
></tf-message>`
|
|
)}
|
|
</div>`;
|
|
} else if (this.message.placeholder) {
|
|
return html` <div
|
|
class="w3-card-4 w3-theme-d4 w3-border-theme"
|
|
style="margin-top: 8px; padding: 16px; overflow-wrap: anywhere"
|
|
>
|
|
<a target="_top" href=${'#' + encodeURIComponent(this.message.id)}
|
|
>${this.message.id}</a
|
|
>
|
|
(placeholder)
|
|
<div>${this.render_votes()}</div>
|
|
${(this.message.child_messages || []).map(
|
|
(x) => html`
|
|
<tf-message
|
|
.message=${x}
|
|
whoami=${this.whoami}
|
|
.users=${this.users}
|
|
.drafts=${this.drafts}
|
|
.expanded=${this.expanded}
|
|
channel=${this.channel}
|
|
channel_unread=${this.channel_unread}
|
|
></tf-message>
|
|
`
|
|
)}
|
|
</div>`;
|
|
} else if (typeof (content?.type === 'string')) {
|
|
if (content.type == 'about') {
|
|
let name;
|
|
let image;
|
|
let description;
|
|
if (content.name !== undefined) {
|
|
name = html`<div><b>Name:</b> ${content.name}</div>`;
|
|
}
|
|
if (content.image !== undefined) {
|
|
image = html`
|
|
<div><img src=${'/' + (typeof content.image?.link == 'string' ? content.image.link : content.image) + '/view'} style="width: 256px; height: auto"></img></div>
|
|
`;
|
|
}
|
|
if (content.description !== undefined) {
|
|
description = html`
|
|
<div style="flex: 1 0 50%; overflow-wrap: anywhere">
|
|
<div>${unsafeHTML(tfutils.markdown(content.description))}</div>
|
|
</div>
|
|
`;
|
|
}
|
|
let update =
|
|
content.about == this.message.author
|
|
? html`<div style="font-weight: bold">Updated profile.</div>`
|
|
: html`<div style="font-weight: bold">
|
|
Updated profile for
|
|
<tf-user id=${content.about} .users=${this.users}></tf-user>.
|
|
</div>`;
|
|
return small_frame(html` ${update} ${name} ${image} ${description} `);
|
|
} else if (content.type == 'contact') {
|
|
return html`
|
|
<div>
|
|
<tf-user id=${this.message.author} .users=${this.users}></tf-user>
|
|
is
|
|
${content.blocking === true
|
|
? 'blocking'
|
|
: content.blocking === false
|
|
? 'no longer blocking'
|
|
: content.following === true
|
|
? 'following'
|
|
: content.following === false
|
|
? 'no longer following'
|
|
: '?'}
|
|
<tf-user
|
|
id=${this.message.content.contact}
|
|
.users=${this.users}
|
|
></tf-user>
|
|
</div>
|
|
`;
|
|
} else if (content.type == 'post') {
|
|
let reply =
|
|
this.drafts[this.message?.id] !== undefined
|
|
? html`
|
|
<tf-compose
|
|
whoami=${this.whoami}
|
|
.users=${this.users}
|
|
root=${content.root || this.message.id}
|
|
branch=${this.message.id}
|
|
.drafts=${this.drafts}
|
|
@tf-discard=${this.discard_reply}
|
|
author=${this.message.author}
|
|
></tf-compose>
|
|
`
|
|
: html`
|
|
<button class="w3-button w3-theme-d1" @click=${this.show_reply}>
|
|
Reply
|
|
</button>
|
|
`;
|
|
let self = this;
|
|
let body;
|
|
switch (this.format) {
|
|
case 'raw':
|
|
body = this.render_raw();
|
|
break;
|
|
case 'md':
|
|
body = html`<code
|
|
style="white-space: pre-wrap; overflow-wrap: anywhere"
|
|
>${content.text}</code
|
|
>`;
|
|
break;
|
|
case 'message':
|
|
body = unsafeHTML(tfutils.markdown(content.text));
|
|
break;
|
|
case 'decrypted':
|
|
body = html`<pre
|
|
style="white-space: pre-wrap; overflow-wrap: anywhere"
|
|
>
|
|
${JSON.stringify(content, null, 2)}</pre
|
|
>`;
|
|
break;
|
|
}
|
|
let content_warning = html`
|
|
<div
|
|
class="w3-panel w3-round-xlarge w3-theme-l4"
|
|
style="cursor: pointer"
|
|
@click=${(x) => this.toggle_expanded(':cw')}
|
|
>
|
|
<p>${content.contentWarning}</p>
|
|
</div>
|
|
`;
|
|
let content_html = html`
|
|
${this.render_channels()}
|
|
<div @click=${this.body_click}>${body}</div>
|
|
${this.render_mentions()}
|
|
`;
|
|
let payload = content.contentWarning
|
|
? self.expanded[(this.message.id || '') + ':cw']
|
|
? html` ${content_warning} ${content_html} `
|
|
: content_warning
|
|
: content_html;
|
|
let is_encrypted = this.message?.decrypted
|
|
? html`<span style="align-self: center">🔓</span>`
|
|
: undefined;
|
|
return html`
|
|
<style>
|
|
code {
|
|
white-space: pre-wrap;
|
|
overflow-wrap: break-word;
|
|
}
|
|
div {
|
|
overflow-wrap: anywhere;
|
|
}
|
|
img {
|
|
max-width: 100%;
|
|
height: auto;
|
|
display: block;
|
|
}
|
|
</style>
|
|
<div
|
|
class="w3-card-4 ${class_background} w3-border-theme"
|
|
style="margin-top: 8px; padding: 16px"
|
|
>
|
|
<div style="display: flex; flex-direction: row">
|
|
<tf-user id=${this.message.author} .users=${this.users}></tf-user>
|
|
${is_encrypted}
|
|
<span style="flex: 1"></span>
|
|
<span style="padding-right: 8px"
|
|
><a
|
|
target="_top"
|
|
href=${'#' + encodeURIComponent(self.message.id)}
|
|
>%</a
|
|
>
|
|
${new Date(this.message.timestamp).toLocaleString()}</span
|
|
>
|
|
<span>${raw_button}</span>
|
|
</div>
|
|
${payload} ${this.render_votes()}
|
|
<p>
|
|
${reply}
|
|
<button class="w3-button w3-theme-d1" @click=${this.react}>
|
|
React
|
|
</button>
|
|
${(!content.root && this.message.rowid <= this.channel_unread) ?
|
|
html`
|
|
<button class="w3-button w3-theme-d1" @click=${this.mark_unread}>Mark Unread</button>
|
|
` :
|
|
undefined}
|
|
</p>
|
|
${this.render_children()}
|
|
</div>
|
|
`;
|
|
} else if (content.type === 'issue') {
|
|
let is_encrypted = this.message?.decrypted
|
|
? html`<span style="align-self: center">🔓</span>`
|
|
: undefined;
|
|
return html`
|
|
<style>
|
|
code {
|
|
white-space: pre-wrap;
|
|
overflow-wrap: break-word;
|
|
}
|
|
div {
|
|
overflow-wrap: anywhere;
|
|
}
|
|
img {
|
|
max-width: 100%;
|
|
height: auto;
|
|
display: block;
|
|
}
|
|
</style>
|
|
<div
|
|
class="w3-card-4 ${class_background} w3-border-theme"
|
|
style="margin-top: 8px; padding: 16px"
|
|
>
|
|
<div style="display: flex; flex-direction: row">
|
|
<tf-user id=${this.message.author} .users=${this.users}></tf-user>
|
|
${is_encrypted}
|
|
<span style="flex: 1"></span>
|
|
<span style="padding-right: 8px"
|
|
><a
|
|
target="_top"
|
|
href=${'#' + encodeURIComponent(self.message.id)}
|
|
>%</a
|
|
>
|
|
${new Date(this.message.timestamp).toLocaleString()}</span
|
|
>
|
|
<span>${raw_button}</span>
|
|
</div>
|
|
${content.text} ${this.render_votes()}
|
|
<p>
|
|
<button class="w3-button w3-theme-d1" @click=${this.react}>
|
|
React
|
|
</button>
|
|
</p>
|
|
${this.render_children()}
|
|
</div>
|
|
`;
|
|
} else if (content.type === 'blog') {
|
|
let self = this;
|
|
tfrpc.rpc.get_blob(content.blog).then(function (data) {
|
|
self.blog_data = data;
|
|
});
|
|
let payload = this.expanded[(this.message.id || '') + ':blog']
|
|
? html`<div>
|
|
${this.blog_data
|
|
? unsafeHTML(tfutils.markdown(this.blog_data))
|
|
: 'Loading...'}
|
|
</div>`
|
|
: undefined;
|
|
let body;
|
|
switch (this.format) {
|
|
case 'raw':
|
|
body = this.render_raw();
|
|
break;
|
|
case 'md':
|
|
body = content.summary;
|
|
break;
|
|
case 'message':
|
|
body = html`
|
|
<div
|
|
style="border: 1px solid #fff; border-radius: 1em; padding: 8px; margin: 4px; cursor: pointer"
|
|
@click=${(x) => self.toggle_expanded(':blog')}>
|
|
<h2>${content.title}</h2>
|
|
<div style="display: flex; flex-direction: row">
|
|
<img src=/${content.thumbnail}/view></img>
|
|
<span>${content.summary}</span>
|
|
</div>
|
|
</div>
|
|
${payload}
|
|
`;
|
|
break;
|
|
}
|
|
let reply =
|
|
this.drafts[this.message?.id] !== undefined
|
|
? html`
|
|
<tf-compose
|
|
whoami=${this.whoami}
|
|
.users=${this.users}
|
|
root=${content.root || this.message.id}
|
|
branch=${this.message.id}
|
|
.drafts=${this.drafts}
|
|
@tf-discard=${this.discard_reply}
|
|
author=${this.message.author}
|
|
></tf-compose>
|
|
`
|
|
: html`
|
|
<button class="w3-button w3-theme-d1" @click=${this.show_reply}>
|
|
Reply
|
|
</button>
|
|
`;
|
|
return html`
|
|
<style>
|
|
code {
|
|
white-space: pre-wrap;
|
|
overflow-wrap: break-word;
|
|
}
|
|
div {
|
|
overflow-wrap: anywhere;
|
|
}
|
|
img {
|
|
max-width: 100%;
|
|
height: auto;
|
|
display: block;
|
|
}
|
|
</style>
|
|
<div
|
|
class="w3-card-4 w3-theme-d4 w3-border-theme"
|
|
style="margin-top: 8px; padding: 16px"
|
|
>
|
|
<div style="display: flex; flex-direction: row">
|
|
<tf-user id=${this.message.author} .users=${this.users}></tf-user>
|
|
<span style="flex: 1"></span>
|
|
<span style="padding-right: 8px"
|
|
><a
|
|
target="_top"
|
|
href=${'#' + encodeURIComponent(self.message.id)}
|
|
>%</a
|
|
>
|
|
${new Date(this.message.timestamp).toLocaleString()}</span
|
|
>
|
|
<span>${raw_button}</span>
|
|
</div>
|
|
|
|
<div>${body}</div>
|
|
${this.render_mentions()}
|
|
<div>
|
|
${reply}
|
|
<button class="w3-button w3-theme-d1" @click=${this.react}>
|
|
React
|
|
</button>
|
|
</div>
|
|
${this.render_votes()} ${this.render_children()}
|
|
</div>
|
|
`;
|
|
} else if (content.type === 'pub') {
|
|
return small_frame(
|
|
html` <style>
|
|
span {
|
|
overflow-wrap: anywhere;
|
|
}
|
|
</style>
|
|
<span>
|
|
<div>
|
|
🍻
|
|
<tf-user
|
|
.users=${this.users}
|
|
id=${content.address.key}
|
|
></tf-user>
|
|
</div>
|
|
<pre>${content.address.host}:${content.address.port}</pre>
|
|
</span>`
|
|
);
|
|
} else if (content.type === 'channel') {
|
|
return small_frame(html`
|
|
<div>
|
|
${content.subscribed ? 'subscribed to' : 'unsubscribed from'}
|
|
<a href=${'#q=' + encodeURIComponent('#' + content.channel)}
|
|
>#${content.channel}</a
|
|
>
|
|
</div>
|
|
`);
|
|
} else if (typeof this.message.content == 'string') {
|
|
if (this.message?.decrypted) {
|
|
if (this.format == 'decrypted') {
|
|
return small_frame(
|
|
html`<span>🔓</span>
|
|
<pre>${JSON.stringify(this.message.decrypted, null, 2)}</pre>`
|
|
);
|
|
} else {
|
|
return small_frame(
|
|
html`<span>🔓</span>
|
|
<div>${this.message.decrypted.type}</div>`
|
|
);
|
|
}
|
|
} else {
|
|
return small_frame(html`<span>🔒</span>`);
|
|
}
|
|
} else {
|
|
return small_frame(html`<div><b>type</b>: ${content.type}</div>`);
|
|
}
|
|
} else {
|
|
return small_frame(this.render_raw());
|
|
}
|
|
}
|
|
}
|
|
|
|
customElements.define('tf-message', TfMessageElement);
|