Notifications!

Plus minor bug fixes (like saving not working after first shot). This
needs to address the favicon issue still, but notifications are largely
working.
This commit is contained in:
Thomas Mathews 2022-12-27 13:57:54 -08:00
parent 5895e0f052
commit 51ab5aae2a
5 changed files with 98 additions and 48 deletions

View file

@ -59,7 +59,7 @@ async function contacts_load(model) {
} else {
db.close();
resolve();
log_debug("contacts loaded successfully");
log_debug(`contacts loaded successfully ${model.contacts.friends.size}`);
}
}
cursor.onerror = (ev) => {
@ -75,13 +75,15 @@ async function contacts_load(model) {
async function dbcall(fn) {
return new Promise((resolve, reject) => {
var open = indexedDB.open("damus", 4);
var open = indexedDB.open("damus", 5);
open.onupgradeneeded = (ev) => {
const db = ev.target.result;
if (!db.objectStoreNames.contains("friends"))
db.createObjectStore("friends", {keyPath: "pubkey"});
if (!db.objectStoreNames.contains("events"))
db.createObjectStore("events", {keyPath: "id"});
if (!db.objectStoreNames.contains("settings"))
db.createObjectStore("settings", {keyPath: "pubkey"});
};
open.onsuccess = (ev) => {
fn(ev, resolve, reject);
@ -94,7 +96,7 @@ async function dbcall(fn) {
async function dbclear() {
function _dbclear(ev, resolve, reject) {
const stores = ["friends", "events"];
const stores = ["friends", "events", "settings"];
const db = ev.target.result;
const tx = db.transaction(stores, "readwrite");
tx.oncomplete = (ev) => {
@ -109,7 +111,6 @@ async function dbclear() {
};
for (const store of stores) {
tx.objectStore(store).clear();
tx.objectStore(store).clear();
}
}
return dbcall(_dbclear);

View file

@ -76,11 +76,9 @@ async function webapp_init() {
init_message_textareas();
view_show_spinner(true);
redraw_my_pfp(model);
document.addEventListener('visibilitychange', () => {
update_title(model);
});
// Load our contacts first
// Load data from storage
await model_load_settings(model);
let err;
err = await contacts_load(model);
if (err) {
@ -131,9 +129,11 @@ function on_timer_invalidations() {
function on_timer_save() {
setTimeout(() => {
model_save_events(DAMUS);
contacts_save(DAMUS.contacts);
on_timer_invalidations();
const model = DAMUS;
model_save_events(model);
model_save_settings(model);
contacts_save(model.contacts);
on_timer_save();
}, 10 * 1000);
}

View file

@ -283,6 +283,55 @@ function test_model_events_arr() {
}
}
async function model_save_settings(model) {
function _settings_save(ev, resolve, reject) {
const db = ev.target.result;
const tx = db.transaction("settings", "readwrite");
const store = tx.objectStore("settings");
tx.oncomplete = (ev) => {
db.close();
resolve();
log_debug("settings saved");
};
tx.onerror = (ev) => {
db.close();
log_error("failed to save events");
reject(ev);
};
store.clear().onsuccess = () => {
store.put({
pubkey: model.pubkey,
notifications_last_viewed: model.notifications.last_viewed,
});
};
}
return dbcall(_settings_save);
}
async function model_load_settings(model) {
function _settings_load(ev, resolve, reject) {
const db = ev.target.result;
const tx = db.transaction("settings", "readonly");
const store = tx.objectStore("settings");
const req = store.get(model.pubkey);
req.onsuccess = (ev) => {
const settings = ev.target.result;
if (settings) {
model.notifications.last_viewed = settings.notifications_last_viewed;
}
db.close();
resolve();
log_debug("Successfully loaded events");
}
req.onerror = (ev) => {
db.close();
reject(ev);
log_error("Could not load settings.");
};
}
return dbcall(_settings_load);
}
async function model_save_events(model) {
function _events_save(ev, resolve, reject) {
const db = ev.target.result;
@ -336,11 +385,12 @@ async function model_load_events(model, fn) {
function new_model() {
return {
all_events: {}, // our master list of all events
done_init: {},
notifications: 0,
notifications: {
last_viewed: 0, // time since last looking at notifications
count: 0, // the number not seen since last looking
},
max_depth: 2,
reactions_to: {},
chatrooms: {},
unknown_ids: {},
unknown_pks: {},
@ -348,7 +398,6 @@ function new_model() {
deletions: {},
deleted: {},
last_event_of_kind: {},
pow: 0, // pow difficulty target
profiles: {}, // pubkey => profile data
profile_events: {}, // pubkey => event id - use with all_events

View file

@ -28,6 +28,11 @@ function view_timeline_apply_mode(model, mode, opts={}) {
view_show_spinner(true);
fetch_profile(pubkey, model.pool);
}
if (mode == VM_NOTIFICATIONS) {
model.notifications.count = 0;
model.notifications.last_viewed = new_creation_time();
update_notifications(model);
}
el.dataset.mode = mode;
switch(mode) {
@ -99,8 +104,8 @@ function view_show_spinner(show=true) {
find_node("#view .loading-events").classList.toggle("hide", !show);
}
/* view_timeline_update iterates through invalidated event ids and either adds
* or removes them from the timeline.
/* view_timeline_update iterates through invalidated event ids and updates the
* state of the timeline and other factors such as notifications, etc.
*/
function view_timeline_update(model) {
const el = view_get_timeline_el();
@ -111,6 +116,7 @@ function view_timeline_update(model) {
};
let count = 0;
let ncount = 0;
const latest_ev = el.firstChild ?
model.all_events[el.firstChild.id.slice(2)] : undefined;
const all = model_events_arr(model);
@ -140,22 +146,34 @@ function view_timeline_update(model) {
continue;
}
// Increase notification count if needed
if (event_refs_pubkey(ev, model.pubkey) &&
ev.created_at > model.notifications.last_viewed) {
ncount++;
}
// If the new element is newer than the latest & is viewable then
// we want to increase the count of how many to add to view
if (event_cmp_created(ev, latest_ev) >= 0 && view_mode_contains_event(model, ev, mode, opts)) {
if (event_cmp_created(ev, latest_ev) >= 0 &&
view_mode_contains_event(model, ev, mode, opts)) {
count++;
}
}
model.invalidated = model.invalidated.concat(left_overs);
// If there are new things to show on our current view lets do it
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) {
view_timeline_show_new(model);
}
view_set_show_count(count, true, false);
}
// Update notification markers and count
if (ncount > 0) {
log_debug(`new notis ${ncount}`);
model.notifications.count += ncount;
update_notifications(model);
}
}
function view_set_show_count(count, add=false, hide=false) {
@ -309,18 +327,6 @@ function get_thread_max_depth(damus, view, root_id) {
return view.depths[root_id]
}
/*function expand_thread(id, reply_id) {
const view = get_current_view()
const root_id = get_thread_root_id(DAMUS, id)
if (!root_id) {
log_debug("could not get root_id for", DAMUS.all_events[id])
return
}
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]
if (!ev) {

View file

@ -47,7 +47,8 @@ function init_message_textareas() {
}
// update_notification_markers will find all markers and hide or show them
// based on the passed in state of 'active'.
// based on the passed in state of 'active'. This applies to the navigation
// icons.
function update_notification_markers(active) {
let els = document.querySelectorAll(".new-notifications")
for (const el of els) {
@ -214,8 +215,7 @@ function redraw_my_pfp(model) {
el.innerHTML = html;
}
function update_favicon(path)
{
function update_favicon(path) {
let link = document.querySelector("link[rel~='icon']");
const head = document.getElementsByTagName('head')[0]
@ -228,21 +228,15 @@ function update_favicon(path)
link.href = path;
}
// update_title updates the document title & visual indicators based on if the
// update_notifications updates the document title & visual indicators based on if the
// number of notifications that are unseen by the user.
function update_title(model) {
// TODO rename update_title to update_notification_state or similar
// TODO only clear notifications once they have seen all targeted events
if (document.visibilityState === 'visible') {
model.notifications = 0
}
const num = model.notifications
const has_notes = num !== 0
document.title = has_notes ? `(${num}) Yo Sup` : "Yo Sup";
function update_notifications(model) {
const { count } = model.notifications;
const suffix = "Yo Sup";
document.title = count ? `(${count}) ${suffix}` : suffix;
// TODO I broke the favicons. I will fix with notications update
update_favicon(has_notes ? "img/damus_notif.svg" : "img/damus.svg");
update_notification_markers(has_notes)
//update_favicon(has_notes ? "img/damus_notif.svg" : "img/damus.svg");
update_notification_markers(count);
}
async function get_pubkey(use_prompt=true) {