buildRows function

List<ChatRow> buildRows(
  1. ChatState state,
  2. HatangChatStrings strings, {
  3. DateTime? now,
})

Rows for state: day separators, greeting when empty, notice / handoff / rated events only, "Seen" under the last read own message, sender names for the team, pending bubbles and the typing indicator.

Implementation

List<ChatRow> buildRows(ChatState state, HatangChatStrings strings, {DateTime? now}) {
  final rows = <ChatRow>[];
  final locale = state.locale;
  final items = state.timeline.items;
  final at = now ?? clock.now();
  if (items.isEmpty) {
    final greeting = greetingRowFor(state.config, locale, online: state.online);
    if (greeting != null) rows.add(greeting);
  }
  var lastVisitorRead = 0;
  for (final i in items) {
    if (i is MessageDto && i.isMine && i.readAt != null && i.seq > lastVisitorRead) lastVisitorRead = i.seq;
  }
  DateTime? lastDay;
  final newest = items.isEmpty ? null : items.last;
  final marks = PaidMarks.of(items);
  for (final i in items) {
    final day = dayOf(i.createdAt);
    if (lastDay != day) {
      lastDay = day;
      rows.add(DayRow(dayLabel(i.createdAt, locale, strings, now: at), day));
    }
    switch (i) {
      case ConversationEventDto():
        switch (i.type) {
          case 'notice':
            final key = i.data['key'];
            final text = key is String ? strings.t(key, locale) : '${i.data['text'] ?? ''}';
            if (text.isNotEmpty) rows.add(SystemRow(i.id, text));
          case 'handoff':
            rows.add(SystemRow(i.id, strings.transferredToAgent));
          case 'rated':
            rows.add(SystemRow(i.id, strings.rateThanks));
          default:
            break;
        }
      case MessageDto():
        final mine = i.isMine;
        String? label;
        if (!mine && i.senderName != null && i.senderName!.isNotEmpty) {
          label = i.senderType == SenderType.bot ? '${i.senderName} ยท ${strings.aiLabel}' : i.senderName;
        } else if (!mine && i.senderType == SenderType.bot) {
          label = strings.aiLabel;
        }
        var card = mine ? null : cardOf(i.metadata);
        if (card is QuickRepliesCard && !identical(i, newest)) card = null;
        final intro = i.metadata?['intro'];
        rows.add(
          MessageRow(
            // A card the SDK draws replaces the plain-text copy the gateway
            // stores for other channels.
            card == null
                ? i
                : i.copyWith(content: cardBubbleText(i.content, card, intro: intro is String ? intro : null)),
            card: card,
            paid: card is PaymentCard && card.isPaid(marks),
            mine: mine,
            showSender: label != null,
            showSeen: mine && lastVisitorRead == i.seq,
            senderLabel: label,
            askEmail: !mine && i.metadata?['kind'] == 'email_request' && (state.visitorEmail ?? '').isEmpty,
          ),
        );
    }
  }
  for (final p in state.pending) {
    rows.add(PendingRow(p));
  }
  final typing = state.typingAgent;
  if (typing != null) rows.add(TypingRow(typing));
  return rows;
}