From f417732942f9693086d68c98b4fb7ac71ef67379 Mon Sep 17 00:00:00 2001 From: Thomas Mathews Date: Fri, 16 Dec 2022 15:15:33 -0800 Subject: [PATCH] Got a unified timeline working --- web/js/damus.js | 31 ++++++++++++++--- web/js/event.js | 23 ++++--------- web/js/lib.js | 10 ++++++ web/js/model.js | 34 +++++++++++++++++++ web/js/ui/fmt.js | 9 +++-- web/js/ui/render.js | 57 +++++++++++++++++++++---------- web/js/ui/state.js | 81 +++++++++++++++++++++++++++++++++++++++++++++ web/js/util.js | 30 +++++++++++------ 8 files changed, 224 insertions(+), 51 deletions(-) diff --git a/web/js/damus.js b/web/js/damus.js index 163b686..c7ce0f3 100644 --- a/web/js/damus.js +++ b/web/js/damus.js @@ -65,6 +65,7 @@ async function damus_web_init_ready() { pool.on("event", on_pool_event); pool.on("notice", on_pool_notice); pool.on("eose", on_pool_eose); + pool.on("ok", on_pool_ok); return pool } @@ -83,11 +84,12 @@ function on_pool_open(relay) { } function on_pool_notice(relay, notice) { - console.info("notice", notice); + console.info("notice", relay.url, notice); // DO NOTHING } -// TODO document what EOSE is +// on_pool_eose occurs when all storage from a relay has been sent to the +// client. async function on_pool_eose(relay, sub_id) { console.info("eose", relay.url, sub_id); const model = DAMUS; @@ -100,16 +102,22 @@ async function on_pool_eose(relay, sub_id) { case ids.profiles: //const view = get_current_view() //handle_profiles_loaded(ids, model, view, relay) + pool.unsubscribe(ids.profiles, relay); break; case ids.unknown: // TODO document why we unsub from unknowns pool.unsubscribe(ids.unknowns, relay); break; + case ids.account: + break; } } +function on_pool_ok(relay) { + console.log("OK", arguments); +} + function on_pool_event(relay, sub_id, ev) { - console.info("event", relay.url, sub_id, ev); const model = DAMUS; const { ids, pool } = model; @@ -127,6 +135,21 @@ function on_pool_event(relay, sub_id, ev) { break; } - // TODO do smart view update logic here + // Refresh view + state.invalidated.push(ev.id); + clearTimeout(state.timer); + state.timer = setTimeout(() => { + view_timeline_update(model, state); + }, 1000); + + // If it was metadata let's refresh the usernames and pics + if (ev.kind == 0) { + view_timeline_update_profiles(model, state, ev); + } } +const state = { + invalidated: [], + timer: -1, +}; + diff --git a/web/js/event.js b/web/js/event.js index cdbf564..c490864 100644 --- a/web/js/event.js +++ b/web/js/event.js @@ -77,21 +77,6 @@ function event_get_tag_refs(tags) { return {root, reply, pubkeys} } -function events_insert_sorted(evs, new_ev) { - for (let i = 0; i < evs.length; i++) { - const ev = evs[i] - if (new_ev.id === ev.id) { - return false - } - if (new_ev.created_at > ev.created_at) { - evs.splice(i, 0, new_ev) - return true - } - } - evs.push(new_ev) - return true -} - function passes_spam_filter(contacts, ev, pow) { log_warn("passes_spam_filter deprecated, use event_is_spam"); return !event_is_spam(ev, contacts, pow); @@ -103,5 +88,11 @@ function event_is_spam(ev, contacts, pow) { return ev.pow >= pow } - +function event_cmp_created(a, b) { + if (a.created_at > b.created_at) + return 1; + if (a.created_at < b.created_at) + return -1; + return 0; +} diff --git a/web/js/lib.js b/web/js/lib.js index e0e01a8..079b183 100644 --- a/web/js/lib.js +++ b/web/js/lib.js @@ -185,3 +185,13 @@ function insert_event_sorted(evs, new_ev) { log_warn("insert_event_sorted deprecated, use events_insert_sorted"); events_insert_sorted(evs, new_ev); } +function can_reply(ev) { + log_warn("can_reply is deprecated, use event_can_reply"); + return event_can_reply(ev); +} +function should_add_to_timeline(ev) { + // TODO rename should_add_to_timeline to is_timeline_event + log_warn("should_add_to_timeline is deprecated, use event_is_timeline"); + return event_is_timeline(ev); +} + diff --git a/web/js/model.js b/web/js/model.js index c10baed..e36fa24 100644 --- a/web/js/model.js +++ b/web/js/model.js @@ -198,6 +198,40 @@ function model_subscribe_defaults(model, relay) { filters_subscribe(filters, model.pool, [relay]); } +function test_model_events_arr() { + const arr = model_events_arr({all_events: { + "c": {name: "c", created_at: 2}, + "a": {name: "a", created_at: 0}, + "b": {name: "b", created_at: 1}, + "e": {name: "e", created_at: 4}, + "d": {name: "d", created_at: 3}, + }}); + let last; + while(arr.length > 0) { + let ev = arr.pop(); + log_debug("test:", ev.name, ev.created_at); + if (!last) { + last = ev; + continue; + } + if (ev.created_at > last.created_at) { + log_error(`ev ${ev.name} should be before ${last.name}`); + } + last = ev; + } +} + +function model_events_arr(model) { + const events = model.all_events; + let arr = []; + for (const evid in events) { + const ev = events[evid]; + const i = arr_bsearch_insert(arr, ev, event_cmp_created); + arr.splice(i, 0, ev); + } + return arr; +} + function new_model() { return { done_init: {}, diff --git a/web/js/ui/fmt.js b/web/js/ui/fmt.js index be92714..14101a6 100644 --- a/web/js/ui/fmt.js +++ b/web/js/ui/fmt.js @@ -1,7 +1,12 @@ function linkify(text, show_media) { return text.replace(URL_REGEX, function(match, p1, p2, p3) { - const url = p2+p3 - const parsed = new URL(url) + const url = p2+p3; + let parsed; + try { + parsed = new URL(url) + } catch (err) { + return match; + } let html; if (show_media && is_img_url(parsed.pathname)) { html = ` diff --git a/web/js/ui/render.js b/web/js/ui/render.js index b7dd500..4e71b1c 100644 --- a/web/js/ui/render.js +++ b/web/js/ui/render.js @@ -97,7 +97,7 @@ function render_unknown_event(damus, ev) { } function render_share(damus, view, ev, opts) { - //todo validate content + // TODO validate content const shared_ev = damus.all_events[ev.refs && ev.refs.root] // share isn't resolved yet. that's ok, we can render this when we have // the event @@ -113,7 +113,7 @@ function render_share(damus, view, ev, opts) { function render_comment_body(damus, ev, opts) { const can_delete = damus.pubkey === ev.pubkey; - const bar = !can_reply(ev) || opts.nobar? "" : render_action_bar(damus, ev, can_delete) + const bar = !can_reply(ev) || opts.nobar? "" : render_action_bar(damus, ev, {can_delete}) const show_media = !opts.is_composing return ` @@ -165,11 +165,9 @@ function render_event(damus, view, ev, opts={}) { view.rendered.add(ev.id) - const profile = damus.profiles[ev.pubkey] - const delta = time_delta(new Date().getTime(), ev.created_at*1000) - const has_bot_line = opts.is_reply const reply_line_bot = (has_bot_line && render_reply_line_bot()) || "" + if (opts.is_composing) has_bot_line = true; const deleted = is_deleted(damus, ev.id) if (deleted && !opts.is_reply) @@ -183,10 +181,30 @@ function render_event(damus, view, ev, opts={}) { } const has_top_line = replied_events !== "" - const border_bottom = opts.is_composing || has_bot_line ? "" : "bottom-border"; return ` ${replied_events} -
+ ` + render_event2(damus, ev, { + deleted, + has_top_line, + has_bot_line, + reply_line_bot, + }); +} + +function render_event2(model, ev, opts={}) { + let { + deleted, + has_bot_line, + has_top_line, + reply_line_bot, + } = opts + + const profile = model.profiles[ev.pubkey] + const delta = time_delta(new Date().getTime(), ev.created_at*1000) + const border_bottom = has_bot_line ? "" : "bottom-border"; + const body = deleted ? render_deleted_comment_body(ev, deleted) : render_comment_body(model, ev, opts) + if (!reply_line_bot) reply_line_bot = ''; + return `
${render_reply_line_top(has_top_line)} ${deleted ? render_deleted_pfp() : render_pfp(ev.pubkey, profile)} @@ -201,11 +219,10 @@ function render_event(damus, view, ev, opts={}) {
- ${deleted ? render_deleted_comment_body(ev, deleted) : render_comment_body(damus, ev, opts)} + ${body}
-
- ` + ` } function render_react_onclick(our_pubkey, reacting_to, emoji, reactions) { @@ -241,7 +258,8 @@ function render_reaction(model, reaction) { return render_pfp(reaction.pubkey, profile) } -function render_action_bar(damus, ev, can_delete) { +function render_action_bar(model, ev, opts={}) { + let { can_delete } = opts; let delete_html = "" if (can_delete) delete_html = ` @@ -249,10 +267,9 @@ function render_action_bar(damus, ev, can_delete) { ` - const groups = get_reactions(damus, ev.id) - const like = "❤️" - const likes = groups[like] || {} - const react_onclick = render_react_onclick(damus.pubkey, ev.id, like, likes) + const groups = get_reactions(model, ev.id) + const react_onclick = render_react_onclick(model.pubkey, ev.id, + "❤️", groups["❤️"] || {}) return `