diff --git a/css/styles.css b/css/styles.css index 56d0aa0..25172ee 100644 --- a/css/styles.css +++ b/css/styles.css @@ -239,17 +239,6 @@ button.nav > img.icon { z-index: var(--zPFP); 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 { flex: 1; @@ -260,13 +249,8 @@ button.nav > img.icon { } .event-content > .info button[role="view-event"] { margin-left: 10px; - opacity: 0; - transition: opacity 0.2s linear; } -.event:hover .event-content > .info button[role="view-event"] { - opacity: 0.6; -} -.username { +.username, .thread-id { font-weight: 800; font-size: var(--fsReduced); } @@ -363,19 +347,6 @@ details.cw summary { 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 { position: fixed; diff --git a/icon/open-thread-here.svg b/icon/open-thread-here.svg new file mode 100644 index 0000000..6ffa7d1 --- /dev/null +++ b/icon/open-thread-here.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icon/open-thread.svg b/icon/open-thread.svg index 55dfa1c..c40bb2f 100644 --- a/icon/open-thread.svg +++ b/icon/open-thread.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/js/core.js b/js/core.js index 4d71ae6..a9bd0cd 100644 --- a/js/core.js +++ b/js/core.js @@ -38,23 +38,22 @@ async function sign_id(privkey, id) { } async function broadcast_related_events(ev) { - ev.tags - .reduce((evs, tag) => { - // cap it at something sane - if (evs.length >= 5) - return evs - const ev = get_tag_event(tag) - if (!ev) - return evs + ev.tags.reduce((evs, tag) => { + // cap it at something sane + if (evs.length >= 5) return evs - }, []) - .forEach((ev, i) => { - // so we don't get rate limited - setTimeout(() => { - log_debug("broadcasting related event", ev) - broadcast_event(ev) - }, (i+1)*1200) - }) + const ev = get_tag_event(tag) + if (!ev) + return evs + return evs + }, []) + .forEach((ev, i) => { + // so we don't get rate limited + setTimeout(() => { + log_debug("broadcasting related event", ev) + broadcast_event(ev) + }, (i+1)*1200) + }); } function broadcast_event(ev) { @@ -278,12 +277,16 @@ function gather_reply_tags(pubkey, from) { } function get_tag_event(tag) { + const model = DAMUS; if (tag.length < 2) return null if (tag[0] === "e") - return DAMUS.all_events[tag[1]] - if (tag[0] === "p") - return DAMUS.all_events[DAMUS.profile_events[tag[1]]] + return model.all_events[tag[1]] + if (tag[0] === "p") { + let profile = model_get_profile(model, tag[1]); + if (profile.evid) + return model.all_events[profile.evid]; + } return null } diff --git a/js/event.js b/js/event.js index 864d447..484c963 100644 --- a/js/event.js +++ b/js/event.js @@ -15,6 +15,25 @@ function event_refs_pubkey(ev, pubkey) { 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) { const id_bits = leading_zero_bits(ev.id) for (const tag of ev.tags) { diff --git a/js/main.js b/js/main.js index 2f070be..f0383e1 100644 --- a/js/main.js +++ b/js/main.js @@ -242,7 +242,7 @@ function fetch_profiles(pool, relay, pubkeys) { function fetch_profile_info(pubkey, pool, relay) { const sid = `${SID_META}:${pubkey}`; pool.subscribe(sid, [{ - kinds: [KIND_METADATA, KIND_CONTACT], + kinds: [KIND_METADATA, KIND_CONTACT, KIND_RELAY], authors: [pubkey], limit: 1, }], relay); diff --git a/js/model.js b/js/model.js index 759198d..b0626d8 100644 --- a/js/model.js +++ b/js/model.js @@ -39,9 +39,12 @@ function model_process_event(model, relay, ev) { if (!relay) return; - // Request the profile if we have never seen it - if (!model.profile_events[ev.pubkey]) - model_que_profile(model, relay, ev.pubkey); + // Request new profiles for unseen pubkeys of the event + event_get_pubkeys(ev).forEach((pubkey) => { + if (!model_has_profile(model, pubkey)) { + model_que_profile(model, relay, pubkey); + } + }); // TODO fetch unknown referenced events & pubkeys from this event // TODO notify user of new events aimed at them! @@ -108,22 +111,40 @@ function model_fetch_next_profile(model, relay) { * in the event. */ function model_process_event_metadata(model, ev, update_view) { - const prev_ev = model.all_events[model.profile_events[ev.pubkey]] - if (prev_ev && prev_ev.created_at > ev.created_at) - return - model.profile_events[ev.pubkey] = ev.id - model.profiles[ev.pubkey] = safe_parse_json(ev.content, "profile contents") - if (update_view) - view_timeline_update_profiles(model, ev); - + const profile = model_get_profile(model, ev.pubkey); + const evs = model.all_events; + if (profile.evid && + evs[ev.id].created_at < evs[profile.evid].created_at) + return; + profile.evid = ev.id; + 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 // This has to happen regardless of update_view because of the it's not // related to events - if (ev.pubkey == model.pubkey) { + if (profile.pubkey == model.pubkey) { 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) { contacts_process_event(model.contacts, model.pubkey, ev) // TODO support loading relays that are stored on the initial relay @@ -241,10 +262,6 @@ function model_is_event_deleted(model, evid) { return false } -function model_has_profile(model, pk) { - return pk in model.profiles -} - function model_has_event(model, evid) { return evid in model.all_events } @@ -399,8 +416,7 @@ function new_model() { deletions: {}, deleted: {}, pow: 0, // pow difficulty target - profiles: {}, // pubkey => profile data - profile_events: {}, // pubkey => event id - use with all_events + profiles: new Map(), // pubkey => profile data contacts: { event: null, friends: new Set(), diff --git a/js/ui/render.js b/js/ui/render.js index 6805788..4a80e8c 100644 --- a/js/ui/render.js +++ b/js/ui/render.js @@ -5,17 +5,23 @@ 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`
`; - pubkeys = [replying_to.pubkey] + // If there is no profile being replied to, it is simply a reply to an + // event itself, thus render it differently. + if (!replying_to) { + return html` `; + } else { + pubkeys = [replying_to.pubkey]; + } } const names = pubkeys.map((pk) => { - return render_mentioned_name(pk, model.profiles[pk]); + return render_name(pk, model_get_profile(model, pk).data); }).join(", ") return `