web: revamp mainly done, couple bugs left
This commit is contained in:
parent
f417732942
commit
03d7d7b0cc
12 changed files with 337 additions and 351 deletions
|
@ -137,7 +137,7 @@ button.nav > img.icon {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
z-index: var(--zHeader);
|
z-index: var(--zHeader);
|
||||||
background: var(--clrBg);
|
backdrop-filter: blur(20px);
|
||||||
}
|
}
|
||||||
#view header > label {
|
#view header > label {
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
|
@ -337,6 +337,7 @@ details.cw summary {
|
||||||
background: rgba(0,0,0,0.4);
|
background: rgba(0,0,0,0.4);
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transition: opacity 0.2s linear;
|
transition: opacity 0.2s linear;
|
||||||
|
backdrop-filter: blur(20px);
|
||||||
}
|
}
|
||||||
.modal.closed {
|
.modal.closed {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
@ -440,19 +441,11 @@ label[role="profile-nip5"] {
|
||||||
|
|
||||||
/* Media Preview */
|
/* Media Preview */
|
||||||
|
|
||||||
.bg-blur {
|
|
||||||
backdrop-filter: blur(20px);
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
z-index: var(--zModal);
|
|
||||||
}
|
|
||||||
.modal .media-container {
|
.modal .media-container {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
z-index: calc(var(--zModal) + 1);
|
|
||||||
}
|
}
|
||||||
.modal .media-container > img {
|
.modal .media-container > img {
|
||||||
object-fit: scale-down;
|
object-fit: scale-down;
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
<button class="icon" role="open-gnav" title="Open Menu" onclick="toggle_gnav(this)">
|
<button class="icon" role="open-gnav" title="Open Menu" onclick="toggle_gnav(this)">
|
||||||
<img class="icon svg invert" src="icon/logo.svg"/>
|
<img class="icon svg invert" src="icon/logo.svg"/>
|
||||||
</button>
|
</button>
|
||||||
<button class="icon" role="home" title="Home" onclick="switch_view('home')">
|
<button class="icon" role="home" title="Home" onclick="switch_view('friends')">
|
||||||
<img class="icon svg invert" src="icon/home.svg"/>
|
<img class="icon svg invert" src="icon/home.svg"/>
|
||||||
</button>
|
</button>
|
||||||
<button class="icon" role="explore" title="Explore" onclick="switch_view('explore')">
|
<button class="icon" role="explore" title="Explore" onclick="switch_view('explore')">
|
||||||
|
@ -63,7 +63,7 @@
|
||||||
<img class="icon svg" title="Damus" src="icon/logo-inverted.svg"/>
|
<img class="icon svg" title="Damus" src="icon/logo-inverted.svg"/>
|
||||||
</div>
|
</div>
|
||||||
<button role="home" class="nav icon"
|
<button role="home" class="nav icon"
|
||||||
title="Home" onclick="switch_view('home')">
|
title="Home" onclick="switch_view('friends')">
|
||||||
<img class="icon svg inactive" src="icon/home.svg"/>
|
<img class="icon svg inactive" src="icon/home.svg"/>
|
||||||
<img class="icon svg active" src="icon/home-active.svg"/>
|
<img class="icon svg active" src="icon/home-active.svg"/>
|
||||||
</button>
|
</button>
|
||||||
|
@ -84,7 +84,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="view">
|
<div id="view">
|
||||||
<div id="home-view">
|
<div>
|
||||||
<header>
|
<header>
|
||||||
<label>Home</label>
|
<label>Home</label>
|
||||||
</header>
|
</header>
|
||||||
|
@ -107,31 +107,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="events"></div>
|
<div role="profile-info" class="bottom-border hide">
|
||||||
</div>
|
|
||||||
<div id="explore-view" class="hide">
|
|
||||||
<header>
|
|
||||||
<label>Explore</label>
|
|
||||||
</header>
|
|
||||||
<div class="events"></div>
|
|
||||||
</div>
|
|
||||||
<div id="notifications-view" class="hide">
|
|
||||||
<header>
|
|
||||||
<label>Notifications</label>
|
|
||||||
</header>
|
|
||||||
<div class="events"></div>
|
|
||||||
</div>
|
|
||||||
<div id="thread-view" class="hide">
|
|
||||||
<header>
|
|
||||||
<label>Thread</label>
|
|
||||||
</header>
|
|
||||||
<div class="events"></div>
|
|
||||||
</div>
|
|
||||||
<div id="profile-view" class="hide">
|
|
||||||
<header>
|
|
||||||
<label role="profile-name">Profile</label>
|
|
||||||
</header>
|
|
||||||
<div role="profile-info" class="bottom-border">
|
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<img role="profile-image" class="pfp jumbo" src="" />
|
<img role="profile-image" class="pfp jumbo" src="" />
|
||||||
<div class="profile-tools">
|
<div class="profile-tools">
|
||||||
|
@ -151,14 +127,18 @@
|
||||||
<p role="profile-desc"></p>
|
<p role="profile-desc"></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="events"></div>
|
<div class="loading-events">
|
||||||
|
<div class="loader" title="Loading...">
|
||||||
|
<img class="dark-invert" src="icon/loader-fragment.svg"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="timeline" class="events"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-fill vertical-hide"></div>
|
<div class="flex-fill vertical-hide"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="modal closed" id="media-preview">
|
<div class="modal closed" id="media-preview">
|
||||||
<div class="bg-blur"></div>
|
|
||||||
<div class="media-container">
|
<div class="media-container">
|
||||||
<img onclick="close_media_preview()" src=""/>
|
<img onclick="close_media_preview()" src=""/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
const KIND_METADATA = 0;
|
||||||
|
const KIND_NOTE = 1;
|
||||||
|
const KIND_SERVER = 2;
|
||||||
|
const KIND_REACTION = 7;
|
||||||
|
|
||||||
function get_local_state(key) {
|
function get_local_state(key) {
|
||||||
if (DAMUS[key] != null)
|
if (DAMUS[key] != null)
|
||||||
return DAMUS[key]
|
return DAMUS[key]
|
||||||
|
@ -197,17 +202,13 @@ function gather_reply_tags(pubkey, from) {
|
||||||
return tags
|
return tags
|
||||||
}
|
}
|
||||||
|
|
||||||
function get_tag_event(tag)
|
function get_tag_event(tag) {
|
||||||
{
|
|
||||||
if (tag.length < 2)
|
if (tag.length < 2)
|
||||||
return null
|
return null
|
||||||
|
|
||||||
if (tag[0] === "e")
|
if (tag[0] === "e")
|
||||||
return DAMUS.all_events[tag[1]]
|
return DAMUS.all_events[tag[1]]
|
||||||
|
|
||||||
if (tag[0] === "p")
|
if (tag[0] === "p")
|
||||||
return DAMUS.all_events[DAMUS.profile_events[tag[1]]]
|
return DAMUS.all_events[DAMUS.profile_events[tag[1]]]
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,8 @@ let DAMUS
|
||||||
const STANDARD_KINDS = [1, 42, 5, 6, 7];
|
const STANDARD_KINDS = [1, 42, 5, 6, 7];
|
||||||
|
|
||||||
const BOOTSTRAP_RELAYS = [
|
const BOOTSTRAP_RELAYS = [
|
||||||
"wss://relay.damus.io",
|
"wss://nostr.rdfriedl.com",
|
||||||
|
//"wss://relay.damus.io",
|
||||||
//"wss://nostr-relay.wlvs.space",
|
//"wss://nostr-relay.wlvs.space",
|
||||||
//"wss://nostr-pub.wellorder.net"
|
//"wss://nostr-pub.wellorder.net"
|
||||||
]
|
]
|
||||||
|
@ -57,10 +58,11 @@ async function damus_web_init_ready() {
|
||||||
model.view_el = document.querySelector("#view")
|
model.view_el = document.querySelector("#view")
|
||||||
//load_cache(model)
|
//load_cache(model)
|
||||||
|
|
||||||
switch_view('home')
|
view_timeline_apply_mode(model, VM_FRIENDS);
|
||||||
document.addEventListener('visibilitychange', () => {
|
document.addEventListener('visibilitychange', () => {
|
||||||
update_title(model)
|
update_title(model)
|
||||||
})
|
});
|
||||||
|
update_timestamps();
|
||||||
pool.on("open", on_pool_open);
|
pool.on("open", on_pool_open);
|
||||||
pool.on("event", on_pool_event);
|
pool.on("event", on_pool_event);
|
||||||
pool.on("notice", on_pool_notice);
|
pool.on("notice", on_pool_notice);
|
||||||
|
@ -69,8 +71,15 @@ async function damus_web_init_ready() {
|
||||||
return pool
|
return pool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function update_timestamps() {
|
||||||
|
setTimeout(() => {
|
||||||
|
update_timestamps();
|
||||||
|
view_timeline_update_timestamps();
|
||||||
|
}, 60 * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
function on_pool_open(relay) {
|
function on_pool_open(relay) {
|
||||||
console.info("opened relay", relay);
|
log_info("opened relay", relay);
|
||||||
const model = DAMUS;
|
const model = DAMUS;
|
||||||
// We check the cache if we have init anything, if not we do our inital
|
// We check the cache if we have init anything, if not we do our inital
|
||||||
// otherwise we do a get since last
|
// otherwise we do a get since last
|
||||||
|
@ -84,32 +93,30 @@ function on_pool_open(relay) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function on_pool_notice(relay, notice) {
|
function on_pool_notice(relay, notice) {
|
||||||
console.info("notice", relay.url, notice);
|
log_info("notice", relay.url, notice);
|
||||||
// DO NOTHING
|
// DO NOTHING
|
||||||
}
|
}
|
||||||
|
|
||||||
// on_pool_eose occurs when all storage from a relay has been sent to the
|
// on_pool_eose occurs when all storage from a relay has been sent to the
|
||||||
// client.
|
// client.
|
||||||
async function on_pool_eose(relay, sub_id) {
|
async function on_pool_eose(relay, sub_id) {
|
||||||
console.info("eose", relay.url, sub_id);
|
//console.info("eose", relay.url, sub_id);
|
||||||
const model = DAMUS;
|
const model = DAMUS;
|
||||||
const { ids, pool } = model;
|
const { ids, pool } = model;
|
||||||
switch (sub_id) {
|
switch (sub_id) {
|
||||||
case ids.home:
|
case ids.home:
|
||||||
//const events = model.views.home.events
|
const events = model_events_arr(model);
|
||||||
//handle_comments_loaded(ids, model, events, relay)
|
// TODO filter out events to friends of friends
|
||||||
|
on_eose_comments(ids, model, events, relay)
|
||||||
|
pool.unsubscribe(ids.home, relay);
|
||||||
break;
|
break;
|
||||||
case ids.profiles:
|
case ids.profiles:
|
||||||
//const view = get_current_view()
|
model.pool.unsubscribe(ids.profiles, relay);
|
||||||
//handle_profiles_loaded(ids, model, view, relay)
|
on_eose_profiles(ids, model, relay)
|
||||||
pool.unsubscribe(ids.profiles, relay);
|
|
||||||
break;
|
break;
|
||||||
case ids.unknown:
|
case ids.unknown:
|
||||||
// TODO document why we unsub from unknowns
|
|
||||||
pool.unsubscribe(ids.unknowns, relay);
|
pool.unsubscribe(ids.unknowns, relay);
|
||||||
break;
|
break;
|
||||||
case ids.account:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,12 +128,14 @@ function on_pool_event(relay, sub_id, ev) {
|
||||||
const model = DAMUS;
|
const model = DAMUS;
|
||||||
const { ids, pool } = model;
|
const { ids, pool } = model;
|
||||||
|
|
||||||
|
// Process event and apply side effects
|
||||||
if (!model.all_events[ev.id]) {
|
if (!model.all_events[ev.id]) {
|
||||||
model.all_events[ev.id] = ev;
|
model.all_events[ev.id] = ev;
|
||||||
model_process_event(model, ev);
|
model_process_event(model, ev);
|
||||||
// schedule_save_events(model);
|
// schedule_save_events(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update subscriptions
|
||||||
switch (sub_id) {
|
switch (sub_id) {
|
||||||
case ids.account:
|
case ids.account:
|
||||||
model.done_init[relay] = true;
|
model.done_init[relay] = true;
|
||||||
|
@ -134,22 +143,44 @@ function on_pool_event(relay, sub_id, ev) {
|
||||||
model_subscribe_defaults(model, relay);
|
model_subscribe_defaults(model, relay);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 = {
|
function on_eose_profiles(ids, model, relay) {
|
||||||
invalidated: [],
|
const prefix = difficulty_to_prefix(model.pow);
|
||||||
timer: -1,
|
const fofs = Array.from(model.contacts.friend_of_friends);
|
||||||
};
|
const standard_kinds = [1,42,5,6,7];
|
||||||
|
let pow_filter = {kinds: standard_kinds, limit: 50};
|
||||||
|
if (model.pow > 0)
|
||||||
|
pow_filter.ids = [ prefix ];
|
||||||
|
let explore_filters = [ pow_filter ];
|
||||||
|
if (fofs.length > 0)
|
||||||
|
explore_filters.push({kinds: standard_kinds, authors: fofs, limit: 50});
|
||||||
|
model.pool.subscribe(ids.explore, explore_filters, relay);
|
||||||
|
}
|
||||||
|
|
||||||
|
function on_eose_comments(ids, model, events, relay) {
|
||||||
|
const pubkeys = events.reduce((s, ev) => {
|
||||||
|
s.add(ev.pubkey);
|
||||||
|
for (const tag of ev.tags) {
|
||||||
|
if (tag.length >= 2 && tag[0] === "p") {
|
||||||
|
if (!model.profile_events[tag[1]])
|
||||||
|
s.add(tag[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}, new Set());
|
||||||
|
const authors = Array.from(pubkeys)
|
||||||
|
// load profiles and noticed chatrooms
|
||||||
|
const profile_filter = {kinds: [0,3], authors: authors};
|
||||||
|
let filters = [];
|
||||||
|
if (authors.length > 0)
|
||||||
|
filters.push(profile_filter);
|
||||||
|
if (filters.length === 0) {
|
||||||
|
//log_debug("No profiles filters to request...")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//console.log("subscribe", profiles_id, filter, relay)
|
||||||
|
//log_debug("subscribing to profiles on %s", relay.url)
|
||||||
|
model.pool.subscribe(ids.profiles, filters, relay)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,6 @@ function event_get_tag_refs(tags) {
|
||||||
let ids = []
|
let ids = []
|
||||||
let pubkeys = []
|
let pubkeys = []
|
||||||
let root, reply
|
let root, reply
|
||||||
let i = 0
|
|
||||||
for (const tag of tags) {
|
for (const tag of tags) {
|
||||||
if (tag.length >= 4 && tag[0] == "e") {
|
if (tag.length >= 4 && tag[0] == "e") {
|
||||||
ids.push(tag[1])
|
ids.push(tag[1])
|
||||||
|
@ -65,7 +64,6 @@ function event_get_tag_refs(tags) {
|
||||||
} else if (tag.length >= 2 && tag[0] == "p") {
|
} else if (tag.length >= 2 && tag[0] == "p") {
|
||||||
pubkeys.push(tag[1])
|
pubkeys.push(tag[1])
|
||||||
}
|
}
|
||||||
i++
|
|
||||||
}
|
}
|
||||||
if (!(root && reply) && ids.length > 0) {
|
if (!(root && reply) && ids.length > 0) {
|
||||||
if (ids.length === 1)
|
if (ids.length === 1)
|
||||||
|
@ -77,11 +75,6 @@ function event_get_tag_refs(tags) {
|
||||||
return {root, reply, pubkeys}
|
return {root, reply, pubkeys}
|
||||||
}
|
}
|
||||||
|
|
||||||
function passes_spam_filter(contacts, ev, pow) {
|
|
||||||
log_warn("passes_spam_filter deprecated, use event_is_spam");
|
|
||||||
return !event_is_spam(ev, contacts, pow);
|
|
||||||
}
|
|
||||||
|
|
||||||
function event_is_spam(ev, contacts, pow) {
|
function event_is_spam(ev, contacts, pow) {
|
||||||
if (contacts.friend_of_friends.has(ev.pubkey))
|
if (contacts.friend_of_friends.has(ev.pubkey))
|
||||||
return true
|
return true
|
||||||
|
@ -96,3 +89,34 @@ function event_cmp_created(a, b) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* event_refs_event checks if event A references event B in its tags.
|
||||||
|
*/
|
||||||
|
function event_refs_event(a, b) {
|
||||||
|
for (const tag of a.tags) {
|
||||||
|
if (tag.length >= 2 && tag[0] === "e" && tag[1] == b.id)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function event_get_last_tags(ev) {
|
||||||
|
let o = {};
|
||||||
|
for (const tag of ev.tags) {
|
||||||
|
if (tag.length >= 2 && (tag[0] === "e" || tag[0] === "p"))
|
||||||
|
o[tag[0]] = tag[1];
|
||||||
|
}
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* event_get_reacted_to returns the reacted to event & pubkey (e, p). Returns
|
||||||
|
* undefined if invalid or incomplete.
|
||||||
|
*/
|
||||||
|
function event_parse_reaction(ev) {
|
||||||
|
if (!is_valid_reaction_content(ev.content) || ev.kind != 7)
|
||||||
|
return;
|
||||||
|
const o = event_get_last_tags(ev);
|
||||||
|
if (o["e"] && o["p"]) {
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
114
web/js/lib.js
114
web/js/lib.js
|
@ -57,116 +57,6 @@ function handle_redraw_logic(model, view_name)
|
||||||
view.redraw_timer = setTimeout(redraw_events.bind(null, model, view), 600)
|
view.redraw_timer = setTimeout(redraw_events.bind(null, model, view), 600)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
function handle_home_event(model, relay, sub_id, ev) {
|
|
||||||
const ids = model.ids
|
|
||||||
|
|
||||||
// ignore duplicates
|
|
||||||
if (!has_event(model, ev.id)) {
|
|
||||||
model.all_events[ev.id] = ev
|
|
||||||
process_event(model, ev)
|
|
||||||
schedule_save_events(model)
|
|
||||||
}
|
|
||||||
|
|
||||||
ev = model.all_events[ev.id]
|
|
||||||
|
|
||||||
let is_new = true
|
|
||||||
switch (sub_id) {
|
|
||||||
case model.ids.explore:
|
|
||||||
const view = model.views.explore
|
|
||||||
|
|
||||||
// show more things in explore timeline
|
|
||||||
if (should_add_to_explore_timeline(model.contacts, view, ev, model.pow)) {
|
|
||||||
view.seen.add(ev.pubkey)
|
|
||||||
is_new = insert_event_sorted(view.events, ev)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_new)
|
|
||||||
handle_redraw_logic(model, 'explore')
|
|
||||||
break;
|
|
||||||
|
|
||||||
case model.ids.notifications:
|
|
||||||
if (should_add_to_notification_timeline(model.pubkey, model.contacts, ev, model.pow))
|
|
||||||
is_new = insert_event_sorted(model.views.notifications.events, ev)
|
|
||||||
|
|
||||||
if (is_new)
|
|
||||||
handle_redraw_logic(model, 'notifications')
|
|
||||||
break;
|
|
||||||
|
|
||||||
case model.ids.home:
|
|
||||||
if (should_add_to_timeline(ev))
|
|
||||||
is_new = insert_event_sorted(model.views.home.events, ev)
|
|
||||||
|
|
||||||
if (is_new)
|
|
||||||
handle_redraw_logic(model, 'home')
|
|
||||||
break;
|
|
||||||
case model.ids.account:
|
|
||||||
switch (ev.kind) {
|
|
||||||
case 3:
|
|
||||||
model.done_init[relay] = true
|
|
||||||
model.pool.unsubscribe(model.ids.account, relay)
|
|
||||||
send_home_filters(model, relay)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
break
|
|
||||||
case model.ids.profiles:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
function handle_profiles_loaded(ids, model, view, relay) {
|
|
||||||
// stop asking for profiles
|
|
||||||
model.pool.unsubscribe(ids.profiles, relay)
|
|
||||||
|
|
||||||
//redraw_events(model, view)
|
|
||||||
redraw_my_pfp(model)
|
|
||||||
|
|
||||||
const prefix = difficulty_to_prefix(model.pow)
|
|
||||||
const fofs = Array.from(model.contacts.friend_of_friends)
|
|
||||||
const standard_kinds = [1,42,5,6,7]
|
|
||||||
let pow_filter = {kinds: standard_kinds, limit: 50}
|
|
||||||
if (model.pow > 0)
|
|
||||||
pow_filter.ids = [ prefix ]
|
|
||||||
|
|
||||||
let explore_filters = [ pow_filter ]
|
|
||||||
|
|
||||||
if (fofs.length > 0) {
|
|
||||||
explore_filters.push({kinds: standard_kinds, authors: fofs, limit: 50})
|
|
||||||
}
|
|
||||||
|
|
||||||
model.pool.subscribe(ids.explore, explore_filters, relay)
|
|
||||||
}
|
|
||||||
|
|
||||||
// load profiles after comment notes are loaded
|
|
||||||
function handle_comments_loaded(ids, model, events, relay) {
|
|
||||||
const pubkeys = events.reduce((s, ev) => {
|
|
||||||
s.add(ev.pubkey)
|
|
||||||
for (const tag of ev.tags) {
|
|
||||||
if (tag.length >= 2 && tag[0] === "p") {
|
|
||||||
if (!model.profile_events[tag[1]])
|
|
||||||
s.add(tag[1])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}, new Set())
|
|
||||||
const authors = Array.from(pubkeys)
|
|
||||||
|
|
||||||
// load profiles and noticed chatrooms
|
|
||||||
const profile_filter = {kinds: [0,3], authors: authors}
|
|
||||||
|
|
||||||
let filters = []
|
|
||||||
if (authors.length > 0)
|
|
||||||
filters.push(profile_filter)
|
|
||||||
if (filters.length === 0) {
|
|
||||||
log_debug("No profiles filters to request...")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//console.log("subscribe", profiles_id, filter, relay)
|
|
||||||
log_debug("subscribing to profiles on %s", relay.url)
|
|
||||||
model.pool.subscribe(ids.profiles, filters, relay)
|
|
||||||
}
|
|
||||||
|
|
||||||
/* DEPRECATED */
|
/* DEPRECATED */
|
||||||
|
|
||||||
function is_deleted(model, evid) {
|
function is_deleted(model, evid) {
|
||||||
|
@ -194,4 +84,8 @@ function should_add_to_timeline(ev) {
|
||||||
log_warn("should_add_to_timeline is deprecated, use event_is_timeline");
|
log_warn("should_add_to_timeline is deprecated, use event_is_timeline");
|
||||||
return event_is_timeline(ev);
|
return event_is_timeline(ev);
|
||||||
}
|
}
|
||||||
|
function passes_spam_filter(contacts, ev, pow) {
|
||||||
|
log_warn("passes_spam_filter deprecated, use event_is_spam");
|
||||||
|
return !event_is_spam(ev, contacts, pow);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,10 @@
|
||||||
* and fetching of unknown pubkey profiles.
|
* and fetching of unknown pubkey profiles.
|
||||||
*/
|
*/
|
||||||
function model_process_event(model, ev) {
|
function model_process_event(model, ev) {
|
||||||
ev.refs = event_get_tag_refs(ev.tags)
|
ev.refs = event_get_tag_refs(ev.tags);
|
||||||
ev.pow = event_calculate_pow(ev)
|
ev.pow = event_calculate_pow(ev);
|
||||||
|
|
||||||
|
// TODO this doesn't actually work because of async nature
|
||||||
ev.is_spam = !event_is_spam(ev, model.contacts, model.pow);
|
ev.is_spam = !event_is_spam(ev, model.contacts, model.pow);
|
||||||
|
|
||||||
// Process specific event needs based on it's kind. Not using a map because
|
// Process specific event needs based on it's kind. Not using a map because
|
||||||
|
@ -47,7 +49,15 @@ function model_process_event(model, ev) {
|
||||||
// If we find some unknown ids lets schedule their subscription for info
|
// If we find some unknown ids lets schedule their subscription for info
|
||||||
if (model_event_has_unknown_ids(model, ev))
|
if (model_event_has_unknown_ids(model, ev))
|
||||||
schedule_unknown_refetch(model);
|
schedule_unknown_refetch(model);
|
||||||
|
|
||||||
|
// Refresh timeline
|
||||||
|
model.invalidated.push(ev.id);
|
||||||
|
clearTimeout(inv_timer);
|
||||||
|
inv_timer = setTimeout(() => {
|
||||||
|
view_timeline_update(model);
|
||||||
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
let inv_timer;
|
||||||
|
|
||||||
//function process_chatroom_event(model, ev) {
|
//function process_chatroom_event(model, ev) {
|
||||||
// model.chatrooms[ev.id] = safe_parse_json(ev.content,
|
// model.chatrooms[ev.id] = safe_parse_json(ev.content,
|
||||||
|
@ -63,22 +73,19 @@ function model_process_event_profile(model, ev) {
|
||||||
return
|
return
|
||||||
model.profile_events[ev.pubkey] = ev.id
|
model.profile_events[ev.pubkey] = ev.id
|
||||||
model.profiles[ev.pubkey] = safe_parse_json(ev.content, "profile contents")
|
model.profiles[ev.pubkey] = safe_parse_json(ev.content, "profile contents")
|
||||||
|
view_timeline_update_profiles(model, ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* model_process_event_reaction updates the reactions dictionary
|
/* model_process_event_reaction updates the reactions dictionary
|
||||||
*/
|
*/
|
||||||
function model_process_event_reaction(model, ev) {
|
function model_process_event_reaction(model, ev) {
|
||||||
if (!is_valid_reaction_content(ev.content))
|
let reaction = event_parse_reaction(ev);
|
||||||
return
|
if (!reaction)
|
||||||
let last = {}
|
return;
|
||||||
for (const tag of ev.tags) {
|
if (!model.reactions_to[reaction.e])
|
||||||
if (tag.length >= 2 && (tag[0] === "e" || tag[0] === "p"))
|
model.reactions_to[reaction.e] = new Set();
|
||||||
last[tag[0]] = tag[1]
|
model.reactions_to[reaction.e].add(ev.id);
|
||||||
}
|
view_timeline_update_reaction(model, ev);
|
||||||
if (last.e) {
|
|
||||||
model.reactions_to[last.e] = model.reactions_to[last.e] || new Set()
|
|
||||||
model.reactions_to[last.e].add(ev.id)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function model_process_event_contact(model, ev) {
|
function model_process_event_contact(model, ev) {
|
||||||
|
@ -244,20 +251,6 @@ function new_model() {
|
||||||
unknown_pks: {},
|
unknown_pks: {},
|
||||||
deletions: {},
|
deletions: {},
|
||||||
but_wait_theres_more: 0,
|
but_wait_theres_more: 0,
|
||||||
cw_open: {},
|
|
||||||
views: {
|
|
||||||
home: new_timeline('home'),
|
|
||||||
explore: {
|
|
||||||
...new_timeline('explore'),
|
|
||||||
seen: new Set(),
|
|
||||||
},
|
|
||||||
notifications: {
|
|
||||||
...new_timeline('notifications'),
|
|
||||||
max_depth: 1,
|
|
||||||
},
|
|
||||||
profile: new_timeline('profile'),
|
|
||||||
thread: new_timeline('thread'),
|
|
||||||
},
|
|
||||||
pow: 0, // pow difficulty target
|
pow: 0, // pow difficulty target
|
||||||
deleted: {},
|
deleted: {},
|
||||||
profiles: {},
|
profiles: {},
|
||||||
|
@ -268,15 +261,6 @@ function new_model() {
|
||||||
friends: new Set(),
|
friends: new Set(),
|
||||||
friend_of_friends: new Set(),
|
friend_of_friends: new Set(),
|
||||||
},
|
},
|
||||||
}
|
invalidated: [],
|
||||||
}
|
|
||||||
|
|
||||||
function new_timeline(name) {
|
|
||||||
return {
|
|
||||||
name,
|
|
||||||
events: [],
|
|
||||||
rendered: new Set(),
|
|
||||||
depths: {},
|
|
||||||
expanded: new Set(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,10 +31,8 @@ function format_content(ev, show_media) {
|
||||||
return "❤️"
|
return "❤️"
|
||||||
return sanitize(ev.content.trim())
|
return sanitize(ev.content.trim())
|
||||||
}
|
}
|
||||||
|
|
||||||
const content = ev.content.trim()
|
const content = ev.content.trim()
|
||||||
const body = convert_quote_blocks(content, show_media)
|
const body = convert_quote_blocks(content, show_media)
|
||||||
|
|
||||||
let cw = get_content_warning(ev.tags)
|
let cw = get_content_warning(ev.tags)
|
||||||
if (cw !== null) {
|
if (cw !== null) {
|
||||||
let cwHTML = "Content Warning"
|
let cwHTML = "Content Warning"
|
||||||
|
@ -43,15 +41,13 @@ function format_content(ev, show_media) {
|
||||||
} else {
|
} else {
|
||||||
cwHTML += `: "<span>${cw}</span>".`
|
cwHTML += `: "<span>${cw}</span>".`
|
||||||
}
|
}
|
||||||
const open = !!DAMUS.cw_open[ev.id]? "open" : ""
|
|
||||||
return `
|
return `
|
||||||
<details ontoggle="toggle_content_warning(this)" class="cw" id="cw_${ev.id}" ${open}>
|
<details class="cw">
|
||||||
<summary class="event-message">${cwHTML}</summary>
|
<summary class="event-message">${cwHTML}</summary>
|
||||||
${body}
|
${body}
|
||||||
</details>
|
</details>
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
return body
|
return body
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// is done by simple string manipulations & templates. If you need to write
|
// is done by simple string manipulations & templates. If you need to write
|
||||||
// loops simply write it in code and return strings.
|
// loops simply write it in code and return strings.
|
||||||
|
|
||||||
function render_timeline_event(damus, view, ev)
|
/*function render_timeline_event(damus, view, ev)
|
||||||
{
|
{
|
||||||
const root_id = get_thread_root_id(damus, ev.id)
|
const root_id = get_thread_root_id(damus, ev.id)
|
||||||
const max_depth = root_id ? get_thread_max_depth(damus, view, root_id) : get_default_max_depth(damus, view)
|
const max_depth = root_id ? get_thread_max_depth(damus, view, root_id) : get_default_max_depth(damus, view)
|
||||||
|
@ -17,7 +17,7 @@ function render_events(damus, view) {
|
||||||
return view.events
|
return view.events
|
||||||
.filter((ev, i) => i < 140)
|
.filter((ev, i) => i < 140)
|
||||||
.map((ev) => render_timeline_event(damus, view, ev)).join("\n")
|
.map((ev) => render_timeline_event(damus, view, ev)).join("\n")
|
||||||
}
|
}*/
|
||||||
|
|
||||||
function render_reply_line_top(has_top_line) {
|
function render_reply_line_top(has_top_line) {
|
||||||
const classes = has_top_line ? "" : "invisible"
|
const classes = has_top_line ? "" : "invisible"
|
||||||
|
@ -113,7 +113,7 @@ function render_share(damus, view, ev, opts) {
|
||||||
|
|
||||||
function render_comment_body(damus, ev, opts) {
|
function render_comment_body(damus, ev, opts) {
|
||||||
const can_delete = damus.pubkey === ev.pubkey;
|
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
|
const show_media = !opts.is_composing
|
||||||
|
|
||||||
return `
|
return `
|
||||||
|
@ -157,7 +157,7 @@ function render_deleted_comment_body(ev, deleted) {
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
function render_event(damus, view, ev, opts={}) {
|
/*function render_event(damus, view, ev, opts={}) {
|
||||||
if (ev.kind === 6)
|
if (ev.kind === 6)
|
||||||
return render_share(damus, view, ev, opts)
|
return render_share(damus, view, ev, opts)
|
||||||
if (shouldnt_render_event(damus.pubkey, view, ev, opts))
|
if (shouldnt_render_event(damus.pubkey, view, ev, opts))
|
||||||
|
@ -189,37 +189,56 @@ function render_event(damus, view, ev, opts={}) {
|
||||||
has_bot_line,
|
has_bot_line,
|
||||||
reply_line_bot,
|
reply_line_bot,
|
||||||
});
|
});
|
||||||
}
|
}*/
|
||||||
|
|
||||||
function render_event2(model, ev, opts={}) {
|
function render_event(model, ev, opts={}) {
|
||||||
let {
|
let {
|
||||||
deleted,
|
|
||||||
has_bot_line,
|
has_bot_line,
|
||||||
has_top_line,
|
has_top_line,
|
||||||
reply_line_bot,
|
reply_line_bot,
|
||||||
} = opts
|
} = opts
|
||||||
|
|
||||||
const profile = model.profiles[ev.pubkey]
|
const thread_root = (ev.refs && ev.refs.root) || ev.id;
|
||||||
const delta = time_delta(new Date().getTime(), ev.created_at*1000)
|
const profile = model.profiles[ev.pubkey];
|
||||||
const border_bottom = has_bot_line ? "" : "bottom-border";
|
const delta = fmt_since_str(new Date().getTime(), ev.created_at*1000)
|
||||||
const body = deleted ? render_deleted_comment_body(ev, deleted) : render_comment_body(model, ev, opts)
|
const border_bottom = opts.is_composing || has_bot_line ? "" : "bottom-border";
|
||||||
|
let thread_btn = "";
|
||||||
if (!reply_line_bot) reply_line_bot = '';
|
if (!reply_line_bot) reply_line_bot = '';
|
||||||
return `<div id="ev${ev.id}" class="event ${border_bottom}">
|
return `<div id="ev${ev.id}" class="event ${border_bottom}">
|
||||||
<div class="userpic">
|
<div class="userpic">
|
||||||
${render_reply_line_top(has_top_line)}
|
${render_reply_line_top(has_top_line)}
|
||||||
${deleted ? render_deleted_pfp() : render_pfp(ev.pubkey, profile)}
|
${render_pfp(ev.pubkey, profile)}
|
||||||
${reply_line_bot}
|
${reply_line_bot}
|
||||||
</div>
|
</div>
|
||||||
<div class="event-content">
|
<div class="event-content">
|
||||||
<div class="info">
|
<div class="info">
|
||||||
${render_name(ev.pubkey, profile)}
|
${render_name(ev.pubkey, profile)}
|
||||||
<span class="timestamp">${delta}</span>
|
<span class="timestamp" data-timestamp="${ev.created_at}">${delta}</span>
|
||||||
<button class="icon" title="View Thread" role="view-event" data-eid="${ev.id}" onclick="click_event(this)">
|
<button class="icon" title="View Thread" role="view-event" onclick="open_thread('${thread_root}')">
|
||||||
<img class="icon svg small" src="icon/open-thread.svg"/>
|
<img class="icon svg small" src="icon/open-thread.svg"/>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="comment">
|
<div class="comment">
|
||||||
${body}
|
${render_comment_body(model, ev, opts)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>`
|
||||||
|
}
|
||||||
|
|
||||||
|
function render_event_nointeract(model, ev, opts={}) {
|
||||||
|
const profile = model.profiles[ev.pubkey];
|
||||||
|
const delta = fmt_since_str(new Date().getTime(), ev.created_at*1000)
|
||||||
|
return `<div class="event border-bottom">
|
||||||
|
<div class="userpic">
|
||||||
|
${render_pfp(ev.pubkey, profile)}
|
||||||
|
</div>
|
||||||
|
<div class="event-content">
|
||||||
|
<div class="info">
|
||||||
|
${render_name(ev.pubkey, profile)}
|
||||||
|
<span class="timestamp" data-timestamp="${ev.created_at}">${delta}</span>
|
||||||
|
</div>
|
||||||
|
<div class="comment">
|
||||||
|
${render_comment_body(model, ev, opts)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>`
|
</div>`
|
||||||
|
@ -337,7 +356,7 @@ function render_name(pk, profile, prefix="") {
|
||||||
<span>
|
<span>
|
||||||
${prefix}
|
${prefix}
|
||||||
<span class="username clickable" data-pubkey="${pk}"
|
<span class="username clickable" data-pubkey="${pk}"
|
||||||
onclick="show_profile('${pk}')">
|
onclick="open_profile('${pk}')">
|
||||||
${render_name_plain(profile)}
|
${render_name_plain(profile)}
|
||||||
</span>
|
</span>
|
||||||
</span>`
|
</span>`
|
||||||
|
@ -349,9 +368,13 @@ function render_deleted_name() {
|
||||||
|
|
||||||
function render_pfp(pk, profile) {
|
function render_pfp(pk, profile) {
|
||||||
const name = render_name_plain(profile)
|
const name = render_name_plain(profile)
|
||||||
return `<img class="pfp" data-pubkey="${pk}" title="${name}"
|
return `<img
|
||||||
|
class="pfp clickable"
|
||||||
|
onclick="open_profile('${pk}')"
|
||||||
|
data-pubkey="${pk}"
|
||||||
|
title="${name}"
|
||||||
onerror="this.onerror=null;this.src='${robohash(pk)}';"
|
onerror="this.onerror=null;this.src='${robohash(pk)}';"
|
||||||
src="${get_picture(pk, profile)}">`
|
src="${get_picture(pk, profile)}"/>`
|
||||||
}
|
}
|
||||||
|
|
||||||
function render_deleted_pfp() {
|
function render_deleted_pfp() {
|
||||||
|
@ -361,8 +384,7 @@ function render_deleted_pfp() {
|
||||||
</div>`
|
</div>`
|
||||||
}
|
}
|
||||||
|
|
||||||
function render_loading_spinner()
|
function render_loading_spinner() {
|
||||||
{
|
|
||||||
return `
|
return `
|
||||||
<div class="loading-events">
|
<div class="loading-events">
|
||||||
<div class="loader" title="Loading...">
|
<div class="loader" title="Loading...">
|
||||||
|
|
|
@ -13,25 +13,7 @@ function get_thread_max_depth(damus, view, root_id) {
|
||||||
return view.depths[root_id]
|
return view.depths[root_id]
|
||||||
}
|
}
|
||||||
|
|
||||||
function shouldnt_render_event(our_pk, view, ev, opts) {
|
/*function expand_thread(id, reply_id) {
|
||||||
return !opts.is_composing &&
|
|
||||||
!view.expanded.has(ev.id) &&
|
|
||||||
view.rendered.has(ev.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggle_content_warning(el) {
|
|
||||||
const id = el.id.split("_")[1]
|
|
||||||
const ev = DAMUS.all_events[id]
|
|
||||||
|
|
||||||
if (!ev) {
|
|
||||||
log_debug("could not find content-warning event", id)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
DAMUS.cw_open[id] = el.open
|
|
||||||
}
|
|
||||||
|
|
||||||
function expand_thread(id, reply_id) {
|
|
||||||
const view = get_current_view()
|
const view = get_current_view()
|
||||||
const root_id = get_thread_root_id(DAMUS, id)
|
const root_id = get_thread_root_id(DAMUS, id)
|
||||||
if (!root_id) {
|
if (!root_id) {
|
||||||
|
@ -41,7 +23,7 @@ function expand_thread(id, reply_id) {
|
||||||
view.expanded.add(reply_id)
|
view.expanded.add(reply_id)
|
||||||
view.depths[root_id] = get_thread_max_depth(DAMUS, view, root_id) + 1
|
view.depths[root_id] = get_thread_max_depth(DAMUS, view, root_id) + 1
|
||||||
redraw_events(DAMUS, view)
|
redraw_events(DAMUS, view)
|
||||||
}
|
}*/
|
||||||
|
|
||||||
function get_thread_root_id(damus, id) {
|
function get_thread_root_id(damus, id) {
|
||||||
const ev = damus.all_events[id]
|
const ev = damus.all_events[id]
|
||||||
|
@ -52,71 +34,18 @@ function get_thread_root_id(damus, id) {
|
||||||
return ev.refs && ev.refs.root
|
return ev.refs && ev.refs.root
|
||||||
}
|
}
|
||||||
|
|
||||||
function redraw_events(damus, view) {
|
function switch_view(mode, opts) {
|
||||||
//log_debug("redrawing events for", view)
|
log_warn("switch_view deprecated, use view_timeline_apply_mode");
|
||||||
view.rendered = new Set()
|
view_timeline_apply_mode(DAMUS, mode, opts);
|
||||||
const events_el = damus.view_el.querySelector(`#${view.name}-view > .events`)
|
|
||||||
events_el.innerHTML = render_events(damus, view)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function redraw_timeline_events(damus, name) {
|
function view_get_timeline_el() {
|
||||||
const view = DAMUS.views[name]
|
return find_node("#timeline");
|
||||||
const events_el = damus.view_el.querySelector(`#${name}-view > .events`)
|
|
||||||
if (view.events.length > 0) {
|
|
||||||
redraw_events(damus, view)
|
|
||||||
} else {
|
|
||||||
events_el.innerHTML = render_loading_spinner()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function switch_view(name, opts={})
|
function view_timeline_update_profiles(model, ev) {
|
||||||
{
|
|
||||||
if (name === DAMUS.current_view) {
|
|
||||||
log_debug("Not switching to '%s', we are already there", name)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const last = get_current_view()
|
|
||||||
if (!last) {
|
|
||||||
// render initial
|
|
||||||
DAMUS.current_view = name
|
|
||||||
redraw_timeline_events(DAMUS, name)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log_debug("switching to '%s' by hiding '%s'", name, DAMUS.current_view)
|
|
||||||
|
|
||||||
DAMUS.current_view = name
|
|
||||||
const current = get_current_view()
|
|
||||||
const last_el = get_view_el(last.name)
|
|
||||||
const current_el = get_view_el(current.name)
|
|
||||||
|
|
||||||
if (last_el)
|
|
||||||
last_el.classList.add("hide");
|
|
||||||
|
|
||||||
// TODO accomodate views that do not render events
|
|
||||||
// TODO find out if having multiple event divs is slow
|
|
||||||
//redraw_timeline_events(DAMUS, name)
|
|
||||||
|
|
||||||
find_node("#nav > div[data-active]").dataset.active = name;
|
|
||||||
|
|
||||||
if (current_el)
|
|
||||||
current_el.classList.remove("hide");
|
|
||||||
}
|
|
||||||
|
|
||||||
function get_current_view()
|
|
||||||
{
|
|
||||||
// TODO resolve memory & html descriptencies
|
|
||||||
// Currently there is tracking of which divs are visible in HTML/CSS and
|
|
||||||
// which is active in memory, simply resolve this by finding the visible
|
|
||||||
// element instead of tracking it in memory (or remove dom elements). This
|
|
||||||
// would simplify state tracking IMO - Thomas
|
|
||||||
return DAMUS.views[DAMUS.current_view]
|
|
||||||
}
|
|
||||||
|
|
||||||
function view_timeline_update_profiles(model, state, ev) {
|
|
||||||
let xs, html;
|
let xs, html;
|
||||||
const el = find_node("#view #home-view .events");
|
const el = view_get_timeline_el();
|
||||||
const pk = ev.pubkey;
|
const pk = ev.pubkey;
|
||||||
const p = model.profiles[pk];
|
const p = model.profiles[pk];
|
||||||
|
|
||||||
|
@ -140,8 +69,98 @@ function view_timeline_update_profiles(model, state, ev) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function view_timeline_update(model, state) {
|
function view_timeline_update_timestamps(model) {
|
||||||
const el = find_node("#view #home-view .events");
|
const el = view_get_timeline_el();
|
||||||
|
let xs = el.querySelectorAll(".timestamp");
|
||||||
|
let now = new Date().getTime();
|
||||||
|
for (const x of xs) {
|
||||||
|
let t = parseInt(x.dataset.timestamp)
|
||||||
|
x.innerText = fmt_since_str(now, t*1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function view_timeline_update_reaction(model, ev) {
|
||||||
|
// TODO loop through elements with ev reactions to and update them
|
||||||
|
}
|
||||||
|
|
||||||
|
const VM_FRIENDS = "friends";
|
||||||
|
const VM_EXPLORE = "explore";
|
||||||
|
const VM_NOTIFICATIONS = "notifications";
|
||||||
|
const VM_THREAD = "thread";
|
||||||
|
const VM_USER = "user";
|
||||||
|
// friends: mine + only events that are from my contacts
|
||||||
|
// explore: all events
|
||||||
|
// notifications: reactions & replys
|
||||||
|
// thread: all events in response to target event
|
||||||
|
// user: all events by pubkey
|
||||||
|
|
||||||
|
function view_mode_contains_event(model, ev, mode, opts={}) {
|
||||||
|
switch(mode) {
|
||||||
|
case VM_EXPLORE:
|
||||||
|
return ev.kind != KIND_REACTION;
|
||||||
|
case VM_USER:
|
||||||
|
return opts.pubkey && ev.pubkey == opts.pubkey;
|
||||||
|
case VM_FRIENDS:
|
||||||
|
return ev.pubkey == model.pubkey || contact_is_friend(model.contacts, ev.pubkey);
|
||||||
|
case VM_THREAD:
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function view_timeline_apply_mode(model, mode, opts={}) {
|
||||||
|
let xs;
|
||||||
|
const { pubkey, thread_id } = opts;
|
||||||
|
const el = view_get_timeline_el();
|
||||||
|
|
||||||
|
el.dataset.mode = mode;
|
||||||
|
switch(mode) {
|
||||||
|
case VM_THREAD:
|
||||||
|
el.dataset.threadId = thread_id;
|
||||||
|
case VM_USER:
|
||||||
|
el.dataset.pubkey = pubkey;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
delete el.dataset.threadId;
|
||||||
|
delete el.dataset.pubkey;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const names = {};
|
||||||
|
names[VM_FRIENDS] = "Home";
|
||||||
|
names[VM_EXPLORE] = "Explore";
|
||||||
|
names[VM_NOTIFICATIONS] = "Notifications";
|
||||||
|
names[VM_USER] = "Profile";
|
||||||
|
names[VM_THREAD] = "Thread";
|
||||||
|
find_node("#view header > label").innerText = mode == VM_USER ? render_name_plain(DAMUS.profiles[opts.pubkey]) : names[mode];
|
||||||
|
find_node("#nav > div[data-active]").dataset.active = names[mode].toLowerCase();
|
||||||
|
find_node("#view [role='profile-info']").classList.toggle("hide", mode != VM_USER);
|
||||||
|
find_node("#newpost").classList.toggle("hide", mode != VM_FRIENDS);
|
||||||
|
|
||||||
|
xs = el.querySelectorAll(".event");
|
||||||
|
for (const x of xs) {
|
||||||
|
let evid = x.id.substr(2);
|
||||||
|
let ev = model.all_events[evid];
|
||||||
|
x.classList.toggle("hide",
|
||||||
|
!view_mode_contains_event(model, ev, mode, opts));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* view_timeline_update iterates through invalidated event ids and either adds
|
||||||
|
* or removes them from the timeline.
|
||||||
|
*/
|
||||||
|
function view_timeline_update(model) {
|
||||||
|
const el = view_get_timeline_el();
|
||||||
|
const mode = el.dataset.mode;
|
||||||
|
const opts = {
|
||||||
|
thread_id: el.dataset.threadId,
|
||||||
|
pubkey: el.dataset.pubkey,
|
||||||
|
};
|
||||||
|
|
||||||
// for each event not rendered, go through the list and render it marking
|
// for each event not rendered, go through the list and render it marking
|
||||||
// it as rendered and adding it to the appropriate fragment. fragments are
|
// it as rendered and adding it to the appropriate fragment. fragments are
|
||||||
|
@ -151,27 +170,31 @@ function view_timeline_update(model, state) {
|
||||||
// const cache = {};
|
// const cache = {};
|
||||||
|
|
||||||
// Dumb function to insert needed events
|
// Dumb function to insert needed events
|
||||||
|
let visible_count = 0;
|
||||||
const all = model_events_arr(model);
|
const all = model_events_arr(model);
|
||||||
while (state.invalidated.length > 0) {
|
while (model.invalidated.length > 0) {
|
||||||
var evid = state.invalidated.pop();
|
var evid = model.invalidated.pop();
|
||||||
var ev = model.all_events[evid];
|
var ev = model.all_events[evid];
|
||||||
if (!event_is_renderable(ev)) {
|
if (!event_is_renderable(ev) || model_is_event_deleted(model, evid)) {
|
||||||
// TODO check deleted
|
// TODO check deleted
|
||||||
let x = find_node("#ev"+evid, el);
|
let x = find_node("#ev"+evid, el);
|
||||||
if (x) el.removeChild(x);
|
if (x) el.removeChild(x);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO if event is not viewable for page, simply hide it
|
|
||||||
|
|
||||||
// if event is in el already, do nothing or update?
|
// if event is in el already, do nothing or update?
|
||||||
let ev_el = find_node("#ev"+evid, el);
|
let ev_el = find_node("#ev"+evid, el);
|
||||||
if (ev_el) {
|
if (ev_el) {
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
let div = document.createElement("div");
|
let div = document.createElement("div");
|
||||||
div.innerHTML = render_event2(model, ev, {});
|
div.innerHTML = render_event(model, ev, {});
|
||||||
ev_el = div.firstChild;
|
ev_el = div.firstChild;
|
||||||
|
if (!view_mode_contains_event(model, ev, mode, opts)) {
|
||||||
|
ev_el.classList.add("hide");
|
||||||
|
} else {
|
||||||
|
visible_count++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// find prior event element and insert it before that
|
// find prior event element and insert it before that
|
||||||
|
@ -187,6 +210,9 @@ function view_timeline_update(model, state) {
|
||||||
el.insertBefore(ev_el, prior_el);
|
el.insertBefore(ev_el, prior_el);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (visible_count > 0)
|
||||||
|
find_node("#view .loading-events").classList.add("hide");
|
||||||
}
|
}
|
||||||
|
|
||||||
function event_is_renderable(ev={}) {
|
function event_is_renderable(ev={}) {
|
||||||
|
|
|
@ -172,17 +172,16 @@ async function do_send_reply() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function reply_to(evid) {
|
function reply_to(evid) {
|
||||||
|
const ev = DAMUS.all_events[evid]
|
||||||
const modal = document.querySelector("#reply-modal")
|
const modal = document.querySelector("#reply-modal")
|
||||||
const replybox = modal.querySelector("#reply-content")
|
const replybox = modal.querySelector("#reply-content")
|
||||||
modal.classList.remove("closed")
|
|
||||||
const replying_to = modal.querySelector("#replying-to")
|
const replying_to = modal.querySelector("#replying-to")
|
||||||
|
|
||||||
replying_to.dataset.evid = evid
|
replying_to.dataset.evid = evid
|
||||||
|
replying_to.innerHTML = render_event_nointeract(DAMUS, ev, {
|
||||||
const ev = DAMUS.all_events[evid]
|
is_composing: true,
|
||||||
const view = get_current_view()
|
nobar: true
|
||||||
replying_to.innerHTML = render_event(DAMUS, view, ev, {is_composing: true, nobar: true, max_depth: 1})
|
});
|
||||||
|
modal.classList.remove("closed")
|
||||||
replybox.focus()
|
replybox.focus()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,7 +189,7 @@ function redraw_my_pfp(model, force = false) {
|
||||||
const p = model.profiles[model.pubkey]
|
const p = model.profiles[model.pubkey]
|
||||||
if (!p) return;
|
if (!p) return;
|
||||||
const html = render_pfp(model.pubkey, p);
|
const html = render_pfp(model.pubkey, p);
|
||||||
const el = document.querySelector(".my-userpic")
|
const el = document.querySelector(".my-userpic");
|
||||||
if (!force && el.dataset.loaded) return;
|
if (!force && el.dataset.loaded) return;
|
||||||
el.dataset.loaded = true;
|
el.dataset.loaded = true;
|
||||||
el.innerHTML = html;
|
el.innerHTML = html;
|
||||||
|
@ -261,4 +260,35 @@ function get_privkey() {
|
||||||
return privkey
|
return privkey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function open_thread(thread_id) {
|
||||||
|
view_timeline_apply_mode(DAMUS, VM_THREAD, { thread_id });
|
||||||
|
}
|
||||||
|
|
||||||
|
function open_profile(pubkey) {
|
||||||
|
view_timeline_apply_mode(DAMUS, VM_USER, { pubkey });
|
||||||
|
|
||||||
|
const profile = DAMUS.profiles[pubkey];
|
||||||
|
const el = find_node("[role='profile-info']");
|
||||||
|
// TODO show loading indicator then render
|
||||||
|
|
||||||
|
find_node("[role='profile-image']", el).src = get_picture(pubkey, profile);
|
||||||
|
find_nodes("[role='profile-name']", el).forEach(el => {
|
||||||
|
el.innerText = render_name_plain(profile);
|
||||||
|
});
|
||||||
|
|
||||||
|
const el_nip5 = find_node("[role='profile-nip5']", el)
|
||||||
|
el_nip5.innerText = profile.nip05;
|
||||||
|
el_nip5.classList.toggle("hide", !profile.nip05);
|
||||||
|
|
||||||
|
const el_desc = find_node("[role='profile-desc']", el)
|
||||||
|
el_desc.innerHTML = newlines_to_br(profile.about);
|
||||||
|
el_desc.classList.toggle("hide", !profile.about);
|
||||||
|
|
||||||
|
find_node("button[role='copy-pk']", el).dataset.pk = pubkey;
|
||||||
|
|
||||||
|
const btn_follow = find_node("button[role='follow-user']", el)
|
||||||
|
btn_follow.dataset.pk = pubkey;
|
||||||
|
// TODO check follow status
|
||||||
|
btn_follow.innerText = contact_is_friend(DAMUS.contacts, pubkey) ? "Unfollow" : "Follow";
|
||||||
|
btn_follow.classList.toggle("hide", pubkey == DAMUS.pubkey);
|
||||||
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ function safe_parse_json(data, message) {
|
||||||
let value = undefined;
|
let value = undefined;
|
||||||
try {
|
try {
|
||||||
value = JSON.parse(data);
|
value = JSON.parse(data);
|
||||||
} catch (e) {
|
} catch (err) {
|
||||||
log_error(`${message} : unable to parse JSON`, err, data);
|
log_error(`${message} : unable to parse JSON`, err, data);
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
|
@ -151,6 +151,11 @@ function difficulty_to_prefix(d) {
|
||||||
/* time_delta returns a string of the time of current since previous.
|
/* time_delta returns a string of the time of current since previous.
|
||||||
*/
|
*/
|
||||||
function time_delta(current, previous) {
|
function time_delta(current, previous) {
|
||||||
|
log_warn("time_delta deprecated, use fmt_since_str");
|
||||||
|
fmt_since_str(current, previous);
|
||||||
|
}
|
||||||
|
|
||||||
|
function fmt_since_str(current, previous) {
|
||||||
var msPerMinute = 60 * 1000;
|
var msPerMinute = 60 * 1000;
|
||||||
var msPerHour = msPerMinute * 60;
|
var msPerHour = msPerMinute * 60;
|
||||||
var msPerDay = msPerHour * 24;
|
var msPerDay = msPerHour * 24;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue