diff --git a/web/icon/event-like.svg b/web/icon/event-like.svg
index de99f58..414fbc2 100644
--- a/web/icon/event-like.svg
+++ b/web/icon/event-like.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/web/icon/no-user.svg b/web/icon/no-user.svg
new file mode 100644
index 0000000..e856dcf
--- /dev/null
+++ b/web/icon/no-user.svg
@@ -0,0 +1,78 @@
+
+
+
+
diff --git a/web/js/core.js b/web/js/core.js
index 3b28cf4..424ac5b 100644
--- a/web/js/core.js
+++ b/web/js/core.js
@@ -171,7 +171,7 @@ function model_get_reacts_to(model, pubkey, evid, emoji) {
if (!r)
return;
for (const id of r.keys()) {
- if (is_deleted(model, id))
+ if (model_is_event_deleted(model, id))
continue;
const reaction = model.all_events[id];
if (!reaction || reaction.pubkey != pubkey)
@@ -189,7 +189,7 @@ function get_reactions(model, evid) {
let reactions = []
for (const id of reactions_set.keys()) {
- if (is_deleted(model, id))
+ if (model_is_event_deleted(model, id))
continue
const reaction = model.all_events[id]
if (!reaction)
diff --git a/web/js/damus.js b/web/js/damus.js
index 2a767bf..efef17c 100644
--- a/web/js/damus.js
+++ b/web/js/damus.js
@@ -2,18 +2,20 @@ let DAMUS
const BOOTSTRAP_RELAYS = [
"wss://relay.damus.io",
- "wss://nostr-relay.wlvs.space",
- "wss://nostr-pub.wellorder.net",
+ //"wss://nostr-relay.wlvs.space",
+ //"wss://nostr-pub.wellorder.net",
]
// TODO autogenerate these constants with a bash script
const IMG_EVENT_LIKED = "icon/event-liked.svg";
const IMG_EVENT_LIKE = "icon/event-like.svg";
+const IMG_NO_USER = "icon/no-user.svg";
-const SID_META = "meta";
-const SID_HISTORY = "history";
+const SID_META = "meta";
+const SID_HISTORY = "history";
const SID_NOTIFICATIONS = "notifications";
-const SID_EXPLORE = "explore";
+const SID_EXPLORE = "explore";
+const SID_PROFILES = "profiles";
async function damus_web_init() {
init_message_textareas();
@@ -23,7 +25,7 @@ async function damus_web_init() {
const max_wait = 500;
const interval = 20;
if (window.nostr || tries >= (max_wait/interval)) {
- console.info("init after", tries);
+ log_info("init after", tries);
damus_web_init_ready();
return;
}
@@ -62,7 +64,6 @@ 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);
// Load all events from storage and re-process them so that apply correct
// effects.
@@ -77,6 +78,7 @@ async function damus_web_init_ready() {
on_timer_timestamps();
on_timer_invalidations();
on_timer_save();
+ on_timer_tick();
return pool;
}
@@ -93,7 +95,7 @@ function on_timer_invalidations() {
if (DAMUS.invalidated.length > 0)
view_timeline_update(DAMUS);
on_timer_invalidations();
- }, 1 * 1000);
+ }, 100);
}
function on_timer_save() {
@@ -104,6 +106,15 @@ function on_timer_save() {
}, 10 * 1000);
}
+function on_timer_tick() {
+ setTimeout(() => {
+ DAMUS.relay_que.forEach((que, relay) => {
+ model_fetch_next_profile(DAMUS, relay);
+ });
+ on_timer_tick();
+ }, 1 * 1000);
+}
+
/* on_pool_open occurs when a relay is opened. It then subscribes for the
* relative REQ as needed.
*/
@@ -122,10 +133,10 @@ function on_pool_open(relay) {
limit: 5000,
}]);
- // Subscribe to the relay's world. We will also never close this.
- relay.subscribe(SID_EXPLORE, [{
+ // Get the latest history as a prefetch
+ relay.subscribe(SID_HISTORY, [{
kinds: STANDARD_KINDS,
- limit: 10000, // TODO this is a lot to handle and we should deal with it another way
+ limit: 500,
}]);
}
@@ -141,36 +152,42 @@ async function on_pool_eose(relay, sub_id) {
const { pool } = model;
const sid = sub_id.slice(0, sub_id.indexOf(":"));
- switch (sub_id) {
+ switch (sid) {
+ case SID_PROFILES:
case SID_META:
- model_get_relay_que(model, relay).busy = false;
- model_fetch_next_profile(model, relay);
case SID_HISTORY:
pool.unsubscribe(sub_id, relay);
break;
}
}
-function on_pool_ok(relay) {
- log_info(`OK(${relay.url})`, arguments);
-}
-
function on_pool_event(relay, sub_id, ev) {
const model = DAMUS;
// Simply ignore any events that happened in the future.
if (new Date(ev.created_at * 1000) > new Date()) {
+ log_debug(`blocked event caust it was newer`, ev);
return;
}
model_process_event(model, relay, ev);
}
+function fetch_profiles(pool, relay, pubkeys) {
+ log_debug(`(${relay.url}) fetching '${pubkeys.length} profiles'`);
+ pool.subscribe(SID_PROFILES, [{
+ kinds: [KIND_METADATA],
+ authors: pubkeys,
+ }], relay);
+}
+
function fetch_profile_info(pubkey, pool, relay) {
- pool.subscribe(`${SID_META}:${pubkey}`, [{
+ const sid = `${SID_META}:${pubkey}`;
+ pool.subscribe(sid, [{
kinds: [KIND_METADATA, KIND_CONTACT],
authors: [pubkey],
limit: 1,
}], relay);
+ return sid;
}
function fetch_profile(pubkey, pool, relay) {
@@ -182,51 +199,13 @@ function fetch_profile(pubkey, pool, relay) {
}], relay);
}
-/*
-function new_sub_id(prefix) {
- return `${prefix}:${uuidv4()}`;
+function subscribe_explore(limit) {
+ DAMUS.pool.subscribe(SID_EXPLORE, [{
+ kinds: STANDARD_KINDS,
+ limit: limit,
+ }]);
}
-let request_profiles_timer;
-function request_profiles() {
- if (request_profiles_timer)
- clearTimeout(request_profiles_timer);
- request_profiles_timer = setTimeout(()=>{
- if (fetch_queued_profiles(DAMUS))
- request_profiles();
- }, 200);
+function unsubscribe_explore() {
+ DAMUS.pool.unsubscribe(SID_EXPLORE);
}
-
-function fetch_queued_profiles(model) {
- const delayed = [];
- const m = new Map();
- for(let i = 0; model.requested_profiles.length > 0 && i < 300; i++) {
- let r = model.requested_profiles.pop();
- let s;
- if (!m.has(r.relay)) {
- s = new Set();
- m.set(r.relay, s);
- } else {
- s = m.get(r.relay);
- }
- if (s.size >= 50) {
- delayed.push(r);
- continue;
- }
- s.add(r.pubkey);
- }
- model.requested_profiles = model.requested_profiles.concat(delayed);
- //log_debug(`m size ${m.size}`);
- m.forEach((set, relay) => {
- let filters = [{
- kinds: [KIND_METADATA, KIND_CONTACT],
- authors: Array.from(set),
- }];
- let sid = new_sub_id(SID_PROFILES);
- model.pool.subscribe(sid, filters, relay);
- log_debug(`(${relay.url}) fetching profiles ${sid} size(${set.size})`);
- })
- return model.requested_profiles.length > 0;
-}*/
-
-
diff --git a/web/js/model.js b/web/js/model.js
index e27e2bc..82bb34c 100644
--- a/web/js/model.js
+++ b/web/js/model.js
@@ -41,24 +41,66 @@ function model_process_event(model, relay, ev) {
// Request the profile if we have never seen it
if (!model.profile_events[ev.pubkey])
- model_fetch_next_profile(model, relay, ev.pubkey);
+ model_que_profile(model, relay, ev.pubkey);
// TODO fetch unknown referenced events & pubkeys from this event
// TODO notify user of new events aimed at them!
}
function model_get_relay_que(model, relay) {
- return map_get(model.relay_que, relay, {profiles:[]});
+ return map_get(model.relay_que, relay, {
+ profiles: [],
+ timestamp: 0,
+ });
}
-function model_fetch_next_profile(model, relay, pubkey) {
+function model_que_profile(model, relay, pubkey) {
const que = model_get_relay_que(model, relay);
- if (pubkey)
- que.profiles.push(pubkey);
- if (que.busy || que.profiles.length == 0)
+ if (que.profiles.indexOf(pubkey) >= 0)
return;
- que.busy = true;
- fetch_profile_info(que.profiles.shift(), model.pool, relay);
+ que.profiles.push(pubkey);
+}
+
+function model_clear_profile_que(model, relay, sid) {
+ const que = model_get_relay_que(model, relay);
+ //log_debug(`cmp '${que.sid}' vs '${sid}'`);
+ if (que.sid != sid)
+ return;
+ delete que.current;
+ delete que.sid;
+ que.timestamp = 0;
+ log_debug("cleared qued");
+}
+
+function model_fetch_next_profile(model, relay) {
+ const que = model_get_relay_que(model, relay);
+
+ // Give up on existing case and add it back to the que
+ if (que.current) {
+ if ((new Date().getTime() - que.timestamp) / 1000 > 30) {
+ que.profiles = que.profiles.concat(que.current);
+ model.pool.unsubscribe(SID_PROFILES, relay);
+ log_debug(`(${relay.url}) gave up on ${que.current.length} profiles`);
+ } else {
+ return;
+ }
+ }
+
+ if (que.profiles.length == 0) {
+ delete que.current;
+ return;
+ }
+ log_debug(`(${relay.url}) has '${que.profiles.length} left profiles to fetch'`);
+
+ const set = new Set();
+ let i = 0;
+ while (que.profiles.length > 0 && i < 100) {
+ set.add(que.profiles.shift());
+ i++;
+ }
+ que.timestamp = new Date().getTime();
+ que.current = Array.from(set);
+ fetch_profiles(model.pool, relay, que.current);
}
/* model_process_event_profile updates the matching profile with the contents found
@@ -72,6 +114,13 @@ function model_process_event_metadata(model, ev, update_view) {
model.profiles[ev.pubkey] = safe_parse_json(ev.content, "profile contents")
if (update_view)
view_timeline_update_profiles(model, ev);
+
+ // 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) {
+ redraw_my_pfp(model);
+ }
}
function model_process_event_following(model, ev, update_view) {
@@ -238,9 +287,6 @@ async function model_save_events(model) {
const db = ev.target.result;
let tx = db.transaction("events", "readwrite");
let store = tx.objectStore("events");
- for (const evid in model.all_events) {
- store.put(model.all_events[evid]);
- }
tx.oncomplete = (ev) => {
db.close();
resolve();
@@ -251,6 +297,11 @@ async function model_save_events(model) {
log_error("failed to save events");
reject(ev);
};
+ store.clear().onsuccess = ()=> {
+ for (const evid in model.all_events) {
+ store.put(model.all_events[evid]);
+ }
+ }
}
return dbcall(_events_save);
}
diff --git a/web/js/ui/render.js b/web/js/ui/render.js
index a061814..5a179d2 100644
--- a/web/js/ui/render.js
+++ b/web/js/ui/render.js
@@ -288,7 +288,7 @@ function render_pfp(pk, profile, opts={}) {
$${str}
data-pubkey="${pk}"
title="${name}"
- onerror="this.onerror=null;this.src='${robohash(pk)}';"
+ onerror="this.onerror=null;this.src='./icon/no-user.svg';"
src="${get_picture(pk, profile)}"/>`
}
diff --git a/web/js/ui/state.js b/web/js/ui/state.js
index b76b679..a2af100 100644
--- a/web/js/ui/state.js
+++ b/web/js/ui/state.js
@@ -13,6 +13,12 @@ function view_timeline_apply_mode(model, mode, opts={}) {
const { pubkey, thread_id } = opts;
const el = view_get_timeline_el();
+ if (mode == VM_EXPLORE) {
+ subscribe_explore(100);
+ } else {
+ unsubscribe_explore();
+ }
+
el.dataset.mode = mode;
switch(mode) {
case VM_THREAD:
@@ -129,10 +135,10 @@ function view_timeline_update(model) {
if (count > 0) {
// If we have things to show and we have initted and we don't have
// anything update the current view
- if (!latest_ev && model.inited) {
+ if (!latest_ev) {
view_timeline_show_new(model);
}
- view_set_show_count(count, true, !model.inited);
+ view_set_show_count(count, true, false);
}
}
@@ -198,12 +204,6 @@ function view_timeline_update_profiles(model, ev) {
const el = view_get_timeline_el();
const pk = ev.pubkey;
const p = model.profiles[pk];
-
- // If it's my pubkey let's redraw my pfp that is not located in the view
- if (pk == model.pubkey) {
- redraw_my_pfp(model);
- }
-
const name = fmt_profile_name(p, fmt_pubkey(pk));
const pic = get_picture(pk, p)
for (const evid in model.elements) {
@@ -268,7 +268,6 @@ function view_mode_contains_event(model, ev, mode, opts={}) {
return ev.id == opts.thread_id || (ev.refs && (
ev.refs.root == opts.thread_id ||
ev.refs.reply == opts.thread_id));
- //event_refs_event(ev, opts.thread_id);
case VM_NOTIFICATIONS:
return event_refs_pubkey(ev, model.pubkey);
}
@@ -277,9 +276,6 @@ function view_mode_contains_event(model, ev, mode, opts={}) {
function event_is_renderable(ev={}) {
return ev.kind == KIND_NOTE || ev.kind == KIND_SHARE;
- return ev.kind == KIND_NOTE ||
- ev.kind == KIND_REACTION ||
- ev.kind == KIND_DELETE;
}
function get_default_max_depth(damus, view) {
diff --git a/web/js/util.js b/web/js/util.js
index fb08bed..d294945 100644
--- a/web/js/util.js
+++ b/web/js/util.js
@@ -195,10 +195,6 @@ function fmt_since_str(current, previous) {
}
}
-function robohash(str) {
- return "https://robohash.org/" + str
-}
-
function is_valid_reaction_content(content) {
return content === "+" || content === "" || is_emoji(content)
}
@@ -233,10 +229,10 @@ function get_qs(loc=location.href) {
function get_picture(pk, profile) {
if (!profile)
- return robohash(pk)
+ return IMG_NO_USER;
if (profile.resolved_picture)
return profile.resolved_picture
- profile.resolved_picture = html`${profile.picture}` || robohash(pk)
+ profile.resolved_picture = html`${profile.picture}` || IMG_NO_USER;
return profile.resolved_picture
}