atagen

malice

public
Star0
Watch0
5d6f6e2eb5a5d86815495c9f47587b55de1e3ba3
2
ssh://brewery@pls.lobotomise.me:33/atagen/malice.git
web/src/lib/link-previews.tsTypescript61 lines2.2 KB
1
import { get, writable } from 'svelte/store';
2
import { ApiError, getAccountData, getUrlPreview, setAccountData } from './api';
3
import { parseLinkPreview } from './types';
4
import type { LinkPreview } from './types';
5
6
const EVENT_TYPE = 'dev.malice.url_previews';
7
const BACKOFF_MS = 30_000;
8
9
export interface UrlPreviewSettings {
10
  encrypted_rooms: boolean;
11
}
12
13
const DEFAULT_SETTINGS: UrlPreviewSettings = { encrypted_rooms: false };
14
15
export const linkPreviews = writable<Record<string, LinkPreview | null>>({});
16
export const urlPreviewSettings = writable<UrlPreviewSettings>(DEFAULT_SETTINGS);
17
18
const inFlight = new Set<string>();
19
let backoffUntil = 0;
20
let refusalLogged = false;
21
22
export function requestLinkPreview(roomId: string, url: string): void {
23
  if (inFlight.has(url) || url in get(linkPreviews) || Date.now() < backoffUntil) return;
24
  inFlight.add(url);
25
  getUrlPreview(roomId, url)
26
    .then((raw) => linkPreviews.update((all) => ({ ...all, [url]: parseLinkPreview(raw) })))
27
    .catch((e) => {
28
      if (e instanceof ApiError && e.status === 429) backoffUntil = Date.now() + BACKOFF_MS;
29
      else if (e instanceof ApiError && e.status === 403) {
30
        linkPreviews.update((all) => ({ ...all, [url]: null }));
31
        if (!refusalLogged) console.warn('The homeserver refused a link preview', e.message);
32
        refusalLogged = true;
33
      }
34
    })
35
    .finally(() => inFlight.delete(url));
36
}
37
38
function parseSettings(raw: unknown): UrlPreviewSettings {
39
  if (typeof raw !== 'object' || raw === null) return DEFAULT_SETTINGS;
40
  return { encrypted_rooms: (raw as Record<string, unknown>)['encrypted_rooms'] === true };
41
}
42
43
export async function loadUrlPreviewSettings(): Promise<void> {
44
  try {
45
    urlPreviewSettings.set(parseSettings(await getAccountData(EVENT_TYPE)));
46
  } catch {
47
    urlPreviewSettings.set(DEFAULT_SETTINGS);
48
  }
49
}
50
51
export async function saveUrlPreviewSettings(settings: UrlPreviewSettings): Promise<void> {
52
  await setAccountData(EVENT_TYPE, { encrypted_rooms: settings.encrypted_rooms });
53
  urlPreviewSettings.set(settings);
54
}
55
56
export function resetLinkPreviews(): void {
57
  linkPreviews.set({});
58
  urlPreviewSettings.set(DEFAULT_SETTINGS);
59
  inFlight.clear();
60
  backoffUntil = 0;
61
}