web: fixed profile fetching to be seq
This commit is contained in:
parent
9f81e93738
commit
4d9151b8ae
7 changed files with 118 additions and 207 deletions
|
@ -19,7 +19,6 @@
|
|||
<script defer src="js/nostr.js?v=7"></script>
|
||||
<script defer src="js/core.js?v=1"></script>
|
||||
<script defer src="js/model.js?v=1"></script>
|
||||
<script defer src="js/filters.js?v=1"></script>
|
||||
<script defer src="js/contacts.js?v=1"></script>
|
||||
<script defer src="js/event.js?v=1"></script>
|
||||
<script defer src="js/unknowns.js?v=1"></script>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
// 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);
|
||||
model_process_event(model, relay, ev);
|
||||
}
|
||||
|
||||
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()}`;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}, {})
|
||||
}
|
||||
|
102
web/js/model.js
102
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")
|
||||
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);
|
||||
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,6 +128,7 @@ function model_remove_reaction(model, evid) {
|
|||
return;
|
||||
if (model.reactions_to[reaction.e])
|
||||
model.reactions_to[reaction.e].delete(target_ev.id);
|
||||
if (update_view)
|
||||
view_timeline_update_reaction(model, target_ev);
|
||||
}
|
||||
|
||||
|
@ -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(),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue