ntfy/web/src/app/SubscriptionManager.js

194 lines
6.6 KiB
JavaScript
Raw Normal View History

import db from "./db";
2022-03-09 09:19:15 +13:00
import {topicUrl} from "./utils";
class SubscriptionManager {
2022-03-09 09:19:15 +13:00
/** All subscriptions, including "new count"; this is a JOIN, see https://dexie.org/docs/API-Reference#joining */
async all() {
2022-03-07 16:37:13 +13:00
const subscriptions = await db.subscriptions.toArray();
await Promise.all(subscriptions.map(async s => {
s.new = await db.notifications
.where({ subscriptionId: s.id, new: 1 })
.count();
}));
return subscriptions;
}
async get(subscriptionId) {
return await db.subscriptions.get(subscriptionId)
}
2023-01-25 09:31:39 +13:00
async add(baseUrl, topic, internal) {
2022-12-09 14:50:48 +13:00
const id = topicUrl(baseUrl, topic);
const existingSubscription = await this.get(id);
if (existingSubscription) {
return existingSubscription;
}
2022-03-09 09:19:15 +13:00
const subscription = {
id: topicUrl(baseUrl, topic),
baseUrl: baseUrl,
topic: topic,
2022-03-09 10:56:41 +13:00
mutedUntil: 0,
2022-12-09 14:50:48 +13:00
last: null,
2023-01-25 09:31:39 +13:00
internal: internal || false
2022-03-09 09:19:15 +13:00
};
await db.subscriptions.put(subscription);
2022-03-09 09:19:15 +13:00
return subscription;
}
2023-01-04 05:28:04 +13:00
async syncFromRemote(remoteSubscriptions, remoteReservations) {
2022-12-26 16:29:55 +13:00
console.log(`[SubscriptionManager] Syncing subscriptions from remote`, remoteSubscriptions);
2022-12-09 14:50:48 +13:00
// Add remote subscriptions
2023-02-13 08:09:44 +13:00
let remoteIds = []; // = topicUrl(baseUrl, topic)
2022-12-09 14:50:48 +13:00
for (let i = 0; i < remoteSubscriptions.length; i++) {
const remote = remoteSubscriptions[i];
2023-02-13 08:09:44 +13:00
const local = await this.add(remote.base_url, remote.topic, false);
2023-01-05 16:47:12 +13:00
const reservation = remoteReservations?.find(r => remote.base_url === config.base_url && remote.topic === r.topic) || null;
2023-01-12 15:38:10 +13:00
await this.update(local.id, {
2023-01-25 09:31:39 +13:00
displayName: remote.display_name, // May be undefined
2023-01-12 15:38:10 +13:00
reservation: reservation // May be null!
});
2023-02-13 08:09:44 +13:00
remoteIds.push(local.id);
2022-12-09 14:50:48 +13:00
}
// Remove local subscriptions that do not exist remotely
const localSubscriptions = await db.subscriptions.toArray();
for (let i = 0; i < localSubscriptions.length; i++) {
const local = localSubscriptions[i];
2023-02-13 08:09:44 +13:00
const remoteExists = remoteIds.includes(local.id);
2023-01-12 15:38:10 +13:00
if (!local.internal && !remoteExists) {
2022-12-09 14:50:48 +13:00
await this.remove(local.id);
}
}
}
async updateState(subscriptionId, state) {
db.subscriptions.update(subscriptionId, { state: state });
}
async remove(subscriptionId) {
await db.subscriptions.delete(subscriptionId);
await db.notifications
.where({subscriptionId: subscriptionId})
.delete();
}
async first() {
return db.subscriptions.toCollection().first(); // May be undefined
}
2022-03-09 05:21:11 +13:00
async getNotifications(subscriptionId) {
2022-03-08 14:11:58 +13:00
// This is quite awkward, but it is the recommended approach as per the Dexie docs.
// It's actually fine, because the reading and filtering is quite fast. The rendering is what's
// killing performance. See https://dexie.org/docs/Collection/Collection.offset()#a-better-paging-approach
return db.notifications
2022-03-08 14:11:58 +13:00
.orderBy("time") // Sort by time first
.filter(n => n.subscriptionId === subscriptionId)
2022-03-08 10:36:49 +13:00
.reverse()
2022-03-08 14:11:58 +13:00
.toArray();
2022-03-08 10:36:49 +13:00
}
async getAllNotifications() {
return db.notifications
.orderBy("time") // Efficient, see docs
.reverse()
.toArray();
}
/** Adds notification, or returns false if it already exists */
async addNotification(subscriptionId, notification) {
const exists = await db.notifications.get(notification.id);
if (exists) {
return false;
}
2022-03-07 16:37:13 +13:00
try {
notification.new = 1; // New marker (used for bubble indicator); cannot be boolean; Dexie index limitation
await db.notifications.add({ ...notification, subscriptionId }); // FIXME consider put() for double tab
await db.subscriptions.update(subscriptionId, {
last: notification.id
});
} catch (e) {
console.error(`[SubscriptionManager] Error adding notification`, e);
}
return true;
}
/** Adds/replaces notifications, will not throw if they exist */
async addNotifications(subscriptionId, notifications) {
const notificationsWithSubscriptionId = notifications
.map(notification => ({ ...notification, subscriptionId }));
const lastNotificationId = notifications.at(-1).id;
await db.notifications.bulkPut(notificationsWithSubscriptionId);
await db.subscriptions.update(subscriptionId, {
last: lastNotificationId
});
}
2022-04-22 08:33:49 +12:00
async updateNotification(notification) {
const exists = await db.notifications.get(notification.id);
if (!exists) {
return false;
}
try {
await db.notifications.put({ ...notification });
} catch (e) {
console.error(`[SubscriptionManager] Error updating notification`, e);
}
return true;
}
async deleteNotification(notificationId) {
await db.notifications.delete(notificationId);
}
async deleteNotifications(subscriptionId) {
await db.notifications
.where({subscriptionId: subscriptionId})
.delete();
}
async markNotificationRead(notificationId) {
await db.notifications
2022-05-08 11:16:08 +12:00
.where({id: notificationId})
.modify({new: 0});
}
2022-03-07 16:37:13 +13:00
async markNotificationsRead(subscriptionId) {
await db.notifications
.where({subscriptionId: subscriptionId, new: 1})
.modify({new: 0});
}
2022-03-09 10:56:41 +13:00
async setMutedUntil(subscriptionId, mutedUntil) {
await db.subscriptions.update(subscriptionId, {
mutedUntil: mutedUntil
});
}
2022-06-30 07:57:56 +12:00
async setDisplayName(subscriptionId, displayName) {
await db.subscriptions.update(subscriptionId, {
displayName: displayName
});
}
2023-01-04 05:28:04 +13:00
async setReservation(subscriptionId, reservation) {
await db.subscriptions.update(subscriptionId, {
reservation: reservation
});
}
2023-01-12 15:38:10 +13:00
async update(subscriptionId, params) {
await db.subscriptions.update(subscriptionId, params);
}
async pruneNotifications(thresholdTimestamp) {
await db.notifications
.where("time").below(thresholdTimestamp)
.delete();
}
}
const subscriptionManager = new SubscriptionManager();
export default subscriptionManager;