mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-06-25 08:44:08 +00:00
fix: group auto rentry when it was deleted
Some checks are pending
Flutter analyze & test / flutter_analyze_and_test (push) Waiting to run
Some checks are pending
Flutter analyze & test / flutter_analyze_and_test (push) Waiting to run
This commit is contained in:
parent
e23ac5932e
commit
e7301020f6
16 changed files with 392 additions and 162 deletions
|
|
@ -368,10 +368,12 @@ class EncryptedContent_GroupCreate extends $pb.GeneratedMessage {
|
||||||
factory EncryptedContent_GroupCreate({
|
factory EncryptedContent_GroupCreate({
|
||||||
$core.List<$core.int>? stateKey,
|
$core.List<$core.int>? stateKey,
|
||||||
$core.List<$core.int>? groupPublicKey,
|
$core.List<$core.int>? groupPublicKey,
|
||||||
|
$core.String? groupName,
|
||||||
}) {
|
}) {
|
||||||
final result = create();
|
final result = create();
|
||||||
if (stateKey != null) result.stateKey = stateKey;
|
if (stateKey != null) result.stateKey = stateKey;
|
||||||
if (groupPublicKey != null) result.groupPublicKey = groupPublicKey;
|
if (groupPublicKey != null) result.groupPublicKey = groupPublicKey;
|
||||||
|
if (groupName != null) result.groupName = groupName;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -391,6 +393,7 @@ class EncryptedContent_GroupCreate extends $pb.GeneratedMessage {
|
||||||
3, _omitFieldNames ? '' : 'stateKey', $pb.PbFieldType.OY)
|
3, _omitFieldNames ? '' : 'stateKey', $pb.PbFieldType.OY)
|
||||||
..a<$core.List<$core.int>>(
|
..a<$core.List<$core.int>>(
|
||||||
4, _omitFieldNames ? '' : 'groupPublicKey', $pb.PbFieldType.OY)
|
4, _omitFieldNames ? '' : 'groupPublicKey', $pb.PbFieldType.OY)
|
||||||
|
..aOS(5, _omitFieldNames ? '' : 'groupName')
|
||||||
..hasRequiredFields = false;
|
..hasRequiredFields = false;
|
||||||
|
|
||||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||||
|
|
@ -433,6 +436,15 @@ class EncryptedContent_GroupCreate extends $pb.GeneratedMessage {
|
||||||
$core.bool hasGroupPublicKey() => $_has(1);
|
$core.bool hasGroupPublicKey() => $_has(1);
|
||||||
@$pb.TagNumber(4)
|
@$pb.TagNumber(4)
|
||||||
void clearGroupPublicKey() => $_clearField(4);
|
void clearGroupPublicKey() => $_clearField(4);
|
||||||
|
|
||||||
|
@$pb.TagNumber(5)
|
||||||
|
$core.String get groupName => $_getSZ(2);
|
||||||
|
@$pb.TagNumber(5)
|
||||||
|
set groupName($core.String value) => $_setString(2, value);
|
||||||
|
@$pb.TagNumber(5)
|
||||||
|
$core.bool hasGroupName() => $_has(2);
|
||||||
|
@$pb.TagNumber(5)
|
||||||
|
void clearGroupName() => $_clearField(5);
|
||||||
}
|
}
|
||||||
|
|
||||||
class EncryptedContent_GroupJoin extends $pb.GeneratedMessage {
|
class EncryptedContent_GroupJoin extends $pb.GeneratedMessage {
|
||||||
|
|
|
||||||
|
|
@ -79,16 +79,20 @@ class EncryptedContent_ErrorMessages_Type extends $pb.ProtobufEnum {
|
||||||
static const EncryptedContent_ErrorMessages_Type SESSION_OUT_OF_SYNC =
|
static const EncryptedContent_ErrorMessages_Type SESSION_OUT_OF_SYNC =
|
||||||
EncryptedContent_ErrorMessages_Type._(
|
EncryptedContent_ErrorMessages_Type._(
|
||||||
3, _omitEnumNames ? '' : 'SESSION_OUT_OF_SYNC');
|
3, _omitEnumNames ? '' : 'SESSION_OUT_OF_SYNC');
|
||||||
|
static const EncryptedContent_ErrorMessages_Type
|
||||||
|
GROUP_NOT_FOUND_OR_NOT_A_MEMBER = EncryptedContent_ErrorMessages_Type._(
|
||||||
|
4, _omitEnumNames ? '' : 'GROUP_NOT_FOUND_OR_NOT_A_MEMBER');
|
||||||
|
|
||||||
static const $core.List<EncryptedContent_ErrorMessages_Type> values =
|
static const $core.List<EncryptedContent_ErrorMessages_Type> values =
|
||||||
<EncryptedContent_ErrorMessages_Type>[
|
<EncryptedContent_ErrorMessages_Type>[
|
||||||
ERROR_PROCESSING_MESSAGE_CREATED_ACCOUNT_REQUEST_INSTEAD,
|
ERROR_PROCESSING_MESSAGE_CREATED_ACCOUNT_REQUEST_INSTEAD,
|
||||||
UNKNOWN_MESSAGE_TYPE,
|
UNKNOWN_MESSAGE_TYPE,
|
||||||
SESSION_OUT_OF_SYNC,
|
SESSION_OUT_OF_SYNC,
|
||||||
|
GROUP_NOT_FOUND_OR_NOT_A_MEMBER,
|
||||||
];
|
];
|
||||||
|
|
||||||
static final $core.List<EncryptedContent_ErrorMessages_Type?> _byValue =
|
static final $core.List<EncryptedContent_ErrorMessages_Type?> _byValue =
|
||||||
$pb.ProtobufEnum.$_initByValueList(values, 3);
|
$pb.ProtobufEnum.$_initByValueList(values, 4);
|
||||||
static EncryptedContent_ErrorMessages_Type? valueOf($core.int value) =>
|
static EncryptedContent_ErrorMessages_Type? valueOf($core.int value) =>
|
||||||
value < 0 || value >= _byValue.length ? null : _byValue[value];
|
value < 0 || value >= _byValue.length ? null : _byValue[value];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -465,6 +465,7 @@ const EncryptedContent_ErrorMessages_Type$json = {
|
||||||
{'1': 'ERROR_PROCESSING_MESSAGE_CREATED_ACCOUNT_REQUEST_INSTEAD', '2': 0},
|
{'1': 'ERROR_PROCESSING_MESSAGE_CREATED_ACCOUNT_REQUEST_INSTEAD', '2': 0},
|
||||||
{'1': 'UNKNOWN_MESSAGE_TYPE', '2': 2},
|
{'1': 'UNKNOWN_MESSAGE_TYPE', '2': 2},
|
||||||
{'1': 'SESSION_OUT_OF_SYNC', '2': 3},
|
{'1': 'SESSION_OUT_OF_SYNC', '2': 3},
|
||||||
|
{'1': 'GROUP_NOT_FOUND_OR_NOT_A_MEMBER', '2': 4},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -474,6 +475,18 @@ const EncryptedContent_GroupCreate$json = {
|
||||||
'2': [
|
'2': [
|
||||||
{'1': 'state_key', '3': 3, '4': 1, '5': 12, '10': 'stateKey'},
|
{'1': 'state_key', '3': 3, '4': 1, '5': 12, '10': 'stateKey'},
|
||||||
{'1': 'group_public_key', '3': 4, '4': 1, '5': 12, '10': 'groupPublicKey'},
|
{'1': 'group_public_key', '3': 4, '4': 1, '5': 12, '10': 'groupPublicKey'},
|
||||||
|
{
|
||||||
|
'1': 'group_name',
|
||||||
|
'3': 5,
|
||||||
|
'4': 1,
|
||||||
|
'5': 9,
|
||||||
|
'9': 0,
|
||||||
|
'10': 'groupName',
|
||||||
|
'17': true
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'8': [
|
||||||
|
{'1': '_group_name'},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -975,78 +988,79 @@ final $typed_data.Uint8List encryptedContentDescriptor = $convert.base64Decode(
|
||||||
'aXNjb3ZlcnlSZXF1ZXN0iAEBEl4KFXVzZXJfZGlzY292ZXJ5X3VwZGF0ZRgXIAEoCzIlLkVuY3'
|
'aXNjb3ZlcnlSZXF1ZXN0iAEBEl4KFXVzZXJfZGlzY292ZXJ5X3VwZGF0ZRgXIAEoCzIlLkVuY3'
|
||||||
'J5cHRlZENvbnRlbnQuVXNlckRpc2NvdmVyeVVwZGF0ZUgWUhN1c2VyRGlzY292ZXJ5VXBkYXRl'
|
'J5cHRlZENvbnRlbnQuVXNlckRpc2NvdmVyeVVwZGF0ZUgWUhN1c2VyRGlzY292ZXJ5VXBkYXRl'
|
||||||
'iAEBEmEKFmtleV92ZXJpZmljYXRpb25fcHJvb2YYGCABKAsyJi5FbmNyeXB0ZWRDb250ZW50Lk'
|
'iAEBEmEKFmtleV92ZXJpZmljYXRpb25fcHJvb2YYGCABKAsyJi5FbmNyeXB0ZWRDb250ZW50Lk'
|
||||||
'tleVZlcmlmaWNhdGlvblByb29mSBdSFGtleVZlcmlmaWNhdGlvblByb29miAEBGvABCg1FcnJv'
|
'tleVZlcmlmaWNhdGlvblByb29mSBdSFGtleVZlcmlmaWNhdGlvblByb29miAEBGpYCCg1FcnJv'
|
||||||
'ck1lc3NhZ2VzEjgKBHR5cGUYASABKA4yJC5FbmNyeXB0ZWRDb250ZW50LkVycm9yTWVzc2FnZX'
|
'ck1lc3NhZ2VzEjgKBHR5cGUYASABKA4yJC5FbmNyeXB0ZWRDb250ZW50LkVycm9yTWVzc2FnZX'
|
||||||
'MuVHlwZVIEdHlwZRIsChJyZWxhdGVkX3JlY2VpcHRfaWQYAiABKAlSEHJlbGF0ZWRSZWNlaXB0'
|
'MuVHlwZVIEdHlwZRIsChJyZWxhdGVkX3JlY2VpcHRfaWQYAiABKAlSEHJlbGF0ZWRSZWNlaXB0'
|
||||||
'SWQidwoEVHlwZRI8CjhFUlJPUl9QUk9DRVNTSU5HX01FU1NBR0VfQ1JFQVRFRF9BQ0NPVU5UX1'
|
'SWQinAEKBFR5cGUSPAo4RVJST1JfUFJPQ0VTU0lOR19NRVNTQUdFX0NSRUFURURfQUNDT1VOVF'
|
||||||
'JFUVVFU1RfSU5TVEVBRBAAEhgKFFVOS05PV05fTUVTU0FHRV9UWVBFEAISFwoTU0VTU0lPTl9P'
|
'9SRVFVRVNUX0lOU1RFQUQQABIYChRVTktOT1dOX01FU1NBR0VfVFlQRRACEhcKE1NFU1NJT05f'
|
||||||
'VVRfT0ZfU1lOQxADGlQKC0dyb3VwQ3JlYXRlEhsKCXN0YXRlX2tleRgDIAEoDFIIc3RhdGVLZX'
|
'T1VUX09GX1NZTkMQAxIjCh9HUk9VUF9OT1RfRk9VTkRfT1JfTk9UX0FfTUVNQkVSEAQahwEKC0'
|
||||||
'kSKAoQZ3JvdXBfcHVibGljX2tleRgEIAEoDFIOZ3JvdXBQdWJsaWNLZXkaNQoJR3JvdXBKb2lu'
|
'dyb3VwQ3JlYXRlEhsKCXN0YXRlX2tleRgDIAEoDFIIc3RhdGVLZXkSKAoQZ3JvdXBfcHVibGlj'
|
||||||
'EigKEGdyb3VwX3B1YmxpY19rZXkYASABKAxSDmdyb3VwUHVibGljS2V5GhYKFFJlc2VuZEdyb3'
|
'X2tleRgEIAEoDFIOZ3JvdXBQdWJsaWNLZXkSIgoKZ3JvdXBfbmFtZRgFIAEoCUgAUglncm91cE'
|
||||||
'VwUHVibGljS2V5GsgCCgtHcm91cFVwZGF0ZRIqChFncm91cF9hY3Rpb25fdHlwZRgBIAEoCVIP'
|
'5hbWWIAQFCDQoLX2dyb3VwX25hbWUaNQoJR3JvdXBKb2luEigKEGdyb3VwX3B1YmxpY19rZXkY'
|
||||||
'Z3JvdXBBY3Rpb25UeXBlEjMKE2FmZmVjdGVkX2NvbnRhY3RfaWQYAiABKANIAFIRYWZmZWN0ZW'
|
'ASABKAxSDmdyb3VwUHVibGljS2V5GhYKFFJlc2VuZEdyb3VwUHVibGljS2V5GsgCCgtHcm91cF'
|
||||||
'RDb250YWN0SWSIAQESKQoObmV3X2dyb3VwX25hbWUYAyABKAlIAVIMbmV3R3JvdXBOYW1liAEB'
|
'VwZGF0ZRIqChFncm91cF9hY3Rpb25fdHlwZRgBIAEoCVIPZ3JvdXBBY3Rpb25UeXBlEjMKE2Fm'
|
||||||
'ElcKJm5ld19kZWxldGVfbWVzc2FnZXNfYWZ0ZXJfbWlsbGlzZWNvbmRzGAQgASgDSAJSIm5ld0'
|
'ZmVjdGVkX2NvbnRhY3RfaWQYAiABKANIAFIRYWZmZWN0ZWRDb250YWN0SWSIAQESKQoObmV3X2'
|
||||||
'RlbGV0ZU1lc3NhZ2VzQWZ0ZXJNaWxsaXNlY29uZHOIAQFCFgoUX2FmZmVjdGVkX2NvbnRhY3Rf'
|
'dyb3VwX25hbWUYAyABKAlIAVIMbmV3R3JvdXBOYW1liAEBElcKJm5ld19kZWxldGVfbWVzc2Fn'
|
||||||
'aWRCEQoPX25ld19ncm91cF9uYW1lQikKJ19uZXdfZGVsZXRlX21lc3NhZ2VzX2FmdGVyX21pbG'
|
'ZXNfYWZ0ZXJfbWlsbGlzZWNvbmRzGAQgASgDSAJSIm5ld0RlbGV0ZU1lc3NhZ2VzQWZ0ZXJNaW'
|
||||||
'xpc2Vjb25kcxqvAQoLVGV4dE1lc3NhZ2USKgoRc2VuZGVyX21lc3NhZ2VfaWQYASABKAlSD3Nl'
|
'xsaXNlY29uZHOIAQFCFgoUX2FmZmVjdGVkX2NvbnRhY3RfaWRCEQoPX25ld19ncm91cF9uYW1l'
|
||||||
'bmRlck1lc3NhZ2VJZBISCgR0ZXh0GAIgASgJUgR0ZXh0EhwKCXRpbWVzdGFtcBgDIAEoA1IJdG'
|
'QikKJ19uZXdfZGVsZXRlX21lc3NhZ2VzX2FmdGVyX21pbGxpc2Vjb25kcxqvAQoLVGV4dE1lc3'
|
||||||
'ltZXN0YW1wEi0KEHF1b3RlX21lc3NhZ2VfaWQYBCABKAlIAFIOcXVvdGVNZXNzYWdlSWSIAQFC'
|
'NhZ2USKgoRc2VuZGVyX21lc3NhZ2VfaWQYASABKAlSD3NlbmRlck1lc3NhZ2VJZBISCgR0ZXh0'
|
||||||
'EwoRX3F1b3RlX21lc3NhZ2VfaWQazgEKFUFkZGl0aW9uYWxEYXRhTWVzc2FnZRIqChFzZW5kZX'
|
'GAIgASgJUgR0ZXh0EhwKCXRpbWVzdGFtcBgDIAEoA1IJdGltZXN0YW1wEi0KEHF1b3RlX21lc3'
|
||||||
'JfbWVzc2FnZV9pZBgBIAEoCVIPc2VuZGVyTWVzc2FnZUlkEhwKCXRpbWVzdGFtcBgCIAEoA1IJ'
|
'NhZ2VfaWQYBCABKAlIAFIOcXVvdGVNZXNzYWdlSWSIAQFCEwoRX3F1b3RlX21lc3NhZ2VfaWQa'
|
||||||
'dGltZXN0YW1wEhIKBHR5cGUYAyABKAlSBHR5cGUSOwoXYWRkaXRpb25hbF9tZXNzYWdlX2RhdG'
|
'zgEKFUFkZGl0aW9uYWxEYXRhTWVzc2FnZRIqChFzZW5kZXJfbWVzc2FnZV9pZBgBIAEoCVIPc2'
|
||||||
'EYBCABKAxIAFIVYWRkaXRpb25hbE1lc3NhZ2VEYXRhiAEBQhoKGF9hZGRpdGlvbmFsX21lc3Nh'
|
'VuZGVyTWVzc2FnZUlkEhwKCXRpbWVzdGFtcBgCIAEoA1IJdGltZXN0YW1wEhIKBHR5cGUYAyAB'
|
||||||
'Z2VfZGF0YRpkCghSZWFjdGlvbhIqChF0YXJnZXRfbWVzc2FnZV9pZBgBIAEoCVIPdGFyZ2V0TW'
|
'KAlSBHR5cGUSOwoXYWRkaXRpb25hbF9tZXNzYWdlX2RhdGEYBCABKAxIAFIVYWRkaXRpb25hbE'
|
||||||
'Vzc2FnZUlkEhQKBWVtb2ppGAIgASgJUgVlbW9qaRIWCgZyZW1vdmUYAyABKAhSBnJlbW92ZRq+'
|
'1lc3NhZ2VEYXRhiAEBQhoKGF9hZGRpdGlvbmFsX21lc3NhZ2VfZGF0YRpkCghSZWFjdGlvbhIq'
|
||||||
'AgoNTWVzc2FnZVVwZGF0ZRI4CgR0eXBlGAEgASgOMiQuRW5jcnlwdGVkQ29udGVudC5NZXNzYW'
|
'ChF0YXJnZXRfbWVzc2FnZV9pZBgBIAEoCVIPdGFyZ2V0TWVzc2FnZUlkEhQKBWVtb2ppGAIgAS'
|
||||||
'dlVXBkYXRlLlR5cGVSBHR5cGUSLwoRc2VuZGVyX21lc3NhZ2VfaWQYAiABKAlIAFIPc2VuZGVy'
|
'gJUgVlbW9qaRIWCgZyZW1vdmUYAyABKAhSBnJlbW92ZRq+AgoNTWVzc2FnZVVwZGF0ZRI4CgR0'
|
||||||
'TWVzc2FnZUlkiAEBEj0KG211bHRpcGxlX3RhcmdldF9tZXNzYWdlX2lkcxgDIAMoCVIYbXVsdG'
|
'eXBlGAEgASgOMiQuRW5jcnlwdGVkQ29udGVudC5NZXNzYWdlVXBkYXRlLlR5cGVSBHR5cGUSLw'
|
||||||
'lwbGVUYXJnZXRNZXNzYWdlSWRzEhcKBHRleHQYBCABKAlIAVIEdGV4dIgBARIcCgl0aW1lc3Rh'
|
'oRc2VuZGVyX21lc3NhZ2VfaWQYAiABKAlIAFIPc2VuZGVyTWVzc2FnZUlkiAEBEj0KG211bHRp'
|
||||||
'bXAYBSABKANSCXRpbWVzdGFtcCItCgRUeXBlEgoKBkRFTEVURRAAEg0KCUVESVRfVEVYVBABEg'
|
'cGxlX3RhcmdldF9tZXNzYWdlX2lkcxgDIAMoCVIYbXVsdGlwbGVUYXJnZXRNZXNzYWdlSWRzEh'
|
||||||
'oKBk9QRU5FRBACQhQKEl9zZW5kZXJfbWVzc2FnZV9pZEIHCgVfdGV4dBqFBgoFTWVkaWESKgoR'
|
'cKBHRleHQYBCABKAlIAVIEdGV4dIgBARIcCgl0aW1lc3RhbXAYBSABKANSCXRpbWVzdGFtcCIt'
|
||||||
'c2VuZGVyX21lc3NhZ2VfaWQYASABKAlSD3NlbmRlck1lc3NhZ2VJZBIwCgR0eXBlGAIgASgOMh'
|
'CgRUeXBlEgoKBkRFTEVURRAAEg0KCUVESVRfVEVYVBABEgoKBk9QRU5FRBACQhQKEl9zZW5kZX'
|
||||||
'wuRW5jcnlwdGVkQ29udGVudC5NZWRpYS5UeXBlUgR0eXBlEkYKHWRpc3BsYXlfbGltaXRfaW5f'
|
'JfbWVzc2FnZV9pZEIHCgVfdGV4dBqFBgoFTWVkaWESKgoRc2VuZGVyX21lc3NhZ2VfaWQYASAB'
|
||||||
'bWlsbGlzZWNvbmRzGAMgASgDSABSGmRpc3BsYXlMaW1pdEluTWlsbGlzZWNvbmRziAEBEjcKF3'
|
'KAlSD3NlbmRlck1lc3NhZ2VJZBIwCgR0eXBlGAIgASgOMhwuRW5jcnlwdGVkQ29udGVudC5NZW'
|
||||||
'JlcXVpcmVzX2F1dGhlbnRpY2F0aW9uGAQgASgIUhZyZXF1aXJlc0F1dGhlbnRpY2F0aW9uEhwK'
|
'RpYS5UeXBlUgR0eXBlEkYKHWRpc3BsYXlfbGltaXRfaW5fbWlsbGlzZWNvbmRzGAMgASgDSABS'
|
||||||
'CXRpbWVzdGFtcBgFIAEoA1IJdGltZXN0YW1wEi0KEHF1b3RlX21lc3NhZ2VfaWQYBiABKAlIAV'
|
'GmRpc3BsYXlMaW1pdEluTWlsbGlzZWNvbmRziAEBEjcKF3JlcXVpcmVzX2F1dGhlbnRpY2F0aW'
|
||||||
'IOcXVvdGVNZXNzYWdlSWSIAQESKgoOZG93bmxvYWRfdG9rZW4YByABKAxIAlINZG93bmxvYWRU'
|
'9uGAQgASgIUhZyZXF1aXJlc0F1dGhlbnRpY2F0aW9uEhwKCXRpbWVzdGFtcBgFIAEoA1IJdGlt'
|
||||||
'b2tlbogBARIqCg5lbmNyeXB0aW9uX2tleRgIIAEoDEgDUg1lbmNyeXB0aW9uS2V5iAEBEioKDm'
|
'ZXN0YW1wEi0KEHF1b3RlX21lc3NhZ2VfaWQYBiABKAlIAVIOcXVvdGVNZXNzYWdlSWSIAQESKg'
|
||||||
'VuY3J5cHRpb25fbWFjGAkgASgMSARSDWVuY3J5cHRpb25NYWOIAQESLgoQZW5jcnlwdGlvbl9u'
|
'oOZG93bmxvYWRfdG9rZW4YByABKAxIAlINZG93bmxvYWRUb2tlbogBARIqCg5lbmNyeXB0aW9u'
|
||||||
'b25jZRgKIAEoDEgFUg9lbmNyeXB0aW9uTm9uY2WIAQESOwoXYWRkaXRpb25hbF9tZXNzYWdlX2'
|
'X2tleRgIIAEoDEgDUg1lbmNyeXB0aW9uS2V5iAEBEioKDmVuY3J5cHRpb25fbWFjGAkgASgMSA'
|
||||||
'RhdGEYCyABKAxIBlIVYWRkaXRpb25hbE1lc3NhZ2VEYXRhiAEBIj4KBFR5cGUSDAoIUkVVUExP'
|
'RSDWVuY3J5cHRpb25NYWOIAQESLgoQZW5jcnlwdGlvbl9ub25jZRgKIAEoDEgFUg9lbmNyeXB0'
|
||||||
'QUQQABIJCgVJTUFHRRABEgkKBVZJREVPEAISBwoDR0lGEAMSCQoFQVVESU8QBEIgCh5fZGlzcG'
|
'aW9uTm9uY2WIAQESOwoXYWRkaXRpb25hbF9tZXNzYWdlX2RhdGEYCyABKAxIBlIVYWRkaXRpb2'
|
||||||
'xheV9saW1pdF9pbl9taWxsaXNlY29uZHNCEwoRX3F1b3RlX21lc3NhZ2VfaWRCEQoPX2Rvd25s'
|
'5hbE1lc3NhZ2VEYXRhiAEBIj4KBFR5cGUSDAoIUkVVUExPQUQQABIJCgVJTUFHRRABEgkKBVZJ'
|
||||||
'b2FkX3Rva2VuQhEKD19lbmNyeXB0aW9uX2tleUIRCg9fZW5jcnlwdGlvbl9tYWNCEwoRX2VuY3'
|
'REVPEAISBwoDR0lGEAMSCQoFQVVESU8QBEIgCh5fZGlzcGxheV9saW1pdF9pbl9taWxsaXNlY2'
|
||||||
'J5cHRpb25fbm9uY2VCGgoYX2FkZGl0aW9uYWxfbWVzc2FnZV9kYXRhGqkBCgtNZWRpYVVwZGF0'
|
'9uZHNCEwoRX3F1b3RlX21lc3NhZ2VfaWRCEQoPX2Rvd25sb2FkX3Rva2VuQhEKD19lbmNyeXB0'
|
||||||
'ZRI2CgR0eXBlGAEgASgOMiIuRW5jcnlwdGVkQ29udGVudC5NZWRpYVVwZGF0ZS5UeXBlUgR0eX'
|
'aW9uX2tleUIRCg9fZW5jcnlwdGlvbl9tYWNCEwoRX2VuY3J5cHRpb25fbm9uY2VCGgoYX2FkZG'
|
||||||
'BlEioKEXRhcmdldF9tZXNzYWdlX2lkGAIgASgJUg90YXJnZXRNZXNzYWdlSWQiNgoEVHlwZRIM'
|
'l0aW9uYWxfbWVzc2FnZV9kYXRhGqkBCgtNZWRpYVVwZGF0ZRI2CgR0eXBlGAEgASgOMiIuRW5j'
|
||||||
'CghSRU9QRU5FRBAAEgoKBlNUT1JFRBABEhQKEERFQ1JZUFRJT05fRVJST1IQAhp4Cg5Db250YW'
|
'cnlwdGVkQ29udGVudC5NZWRpYVVwZGF0ZS5UeXBlUgR0eXBlEioKEXRhcmdldF9tZXNzYWdlX2'
|
||||||
'N0UmVxdWVzdBI5CgR0eXBlGAEgASgOMiUuRW5jcnlwdGVkQ29udGVudC5Db250YWN0UmVxdWVz'
|
'lkGAIgASgJUg90YXJnZXRNZXNzYWdlSWQiNgoEVHlwZRIMCghSRU9QRU5FRBAAEgoKBlNUT1JF'
|
||||||
'dC5UeXBlUgR0eXBlIisKBFR5cGUSCwoHUkVRVUVTVBAAEgoKBlJFSkVDVBABEgoKBkFDQ0VQVB'
|
'RBABEhQKEERFQ1JZUFRJT05fRVJST1IQAhp4Cg5Db250YWN0UmVxdWVzdBI5CgR0eXBlGAEgAS'
|
||||||
'ACGqQCCg1Db250YWN0VXBkYXRlEjgKBHR5cGUYASABKA4yJC5FbmNyeXB0ZWRDb250ZW50LkNv'
|
'gOMiUuRW5jcnlwdGVkQ29udGVudC5Db250YWN0UmVxdWVzdC5UeXBlUgR0eXBlIisKBFR5cGUS'
|
||||||
'bnRhY3RVcGRhdGUuVHlwZVIEdHlwZRI3ChVhdmF0YXJfc3ZnX2NvbXByZXNzZWQYAiABKAxIAF'
|
'CwoHUkVRVUVTVBAAEgoKBlJFSkVDVBABEgoKBkFDQ0VQVBACGqQCCg1Db250YWN0VXBkYXRlEj'
|
||||||
'ITYXZhdGFyU3ZnQ29tcHJlc3NlZIgBARIfCgh1c2VybmFtZRgDIAEoCUgBUgh1c2VybmFtZYgB'
|
'gKBHR5cGUYASABKA4yJC5FbmNyeXB0ZWRDb250ZW50LkNvbnRhY3RVcGRhdGUuVHlwZVIEdHlw'
|
||||||
'ARImCgxkaXNwbGF5X25hbWUYBCABKAlIAlILZGlzcGxheU5hbWWIAQEiHwoEVHlwZRILCgdSRV'
|
'ZRI3ChVhdmF0YXJfc3ZnX2NvbXByZXNzZWQYAiABKAxIAFITYXZhdGFyU3ZnQ29tcHJlc3NlZI'
|
||||||
'FVRVNUEAASCgoGVVBEQVRFEAFCGAoWX2F2YXRhcl9zdmdfY29tcHJlc3NlZEILCglfdXNlcm5h'
|
'gBARIfCgh1c2VybmFtZRgDIAEoCUgBUgh1c2VybmFtZYgBARImCgxkaXNwbGF5X25hbWUYBCAB'
|
||||||
'bWVCDwoNX2Rpc3BsYXlfbmFtZRrZAQoIUHVzaEtleXMSMwoEdHlwZRgBIAEoDjIfLkVuY3J5cH'
|
'KAlIAlILZGlzcGxheU5hbWWIAQEiHwoEVHlwZRILCgdSRVFVRVNUEAASCgoGVVBEQVRFEAFCGA'
|
||||||
'RlZENvbnRlbnQuUHVzaEtleXMuVHlwZVIEdHlwZRIaCgZrZXlfaWQYAiABKANIAFIFa2V5SWSI'
|
'oWX2F2YXRhcl9zdmdfY29tcHJlc3NlZEILCglfdXNlcm5hbWVCDwoNX2Rpc3BsYXlfbmFtZRrZ'
|
||||||
'AQESFQoDa2V5GAMgASgMSAFSA2tleYgBARIiCgpjcmVhdGVkX2F0GAQgASgDSAJSCWNyZWF0ZW'
|
'AQoIUHVzaEtleXMSMwoEdHlwZRgBIAEoDjIfLkVuY3J5cHRlZENvbnRlbnQuUHVzaEtleXMuVH'
|
||||||
'RBdIgBASIfCgRUeXBlEgsKB1JFUVVFU1QQABIKCgZVUERBVEUQAUIJCgdfa2V5X2lkQgYKBF9r'
|
'lwZVIEdHlwZRIaCgZrZXlfaWQYAiABKANIAFIFa2V5SWSIAQESFQoDa2V5GAMgASgMSAFSA2tl'
|
||||||
'ZXlCDQoLX2NyZWF0ZWRfYXQarwEKCUZsYW1lU3luYxIjCg1mbGFtZV9jb3VudGVyGAEgASgDUg'
|
'eYgBARIiCgpjcmVhdGVkX2F0GAQgASgDSAJSCWNyZWF0ZWRBdIgBASIfCgRUeXBlEgsKB1JFUV'
|
||||||
'xmbGFtZUNvdW50ZXISOQoZbGFzdF9mbGFtZV9jb3VudGVyX2NoYW5nZRgCIAEoA1IWbGFzdEZs'
|
'VFU1QQABIKCgZVUERBVEUQAUIJCgdfa2V5X2lkQgYKBF9rZXlCDQoLX2NyZWF0ZWRfYXQarwEK'
|
||||||
'YW1lQ291bnRlckNoYW5nZRIfCgtiZXN0X2ZyaWVuZBgDIAEoCFIKYmVzdEZyaWVuZBIhCgxmb3'
|
'CUZsYW1lU3luYxIjCg1mbGFtZV9jb3VudGVyGAEgASgDUgxmbGFtZUNvdW50ZXISOQoZbGFzdF'
|
||||||
'JjZV91cGRhdGUYBCABKAhSC2ZvcmNlVXBkYXRlGk0KD1R5cGluZ0luZGljYXRvchIbCglpc190'
|
'9mbGFtZV9jb3VudGVyX2NoYW5nZRgCIAEoA1IWbGFzdEZsYW1lQ291bnRlckNoYW5nZRIfCgti'
|
||||||
'eXBpbmcYASABKAhSCGlzVHlwaW5nEh0KCmNyZWF0ZWRfYXQYAiABKANSCWNyZWF0ZWRBdBo/Ch'
|
'ZXN0X2ZyaWVuZBgDIAEoCFIKYmVzdEZyaWVuZBIhCgxmb3JjZV91cGRhdGUYBCABKAhSC2Zvcm'
|
||||||
'RVc2VyRGlzY292ZXJ5UmVxdWVzdBInCg9jdXJyZW50X3ZlcnNpb24YASABKAxSDmN1cnJlbnRW'
|
'NlVXBkYXRlGk0KD1R5cGluZ0luZGljYXRvchIbCglpc190eXBpbmcYASABKAhSCGlzVHlwaW5n'
|
||||||
'ZXJzaW9uGjEKE1VzZXJEaXNjb3ZlcnlVcGRhdGUSGgoIbWVzc2FnZXMYASADKAxSCG1lc3NhZ2'
|
'Eh0KCmNyZWF0ZWRfYXQYAiABKANSCWNyZWF0ZWRBdBo/ChRVc2VyRGlzY292ZXJ5UmVxdWVzdB'
|
||||||
'VzGj0KFEtleVZlcmlmaWNhdGlvblByb29mEiUKDmNhbGN1bGF0ZWRfbWFjGAEgASgMUg1jYWxj'
|
'InCg9jdXJyZW50X3ZlcnNpb24YASABKAxSDmN1cnJlbnRWZXJzaW9uGjEKE1VzZXJEaXNjb3Zl'
|
||||||
'dWxhdGVkTWFjQgsKCV9ncm91cF9pZEIRCg9faXNfZGlyZWN0X2NoYXRCGQoXX3NlbmRlcl9wcm'
|
'cnlVcGRhdGUSGgoIbWVzc2FnZXMYASADKAxSCG1lc3NhZ2VzGj0KFEtleVZlcmlmaWNhdGlvbl'
|
||||||
'9maWxlX2NvdW50ZXJCIAoeX3NlbmRlcl91c2VyX2Rpc2NvdmVyeV92ZXJzaW9uQhwKGl9hc2tf'
|
'Byb29mEiUKDmNhbGN1bGF0ZWRfbWFjGAEgASgMUg1jYWxjdWxhdGVkTWFjQgsKCV9ncm91cF9p'
|
||||||
'Zm9yX2ZyaWVuZF9wcm9tb3Rpb25zQhEKD19tZXNzYWdlX3VwZGF0ZUIICgZfbWVkaWFCDwoNX2'
|
'ZEIRCg9faXNfZGlyZWN0X2NoYXRCGQoXX3NlbmRlcl9wcm9maWxlX2NvdW50ZXJCIAoeX3Nlbm'
|
||||||
'1lZGlhX3VwZGF0ZUIRCg9fY29udGFjdF91cGRhdGVCEgoQX2NvbnRhY3RfcmVxdWVzdEINCgtf'
|
'Rlcl91c2VyX2Rpc2NvdmVyeV92ZXJzaW9uQhwKGl9hc2tfZm9yX2ZyaWVuZF9wcm9tb3Rpb25z'
|
||||||
'ZmxhbWVfc3luY0IMCgpfcHVzaF9rZXlzQgsKCV9yZWFjdGlvbkIPCg1fdGV4dF9tZXNzYWdlQg'
|
'QhEKD19tZXNzYWdlX3VwZGF0ZUIICgZfbWVkaWFCDwoNX21lZGlhX3VwZGF0ZUIRCg9fY29udG'
|
||||||
'8KDV9ncm91cF9jcmVhdGVCDQoLX2dyb3VwX2pvaW5CDwoNX2dyb3VwX3VwZGF0ZUIaChhfcmVz'
|
'FjdF91cGRhdGVCEgoQX2NvbnRhY3RfcmVxdWVzdEINCgtfZmxhbWVfc3luY0IMCgpfcHVzaF9r'
|
||||||
'ZW5kX2dyb3VwX3B1YmxpY19rZXlCEQoPX2Vycm9yX21lc3NhZ2VzQhoKGF9hZGRpdGlvbmFsX2'
|
'ZXlzQgsKCV9yZWFjdGlvbkIPCg1fdGV4dF9tZXNzYWdlQg8KDV9ncm91cF9jcmVhdGVCDQoLX2'
|
||||||
'RhdGFfbWVzc2FnZUITChFfdHlwaW5nX2luZGljYXRvckIZChdfdXNlcl9kaXNjb3ZlcnlfcmVx'
|
'dyb3VwX2pvaW5CDwoNX2dyb3VwX3VwZGF0ZUIaChhfcmVzZW5kX2dyb3VwX3B1YmxpY19rZXlC'
|
||||||
'dWVzdEIYChZfdXNlcl9kaXNjb3ZlcnlfdXBkYXRlQhkKF19rZXlfdmVyaWZpY2F0aW9uX3Byb2'
|
'EQoPX2Vycm9yX21lc3NhZ2VzQhoKGF9hZGRpdGlvbmFsX2RhdGFfbWVzc2FnZUITChFfdHlwaW'
|
||||||
'9m');
|
'5nX2luZGljYXRvckIZChdfdXNlcl9kaXNjb3ZlcnlfcmVxdWVzdEIYChZfdXNlcl9kaXNjb3Zl'
|
||||||
|
'cnlfdXBkYXRlQhkKF19rZXlfdmVyaWZpY2F0aW9uX3Byb29m');
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,7 @@ message EncryptedContent {
|
||||||
ERROR_PROCESSING_MESSAGE_CREATED_ACCOUNT_REQUEST_INSTEAD = 0;
|
ERROR_PROCESSING_MESSAGE_CREATED_ACCOUNT_REQUEST_INSTEAD = 0;
|
||||||
UNKNOWN_MESSAGE_TYPE = 2;
|
UNKNOWN_MESSAGE_TYPE = 2;
|
||||||
SESSION_OUT_OF_SYNC = 3;
|
SESSION_OUT_OF_SYNC = 3;
|
||||||
|
GROUP_NOT_FOUND_OR_NOT_A_MEMBER = 4;
|
||||||
}
|
}
|
||||||
Type type = 1;
|
Type type = 1;
|
||||||
string related_receipt_id = 2;
|
string related_receipt_id = 2;
|
||||||
|
|
@ -71,8 +72,9 @@ message EncryptedContent {
|
||||||
|
|
||||||
message GroupCreate {
|
message GroupCreate {
|
||||||
// key for the state stored on the server
|
// key for the state stored on the server
|
||||||
bytes state_key = 3;
|
bytes state_key = 3;
|
||||||
bytes group_public_key = 4;
|
bytes group_public_key = 4;
|
||||||
|
optional string group_name = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
message GroupJoin {
|
message GroupJoin {
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,22 @@
|
||||||
import 'package:clock/clock.dart';
|
import 'package:clock/clock.dart';
|
||||||
import 'package:drift/drift.dart' show Value;
|
import 'package:drift/drift.dart' show Value;
|
||||||
|
import 'package:fixnum/fixnum.dart' show Int64;
|
||||||
|
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart'
|
||||||
|
show IdentityKeyPair;
|
||||||
import 'package:twonly/locator.dart';
|
import 'package:twonly/locator.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/model/protobuf/client/generated/messages.pbserver.dart';
|
import 'package:twonly/src/model/protobuf/client/generated/messages.pbserver.dart';
|
||||||
|
import 'package:twonly/src/services/api/messages.api.dart'
|
||||||
|
show sendCipherText, tryToSendCompleteMessage;
|
||||||
|
import 'package:twonly/src/services/group.service.dart' show fetchGroupState;
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
|
|
||||||
Future<void> handleErrorMessage(
|
Future<void> handleErrorMessage(
|
||||||
int fromUserId,
|
int fromUserId,
|
||||||
EncryptedContent_ErrorMessages error,
|
EncryptedContent_ErrorMessages error,
|
||||||
String receiptId,
|
String receiptId, {
|
||||||
) async {
|
String? groupId,
|
||||||
|
}) async {
|
||||||
Log.error('[$receiptId] Got error from $fromUserId: $error');
|
Log.error('[$receiptId] Got error from $fromUserId: $error');
|
||||||
|
|
||||||
switch (error.type) {
|
switch (error.type) {
|
||||||
|
|
@ -29,6 +36,59 @@ Future<void> handleErrorMessage(
|
||||||
);
|
);
|
||||||
case EncryptedContent_ErrorMessages_Type.SESSION_OUT_OF_SYNC:
|
case EncryptedContent_ErrorMessages_Type.SESSION_OUT_OF_SYNC:
|
||||||
break; // The other user initiated a new signal session, so ignore the error in this case, as the new session works...
|
break; // The other user initiated a new signal session, so ignore the error in this case, as the new session works...
|
||||||
|
case EncryptedContent_ErrorMessages_Type.GROUP_NOT_FOUND_OR_NOT_A_MEMBER:
|
||||||
|
if (groupId == null) {
|
||||||
|
Log.error(
|
||||||
|
'[$receiptId] GROUP_NOT_FOUND_OR_NOT_A_MEMBER error received, but groupId is null.',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final group = await twonlyDB.groupsDao.getGroup(groupId);
|
||||||
|
if (group == null) {
|
||||||
|
Log.error(
|
||||||
|
'[$receiptId] GROUP_NOT_FOUND_OR_NOT_A_MEMBER error received, but group $groupId is not found in database.',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Update group state from the server to ensure the user is still part of the group...
|
||||||
|
final updatedState = await fetchGroupState(group);
|
||||||
|
if (updatedState != null) {
|
||||||
|
final (_, state) = updatedState;
|
||||||
|
final isStillMember = state.memberIds.contains(Int64(fromUserId));
|
||||||
|
if (isStillMember) {
|
||||||
|
final keyPair = IdentityKeyPair.fromSerialized(
|
||||||
|
group.myGroupPrivateKey!,
|
||||||
|
);
|
||||||
|
await sendCipherText(
|
||||||
|
fromUserId,
|
||||||
|
EncryptedContent(
|
||||||
|
groupId: groupId,
|
||||||
|
groupCreate: EncryptedContent_GroupCreate(
|
||||||
|
stateKey: group.stateEncryptionKey,
|
||||||
|
groupPublicKey: keyPair.getPublicKey().serialize(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
final r = await twonlyDB.receiptsDao.getReceiptById(
|
||||||
|
error.relatedReceiptId,
|
||||||
|
);
|
||||||
|
if (r != null) {
|
||||||
|
await twonlyDB.receiptsDao.updateReceiptWidthUserId(
|
||||||
|
fromUserId,
|
||||||
|
error.relatedReceiptId,
|
||||||
|
ReceiptsCompanion(
|
||||||
|
markForRetry: Value(clock.now()),
|
||||||
|
retryCount: Value(r.retryCount + 1),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// then resend: error.relatedReceiptId
|
||||||
|
await tryToSendCompleteMessage(
|
||||||
|
receiptId: error.relatedReceiptId,
|
||||||
|
blocking: false,
|
||||||
|
);
|
||||||
|
} else {}
|
||||||
// ignore: no_default_cases
|
// ignore: no_default_cases
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,9 @@ Future<void> handleGroupCreate(
|
||||||
EncryptedContent_GroupCreate newGroup,
|
EncryptedContent_GroupCreate newGroup,
|
||||||
String receiptId,
|
String receiptId,
|
||||||
) async {
|
) async {
|
||||||
final user = await twonlyDB.contactsDao.getContactByUserId(fromUserId).getSingleOrNull();
|
final user = await twonlyDB.contactsDao
|
||||||
|
.getContactByUserId(fromUserId)
|
||||||
|
.getSingleOrNull();
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
// Only contacts can invite other contacts, so this can (via the UI) not happen.
|
// Only contacts can invite other contacts, so this can (via the UI) not happen.
|
||||||
Log.error(
|
Log.error(
|
||||||
|
|
@ -43,11 +45,26 @@ Future<void> handleGroupCreate(
|
||||||
stateVersionId: const Value(0),
|
stateVersionId: const Value(0),
|
||||||
stateEncryptionKey: Value(Uint8List.fromList(newGroup.stateKey)),
|
stateEncryptionKey: Value(Uint8List.fromList(newGroup.stateKey)),
|
||||||
myGroupPrivateKey: Value(myGroupKey.serialize()),
|
myGroupPrivateKey: Value(myGroupKey.serialize()),
|
||||||
groupName: const Value(''),
|
groupName: Value(newGroup.hasGroupName() ? newGroup.groupName : ''),
|
||||||
joinedGroup: const Value(false),
|
joinedGroup: const Value(false),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
// In this case make a group state update and check if the fromUserId is still a admin. otherwise return with an log error message
|
||||||
|
final updatedState = await fetchGroupState(group);
|
||||||
|
if (updatedState == null) {
|
||||||
|
Log.error(
|
||||||
|
'[$receiptId] Received group invite/create for $groupId, but failed to fetch group state from server.',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final (_, state) = updatedState;
|
||||||
|
if (!state.adminIds.any((id) => id.toInt() == fromUserId)) {
|
||||||
|
Log.error(
|
||||||
|
'[$receiptId] Received group invite/create for $groupId from $fromUserId, but they are not an admin of this group.',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
// User was already in the group, so update leftGroup back to false
|
// User was already in the group, so update leftGroup back to false
|
||||||
await twonlyDB.groupsDao.updateGroup(
|
await twonlyDB.groupsDao.updateGroup(
|
||||||
groupId,
|
groupId,
|
||||||
|
|
@ -55,7 +72,6 @@ Future<void> handleGroupCreate(
|
||||||
stateVersionId: const Value(0),
|
stateVersionId: const Value(0),
|
||||||
stateEncryptionKey: Value(Uint8List.fromList(newGroup.stateKey)),
|
stateEncryptionKey: Value(Uint8List.fromList(newGroup.stateKey)),
|
||||||
myGroupPrivateKey: Value(myGroupKey.serialize()),
|
myGroupPrivateKey: Value(myGroupKey.serialize()),
|
||||||
groupName: const Value(''),
|
|
||||||
joinedGroup: const Value(false),
|
joinedGroup: const Value(false),
|
||||||
leftGroup: const Value(false),
|
leftGroup: const Value(false),
|
||||||
deletedContent: const Value(false),
|
deletedContent: const Value(false),
|
||||||
|
|
@ -79,18 +95,8 @@ Future<void> handleGroupCreate(
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
await twonlyDB.groupsDao.insertOrUpdateGroupMember(
|
// Load group members from the server as this is the single source of truth.
|
||||||
GroupMembersCompanion(
|
// This can be done in the background, so the WebSocket message can be ACKed.
|
||||||
groupId: Value(groupId),
|
|
||||||
contactId: Value(fromUserId),
|
|
||||||
memberState: const Value(
|
|
||||||
MemberState.admin, // is the group creator, so must be admin...
|
|
||||||
),
|
|
||||||
groupPublicKey: Value(Uint8List.fromList(newGroup.groupPublicKey)),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// can be done in the background -> websocket message can be ACK
|
|
||||||
unawaited(fetchGroupStatesForUnjoinedGroups());
|
unawaited(fetchGroupStatesForUnjoinedGroups());
|
||||||
|
|
||||||
await sendCipherTextToGroup(
|
await sendCipherTextToGroup(
|
||||||
|
|
@ -113,7 +119,9 @@ Future<void> handleGroupUpdate(
|
||||||
|
|
||||||
final actionType = groupActionTypeFromString(update.groupActionType);
|
final actionType = groupActionTypeFromString(update.groupActionType);
|
||||||
if (actionType == null) {
|
if (actionType == null) {
|
||||||
Log.error('[$receiptId] Group action ${update.groupActionType} is unknown ignoring.');
|
Log.error(
|
||||||
|
'[$receiptId] Group action ${update.groupActionType} is unknown ignoring.',
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -177,7 +177,17 @@ Future<(Uint8List, Uint8List?)?> _tryToSendCompleteMessageInternal({
|
||||||
Uint8List.fromList(message.encryptedContent),
|
Uint8List.fromList(message.encryptedContent),
|
||||||
);
|
);
|
||||||
if (cipherText == null) {
|
if (cipherText == null) {
|
||||||
Log.error('Could not encrypt the message. Aborting and trying again.');
|
Log.error(
|
||||||
|
'[${receipt.receiptId}] Could not encrypt the message for user ${receipt.contactId}. Aborting and trying again.',
|
||||||
|
);
|
||||||
|
if (receipt.messageId != null) {
|
||||||
|
await twonlyDB.messagesDao.handleMessageAckByServer(
|
||||||
|
receipt.contactId,
|
||||||
|
receipt.messageId!,
|
||||||
|
clock.now(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await twonlyDB.receiptsDao.deleteReceipt(receipt.receiptId);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
message.encryptedContent = cipherText.serialize();
|
message.encryptedContent = cipherText.serialize();
|
||||||
|
|
@ -435,7 +445,7 @@ Future<(Uint8List, Uint8List?)?> sendCipherText(
|
||||||
final openReceipts = await twonlyDB.receiptsDao.getReceiptCountForContact(
|
final openReceipts = await twonlyDB.receiptsDao.getReceiptCountForContact(
|
||||||
contactId,
|
contactId,
|
||||||
);
|
);
|
||||||
if (openReceipts > 6) {
|
if (openReceipts > 10) {
|
||||||
// this prevents that these types of messages are send in case the receiver is offline
|
// this prevents that these types of messages are send in case the receiver is offline
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -211,6 +211,8 @@ Future<void> _handleClient2ClientMessage(
|
||||||
type: Message_Type.CIPHERTEXT,
|
type: Message_Type.CIPHERTEXT,
|
||||||
encryptedContent: encryptedContent.writeToBuffer(),
|
encryptedContent: encryptedContent.writeToBuffer(),
|
||||||
);
|
);
|
||||||
|
// Use Value.absent() for CIPHERTEXT messages so that insertReceipt generates a new UUID.
|
||||||
|
// This prevents receipt ID collisions and ensures the recipient's ACK is tracked correctly.
|
||||||
receiptIdDB = const Value.absent();
|
receiptIdDB = const Value.absent();
|
||||||
} else {
|
} else {
|
||||||
// Message was successful processed
|
// Message was successful processed
|
||||||
|
|
@ -219,8 +221,9 @@ Future<void> _handleClient2ClientMessage(
|
||||||
|
|
||||||
response ??= Message(type: Message_Type.SENDER_DELIVERY_RECEIPT);
|
response ??= Message(type: Message_Type.SENDER_DELIVERY_RECEIPT);
|
||||||
|
|
||||||
|
String? targetReceiptId;
|
||||||
try {
|
try {
|
||||||
await twonlyDB.receiptsDao.insertReceipt(
|
final inserted = await twonlyDB.receiptsDao.insertReceipt(
|
||||||
ReceiptsCompanion(
|
ReceiptsCompanion(
|
||||||
receiptId: receiptIdDB ?? Value(receiptId),
|
receiptId: receiptIdDB ?? Value(receiptId),
|
||||||
contactId: Value(fromUserId),
|
contactId: Value(fromUserId),
|
||||||
|
|
@ -228,10 +231,18 @@ Future<void> _handleClient2ClientMessage(
|
||||||
contactWillSendsReceipt: const Value(false),
|
contactWillSendsReceipt: const Value(false),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
// Use the inserted receipt's ID because for CIPHERTEXT messages we generate a new UUID
|
||||||
|
// (receiptIdDB is Value.absent()) to avoid ID collisions and properly track individual ACKs.
|
||||||
|
targetReceiptId = inserted?.receiptId;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Log.warn('[$receiptId] Error inserting receipt: $e');
|
Log.warn('[$receiptId] Error inserting receipt: $e');
|
||||||
}
|
}
|
||||||
await tryToSendCompleteMessage(receiptId: receiptId, blocking: false);
|
if (targetReceiptId != null) {
|
||||||
|
await tryToSendCompleteMessage(
|
||||||
|
receiptId: targetReceiptId,
|
||||||
|
blocking: false,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case Message_Type.TEST_NOTIFICATION:
|
case Message_Type.TEST_NOTIFICATION:
|
||||||
break;
|
break;
|
||||||
|
|
@ -350,6 +361,7 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage(
|
||||||
fromUserId,
|
fromUserId,
|
||||||
content.errorMessages,
|
content.errorMessages,
|
||||||
receiptId,
|
receiptId,
|
||||||
|
groupId: content.hasGroupId() ? content.groupId : null,
|
||||||
);
|
);
|
||||||
return (null, null);
|
return (null, null);
|
||||||
}
|
}
|
||||||
|
|
@ -430,6 +442,7 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage(
|
||||||
|
|
||||||
/// Verify that the user is (still) in that group...
|
/// Verify that the user is (still) in that group...
|
||||||
if (!await twonlyDB.groupsDao.isContactInGroup(fromUserId, content.groupId)) {
|
if (!await twonlyDB.groupsDao.isContactInGroup(fromUserId, content.groupId)) {
|
||||||
|
// Check if this is a direct chat...
|
||||||
if (getUUIDforDirectChat(userService.currentUser.userId, fromUserId) ==
|
if (getUUIDforDirectChat(userService.currentUser.userId, fromUserId) ==
|
||||||
content.groupId) {
|
content.groupId) {
|
||||||
final contact = await twonlyDB.contactsDao
|
final contact = await twonlyDB.contactsDao
|
||||||
|
|
@ -477,9 +490,19 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage(
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.error(
|
Log.error(
|
||||||
'[$receiptId] User $fromUserId tried to access group ${content.groupId}.',
|
'[$receiptId] User $fromUserId tried to access group ${content.groupId}. Sending GROUP_NOT_FOUND_OR_NOT_A_MEMBER error.',
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
EncryptedContent(
|
||||||
|
groupId: content.groupId,
|
||||||
|
errorMessages: EncryptedContent_ErrorMessages(
|
||||||
|
type: EncryptedContent_ErrorMessages_Type
|
||||||
|
.GROUP_NOT_FOUND_OR_NOT_A_MEMBER,
|
||||||
|
relatedReceiptId: receiptId,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
null,
|
||||||
);
|
);
|
||||||
return (null, null);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -139,6 +139,7 @@ Future<bool> createNewGroup(String groupName, List<Contact> members) async {
|
||||||
groupCreate: EncryptedContent_GroupCreate(
|
groupCreate: EncryptedContent_GroupCreate(
|
||||||
stateKey: stateEncryptionKey,
|
stateKey: stateEncryptionKey,
|
||||||
groupPublicKey: myGroupKey.getPublicKey().serialize(),
|
groupPublicKey: myGroupKey.getPublicKey().serialize(),
|
||||||
|
groupName: group.groupName,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -770,6 +771,7 @@ Future<bool> addNewGroupMembers(
|
||||||
groupCreate: EncryptedContent_GroupCreate(
|
groupCreate: EncryptedContent_GroupCreate(
|
||||||
stateKey: group.stateEncryptionKey,
|
stateKey: group.stateEncryptionKey,
|
||||||
groupPublicKey: keyPair.getPublicKey().serialize(),
|
groupPublicKey: keyPair.getPublicKey().serialize(),
|
||||||
|
groupName: group.groupName,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ Future<CiphertextMessage?> _signalEncryptMessage(
|
||||||
final session = SessionCipher.fromStore(signalStore, address);
|
final session = SessionCipher.fromStore(signalStore, address);
|
||||||
return await session.encrypt(plaintextContent);
|
return await session.encrypt(plaintextContent);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Log.error(e.toString());
|
Log.error('Could not encrypt message for target $target: $e');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -78,12 +78,13 @@ class GroupContextMenu extends StatelessWidget {
|
||||||
if (group.isDirectChat) {
|
if (group.isDirectChat) {
|
||||||
await twonlyDB.groupsDao.deleteGroup(group.groupId);
|
await twonlyDB.groupsDao.deleteGroup(group.groupId);
|
||||||
} else {
|
} else {
|
||||||
await twonlyDB.groupsDao.updateGroup(
|
await twonlyDB.groupsDao.deleteGroup(group.groupId);
|
||||||
group.groupId,
|
// await twonlyDB.groupsDao.updateGroup(
|
||||||
const GroupsCompanion(
|
// group.groupId,
|
||||||
deletedContent: Value(true),
|
// const GroupsCompanion(
|
||||||
),
|
// deletedContent: Value(true),
|
||||||
);
|
// ),
|
||||||
|
// );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -141,13 +141,13 @@ class _ContactRowState extends State<_ContactRow> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (added > 0) await importSignalContactAndCreateRequest(userdata);
|
||||||
|
|
||||||
await KeyVerificationService.verifySharedContact(
|
await KeyVerificationService.verifySharedContact(
|
||||||
contactId: userdata.userId.toInt(),
|
contactId: userdata.userId.toInt(),
|
||||||
sharedPublicIdentityKey: widget.contact.publicIdentityKey,
|
sharedPublicIdentityKey: widget.contact.publicIdentityKey,
|
||||||
senderId: widget.message.senderId!,
|
senderId: widget.message.senderId!,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (added > 0) await importSignalContactAndCreateRequest(userdata);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Log.error(e);
|
Log.error(e);
|
||||||
} finally {
|
} finally {
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ class _TypingIndicatorState extends State<TypingIndicator> {
|
||||||
|
|
||||||
StreamSubscription<List<(Contact, GroupMember)>>? membersSub;
|
StreamSubscription<List<(Contact, GroupMember)>>? membersSub;
|
||||||
Timer? _periodicUpdate;
|
Timer? _periodicUpdate;
|
||||||
bool _wasShownOnce = false;
|
double _wasShownOnce = 0;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
|
@ -81,19 +81,22 @@ class _TypingIndicatorState extends State<TypingIndicator> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (_groupMembers.isEmpty) {
|
if (_groupMembers.isEmpty) {
|
||||||
return SizedBox(height: _wasShownOnce ? 19 : 0);
|
return SizedBox(height: _wasShownOnce);
|
||||||
} else {
|
|
||||||
_wasShownOnce = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final height =
|
||||||
|
(widget.group.isDirectChat ? 20 : 24) * _groupMembers.length.toDouble();
|
||||||
|
_wasShownOnce = height;
|
||||||
|
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: 19,
|
height: height,
|
||||||
child: Align(
|
child: Align(
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(left: 12),
|
padding: const EdgeInsets.only(left: 12),
|
||||||
child: Row(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: _groupMembers
|
children: _groupMembers
|
||||||
.map(
|
.map(
|
||||||
(member) => Padding(
|
(member) => Padding(
|
||||||
|
|
|
||||||
66
pubspec.lock
66
pubspec.lock
|
|
@ -65,7 +65,7 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.4"
|
version: "1.0.4"
|
||||||
archive:
|
archive:
|
||||||
dependency: "direct main"
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: archive
|
name: archive
|
||||||
sha256: a96e8b390886ee8abb49b7bd3ac8df6f451c621619f52a26e815fdcf568959ff
|
sha256: a96e8b390886ee8abb49b7bd3ac8df6f451c621619f52a26e815fdcf568959ff
|
||||||
|
|
@ -389,18 +389,18 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: device_info_plus
|
name: device_info_plus
|
||||||
sha256: b4fed1b2835da9d670d7bed7db79ae2a94b0f5ad6312268158a9b5479abbacdd
|
sha256: "6a642e1daa10190af89ba6cb6386c0df7d071a3592080bfe1e44faa63ae1df65"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "12.4.0"
|
version: "13.1.0"
|
||||||
device_info_plus_platform_interface:
|
device_info_plus_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: device_info_plus_platform_interface
|
name: device_info_plus_platform_interface
|
||||||
sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f
|
sha256: "04b173a92e2d9161dfead145667037c8d834db725ce2e7b942bfe18fd2f45a46"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.0.3"
|
version: "8.1.0"
|
||||||
dots_indicator:
|
dots_indicator:
|
||||||
dependency: "direct overridden"
|
dependency: "direct overridden"
|
||||||
description:
|
description:
|
||||||
|
|
@ -471,6 +471,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.0"
|
version: "2.2.0"
|
||||||
|
ffi_leak_tracker:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: ffi_leak_tracker
|
||||||
|
sha256: "4093d4ef9ca06ffe2786e73bfb25e22aa92112b9bb4ec941f11e3e6b61489a97"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.2"
|
||||||
file:
|
file:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -479,14 +487,6 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.0.1"
|
version: "7.0.1"
|
||||||
file_picker:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: file_picker
|
|
||||||
sha256: "57d9a1dd5063f85fa3107fb42d1faffda52fdc948cefd5fe5ea85267a5fc7343"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "10.3.10"
|
|
||||||
file_selector_linux:
|
file_selector_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -834,10 +834,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: flutter_secure_storage_windows
|
name: flutter_secure_storage_windows
|
||||||
sha256: "3b7c8e068875dfd46719ff57c90d8c459c87f2302ed6b00ff006b3c9fcad1613"
|
sha256: "471951813a97006d899db4948acc654a4f28c440083ea08178935ce20b173ec1"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.1.0"
|
version: "4.2.2"
|
||||||
flutter_sharing_intent:
|
flutter_sharing_intent:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
@ -892,14 +892,6 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.2"
|
version: "2.3.2"
|
||||||
get:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: get
|
|
||||||
sha256: "5ed34a7925b85336e15d472cc4cfe7d9ebf4ab8e8b9f688585bf6b50f4c3d79a"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "4.7.3"
|
|
||||||
get_it:
|
get_it:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
@ -1037,10 +1029,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: image_picker
|
name: image_picker
|
||||||
sha256: "784210112be18ea55f69d7076e2c656a4e24949fa9e76429fe53af0c0f4fa320"
|
sha256: "91c025426c2881c551100bce834e201c835a170151545f58d17da5180ca7d9ac"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.1"
|
version: "1.2.2"
|
||||||
image_picker_android:
|
image_picker_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -1381,18 +1373,18 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: package_info_plus
|
name: package_info_plus
|
||||||
sha256: "468c26b4254ab01979fa5e4a98cb343ea3631b9acee6f21028997419a80e1a20"
|
sha256: "4bf625947f6c7713ee242296a682e23e44823c09cf9d79e4f1238923c92db852"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "9.0.1"
|
version: "10.1.0"
|
||||||
package_info_plus_platform_interface:
|
package_info_plus_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: package_info_plus_platform_interface
|
name: package_info_plus_platform_interface
|
||||||
sha256: "202a487f08836a592a6bd4f901ac69b3a8f146af552bbd14407b6b41e1c3f086"
|
sha256: db762cb2f4f25ee60fb6359773861b0f199e00b90d237bd85a76a1e806b46ef4
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.2.1"
|
version: "4.1.0"
|
||||||
path:
|
path:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
@ -1694,18 +1686,18 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: share_plus
|
name: share_plus
|
||||||
sha256: "223873d106614442ea6f20db5a038685cc5b32a2fba81cdecaefbbae0523f7fa"
|
sha256: a857d8b1479250aff6b57a51b2c02d31ca05848d441817c43f1640c885c286c0
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "12.0.2"
|
version: "13.1.0"
|
||||||
share_plus_platform_interface:
|
share_plus_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: share_plus_platform_interface
|
name: share_plus_platform_interface
|
||||||
sha256: "88023e53a13429bd65d8e85e11a9b484f49d4c190abbd96c7932b74d6927cc9a"
|
sha256: "7f7ae28cf400d13f811e297ff37742dba83b79e0a6f5dce14eec0248274e6ce9"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.0"
|
version: "7.1.0"
|
||||||
shared_preferences:
|
shared_preferences:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -2170,18 +2162,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: win32
|
name: win32
|
||||||
sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e
|
sha256: ba6f4bba816c8d7e3c1580e170f3786d216951cc6b94babc3b814c08d2cb2738
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.15.0"
|
version: "6.3.0"
|
||||||
win32_registry:
|
win32_registry:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: win32_registry
|
name: win32_registry
|
||||||
sha256: "6f1b564492d0147b330dd794fee8f512cec4977957f310f9951b5f9d83618dae"
|
sha256: "73b1d78920a9d6e03f8b4e43e612b87bf3152a0e5c5e5150267762b7c4116904"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0"
|
version: "3.0.3"
|
||||||
workmanager:
|
workmanager:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
17
pubspec.yaml
17
pubspec.yaml
|
|
@ -17,8 +17,8 @@ dependencies:
|
||||||
# Trusted published dart.dev or tools.dart.dev
|
# Trusted published dart.dev or tools.dart.dev
|
||||||
collection: ^1.18.0
|
collection: ^1.18.0
|
||||||
fixnum: ^1.1.1
|
fixnum: ^1.1.1
|
||||||
meta: ^1.17.0
|
meta: ^1.17.0 # used by overwritten dependencies...
|
||||||
http: ^1.3.0
|
http: ^1.6.0
|
||||||
intl: ^0.20.2
|
intl: ^0.20.2
|
||||||
path: ^1.9.0
|
path: ^1.9.0
|
||||||
logging: ^1.3.0
|
logging: ^1.3.0
|
||||||
|
|
@ -32,7 +32,7 @@ dependencies:
|
||||||
# Trusted publisher flutter.dev
|
# Trusted publisher flutter.dev
|
||||||
camera: ^0.12.0+1
|
camera: ^0.12.0+1
|
||||||
flutter_svg: ^2.0.17
|
flutter_svg: ^2.0.17
|
||||||
image_picker: ^1.1.2
|
image_picker: ^1.2.2
|
||||||
local_auth: ^3.0.0
|
local_auth: ^3.0.0
|
||||||
path_provider: ^2.1.5
|
path_provider: ^2.1.5
|
||||||
url_launcher: ^6.3.2
|
url_launcher: ^6.3.2
|
||||||
|
|
@ -44,10 +44,10 @@ dependencies:
|
||||||
|
|
||||||
# Trusted publisher fluttercommunity.dev
|
# Trusted publisher fluttercommunity.dev
|
||||||
connectivity_plus: ^7.0.0
|
connectivity_plus: ^7.0.0
|
||||||
device_info_plus: ^12.1.0
|
device_info_plus: ^13.1.0
|
||||||
font_awesome_flutter: ^11.0.0
|
font_awesome_flutter: ^11.0.0
|
||||||
share_plus: ^12.0.0
|
share_plus: ^13.1.0
|
||||||
package_info_plus: ^9.0.0
|
package_info_plus: ^10.1.0
|
||||||
workmanager: ^0.9.0+3
|
workmanager: ^0.9.0+3
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -91,14 +91,11 @@ dependencies:
|
||||||
# With high download. (But should be checked nonetheless.)
|
# With high download. (But should be checked nonetheless.)
|
||||||
app_links: ^7.0.0 # 1.6 mio
|
app_links: ^7.0.0 # 1.6 mio
|
||||||
image: ^4.3.0 # 3.3 mio
|
image: ^4.3.0 # 3.3 mio
|
||||||
archive: ^4.0.7 # 6.5 mio
|
|
||||||
file_picker: ^10.3.6 # 2 mio
|
|
||||||
get: ^4.7.2 # 740 k
|
|
||||||
flutter_secure_storage: ^10.3.1 # 1.85 mio
|
flutter_secure_storage: ^10.3.1 # 1.85 mio
|
||||||
permission_handler: ^12.0.0+1 # 2 mio
|
permission_handler: ^12.0.0+1 # 2 mio
|
||||||
|
|
||||||
# Not yet checked
|
# Not yet checked
|
||||||
audio_waveforms: ^2.0.0
|
audio_waveforms: ^2.0.2
|
||||||
avatar_maker: ^0.4.0
|
avatar_maker: ^0.4.0
|
||||||
background_downloader: ^9.4.0
|
background_downloader: ^9.4.0
|
||||||
cached_network_image: ^3.4.1
|
cached_network_image: ^3.4.1
|
||||||
|
|
|
||||||
102
scripts/check_dependencies.py
Executable file
102
scripts/check_dependencies.py
Executable file
|
|
@ -0,0 +1,102 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def parse_dependencies(pubspec_path):
|
||||||
|
deps = []
|
||||||
|
in_deps = False
|
||||||
|
|
||||||
|
with open(pubspec_path, 'r', encoding='utf-8') as f:
|
||||||
|
for line in f:
|
||||||
|
stripped = line.strip()
|
||||||
|
if not stripped or stripped.startswith('#'):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Detect starting of dependencies or ending
|
||||||
|
if line[0].isalpha() or line.startswith('dev_dependencies') or line.startswith('dependency_overrides'):
|
||||||
|
if stripped.startswith('dependencies:'):
|
||||||
|
in_deps = True
|
||||||
|
else:
|
||||||
|
in_deps = False
|
||||||
|
continue
|
||||||
|
|
||||||
|
if in_deps:
|
||||||
|
# Matches exactly 2 spaces indentation, followed by package name and colon
|
||||||
|
match = re.match(r'^ ([a-zA-Z0-9_-]+):', line)
|
||||||
|
if match:
|
||||||
|
dep = match.group(1)
|
||||||
|
# Ignore Flutter SDK built-ins and custom local rust bridge wrapper
|
||||||
|
if dep not in ['flutter', 'flutter_localizations', 'rust_lib_twonly']:
|
||||||
|
deps.append(dep)
|
||||||
|
return deps
|
||||||
|
|
||||||
|
def find_used_packages(root_dir):
|
||||||
|
used = set()
|
||||||
|
# Matches: import 'package:package_name/...'; or export 'package:package_name/...';
|
||||||
|
pattern = re.compile(r'(?:import|export)\s+[\'"]package:([a-zA-Z0-9_-]+)/')
|
||||||
|
|
||||||
|
for dirpath, _, filenames in os.walk(root_dir):
|
||||||
|
# Skip hidden directories (.git, .dart_tool, etc.)
|
||||||
|
if any(part.startswith('.') for part in dirpath.split(os.sep)):
|
||||||
|
continue
|
||||||
|
for filename in filenames:
|
||||||
|
if filename.endswith('.dart'):
|
||||||
|
filepath = os.path.join(dirpath, filename)
|
||||||
|
try:
|
||||||
|
with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
|
||||||
|
for line in f:
|
||||||
|
match = pattern.search(line)
|
||||||
|
if match:
|
||||||
|
used.add(match.group(1))
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return used
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# Locate project root (assuming script is in scripts/ or root/)
|
||||||
|
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
project_root = os.path.abspath(os.path.join(script_dir, '..'))
|
||||||
|
|
||||||
|
pubspec_path = os.path.join(project_root, 'pubspec.yaml')
|
||||||
|
if not os.path.exists(pubspec_path):
|
||||||
|
print(f"Error: pubspec.yaml not found at {pubspec_path}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
print("Parsing dependencies from pubspec.yaml...")
|
||||||
|
declared_deps = parse_dependencies(pubspec_path)
|
||||||
|
print(f"Found {len(declared_deps)} runtime dependencies.")
|
||||||
|
|
||||||
|
print("\nScanning codebase for imports/exports in lib/, test/, integration_test/...")
|
||||||
|
used_in_code = set()
|
||||||
|
for folder in ['lib', 'test', 'integration_test']:
|
||||||
|
folder_path = os.path.join(project_root, folder)
|
||||||
|
if os.path.exists(folder_path):
|
||||||
|
used_in_code.update(find_used_packages(folder_path))
|
||||||
|
|
||||||
|
unused_deps = []
|
||||||
|
used_deps = []
|
||||||
|
|
||||||
|
for dep in declared_deps:
|
||||||
|
if dep in used_in_code:
|
||||||
|
used_deps.append(dep)
|
||||||
|
else:
|
||||||
|
unused_deps.append(dep)
|
||||||
|
|
||||||
|
print("\n" + "=" * 50)
|
||||||
|
print(" RESULTS")
|
||||||
|
print("=" * 50)
|
||||||
|
|
||||||
|
if unused_deps:
|
||||||
|
print(f"\n❌ UNUSED DEPENDENCIES ({len(unused_deps)}):")
|
||||||
|
for dep in sorted(unused_deps):
|
||||||
|
print(f" - {dep}")
|
||||||
|
else:
|
||||||
|
print("\n✅ All dependencies listed in pubspec.yaml are used in the codebase!")
|
||||||
|
|
||||||
|
print(f"\n✨ USED DEPENDENCIES ({len(used_deps)}):")
|
||||||
|
for dep in sorted(used_deps):
|
||||||
|
print(f" - {dep}")
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
Loading…
Reference in a new issue