mirror of
https://github.com/binwiederhier/ntfy.git
synced 2025-09-08 12:57:55 +02:00
Switch everything to Dexie.js
This commit is contained in:
parent
39f4613719
commit
349872bdb3
16 changed files with 243 additions and 316 deletions
|
@ -10,11 +10,12 @@ class ConnectionManager {
|
|||
return;
|
||||
}
|
||||
console.log(`[ConnectionManager] Refreshing connections`);
|
||||
const subscriptionIds = subscriptions.ids();
|
||||
const subscriptionIds = subscriptions.map(s => s.id);
|
||||
const deletedIds = Array.from(this.connections.keys()).filter(id => !subscriptionIds.includes(id));
|
||||
|
||||
// Create and add new connections
|
||||
subscriptions.forEach((id, subscription) => {
|
||||
subscriptions.forEach(subscription => {
|
||||
const id = subscription.id;
|
||||
const added = !this.connections.get(id)
|
||||
if (added) {
|
||||
const baseUrl = subscription.baseUrl;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import {formatMessage, formatTitleWithFallback, topicShortUrl} from "./utils";
|
||||
import repository from "./Repository";
|
||||
import prefs from "./Prefs";
|
||||
|
||||
class NotificationManager {
|
||||
async notify(subscription, notification, onClickFallback) {
|
||||
|
@ -7,8 +7,11 @@ class NotificationManager {
|
|||
if (!shouldNotify) {
|
||||
return;
|
||||
}
|
||||
const shortUrl = topicShortUrl(subscription.baseUrl, subscription.topic);
|
||||
const message = formatMessage(notification);
|
||||
const title = formatTitleWithFallback(notification, topicShortUrl(subscription.baseUrl, subscription.topic));
|
||||
const title = formatTitleWithFallback(notification, shortUrl);
|
||||
|
||||
console.log(`[NotificationManager, ${shortUrl}] Displaying notification ${notification.id}: ${message}`);
|
||||
const n = new Notification(title, {
|
||||
body: message,
|
||||
icon: '/static/img/favicon.png'
|
||||
|
@ -35,7 +38,7 @@ class NotificationManager {
|
|||
|
||||
async shouldNotify(subscription, notification) {
|
||||
const priority = (notification.priority) ? notification.priority : 3;
|
||||
const minPriority = await repository.getMinPriority();
|
||||
const minPriority = await prefs.minPriority();
|
||||
if (priority < minPriority) {
|
||||
return false;
|
||||
}
|
||||
|
|
49
web/src/app/Poller.js
Normal file
49
web/src/app/Poller.js
Normal file
|
@ -0,0 +1,49 @@
|
|||
import db from "./db";
|
||||
import api from "./Api";
|
||||
|
||||
const delayMillis = 3000; // 3 seconds
|
||||
const intervalMillis = 300000; // 5 minutes
|
||||
|
||||
class Poller {
|
||||
constructor() {
|
||||
this.timer = null;
|
||||
}
|
||||
|
||||
startWorker() {
|
||||
if (this.timer !== null) {
|
||||
return;
|
||||
}
|
||||
this.timer = setInterval(() => this.pollAll(), intervalMillis);
|
||||
setTimeout(() => this.pollAll(), delayMillis);
|
||||
}
|
||||
|
||||
async pollAll() {
|
||||
console.log(`[Poller] Polling all subscriptions`);
|
||||
const subscriptions = await db.subscriptions.toArray();
|
||||
for (const s of subscriptions) {
|
||||
try {
|
||||
await this.poll(s);
|
||||
} catch (e) {
|
||||
console.log(`[Poller] Error polling ${s.id}`, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async poll(subscription) {
|
||||
console.log(`[Poller] Polling ${subscription.id}`);
|
||||
|
||||
const since = subscription.last;
|
||||
const notifications = await api.poll(subscription.baseUrl, subscription.topic, since);
|
||||
if (!notifications || notifications.length === 0) {
|
||||
console.log(`[Poller] No new notifications found for ${subscription.id}`);
|
||||
return;
|
||||
}
|
||||
const notificationsWithSubscriptionId = notifications
|
||||
.map(notification => ({ ...notification, subscriptionId: subscription.id }));
|
||||
await db.notifications.bulkPut(notificationsWithSubscriptionId); // FIXME
|
||||
await db.subscriptions.update(subscription.id, {last: notifications.at(-1).id}); // FIXME
|
||||
};
|
||||
}
|
||||
|
||||
const poller = new Poller();
|
||||
export default poller;
|
33
web/src/app/Prefs.js
Normal file
33
web/src/app/Prefs.js
Normal file
|
@ -0,0 +1,33 @@
|
|||
import db from "./db";
|
||||
|
||||
class Prefs {
|
||||
async setSelectedSubscriptionId(selectedSubscriptionId) {
|
||||
db.prefs.put({key: 'selectedSubscriptionId', value: selectedSubscriptionId});
|
||||
}
|
||||
|
||||
async selectedSubscriptionId() {
|
||||
const selectedSubscriptionId = await db.prefs.get('selectedSubscriptionId');
|
||||
return (selectedSubscriptionId) ? selectedSubscriptionId.value : "";
|
||||
}
|
||||
|
||||
async setMinPriority(minPriority) {
|
||||
db.prefs.put({key: 'minPriority', value: minPriority.toString()});
|
||||
}
|
||||
|
||||
async minPriority() {
|
||||
const minPriority = await db.prefs.get('minPriority');
|
||||
return (minPriority) ? Number(minPriority.value) : 1;
|
||||
}
|
||||
|
||||
async setDeleteAfter(deleteAfter) {
|
||||
db.prefs.put({key:'deleteAfter', value: deleteAfter.toString()});
|
||||
}
|
||||
|
||||
async deleteAfter() {
|
||||
const deleteAfter = await db.prefs.get('deleteAfter');
|
||||
return (deleteAfter) ? Number(deleteAfter.value) : 604800; // Default is one week
|
||||
}
|
||||
}
|
||||
|
||||
const prefs = new Prefs();
|
||||
export default prefs;
|
39
web/src/app/Pruner.js
Normal file
39
web/src/app/Pruner.js
Normal file
|
@ -0,0 +1,39 @@
|
|||
import db from "./db";
|
||||
import prefs from "./Prefs";
|
||||
|
||||
const delayMillis = 15000; // 15 seconds
|
||||
const intervalMillis = 1800000; // 30 minutes
|
||||
|
||||
class Pruner {
|
||||
constructor() {
|
||||
this.timer = null;
|
||||
}
|
||||
|
||||
startWorker() {
|
||||
if (this.timer !== null) {
|
||||
return;
|
||||
}
|
||||
this.timer = setInterval(() => this.prune(), intervalMillis);
|
||||
setTimeout(() => this.prune(), delayMillis);
|
||||
}
|
||||
|
||||
async prune() {
|
||||
const deleteAfterSeconds = await prefs.deleteAfter();
|
||||
const pruneThresholdTimestamp = Math.round(Date.now()/1000) - deleteAfterSeconds;
|
||||
if (deleteAfterSeconds === 0) {
|
||||
console.log(`[Pruner] Pruning is disabled. Skipping.`);
|
||||
return;
|
||||
}
|
||||
console.log(`[Pruner] Pruning notifications older than ${deleteAfterSeconds}s (timestamp ${pruneThresholdTimestamp})`);
|
||||
try {
|
||||
await db.notifications
|
||||
.where("time").below(pruneThresholdTimestamp)
|
||||
.delete();
|
||||
} catch (e) {
|
||||
console.log(`[Pruner] Error pruning old subscriptions`, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const pruner = new Pruner();
|
||||
export default pruner;
|
|
@ -1,83 +0,0 @@
|
|||
import Subscription from "./Subscription";
|
||||
import Subscriptions from "./Subscriptions";
|
||||
import db from "./db";
|
||||
|
||||
class Repository {
|
||||
loadSubscriptions() {
|
||||
console.log(`[Repository] Loading subscriptions from localStorage`);
|
||||
const subscriptions = new Subscriptions();
|
||||
subscriptions.loaded = true;
|
||||
const serialized = localStorage.getItem('subscriptions');
|
||||
if (serialized === null) {
|
||||
return subscriptions;
|
||||
}
|
||||
try {
|
||||
JSON.parse(serialized).forEach(s => {
|
||||
const subscription = new Subscription(s.baseUrl, s.topic);
|
||||
subscription.addNotifications(s.notifications);
|
||||
subscription.last = s.last; // Explicitly set, in case old notifications have been deleted
|
||||
subscriptions.add(subscription);
|
||||
});
|
||||
console.log(`[Repository] Loaded ${subscriptions.size()} subscription(s) from localStorage`);
|
||||
return subscriptions;
|
||||
} catch (e) {
|
||||
console.log(`[Repository] Unable to deserialize subscriptions: ${e.message}`);
|
||||
return subscriptions;
|
||||
}
|
||||
}
|
||||
|
||||
saveSubscriptions(subscriptions) {
|
||||
if (!subscriptions.loaded) {
|
||||
return; // Avoid saving invalid state, triggered by initial useEffect hook
|
||||
}
|
||||
console.log(`[Repository] Saving ${subscriptions.size()} subscription(s) to localStorage`);
|
||||
const serialized = JSON.stringify(subscriptions.map( (id, subscription) => {
|
||||
return {
|
||||
baseUrl: subscription.baseUrl,
|
||||
topic: subscription.topic,
|
||||
last: subscription.last
|
||||
}
|
||||
}));
|
||||
localStorage.setItem('subscriptions', serialized);
|
||||
}
|
||||
|
||||
async setSelectedSubscriptionId(selectedSubscriptionId) {
|
||||
console.log(`[Repository] Saving selected subscription ${selectedSubscriptionId}`);
|
||||
db.prefs.put({key: 'selectedSubscriptionId', value: selectedSubscriptionId});
|
||||
}
|
||||
|
||||
async getSelectedSubscriptionId() {
|
||||
console.log(`[Repository] Loading selected subscription ID`);
|
||||
const selectedSubscriptionId = await db.prefs.get('selectedSubscriptionId');
|
||||
return (selectedSubscriptionId) ? selectedSubscriptionId.value : "";
|
||||
}
|
||||
|
||||
async setMinPriority(minPriority) {
|
||||
db.prefs.put({key: 'minPriority', value: minPriority.toString()});
|
||||
}
|
||||
|
||||
async getMinPriority() {
|
||||
const minPriority = await db.prefs.get('minPriority');
|
||||
return (minPriority) ? Number(minPriority.value) : 1;
|
||||
}
|
||||
|
||||
minPriority() {
|
||||
return db.prefs.get('minPriority');
|
||||
}
|
||||
|
||||
async setDeleteAfter(deleteAfter) {
|
||||
db.prefs.put({key:'deleteAfter', value: deleteAfter.toString()});
|
||||
}
|
||||
|
||||
async getDeleteAfter() {
|
||||
const deleteAfter = await db.prefs.get('deleteAfter');
|
||||
return (deleteAfter) ? Number(deleteAfter.value) : 604800; // Default is one week
|
||||
}
|
||||
|
||||
deleteAfter() {
|
||||
return db.prefs.get('deleteAfter');
|
||||
}
|
||||
}
|
||||
|
||||
const repository = new Repository();
|
||||
export default repository;
|
|
@ -1,33 +0,0 @@
|
|||
import {topicShortUrl, topicUrl} from './utils';
|
||||
|
||||
class Subscription {
|
||||
constructor(baseUrl, topic) {
|
||||
this.id = topicUrl(baseUrl, topic);
|
||||
this.baseUrl = baseUrl;
|
||||
this.topic = topic;
|
||||
this.last = null; // Last message ID
|
||||
}
|
||||
|
||||
addNotification(notification) {
|
||||
if (!notification.event || notification.event !== 'message') {
|
||||
return false;
|
||||
}
|
||||
this.last = notification.id;
|
||||
return true;
|
||||
}
|
||||
|
||||
addNotifications(notifications) {
|
||||
notifications.forEach(n => this.addNotification(n));
|
||||
return this;
|
||||
}
|
||||
|
||||
url() {
|
||||
return topicUrl(this.baseUrl, this.topic);
|
||||
}
|
||||
|
||||
shortUrl() {
|
||||
return topicShortUrl(this.baseUrl, this.topic);
|
||||
}
|
||||
}
|
||||
|
||||
export default Subscription;
|
|
@ -1,56 +0,0 @@
|
|||
class Subscriptions {
|
||||
constructor() {
|
||||
this.loaded = false; // FIXME I hate this
|
||||
this.subscriptions = new Map();
|
||||
}
|
||||
|
||||
add(subscription) {
|
||||
this.subscriptions.set(subscription.id, subscription);
|
||||
return this;
|
||||
}
|
||||
|
||||
get(subscriptionId) {
|
||||
const subscription = this.subscriptions.get(subscriptionId);
|
||||
return (subscription) ? subscription : null;
|
||||
}
|
||||
|
||||
update(subscription) {
|
||||
return this.add(subscription);
|
||||
}
|
||||
|
||||
remove(subscriptionId) {
|
||||
this.subscriptions.delete(subscriptionId);
|
||||
return this;
|
||||
}
|
||||
|
||||
forEach(cb) {
|
||||
this.subscriptions.forEach((value, key) => cb(key, value));
|
||||
}
|
||||
|
||||
map(cb) {
|
||||
return Array.from(this.subscriptions.values())
|
||||
.map(subscription => cb(subscription.id, subscription));
|
||||
}
|
||||
|
||||
ids() {
|
||||
return Array.from(this.subscriptions.keys());
|
||||
}
|
||||
|
||||
firstOrNull() {
|
||||
const first = this.subscriptions.values().next().value;
|
||||
return (first) ? first : null;
|
||||
}
|
||||
|
||||
size() {
|
||||
return this.subscriptions.size;
|
||||
}
|
||||
|
||||
clone() {
|
||||
const c = new Subscriptions();
|
||||
c.loaded = this.loaded;
|
||||
c.subscriptions = new Map(this.subscriptions);
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
export default Subscriptions;
|
|
@ -9,8 +9,8 @@ import Dexie from 'dexie';
|
|||
const db = new Dexie('ntfy');
|
||||
|
||||
db.version(1).stores({
|
||||
subscriptions: '&id',
|
||||
notifications: '&id,subscriptionId',
|
||||
subscriptions: '&id,baseUrl',
|
||||
notifications: '&id,subscriptionId,time',
|
||||
users: '&baseUrl,username',
|
||||
prefs: '&key'
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue