import 'package:meta/meta.dart'; import 'package:veilid_support/veilid_support.dart'; import '../../account_manager/account_manager.dart'; import '../../contacts/contacts.dart'; import '../../proto/proto.dart' as proto; import '../../tools/tools.dart'; import 'models.dart'; ////////////////////////////////////////////////// /// class ValidContactInvitation { @internal ValidContactInvitation( {required ActiveAccountInfo activeAccountInfo, required proto.Account account, required TypedKey contactRequestInboxKey, required proto.ContactRequestPrivate contactRequestPrivate, required IdentityMaster contactIdentityMaster, required KeyPair writer}) : _activeAccountInfo = activeAccountInfo, _account = account, _contactRequestInboxKey = contactRequestInboxKey, _contactRequestPrivate = contactRequestPrivate, _contactIdentityMaster = contactIdentityMaster, _writer = writer; proto.Profile get remoteProfile => _contactRequestPrivate.profile; Future accept() async { final pool = DHTRecordPool.instance; try { // Ensure we don't delete this if we're trying to chat to self // The initiating side will delete the records in deleteInvitation() final isSelf = _contactIdentityMaster.identityPublicKey == _activeAccountInfo.localAccount.identityMaster.identityPublicKey; final accountRecordKey = _activeAccountInfo.accountRecordKey; return (await pool.openWrite(_contactRequestInboxKey, _writer, parent: accountRecordKey)) // ignore: prefer_expression_function_bodies .maybeDeleteScope(!isSelf, (contactRequestInbox) async { // Create local conversation key for this // contact and send via contact response final conversation = ConversationCubit( activeAccountInfo: _activeAccountInfo, remoteIdentityPublicKey: _contactIdentityMaster.identityPublicTypedKey()); return conversation.initLocalConversation( profile: _account.profile, callback: (localConversation) async { final contactResponse = proto.ContactResponse() ..accept = true ..remoteConversationRecordKey = localConversation.key.toProto() ..identityMasterRecordKey = _activeAccountInfo .localAccount.identityMaster.masterRecordKey .toProto(); final contactResponseBytes = contactResponse.writeToBuffer(); final cs = await pool.veilid .getCryptoSystem(_contactRequestInboxKey.kind); final identitySignature = await cs.sign( _activeAccountInfo.conversationWriter.key, _activeAccountInfo.conversationWriter.secret, contactResponseBytes); final signedContactResponse = proto.SignedContactResponse() ..contactResponse = contactResponseBytes ..identitySignature = identitySignature.toProto(); // Write the acceptance to the inbox if (await contactRequestInbox.tryWriteProtobuf( proto.SignedContactResponse.fromBuffer, signedContactResponse, subkey: 1) != null) { throw Exception('failed to accept contact invitation'); } return AcceptedContact( remoteProfile: _contactRequestPrivate.profile, remoteIdentity: _contactIdentityMaster, remoteConversationRecordKey: _contactRequestPrivate.chatRecordKey.toVeilid(), localConversationRecordKey: localConversation.key, ); }); }); } on Exception catch (e) { log.debug('exception: $e', e); return null; } } Future reject() async { final pool = DHTRecordPool.instance; // Ensure we don't delete this if we're trying to chat to self final isSelf = _contactIdentityMaster.identityPublicKey == _activeAccountInfo.localAccount.identityMaster.identityPublicKey; final accountRecordKey = _activeAccountInfo.userLogin.accountRecordInfo.accountRecord.recordKey; return (await pool.openWrite(_contactRequestInboxKey, _writer, parent: accountRecordKey)) .maybeDeleteScope(!isSelf, (contactRequestInbox) async { final cs = await pool.veilid.getCryptoSystem(_contactRequestInboxKey.kind); final contactResponse = proto.ContactResponse() ..accept = false ..identityMasterRecordKey = _activeAccountInfo .localAccount.identityMaster.masterRecordKey .toProto(); final contactResponseBytes = contactResponse.writeToBuffer(); final identitySignature = await cs.sign( _activeAccountInfo.conversationWriter.key, _activeAccountInfo.conversationWriter.secret, contactResponseBytes); final signedContactResponse = proto.SignedContactResponse() ..contactResponse = contactResponseBytes ..identitySignature = identitySignature.toProto(); // Write the rejection to the inbox if (await contactRequestInbox.tryWriteProtobuf( proto.SignedContactResponse.fromBuffer, signedContactResponse, subkey: 1) != null) { log.error('failed to reject contact invitation'); return false; } return true; }); } // final ActiveAccountInfo _activeAccountInfo; final proto.Account _account; final TypedKey _contactRequestInboxKey; final IdentityMaster _contactIdentityMaster; final KeyPair _writer; final proto.ContactRequestPrivate _contactRequestPrivate; }