sendEvent method

Future<String?> sendEvent(
  1. Map<String, dynamic> content, {
  2. String type = EventTypes.Message,
  3. String? txid,
  4. Event? inReplyTo,
  5. String? editEventId,
  6. String? threadRootEventId,
  7. String? threadLastEventId,
})

Sends an event to this room with this json as a content. Returns the event ID generated from the server. It uses list of completer to make sure events are sending in a row.

Implementation

Future<String?> sendEvent(
  Map<String, dynamic> content, {
  String type = EventTypes.Message,
  String? txid,
  Event? inReplyTo,
  String? editEventId,
  String? threadRootEventId,
  String? threadLastEventId,
}) async {
  // Create new transaction id
  final String messageID;
  if (txid == null) {
    messageID = client.generateUniqueTransactionId();
  } else {
    messageID = txid;
  }

  if (inReplyTo != null) {
    var replyText =
        '<${inReplyTo.senderId}> ${_stripBodyFallback(inReplyTo.body)}';
    replyText = replyText.split('\n').map((line) => '> $line').join('\n');
    content['format'] = 'org.sdn.custom.html';
    // be sure that we strip any previous reply fallbacks
    final replyHtml = (inReplyTo.formattedText.isNotEmpty
            ? inReplyTo.formattedText
            : htmlEscape.convert(inReplyTo.body).replaceAll('\n', '<br>'))
        .replaceAll(
            RegExp(r'<mx-reply>.*</mx-reply>',
                caseSensitive: false, multiLine: false, dotAll: true),
            '');
    final repliedHtml = content.tryGet<String>('formatted_body') ??
        htmlEscape
            .convert(content.tryGet<String>('body') ?? '')
            .replaceAll('\n', '<br>');
    content['formatted_body'] =
        '<mx-reply><blockquote><a href="https://sdn.to/#/${inReplyTo.roomId!}/${inReplyTo.eventId}">In reply to</a> <a href="https://sdn.to/#/${inReplyTo.senderId}">${inReplyTo.senderId}</a><br>$replyHtml</blockquote></mx-reply>$repliedHtml';
    // We escape all @room-mentions here to prevent accidental room pings when an admin
    // replies to a message containing that!
    content['body'] =
        '${replyText.replaceAll('@room', '@\u200broom')}\n\n${content.tryGet<String>('body') ?? ''}';
    content['m.relates_to'] = {
      'm.in_reply_to': {
        'event_id': inReplyTo.eventId,
      },
    };
  }

  if (threadRootEventId != null) {
    content['m.relates_to'] = {
      'event_id': threadRootEventId,
      'rel_type': RelationshipTypes.thread,
      'is_falling_back': inReplyTo == null,
      if (inReplyTo != null) ...{
        'm.in_reply_to': {
          'event_id': inReplyTo.eventId,
        },
      } else ...{
        if (threadLastEventId != null)
          'm.in_reply_to': {
            'event_id': threadLastEventId,
          },
      }
    };
  }

  if (editEventId != null) {
    final newContent = content.copy();
    content['m.new_content'] = newContent;
    content['m.relates_to'] = {
      'event_id': editEventId,
      'rel_type': RelationshipTypes.edit,
    };
    if (content['body'] is String) {
      content['body'] = '* ${content['body']}';
    }
    if (content['formatted_body'] is String) {
      content['formatted_body'] = '* ${content['formatted_body']}';
    }
  }
  final sentDate = DateTime.now();
  final syncUpdate = SyncUpdate(
    nextBatch: '',
    rooms: RoomsUpdate(
      join: {
        id: JoinedRoomUpdate(
          timeline: TimelineUpdate(
            events: [
              SDNEvent(
                content: content,
                type: type,
                eventId: messageID,
                senderId: client.userID!,
                originServerTs: sentDate,
                unsigned: {
                  messageSendingStatusKey: EventStatus.sending.intValue,
                  'transaction_id': messageID,
                },
              ),
            ],
          ),
        ),
      },
    ),
  );
  await _handleFakeSync(syncUpdate);
  final completer = Completer();
  _sendingQueue.add(completer);
  while (_sendingQueue.first != completer) {
    await _sendingQueue.first.future;
  }

  final timeoutDate = DateTime.now().add(client.sendTimelineEventTimeout);
  // Send the text and on success, store and display a *sent* event.
  String? res;

  while (res == null) {
    try {
      res = await _sendContent(
        type,
        content,
        txid: messageID,
      );
    } catch (e, s) {
      if (e is SDNException || DateTime.now().isAfter(timeoutDate)) {
        Logs().w('Problem while sending message', e, s);
        syncUpdate.rooms!.join!.values.first.timeline!.events!.first
            .unsigned![messageSendingStatusKey] = EventStatus.error.intValue;
        await _handleFakeSync(syncUpdate);
        completer.complete();
        _sendingQueue.remove(completer);
        return null;
      }
      Logs().w('Problem while sending message: $e Try again in 1 seconds...');
      await Future.delayed(Duration(seconds: 1));
    }
  }
  syncUpdate.rooms!.join!.values.first.timeline!.events!.first
      .unsigned![messageSendingStatusKey] = EventStatus.sent.intValue;
  syncUpdate.rooms!.join!.values.first.timeline!.events!.first.eventId = res;
  await _handleFakeSync(syncUpdate);
  completer.complete();
  _sendingQueue.remove(completer);

  return res;
}