Files
.gitea
apps
admin
api
apps
blog
db
follow
identity
intro
issues
journal
room
sneaker
ssb
app.js
commonmark-hashtag.js
commonmark.min.js
emojis.js
emojis.json
filesaver.min.js
filesaver.min.js.map
index.html
lit-all.min.js
lit-all.min.js.map
script.js
tf-app.js
tf-compose.js
tf-message.js
tf-news.js
tf-profile.js
tf-reactions-modal.js
tf-styles.js
tf-tab-connections.js
tf-tab-news-feed.js
tf-tab-news.js
tf-tab-query.js
tf-tab-search.js
tf-tag.js
tf-user.js
tf-utils.js
tribute.css
tribute.esm.js
storage
test
todo
web
welcome
wiki
admin.json
api.json
apps.json
blog.json
db.json
follow.json
identity.json
intro.json
issues.json
journal.json
room.json
sneaker.json
ssb.json
storage.json
test.json
todo.json
web.json
welcome.json
wiki.json
core
deps
docs
metadata
src
tools
.clang-format
.dockerignore
.git-blame-ignore-revs
.gitignore
.gitmodules
.prettierignore
.prettierrc.yaml
CONTRIBUTING.md
Dockerfile
Doxyfile
GNUmakefile
LICENSE
README.md
default.nix
flake.lock
flake.nix
package-lock.json
package.json
tildefriends/apps/ssb/tf-message.js

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1003 lines
25 KiB
JavaScript
Raw Normal View History

