atagen

malice

public
Star0
Watch0
f499972067e75178cb090fa6f5b44c8c4e7d3d79
2
ssh://brewery@pls.lobotomise.me:33/atagen/malice.git

Blame: web/src/lib/arrivals.ts

d2a34d0f4861atagen2026-10-25 07:29 UTC1
import type { TimelineItem } from './types';
d2a34d0f4861atagen2026-10-25 07:29 UTC2
d2a34d0f4861atagen2026-10-25 07:29 UTC3
export const FEDERATION_LAG_MS = 60 * 1000;
d2a34d0f4861atagen2026-10-25 07:29 UTC4
a802a95814e6atagen2026-10-26 03:48 UTC5
const decorated = new WeakMap<TimelineItem, { lag: number; item: TimelineItem }>();
a802a95814e6atagen2026-10-26 03:48 UTC6
a802a95814e6atagen2026-10-26 03:48 UTC7
function withLag(item: TimelineItem & { kind: 'event' }, lag: number): TimelineItem {
a802a95814e6atagen2026-10-26 03:48 UTC8
  const hit = decorated.get(item);
a802a95814e6atagen2026-10-26 03:48 UTC9
  if (hit && hit.lag === lag) return hit.item;
a802a95814e6atagen2026-10-26 03:48 UTC10
  const next: TimelineItem = { ...item, event: { ...item.event, delivery_lag_ms: lag } };
a802a95814e6atagen2026-10-26 03:48 UTC11
  decorated.set(item, { lag, item: next });
a802a95814e6atagen2026-10-26 03:48 UTC12
  return next;
a802a95814e6atagen2026-10-26 03:48 UTC13
}
a802a95814e6atagen2026-10-26 03:48 UTC14
d2a34d0f4861atagen2026-10-25 07:29 UTC15
/**
d2a34d0f4861atagen2026-10-25 07:29 UTC16
 * Carries delivery lag forward from the previous list and measures it for events appended
d2a34d0f4861atagen2026-10-25 07:29 UTC17
 * after the last one already known. `since` is when the room last became able to receive
d2a34d0f4861atagen2026-10-25 07:29 UTC18
 * events, so catch-up after being offline or closed doesn't count as federation delay.
d2a34d0f4861atagen2026-10-25 07:29 UTC19
 * Returns null when nothing in `items` was known before, meaning the list was replaced.
d2a34d0f4861atagen2026-10-25 07:29 UTC20
 */
d2a34d0f4861atagen2026-10-25 07:29 UTC21
export function observeArrivals(previous: TimelineItem[], items: TimelineItem[], since: number, now: number): TimelineItem[] | null {
d2a34d0f4861atagen2026-10-25 07:29 UTC22
  const lags = new Map<string, number | undefined>();
d2a34d0f4861atagen2026-10-25 07:29 UTC23
  for (const item of previous) if (item.kind === 'event') lags.set(item.event.item_id, item.event.delivery_lag_ms);
d2a34d0f4861atagen2026-10-25 07:29 UTC24
  let lastKnown = -1;
d2a34d0f4861atagen2026-10-25 07:29 UTC25
  items.forEach((item, index) => {
d2a34d0f4861atagen2026-10-25 07:29 UTC26
    if (item.kind === 'event' && lags.has(item.event.item_id)) lastKnown = index;
d2a34d0f4861atagen2026-10-25 07:29 UTC27
  });
d2a34d0f4861atagen2026-10-25 07:29 UTC28
  if (lastKnown === -1) return null;
a802a95814e6atagen2026-10-26 03:48 UTC29
  const next = items.map((item, index) => {
d2a34d0f4861atagen2026-10-25 07:29 UTC30
    if (item.kind !== 'event') return item;
d2a34d0f4861atagen2026-10-25 07:29 UTC31
    const event = item.event;
d2a34d0f4861atagen2026-10-25 07:29 UTC32
    if (lags.has(event.item_id)) {
d2a34d0f4861atagen2026-10-25 07:29 UTC33
      const carried = lags.get(event.item_id);
a802a95814e6atagen2026-10-26 03:48 UTC34
      return carried === undefined ? item : withLag(item, carried);
d2a34d0f4861atagen2026-10-25 07:29 UTC35
    }
d2a34d0f4861atagen2026-10-25 07:29 UTC36
    if (index < lastKnown || event.own) return item;
d2a34d0f4861atagen2026-10-25 07:29 UTC37
    const lag = now - Math.max(event.timestamp_ms, since);
a802a95814e6atagen2026-10-26 03:48 UTC38
    return lag < FEDERATION_LAG_MS ? item : withLag(item, lag);
d2a34d0f4861atagen2026-10-25 07:29 UTC39
  });
a802a95814e6atagen2026-10-26 03:48 UTC40
  return previous.length === next.length && next.every((item, index) => item === previous[index]) ? previous : next;
d2a34d0f4861atagen2026-10-25 07:29 UTC41
}