mirror of
https://gitlab.com/veilid/veilidchat.git
synced 2025-02-02 11:24:50 -05:00
contact accept
This commit is contained in:
parent
b12cbcf684
commit
6f525843ff
@ -88,7 +88,9 @@
|
|||||||
"paste": "Paste",
|
"paste": "Paste",
|
||||||
"message_from_contact": "Message from contact",
|
"message_from_contact": "Message from contact",
|
||||||
"validating": "Validating...",
|
"validating": "Validating...",
|
||||||
"invalid_invitation": "Invalid invitation"
|
"invalid_invitation": "Invalid invitation",
|
||||||
|
"failed_to_accept": "Failed to accept contact invite",
|
||||||
|
"failed_to_reject": "Failed to reject contact invite"
|
||||||
},
|
},
|
||||||
"enter_pin_dialog": {
|
"enter_pin_dialog": {
|
||||||
"enter_pin": "Enter PIN",
|
"enter_pin": "Enter PIN",
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
3. Set ContactRequest unicastinbox DHT record writer subkey with SignedContactResponse, encrypted with writer secret
|
3. Set ContactRequest unicastinbox DHT record writer subkey with SignedContactResponse, encrypted with writer secret
|
||||||
|
|
||||||
## Receiving an accept/reject
|
## Receiving an accept/reject
|
||||||
1. Open and get SignedContactResponse from ContactRequest unicaseinbox DHT record
|
1. Open and get SignedContactResponse from ContactRequest unicastinbox DHT record
|
||||||
2. Decrypt with writer secret
|
2. Decrypt with writer secret
|
||||||
3. Get DHT record for contact's AccountMaster
|
3. Get DHT record for contact's AccountMaster
|
||||||
4. Validate the SignedContactResponse signature
|
4. Validate the SignedContactResponse signature
|
||||||
@ -42,8 +42,10 @@
|
|||||||
If accept == false:
|
If accept == false:
|
||||||
1. Announce rejection
|
1. Announce rejection
|
||||||
2. Delete local invitation from table
|
2. Delete local invitation from table
|
||||||
|
3. Overwrite and delete ContactRequest inbox
|
||||||
|
|
||||||
If accept == true:
|
If accept == true:
|
||||||
1. Add a local contact with the remote chat dht record, updating from the remote profile in it.
|
1. Add a local contact with the remote chat dht record, updating from the remote profile in it.
|
||||||
2. Delete local invitation from table
|
2. Delete local invitation from table
|
||||||
|
3. Overwrite and delete ContactRequest inbox
|
||||||
|
|
||||||
|
@ -111,7 +111,24 @@ class PasteInviteDialogState extends ConsumerState<PasteInviteDialog> {
|
|||||||
}
|
}
|
||||||
final validInvitation = _validInvitation;
|
final validInvitation = _validInvitation;
|
||||||
if (validInvitation != null) {
|
if (validInvitation != null) {
|
||||||
await acceptContactInvitation(activeAccountInfo, validInvitation);
|
final acceptedContact =
|
||||||
|
await acceptContactInvitation(activeAccountInfo, validInvitation);
|
||||||
|
if (acceptedContact != null) {
|
||||||
|
await createContact(
|
||||||
|
activeAccountInfo: activeAccountInfo,
|
||||||
|
profile: acceptedContact.profile,
|
||||||
|
remoteIdentity: acceptedContact.remoteIdentity,
|
||||||
|
remoteConversation: acceptedContact.remoteConversation,
|
||||||
|
localConversation: acceptedContact.localConversation,
|
||||||
|
);
|
||||||
|
ref
|
||||||
|
..invalidate(fetchContactInvitationRecordsProvider)
|
||||||
|
..invalidate(fetchContactListProvider);
|
||||||
|
} else {
|
||||||
|
if (context.mounted) {
|
||||||
|
showErrorToast(context, 'paste_invite_dialog.failed_to_accept');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
setState(() {
|
setState(() {
|
||||||
_isAccepting = false;
|
_isAccepting = false;
|
||||||
@ -135,7 +152,13 @@ class PasteInviteDialogState extends ConsumerState<PasteInviteDialog> {
|
|||||||
}
|
}
|
||||||
final validInvitation = _validInvitation;
|
final validInvitation = _validInvitation;
|
||||||
if (validInvitation != null) {
|
if (validInvitation != null) {
|
||||||
await rejectContactInvitation(activeAccountInfo, validInvitation);
|
if (await rejectContactInvitation(activeAccountInfo, validInvitation)) {
|
||||||
|
// do nothing right now
|
||||||
|
} else {
|
||||||
|
if (context.mounted) {
|
||||||
|
showErrorToast(context, 'paste_invite_dialog.failed_to_reject');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
setState(() {
|
setState(() {
|
||||||
_isAccepting = false;
|
_isAccepting = false;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'dart:convert';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
|
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
|
||||||
@ -330,46 +331,79 @@ Future<ValidContactInvitation> validateContactInvitation(Uint8List inviteData,
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> acceptContactInvitation(ActiveAccountInfo activeAccountInfo,
|
class AcceptedContact {
|
||||||
|
AcceptedContact({
|
||||||
|
required this.profile,
|
||||||
|
required this.remoteIdentity,
|
||||||
|
required this.remoteConversation,
|
||||||
|
required this.localConversation,
|
||||||
|
});
|
||||||
|
|
||||||
|
proto.Profile profile;
|
||||||
|
IdentityMaster remoteIdentity;
|
||||||
|
TypedKey remoteConversation;
|
||||||
|
OwnedDHTRecordPointer localConversation;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<AcceptedContact?> acceptContactInvitation(
|
||||||
|
ActiveAccountInfo activeAccountInfo,
|
||||||
ValidContactInvitation validContactInvitation) async {
|
ValidContactInvitation validContactInvitation) async {
|
||||||
final pool = await DHTRecordPool.instance();
|
final pool = await DHTRecordPool.instance();
|
||||||
await (await pool.openWrite(validContactInvitation.contactRequestInboxKey,
|
final accountRecordKey =
|
||||||
|
activeAccountInfo.userLogin.accountRecordInfo.accountRecord.recordKey;
|
||||||
|
|
||||||
|
return (await pool.openWrite(validContactInvitation.contactRequestInboxKey,
|
||||||
validContactInvitation.writer))
|
validContactInvitation.writer))
|
||||||
.deleteScope((contactRequestInbox) async {
|
.deleteScope((contactRequestInbox) async {
|
||||||
final cs = await pool.veilid
|
final cs = await pool.veilid
|
||||||
.getCryptoSystem(validContactInvitation.contactRequestInboxKey.kind);
|
.getCryptoSystem(validContactInvitation.contactRequestInboxKey.kind);
|
||||||
|
|
||||||
// xxx
|
// Create local conversation key for this
|
||||||
final contactResponse = ContactResponse()
|
// contact and send via contact response
|
||||||
..accept = false
|
return (await pool.create(parent: accountRecordKey))
|
||||||
..identityMasterRecordKey = activeAccountInfo
|
.deleteScope((localConversation) async {
|
||||||
.localAccount.identityMaster.masterRecordKey
|
final contactResponse = ContactResponse()
|
||||||
.toProto();
|
..accept = true
|
||||||
final contactResponseBytes = contactResponse.writeToBuffer();
|
..remoteConversationKey = localConversation.key.toProto()
|
||||||
|
..identityMasterRecordKey = activeAccountInfo
|
||||||
|
.localAccount.identityMaster.masterRecordKey
|
||||||
|
.toProto();
|
||||||
|
final contactResponseBytes = contactResponse.writeToBuffer();
|
||||||
|
|
||||||
final identitySignature = await cs.sign(
|
final identitySignature = await cs.sign(
|
||||||
activeAccountInfo.localAccount.identityMaster.identityPublicKey,
|
activeAccountInfo.localAccount.identityMaster.identityPublicKey,
|
||||||
activeAccountInfo.userLogin.identitySecret.value,
|
activeAccountInfo.userLogin.identitySecret.value,
|
||||||
contactResponseBytes);
|
contactResponseBytes);
|
||||||
|
|
||||||
final signedContactResponse = SignedContactResponse()
|
final signedContactResponse = SignedContactResponse()
|
||||||
..contactResponse = contactResponseBytes
|
..contactResponse = contactResponseBytes
|
||||||
..identitySignature = identitySignature.toProto();
|
..identitySignature = identitySignature.toProto();
|
||||||
|
|
||||||
// Write the rejection to the invox
|
// Write the acceptance to the inbox
|
||||||
if (await contactRequestInbox.tryWriteProtobuf(
|
if (await contactRequestInbox.tryWriteProtobuf(
|
||||||
SignedContactResponse.fromBuffer, signedContactResponse,
|
SignedContactResponse.fromBuffer, signedContactResponse,
|
||||||
subkey: 1) !=
|
subkey: 1) !=
|
||||||
null) {
|
null) {
|
||||||
log.error('failed to accept contact invitation');
|
log.error('failed to accept contact invitation');
|
||||||
}
|
await localConversation.delete();
|
||||||
|
await contactRequestInbox.delete();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return AcceptedContact(
|
||||||
|
profile: validContactInvitation.contactRequestPrivate.profile,
|
||||||
|
remoteIdentity: validContactInvitation.contactIdentityMaster,
|
||||||
|
remoteConversation: proto.TypedKeyProto.fromProto(
|
||||||
|
validContactInvitation.contactRequestPrivate.chatRecordKey),
|
||||||
|
localConversation: localConversation.ownedDHTRecordPointer,
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> rejectContactInvitation(ActiveAccountInfo activeAccountInfo,
|
Future<bool> rejectContactInvitation(ActiveAccountInfo activeAccountInfo,
|
||||||
ValidContactInvitation validContactInvitation) async {
|
ValidContactInvitation validContactInvitation) async {
|
||||||
final pool = await DHTRecordPool.instance();
|
final pool = await DHTRecordPool.instance();
|
||||||
await (await pool.openWrite(validContactInvitation.contactRequestInboxKey,
|
return (await pool.openWrite(validContactInvitation.contactRequestInboxKey,
|
||||||
validContactInvitation.writer))
|
validContactInvitation.writer))
|
||||||
.deleteScope((contactRequestInbox) async {
|
.deleteScope((contactRequestInbox) async {
|
||||||
final cs = await pool.veilid
|
final cs = await pool.veilid
|
||||||
@ -397,6 +431,40 @@ Future<void> rejectContactInvitation(ActiveAccountInfo activeAccountInfo,
|
|||||||
subkey: 1) !=
|
subkey: 1) !=
|
||||||
null) {
|
null) {
|
||||||
log.error('failed to reject contact invitation');
|
log.error('failed to reject contact invitation');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> createContact({
|
||||||
|
required ActiveAccountInfo activeAccountInfo,
|
||||||
|
required proto.Profile profile,
|
||||||
|
required IdentityMaster remoteIdentity,
|
||||||
|
required TypedKey remoteConversation,
|
||||||
|
required OwnedDHTRecordPointer localConversation,
|
||||||
|
}) async {
|
||||||
|
final accountRecordKey =
|
||||||
|
activeAccountInfo.userLogin.accountRecordInfo.accountRecord.recordKey;
|
||||||
|
|
||||||
|
// Create Contact
|
||||||
|
final contact = Contact()
|
||||||
|
..editedProfile = profile
|
||||||
|
..remoteProfile = profile
|
||||||
|
..remoteIdentity = jsonEncode(remoteIdentity.toJson())
|
||||||
|
..remoteConversationKey = remoteConversation.toProto()
|
||||||
|
..localConversation = localConversation.toProto()
|
||||||
|
..showAvailability = false;
|
||||||
|
|
||||||
|
// Add Contact to account's list
|
||||||
|
// if this fails, don't keep retrying, user can try again later
|
||||||
|
await (await DHTShortArray.openOwned(
|
||||||
|
proto.OwnedDHTRecordPointerProto.fromProto(
|
||||||
|
activeAccountInfo.account.contactList),
|
||||||
|
parent: accountRecordKey))
|
||||||
|
.scope((contactList) async {
|
||||||
|
if (await contactList.tryAddItem(contact.writeToBuffer()) == false) {
|
||||||
|
throw StateError('Failed to add contact');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user