diff --git a/lib/entities/proto/veilidchat.pb.dart b/lib/entities/proto/veilidchat.pb.dart index fc3ca45..09e940d 100644 --- a/lib/entities/proto/veilidchat.pb.dart +++ b/lib/entities/proto/veilidchat.pb.dart @@ -932,7 +932,7 @@ class Conversation extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Conversation', createEmptyInstance: create) ..aOM(1, _omitFieldNames ? '' : 'profile', subBuilder: Profile.create) ..aOS(2, _omitFieldNames ? '' : 'identityMasterJson') - ..aOM(3, _omitFieldNames ? '' : 'messages', subBuilder: OwnedDHTRecordPointer.create) + ..aOM(3, _omitFieldNames ? '' : 'messages', subBuilder: TypedKey.create) ..hasRequiredFields = false ; @@ -978,15 +978,15 @@ class Conversation extends $pb.GeneratedMessage { void clearIdentityMasterJson() => clearField(2); @$pb.TagNumber(3) - OwnedDHTRecordPointer get messages => $_getN(2); + TypedKey get messages => $_getN(2); @$pb.TagNumber(3) - set messages(OwnedDHTRecordPointer v) { setField(3, v); } + set messages(TypedKey v) { setField(3, v); } @$pb.TagNumber(3) $core.bool hasMessages() => $_has(2); @$pb.TagNumber(3) void clearMessages() => clearField(3); @$pb.TagNumber(3) - OwnedDHTRecordPointer ensureMessages() => $_ensure(2); + TypedKey ensureMessages() => $_ensure(2); } class Contact extends $pb.GeneratedMessage { diff --git a/lib/entities/proto/veilidchat.pbjson.dart b/lib/entities/proto/veilidchat.pbjson.dart index eefc75f..95b842f 100644 --- a/lib/entities/proto/veilidchat.pbjson.dart +++ b/lib/entities/proto/veilidchat.pbjson.dart @@ -285,15 +285,15 @@ const Conversation$json = { '2': [ {'1': 'profile', '3': 1, '4': 1, '5': 11, '6': '.Profile', '10': 'profile'}, {'1': 'identity_master_json', '3': 2, '4': 1, '5': 9, '10': 'identityMasterJson'}, - {'1': 'messages', '3': 3, '4': 1, '5': 11, '6': '.OwnedDHTRecordPointer', '10': 'messages'}, + {'1': 'messages', '3': 3, '4': 1, '5': 11, '6': '.TypedKey', '10': 'messages'}, ], }; /// Descriptor for `Conversation`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List conversationDescriptor = $convert.base64Decode( 'CgxDb252ZXJzYXRpb24SIgoHcHJvZmlsZRgBIAEoCzIILlByb2ZpbGVSB3Byb2ZpbGUSMAoUaW' - 'RlbnRpdHlfbWFzdGVyX2pzb24YAiABKAlSEmlkZW50aXR5TWFzdGVySnNvbhIyCghtZXNzYWdl' - 'cxgDIAEoCzIWLk93bmVkREhUUmVjb3JkUG9pbnRlclIIbWVzc2FnZXM='); + 'RlbnRpdHlfbWFzdGVyX2pzb24YAiABKAlSEmlkZW50aXR5TWFzdGVySnNvbhIlCghtZXNzYWdl' + 'cxgDIAEoCzIJLlR5cGVkS2V5UghtZXNzYWdlcw=='); @$core.Deprecated('Use contactDescriptor instead') const Contact$json = { diff --git a/lib/entities/veilidchat.proto b/lib/entities/veilidchat.proto index ca11446..c1067f3 100644 --- a/lib/entities/veilidchat.proto +++ b/lib/entities/veilidchat.proto @@ -156,6 +156,7 @@ message Attachment { // A single message as part of a series of messages // Messages are stored in a DHTLog +// DHT Schema: SMPL(0,1,[identityPublicKey]) message Message { // Author of the message TypedKey author = 1; @@ -183,7 +184,7 @@ message Conversation { // Identity master (JSON) to publish to friend string identity_master_json = 2; // Messages DHTLog (xxx for now DHTShortArray) - OwnedDHTRecordPointer messages = 3; + TypedKey messages = 3; } // A record of a contact that has accepted a contact invitation diff --git a/lib/providers/conversation.dart b/lib/providers/conversation.dart index 11d8094..e382ced 100644 --- a/lib/providers/conversation.dart +++ b/lib/providers/conversation.dart @@ -205,3 +205,36 @@ Future?> getLocalConversationMessages({ return out; }); } + +Future?> getRemoteConversationMessages({ + required ActiveAccountInfo activeAccountInfo, + required TypedKey remoteConversationKey, + required TypedKey remoteIdentityPublicKey, +}) async { + final conversation = await readRemoteConversation( + activeAccountInfo: activeAccountInfo, + remoteConversationKey: remoteConversationKey, + remoteIdentityPublicKey: remoteIdentityPublicKey); + if (conversation == null) { + return null; + } + final messagesOwned = + proto.OwnedDHTRecordPointerProto.fromProto(conversation.messages); + final crypto = await getConversationCrypto( + activeAccountInfo: activeAccountInfo, + remoteIdentityPublicKey: remoteIdentityPublicKey); + + return (await DHTShortArray.openOwned(messagesOwned, + parent: localConversationOwned.recordKey, crypto: crypto)) + .scope((messages) async { + var out = IList(); + for (var i = 0; i < messages.length; i++) { + final msg = await messages.getItemProtobuf(proto.Message.fromBuffer, i); + if (msg == null) { + throw Exception('Failed to get message'); + } + out = out.add(msg); + } + return out; + }); +} diff --git a/lib/veilid_support/dht_support/dht_short_array.dart b/lib/veilid_support/dht_support/dht_short_array.dart index c896dab..86dbab3 100644 --- a/lib/veilid_support/dht_support/dht_short_array.dart +++ b/lib/veilid_support/dht_support/dht_short_array.dart @@ -37,12 +37,15 @@ class DHTShortArray { late final int stride; switch (headRecord.schema) { case DHTSchemaDFLT(oCnt: final oCnt): - stride = oCnt - 1; - if (stride <= 0) { - throw StateError('Invalid stride in DHTShortArray'); + if (oCnt <= 1) { + throw StateError('Invalid DFLT schema in DHTShortArray'); } - case DHTSchemaSMPL(): - throw StateError('Wrote kind of DHT record for DHTShortArray'); + stride = oCnt - 1; + case DHTSchemaSMPL(oCnt: final oCnt, members: final members): + if (oCnt != 0 || members.length != 1 || members[1].mCnt <= 1) { + throw StateError('Invalid SMPL schema in DHTShortArray'); + } + stride = members[1].mCnt - 1; } assert(stride <= maxElements, 'stride too long'); _stride = stride; @@ -61,15 +64,34 @@ class DHTShortArray { {int stride = maxElements, VeilidRoutingContext? routingContext, TypedKey? parent, - DHTRecordCrypto? crypto}) async { + DHTRecordCrypto? crypto, + KeyPair? smplWriter}) async { assert(stride <= maxElements, 'stride too long'); final pool = await DHTRecordPool.instance(); - final dhtRecord = await pool.create( - parent: parent, - routingContext: routingContext, - schema: DHTSchema.dflt(oCnt: stride + 1), - crypto: crypto); + late final DHTRecord dhtRecord; + if (smplWriter != null) { + final schema = DHTSchema.smpl( + oCnt: 0, + members: [DHTSchemaMember(mKey: smplWriter.key, mCnt: stride + 1)]); + final dhtCreateRecord = await pool.create( + parent: parent, + routingContext: routingContext, + schema: schema, + crypto: crypto); + // Reopen with SMPL writer + await dhtCreateRecord.close(); + dhtRecord = await pool.openWrite(dhtCreateRecord.key, smplWriter, + parent: parent, routingContext: routingContext, crypto: crypto); + } else { + final schema = DHTSchema.dflt(oCnt: stride + 1); + dhtRecord = await pool.create( + parent: parent, + routingContext: routingContext, + schema: schema, + crypto: crypto); + } + try { final dhtShortArray = DHTShortArray._(headRecord: dhtRecord); if (!await dhtShortArray._tryWriteHead()) { diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt index ad3732c..b669b11 100644 --- a/linux/CMakeLists.txt +++ b/linux/CMakeLists.txt @@ -7,7 +7,7 @@ project(runner LANGUAGES CXX) set(BINARY_NAME "veilidchat") # The unique GTK application identifier for this application. See: # https://wiki.gnome.org/HowDoI/ChooseApplicationID -set(APPLICATION_ID "com.vekoni.veilidchat.veilidchat") +set(APPLICATION_ID "com.veilid.veilidchat") # Explicitly opt in to modern CMake behaviors to avoid warnings with recent # versions of CMake. diff --git a/snap/gui/app_icon.desktop b/snap/gui/app_icon.desktop index 92a01f6..3b42dc7 100644 --- a/snap/gui/app_icon.desktop +++ b/snap/gui/app_icon.desktop @@ -1,8 +1,8 @@ [Desktop Entry] -Name=Flutter Linux App -Comment=Flutter Linux launcher icon -Exec=app_icon +Name=VeilidChat +Comment=VeilidChat Private Messaging +Exec=veilidchat Icon=app_icon.png Terminal=false Type=Application -Categories=Entertainment; +Categories=Network;