import {LitElement, html, 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}, raw: {type: Boolean}, collapsed: {type: Boolean}, content_warning_expanded: {type: Boolean}, blog_data: {type: String}, blog_expanded: {type: Boolean}, } } static styles = styles; constructor() { super(); let self = this; this.whoami = null; this.message = {}; this.users = {}; this.drafts = {}; this.raw = false; this.collapsed = false; } show_reply() { let event = new CustomEvent('tf-draft', {bubbles: true, composed: true, detail: {id: this.message?.id, draft: ''}}); this.dispatchEvent(event); } discard_reply() { this.dispatchEvent(new CustomEvent('tf-draft', {bubbles: true, composed: true, detail: {id: this.id, draft: undefined}})); } render_votes() { function normalize_expression(expression) { if (expression === 'Like' || !expression) { return '👍'; } else if (expression === 'Unlike') { return '👎'; } else if (expression === 'heart') { return '❤️'; } else { return expression; } } return html`
${(this.message.votes || []).map( vote => html` ${normalize_expression(vote.content.vote.expression)} `)}
`; } 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`
${JSON.stringify(raw, null, 2)}
` } vote(emoji) { let reaction = emoji.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)); } 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); } } render_mention(mention) { if (!mention?.link || typeof(mention.link) != 'string') { return html`
${JSON.stringify(mention)}
`; } else if (mention?.link?.startsWith('&') && mention?.type?.startsWith('image/')) { return html` this.show_image('/' + mention.link + '/view')}> `; } else if (mention.link?.startsWith('&') && mention.name?.startsWith('audio:')) { return html` `; } else if (mention.link?.startsWith('&') && mention.name?.startsWith('video:')) { return html` `; } else if (mention.link?.startsWith('&') && mention?.type === 'application/tildefriends') { return html` 😎 ${mention.name}`; } else if (mention.link?.startsWith('%') || mention.link?.startsWith('@')) { return html` ${mention.name}`; } else if (mention.link?.startsWith('#')) { return html` ${mention.link}`; } else if (Object.keys(mention).length == 2 && mention.link && mention.name) { return html` ${mention.name}`; } else { return html`
${JSON.stringify(mention, null, 2)}
`; } } render_mentions() { let mentions = this.message?.content?.mentions || []; mentions = mentions.filter(x => x.name?.startsWith('audio:') || x.name?.startsWith('video:') || this.message?.content?.text?.indexOf(x.link) === -1); if (mentions.length) { let self = this; return html`
Mentions ${mentions.map(x => self.render_mention(x))}
`; } } 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; } render_children() { let self = this; if (this.message.child_messages?.length) { if (this.collapsed) { return html` self.collapsed = false}>`; } else { return html` self.collapsed = true}>${(this.message.child_messages || []).map(x => html``)}`; } } } render() { let content = this.message?.content; let self = this; let raw_button = this.raw ? html` self.raw = false}>` : html` self.raw = true}>`; function small_frame(inner) { return html`
% ${new Date(self.message.timestamp).toLocaleString()} ${raw_button} ${self.raw ? self.render_raw() : inner} ${self.render_votes()}
` } if (this.message.placeholder) { return html`
${this.message.id} (placeholder)
${this.render_votes()}
${(this.message.child_messages || []).map(x => html` `)}
`; } else if (typeof(content?.type === 'string')) { if (content.type == 'about') { let name; let image; let description; if (content.name !== undefined) { name = html`
Name: ${content.name}
`; } if (content.image !== undefined) { image = html`
`; } if (content.description !== undefined) { description = html`
${unsafeHTML(tfutils.markdown(content.description))}
` } let update = content.about == this.message.author ? html`
Updated profile.
` : html`
Updated profile for .
`; return small_frame(html` ${update} ${name} ${image} ${description} `); } else if (content.type == 'contact') { return small_frame(html`
is ${ content.blocking === true ? 'blocking' : content.blocking === false ? 'no longer blocking' : content.following === true ? 'following' : content.following === false ? 'no longer following' : '?' }
`); } else if (content.type == 'post') { let reply = (this.drafts[this.message?.id] !== undefined) ? html` ` : html` `; let self = this; let body = this.raw ? this.render_raw() : unsafeHTML(tfutils.markdown(content.text)); let content_warning = html`
self.content_warning_expanded = !self.content_warning_expanded}>${content.contentWarning}
`; let content_html = html`
${body}
${this.render_mentions()} `; let payload = content.contentWarning ? self.content_warning_expanded ? html` ${content_warning} ${content_html} ` : content_warning : content_html; return html`
% ${new Date(this.message.timestamp).toLocaleString()} ${raw_button}
${payload} ${this.render_votes()}
${reply}
${this.render_children()}
`; } else if (content.type === 'blog') { let self = this; tfrpc.rpc.get_blob(content.blog).then(function(data) { self.blog_data = data; }); let payload = this.blog_expanded ? html`
${this.blog_data ? unsafeHTML(tfutils.markdown(this.blog_data)) : 'Loading...'}
` : undefined; let body = this.raw ? this.render_raw() : html`
self.blog_expanded = !self.blog_expanded}>

${content.title}

${content.summary}
${payload} `; return html`
% ${new Date(this.message.timestamp).toLocaleString()} ${raw_button}
${body}
${this.render_mentions()} ${this.render_votes()}
`; } else if (content.type === 'pub') { return small_frame(html`
🍻
${content.address.host}:${content.address.port}
`); } else if (content.type === 'channel') { return small_frame(html`
${content.subscribed ? 'subscribed to' : 'unsubscribed from'} #${content.channel}
`); } else if (typeof(this.message.content) == 'string') { return small_frame(html`🔒`); } else { return small_frame(html`
type: ${content.type}
`); } } else { return small_frame(this.render_raw()); } } } customElements.define('tf-message', TfMessageElement);