Rename to sequence_id

This commit is contained in:
binwiederhier
2026-01-08 14:27:18 -05:00
parent fd8cd5ca91
commit 1ab7ca876c
13 changed files with 125 additions and 121 deletions

View File

@@ -47,22 +47,25 @@ class Poller {
// Filter out notifications older than the prune threshold
const deleteAfterSeconds = await prefs.deleteAfter();
const pruneThresholdTimestamp = deleteAfterSeconds > 0 ? Math.round(Date.now() / 1000) - deleteAfterSeconds : 0;
const recentNotifications = pruneThresholdTimestamp > 0 ? notifications.filter((n) => n.time >= pruneThresholdTimestamp) : notifications;
const recentNotifications =
pruneThresholdTimestamp > 0 ? notifications.filter((n) => n.time >= pruneThresholdTimestamp) : notifications;
// Find the latest notification for each sequence ID
const latestBySid = this.latestNotificationsBySid(recentNotifications);
const latestBySequenceId = this.latestNotificationsBySequenceId(recentNotifications);
// Delete all existing notifications for which the latest notification is marked as deleted
const deletedSids = Object.entries(latestBySid)
const deletedSequenceIds = Object.entries(latestBySequenceId)
.filter(([, notification]) => notification.deleted)
.map(([sid]) => sid);
if (deletedSids.length > 0) {
console.log(`[Poller] Deleting notifications with deleted sequence IDs for ${subscription.id}`, deletedSids);
await Promise.all(deletedSids.map((sid) => subscriptionManager.deleteNotificationBySid(subscription.id, sid)));
.map(([sequenceId]) => sequenceId);
if (deletedSequenceIds.length > 0) {
console.log(`[Poller] Deleting notifications with deleted sequence IDs for ${subscription.id}`, deletedSequenceIds);
await Promise.all(
deletedSequenceIds.map((sequenceId) => subscriptionManager.deleteNotificationBySequenceId(subscription.id, sequenceId))
);
}
// Add only the latest notification for each non-deleted sequence
const notificationsToAdd = Object.values(latestBySid).filter((n) => !n.deleted);
const notificationsToAdd = Object.values(latestBySequenceId).filter((n) => !n.deleted);
if (notificationsToAdd.length > 0) {
console.log(`[Poller] Adding ${notificationsToAdd.length} notification(s) for ${subscription.id}`);
await subscriptionManager.addNotifications(subscription.id, notificationsToAdd);
@@ -82,18 +85,18 @@ class Poller {
}
/**
* Groups notifications by sid and returns only the latest (highest time) for each sequence.
* Returns an object mapping sid -> latest notification.
* Groups notifications by sequenceId and returns only the latest (highest time) for each sequence.
* Returns an object mapping sequenceId -> latest notification.
*/
latestNotificationsBySid(notifications) {
const latestBySid = {};
latestNotificationsBySequenceId(notifications) {
const latestBySequenceId = {};
notifications.forEach((notification) => {
const sid = notification.sid || notification.id;
if (!(sid in latestBySid) || notification.time >= latestBySid[sid].time) {
latestBySid[sid] = notification;
const sequenceId = notification.sequence_id || notification.id;
if (!(sequenceId in latestBySequenceId) || notification.time >= latestBySequenceId[sequenceId].time) {
latestBySequenceId[sequenceId] = notification;
}
});
return latestBySid;
return latestBySequenceId;
}
}

View File

@@ -2,7 +2,7 @@ import api from "./Api";
import notifier from "./Notifier";
import prefs from "./Prefs";
import db from "./db";
import { messageWithSID, topicUrl } from "./utils";
import { messageWithSequenceId, topicUrl } from "./utils";
class SubscriptionManager {
constructor(dbImpl) {
@@ -15,7 +15,7 @@ class SubscriptionManager {
return Promise.all(
subscriptions.map(async (s) => ({
...s,
new: await this.db.notifications.where({ subscriptionId: s.id, new: 1 }).count()
new: await this.db.notifications.where({ subscriptionId: s.id, new: 1 }).count(),
}))
);
}
@@ -83,7 +83,7 @@ class SubscriptionManager {
baseUrl,
topic,
mutedUntil: 0,
last: null
last: null,
};
await this.db.subscriptions.put(subscription);
@@ -101,7 +101,7 @@ class SubscriptionManager {
const local = await this.add(remote.base_url, remote.topic, {
displayName: remote.display_name, // May be undefined
reservation // May be null!
reservation, // May be null!
});
return local.id;
@@ -183,15 +183,15 @@ class SubscriptionManager {
// Add notification to database
await this.db.notifications.add({
...messageWithSID(notification),
...messageWithSequenceId(notification),
subscriptionId,
new: 1 // New marker (used for bubble indicator); cannot be boolean; Dexie index limitation
new: 1, // New marker (used for bubble indicator); cannot be boolean; Dexie index limitation
});
// FIXME consider put() for double tab
// Update subscription last message id (for ?since=... queries)
await this.db.subscriptions.update(subscriptionId, {
last: notification.id
last: notification.id,
});
} catch (e) {
console.error(`[SubscriptionManager] Error adding notification`, e);
@@ -202,12 +202,12 @@ class SubscriptionManager {
/** Adds/replaces notifications, will not throw if they exist */
async addNotifications(subscriptionId, notifications) {
const notificationsWithSubscriptionId = notifications.map((notification) => {
return { ...messageWithSID(notification), subscriptionId };
return { ...messageWithSequenceId(notification), subscriptionId };
});
const lastNotificationId = notifications.at(-1).id;
await this.db.notifications.bulkPut(notificationsWithSubscriptionId);
await this.db.subscriptions.update(subscriptionId, {
last: lastNotificationId
last: lastNotificationId,
});
}
@@ -228,8 +228,8 @@ class SubscriptionManager {
await this.db.notifications.delete(notificationId);
}
async deleteNotificationBySid(subscriptionId, sid) {
await this.db.notifications.where({ subscriptionId, sid }).delete();
async deleteNotificationBySequenceId(subscriptionId, sequenceId) {
await this.db.notifications.where({ subscriptionId, sequenceId }).delete();
}
async deleteNotifications(subscriptionId) {
@@ -240,8 +240,8 @@ class SubscriptionManager {
await this.db.notifications.where({ id: notificationId }).modify({ new: 0 });
}
async markNotificationReadBySid(subscriptionId, sid) {
await this.db.notifications.where({ subscriptionId, sid }).modify({ new: 0 });
async markNotificationReadBySequenceId(subscriptionId, sequenceId) {
await this.db.notifications.where({ subscriptionId, sequenceId }).modify({ new: 0 });
}
async markNotificationsRead(subscriptionId) {
@@ -250,19 +250,19 @@ class SubscriptionManager {
async setMutedUntil(subscriptionId, mutedUntil) {
await this.db.subscriptions.update(subscriptionId, {
mutedUntil
mutedUntil,
});
}
async setDisplayName(subscriptionId, displayName) {
await this.db.subscriptions.update(subscriptionId, {
displayName
displayName,
});
}
async setReservation(subscriptionId, reservation) {
await this.db.subscriptions.update(subscriptionId, {
reservation
reservation,
});
}

View File

@@ -11,12 +11,11 @@ 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(6).stores({
// FIXME Should be 3
db.version(3).stores({
subscriptions: "&id,baseUrl,[baseUrl+mutedUntil]",
notifications: "&id,sid,subscriptionId,time,new,deleted,[subscriptionId+new],[subscriptionId+sid]",
notifications: "&id,sequenceId,subscriptionId,time,new,deleted,[subscriptionId+new],[subscriptionId+sequenceId]",
users: "&baseUrl,username",
prefs: "&key",
prefs: "&key"
});
return db;

View File

@@ -62,7 +62,7 @@ export const toNotificationParams = ({ subscriptionId, message, defaultTitle, to
icon,
image,
timestamp: message.time * 1000,
tag: message.sid || message.id, // Update notification if there is a sequence ID
tag: message.sequence_id || message.id, // Update notification if there is a sequence ID
renotify: true,
silent: false,
// This is used by the notification onclick event

View File

@@ -103,9 +103,9 @@ export const maybeActionErrors = (notification) => {
return actionErrors;
};
export const messageWithSID = (message) => {
if (!message.sid) {
message.sid = message.id;
export const messageWithSequenceId = (message) => {
if (!message.sequenceId) {
message.sequenceId = message.sequence_id || message.id;
}
return message;
};

View File

@@ -53,9 +53,10 @@ export const useConnectionListeners = (account, subscriptions, users, webPushTop
// Note: This logic is duplicated in the Android app in SubscriberService::onNotificationReceived()
// and FirebaseService::handleMessage().
// Delete existing notification with same sid, if any
if (notification.sid) {
await subscriptionManager.deleteNotificationBySid(subscriptionId, notification.sid);
// Delete existing notification with same sequenceId, if any
const sequenceId = notification.sequence_id || notification.id;
if (sequenceId) {
await subscriptionManager.deleteNotificationBySequenceId(subscriptionId, sequenceId);
}
// Add notification to database
if (!notification.deleted) {