Refactor DM loading to be opportunistic and serialize notification updates

Co-authored-by: PR0M3TH3AN <[email protected]>
This commit is contained in:
google-labs-jules[bot]andPR0M3TH3AN committed 2026-02-01 15:47:54 +00:00
1 parent d639d0f2df
commit 77cd03b2f7
2 files changed
+146 -116

No files matched your search

+84 -62
View File
@@ -39,6 +39,7 @@ export class DmNotificationManager {
constructor({ logger } = {}) {
this.logger = logger || userLogger;
this.conversationCache = new Map();
this.updateQueue = new Map();
this.focusedConversationId = "";
}
@@ -46,8 +47,24 @@ export class DmNotificationManager {
return this.focusedConversationId;
}
enqueueUpdate(conversationId, task) {
const key = conversationId || "global";
const previous = this.updateQueue.get(key) || Promise.resolve();
const next = previous
.then(() => task())
.catch((error) => {
this.logger.warn(
`[dmNotifications] Update task failed for ${key}:`,
error,
);
});
this.updateQueue.set(key, next);
return next;
}
reset() {
this.conversationCache.clear();
this.updateQueue.clear();
this.focusedConversationId = "";
}
@@ -97,9 +114,9 @@ export class DmNotificationManager {
}
}
async recordMessage({ record, preview = "", direction = "" } = {}) {
recordMessage({ record, preview = "", direction = "" } = {}) {
if (!record || typeof record !== "object") {
return null;
return Promise.resolve(null);
}
const conversationId =
@@ -107,84 +124,89 @@ export class DmNotificationManager {
? record.conversation_id.trim()
: "";
if (!conversationId) {
return null;
return Promise.resolve(null);
}
const createdAt = sanitizeTimestamp(record.created_at);
const conversationState = await this.ensureConversationState(conversationId);
if (!conversationState) {
return null;
}
return this.enqueueUpdate(conversationId, async () => {
const createdAt = sanitizeTimestamp(record.created_at);
const conversationState = await this.ensureConversationState(conversationId);
if (!conversationState) {
return null;
}
const isIncoming = direction === "incoming";
const isFocused = this.focusedConversationId === conversationId;
const shouldNotify =
isIncoming && !isFocused && createdAt > conversationState.opened_until;
const unseenDelta =
isIncoming && createdAt > conversationState.opened_until ? 1 : 0;
const isIncoming = direction === "incoming";
const isFocused = this.focusedConversationId === conversationId;
const shouldNotify =
isIncoming && !isFocused && createdAt > conversationState.opened_until;
const unseenDelta =
isIncoming && createdAt > conversationState.opened_until ? 1 : 0;
let updated = null;
try {
updated = await updateConversationFromMessage(record, {
preview,
unseenDelta,
downloadedUntil: createdAt,
let updated = null;
try {
updated = await updateConversationFromMessage(record, {
preview,
unseenDelta,
downloadedUntil: createdAt,
});
} catch (error) {
this.logger.warn("[dmNotifications] Failed to update conversation", error);
}
const normalizedUpdate = normalizeConversationState({
conversation_id: conversationId,
...(updated || conversationState),
opened_until: updated?.opened_until ?? conversationState.opened_until,
});
} catch (error) {
this.logger.warn("[dmNotifications] Failed to update conversation", error);
}
const normalizedUpdate = normalizeConversationState({
conversation_id: conversationId,
...updated,
opened_until: conversationState.opened_until,
this.conversationCache.set(conversationId, normalizedUpdate);
return {
conversation: normalizedUpdate,
shouldNotify,
isFocused,
};
});
this.conversationCache.set(conversationId, normalizedUpdate);
return {
conversation: normalizedUpdate,
shouldNotify,
isFocused,
};
}
async acknowledgeRenderedMessages({ conversationId, renderedUntil } = {}) {
acknowledgeRenderedMessages({ conversationId, renderedUntil } = {}) {
if (typeof conversationId !== "string" || !conversationId.trim()) {
return null;
return Promise.resolve(null);
}
const normalized = conversationId.trim();
const conversationState = await this.ensureConversationState(normalized);
if (!conversationState) {
return null;
}
const nextOpenedUntil = Math.max(
conversationState.opened_until || 0,
sanitizeTimestamp(renderedUntil),
);
return this.enqueueUpdate(normalized, async () => {
const conversationState = await this.ensureConversationState(normalized);
if (!conversationState) {
return null;
}
let updated = null;
try {
updated = await updateConversationOpenState(normalized, {
openedUntil: nextOpenedUntil,
unseenCount: 0,
const nextOpenedUntil = Math.max(
conversationState.opened_until || 0,
sanitizeTimestamp(renderedUntil),
);
let updated = null;
try {
updated = await updateConversationOpenState(normalized, {
openedUntil: nextOpenedUntil,
unseenCount: 0,
});
} catch (error) {
this.logger.warn("[dmNotifications] Failed to update opened_until", error);
}
const normalizedUpdate = normalizeConversationState({
conversation_id: normalized,
...conversationState,
...updated,
opened_until: nextOpenedUntil,
unseen_count: 0,
});
} catch (error) {
this.logger.warn("[dmNotifications] Failed to update opened_until", error);
}
const normalizedUpdate = normalizeConversationState({
conversation_id: normalized,
...conversationState,
...updated,
opened_until: nextOpenedUntil,
unseen_count: 0,
this.conversationCache.set(normalized, normalizedUpdate);
return normalizedUpdate;
});
this.conversationCache.set(normalized, normalizedUpdate);
return normalizedUpdate;
}
async listConversationSummaries() {