update notification text using sid in web app

This commit is contained in:
Hunter Kehoe
2025-10-17 22:10:11 -06:00
parent 83b5470bc5
commit 8293a24cf9
6 changed files with 90 additions and 11 deletions

View File

@@ -156,18 +156,41 @@ class SubscriptionManager {
// 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 this.db.notifications
.orderBy("time") // Sort by time first
const notifications = await this.db.notifications
.orderBy("mtime") // Sort by time first
.filter((n) => n.subscriptionId === subscriptionId)
.reverse()
.toArray();
return this.groupNotificationsBySID(notifications);
}
async getAllNotifications() {
return this.db.notifications
.orderBy("time") // Efficient, see docs
const notifications = await this.db.notifications
.orderBy("mtime") // Efficient, see docs
.reverse()
.toArray();
return this.groupNotificationsBySID(notifications);
}
// Collapse notification updates based on sids
groupNotificationsBySID(notifications) {
const results = {};
notifications.forEach((notification) => {
const key = `${notification.subscriptionId}:${notification.sid}`;
if (key in results) {
if ("history" in results[key]) {
results[key].history.push(notification);
} else {
results[key].history = [notification];
}
} else {
results[key] = notification;
}
});
return Object.values(results);
}
/** Adds notification, or returns false if it already exists */
@@ -177,9 +200,16 @@ class SubscriptionManager {
return false;
}
try {
const populatedNotification = notification;
if (!("mtime" in populatedNotification)) {
populatedNotification.mtime = notification.time * 1000;
}
if (!("sid" in populatedNotification)) {
populatedNotification.sid = notification.id;
}
// sw.js duplicates this logic, so if you change it here, change it there too
await this.db.notifications.add({
...notification,
...populatedNotification,
subscriptionId,
// New marker (used for bubble indicator); cannot be boolean; Dexie index limitation
new: 1,
@@ -195,7 +225,16 @@ class SubscriptionManager {
/** Adds/replaces notifications, will not throw if they exist */
async addNotifications(subscriptionId, notifications) {
const notificationsWithSubscriptionId = notifications.map((notification) => ({ ...notification, subscriptionId }));
const notificationsWithSubscriptionId = notifications.map((notification) => {
const populatedNotification = notification;
if (!("mtime" in populatedNotification)) {
populatedNotification.mtime = notification.time * 1000;
}
if (!("sid" in populatedNotification)) {
populatedNotification.sid = notification.id;
}
return { ...populatedNotification, subscriptionId };
});
const lastNotificationId = notifications.at(-1).id;
await this.db.notifications.bulkPut(notificationsWithSubscriptionId);
await this.db.subscriptions.update(subscriptionId, {

View File

@@ -11,9 +11,9 @@ 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(3).stores({
subscriptions: "&id,baseUrl,[baseUrl+mutedUntil]",
notifications: "&id,subscriptionId,time,new,[subscriptionId+new]", // compound key for query performance
notifications: "&id,sid,subscriptionId,time,mtime,new,[subscriptionId+new]", // compound key for query performance
users: "&baseUrl,username",
prefs: "&key",
});

View File

@@ -53,6 +53,14 @@ export const badge = "/static/images/mask-icon.svg";
export const toNotificationParams = ({ subscriptionId, message, defaultTitle, topicRoute }) => {
const image = isImage(message.attachment) ? message.attachment.url : undefined;
let tag;
if (message.sid) {
tag = message.sid;
} else {
tag = subscriptionId;
}
// https://developer.mozilla.org/en-US/docs/Web/API/Notifications_API
return [
formatTitleWithDefault(message, defaultTitle),
@@ -61,8 +69,8 @@ export const toNotificationParams = ({ subscriptionId, message, defaultTitle, to
badge,
icon,
image,
timestamp: message.time * 1_000,
tag: subscriptionId,
timestamp: message.mtime,
tag,
renotify: true,
silent: false,
// This is used by the notification onclick event