diff --git a/web/index.html b/web/index.html
index 3e92613..5e254b2 100644
--- a/web/index.html
+++ b/web/index.html
@@ -19,7 +19,6 @@
-
diff --git a/web/js/contacts.js b/web/js/contacts.js
index 91e3c81..b3164d3 100644
--- a/web/js/contacts.js
+++ b/web/js/contacts.js
@@ -28,10 +28,6 @@ async function contacts_save(contacts) {
const db = ev.target.result;
let tx = db.transaction("friends", "readwrite");
let store = tx.objectStore("friends");
- contacts.friends.forEach((pubkey) => {
- //log_debug("storing", pubkey);
- store.put({pubkey});
- });
tx.oncomplete = (ev) => {
db.close();
resolve();
@@ -43,6 +39,13 @@ async function contacts_save(contacts) {
window.alert("An error occured saving contacts. Check console.");
reject(ev);
};
+
+ store.clear().onsuccess = () => {
+ contacts.friends.forEach((pubkey) => {
+ //log_debug("storing", pubkey);
+ store.put({pubkey});
+ });
+ };
}
return dbcall(_contacts_save);
}
diff --git a/web/js/damus.js b/web/js/damus.js
index 736097b..2a767bf 100644
--- a/web/js/damus.js
+++ b/web/js/damus.js
@@ -10,7 +10,10 @@ const BOOTSTRAP_RELAYS = [
const IMG_EVENT_LIKED = "icon/event-liked.svg";
const IMG_EVENT_LIKE = "icon/event-like.svg";
-const SID_PROFILES = "profiles";
+const SID_META = "meta";
+const SID_HISTORY = "history";
+const SID_NOTIFICATIONS = "notifications";
+const SID_EXPLORE = "explore";
async function damus_web_init() {
init_message_textareas();
@@ -64,7 +67,7 @@ async function damus_web_init_ready() {
// Load all events from storage and re-process them so that apply correct
// effects.
await model_load_events(model, (ev)=> {
- model_process_event(model, ev);
+ model_process_event(model, undefined, ev);
});
log_debug("loaded events", Object.keys(model.all_events).length);
@@ -107,7 +110,23 @@ function on_timer_save() {
function on_pool_open(relay) {
log_info(`OPEN(${relay.url})`);
const model = DAMUS;
- relay.subscribe(model.ids.account, filter_new_initial(model.pk));
+ const { pubkey } = model;
+
+ // Get all our info & history, well close this after we get it
+ fetch_profile(pubkey, model.pool, relay);
+
+ // Get our notifications. We will never close this.
+ relay.subscribe(SID_NOTIFICATIONS, [{
+ kinds: STANDARD_KINDS,
+ "#p": [pubkey],
+ limit: 5000,
+ }]);
+
+ // Subscribe to the relay's world. We will also never close this.
+ relay.subscribe(SID_EXPLORE, [{
+ kinds: STANDARD_KINDS,
+ limit: 10000, // TODO this is a lot to handle and we should deal with it another way
+ }]);
}
function on_pool_notice(relay, notice) {
@@ -119,28 +138,15 @@ function on_pool_notice(relay, notice) {
async function on_pool_eose(relay, sub_id) {
log_info(`EOSE(${relay.url}): ${sub_id}`);
const model = DAMUS;
- const { ids, pool } = model;
-
- if (sub_id.indexOf(SID_PROFILES) == 0) {
- model.pool.unsubscribe(sub_id, relay);
- request_profiles(model);
- return;
- }
-
+ const { pool } = model;
+
+ const sid = sub_id.slice(0, sub_id.indexOf(":"));
switch (sub_id) {
- case ids.home:
- pool.unsubscribe(ids.home, relay);
- if (!model.inited) {
- model.inited = true;
- }
- break;
- case ids.unknown:
- pool.unsubscribe(ids.unknowns, relay);
- break;
- case ids.account:
- model.done_init[relay] = true;
- pool.unsubscribe(ids.account, relay);
- model_subscribe_defaults(model, relay);
+ 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;
}
}
@@ -156,13 +162,29 @@ function on_pool_event(relay, sub_id, ev) {
if (new Date(ev.created_at * 1000) > new Date()) {
return;
}
- model_process_event(model, ev);
+ model_process_event(model, relay, ev);
+}
- // Request the profile if we have never seen it
- if (!model.profile_events[ev.pubkey]) {
- model.requested_profiles.push({relay, pubkey: ev.pubkey});
- request_profiles(model);
- }
+function fetch_profile_info(pubkey, pool, relay) {
+ pool.subscribe(`${SID_META}:${pubkey}`, [{
+ kinds: [KIND_METADATA, KIND_CONTACT],
+ authors: [pubkey],
+ limit: 1,
+ }], relay);
+}
+
+function fetch_profile(pubkey, pool, relay) {
+ fetch_profile_info(pubkey, pool, relay);
+ pool.subscribe(`${SID_HISTORY}:${pubkey}`, [{
+ kinds: STANDARD_KINDS,
+ authors: [pubkey],
+ limit: 1000,
+ }], relay);
+}
+
+/*
+function new_sub_id(prefix) {
+ return `${prefix}:${uuidv4()}`;
}
let request_profiles_timer;
@@ -205,9 +227,6 @@ function fetch_queued_profiles(model) {
log_debug(`(${relay.url}) fetching profiles ${sid} size(${set.size})`);
})
return model.requested_profiles.length > 0;
-}
+}*/
-function new_sub_id(prefix) {
- return `${prefix}:${uuidv4()}`;
-}
diff --git a/web/js/filters.js b/web/js/filters.js
deleted file mode 100644
index 64fd5c7..0000000
--- a/web/js/filters.js
+++ /dev/null
@@ -1,107 +0,0 @@
-function filters_subscribe(filters, pool, relays=undefined) {
- for (const key in filters) {
- pool.subscribe(key, filters[key], relays);
- }
-}
-
-function filters_new_default(model) {
- const { pubkey, ids, contacts } = model;
- const friends = Array.from(contacts);
- friends.push(pubkey);
- const f = {};
- f[ids.home] = filters_new_friends(friends);
- //f[ids.home] = [{authors: [pubkey], kinds: STANDARD_KINDS}];
- f[ids.contacts] = filters_new_contacts(friends);
- f[ids.dms] = filters_new_dms(pubkey);
- f[ids.notifications] = filters_new_notifications(pubkey);
- f[ids.explore] = [{kinds: STANDARD_KINDS, limit: 500}];
- return f;
-}
-
-function filters_new_default_since(model, cache) {
- const filters = filters_new_default(model);
- for (const key in filters) {
- filters[key] = filters_set_since(filters[key], cache);
- }
- return filters;
-}
-
-function filter_new_initial(pubkey) {
- return {
- authors: [pubkey],
- kinds: [KIND_CONTACT],
- limit: 1,
- };
-}
-
-function filters_new_contacts(friends) {
- return [{
- kinds: [KIND_METADATA],
- authors: friends,
- }]
-}
-
-function filters_new_dms(pubkey, limit=100) {
- return [
- { // dms we sent
- kinds: [KIND_DM],
- limit,
- authors: [pubkey],
- }, { // replys to us
- kinds: [KIND_DM],
- limit: limit,
- "#p": [pubkey],
- }]
-}
-
-function filters_new_friends(friends, limit=500) {
- return [{
- kinds: STANDARD_KINDS,
- authors: friends,
- limit,
- }]
-}
-
-function filters_new_notifications(pubkey, limit=100) {
- return [{
- kinds: STANDARD_KINDS,
- "#p": [pubkey],
- limit,
- }]
-}
-
-function filters_set_since(filters=[], cache={}) {
- filters.forEach((filter) => {
- const since = get_earliest_since_time(filter.kinds, cache)
- delete filter.since;
- if (since) filter.since = since;
- });
- return filters
-}
-
-function get_earliest_since_time(kinds=[], cache={}) {
- const earliest = kinds.reduce((a, kind) => {
- const b = get_since_time(cache[kind]);
- if (!a) {
- return b;
- }
- return b < a ? b : a;
- }, undefined);
- return earliest;
-}
-
-/* calculate_last_of_kind returns a map of kinds to time, where time is the
- * last time it saw that kind.
- */
-function calculate_last_of_kind(evs) {
- const now_sec = new Date().getTime() / 1000
- return Object.keys(evs).reduce((obj, evid) => {
- const ev = evs[evid]
- if (!is_valid_time(now_sec, ev.created_at))
- return obj
- const prev = obj[ev.kind] || 0
- obj[ev.kind] = get_since_time(max(ev.created_at, prev))
- return obj
- }, {})
-}
-
diff --git a/web/js/model.js b/web/js/model.js
index 6c9594f..e27e2bc 100644
--- a/web/js/model.js
+++ b/web/js/model.js
@@ -2,7 +2,7 @@
* a relay. Additionally other side effects happen such as notification checks
* and fetching of unknown pubkey profiles.
*/
-function model_process_event(model, ev) {
+function model_process_event(model, relay, ev) {
if (model.all_events[ev.id]) {
return;
}
@@ -16,10 +16,10 @@ function model_process_event(model, ev) {
let fn;
switch(ev.kind) {
case KIND_METADATA:
- fn = model_process_event_profile;
+ fn = model_process_event_metadata;
break;
case KIND_CONTACT:
- fn = model_process_event_contact;
+ fn = model_process_event_following;
break;
case KIND_DELETE:
fn = model_process_event_deletion;
@@ -29,47 +29,62 @@ function model_process_event(model, ev) {
break;
}
if (fn)
- fn(model, ev);
-
- // check if the event that just came in should notify the user and is newer
- // than the last recorded notification event, if it is notify
- const notify_user = event_refs_pubkey(ev, model.pubkey)
- const last_notified = get_local_state('last_notified_date')
- ev.notified = notify_user;
- if (notify_user && (last_notified == null || ((ev.created_at*1000) > last_notified))) {
- set_local_state('last_notified_date', new Date().getTime());
- model.notifications++;
- update_title(model);
- }
-
- // If we find some unknown ids lets schedule their subscription for info
- if (model_event_has_unknown_ids(model, ev))
- schedule_unknown_refetch(model);
+ fn(model, ev, !!relay);
// Queue event for rendering
model.invalidated.push(ev.id);
+
+ // If the processing did not come from a relay, but instead storage then
+ // let us simply ignore fetching new things.
+ if (!relay)
+ return;
+
+ // Request the profile if we have never seen it
+ if (!model.profile_events[ev.pubkey])
+ model_fetch_next_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:[]});
+}
+
+function model_fetch_next_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)
+ return;
+ que.busy = true;
+ fetch_profile_info(que.profiles.shift(), model.pool, relay);
}
/* model_process_event_profile updates the matching profile with the contents found
* in the event.
*/
-function model_process_event_profile(model, ev) {
+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")
- view_timeline_update_profiles(model, ev);
+ if (update_view)
+ view_timeline_update_profiles(model, ev);
}
-function model_process_event_contact(model, ev) {
+function model_process_event_following(model, ev, update_view) {
contacts_process_event(model.contacts, model.pubkey, ev)
- load_our_relays(model.pubkey, model.pool, ev)
+ // TODO support loading relays that are stored on the initial relay
+ // I find this wierd since I may never want to do that and only have that
+ // information provided by the client - to be better understood
+// load_our_relays(model.pubkey, model.pool, ev)
}
/* model_process_event_reaction updates the reactions dictionary
*/
-function model_process_event_reaction(model, ev) {
+function model_process_event_reaction(model, ev, update_view) {
let reaction = event_parse_reaction(ev);
if (!reaction) {
return;
@@ -77,18 +92,19 @@ function model_process_event_reaction(model, ev) {
if (!model.reactions_to[reaction.e])
model.reactions_to[reaction.e] = new Set();
model.reactions_to[reaction.e].add(ev.id);
- view_timeline_update_reaction(model, ev);
+ if (update_view)
+ view_timeline_update_reaction(model, ev);
}
/* event_process_deletion updates the list of deleted events. Additionally
* pushes event ids onto the invalidated stack for any found.
*/
-function model_process_event_deletion(model, ev) {
+function model_process_event_deletion(model, ev, update_view) {
for (const tag of ev.tags) {
if (tag.length >= 2 && tag[0] === "e" && tag[1]) {
let evid = tag[1];
model.invalidated.push(evid);
- model_remove_reaction(model, evid);
+ model_remove_reaction(model, evid, update_view);
if (model.deleted[evid])
continue;
let ds = model.deletions[evid] =
@@ -98,7 +114,7 @@ function model_process_event_deletion(model, ev) {
}
}
-function model_remove_reaction(model, evid) {
+function model_remove_reaction(model, evid, update_view) {
// deleted_ev -> target_ev -> original_ev
// Here we want to find the original react event to and remove it from our
// reactions map, then we want to update the element on the page. If the
@@ -112,7 +128,8 @@ function model_remove_reaction(model, evid) {
return;
if (model.reactions_to[reaction.e])
model.reactions_to[reaction.e].delete(target_ev.id);
- view_timeline_update_reaction(model, target_ev);
+ if (update_view)
+ view_timeline_update_reaction(model, target_ev);
}
/* model_event_has_unknown_ids checks the event if there are any referenced keys with
@@ -182,25 +199,6 @@ function model_has_event(model, evid) {
return evid in model.all_events
}
-/* model_relay_update_lok returns a map of kinds found in all events based on
- * the last seen event of each kind. It also updates the model's cached value.
- * If the cached value is found it returns that instead
- */
-function model_relay_update_lok(model, relay) {
- let last_of_kind = model.last_event_of_kind[relay];
- if (!last_of_kind) {
- last_of_kind = model.last_event_of_kind[relay]
- = calculate_last_of_kind(model.all_events);
- }
- return last_of_kind;
-}
-
-function model_subscribe_defaults(model, relay) {
- const lok = model_relay_update_lok(model, relay);
- const filters = filters_new_default_since(model, lok);
- filters_subscribe(filters, model.pool, [relay]);
-}
-
function model_events_arr(model) {
const events = model.all_events;
let arr = [];
@@ -309,18 +307,6 @@ function new_model() {
},
invalidated: [], // event ids which should be added/removed
elements: {}, // map of evid > rendered element
- requested_profiles: [], // an array of {relay_id, pubkey} to fetching
-
- ids: {
- comments: "comments",
- explore: "explore",
- refevents: "refevents",
- account: "account",
- home: "home",
- contacts: "contacts",
- notifications: "notifications",
- unknowns: "unknowns",
- dms: "dms",
- },
+ relay_que: new Map(),
};
}
diff --git a/web/js/ui/state.js b/web/js/ui/state.js
index 6f0b57d..b76b679 100644
--- a/web/js/ui/state.js
+++ b/web/js/ui/state.js
@@ -66,6 +66,10 @@ function view_timeline_apply_mode(model, mode, opts={}) {
view_show_spinner(false);
}
+ // Request the background info for this user
+ if (pubkey)
+ fetch_profile(pubkey, model.pool);
+
return mode;
}
diff --git a/web/js/util.js b/web/js/util.js
index 84b4bec..fb08bed 100644
--- a/web/js/util.js
+++ b/web/js/util.js
@@ -74,6 +74,13 @@ function arr_bsearch_insert(arr, item, cmp) {
return start;
}
+function map_get(m, k, def) {
+ if (!m.has(k)) {
+ m.set(k, def);
+ }
+ return m.get(k);
+}
+
function is_valid_time(now_sec, created_at) {
// don't count events far in the future
if (created_at - now_sec >= 120) {