Almost got everythign working.
Issues left to resolve: * Removing a reaction doesn't properly remove it from UI, but the event is recorded correctly. * Since contacts are not being saved there will be issues with "following" users and you could overwrite your follower's list with an empty list. * Caching is no longer working. * I skipped chat room implementation. * Rendering shared event's doesn't work and needs to be revised.
This commit is contained in:
parent
e68a022952
commit
d02992c7e6
17 changed files with 1383 additions and 1131 deletions
|
@ -1,5 +1,215 @@
|
|||
function get_view_el(name) {
|
||||
return DAMUS.view_el.querySelector(`#${name}-view`)
|
||||
const VM_FRIENDS = "friends"; // mine + only events that are from my contacts
|
||||
const VM_EXPLORE = "explore"; // all events
|
||||
const VM_NOTIFICATIONS = "notifications"; // reactions & replys
|
||||
const VM_THREAD = "thread"; // all events in response to target event
|
||||
const VM_USER = "user"; // all events by pubkey
|
||||
|
||||
function view_get_timeline_el() {
|
||||
return find_node("#timeline");
|
||||
}
|
||||
|
||||
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";
|
||||
|
||||
// Do some visual updates
|
||||
find_node("#view header > label").innerText = mode == VM_USER ?
|
||||
fmt_profile_name(DAMUS.profiles[opts.pubkey], fmt_pubkey(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);
|
||||
find_node("#timeline").classList.toggle("reverse", mode == VM_THREAD);
|
||||
|
||||
// Show or hide all applicable events related to the mode.
|
||||
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));
|
||||
}
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
/* 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
|
||||
// it as rendered and adding it to the appropriate fragment. fragments are
|
||||
// created based on slices in the existing timeline. fragments are started
|
||||
// at the previous event
|
||||
// const fragments = {};
|
||||
// const cache = {};
|
||||
|
||||
// Dumb function to insert needed events
|
||||
let visible_count = 0;
|
||||
const all = model_events_arr(model);
|
||||
while (model.invalidated.length > 0) {
|
||||
var evid = model.invalidated.pop();
|
||||
var ev = model.all_events[evid];
|
||||
if (!event_is_renderable(ev) || model_is_event_deleted(model, evid)) {
|
||||
let x = find_node("#ev"+evid, el);
|
||||
if (x) el.removeChild(x);
|
||||
continue;
|
||||
}
|
||||
|
||||
// if event is in el already, do nothing or update?
|
||||
let ev_el = find_node("#ev"+evid, el);
|
||||
if (ev_el) {
|
||||
continue;
|
||||
} else {
|
||||
let div = document.createElement("div");
|
||||
div.innerHTML = render_event(model, ev, {});
|
||||
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
|
||||
let prior_el;
|
||||
let prior_idx = arr_bsearch_insert(all, ev, event_cmp_created);
|
||||
while (prior_idx > 0 && !prior_el) {
|
||||
prior_el = find_node("#ev"+all[prior_idx].id, el);
|
||||
prior_idx--;
|
||||
}
|
||||
if (!prior_el) {
|
||||
el.appendChild(ev_el);
|
||||
} else {
|
||||
el.insertBefore(ev_el, prior_el);
|
||||
}
|
||||
}
|
||||
|
||||
if (visible_count > 0)
|
||||
find_node("#view .loading-events").classList.add("hide");
|
||||
}
|
||||
|
||||
function view_timeline_update_profiles(model, ev) {
|
||||
let xs, html;
|
||||
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);
|
||||
}
|
||||
|
||||
// Update displayed names
|
||||
xs = el.querySelectorAll(`.username[data-pubkey='${pk}']`)
|
||||
html = fmt_profile_name(p, fmt_pubkey(pk));
|
||||
for (const x of xs) {
|
||||
x.innerText = html;
|
||||
}
|
||||
|
||||
// Update profile pictures
|
||||
xs = el.querySelectorAll(`img.pfp[data-pubkey='${pk}']`);
|
||||
html = get_picture(pk, p)
|
||||
for (const x of xs) {
|
||||
x.src = html;
|
||||
}
|
||||
}
|
||||
|
||||
function view_timeline_update_timestamps(model) {
|
||||
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) {
|
||||
let el;
|
||||
const o = event_parse_reaction(ev);
|
||||
if (!o) {
|
||||
return;
|
||||
}
|
||||
const ev_id = o.e;
|
||||
const root = find_node(`#ev${ev_id}`);
|
||||
if (!root) {
|
||||
// It's possible the event didn't get rendered yet from the
|
||||
// invalidation stack. In which case emojis will get rendered then.
|
||||
return;
|
||||
}
|
||||
|
||||
// Update reaction groups
|
||||
el = find_node(`.reactions`, root);
|
||||
el.innerHTML = render_reactions_inner(model, model.all_events[ev_id]);
|
||||
|
||||
if (ev.pubkey == model.pubkey) {
|
||||
const reaction = model_get_reacts_to(model, model.pubkey, ev_id, R_HEART);
|
||||
const liked = !!reaction;
|
||||
const img = find_node("button.icon.heart > img", root);
|
||||
const btn = find_node("button.icon.heart", root)
|
||||
btn.classList.toggle("liked", liked);
|
||||
btn.title = liked ? "Unlike" : "Like";
|
||||
btn.disabled = false;
|
||||
btn.dataset.liked = liked ? "yes" : "no";
|
||||
btn.dataset.reactionId = liked ? reaction.id : "";
|
||||
img.classList.toggle("dark-noinvert", liked);
|
||||
img.src = liked ? IMG_EVENT_LIKED : IMG_EVENT_LIKE;
|
||||
}
|
||||
}
|
||||
|
||||
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 event_is_renderable(ev={}) {
|
||||
return ev.kind == KIND_NOTE;
|
||||
return ev.kind == KIND_NOTE ||
|
||||
ev.kind == KIND_REACTION ||
|
||||
ev.kind == KIND_DELETE;
|
||||
}
|
||||
|
||||
function get_default_max_depth(damus, view) {
|
||||
|
@ -13,25 +223,7 @@ function get_thread_max_depth(damus, view, root_id) {
|
|||
return view.depths[root_id]
|
||||
}
|
||||
|
||||
function shouldnt_render_event(our_pk, view, ev, opts) {
|
||||
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) {
|
||||
/*function expand_thread(id, reply_id) {
|
||||
const view = get_current_view()
|
||||
const root_id = get_thread_root_id(DAMUS, id)
|
||||
if (!root_id) {
|
||||
|
@ -41,7 +233,7 @@ function expand_thread(id, reply_id) {
|
|||
view.expanded.add(reply_id)
|
||||
view.depths[root_id] = get_thread_max_depth(DAMUS, view, root_id) + 1
|
||||
redraw_events(DAMUS, view)
|
||||
}
|
||||
}*/
|
||||
|
||||
function get_thread_root_id(damus, id) {
|
||||
const ev = damus.all_events[id]
|
||||
|
@ -52,65 +244,8 @@ function get_thread_root_id(damus, id) {
|
|||
return ev.refs && ev.refs.root
|
||||
}
|
||||
|
||||
function redraw_events(damus, view) {
|
||||
//log_debug("redrawing events for", view)
|
||||
view.rendered = new Set()
|
||||
const events_el = damus.view_el.querySelector(`#${view.name}-view > .events`)
|
||||
events_el.innerHTML = render_events(damus, view)
|
||||
}
|
||||
|
||||
function redraw_timeline_events(damus, name) {
|
||||
const view = DAMUS.views[name]
|
||||
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={})
|
||||
{
|
||||
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 switch_view(mode, opts) {
|
||||
log_warn("switch_view deprecated, use view_timeline_apply_mode");
|
||||
view_timeline_apply_mode(DAMUS, mode, opts);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue