perf(*): bound list queries, batch fetches, harden media downloads

This commit is contained in:
hh committed 2026-09-01 21:38:22 +02:00
1 parent 9c12628304
commit 1fa4f18a9b
35 files changed
+473 -138

No files matched your search

+18 -1
View File
@@ -9,10 +9,27 @@ export interface CustomEmojiAsset {
url: string;
}
const MAX_CACHED = 500;
const ready = new Map<string, CustomEmojiAsset>();
const missing = new Set<string>();
const inflight = new Map<string, Promise<CustomEmojiAsset | null>>();
function remember(key: string, asset: CustomEmojiAsset) {
ready.set(key, asset);
while (ready.size > MAX_CACHED) {
const oldest = ready.keys().next();
if (oldest.done) {
return;
}
const stale = ready.get(oldest.value);
ready.delete(oldest.value);
if (stale) {
URL.revokeObjectURL(stale.url);
}
}
}
function authHeaders(): Record<string, string> {
return auth.token ? { Authorization: `Bearer ${auth.token}` } : {};
}
@@ -34,7 +51,7 @@ async function fetchEmoji(
if (response.ok) {
const blob = await response.blob();
const asset = { url: URL.createObjectURL(blob), mime: blob.type };
ready.set(key, asset);
remember(key, asset);
return asset;
}
if (response.status === 409 && retry) {
+18 -2
View File
@@ -303,8 +303,14 @@ export function getMessageLinks(
});
}
export function getChatCalendar(chatId: number): Promise<DayCount[]> {
return request<DayCount[]>(`/chats/${chatId}/calendar`, { account: true });
export function getChatCalendar(
chatId: number,
range: { date_from?: string; date_to?: string } = {}
): Promise<DayCount[]> {
return request<DayCount[]>(`/chats/${chatId}/calendar`, {
account: true,
query: { ...range },
});
}
export function getMessageAt(chatId: number, date: string): Promise<MessageAt> {
@@ -334,6 +340,16 @@ export function getPeers(ids: number[]): Promise<PeerView[]> {
});
}
export function getChatsBatch(ids: number[]): Promise<Chat[]> {
if (ids.length === 0) {
return Promise.resolve([]);
}
return request<Chat[]>("/chats/batch", {
account: true,
query: { ids: ids.join(",") },
});
}
export function enrichChat(chatId: number): Promise<{ job_id: number }> {
return request<{ job_id: number }>(`/chats/${chatId}/enrich`, {
method: "POST",
+23 -2
View File
@@ -102,9 +102,30 @@ export function visualKind(kind: string): VisualKind {
return "other";
}
const MAX_CACHED = 200;
const ready = new Map<string, InlineMedia>();
const inflight = new Map<string, Promise<InlineMedia>>();
function remember(
cache: Map<string, InlineMedia>,
key: string,
item: InlineMedia
) {
cache.set(key, item);
while (cache.size > MAX_CACHED) {
const oldest = cache.keys().next();
if (oldest.done) {
return;
}
const stale = cache.get(oldest.value);
cache.delete(oldest.value);
if (stale?.state === "ready") {
URL.revokeObjectURL(stale.url);
}
}
}
function cacheKey(account: number, chatId: number, messageId: number): string {
return `${account}:${chatId}:${messageId}`;
}
@@ -183,7 +204,7 @@ export function loadMediaItem(media: MediaRef): Promise<InlineMedia> {
const promise = resolveById(media)
.then((result) => {
if (result.state === "ready") {
byId.set(key, result);
remember(byId, key, result);
}
return result;
})
@@ -251,7 +272,7 @@ export function loadInlineMedia(
const promise = resolve(chatId, messageId)
.then((result) => {
if (result.state === "ready") {
ready.set(key, result);
remember(ready, key, result);
}
return result;
})
+18 -1
View File
@@ -3,10 +3,27 @@ import { auth } from "$lib/stores/auth.svelte";
const BASE = import.meta.env.VITE_API_BASE ?? "/api";
const MAX_CACHED = 100;
const ready = new Map<string, string>();
const missing = new Set<string>();
const inflight = new Map<string, Promise<string | null>>();
function remember(key: string, url: string) {
ready.set(key, url);
while (ready.size > MAX_CACHED) {
const oldest = ready.keys().next();
if (oldest.done) {
return;
}
const stale = ready.get(oldest.value);
ready.delete(oldest.value);
if (stale) {
URL.revokeObjectURL(stale);
}
}
}
function authHeaders(): Record<string, string> {
return auth.token ? { Authorization: `Bearer ${auth.token}` } : {};
}
@@ -21,7 +38,7 @@ async function fetchStoryMedia(
const response = await fetch(url, { headers: authHeaders() });
if (response.ok) {
const objectUrl = URL.createObjectURL(await response.blob());
ready.set(key, objectUrl);
remember(key, objectUrl);
return objectUrl;
}
missing.add(key);