web: fixed shit i'm tired
This commit is contained in:
parent
4d9151b8ae
commit
1ef133eb75
8 changed files with 197 additions and 97 deletions
|
@ -1 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M0 190.9V185.1C0 115.2 50.52 55.58 119.4 44.1C164.1 36.51 211.4 51.37 244 84.02L256 96L267.1 84.02C300.6 51.37 347 36.51 392.6 44.1C461.5 55.58 512 115.2 512 185.1V190.9C512 232.4 494.8 272.1 464.4 300.4L283.7 469.1C276.2 476.1 266.3 480 256 480C245.7 480 235.8 476.1 228.3 469.1L47.59 300.4C17.23 272.1 .0003 232.4 .0003 190.9L0 190.9z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M244 84L255.1 96L267.1 84.02C300.6 51.37 347 36.51 392.6 44.1C461.5 55.58 512 115.2 512 185.1V190.9C512 232.4 494.8 272.1 464.4 300.4L283.7 469.1C276.2 476.1 266.3 480 256 480C245.7 480 235.8 476.1 228.3 469.1L47.59 300.4C17.23 272.1 0 232.4 0 190.9V185.1C0 115.2 50.52 55.58 119.4 44.1C164.1 36.51 211.4 51.37 244 84C243.1 84 244 84.01 244 84L244 84zM255.1 163.9L210.1 117.1C188.4 96.28 157.6 86.4 127.3 91.44C81.55 99.07 48 138.7 48 185.1V190.9C48 219.1 59.71 246.1 80.34 265.3L256 429.3L431.7 265.3C452.3 246.1 464 219.1 464 190.9V185.1C464 138.7 430.4 99.07 384.7 91.44C354.4 86.4 323.6 96.28 301.9 117.1L255.1 163.9z"/></svg>
|
Before Width: | Height: | Size: 584 B After Width: | Height: | Size: 869 B |
78
web/icon/no-user.svg
Normal file
78
web/icon/no-user.svg
Normal file
|
@ -0,0 +1,78 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Written by Treer (gitlab.com/Treer) -->
|
||||
|
||||
<svg
|
||||
version="1.1"
|
||||
width="600"
|
||||
height="600"
|
||||
fill="white"
|
||||
id="svg562"
|
||||
sodipodi:docname="no-user.svg"
|
||||
inkscape:version="1.2.1 (9c6d41e, 2022-07-14)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<sodipodi:namedview
|
||||
id="namedview564"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.7116667"
|
||||
inkscape:cx="248.00389"
|
||||
inkscape:cy="291.82084"
|
||||
inkscape:window-width="3440"
|
||||
inkscape:window-height="1388"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg562" />
|
||||
<title
|
||||
id="title549">Abstract user icon</title>
|
||||
<defs
|
||||
id="defs554">
|
||||
<clipPath
|
||||
id="circular-border">
|
||||
<circle
|
||||
cx="300"
|
||||
cy="300"
|
||||
r="250"
|
||||
id="circle551" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<rect
|
||||
style="fill:#1a1a1a;fill-opacity:1;stroke:#000000;stroke-width:0"
|
||||
id="rect2537"
|
||||
width="600"
|
||||
height="600"
|
||||
x="4.9909852e-07"
|
||||
y="4.9906225e-07" />
|
||||
<circle
|
||||
cx="300"
|
||||
cy="230"
|
||||
r="100"
|
||||
id="circle558" />
|
||||
<circle
|
||||
cx="300"
|
||||
cy="550"
|
||||
clip-path="url(#circular-border)"
|
||||
id="circle560"
|
||||
r="190" />
|
||||
<metadata
|
||||
id="metadata4117">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:title>Abstract user icon</dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
</svg>
|
After Width: | Height: | Size: 2 KiB |
|
@ -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)
|
||||
|
|
107
web/js/damus.js
107
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;
|
||||
}*/
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)}"/>`
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue