perf(*): bound list queries, batch fetches, harden media downloads
This commit is contained in:
1 parent
9c12628304
commit
1fa4f18a9b
35 files changed
+473
-138
No files matched your search
@@ -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) {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
})
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user