atagen

malice

public
Star0
Watch0
061a70b6a034a7ab04cd9227b253dfdfd3c71d58
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;
d07bca5da4aeatagen2026-10-26 03:48 UTC26
      const key =
d07bca5da4aeatagen2026-10-26 03:48 UTC27
        item.kind === 'historyGap' ? `gap-${item.gap.id}` :
d07bca5da4aeatagen2026-10-26 03:48 UTC28
        item.kind === 'day' ? `day-${item.timestamp_ms}` :
d07bca5da4aeatagen2026-10-26 03:48 UTC29
        item.kind === 'readMarker' ? 'readMarker' :
d07bca5da4aeatagen2026-10-26 03:48 UTC30
        `${item.kind}-${index}`;
f93c7e88ea05atagen2026-10-25 10:54 UTC31
      out.push({ key, item, showSender: true });
f93c7e88ea05atagen2026-10-25 10:54 UTC32
    }
f93c7e88ea05atagen2026-10-25 10:54 UTC33
  });
f93c7e88ea05atagen2026-10-25 10:54 UTC34
  return out;
f93c7e88ea05atagen2026-10-25 10:54 UTC35
}
f93c7e88ea05atagen2026-10-25 10:54 UTC36
f93c7e88ea05atagen2026-10-25 10:54 UTC37
export function lastOwnSent(list: TimelineItem[]): string | null {
f93c7e88ea05atagen2026-10-25 10:54 UTC38
  for (let i = list.length - 1; i >= 0; i--) {
f93c7e88ea05atagen2026-10-25 10:54 UTC39
    const item = list[i];
f93c7e88ea05atagen2026-10-25 10:54 UTC40
    if (item.kind === 'event' && item.event.own && item.event.send_state.kind === 'Sent') return item.event.item_id;
f93c7e88ea05atagen2026-10-25 10:54 UTC41
  }
f93c7e88ea05atagen2026-10-25 10:54 UTC42
  return null;
f93c7e88ea05atagen2026-10-25 10:54 UTC43
}
f93c7e88ea05atagen2026-10-25 10:54 UTC44
f93c7e88ea05atagen2026-10-25 10:54 UTC45
export function lastSenderIs(list: TimelineItem[], userId: string | null): boolean {
f93c7e88ea05atagen2026-10-25 10:54 UTC46
  for (let i = list.length - 1; i >= 0; i -= 1) {
f93c7e88ea05atagen2026-10-25 10:54 UTC47
    const item = list[i];
f93c7e88ea05atagen2026-10-25 10:54 UTC48
    if (item.kind === 'event') return item.event.sender.user_id === userId;
f93c7e88ea05atagen2026-10-25 10:54 UTC49
  }
f93c7e88ea05atagen2026-10-25 10:54 UTC50
  return false;
f93c7e88ea05atagen2026-10-25 10:54 UTC51
}
f93c7e88ea05atagen2026-10-25 10:54 UTC52
f93c7e88ea05atagen2026-10-25 10:54 UTC53
export function pendingToEvent(p: PendingMessage, self: string | null, displayName: string | null): TimelineEvent {
f93c7e88ea05atagen2026-10-25 10:54 UTC54
  const state: SendState = p.status === 'failed' ? { kind: 'Failed', will_retry: false } : { kind: 'Sending' };
f93c7e88ea05atagen2026-10-25 10:54 UTC55
  return {
f93c7e88ea05atagen2026-10-25 10:54 UTC56
    item_id: `pending-${p.id}`,
f93c7e88ea05atagen2026-10-25 10:54 UTC57
    event_id: null,
f93c7e88ea05atagen2026-10-25 10:54 UTC58
    transaction_id: p.id,
f93c7e88ea05atagen2026-10-25 10:54 UTC59
    sender: { user_id: self ?? '', display_name: displayName, avatar: null, name_ambiguous: false },
f93c7e88ea05atagen2026-10-25 10:54 UTC60
    timestamp_ms: p.createdAt,
f93c7e88ea05atagen2026-10-25 10:54 UTC61
    own: true,
f93c7e88ea05atagen2026-10-25 10:54 UTC62
    send_state: state,
f93c7e88ea05atagen2026-10-25 10:54 UTC63
    content: {
f93c7e88ea05atagen2026-10-25 10:54 UTC64
      kind: 'Message',
f93c7e88ea05atagen2026-10-25 10:54 UTC65
      message: { kind: 'Text', body: p.body, geo_uri: null, formatted_body: p.formatted, edited: false, media: null, link_previews: [] }
f93c7e88ea05atagen2026-10-25 10:54 UTC66
    },
f93c7e88ea05atagen2026-10-25 10:54 UTC67
    reply_to: p.replyTo,
f93c7e88ea05atagen2026-10-25 10:54 UTC68
    thread_root: null,
f93c7e88ea05atagen2026-10-25 10:54 UTC69
    thread_summary: null,
f93c7e88ea05atagen2026-10-25 10:54 UTC70
    reactions: [],
f93c7e88ea05atagen2026-10-25 10:54 UTC71
    read_by: [],
f93c7e88ea05atagen2026-10-25 10:54 UTC72
    editable: false
f93c7e88ea05atagen2026-10-25 10:54 UTC73
  };
f93c7e88ea05atagen2026-10-25 10:54 UTC74
}