atagen

malice

public
Star0
Watch0
8c81366a752f49d6dae455bee8b0d7ecfab3e73d
2
ssh://brewery@pls.lobotomise.me:33/atagen/malice.git
web/src/lib/timeline-groups.tsTypescript74 lines2.5 KB
1
import type { PendingMessage } from './pending';
2
import type { SendState, TimelineEvent, TimelineItem } from './types';
3
4
const SENDER_RUN_MS = 5 * 60 * 1000;
5
6
export interface Group {
7
  key: string;
8
  item: TimelineItem;
9
  showSender: boolean;
10
}
11
12
/** Consecutive events from one sender within a few minutes share a header. */
13
export function buildGroups(list: TimelineItem[]): Group[] {
14
  const out: Group[] = [];
15
  let prevSender: string | null = null;
16
  let prevTs = 0;
17
  list.forEach((item, index) => {
18
    if (item.kind === 'event') {
19
      const e = item.event;
20
      const showSender = prevSender !== e.sender.user_id || Math.abs(e.timestamp_ms - prevTs) > SENDER_RUN_MS;
21
      prevSender = e.sender.user_id;
22
      prevTs = e.timestamp_ms;
23
      out.push({ key: e.item_id || e.event_id || `m-${index}`, item, showSender });
24
    } else {
25
      prevSender = null;
26
      const key =
27
        item.kind === 'historyGap' ? `gap-${item.gap.id}` :
28
        item.kind === 'day' ? `day-${item.timestamp_ms}` :
29
        item.kind === 'readMarker' ? 'readMarker' :
30
        `${item.kind}-${index}`;
31
      out.push({ key, item, showSender: true });
32
    }
33
  });
34
  return out;
35
}
36
37
export function lastOwnSent(list: TimelineItem[]): string | null {
38
  for (let i = list.length - 1; i >= 0; i--) {
39
    const item = list[i];
40
    if (item.kind === 'event' && item.event.own && item.event.send_state.kind === 'Sent') return item.event.item_id;
41
  }
42
  return null;
43
}
44
45
export function lastSenderIs(list: TimelineItem[], userId: string | null): boolean {
46
  for (let i = list.length - 1; i >= 0; i -= 1) {
47
    const item = list[i];
48
    if (item.kind === 'event') return item.event.sender.user_id === userId;
49
  }
50
  return false;
51
}
52
53
export function pendingToEvent(p: PendingMessage, self: string | null, displayName: string | null): TimelineEvent {
54
  const state: SendState = p.status === 'failed' ? { kind: 'Failed', will_retry: false } : { kind: 'Sending' };
55
  return {
56
    item_id: `pending-${p.id}`,
57
    event_id: null,
58
    transaction_id: p.id,
59
    sender: { user_id: self ?? '', display_name: displayName, avatar: null, name_ambiguous: false },
60
    timestamp_ms: p.createdAt,
61
    own: true,
62
    send_state: state,
63
    content: {
64
      kind: 'Message',
65
      message: { kind: 'Text', body: p.body, geo_uri: null, formatted_body: p.formatted, edited: false, media: null, link_previews: [] }
66
    },
67
    reply_to: p.replyTo,
68
    thread_root: null,
69
    thread_summary: null,
70
    reactions: [],
71
    read_by: [],
72
    editable: false
73
  };
74
}