// This file contains all methods related to rendering UI elements. Rendering
// is done by simple string manipulations & templates. If you need to write
// loops simply write it in code and return strings.
/*function render_timeline_event(damus, view, ev)
{
const root_id = get_thread_root_id(damus, ev.id)
const max_depth = root_id ? get_thread_max_depth(damus, view, root_id) : get_default_max_depth(damus, view)
if (ev.refs && ev.refs.root && view.expanded.has(ev.refs.root))
max_depth = null
return render_event(damus, view, ev, {max_depth})
}
*/
function render_reply_line_top(has_top_line) {
const classes = has_top_line ? "" : "invisible"
return `
`
}
function render_reply_line_bot() {
return ``
}
function render_thread_collapsed(model, ev, opts)
{
if (opts.is_composing)
return ""
return `
Read More
`
}
/*function render_replied_events(damus, view, ev, opts) {
if (!(ev.refs && ev.refs.reply))
return "";
const reply_ev = damus.all_events[ev.refs.reply]
if (!reply_ev)
return "";
opts.replies = opts.replies == null ? 1 : opts.replies + 1
if (!(opts.max_depth == null || opts.replies < opts.max_depth))
return render_thread_collapsed(damus, ev, opts);
opts.is_reply = true
return render_event(damus, view, reply_ev, opts)
}
function render_replying_to_chat(damus, ev) {
const chatroom = (ev.refs.root && damus.chatrooms[ev.refs.root]) || {}
const roomname = chatroom.name || ev.refs.root || "??"
const pks = ev.refs.pubkeys || []
const names = pks.map(pk => render_mentioned_name(pk, damus.profiles[pk])).join(", ")
const to_users = pks.length === 0 ? "" : ` to ${names}`
return `replying${to_users} in ${roomname}
`
}*/
function render_replying_to(model, ev) {
if (!(ev.refs && ev.refs.reply))
return "";
if (ev.kind === KIND_CHATROOM)
return render_replying_to_chat(model, ev);
let pubkeys = ev.refs.pubkeys || []
if (pubkeys.length === 0 && ev.refs.reply) {
const replying_to = model.all_events[ev.refs.reply]
if (!replying_to)
return html`reply to ${ev.refs.reply}
`;
pubkeys = [replying_to.pubkey]
}
const names = pubkeys.map((pk) => {
return render_mentioned_name(pk, model.profiles[pk]);
}).join(", ")
return `
replying to ${names}
`
}
function render_share(damus, view, ev, opts) {
const shared_ev = damus.all_events[ev.refs && ev.refs.root]
// If the shared event hasn't been resolved or leads to a circular event
// kind we will skip out on it.
if (!shared_ev || shared_ev.kind == KIND_SHARE)
return "";
opts.shared = {
pubkey: ev.pubkey,
profile: damus.profiles[ev.pubkey]
}
return render_event(damus, shared_ev, opts)
}
function render_comment_body(model, ev, opts) {
const can_delete = model.pubkey === ev.pubkey;
const bar = !event_can_reply(ev) || opts.nobar ?
"" : render_action_bar(model, ev, {can_delete});
// Only show media for content that is by friends.
const show_media = !opts.is_composing &&
model.contacts.friends.has(ev.pubkey);
return `
${render_replying_to(model, ev)}
${render_shared_by(ev, opts)}
${format_content(ev, show_media)}
${render_reactions(model, ev)}
${bar}`
}
function render_shared_by(ev, opts) {
if (!opts.shared)
return "";
const { profile, pubkey } = opts.shared
return `Shared by ${render_name(pubkey, profile)}
`
}
function render_event(model, ev, opts={}) {
let {
has_bot_line,
has_top_line,
reply_line_bot,
} = opts
if (ev.kind == KIND_SHARE) {
return render_share(model, ev, opts);
}
const thread_root = (ev.refs && ev.refs.root) || ev.id;
const profile = model.profiles[ev.pubkey];
const delta = fmt_since_str(new Date().getTime(), ev.created_at*1000)
const border_bottom = opts.is_composing || has_bot_line ? "" : "bottom-border";
let thread_btn = "";
if (!reply_line_bot) reply_line_bot = '';
return html`
$${render_reply_line_top(has_top_line)}
$${render_pfp(ev.pubkey, profile)}
$${reply_line_bot}
$${render_name(ev.pubkey, profile)}
${delta}
`
}
function render_event_nointeract(model, ev, opts={}) {
const profile = model.profiles[ev.pubkey];
const delta = fmt_since_str(new Date().getTime(), ev.created_at*1000)
return html`
$${render_pfp(ev.pubkey, profile)}
$${render_name(ev.pubkey, profile)}
${delta}
`
}
function render_react_onclick(our_pubkey, reacting_to, emoji, reactions) {
const reaction = reactions[our_pubkey]
if (!reaction) {
return html`onclick="send_reply('${emoji}', '${reacting_to}')"`
} else {
return html`onclick="delete_post('${reaction.id}')"`
}
}
function render_reaction_group(model, emoji, reactions, reacting_to) {
let count = 0;
let str = "";
for (const k in reactions) {
count++;
if (count > 5) {
str = `${count}`;
break;
}
const pubkey = reactions[k].pubkey;
str += render_pfp(pubkey, model.profiles[pubkey], {noclick:true});
}
let onclick = render_react_onclick(model.pubkey,
reacting_to.id, emoji, reactions);
return html`
${emoji}
$${str}
`;
}
function render_action_bar(model, ev, opts={}) {
const { pubkey } = model;
let { can_delete } = opts;
let delete_html = ""
if (can_delete) {
delete_html = html`
`
}
// TODO rewrite all of the toggle heart code. It's mine & I hate it.
const reaction = model_get_reacts_to(model, pubkey, ev.id, R_HEART);
const liked = !!reaction;
const reaction_id = reaction ? reaction.id : "";
return html`
$${delete_html}
`
}
function render_reactions_inner(model, ev) {
const groups = get_reactions(model, ev.id)
let str = ""
for (const emoji of Object.keys(groups)) {
str += render_reaction_group(model, emoji, groups[emoji], ev)
}
return str;
}
function render_reactions(model, ev) {
return html`$${render_reactions_inner(model, ev)}
`
}
// Utility Methods
function render_pubkey(pk) {
return fmt_pubkey(pk);
}
function render_username(pk, profile)
{
return (profile && profile.name) || render_pubkey(pk)
}
function render_mentioned_name(pk, profile) {
return render_name(pk, profile, "");
}
function render_name(pk, profile, prefix="") {
// Beware of whitespace.
return html`${prefix}${fmt_profile_name(profile, fmt_pubkey(pk))}`
}
function render_pfp(pk, profile, opts={}) {
const name = fmt_profile_name(profile, fmt_pubkey(pk));
let str = html`class="pfp clickable" onclick="open_profile('${pk}')"`;
if (opts.noclick)
str = "class='pfp'";
return html`
`
}