Remove mtime

This commit is contained in:
binwiederhier
2026-01-05 21:14:29 -05:00
parent 1c2550d749
commit aca9a77498
10 changed files with 67 additions and 97 deletions

View File

@@ -70,8 +70,7 @@
"notifications_delete": "Delete",
"notifications_copied_to_clipboard": "Copied to clipboard",
"notifications_tags": "Tags",
"notifications_sid": "Sequence ID",
"notifications_revisions": "Revisions",
"notifications_modified": "modified {{date}}",
"notifications_priority_x": "Priority {{priority}}",
"notifications_new_indicator": "New notification",
"notifications_attachment_image": "Attachment image",

View File

@@ -25,9 +25,6 @@ const addNotification = async ({ subscriptionId, message }) => {
const db = await dbAsync();
const populatedMessage = message;
if (!("mtime" in populatedMessage)) {
populatedMessage.mtime = message.time * 1000;
}
if (!("sid" in populatedMessage)) {
populatedMessage.sid = message.id;
}

View File

@@ -157,7 +157,7 @@ class SubscriptionManager {
// killing performance. See https://dexie.org/docs/Collection/Collection.offset()#a-better-paging-approach
const notifications = await this.db.notifications
.orderBy("mtime") // Sort by time first
.orderBy("time") // Sort by time
.filter((n) => n.subscriptionId === subscriptionId)
.reverse()
.toArray();
@@ -167,30 +167,39 @@ class SubscriptionManager {
async getAllNotifications() {
const notifications = await this.db.notifications
.orderBy("mtime") // Efficient, see docs
.orderBy("time") // Efficient, see docs
.reverse()
.toArray();
return this.groupNotificationsBySID(notifications);
}
// Collapse notification updates based on sids
// Collapse notification updates based on sids, keeping only the latest version
// Also tracks the original time (earliest) for each sequence
groupNotificationsBySID(notifications) {
const results = {};
const latestBySid = {};
const originalTimeBySid = {};
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;
// Track the latest notification for each sid (first one since sorted DESC)
if (!(key in latestBySid)) {
latestBySid[key] = notification;
}
// Track the original (earliest) time for each sid
const currentOriginal = originalTimeBySid[key];
if (currentOriginal === undefined || notification.time < currentOriginal) {
originalTimeBySid[key] = notification.time;
}
});
return Object.values(results);
// Return latest notifications with originalTime set
return Object.entries(latestBySid).map(([key, notification]) => ({
...notification,
originalTime: originalTimeBySid[key],
}));
}
/** Adds notification, or returns false if it already exists */
@@ -201,9 +210,6 @@ class SubscriptionManager {
}
try {
const populatedNotification = notification;
if (!("mtime" in populatedNotification)) {
populatedNotification.mtime = notification.time * 1000;
}
if (!("sid" in populatedNotification)) {
populatedNotification.sid = notification.id;
}
@@ -227,9 +233,6 @@ class SubscriptionManager {
async addNotifications(subscriptionId, notifications) {
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;
}

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

View File

@@ -69,7 +69,7 @@ export const toNotificationParams = ({ subscriptionId, message, defaultTitle, to
badge,
icon,
image,
timestamp: message.mtime,
timestamp: message.time * 1000,
tag,
renotify: true,
silent: false,

View File

@@ -236,7 +236,9 @@ const NotificationItem = (props) => {
const { t, i18n } = useTranslation();
const { notification } = props;
const { attachment } = notification;
const date = formatShortDateTime(notification.time, i18n.language);
const isModified = notification.originalTime && notification.originalTime !== notification.time;
const originalDate = formatShortDateTime(notification.originalTime || notification.time, i18n.language);
const modifiedDate = isModified ? formatShortDateTime(notification.time, i18n.language) : null;
const otherTags = unmatchedTags(notification.tags);
const tags = otherTags.length > 0 ? otherTags.join(", ") : null;
const handleDelete = async () => {
@@ -267,8 +269,6 @@ const NotificationItem = (props) => {
const hasUserActions = notification.actions && notification.actions.length > 0;
const showActions = hasAttachmentActions || hasClickAction || hasUserActions;
const showSid = notification.id !== notification.sid || notification.history;
return (
<Card sx={{ padding: 1 }} role="listitem" aria-label={t("notifications_list_item")}>
<CardContent>
@@ -289,7 +289,8 @@ const NotificationItem = (props) => {
</Tooltip>
)}
<Typography sx={{ fontSize: 14 }} color="text.secondary">
{date}
{originalDate}
{modifiedDate && ` (${t("notifications_modified", { date: modifiedDate })})`}
{[1, 2, 4, 5].includes(notification.priority) && (
<img
src={priorityFiles[notification.priority]}
@@ -325,16 +326,6 @@ const NotificationItem = (props) => {
{t("notifications_tags")}: {tags}
</Typography>
)}
{showSid && (
<Typography sx={{ fontSize: 14 }} color="text.secondary">
{t("notifications_sid")}: {notification.sid}
</Typography>
)}
{notification.history && (
<Typography sx={{ fontSize: 14 }} color="text.secondary">
{t("notifications_revisions")}: {notification.history.length + 1}
</Typography>
)}
</CardContent>
{showActions && (
<CardActions sx={{ paddingTop: 0 }}>