diff --git a/web/public/sw.js b/web/public/sw.js index 39d60a8a..6e834367 100644 --- a/web/public/sw.js +++ b/web/public/sw.js @@ -3,7 +3,7 @@ import { cleanupOutdatedCaches, createHandlerBoundToURL, precacheAndRoute } from import { NavigationRoute, registerRoute } from "workbox-routing"; import { NetworkFirst } from "workbox-strategies"; -import { getDbAsync } from "../src/app/getDb"; +import { dbAsync } from "../src/app/db"; import { formatMessage, formatTitleWithDefault } from "../src/app/notificationUtils"; // See WebPushWorker, this is to play a sound on supported browsers, @@ -44,8 +44,7 @@ self.addEventListener("push", (event) => { const { subscription_id: subscriptionId, message } = data; broadcastChannel.postMessage(message); - const db = await getDbAsync(); - + const db = await dbAsync(); const image = message.attachment?.name.match(/\.(png|jpe?g|gif|webp)$/i) ? message.attachment.url : undefined; const actions = message.actions diff --git a/web/src/app/Prefs.js b/web/src/app/Prefs.js index 1f1a6d80..ac1d82db 100644 --- a/web/src/app/Prefs.js +++ b/web/src/app/Prefs.js @@ -1,4 +1,4 @@ -import getDb from "./getDb"; +import db from "./db"; class Prefs { constructor(db) { @@ -42,5 +42,5 @@ class Prefs { } } -const prefs = new Prefs(getDb()); +const prefs = new Prefs(db()); export default prefs; diff --git a/web/src/app/Session.js b/web/src/app/Session.js index 8affa53c..bc50864b 100644 --- a/web/src/app/Session.js +++ b/web/src/app/Session.js @@ -1,5 +1,9 @@ import sessionReplica from "./SessionReplica"; +/** + * Manages the logged-in user's session and access token. + * The session replica is stored in IndexedDB so that the service worker can access it. + */ class Session { constructor(replica) { this.replica = replica; @@ -8,14 +12,12 @@ class Session { store(username, token) { localStorage.setItem("user", username); localStorage.setItem("token", token); - this.replica.store(username, token); } reset() { localStorage.removeItem("user"); localStorage.removeItem("token"); - this.replica.reset(); } diff --git a/web/src/app/SessionReplica.js b/web/src/app/SessionReplica.js index 808833f6..a68d4c70 100644 --- a/web/src/app/SessionReplica.js +++ b/web/src/app/SessionReplica.js @@ -1,24 +1,21 @@ import Dexie from "dexie"; -// Store to IndexedDB as well so that the -// service worker can access it -// TODO: Probably make everything depend on this and not use localStorage, -// but that's a larger refactoring effort for another PR - +/** + * Replica of the session in IndexedDB. This is used by the service + * worker to access the session. This is a bit of a hack. + */ class SessionReplica { constructor() { const db = new Dexie("session-replica"); - db.version(1).stores({ - keyValueStore: "&key", + kv: "&key", }); - this.db = db; } async store(username, token) { try { - await this.db.keyValueStore.bulkPut([ + await this.db.kv.bulkPut([ { key: "user", value: username }, { key: "token", value: token }, ]); @@ -36,7 +33,7 @@ class SessionReplica { } async username() { - return (await this.db.keyValueStore.get({ key: "user" }))?.value; + return (await this.db.kv.get({ key: "user" }))?.value; } } diff --git a/web/src/app/SubscriptionManager.js b/web/src/app/SubscriptionManager.js index 592db6f9..88b95e7b 100644 --- a/web/src/app/SubscriptionManager.js +++ b/web/src/app/SubscriptionManager.js @@ -1,7 +1,7 @@ import api from "./Api"; import notifier from "./Notifier"; import prefs from "./Prefs"; -import getDb from "./getDb"; +import db from "./db"; import { topicUrl } from "./utils"; class SubscriptionManager { @@ -244,4 +244,4 @@ class SubscriptionManager { } } -export default new SubscriptionManager(getDb()); +export default new SubscriptionManager(db()); diff --git a/web/src/app/UserManager.js b/web/src/app/UserManager.js index a3dee0a3..412e41da 100644 --- a/web/src/app/UserManager.js +++ b/web/src/app/UserManager.js @@ -1,4 +1,4 @@ -import getDb from "./getDb"; +import db from "./db"; import session from "./Session"; class UserManager { @@ -47,4 +47,4 @@ class UserManager { } } -export default new UserManager(getDb()); +export default new UserManager(db()); diff --git a/web/src/app/WebPushWorker.js b/web/src/app/WebPushWorker.js index 4ba0f9e1..b0d319c7 100644 --- a/web/src/app/WebPushWorker.js +++ b/web/src/app/WebPushWorker.js @@ -26,7 +26,7 @@ export const useWebPushUpdateWorker = () => { }, [topics, lastTopics]); }; -const intervalMillis = 5 * 60 * 1_000; // 5 minutes +const intervalMillis = 13 * 60 * 1_000; // 13 minutes const updateIntervalMillis = 60 * 60 * 1_000; // 1 hour class WebPushRefreshWorker { diff --git a/web/src/app/getDb.js b/web/src/app/db.js similarity index 64% rename from web/src/app/getDb.js rename to web/src/app/db.js index e52932c7..6a192011 100644 --- a/web/src/app/getDb.js +++ b/web/src/app/db.js @@ -8,12 +8,11 @@ import sessionReplica from "./SessionReplica"; // Notes: // - As per docs, we only declare the indexable columns, not all columns -const getDbBase = (username) => { - // The IndexedDB database name is based on the logged-in user - const dbName = username ? `ntfy-${username}` : "ntfy"; +const createDatabase = (username) => { + const dbName = username ? `ntfy-${username}` : "ntfy"; // IndexedDB database is based on the logged-in user const db = new Dexie(dbName); - db.version(2).stores({ + db.version(1).stores({ subscriptions: "&id,baseUrl,[baseUrl+mutedUntil]", notifications: "&id,subscriptionId,time,new,[subscriptionId+new]", // compound key for query performance users: "&baseUrl,username", @@ -23,12 +22,11 @@ const getDbBase = (username) => { return db; }; -export const getDbAsync = async () => { +export const dbAsync = async () => { const username = await sessionReplica.username(); - - return getDbBase(username); + return createDatabase(username); }; -const getDb = () => getDbBase(session.username()); +export const db = () => createDatabase(session.username()); -export default getDb; +export default db; diff --git a/web/src/components/Account.jsx b/web/src/components/Account.jsx index 400ca08c..47449515 100644 --- a/web/src/components/Account.jsx +++ b/web/src/components/Account.jsx @@ -48,7 +48,7 @@ import routes from "./routes"; import { formatBytes, formatShortDate, formatShortDateTime, openUrl } from "../app/utils"; import accountApi, { LimitBasis, Role, SubscriptionInterval, SubscriptionStatus } from "../app/AccountApi"; import { Pref, PrefGroup } from "./Pref"; -import getDb from "../app/getDb"; +import db from "../app/db"; import UpgradeDialog from "./UpgradeDialog"; import { AccountContext } from "./App"; import DialogFooter from "./DialogFooter"; @@ -1078,7 +1078,7 @@ const DeleteAccountDialog = (props) => { const handleSubmit = async () => { try { await accountApi.delete(password); - await getDb().delete(); + await db().delete(); console.debug(`[Account] Account deleted`); session.resetAndRedirect(routes.app); } catch (e) { diff --git a/web/src/components/ActionBar.jsx b/web/src/components/ActionBar.jsx index f0b031a3..a8cb18ce 100644 --- a/web/src/components/ActionBar.jsx +++ b/web/src/components/ActionBar.jsx @@ -13,7 +13,7 @@ import session from "../app/Session"; import logo from "../img/ntfy.svg"; import subscriptionManager from "../app/SubscriptionManager"; import routes from "./routes"; -import getDb from "../app/getDb"; +import db from "../app/db"; import { topicDisplayName } from "../app/utils"; import Navigation from "./Navigation"; import accountApi from "../app/AccountApi"; @@ -121,7 +121,7 @@ const ProfileIcon = () => { const handleLogout = async () => { try { await accountApi.logout(); - await getDb().delete(); + await db().delete(); } finally { session.resetAndRedirect(routes.app); }