feat: Update how messages load and sync, allow plugins to import messages
All checks were successful
Queue Release Build / prepare (push) Successful in 23s
Deploy Web Apps / deploy (push) Successful in 7m36s
Queue Release Build / build-windows (push) Successful in 28m3s
Queue Release Build / build-linux (push) Successful in 44m14s
Queue Release Build / finalize (push) Successful in 39s

This commit is contained in:
2026-05-18 23:21:09 +02:00
parent 94428ed170
commit 54e8b9a5e4
19 changed files with 542 additions and 86 deletions

View File

@@ -45,10 +45,17 @@ export async function hydrateMessages(
return messages.map((msg) => msg.isDeleted ? normaliseDeletedMessage(msg) : msg);
}
/** Builds a sync inventory item from a message and its reaction count. */
/** Builds a sync inventory item from a message and its reaction count.
*
* Reactions are read from the already-hydrated `msg.reactions` array (the
* persistence layer joins them in via `getMessages`), and attachment counts
* only come from the in-memory override. We deliberately avoid per-message
* DB lookups here so a whole-room inventory stays O(1) DB calls even when
* the room contains tens of thousands of messages.
*/
export async function buildInventoryItem(
msg: Message,
db: DatabaseService,
_db: DatabaseService,
attachmentCountOverride?: number
): Promise<InventoryItem> {
if (msg.isDeleted) {
@@ -60,50 +67,49 @@ export async function buildInventoryItem(
};
}
const reactions = await db.getReactionsForMessage(msg.id);
const attachments =
attachmentCountOverride === undefined
? await db.getAttachmentsForMessage(msg.id)
: [];
return { id: msg.id,
const item: InventoryItem = {
id: msg.id,
ts: getMessageTimestamp(msg),
rc: reactions.length,
ac: attachmentCountOverride ?? attachments.length };
rc: msg.reactions?.length ?? 0
};
if (attachmentCountOverride !== undefined) {
item.ac = attachmentCountOverride;
}
return item;
}
/** Builds a local map of `{timestamp, reactionCount, attachmentCount}` keyed by message ID. */
/** Builds a local map of `{timestamp, reactionCount, attachmentCount}` keyed by message ID.
*
* As with {@link buildInventoryItem}, reactions come from the already-hydrated
* `msg.reactions` array and attachment counts only come from the in-memory
* override map.
*/
export async function buildLocalInventoryMap(
messages: Message[],
db: DatabaseService,
_db: DatabaseService,
attachmentCountOverrides?: ReadonlyMap<string, number>
): Promise<Map<string, { ts: number; rc: number; ac: number }>> {
const map = new Map<string, { ts: number; rc: number; ac: number }>();
await Promise.all(
messages.map(async (msg) => {
if (msg.isDeleted) {
map.set(msg.id, {
ts: getMessageTimestamp(msg),
rc: 0,
ac: 0
});
for (const msg of messages) {
if (msg.isDeleted) {
map.set(msg.id, {
ts: getMessageTimestamp(msg),
rc: 0,
ac: 0
});
return;
}
continue;
}
const reactions = await db.getReactionsForMessage(msg.id);
const attachmentCountOverride = attachmentCountOverrides?.get(msg.id);
const attachments =
attachmentCountOverride === undefined
? await db.getAttachmentsForMessage(msg.id)
: [];
map.set(msg.id, { ts: getMessageTimestamp(msg),
rc: reactions.length,
ac: attachmentCountOverride ?? attachments.length });
})
);
map.set(msg.id, {
ts: getMessageTimestamp(msg),
rc: msg.reactions?.length ?? 0,
ac: attachmentCountOverrides?.get(msg.id) ?? 0
});
}
return map;
}