atagen

malice

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

Blame: web/src/lib/timeline-groups.ts

f93c7e88ea05atagen2026-10-25 10:54 UTC1
import type { PendingMessage } from './pending';
f93c7e88ea05atagen2026-10-25 10:54 UTC2
import type { SendState, TimelineEvent, TimelineItem } from './types';
f93c7e88ea05atagen2026-10-25 10:54 UTC3
f93c7e88ea05atagen2026-10-25 10:54 UTC4
const SENDER_RUN_MS = 5 * 60 * 1000;
f93c7e88ea05atagen2026-10-25 10:54 UTC5
f93c7e88ea05atagen2026-10-25 10:54 UTC6
export interface Group {
f93c7e88ea05atagen2026-10-25 10:54 UTC7
  key: string;
f93c7e88ea05atagen2026-10-25 10:54 UTC8
  item: TimelineItem;
f93c7e88ea05atagen2026-10-25 10:54 UTC9
  showSender: boolean;
f93c7e88ea05atagen2026-10-25 10:54 UTC10
}
f93c7e88ea05atagen2026-10-25 10:54 UTC11
f93c7e88ea05atagen2026-10-25 10:54 UTC12
/** Consecutive events from one sender within a few minutes share a header. */
f93c7e88ea05atagen2026-10-25 10:54 UTC13
export function buildGroups(list: TimelineItem[]): Group[] {
f93c7e88ea05atagen2026-10-25 10:54 UTC14
  const out: Group[] = [];
f93c7e88ea05atagen2026-10-25 10:54 UTC15
  let prevSender: string | null = null;
f93c7e88ea05atagen2026-10-25 10:54 UTC16
  let prevTs = 0;
f93c7e88ea05atagen2026-10-25 10:54 UTC17
  list.forEach((item, index) => {
f93c7e88ea05atagen2026-10-25 10:54 UTC18
    if (item.kind === 'event') {
f93c7e88ea05atagen2026-10-25 10:54 UTC19
      const e = item.event;
f93c7e88ea05atagen2026-10-25 10:54 UTC20
      const showSender = prevSender !== e.sender.user_id || Math.abs(e.timestamp_ms - prevTs) > SENDER_RUN_MS;
f93c7e88ea05atagen2026-10-25 10:54 UTC21
      prevSender = e.sender.user_id;
f93c7e88ea05atagen2026-10-25 10:54 UTC22
      prevTs = e.timestamp_ms;
f93c7e88ea05atagen2026-10-25 10:54 UTC23
      out.push({ key: e.item_id || e.event_id || `m-${index}`, item, showSender });
f93c7e88ea05atagen2026-10-25 10:54 UTC24
    } else {
f93c7e88ea05atagen2026-10-25 10:54 UTC25
      prevSender = null;
f93c7e88ea05atagen2026-10-25 10:54 UTC26
      const key = item.kind === 'historyGap' ? `gap-${item.gap.id}` : `${item.kind}-${index}`;
f93c7e88ea05atagen2026-10-25 10:54 UTC27
      out.push({ key, item, showSender: true });
f93c7e88ea05atagen2026-10-25 10:54 UTC28
    }
f93c7e88ea05atagen2026-10-25 10:54 UTC29
  });
f93c7e88ea05atagen2026-10-25 10:54 UTC30
  return out;
f93c7e88ea05atagen2026-10-25 10:54 UTC31
}
f93c7e88ea05atagen2026-10-25 10:54 UTC32
f93c7e88ea05atagen2026-10-25 10:54 UTC33
export function lastOwnSent(list: TimelineItem[]): string | null {
f93c7e88ea05atagen2026-10-25 10:54 UTC34
  for (let i = list.length - 1; i >= 0; i--) {
f93c7e88ea05atagen2026-10-25 10:54 UTC35
    const item = list[i];
f93c7e88ea05atagen2026-10-25 10:54 UTC36
    if (item.kind === 'event' && item.event.own && item.event.send_state.kind === 'Sent') return item.event.item_id;
f93c7e88ea05atagen2026-10-25 10:54 UTC37
  }
f93c7e88ea05atagen2026-10-25 10:54 UTC38
  return null;
f93c7e88ea05atagen2026-10-25 10:54 UTC39
}
f93c7e88ea05atagen2026-10-25 10:54 UTC40
f93c7e88ea05atagen2026-10-25 10:54 UTC41
export function lastSenderIs(list: TimelineItem[], userId: string | null): boolean {
f93c7e88ea05atagen2026-10-25 10:54 UTC42
  for (let i = list.length - 1; i >= 0; i -= 1) {
f93c7e88ea05atagen2026-10-25 10:54 UTC43
    const item = list[i];
f93c7e88ea05atagen2026-10-25 10:54 UTC44
    if (item.kind === 'event') return item.event.sender.user_id === userId;
f93c7e88ea05atagen2026-10-25 10:54 UTC45
  }
f93c7e88ea05atagen2026-10-25 10:54 UTC46
  return false;
f93c7e88ea05atagen2026-10-25 10:54 UTC47
}
f93c7e88ea05atagen2026-10-25 10:54 UTC48
f93c7e88ea05atagen2026-10-25 10:54 UTC49
export function pendingToEvent(p: PendingMessage, self: string | null, displayName: string | null): TimelineEvent {
f93c7e88ea05atagen2026-10-25 10:54 UTC50
  const state: SendState = p.status === 'failed' ? { kind: 'Failed', will_retry: false } : { kind: 'Sending' };
f93c7e88ea05atagen2026-10-25 10:54 UTC51
  return {
f93c7e88ea05atagen2026-10-25 10:54 UTC52
    item_id: `pending-${p.id}`,
f93c7e88ea05atagen2026-10-25 10:54 UTC53
    event_id: null,
f93c7e88ea05atagen2026-10-25 10:54 UTC54
    transaction_id: p.id,
f93c7e88ea05atagen2026-10-25 10:54 UTC55
    sender: { user_id: self ?? '', display_name: displayName, avatar: null, name_ambiguous: false },
f93c7e88ea05atagen2026-10-25 10:54 UTC56
    timestamp_ms: p.createdAt,
f93c7e88ea05atagen2026-10-25 10:54 UTC57
    own: true,
f93c7e88ea05atagen2026-10-25 10:54 UTC58
    send_state: state,
f93c7e88ea05atagen2026-10-25 10:54 UTC59
    content: {
f93c7e88ea05atagen2026-10-25 10:54 UTC60
      kind: 'Message',
f93c7e88ea05atagen2026-10-25 10:54 UTC61
      message: { kind: 'Text', body: p.body, geo_uri: null, formatted_body: p.formatted, edited: false, media: null, link_previews: [] }
f93c7e88ea05atagen2026-10-25 10:54 UTC62
    },
f93c7e88ea05atagen2026-10-25 10:54 UTC63
    reply_to: p.replyTo,
f93c7e88ea05atagen2026-10-25 10:54 UTC64
    thread_root: null,
f93c7e88ea05atagen2026-10-25 10:54 UTC65
    thread_summary: null,
f93c7e88ea05atagen2026-10-25 10:54 UTC66
    reactions: [],
f93c7e88ea05atagen2026-10-25 10:54 UTC67
    read_by: [],
f93c7e88ea05atagen2026-10-25 10:54 UTC68
    editable: false
f93c7e88ea05atagen2026-10-25 10:54 UTC69
  };
f93c7e88ea05atagen2026-10-25 10:54 UTC70
}