Updates
Improved recognization of replying to thread and being able to open it. Rewrote profile storing on the model. Additionally fixed issues where the profile was not getting loaded for referenced pubkeys on an event.
This commit is contained in:
parent
1850ed6aa1
commit
f00f327a3d
11 changed files with 145 additions and 122 deletions
|
@ -239,17 +239,6 @@ button.nav > img.icon {
|
||||||
z-index: var(--zPFP);
|
z-index: var(--zPFP);
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
}
|
}
|
||||||
.pfp.deleted {
|
|
||||||
font-size: 32px;
|
|
||||||
color: var(--clrBg);
|
|
||||||
background: var(--clrText);
|
|
||||||
}
|
|
||||||
.pfp.deleted > i {
|
|
||||||
top: 40%;
|
|
||||||
left: 50%;
|
|
||||||
position: relative;
|
|
||||||
transform: translateX(-50%) translateY(-50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.event-content {
|
.event-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
@ -260,13 +249,8 @@ button.nav > img.icon {
|
||||||
}
|
}
|
||||||
.event-content > .info button[role="view-event"] {
|
.event-content > .info button[role="view-event"] {
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
opacity: 0;
|
|
||||||
transition: opacity 0.2s linear;
|
|
||||||
}
|
}
|
||||||
.event:hover .event-content > .info button[role="view-event"] {
|
.username, .thread-id {
|
||||||
opacity: 0.6;
|
|
||||||
}
|
|
||||||
.username {
|
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
font-size: var(--fsReduced);
|
font-size: var(--fsReduced);
|
||||||
}
|
}
|
||||||
|
@ -363,19 +347,6 @@ details.cw summary {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Thread Expansion */
|
|
||||||
.thread-collapsed {
|
|
||||||
padding: 7px;
|
|
||||||
}
|
|
||||||
.thread-summary {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.thread-summary img.icon {
|
|
||||||
opacity: 0.6;
|
|
||||||
position: relative;
|
|
||||||
top: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Modal */
|
/* Modal */
|
||||||
.modal {
|
.modal {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
|
1
icon/open-thread-here.svg
Normal file
1
icon/open-thread-here.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M72 48C85.25 48 96 58.75 96 72V120C96 133.3 85.25 144 72 144V232H128C128 218.7 138.7 208 152 208H200C213.3 208 224 218.7 224 232V280C224 293.3 213.3 304 200 304H152C138.7 304 128 293.3 128 280H72V384C72 388.4 75.58 392 80 392H128C128 378.7 138.7 368 152 368H200C213.3 368 224 378.7 224 392V440C224 453.3 213.3 464 200 464H152C138.7 464 128 453.3 128 440H80C49.07 440 24 414.9 24 384V144C10.75 144 0 133.3 0 120V72C0 58.75 10.75 48 24 48H72zM160 96C160 78.33 174.3 64 192 64H480C497.7 64 512 78.33 512 96C512 113.7 497.7 128 480 128H192C174.3 128 160 113.7 160 96zM288 256C288 238.3 302.3 224 320 224H480C497.7 224 512 238.3 512 256C512 273.7 497.7 288 480 288H320C302.3 288 288 273.7 288 256zM288 416C288 398.3 302.3 384 320 384H480C497.7 384 512 398.3 512 416C512 433.7 497.7 448 480 448H320C302.3 448 288 433.7 288 416z"/></svg>
|
After Width: | Height: | Size: 1 KiB |
|
@ -1 +1 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M392 320c-13.25 0-24 10.75-24 24v112c0 4.406-3.594 8-8 8h-304c-4.406 0-8-3.594-8-8v-304c0-4.406 3.594-8 8-8h112C181.3 144 192 133.3 192 120S181.3 96 168 96h-112C25.13 96 0 121.1 0 152v304C0 486.9 25.13 512 56 512h304c30.88 0 56-25.12 56-56v-112C416 330.8 405.3 320 392 320zM488 0H320c-13.25 0-24 10.75-24 24S306.8 48 320 48h110.1L183 295c-9.375 9.375-9.375 24.56 0 33.94C187.7 333.7 193.8 336 200 336s12.28-2.344 16.97-7.031L464 81.94V192c0 13.25 10.75 24 24 24S512 205.3 512 192V24C512 10.75 501.3 0 488 0z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M112 96C112 122.5 90.51 144 64 144C37.49 144 16 122.5 16 96C16 69.49 37.49 48 64 48C90.51 48 112 69.49 112 96zM16 256C16 229.5 37.49 208 64 208C90.51 208 112 229.5 112 256C112 282.5 90.51 304 64 304C37.49 304 16 282.5 16 256zM16 416C16 389.5 37.49 368 64 368C90.51 368 112 389.5 112 416C112 442.5 90.51 464 64 464C37.49 464 16 442.5 16 416zM210.7 48H424C437.3 48 448 58.75 448 72V120C448 133.3 437.3 144 424 144H210.7C203.7 144 197 141.8 191.5 137.6L153.1 108.8C144.5 102.4 144.5 89.6 153.1 83.2L191.5 54.4C197 50.25 203.7 48 210.7 48V48zM153.1 243.2L191.5 214.4C197 210.2 203.7 208 210.7 208H488C501.3 208 512 218.7 512 232V280C512 293.3 501.3 304 488 304H210.7C203.7 304 197 301.8 191.5 297.6L153.1 268.8C144.5 262.4 144.5 249.6 153.1 243.2V243.2zM153.1 403.2L191.5 374.4C197 370.2 203.7 368 210.7 368H424C437.3 368 448 378.7 448 392V440C448 453.3 437.3 464 424 464H210.7C203.7 464 197 461.8 191.5 457.6L153.1 428.8C144.5 422.4 144.5 409.6 153.1 403.2z"/></svg>
|
Before Width: | Height: | Size: 755 B After Width: | Height: | Size: 1.2 KiB |
41
js/core.js
41
js/core.js
|
@ -38,23 +38,22 @@ async function sign_id(privkey, id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function broadcast_related_events(ev) {
|
async function broadcast_related_events(ev) {
|
||||||
ev.tags
|
ev.tags.reduce((evs, tag) => {
|
||||||
.reduce((evs, tag) => {
|
// cap it at something sane
|
||||||
// cap it at something sane
|
if (evs.length >= 5)
|
||||||
if (evs.length >= 5)
|
|
||||||
return evs
|
|
||||||
const ev = get_tag_event(tag)
|
|
||||||
if (!ev)
|
|
||||||
return evs
|
|
||||||
return evs
|
return evs
|
||||||
}, [])
|
const ev = get_tag_event(tag)
|
||||||
.forEach((ev, i) => {
|
if (!ev)
|
||||||
// so we don't get rate limited
|
return evs
|
||||||
setTimeout(() => {
|
return evs
|
||||||
log_debug("broadcasting related event", ev)
|
}, [])
|
||||||
broadcast_event(ev)
|
.forEach((ev, i) => {
|
||||||
}, (i+1)*1200)
|
// so we don't get rate limited
|
||||||
})
|
setTimeout(() => {
|
||||||
|
log_debug("broadcasting related event", ev)
|
||||||
|
broadcast_event(ev)
|
||||||
|
}, (i+1)*1200)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function broadcast_event(ev) {
|
function broadcast_event(ev) {
|
||||||
|
@ -278,12 +277,16 @@ function gather_reply_tags(pubkey, from) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function get_tag_event(tag) {
|
function get_tag_event(tag) {
|
||||||
|
const model = DAMUS;
|
||||||
if (tag.length < 2)
|
if (tag.length < 2)
|
||||||
return null
|
return null
|
||||||
if (tag[0] === "e")
|
if (tag[0] === "e")
|
||||||
return DAMUS.all_events[tag[1]]
|
return model.all_events[tag[1]]
|
||||||
if (tag[0] === "p")
|
if (tag[0] === "p") {
|
||||||
return DAMUS.all_events[DAMUS.profile_events[tag[1]]]
|
let profile = model_get_profile(model, tag[1]);
|
||||||
|
if (profile.evid)
|
||||||
|
return model.all_events[profile.evid];
|
||||||
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
19
js/event.js
19
js/event.js
|
@ -15,6 +15,25 @@ function event_refs_pubkey(ev, pubkey) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function event_contains_pubkey(ev, pubkey) {
|
||||||
|
if (ev.pubkey == pubkey)
|
||||||
|
return true;
|
||||||
|
for (const tag of ev.tags) {
|
||||||
|
if (tag.length >= 2 && tag[0] == "p" && tag[1] == pubkey)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function event_get_pubkeys(ev) {
|
||||||
|
const keys = [ev.pubkey];
|
||||||
|
for (const tag of ev.tags) {
|
||||||
|
if (tag.length >= 2 && tag[0] == "p")
|
||||||
|
keys.push(tag[1]);
|
||||||
|
}
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
function event_calculate_pow(ev) {
|
function event_calculate_pow(ev) {
|
||||||
const id_bits = leading_zero_bits(ev.id)
|
const id_bits = leading_zero_bits(ev.id)
|
||||||
for (const tag of ev.tags) {
|
for (const tag of ev.tags) {
|
||||||
|
|
|
@ -242,7 +242,7 @@ function fetch_profiles(pool, relay, pubkeys) {
|
||||||
function fetch_profile_info(pubkey, pool, relay) {
|
function fetch_profile_info(pubkey, pool, relay) {
|
||||||
const sid = `${SID_META}:${pubkey}`;
|
const sid = `${SID_META}:${pubkey}`;
|
||||||
pool.subscribe(sid, [{
|
pool.subscribe(sid, [{
|
||||||
kinds: [KIND_METADATA, KIND_CONTACT],
|
kinds: [KIND_METADATA, KIND_CONTACT, KIND_RELAY],
|
||||||
authors: [pubkey],
|
authors: [pubkey],
|
||||||
limit: 1,
|
limit: 1,
|
||||||
}], relay);
|
}], relay);
|
||||||
|
|
52
js/model.js
52
js/model.js
|
@ -39,9 +39,12 @@ function model_process_event(model, relay, ev) {
|
||||||
if (!relay)
|
if (!relay)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Request the profile if we have never seen it
|
// Request new profiles for unseen pubkeys of the event
|
||||||
if (!model.profile_events[ev.pubkey])
|
event_get_pubkeys(ev).forEach((pubkey) => {
|
||||||
model_que_profile(model, relay, ev.pubkey);
|
if (!model_has_profile(model, pubkey)) {
|
||||||
|
model_que_profile(model, relay, pubkey);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// TODO fetch unknown referenced events & pubkeys from this event
|
// TODO fetch unknown referenced events & pubkeys from this event
|
||||||
// TODO notify user of new events aimed at them!
|
// TODO notify user of new events aimed at them!
|
||||||
|
@ -108,22 +111,40 @@ function model_fetch_next_profile(model, relay) {
|
||||||
* in the event.
|
* in the event.
|
||||||
*/
|
*/
|
||||||
function model_process_event_metadata(model, ev, update_view) {
|
function model_process_event_metadata(model, ev, update_view) {
|
||||||
const prev_ev = model.all_events[model.profile_events[ev.pubkey]]
|
const profile = model_get_profile(model, ev.pubkey);
|
||||||
if (prev_ev && prev_ev.created_at > ev.created_at)
|
const evs = model.all_events;
|
||||||
return
|
if (profile.evid &&
|
||||||
model.profile_events[ev.pubkey] = ev.id
|
evs[ev.id].created_at < evs[profile.evid].created_at)
|
||||||
model.profiles[ev.pubkey] = safe_parse_json(ev.content, "profile contents")
|
return;
|
||||||
if (update_view)
|
profile.evid = ev.id;
|
||||||
view_timeline_update_profiles(model, ev);
|
profile.data = safe_parse_json(ev.content, "profile contents");
|
||||||
|
if (update_view)
|
||||||
|
view_timeline_update_profiles(model, profile.pubkey);
|
||||||
// If it's my pubkey let's redraw my pfp that is not located in the view
|
// If it's my pubkey let's redraw my pfp that is not located in the view
|
||||||
// This has to happen regardless of update_view because of the it's not
|
// This has to happen regardless of update_view because of the it's not
|
||||||
// related to events
|
// related to events
|
||||||
if (ev.pubkey == model.pubkey) {
|
if (profile.pubkey == model.pubkey) {
|
||||||
redraw_my_pfp(model);
|
redraw_my_pfp(model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function model_has_profile(model, pk) {
|
||||||
|
return !!model_get_profile(model, pk).evid;
|
||||||
|
}
|
||||||
|
|
||||||
|
function model_get_profile(model, pubkey) {
|
||||||
|
if (model.profiles.has(pubkey)) {
|
||||||
|
return model.profiles.get(pubkey);
|
||||||
|
}
|
||||||
|
model.profiles.set(pubkey, {
|
||||||
|
pubkey: pubkey,
|
||||||
|
evid: "",
|
||||||
|
relays: [],
|
||||||
|
data: {},
|
||||||
|
});
|
||||||
|
return model.profiles.get(pubkey);
|
||||||
|
}
|
||||||
|
|
||||||
function model_process_event_following(model, ev, update_view) {
|
function model_process_event_following(model, ev, update_view) {
|
||||||
contacts_process_event(model.contacts, model.pubkey, ev)
|
contacts_process_event(model.contacts, model.pubkey, ev)
|
||||||
// TODO support loading relays that are stored on the initial relay
|
// TODO support loading relays that are stored on the initial relay
|
||||||
|
@ -241,10 +262,6 @@ function model_is_event_deleted(model, evid) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
function model_has_profile(model, pk) {
|
|
||||||
return pk in model.profiles
|
|
||||||
}
|
|
||||||
|
|
||||||
function model_has_event(model, evid) {
|
function model_has_event(model, evid) {
|
||||||
return evid in model.all_events
|
return evid in model.all_events
|
||||||
}
|
}
|
||||||
|
@ -399,8 +416,7 @@ function new_model() {
|
||||||
deletions: {},
|
deletions: {},
|
||||||
deleted: {},
|
deleted: {},
|
||||||
pow: 0, // pow difficulty target
|
pow: 0, // pow difficulty target
|
||||||
profiles: {}, // pubkey => profile data
|
profiles: new Map(), // pubkey => profile data
|
||||||
profile_events: {}, // pubkey => event id - use with all_events
|
|
||||||
contacts: {
|
contacts: {
|
||||||
event: null,
|
event: null,
|
||||||
friends: new Set(),
|
friends: new Set(),
|
||||||
|
|
|
@ -5,17 +5,23 @@
|
||||||
function render_replying_to(model, ev) {
|
function render_replying_to(model, ev) {
|
||||||
if (!(ev.refs && ev.refs.reply))
|
if (!(ev.refs && ev.refs.reply))
|
||||||
return "";
|
return "";
|
||||||
if (ev.kind === KIND_CHATROOM)
|
|
||||||
return render_replying_to_chat(model, ev);
|
|
||||||
let pubkeys = ev.refs.pubkeys || []
|
let pubkeys = ev.refs.pubkeys || []
|
||||||
if (pubkeys.length === 0 && ev.refs.reply) {
|
if (pubkeys.length === 0 && ev.refs.reply) {
|
||||||
const replying_to = model.all_events[ev.refs.reply]
|
const replying_to = model.all_events[ev.refs.reply]
|
||||||
if (!replying_to)
|
// If there is no profile being replied to, it is simply a reply to an
|
||||||
return html`<div class="replying-to small-txt">reply to ${ev.refs.reply}</div>`;
|
// event itself, thus render it differently.
|
||||||
pubkeys = [replying_to.pubkey]
|
if (!replying_to) {
|
||||||
|
return html`<span class="replying-to small-txt">
|
||||||
|
replying in thread
|
||||||
|
<span class="thread-id clickable"
|
||||||
|
onclick="open_thread('${ev.refs.reply}')">
|
||||||
|
${fmt_pubkey(ev.refs.reply)}</span></span>`;
|
||||||
|
} else {
|
||||||
|
pubkeys = [replying_to.pubkey];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const names = pubkeys.map((pk) => {
|
const names = pubkeys.map((pk) => {
|
||||||
return render_mentioned_name(pk, model.profiles[pk]);
|
return render_name(pk, model_get_profile(model, pk).data);
|
||||||
}).join(", ")
|
}).join(", ")
|
||||||
return `
|
return `
|
||||||
<span class="replying-to small-txt">
|
<span class="replying-to small-txt">
|
||||||
|
@ -24,19 +30,19 @@ function render_replying_to(model, ev) {
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
function render_share(damus, ev, opts) {
|
function render_share(model, ev, opts) {
|
||||||
const shared_ev = damus.all_events[ev.refs && ev.refs.root]
|
const shared_ev = model.all_events[ev.refs && ev.refs.root]
|
||||||
// If the shared event hasn't been resolved or leads to a circular event
|
// If the shared event hasn't been resolved or leads to a circular event
|
||||||
// kind we will skip out on it.
|
// kind we will skip out on it.
|
||||||
if (!shared_ev || shared_ev.kind == KIND_SHARE)
|
if (!shared_ev || shared_ev.kind == KIND_SHARE)
|
||||||
return "";
|
return "";
|
||||||
opts.shared = {
|
opts.shared = {
|
||||||
pubkey: ev.pubkey,
|
pubkey: ev.pubkey,
|
||||||
profile: damus.profiles[ev.pubkey],
|
profile: model_get_profile(model, ev.pubkey),
|
||||||
share_time: ev.created_at,
|
share_time: ev.created_at,
|
||||||
share_evid: ev.id,
|
share_evid: ev.id,
|
||||||
}
|
}
|
||||||
return render_event(damus, shared_ev, opts)
|
return render_event(model, shared_ev, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
function render_shared_by(ev, opts) {
|
function render_shared_by(ev, opts) {
|
||||||
|
@ -52,22 +58,18 @@ function render_event(model, ev, opts={}) {
|
||||||
return render_share(model, ev, opts);
|
return render_share(model, ev, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
const thread_root = (ev.refs && ev.refs.root) || ev.id;
|
const profile = model_get_profile(model, ev.pubkey);
|
||||||
const profile = model.profiles[ev.pubkey];
|
|
||||||
const delta = fmt_since_str(new Date().getTime(), ev.created_at*1000)
|
const delta = fmt_since_str(new Date().getTime(), ev.created_at*1000)
|
||||||
const border_bottom = opts.is_composing ? "" : "bottom-border";
|
const border_bottom = opts.is_composing ? "" : "bottom-border";
|
||||||
let thread_btn = "";
|
let thread_btn = "";
|
||||||
return html`<div id="ev${ev.id}" class="event ${border_bottom}">
|
return html`<div id="ev${ev.id}" class="event ${border_bottom}">
|
||||||
<div class="userpic">
|
<div class="userpic">
|
||||||
$${render_pfp(ev.pubkey, profile)}
|
$${render_pfp(ev.pubkey, profile.data)}
|
||||||
</div>
|
</div>
|
||||||
<div class="event-content">
|
<div class="event-content">
|
||||||
<div class="info">
|
<div class="info">
|
||||||
$${render_name(ev.pubkey, profile)}
|
$${render_name(ev.pubkey, profile.data)}
|
||||||
<span class="timestamp" data-timestamp="${ev.created_at}">${delta}</span>
|
<span class="timestamp" data-timestamp="${ev.created_at}">${delta}</span>
|
||||||
<button class="icon" title="View Thread" role="view-event" onclick="open_thread('${thread_root}')">
|
|
||||||
<img class="icon svg small" src="icon/open-thread.svg"/>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="comment">
|
<div class="comment">
|
||||||
$${render_event_body(model, ev, opts)}
|
$${render_event_body(model, ev, opts)}
|
||||||
|
@ -77,15 +79,15 @@ function render_event(model, ev, opts={}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function render_event_nointeract(model, ev, opts={}) {
|
function render_event_nointeract(model, ev, opts={}) {
|
||||||
const profile = model.profiles[ev.pubkey];
|
const profile = model_get_profiles(model, ev.pubkey);
|
||||||
const delta = fmt_since_str(new Date().getTime(), ev.created_at*1000)
|
const delta = fmt_since_str(new Date().getTime(), ev.created_at*1000)
|
||||||
return html`<div class="event border-bottom">
|
return html`<div class="event border-bottom">
|
||||||
<div class="userpic">
|
<div class="userpic">
|
||||||
$${render_pfp(ev.pubkey, profile)}
|
$${render_pfp(ev.pubkey, profile.data)}
|
||||||
</div>
|
</div>
|
||||||
<div class="event-content">
|
<div class="event-content">
|
||||||
<div class="info">
|
<div class="info">
|
||||||
$${render_name(ev.pubkey, profile)}
|
$${render_name(ev.pubkey, profile.data)}
|
||||||
<span class="timestamp" data-timestamp="${ev.created_at}">${delta}</span>
|
<span class="timestamp" data-timestamp="${ev.created_at}">${delta}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="comment">
|
<div class="comment">
|
||||||
|
@ -132,7 +134,7 @@ function render_reaction_group(model, emoji, reactions, reacting_to) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
const pubkey = reactions[k].pubkey;
|
const pubkey = reactions[k].pubkey;
|
||||||
str += render_pfp(pubkey, model.profiles[pubkey], {noclick:true});
|
str += render_pfp(pubkey, model_get_profile(model, pubkey).data, {noclick:true});
|
||||||
}
|
}
|
||||||
let onclick = render_react_onclick(model.pubkey,
|
let onclick = render_react_onclick(model.pubkey,
|
||||||
reacting_to.id, emoji, reactions);
|
reacting_to.id, emoji, reactions);
|
||||||
|
@ -149,6 +151,7 @@ function render_action_bar(model, ev, opts={}) {
|
||||||
const { pubkey } = model;
|
const { pubkey } = model;
|
||||||
let { can_delete, shared } = opts;
|
let { can_delete, shared } = opts;
|
||||||
// TODO rewrite all of the toggle heart code. It's mine & I hate it.
|
// TODO rewrite all of the toggle heart code. It's mine & I hate it.
|
||||||
|
const thread_root = (ev.refs && ev.refs.root) || ev.id;
|
||||||
const reaction = model_get_reacts_to(model, pubkey, ev.id, R_HEART);
|
const reaction = model_get_reacts_to(model, pubkey, ev.id, R_HEART);
|
||||||
const liked = !!reaction;
|
const liked = !!reaction;
|
||||||
const reaction_id = reaction ? reaction.id : "";
|
const reaction_id = reaction ? reaction.id : "";
|
||||||
|
@ -181,7 +184,15 @@ function render_action_bar(model, ev, opts={}) {
|
||||||
<img class="icon svg small" src="icon/event-delete.svg"/>
|
<img class="icon svg small" src="icon/event-delete.svg"/>
|
||||||
</button>`
|
</button>`
|
||||||
}
|
}
|
||||||
return str + "</div>";
|
return str + `
|
||||||
|
<button class="icon" title="View Thread" role="view-event"
|
||||||
|
onclick="open_thread('${thread_root}')">
|
||||||
|
<img class="icon svg small" src="icon/open-thread.svg"/>
|
||||||
|
</button>
|
||||||
|
<button class="icon" title="View Replies" role="view-event"
|
||||||
|
onclick="open_thread('${ev.id}')">
|
||||||
|
<img class="icon svg small" src="icon/open-thread-here.svg"/>
|
||||||
|
</button></div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function render_reactions_inner(model, ev) {
|
function render_reactions_inner(model, ev) {
|
||||||
|
|
|
@ -234,26 +234,29 @@ function view_render_event(model, ev, force=false) {
|
||||||
return el;
|
return el;
|
||||||
}
|
}
|
||||||
|
|
||||||
function view_timeline_update_profiles(model, ev) {
|
function view_timeline_update_profiles(model, pubkey) {
|
||||||
let xs, html;
|
let xs, html;
|
||||||
const el = view_get_timeline_el();
|
const el = view_get_timeline_el();
|
||||||
const pk = ev.pubkey;
|
const p = model_get_profile(model, pubkey);
|
||||||
const p = model.profiles[pk];
|
const name = fmt_profile_name(p.data, fmt_pubkey(pubkey));
|
||||||
const name = fmt_profile_name(p, fmt_pubkey(pk));
|
const pic = get_picture(pubkey, p.data)
|
||||||
const pic = get_picture(pk, p)
|
|
||||||
for (const evid in model.elements) {
|
for (const evid in model.elements) {
|
||||||
if (model.all_events[evid].pubkey != pk)
|
if (!event_contains_pubkey(model.all_events[evid], pubkey))
|
||||||
continue;
|
continue;
|
||||||
const el = model.elements[evid];
|
const el = model.elements[evid];
|
||||||
find_node(`.username[data-pubkey='${pk}']`, el).innerText = name;
|
let xs;
|
||||||
// TODO Sometimes this fails and I don't know why
|
xs = find_nodes(`.username[data-pubkey='${pubkey}']`, el)
|
||||||
let img = find_node(`img.pfp[data-pubkey='${pk}']`, el);
|
xs.forEach((el)=> {
|
||||||
if (img)
|
el.innerText = name;
|
||||||
img.src = pic;
|
});
|
||||||
|
xs = find_nodes(`img[data-pubkey='${pubkey}']`, el)
|
||||||
|
xs.forEach((el)=> {
|
||||||
|
el.src = pic;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
// Update the profile view if it's active
|
// Update the profile view if it's active
|
||||||
if (el.dataset.mode == VM_USER && el.dataset.pubkey == pk) {
|
if (el.dataset.mode == VM_USER && el.dataset.pubkey == pubkey) {
|
||||||
view_update_profile(model, pk);
|
view_update_profile(model, pubkey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,8 @@ function update_notification_markers(active) {
|
||||||
*/
|
*/
|
||||||
function show_profile(pk) {
|
function show_profile(pk) {
|
||||||
switch_view("profile");
|
switch_view("profile");
|
||||||
const profile = DAMUS.profiles[pk];
|
const model = DAMUS;
|
||||||
|
const profile = model_get_profile(model, pk).data;
|
||||||
const el = find_node("#profile-view");
|
const el = find_node("#profile-view");
|
||||||
// TODO show loading indicator then render
|
// TODO show loading indicator then render
|
||||||
|
|
||||||
|
@ -85,7 +86,7 @@ function show_profile(pk) {
|
||||||
btn_follow.dataset.pk = pk;
|
btn_follow.dataset.pk = pk;
|
||||||
// TODO check follow status
|
// TODO check follow status
|
||||||
btn_follow.innerText = 1 == 1 ? "Follow" : "Unfollow";
|
btn_follow.innerText = 1 == 1 ? "Follow" : "Unfollow";
|
||||||
btn_follow.classList.toggle("hide", pk == DAMUS.pubkey);
|
btn_follow.classList.toggle("hide", pk == model.pubkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* newlines_to_br takes a string and converts all newlines to HTML 'br' tags.
|
/* newlines_to_br takes a string and converts all newlines to HTML 'br' tags.
|
||||||
|
@ -215,7 +216,7 @@ function reply_all(evid) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function redraw_my_pfp(model) {
|
function redraw_my_pfp(model) {
|
||||||
const p = model.profiles[model.pubkey]
|
const p = model_get_profile(model, model.pubkey).data;
|
||||||
const html = render_pfp(model.pubkey, p || {});
|
const html = render_pfp(model.pubkey, p || {});
|
||||||
const el = document.querySelector(".my-userpic");
|
const el = document.querySelector(".my-userpic");
|
||||||
el.innerHTML = html;
|
el.innerHTML = html;
|
||||||
|
@ -306,23 +307,23 @@ function close_modal(el) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function view_update_profile(model, pubkey) {
|
function view_update_profile(model, pubkey) {
|
||||||
const profile = model.profiles[pubkey] || {};
|
const profile = model_get_profile(pubkey);
|
||||||
const el = find_node("[role='profile-info']");
|
const el = find_node("[role='profile-info']");
|
||||||
|
|
||||||
const name = fmt_profile_name(profile, fmt_pubkey(pubkey));
|
const name = fmt_profile_name(profile.data, fmt_pubkey(pubkey));
|
||||||
find_node("#view header > label").innerText = name;
|
find_node("#view header > label").innerText = name;
|
||||||
find_node("[role='profile-image']", el).src = get_picture(pubkey, profile);
|
find_node("[role='profile-image']", el).src = get_picture(pubkey, profile.data);
|
||||||
find_nodes("[role='profile-name']", el).forEach(el => {
|
find_nodes("[role='profile-name']", el).forEach(el => {
|
||||||
el.innerText = name;
|
el.innerText = name;
|
||||||
});
|
});
|
||||||
|
|
||||||
const el_nip5 = find_node("[role='profile-nip5']", el)
|
const el_nip5 = find_node("[role='profile-nip5']", el)
|
||||||
el_nip5.innerText = profile.nip05;
|
el_nip5.innerText = profile.data.nip05;
|
||||||
el_nip5.classList.toggle("hide", !profile.nip05);
|
el_nip5.classList.toggle("hide", !profile.data.nip05);
|
||||||
|
|
||||||
const el_desc = find_node("[role='profile-desc']", el)
|
const el_desc = find_node("[role='profile-desc']", el)
|
||||||
el_desc.innerHTML = newlines_to_br(linkify(profile.about));
|
el_desc.innerHTML = newlines_to_br(linkify(profile.data.about));
|
||||||
el_desc.classList.toggle("hide", !profile.about);
|
el_desc.classList.toggle("hide", !profile.data.about);
|
||||||
|
|
||||||
find_node("button[role='copy-pk']", el).dataset.pk = pubkey;
|
find_node("button[role='copy-pk']", el).dataset.pk = pubkey;
|
||||||
find_node("button[role='edit-profile']", el)
|
find_node("button[role='edit-profile']", el)
|
||||||
|
@ -331,18 +332,18 @@ function view_update_profile(model, pubkey) {
|
||||||
const btn_follow = find_node("button[role='follow-user']", el)
|
const btn_follow = find_node("button[role='follow-user']", el)
|
||||||
btn_follow.dataset.pk = pubkey;
|
btn_follow.dataset.pk = pubkey;
|
||||||
// TODO check follow status
|
// TODO check follow status
|
||||||
btn_follow.innerText = contact_is_friend(DAMUS.contacts, pubkey) ? "Unfollow" : "Follow";
|
btn_follow.innerText = contact_is_friend(model.contacts, pubkey) ? "Unfollow" : "Follow";
|
||||||
btn_follow.classList.toggle("hide", pubkey == DAMUS.pubkey);
|
btn_follow.classList.toggle("hide", pubkey == model.pubkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
const PROFILE_FIELDS = ['name', 'picture', 'nip05', 'about'];
|
const PROFILE_FIELDS = ['name', 'picture', 'nip05', 'about'];
|
||||||
|
|
||||||
function show_profile_editor() {
|
function show_profile_editor() {
|
||||||
const p = DAMUS.profiles[DAMUS.pubkey];
|
const p = model_get_profile(DAMUS, DAMUS.pubkey);
|
||||||
const el = find_node("#profile-editor");
|
const el = find_node("#profile-editor");
|
||||||
el.classList.remove("closed");
|
el.classList.remove("closed");
|
||||||
for (const key of PROFILE_FIELDS) {
|
for (const key of PROFILE_FIELDS) {
|
||||||
find_node(`[name='${key}']`, el).value = p[key];
|
find_node(`[name='${key}']`, el).value = p.data[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -103,16 +103,14 @@ function get_since_time(created_at) {
|
||||||
* parent element to search on.
|
* parent element to search on.
|
||||||
*/
|
*/
|
||||||
function find_node(selector, parentEl) {
|
function find_node(selector, parentEl) {
|
||||||
const el = parentEl ? parentEl : document;
|
return (parentEl || document).querySelector(selector)
|
||||||
return el.querySelector(selector)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* find_nodes is a short name for document.querySelectorAll, it also takes in a
|
/* find_nodes is a short name for document.querySelectorAll, it also takes in a
|
||||||
* parent element to search on.
|
* parent element to search on.
|
||||||
*/
|
*/
|
||||||
function find_nodes(selector, parentEl) {
|
function find_nodes(selector, parentEl) {
|
||||||
const el = parentEl ? parentEl : document;
|
return (parentEl || document).querySelectorAll(selector);
|
||||||
return el.querySelectorAll(selector)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* uuidv4 returns a new uuid v4
|
/* uuidv4 returns a new uuid v4
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue