send method

Future<void> send(
  1. MessagePayloadModel payload, {
  2. required List<String> recipients,
  3. List<File> files = const [],
})

Implementation

Future<void> send(
  MessagePayloadModel payload, {
  required List<String> recipients,
  List<File> files = const [],
}) async {
  final currentPeer = await _db.peers.current.get();
  if (currentPeer == null) {
    throw StateError('Missing current peer');
  }

  final secretKey = await _encryption.createSecretKey();
  final secretKeyBytes = await secretKey.extractBytes();
  final encryptedPayload = await _encryption.encryptSecret(
    secretKey,
    jsonEncode(payload.toJson()),
  );

  final requestedPeerKeys = {...recipients, currentPeer.key};
  final knownPeers = await _db.peers.ids(requestedPeerKeys.toList()).get();
  final revokedPeerKeys = knownPeers
      .where((peer) => peer.isRevoked)
      .map((peer) => peer.key)
      .toSet();
  final peerKeys = requestedPeerKeys
      .where((peerKey) => !revokedPeerKeys.contains(peerKey))
      .toList();
  final devices = await _db.devices.peers(peerKeys).get();
  final activeDevices = devices.where((device) => !device.isRevoked);
  final recipientKeys = activeDevices.map((d) => d.signingKey).toList();
  final keyIndex = {
    for (final d in activeDevices) d.signingKey: d.encryptionKey,
  };

  final secretKeyIndex = <String, String>{};
  for (final recipientKey in recipientKeys) {
    final encryptionKey = keyIndex[recipientKey];
    if (encryptionKey == null) {
      _logger.log('Missing device encryption key for $recipientKey');
      continue;
    }

    secretKeyIndex[recipientKey] = base64Encode(
      await _encryption.encryptSharedSecret(secretKeyBytes, encryptionKey),
    );
  }

  final attachments = <NetworkMediaModel>[];
  for (final file in files) {
    final bytes = await file.readAsBytes();
    final encryptedBytes = await _encryption.encryptSecretBytes(
      secretKey,
      bytes,
    );

    final hash = sha256.convert(encryptedBytes).toString();
    await MediaStorage.instance.put(hash, Uint8List.fromList(encryptedBytes));

    attachments.add(
      NetworkMediaModel(
        hash: hash,
        mime: detectMimeType(file.path, headerBytes: bytes),
        size: encryptedBytes.length,
        name: file.uri.pathSegments.last,
      ),
    );
  }

  final body = MessageEventBodyModel(
    peers: peerKeys,
    keys: secretKeyIndex,
    payload: encryptedPayload,
    encryptionKey: Encryption.instance.publicKey,
    attachments: attachments,
  );

  final event = await _client.events.publish(body);
  final onlineDevices = await _db.devices.online.get();
  await Future.wait(
    onlineDevices
        .where((device) => peerKeys.contains(device.peerKey))
        .map(
          (device) => _client.events.relayDevice(device.signingKey, event),
        ),
  );

  await _createReceipt(event, MessageReceiptStatus.sent);
}