tildefriends/apps/cory/ssblit/tf-message.js

207 lines
6.4 KiB
JavaScript
Raw Normal View History

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},
reply: {type: Boolean},
raw: {type: Boolean},
}
}
static styles = styles;
constructor() {
super();
let self = this;
this.whoami = null;
this.message = {};
this.users = {};
this.reply = false;
this.raw = false;
}
show_reply() {
this.reply = true;
}
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`<div>${(this.message.votes || []).map(vote => html`<span title="${this.users[vote.author]?.name ?? vote.author}">${normalize_expression(vote.content.vote.expression)}</span>`)}</div>`;
}
render_raw() {
return html`<div style="white-space: pre-wrap">${JSON.stringify(this.message, null, 2)}</div>`
}
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));
}
render_mention(mention) {
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}>
`;
} 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.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 {
return html`<pre>${JSON.stringify(mention)}</pre>`;
}
}
render_mentions() {
if (this.message?.content?.mentions?.length) {
let self = this;
return html`
<fieldset style="background-color: rgba(0, 0, 0, 0.1); padding: 0.5em; border: 1px solid black">
<legend>Mentions</legend>
${(this.message?.content?.mentions || []).map(x => self.render_mention(x))}
</fieldset>
`;
}
}
render() {
let content = this.message?.content;
let self = this;
let raw_button = this.raw ?
html`<input type="button" value="Message" @click=${() => self.raw = false}></input>` :
html`<input type="button" value="Raw" @click=${() => self.raw = true}></input>`;
function small_frame(inner) {
return html`
<div style="border: 1px solid black; background-color: rgba(255, 255, 255, 0.1); margin-top: 8px; padding: 16px; display: inline-block">
<tf-user id=${self.message.author} .users=${self.users}></tf-user>
<span style="padding-right: 8px"><a target="_top" href=${'#' + self.message.id}>%</a> ${new Date(self.message.timestamp).toLocaleString()}</span>
${raw_button}
${self.raw ? self.render_raw() : inner}
${self.render_votes()}
</div>
`
}
if (this.message.placeholder) {
return html`
<div style="border: 1px solid black; background-color: rgba(255, 255, 255, 0.1); margin-top: 8px; padding: 16px">
${this.message.id} (placeholder)
<div>${this.render_votes()}</div>
${(this.message.child_messages || []).map(x => html`
<tf-message .message=${x} whoami=${this.whoami} .users=${this.users}></tf-message>
`)}
</div>`;
} else if (content.type == 'about') {
return small_frame(html`
<div style="font-weight: bold">Updated profile:</div>
<pre style="white-space: pre-wrap">${JSON.stringify(content, null, 2)}</pre>
`);
} else if (content.type == 'contact') {
return small_frame(html`
<div>
is now
${
content.blocking === true ? 'blocking' :
content.blocking === false ? 'unblocking' :
content.following === true ? 'following' :
content.following === false ? 'unfollowing' :
'?'
}
<tf-user id=${this.message.content.contact} .users=${this.users}></tf-user>
</div>
`);
} else if (content.type == 'post') {
let reply = this.reply ? html`
<tf-compose
?enabled=${this.reply}
whoami=${this.whoami}
.users=${this.users}
root=${this.message.content.root || this.message.id}
branch=${this.message.id}
@tf-discard=${() => this.reply = false}></tf-compose>
` : html`
<input type="button" value="Reply" @click=${this.show_reply}></input>
`;
let self = this;
let body = this.raw ?
this.render_raw() :
unsafeHTML(tfutils.markdown(content.text));
return html`
<style>
code {
white-space: pre-wrap;
overflow-wrap: break-word;
}
img {
max-width: 100%;
height: auto;
}
</style>
<div style="border: 1px solid black; background-color: rgba(255, 255, 255, 0.1); 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=${'#' + self.message.id}>%</a> ${new Date(this.message.timestamp).toLocaleString()}</span>
<span>${raw_button}</span>
</div>
<div>${body}</div>
${this.render_mentions()}
${this.render_votes()}
<div>
${reply}
<input type="button" value="React" @click=${this.react}></input>
</div>
${(this.message.child_messages || []).map(x => html`<tf-message .message=${x} whoami=${this.whoami} .users=${this.users}></tf-message>`)}
</div>
`;
} else if (typeof(this.message.content) == 'string') {
return small_frame(html`<span>🔒</span>`);
} else {
return small_frame(this.render_raw());
}
}
}
customElements.define('tf-message', TfMessageElement);