import {
LitElement,
css,
html,
repeat,
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},
recent_reactions: {type: Array},
};
}
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;
this.recent_reactions = [];
}
connectedCallback() {
super.connectedCallback();
this._click_callback = this.document_click.bind(this);
document.body.addEventListener('mouseup', this._click_callback);
}
disconnectedCallback() {
super.disconnectedCallback();
document.body.removeEventListener('mouseup', this._click_callback);
}
document_click(event) {
let content = this.renderRoot.querySelector('.w3-dropdown-content');
let target = event.target;
2025-04-09 12:28:56 -04:00
if (content && !content.contains(target)) {
content.classList.remove('w3-show');
}
}
show_reply() {
2024-02-24 11:09:34 -05:00
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() {
2024-02-24 11:09:34 -05:00
this.dispatchEvent(
new CustomEvent('tf-draft', {
bubbles: true,
composed: true,
detail: {id: this.id, draft: undefined},
})
);
}
2024-04-21 14:18:06 -04:00
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 === 'Unlike' ||
expression === 'unlike' ||
expression == 'undig'
) {
return '👎';
} else if (expression === 'heart') {
return '❤️';
} else if (
(expression ?? '').split('').every((x) => x.charCodeAt(0) < 256)
) {
return '👍';
} else {
return expression;
}
}
if (this.message?.votes?.length) {
return html` <footer class="w3-container">
<div
class="w3-button w3-bar"
style="padding: 0"
@click=${this.show_reactions}
>
2024-09-23 12:43:48 -04:00
${(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)}"
2024-09-23 12:43:48 -04:00
>
${normalize_expression(vote.content.vote.expression)}
</span>
`
)}
</div>
</footer>`;
}
}
render_json(value) {
let json = JSON.stringify(value, null, 2);
return html`
<pre style="white-space: pre-wrap; overflow-wrap: anywhere">${json}</pre>
`;
}
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 this.render_json(raw);
}
vote(emoji) {
let reaction = emoji;
let message = this.message.id;
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,
this.recent_reactions
);
}
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);
2024-02-24 11:09:34 -05:00
} 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) {
2024-02-24 11:09:34 -05:00
if (!mention?.link || typeof mention.link != 'string') {
return this.render_json(mention);
2024-02-24 11:09:34 -05:00
} else if (
mention?.link?.startsWith('&') &&
mention?.type?.startsWith('image/')
) {
return html`
2024-02-24 11:09:34 -05:00
<img
src=${'/' + mention.link + '/view'}
style="max-width: 128px; max-height: 128px"
title=${mention.name}
@click=${() => this.show_image('/' + mention.link + '/view')}
/>
`;
2024-02-24 11:09:34 -05:00
} else if (
mention.link?.startsWith('&') &&
mention.name?.startsWith('audio:')
) {
return html`
<audio controls style="height: 32px">
<source src=${'/' + mention.link + '/view'}></source>
</audio>
`;
2024-02-24 11:09:34 -05:00
} 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>
`;
2024-02-24 11:09:34 -05:00
} 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('@')) {
2024-02-24 11:09:34 -05:00
return html` <a href=${'#' + encodeURIComponent(mention.link)}
>${mention.name}</a
>`;
} else if (mention.link?.startsWith('#')) {
return html` <a href=${'#' + encodeURIComponent('#' + mention.link)}
2024-02-24 11:09:34 -05:00
>${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 this.render_json(mention);
}
}
render_mentions() {
let mentions = this.message?.content?.mentions || [];
2024-02-24 11:09:34 -05:00
mentions = mentions.filter(
(x) =>
this.message?.content?.text?.indexOf(
typeof x === 'string' ? x : x.link
) === -1
2024-02-24 11:09:34 -05:00
);
if (mentions.length) {
let self = this;
return html`
2024-05-12 07:48:34 -04:00
<fieldset style="padding: 0.5em; border: 1px solid black">
<legend>Mentions</legend>
2024-02-24 11:09:34 -05:00
${mentions.map((x) => self.render_mention(x))}
</fieldset>
`;
}
}
total_child_messages(message) {
if (!message.child_messages) {
return 0;
}
let total = message.child_messages.length;
2024-02-24 11:09:34 -05:00
for (let m of message.child_messages) {
total += this.total_child_messages(m);
}
return total;
}
expanded_key() {
return this.message?.id || this.messages?.map((x) => x.id).join(':');
}
set_expanded(expanded, tag) {
let key = this.expanded_key();
2024-02-24 11:09:34 -05:00
this.dispatchEvent(
new CustomEvent('tf-expand', {
bubbles: true,
composed: true,
detail: {id: key + (tag || ''), expanded: expanded},
2024-02-24 11:09:34 -05:00
})
);
}
toggle_expanded(tag) {
let key = this.expanded_key();
this.set_expanded(!this.expanded[key + (tag || '')], tag);
}
is_expanded(tag) {
let key = this.expanded_key();
return this.expanded[key + (tag || '')];
}
render_children() {
let self = this;
if (this.message.child_messages?.length) {
if (!this.expanded[this.expanded_key()]) {
return html`
<button
class="w3-button w3-theme-d1 w3-block w3-bar"
style="box-sizing: border-box"
@click=${() => self.set_expanded(true)}
2024-02-24 11:09:34 -05:00
>
+ ${this.total_child_messages(this.message) + ' More'}
</button>
2025-04-16 19:27:24 -04:00
`;
} else {
2025-04-16 19:27:24 -04:00
return html` <div class="w3-container w3-margin-bottom">
${repeat(
this.message.child_messages || [],
(x) => x.id,
(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}
.recent_reactions=${this.recent_reactions}
></tf-message>`
)}
</div>
<button
class="w3-button w3-theme-d1 w3-block w3-bar"
style="box-sizing: border-box"
@click=${() => self.set_expanded(false)}
>
2025-04-16 19:27:24 -04:00
Collapse
</button>`;
}
} else {
return undefined;
}
}
mark_unread() {
2024-12-05 20:47:02 -05:00
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) {
2024-02-24 11:09:34 -05:00
if (typeof mention?.link === 'string' && mention.link.startsWith('#')) {
channels.push(mention.link);
}
}
}
2024-02-24 11:09:34 -05:00
return channels.map((x) => html`<tf-tag tag=${x}></tf-tag>`);
}
class_background() {
return this.message?.decrypted
? 'w3-pale-red'
2025-06-04 20:48:04 -04:00
: this.allow_unread() && this.message?.rowid >= this.channel_unread
? 'w3-theme-d2'
: 'w3-theme-d4';
}
get_content() {
let content = this.message?.content;
if (this.message?.decrypted?.type == 'post') {
content = this.message.decrypted;
}
return content;
}
copy_id(event) {
navigator.clipboard.writeText(this.message?.id);
}
toggle_menu(event) {
2025-04-09 12:28:56 -04:00
event.srcElement.parentNode
.querySelector('.w3-dropdown-content')
.classList.toggle('w3-show');
}
render_menu() {
let content = this.get_content();
2025-04-05 22:05:26 -04:00
let formats = [['message', 'Message']];
if (content?.type == 'post' || content?.type == 'blog') {
formats.push(['md', 'Markdown']);
}
if (this.message?.decrypted) {
formats.push(['decrypted', 'Decrypted']);
}
formats.push(['raw', 'Raw']);
return html`
<div class="w3-bar-item w3-right">
2025-04-09 12:28:56 -04:00
<button class="w3-button w3-theme-d1" @click=${this.toggle_menu}>
%
</button>
2025-04-05 22:05:26 -04:00
<div
class="w3-dropdown-content w3-bar-block w3-card-4 w3-theme-l1"
style="right: 48px"
>
<a
target="_top"
class="w3-button w3-bar-item"
href=${'#' + encodeURIComponent(this.message?.id)}
>View Message</a
2025-04-05 22:05:26 -04:00
>
<button
class="w3-button w3-bar-item w3-border-bottom"
@click=${this.copy_id}
>
Copy ID
</button>
2025-04-09 22:15:51 -04:00
${this.drafts[this.message?.id] === undefined
? html`
<button class="w3-button w3-bar-item" @click=${this.show_reply}>
Reply
2025-04-09 22:15:51 -04:00
</button>
`
: undefined}
<button
class="w3-button w3-bar-item w3-border-bottom"
@click=${this.react}
>
👍 React
</button>
2025-04-05 22:05:26 -04:00
${formats.map(
([format, name]) => html`
<button
class="w3-button w3-bar-item"
style=${format == this.format ? 'font-weight: bold' : ''}
@click=${() => (this.format = format)}
>
${name}
</button>
`
)}
</div>
</div>
`;
}
render_header() {
let is_encrypted = this.message?.decrypted
? html`<span class="w3-bar-item">🔓</span>`
2025-01-04 12:41:04 -05:00
: typeof this.message?.content == 'string'
? html`<span class="w3-bar-item">🔒</span>`
: undefined;
return html`
<header class="w3-bar">
<span class="w3-bar-item">
${this.render_unread_icon()}<tf-user
id=${this.message.author}
.users=${this.users}
></tf-user>
</span>
2025-04-05 22:05:26 -04:00
${is_encrypted} ${this.render_menu()}
<div class="w3-bar-item w3-right" style="text-wrap: nowrap">
${new Date(this.message.timestamp).toLocaleString()}
</div>
</header>
`;
}
render_frame(inner) {
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
2025-01-02 21:16:49 -05:00
class="w3-card-4 ${this.class_background()} w3-border-theme w3-margin-top"
style="overflow: auto; overflow-wrap: anywhere; display: block; max-width: 100%"
>
${inner}
</div>
2025-01-04 12:41:04 -05:00
`;
}
render_small_frame(inner) {
let self = this;
return this.render_frame(html`
2025-01-04 12:41:04 -05:00
${self.render_header()}
${self.format == 'raw'
? html`<div class="w3-container">${self.render_raw()}</div>`
: 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=${self.channel}
channel_unread=${self.channel_unread}
.recent_reactions=${self.recent_reactions}
2025-01-04 12:41:04 -05:00
></tf-message>
`
)}
`);
}
render_actions() {
let content = this.get_content();
let reply =
this.drafts[this.message?.id] !== undefined
? html`
<div class="w3-section w3-container">
<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}
.recent_reactions=${this.recent_reactions}
></tf-compose>
</div>
`
: undefined;
return html`
${reply}
2025-04-16 19:27:24 -04:00
<footer>${this.render_children()}</footer>
`;
}
content_group_by_author() {
let sorted = this.message.messages
.map((x) => [
x.author,
x.content.blocking !== undefined
? x.content.blocking
? 'is blocking'
: 'is no longer blocking'
: x.content.following !== undefined
? x.content.following
? 'is following'
: 'is no longer following'
: '',
x.content.contact,
x,
])
.sort();
let result = [];
let last;
let group;
for (let row of sorted) {
if (last && last[0] == row[0] && last[1] == row[1]) {
group.push(row[2]);
} else {
if (group) {
result.push({author: last[0], action: last[1], users: group});
}
last = row;
group = [row[2]];
}
}
if (group) {
result.push({author: last[0], action: last[1], users: group});
}
return result;
}
allow_unread() {
2025-06-12 12:50:38 -04:00
return (
this.channel == '@' ||
(!this.channel.startsWith('@') && !this.channel.startsWith('%'))
);
}
render_unread_icon() {
2025-06-04 20:48:04 -04:00
return this.allow_unread() && this.message?.rowid >= this.channel_unread
? html`✉️`
: undefined;
}
render() {
let content = this.message?.content;
if (this.message?.decrypted?.type == 'post') {
content = this.message.decrypted;
}
let class_background = this.class_background();
let self = this;
if (this.message?.type === 'contact_group') {
if (this.expanded[this.expanded_key()]) {
return this.render_frame(html`
<div class="w3-padding">
${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>
<button
class="w3-button w3-theme-d1 w3-block w3-bar"
style="box-sizing: border-box"
@click=${() => self.set_expanded(false)}
>
Collapse
</button>
`);
} else {
return this.render_frame(html`
<div class="w3-padding">
${this.content_group_by_author().map(
(x) => html`
<div>
<tf-user id=${x.author} .users=${this.users}></tf-user>
${x.action}
${x.users.map(
(y) => html`
<tf-user id=${y} .users=${this.users}></tf-user>
`
)}
</div>
`
)}
</div>
<button
class="w3-button w3-theme-d1 w3-block w3-bar"
style="box-sizing: border-box"
@click=${() => self.set_expanded(true)}
>
Expand
</button>
`);
}
} else if (this.message.placeholder) {
2025-01-04 12:41:04 -05:00
return this.render_frame(
html`<div>
<div class="w3-bar">
<a
class="w3-bar-item w3-panel w3-round-xlarge w3-theme-d1 w3-margin w3-button"
target="_top"
href=${'#' + encodeURIComponent(this.message?.id)}
>
This message is not currently available.
</a>
<div class="w3-bar-item w3-right">
<button class="w3-button w3-theme-d1" @click=${this.toggle_menu}>
%
</button>
<div
class="w3-dropdown-content w3-bar-block w3-card-4 w3-theme-l1"
style="right: 48px"
>
<a
target="_top"
class="w3-button w3-bar-item"
href=${'#' + encodeURIComponent(this.message?.id)}
>View Message</a
>
<button
class="w3-button w3-bar-item w3-border-bottom"
@click=${this.copy_id}
>
Copy ID
</button>
</div>
</div>
</div>
2025-01-04 12:41:04 -05:00
<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>`
2025-01-04 12:41:04 -05:00
);
} 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 @click=${this.body_click}><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>
`;
}
2024-02-24 11:09:34 -05:00
let update =
content.about == this.message.author
? html`<div style="font-weight: bold">Updated profile.</div>`
2025-01-05 21:43:26 -05:00
: html`<div style="font-weight: bold">
2024-02-24 11:09:34 -05:00
Updated profile for
<tf-user id=${content.about} .users=${this.users}></tf-user>.
</div>`;
return this.render_small_frame(html`
2025-01-05 21:43:26 -05:00
<div class="w3-container">
<p>${update} ${name} ${image} ${description}</p>
</div>
`);
} else if (content.type == 'contact') {
return this.render_frame(html`
<div class="w3-bar">
<div class="w3-bar-item">
<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>
<div class="w3-bar-item w3-right">
<button class="w3-button w3-theme-d1" @click=${this.toggle_menu}>
%
</button>
<div
class="w3-dropdown-content w3-bar-block w3-card-4 w3-theme-l1"
style="right: 48px"
>
<a
target="_top"
class="w3-button w3-bar-item"
href=${'#' + encodeURIComponent(this.message?.id)}
>View Message</a
>
<button
class="w3-button w3-bar-item w3-border-bottom"
@click=${this.copy_id}
>
Copy ID
</button>
${this.drafts[this.message?.id] === undefined
? html`
<button
class="w3-button w3-bar-item"
@click=${this.show_reply}
>
Reply
</button>
`
: undefined}
</div>
</div>
${this.render_votes()} ${this.render_actions()}
</div>
`);
} else if (content.type == 'post') {
let self = this;
let body;
switch (this.format) {
case 'raw':
body = this.render_raw();
break;
case 'md':
2024-02-24 11:09:34 -05:00
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 = this.render_json(content);
break;
}
let content_warning = html`
2024-02-24 11:09:34 -05:00
<div
class="w3-panel w3-round-xlarge w3-theme-l4 w3"
2024-02-24 11:09:34 -05:00
style="cursor: pointer"
@click=${(x) => this.toggle_expanded(':cw')}
>
<p>${content.contentWarning}</p>
<p class="w3-small">
${this.is_expanded(':cw') ? 'Show less' : 'Show more'}
</p>
2024-02-24 11:09:34 -05:00
</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;
return this.render_frame(html`
${this.render_header()}
<div class="w3-container">${payload}</div>
${this.render_votes()} ${this.render_actions()}
</div>
`);
} else if (content.type === 'issue') {
return this.render_frame(html`
2025-01-04 12:41:04 -05:00
${this.render_header()} ${content.text} ${this.render_votes()}
<footer class="w3-container">
<button class="w3-button w3-theme-d1" @click=${this.react}>
React
</button>
${this.render_children()}
</footer>
`);
} else if (content.type === 'blog') {
let self = this;
2024-02-24 11:09:34 -05:00
tfrpc.rpc.get_blob(content.blog).then(function (data) {
self.blog_data = data;
});
2024-02-24 11:09:34 -05:00
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"
2024-02-24 11:09:34 -05:00
@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;
}
return this.render_frame(html`
${this.render_header()}
<div>${body}</div>
${this.render_mentions()} ${this.render_votes()}
${this.render_actions()}
`);
} else if (content.type === 'pub') {
return this.render_small_frame(
2024-02-24 11:09:34 -05:00
html` <style>
span {
overflow-wrap: anywhere;
}
</style>
2025-01-14 21:50:38 -05:00
<div class="w3-padding">
2024-02-24 11:09:34 -05:00
<div>
🍻
<tf-user
.users=${this.users}
id=${content.address.key}
></tf-user>
</div>
<pre>${content.address.host}:${content.address.port}</pre>
2025-01-14 21:50:38 -05:00
</div>`
2024-02-24 11:09:34 -05:00
);
} else if (content.type === 'channel') {
return this.render_small_frame(html`
<div class="w3-container">
2025-01-05 21:43:26 -05:00
<p>
${content.subscribed ? 'subscribed to' : 'unsubscribed from'}
<a href=${'#' + encodeURIComponent('#' + content.channel)}
>#${content.channel}</a
>
2025-01-05 21:43:26 -05:00
</p>
</div>
`);
2025-01-04 12:41:04 -05:00
} else if (typeof this.message.content == 'string') {
if (this.message?.decrypted) {
if (this.format == 'decrypted') {
return this.render_small_frame(
2025-01-04 12:41:04 -05:00
html`<span class="w3-container">🔓</span> ${this.render_json(
this.message.decrypted
)}`
2024-02-24 11:09:34 -05:00
);
} else {
return this.render_small_frame(
html`<span class="w3-container">🔓</span>
<div class="w3-container">${this.message.decrypted.type}</div>`
2024-02-24 11:09:34 -05:00
);
}
} else {
return this.render_small_frame();
}
} else {
return this.render_small_frame(
html`<div class="w3-container">
<p><b>type</b>: ${content.type}</p>
</div>`
);
}
2025-01-04 12:41:04 -05:00
} else if (typeof this.message.content == 'string') {
return this.render_small_frame();
} else {
return this.render_small_frame(this.render_raw());
}
}
}
2024-02-24 11:09:34 -05:00
customElements.define('tf-message', TfMessageElement);