mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-03-03 18:26:46 +00:00
Compare commits
9 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 525552ac94 | |||
| fb1e286cf9 | |||
| 3806525653 | |||
| cd00910e86 | |||
| 609c7abb55 | |||
| 87ddb8ebdb | |||
| d04828b020 | |||
| e9ea0a7f16 | |||
| ca62069652 |
33 changed files with 484 additions and 431 deletions
|
|
@ -1,7 +1,11 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## 0.0.94
|
## 0.0.96
|
||||||
|
|
||||||
|
Feature: Show link in chat if the saved media file contains one
|
||||||
|
Improve: Verification badge for groups
|
||||||
|
Improve: Huge reduction in app size
|
||||||
|
Fix: Crash on older devices when compressing a video
|
||||||
Fix: Problem with decrypting messages fixed
|
Fix: Problem with decrypting messages fixed
|
||||||
|
|
||||||
## 0.0.93
|
## 0.0.93
|
||||||
|
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#ffff00" class="bi bi-patch-check" viewBox="0 0 16 16">
|
|
||||||
<path fill-rule="evenodd" d="M10.354 6.146a.5.5 0 0 1 0 .708l-3 3a.5.5 0 0 1-.708 0l-1.5-1.5a.5.5 0 1 1 .708-.708L7 8.793l2.646-2.647a.5.5 0 0 1 .708 0"/>
|
|
||||||
<path d="m10.273 2.513-.921-.944.715-.698.622.637.89-.011a2.89 2.89 0 0 1 2.924 2.924l-.01.89.636.622a2.89 2.89 0 0 1 0 4.134l-.637.622.011.89a2.89 2.89 0 0 1-2.924 2.924l-.89-.01-.622.636a2.89 2.89 0 0 1-4.134 0l-.622-.637-.89.011a2.89 2.89 0 0 1-2.924-2.924l.01-.89-.636-.622a2.89 2.89 0 0 1 0-4.134l.637-.622-.011-.89a2.89 2.89 0 0 1 2.924-2.924l.89.01.622-.636a2.89 2.89 0 0 1 4.134 0l-.715.698a1.89 1.89 0 0 0-2.704 0l-.92.944-1.32-.016a1.89 1.89 0 0 0-1.911 1.912l.016 1.318-.944.921a1.89 1.89 0 0 0 0 2.704l.944.92-.016 1.32a1.89 1.89 0 0 0 1.912 1.911l1.318-.016.921.944a1.89 1.89 0 0 0 2.704 0l.92-.944 1.32.016a1.89 1.89 0 0 0 1.911-1.912l-.016-1.318.944-.921a1.89 1.89 0 0 0 0-2.704l-.944-.92.016-1.32a1.89 1.89 0 0 0-1.912-1.911z"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1 KiB |
|
|
@ -46,11 +46,6 @@ PODS:
|
||||||
- SwiftyGif
|
- SwiftyGif
|
||||||
- emoji_picker_flutter (0.0.1):
|
- emoji_picker_flutter (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- ffmpeg_kit_flutter_new (1.0.0):
|
|
||||||
- ffmpeg_kit_flutter_new/full-gpl (= 1.0.0)
|
|
||||||
- Flutter
|
|
||||||
- ffmpeg_kit_flutter_new/full-gpl (1.0.0):
|
|
||||||
- Flutter
|
|
||||||
- file_picker (0.0.1):
|
- file_picker (0.0.1):
|
||||||
- DKImagePickerController/PhotoGallery
|
- DKImagePickerController/PhotoGallery
|
||||||
- Flutter
|
- Flutter
|
||||||
|
|
@ -278,6 +273,8 @@ PODS:
|
||||||
- Flutter
|
- Flutter
|
||||||
- permission_handler_apple (9.3.0):
|
- permission_handler_apple (9.3.0):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
- pro_video_editor (0.0.1):
|
||||||
|
- Flutter
|
||||||
- PromisesObjC (2.4.0)
|
- PromisesObjC (2.4.0)
|
||||||
- restart_app (0.0.1):
|
- restart_app (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
|
@ -329,6 +326,8 @@ PODS:
|
||||||
- SwiftyGif (5.4.5)
|
- SwiftyGif (5.4.5)
|
||||||
- url_launcher_ios (0.0.1):
|
- url_launcher_ios (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
- video_compress (0.3.0):
|
||||||
|
- Flutter
|
||||||
- video_player_avfoundation (0.0.1):
|
- video_player_avfoundation (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
|
|
@ -342,7 +341,6 @@ DEPENDENCIES:
|
||||||
- cryptography_flutter_plus (from `.symlinks/plugins/cryptography_flutter_plus/ios`)
|
- cryptography_flutter_plus (from `.symlinks/plugins/cryptography_flutter_plus/ios`)
|
||||||
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
||||||
- emoji_picker_flutter (from `.symlinks/plugins/emoji_picker_flutter/ios`)
|
- emoji_picker_flutter (from `.symlinks/plugins/emoji_picker_flutter/ios`)
|
||||||
- ffmpeg_kit_flutter_new (from `.symlinks/plugins/ffmpeg_kit_flutter_new/ios`)
|
|
||||||
- file_picker (from `.symlinks/plugins/file_picker/ios`)
|
- file_picker (from `.symlinks/plugins/file_picker/ios`)
|
||||||
- Firebase
|
- Firebase
|
||||||
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
|
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
|
||||||
|
|
@ -368,6 +366,7 @@ DEPENDENCIES:
|
||||||
- no_screenshot (from `.symlinks/plugins/no_screenshot/ios`)
|
- no_screenshot (from `.symlinks/plugins/no_screenshot/ios`)
|
||||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||||
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
||||||
|
- pro_video_editor (from `.symlinks/plugins/pro_video_editor/ios`)
|
||||||
- restart_app (from `.symlinks/plugins/restart_app/ios`)
|
- restart_app (from `.symlinks/plugins/restart_app/ios`)
|
||||||
- sentry_flutter (from `.symlinks/plugins/sentry_flutter/ios`)
|
- sentry_flutter (from `.symlinks/plugins/sentry_flutter/ios`)
|
||||||
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
||||||
|
|
@ -376,6 +375,7 @@ DEPENDENCIES:
|
||||||
- sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/darwin`)
|
- sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/darwin`)
|
||||||
- SwiftProtobuf
|
- SwiftProtobuf
|
||||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||||
|
- video_compress (from `.symlinks/plugins/video_compress/ios`)
|
||||||
- video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/darwin`)
|
- video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/darwin`)
|
||||||
|
|
||||||
SPEC REPOS:
|
SPEC REPOS:
|
||||||
|
|
@ -428,8 +428,6 @@ EXTERNAL SOURCES:
|
||||||
:path: ".symlinks/plugins/device_info_plus/ios"
|
:path: ".symlinks/plugins/device_info_plus/ios"
|
||||||
emoji_picker_flutter:
|
emoji_picker_flutter:
|
||||||
:path: ".symlinks/plugins/emoji_picker_flutter/ios"
|
:path: ".symlinks/plugins/emoji_picker_flutter/ios"
|
||||||
ffmpeg_kit_flutter_new:
|
|
||||||
:path: ".symlinks/plugins/ffmpeg_kit_flutter_new/ios"
|
|
||||||
file_picker:
|
file_picker:
|
||||||
:path: ".symlinks/plugins/file_picker/ios"
|
:path: ".symlinks/plugins/file_picker/ios"
|
||||||
firebase_core:
|
firebase_core:
|
||||||
|
|
@ -470,6 +468,8 @@ EXTERNAL SOURCES:
|
||||||
:path: ".symlinks/plugins/package_info_plus/ios"
|
:path: ".symlinks/plugins/package_info_plus/ios"
|
||||||
permission_handler_apple:
|
permission_handler_apple:
|
||||||
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
||||||
|
pro_video_editor:
|
||||||
|
:path: ".symlinks/plugins/pro_video_editor/ios"
|
||||||
restart_app:
|
restart_app:
|
||||||
:path: ".symlinks/plugins/restart_app/ios"
|
:path: ".symlinks/plugins/restart_app/ios"
|
||||||
sentry_flutter:
|
sentry_flutter:
|
||||||
|
|
@ -484,6 +484,8 @@ EXTERNAL SOURCES:
|
||||||
:path: ".symlinks/plugins/sqlite3_flutter_libs/darwin"
|
:path: ".symlinks/plugins/sqlite3_flutter_libs/darwin"
|
||||||
url_launcher_ios:
|
url_launcher_ios:
|
||||||
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
||||||
|
video_compress:
|
||||||
|
:path: ".symlinks/plugins/video_compress/ios"
|
||||||
video_player_avfoundation:
|
video_player_avfoundation:
|
||||||
:path: ".symlinks/plugins/video_player_avfoundation/darwin"
|
:path: ".symlinks/plugins/video_player_avfoundation/darwin"
|
||||||
|
|
||||||
|
|
@ -498,7 +500,6 @@ SPEC CHECKSUMS:
|
||||||
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
|
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
|
||||||
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
|
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
|
||||||
emoji_picker_flutter: ece213fc274bdddefb77d502d33080dc54e616cc
|
emoji_picker_flutter: ece213fc274bdddefb77d502d33080dc54e616cc
|
||||||
ffmpeg_kit_flutter_new: 12426a19f10ac81186c67c6ebc4717f8f4364b7f
|
|
||||||
file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be
|
file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be
|
||||||
Firebase: 9a58fdbc9d8655ed7b79a19cf9690bb007d3d46d
|
Firebase: 9a58fdbc9d8655ed7b79a19cf9690bb007d3d46d
|
||||||
firebase_core: ee30637e6744af8e0c12a6a1e8a9718506ec2398
|
firebase_core: ee30637e6744af8e0c12a6a1e8a9718506ec2398
|
||||||
|
|
@ -540,6 +541,7 @@ SPEC CHECKSUMS:
|
||||||
no_screenshot: 5e345998c43ffcad5d6834f249590483fcc037bd
|
no_screenshot: 5e345998c43ffcad5d6834f249590483fcc037bd
|
||||||
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
|
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
|
||||||
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
|
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
|
||||||
|
pro_video_editor: 44ef9a6d48dbd757ed428cf35396dd05f35c7830
|
||||||
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
||||||
restart_app: 9cda5378aacc5000e3f66ee76a9201534e7d3ecf
|
restart_app: 9cda5378aacc5000e3f66ee76a9201534e7d3ecf
|
||||||
SDWebImage: 1bb6a1b84b6fe87b972a102bdc77dd589df33477
|
SDWebImage: 1bb6a1b84b6fe87b972a102bdc77dd589df33477
|
||||||
|
|
@ -554,6 +556,7 @@ SPEC CHECKSUMS:
|
||||||
SwiftProtobuf: c901f00a3e125dc33cac9b16824da85682ee47da
|
SwiftProtobuf: c901f00a3e125dc33cac9b16824da85682ee47da
|
||||||
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
|
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
|
||||||
url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b
|
url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b
|
||||||
|
video_compress: f2133a07762889d67f0711ac831faa26f956980e
|
||||||
video_player_avfoundation: dd410b52df6d2466a42d28550e33e4146928280a
|
video_player_avfoundation: dd410b52df6d2466a42d28550e33e4146928280a
|
||||||
|
|
||||||
PODFILE CHECKSUM: ae041999f13ba7b2285ff9ad9bc688ed647bbcb7
|
PODFILE CHECKSUM: ae041999f13ba7b2285ff9ad9bc688ed647bbcb7
|
||||||
|
|
|
||||||
|
|
@ -212,32 +212,41 @@ class MessagesDao extends DatabaseAccessor<TwonlyDB> with _$MessagesDaoMixin {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> handleMessageOpened(
|
Future<void> handleMessagesOpened(
|
||||||
int contactId,
|
int contactId,
|
||||||
String messageId,
|
List<String> messageIds,
|
||||||
DateTime timestamp,
|
DateTime timestamp,
|
||||||
) async {
|
) async {
|
||||||
await into(messageActions).insertOnConflictUpdate(
|
await batch((batch) async {
|
||||||
|
for (final messageId in messageIds) {
|
||||||
|
batch.insert(
|
||||||
|
messageActions,
|
||||||
MessageActionsCompanion(
|
MessageActionsCompanion(
|
||||||
messageId: Value(messageId),
|
messageId: Value(messageId),
|
||||||
contactId: Value(contactId),
|
contactId: Value(contactId),
|
||||||
type: const Value(MessageActionType.openedAt),
|
type: const Value(MessageActionType.openedAt),
|
||||||
actionAt: Value(timestamp),
|
actionAt: Value(timestamp),
|
||||||
),
|
),
|
||||||
|
mode: InsertMode.insertOrReplace,
|
||||||
);
|
);
|
||||||
// Directly show as message opened as soon as one person has opened it
|
}
|
||||||
final openedByAll =
|
|
||||||
await haveAllMembers(messageId, MessageActionType.openedAt)
|
for (final messageId in messageIds) {
|
||||||
? clock.now()
|
final isOpenedByAll =
|
||||||
: null;
|
await haveAllMembers(messageId, MessageActionType.openedAt);
|
||||||
await twonlyDB.messagesDao.updateMessageId(
|
final now = clock.now();
|
||||||
messageId,
|
|
||||||
|
batch.update(
|
||||||
|
twonlyDB.messages,
|
||||||
MessagesCompanion(
|
MessagesCompanion(
|
||||||
openedAt: Value(clock.now()),
|
openedAt: Value(now),
|
||||||
openedByAll: Value(openedByAll),
|
openedByAll: Value(isOpenedByAll ? now : null),
|
||||||
),
|
),
|
||||||
|
where: (tbl) => tbl.messageId.equals(messageId),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> handleMessageAckByServer(
|
Future<void> handleMessageAckByServer(
|
||||||
int contactId,
|
int contactId,
|
||||||
|
|
@ -342,38 +351,6 @@ class MessagesDao extends DatabaseAccessor<TwonlyDB> with _$MessagesDaoMixin {
|
||||||
.getSingleOrNull();
|
.getSingleOrNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream<Future<List<(Message, Contact)>>> watchLastOpenedMessagePerContact(
|
|
||||||
String groupId,
|
|
||||||
) {
|
|
||||||
const sql = '''
|
|
||||||
SELECT m.*, c.*
|
|
||||||
FROM (
|
|
||||||
SELECT ma.contact_id, ma.message_id,
|
|
||||||
ROW_NUMBER() OVER (PARTITION BY ma.contact_id
|
|
||||||
ORDER BY ma.action_at DESC, ma.message_id DESC) AS rn
|
|
||||||
FROM message_actions ma
|
|
||||||
WHERE ma.type = 'openedAt'
|
|
||||||
) last_open
|
|
||||||
JOIN messages m ON m.message_id = last_open.message_id
|
|
||||||
JOIN contacts c ON c.user_id = last_open.contact_id
|
|
||||||
WHERE last_open.rn = 1 AND m.group_id = ?;
|
|
||||||
''';
|
|
||||||
|
|
||||||
return customSelect(
|
|
||||||
sql,
|
|
||||||
variables: [Variable.withString(groupId)],
|
|
||||||
readsFrom: {messages, messageActions, contacts},
|
|
||||||
).watch().map((rows) async {
|
|
||||||
final res = <(Message, Contact)>[];
|
|
||||||
for (final row in rows) {
|
|
||||||
final message = await messages.mapFromRow(row);
|
|
||||||
final contact = await contacts.mapFromRow(row);
|
|
||||||
res.add((message, contact));
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> deleteMessagesById(String messageId) {
|
Future<void> deleteMessagesById(String messageId) {
|
||||||
return (delete(messages)..where((t) => t.messageId.equals(messageId))).go();
|
return (delete(messages)..where((t) => t.messageId.equals(messageId))).go();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,9 +31,6 @@ class UserData {
|
||||||
@JsonKey(defaultValue: false)
|
@JsonKey(defaultValue: false)
|
||||||
bool isDeveloper = false;
|
bool isDeveloper = false;
|
||||||
|
|
||||||
@JsonKey(defaultValue: false)
|
|
||||||
bool disableVideoCompression = false;
|
|
||||||
|
|
||||||
@JsonKey(defaultValue: 0)
|
@JsonKey(defaultValue: 0)
|
||||||
int deviceId = 0;
|
int deviceId = 0;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,6 @@ UserData _$UserDataFromJson(Map<String, dynamic> json) => UserData(
|
||||||
..appVersion = (json['appVersion'] as num?)?.toInt() ?? 0
|
..appVersion = (json['appVersion'] as num?)?.toInt() ?? 0
|
||||||
..avatarCounter = (json['avatarCounter'] as num?)?.toInt() ?? 0
|
..avatarCounter = (json['avatarCounter'] as num?)?.toInt() ?? 0
|
||||||
..isDeveloper = json['isDeveloper'] as bool? ?? false
|
..isDeveloper = json['isDeveloper'] as bool? ?? false
|
||||||
..disableVideoCompression =
|
|
||||||
json['disableVideoCompression'] as bool? ?? false
|
|
||||||
..deviceId = (json['deviceId'] as num?)?.toInt() ?? 0
|
..deviceId = (json['deviceId'] as num?)?.toInt() ?? 0
|
||||||
..subscriptionPlanIdStore = json['subscriptionPlanIdStore'] as String?
|
..subscriptionPlanIdStore = json['subscriptionPlanIdStore'] as String?
|
||||||
..lastImageSend = json['lastImageSend'] == null
|
..lastImageSend = json['lastImageSend'] == null
|
||||||
|
|
@ -95,7 +93,6 @@ Map<String, dynamic> _$UserDataToJson(UserData instance) => <String, dynamic>{
|
||||||
'appVersion': instance.appVersion,
|
'appVersion': instance.appVersion,
|
||||||
'avatarCounter': instance.avatarCounter,
|
'avatarCounter': instance.avatarCounter,
|
||||||
'isDeveloper': instance.isDeveloper,
|
'isDeveloper': instance.isDeveloper,
|
||||||
'disableVideoCompression': instance.disableVideoCompression,
|
|
||||||
'deviceId': instance.deviceId,
|
'deviceId': instance.deviceId,
|
||||||
'subscriptionPlan': instance.subscriptionPlan,
|
'subscriptionPlan': instance.subscriptionPlan,
|
||||||
'subscriptionPlanIdStore': instance.subscriptionPlanIdStore,
|
'subscriptionPlanIdStore': instance.subscriptionPlanIdStore,
|
||||||
|
|
|
||||||
|
|
@ -76,17 +76,21 @@ class EncryptedContent_ErrorMessages_Type extends $pb.ProtobufEnum {
|
||||||
static const EncryptedContent_ErrorMessages_Type UNKNOWN_MESSAGE_TYPE =
|
static const EncryptedContent_ErrorMessages_Type UNKNOWN_MESSAGE_TYPE =
|
||||||
EncryptedContent_ErrorMessages_Type._(
|
EncryptedContent_ErrorMessages_Type._(
|
||||||
2, _omitEnumNames ? '' : 'UNKNOWN_MESSAGE_TYPE');
|
2, _omitEnumNames ? '' : 'UNKNOWN_MESSAGE_TYPE');
|
||||||
|
static const EncryptedContent_ErrorMessages_Type SESSION_OUT_OF_SYNC =
|
||||||
|
EncryptedContent_ErrorMessages_Type._(
|
||||||
|
3, _omitEnumNames ? '' : 'SESSION_OUT_OF_SYNC');
|
||||||
|
|
||||||
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,
|
||||||
];
|
];
|
||||||
|
|
||||||
static final $core.Map<$core.int, EncryptedContent_ErrorMessages_Type>
|
static final $core.List<EncryptedContent_ErrorMessages_Type?> _byValue =
|
||||||
_byValue = $pb.ProtobufEnum.initByValue(values);
|
$pb.ProtobufEnum.$_initByValueList(values, 3);
|
||||||
static EncryptedContent_ErrorMessages_Type? valueOf($core.int value) =>
|
static EncryptedContent_ErrorMessages_Type? valueOf($core.int value) =>
|
||||||
_byValue[value];
|
value < 0 || value >= _byValue.length ? null : _byValue[value];
|
||||||
|
|
||||||
const EncryptedContent_ErrorMessages_Type._(super.value, super.name);
|
const EncryptedContent_ErrorMessages_Type._(super.value, super.name);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -395,6 +395,7 @@ const EncryptedContent_ErrorMessages_Type$json = {
|
||||||
'2': [
|
'2': [
|
||||||
{'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},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -863,67 +864,68 @@ final $typed_data.Uint8List encryptedContentDescriptor = $convert.base64Decode(
|
||||||
'gBARJLCg5lcnJvcl9tZXNzYWdlcxgSIAEoCzIfLkVuY3J5cHRlZENvbnRlbnQuRXJyb3JNZXNz'
|
'gBARJLCg5lcnJvcl9tZXNzYWdlcxgSIAEoCzIfLkVuY3J5cHRlZENvbnRlbnQuRXJyb3JNZXNz'
|
||||||
'YWdlc0gQUg1lcnJvck1lc3NhZ2VziAEBEmQKF2FkZGl0aW9uYWxfZGF0YV9tZXNzYWdlGBMgAS'
|
'YWdlc0gQUg1lcnJvck1lc3NhZ2VziAEBEmQKF2FkZGl0aW9uYWxfZGF0YV9tZXNzYWdlGBMgAS'
|
||||||
'gLMicuRW5jcnlwdGVkQ29udGVudC5BZGRpdGlvbmFsRGF0YU1lc3NhZ2VIEVIVYWRkaXRpb25h'
|
'gLMicuRW5jcnlwdGVkQ29udGVudC5BZGRpdGlvbmFsRGF0YU1lc3NhZ2VIEVIVYWRkaXRpb25h'
|
||||||
'bERhdGFNZXNzYWdliAEBGtcBCg1FcnJvck1lc3NhZ2VzEjgKBHR5cGUYASABKA4yJC5FbmNyeX'
|
'bERhdGFNZXNzYWdliAEBGvABCg1FcnJvck1lc3NhZ2VzEjgKBHR5cGUYASABKA4yJC5FbmNyeX'
|
||||||
'B0ZWRDb250ZW50LkVycm9yTWVzc2FnZXMuVHlwZVIEdHlwZRIsChJyZWxhdGVkX3JlY2VpcHRf'
|
'B0ZWRDb250ZW50LkVycm9yTWVzc2FnZXMuVHlwZVIEdHlwZRIsChJyZWxhdGVkX3JlY2VpcHRf'
|
||||||
'aWQYAiABKAlSEHJlbGF0ZWRSZWNlaXB0SWQiXgoEVHlwZRI8CjhFUlJPUl9QUk9DRVNTSU5HX0'
|
'aWQYAiABKAlSEHJlbGF0ZWRSZWNlaXB0SWQidwoEVHlwZRI8CjhFUlJPUl9QUk9DRVNTSU5HX0'
|
||||||
'1FU1NBR0VfQ1JFQVRFRF9BQ0NPVU5UX1JFUVVFU1RfSU5TVEVBRBAAEhgKFFVOS05PV05fTUVT'
|
'1FU1NBR0VfQ1JFQVRFRF9BQ0NPVU5UX1JFUVVFU1RfSU5TVEVBRBAAEhgKFFVOS05PV05fTUVT'
|
||||||
'U0FHRV9UWVBFEAIaUQoLR3JvdXBDcmVhdGUSGgoIc3RhdGVLZXkYAyABKAxSCHN0YXRlS2V5Ei'
|
'U0FHRV9UWVBFEAISFwoTU0VTU0lPTl9PVVRfT0ZfU1lOQxADGlEKC0dyb3VwQ3JlYXRlEhoKCH'
|
||||||
'YKDmdyb3VwUHVibGljS2V5GAQgASgMUg5ncm91cFB1YmxpY0tleRozCglHcm91cEpvaW4SJgoO'
|
'N0YXRlS2V5GAMgASgMUghzdGF0ZUtleRImCg5ncm91cFB1YmxpY0tleRgEIAEoDFIOZ3JvdXBQ'
|
||||||
'Z3JvdXBQdWJsaWNLZXkYASABKAxSDmdyb3VwUHVibGljS2V5GhYKFFJlc2VuZEdyb3VwUHVibG'
|
'dWJsaWNLZXkaMwoJR3JvdXBKb2luEiYKDmdyb3VwUHVibGljS2V5GAEgASgMUg5ncm91cFB1Ym'
|
||||||
'ljS2V5GrYCCgtHcm91cFVwZGF0ZRIoCg9ncm91cEFjdGlvblR5cGUYASABKAlSD2dyb3VwQWN0'
|
'xpY0tleRoWChRSZXNlbmRHcm91cFB1YmxpY0tleRq2AgoLR3JvdXBVcGRhdGUSKAoPZ3JvdXBB'
|
||||||
'aW9uVHlwZRIxChFhZmZlY3RlZENvbnRhY3RJZBgCIAEoA0gAUhFhZmZlY3RlZENvbnRhY3RJZI'
|
'Y3Rpb25UeXBlGAEgASgJUg9ncm91cEFjdGlvblR5cGUSMQoRYWZmZWN0ZWRDb250YWN0SWQYAi'
|
||||||
'gBARInCgxuZXdHcm91cE5hbWUYAyABKAlIAVIMbmV3R3JvdXBOYW1liAEBElMKIm5ld0RlbGV0'
|
'ABKANIAFIRYWZmZWN0ZWRDb250YWN0SWSIAQESJwoMbmV3R3JvdXBOYW1lGAMgASgJSAFSDG5l'
|
||||||
'ZU1lc3NhZ2VzQWZ0ZXJNaWxsaXNlY29uZHMYBCABKANIAlIibmV3RGVsZXRlTWVzc2FnZXNBZn'
|
'd0dyb3VwTmFtZYgBARJTCiJuZXdEZWxldGVNZXNzYWdlc0FmdGVyTWlsbGlzZWNvbmRzGAQgAS'
|
||||||
'Rlck1pbGxpc2Vjb25kc4gBAUIUChJfYWZmZWN0ZWRDb250YWN0SWRCDwoNX25ld0dyb3VwTmFt'
|
'gDSAJSIm5ld0RlbGV0ZU1lc3NhZ2VzQWZ0ZXJNaWxsaXNlY29uZHOIAQFCFAoSX2FmZmVjdGVk'
|
||||||
'ZUIlCiNfbmV3RGVsZXRlTWVzc2FnZXNBZnRlck1pbGxpc2Vjb25kcxqpAQoLVGV4dE1lc3NhZ2'
|
'Q29udGFjdElkQg8KDV9uZXdHcm91cE5hbWVCJQojX25ld0RlbGV0ZU1lc3NhZ2VzQWZ0ZXJNaW'
|
||||||
'USKAoPc2VuZGVyTWVzc2FnZUlkGAEgASgJUg9zZW5kZXJNZXNzYWdlSWQSEgoEdGV4dBgCIAEo'
|
'xsaXNlY29uZHMaqQEKC1RleHRNZXNzYWdlEigKD3NlbmRlck1lc3NhZ2VJZBgBIAEoCVIPc2Vu'
|
||||||
'CVIEdGV4dBIcCgl0aW1lc3RhbXAYAyABKANSCXRpbWVzdGFtcBIrCg5xdW90ZU1lc3NhZ2VJZB'
|
'ZGVyTWVzc2FnZUlkEhIKBHRleHQYAiABKAlSBHRleHQSHAoJdGltZXN0YW1wGAMgASgDUgl0aW'
|
||||||
'gEIAEoCUgAUg5xdW90ZU1lc3NhZ2VJZIgBAUIRCg9fcXVvdGVNZXNzYWdlSWQazgEKFUFkZGl0'
|
'1lc3RhbXASKwoOcXVvdGVNZXNzYWdlSWQYBCABKAlIAFIOcXVvdGVNZXNzYWdlSWSIAQFCEQoP'
|
||||||
'aW9uYWxEYXRhTWVzc2FnZRIqChFzZW5kZXJfbWVzc2FnZV9pZBgBIAEoCVIPc2VuZGVyTWVzc2'
|
'X3F1b3RlTWVzc2FnZUlkGs4BChVBZGRpdGlvbmFsRGF0YU1lc3NhZ2USKgoRc2VuZGVyX21lc3'
|
||||||
'FnZUlkEhwKCXRpbWVzdGFtcBgCIAEoA1IJdGltZXN0YW1wEhIKBHR5cGUYAyABKAlSBHR5cGUS'
|
'NhZ2VfaWQYASABKAlSD3NlbmRlck1lc3NhZ2VJZBIcCgl0aW1lc3RhbXAYAiABKANSCXRpbWVz'
|
||||||
'OwoXYWRkaXRpb25hbF9tZXNzYWdlX2RhdGEYBCABKAxIAFIVYWRkaXRpb25hbE1lc3NhZ2VEYX'
|
'dGFtcBISCgR0eXBlGAMgASgJUgR0eXBlEjsKF2FkZGl0aW9uYWxfbWVzc2FnZV9kYXRhGAQgAS'
|
||||||
'RhiAEBQhoKGF9hZGRpdGlvbmFsX21lc3NhZ2VfZGF0YRpiCghSZWFjdGlvbhIoCg90YXJnZXRN'
|
'gMSABSFWFkZGl0aW9uYWxNZXNzYWdlRGF0YYgBAUIaChhfYWRkaXRpb25hbF9tZXNzYWdlX2Rh'
|
||||||
'ZXNzYWdlSWQYASABKAlSD3RhcmdldE1lc3NhZ2VJZBIUCgVlbW9qaRgCIAEoCVIFZW1vamkSFg'
|
'dGEaYgoIUmVhY3Rpb24SKAoPdGFyZ2V0TWVzc2FnZUlkGAEgASgJUg90YXJnZXRNZXNzYWdlSW'
|
||||||
'oGcmVtb3ZlGAMgASgIUgZyZW1vdmUatwIKDU1lc3NhZ2VVcGRhdGUSOAoEdHlwZRgBIAEoDjIk'
|
'QSFAoFZW1vamkYAiABKAlSBWVtb2ppEhYKBnJlbW92ZRgDIAEoCFIGcmVtb3ZlGrcCCg1NZXNz'
|
||||||
'LkVuY3J5cHRlZENvbnRlbnQuTWVzc2FnZVVwZGF0ZS5UeXBlUgR0eXBlEi0KD3NlbmRlck1lc3'
|
'YWdlVXBkYXRlEjgKBHR5cGUYASABKA4yJC5FbmNyeXB0ZWRDb250ZW50Lk1lc3NhZ2VVcGRhdG'
|
||||||
'NhZ2VJZBgCIAEoCUgAUg9zZW5kZXJNZXNzYWdlSWSIAQESOgoYbXVsdGlwbGVUYXJnZXRNZXNz'
|
'UuVHlwZVIEdHlwZRItCg9zZW5kZXJNZXNzYWdlSWQYAiABKAlIAFIPc2VuZGVyTWVzc2FnZUlk'
|
||||||
'YWdlSWRzGAMgAygJUhhtdWx0aXBsZVRhcmdldE1lc3NhZ2VJZHMSFwoEdGV4dBgEIAEoCUgBUg'
|
'iAEBEjoKGG11bHRpcGxlVGFyZ2V0TWVzc2FnZUlkcxgDIAMoCVIYbXVsdGlwbGVUYXJnZXRNZX'
|
||||||
'R0ZXh0iAEBEhwKCXRpbWVzdGFtcBgFIAEoA1IJdGltZXN0YW1wIi0KBFR5cGUSCgoGREVMRVRF'
|
'NzYWdlSWRzEhcKBHRleHQYBCABKAlIAVIEdGV4dIgBARIcCgl0aW1lc3RhbXAYBSABKANSCXRp'
|
||||||
'EAASDQoJRURJVF9URVhUEAESCgoGT1BFTkVEEAJCEgoQX3NlbmRlck1lc3NhZ2VJZEIHCgVfdG'
|
'bWVzdGFtcCItCgRUeXBlEgoKBkRFTEVURRAAEg0KCUVESVRfVEVYVBABEgoKBk9QRU5FRBACQh'
|
||||||
'V4dBrwBQoFTWVkaWESKAoPc2VuZGVyTWVzc2FnZUlkGAEgASgJUg9zZW5kZXJNZXNzYWdlSWQS'
|
'IKEF9zZW5kZXJNZXNzYWdlSWRCBwoFX3RleHQa8AUKBU1lZGlhEigKD3NlbmRlck1lc3NhZ2VJ'
|
||||||
'MAoEdHlwZRgCIAEoDjIcLkVuY3J5cHRlZENvbnRlbnQuTWVkaWEuVHlwZVIEdHlwZRJDChpkaX'
|
'ZBgBIAEoCVIPc2VuZGVyTWVzc2FnZUlkEjAKBHR5cGUYAiABKA4yHC5FbmNyeXB0ZWRDb250ZW'
|
||||||
'NwbGF5TGltaXRJbk1pbGxpc2Vjb25kcxgDIAEoA0gAUhpkaXNwbGF5TGltaXRJbk1pbGxpc2Vj'
|
'50Lk1lZGlhLlR5cGVSBHR5cGUSQwoaZGlzcGxheUxpbWl0SW5NaWxsaXNlY29uZHMYAyABKANI'
|
||||||
'b25kc4gBARI2ChZyZXF1aXJlc0F1dGhlbnRpY2F0aW9uGAQgASgIUhZyZXF1aXJlc0F1dGhlbn'
|
'AFIaZGlzcGxheUxpbWl0SW5NaWxsaXNlY29uZHOIAQESNgoWcmVxdWlyZXNBdXRoZW50aWNhdG'
|
||||||
'RpY2F0aW9uEhwKCXRpbWVzdGFtcBgFIAEoA1IJdGltZXN0YW1wEisKDnF1b3RlTWVzc2FnZUlk'
|
'lvbhgEIAEoCFIWcmVxdWlyZXNBdXRoZW50aWNhdGlvbhIcCgl0aW1lc3RhbXAYBSABKANSCXRp'
|
||||||
'GAYgASgJSAFSDnF1b3RlTWVzc2FnZUlkiAEBEikKDWRvd25sb2FkVG9rZW4YByABKAxIAlINZG'
|
'bWVzdGFtcBIrCg5xdW90ZU1lc3NhZ2VJZBgGIAEoCUgBUg5xdW90ZU1lc3NhZ2VJZIgBARIpCg'
|
||||||
'93bmxvYWRUb2tlbogBARIpCg1lbmNyeXB0aW9uS2V5GAggASgMSANSDWVuY3J5cHRpb25LZXmI'
|
'1kb3dubG9hZFRva2VuGAcgASgMSAJSDWRvd25sb2FkVG9rZW6IAQESKQoNZW5jcnlwdGlvbktl'
|
||||||
'AQESKQoNZW5jcnlwdGlvbk1hYxgJIAEoDEgEUg1lbmNyeXB0aW9uTWFjiAEBEi0KD2VuY3J5cH'
|
'eRgIIAEoDEgDUg1lbmNyeXB0aW9uS2V5iAEBEikKDWVuY3J5cHRpb25NYWMYCSABKAxIBFINZW'
|
||||||
'Rpb25Ob25jZRgKIAEoDEgFUg9lbmNyeXB0aW9uTm9uY2WIAQESOwoXYWRkaXRpb25hbF9tZXNz'
|
'5jcnlwdGlvbk1hY4gBARItCg9lbmNyeXB0aW9uTm9uY2UYCiABKAxIBVIPZW5jcnlwdGlvbk5v'
|
||||||
'YWdlX2RhdGEYCyABKAxIBlIVYWRkaXRpb25hbE1lc3NhZ2VEYXRhiAEBIj4KBFR5cGUSDAoIUk'
|
'bmNliAEBEjsKF2FkZGl0aW9uYWxfbWVzc2FnZV9kYXRhGAsgASgMSAZSFWFkZGl0aW9uYWxNZX'
|
||||||
'VVUExPQUQQABIJCgVJTUFHRRABEgkKBVZJREVPEAISBwoDR0lGEAMSCQoFQVVESU8QBEIdChtf'
|
'NzYWdlRGF0YYgBASI+CgRUeXBlEgwKCFJFVVBMT0FEEAASCQoFSU1BR0UQARIJCgVWSURFTxAC'
|
||||||
'ZGlzcGxheUxpbWl0SW5NaWxsaXNlY29uZHNCEQoPX3F1b3RlTWVzc2FnZUlkQhAKDl9kb3dubG'
|
'EgcKA0dJRhADEgkKBUFVRElPEARCHQobX2Rpc3BsYXlMaW1pdEluTWlsbGlzZWNvbmRzQhEKD1'
|
||||||
'9hZFRva2VuQhAKDl9lbmNyeXB0aW9uS2V5QhAKDl9lbmNyeXB0aW9uTWFjQhIKEF9lbmNyeXB0'
|
'9xdW90ZU1lc3NhZ2VJZEIQCg5fZG93bmxvYWRUb2tlbkIQCg5fZW5jcnlwdGlvbktleUIQCg5f'
|
||||||
'aW9uTm9uY2VCGgoYX2FkZGl0aW9uYWxfbWVzc2FnZV9kYXRhGqcBCgtNZWRpYVVwZGF0ZRI2Cg'
|
'ZW5jcnlwdGlvbk1hY0ISChBfZW5jcnlwdGlvbk5vbmNlQhoKGF9hZGRpdGlvbmFsX21lc3NhZ2'
|
||||||
'R0eXBlGAEgASgOMiIuRW5jcnlwdGVkQ29udGVudC5NZWRpYVVwZGF0ZS5UeXBlUgR0eXBlEigK'
|
'VfZGF0YRqnAQoLTWVkaWFVcGRhdGUSNgoEdHlwZRgBIAEoDjIiLkVuY3J5cHRlZENvbnRlbnQu'
|
||||||
'D3RhcmdldE1lc3NhZ2VJZBgCIAEoCVIPdGFyZ2V0TWVzc2FnZUlkIjYKBFR5cGUSDAoIUkVPUE'
|
'TWVkaWFVcGRhdGUuVHlwZVIEdHlwZRIoCg90YXJnZXRNZXNzYWdlSWQYAiABKAlSD3RhcmdldE'
|
||||||
'VORUQQABIKCgZTVE9SRUQQARIUChBERUNSWVBUSU9OX0VSUk9SEAIaeAoOQ29udGFjdFJlcXVl'
|
'1lc3NhZ2VJZCI2CgRUeXBlEgwKCFJFT1BFTkVEEAASCgoGU1RPUkVEEAESFAoQREVDUllQVElP'
|
||||||
'c3QSOQoEdHlwZRgBIAEoDjIlLkVuY3J5cHRlZENvbnRlbnQuQ29udGFjdFJlcXVlc3QuVHlwZV'
|
'Tl9FUlJPUhACGngKDkNvbnRhY3RSZXF1ZXN0EjkKBHR5cGUYASABKA4yJS5FbmNyeXB0ZWRDb2'
|
||||||
'IEdHlwZSIrCgRUeXBlEgsKB1JFUVVFU1QQABIKCgZSRUpFQ1QQARIKCgZBQ0NFUFQQAhqeAgoN'
|
'50ZW50LkNvbnRhY3RSZXF1ZXN0LlR5cGVSBHR5cGUiKwoEVHlwZRILCgdSRVFVRVNUEAASCgoG'
|
||||||
'Q29udGFjdFVwZGF0ZRI4CgR0eXBlGAEgASgOMiQuRW5jcnlwdGVkQ29udGVudC5Db250YWN0VX'
|
'UkVKRUNUEAESCgoGQUNDRVBUEAIangIKDUNvbnRhY3RVcGRhdGUSOAoEdHlwZRgBIAEoDjIkLk'
|
||||||
'BkYXRlLlR5cGVSBHR5cGUSNQoTYXZhdGFyU3ZnQ29tcHJlc3NlZBgCIAEoDEgAUhNhdmF0YXJT'
|
'VuY3J5cHRlZENvbnRlbnQuQ29udGFjdFVwZGF0ZS5UeXBlUgR0eXBlEjUKE2F2YXRhclN2Z0Nv'
|
||||||
'dmdDb21wcmVzc2VkiAEBEh8KCHVzZXJuYW1lGAMgASgJSAFSCHVzZXJuYW1liAEBEiUKC2Rpc3'
|
'bXByZXNzZWQYAiABKAxIAFITYXZhdGFyU3ZnQ29tcHJlc3NlZIgBARIfCgh1c2VybmFtZRgDIA'
|
||||||
'BsYXlOYW1lGAQgASgJSAJSC2Rpc3BsYXlOYW1liAEBIh8KBFR5cGUSCwoHUkVRVUVTVBAAEgoK'
|
'EoCUgBUgh1c2VybmFtZYgBARIlCgtkaXNwbGF5TmFtZRgEIAEoCUgCUgtkaXNwbGF5TmFtZYgB'
|
||||||
'BlVQREFURRABQhYKFF9hdmF0YXJTdmdDb21wcmVzc2VkQgsKCV91c2VybmFtZUIOCgxfZGlzcG'
|
'ASIfCgRUeXBlEgsKB1JFUVVFU1QQABIKCgZVUERBVEUQAUIWChRfYXZhdGFyU3ZnQ29tcHJlc3'
|
||||||
'xheU5hbWUa1QEKCFB1c2hLZXlzEjMKBHR5cGUYASABKA4yHy5FbmNyeXB0ZWRDb250ZW50LlB1'
|
'NlZEILCglfdXNlcm5hbWVCDgoMX2Rpc3BsYXlOYW1lGtUBCghQdXNoS2V5cxIzCgR0eXBlGAEg'
|
||||||
'c2hLZXlzLlR5cGVSBHR5cGUSGQoFa2V5SWQYAiABKANIAFIFa2V5SWSIAQESFQoDa2V5GAMgAS'
|
'ASgOMh8uRW5jcnlwdGVkQ29udGVudC5QdXNoS2V5cy5UeXBlUgR0eXBlEhkKBWtleUlkGAIgAS'
|
||||||
'gMSAFSA2tleYgBARIhCgljcmVhdGVkQXQYBCABKANIAlIJY3JlYXRlZEF0iAEBIh8KBFR5cGUS'
|
'gDSABSBWtleUlkiAEBEhUKA2tleRgDIAEoDEgBUgNrZXmIAQESIQoJY3JlYXRlZEF0GAQgASgD'
|
||||||
'CwoHUkVRVUVTVBAAEgoKBlVQREFURRABQggKBl9rZXlJZEIGCgRfa2V5QgwKCl9jcmVhdGVkQX'
|
'SAJSCWNyZWF0ZWRBdIgBASIfCgRUeXBlEgsKB1JFUVVFU1QQABIKCgZVUERBVEUQAUIICgZfa2'
|
||||||
'QaqQEKCUZsYW1lU3luYxIiCgxmbGFtZUNvdW50ZXIYASABKANSDGZsYW1lQ291bnRlchI2ChZs'
|
'V5SWRCBgoEX2tleUIMCgpfY3JlYXRlZEF0GqkBCglGbGFtZVN5bmMSIgoMZmxhbWVDb3VudGVy'
|
||||||
'YXN0RmxhbWVDb3VudGVyQ2hhbmdlGAIgASgDUhZsYXN0RmxhbWVDb3VudGVyQ2hhbmdlEh4KCm'
|
'GAEgASgDUgxmbGFtZUNvdW50ZXISNgoWbGFzdEZsYW1lQ291bnRlckNoYW5nZRgCIAEoA1IWbG'
|
||||||
'Jlc3RGcmllbmQYAyABKAhSCmJlc3RGcmllbmQSIAoLZm9yY2VVcGRhdGUYBCABKAhSC2ZvcmNl'
|
'FzdEZsYW1lQ291bnRlckNoYW5nZRIeCgpiZXN0RnJpZW5kGAMgASgIUgpiZXN0RnJpZW5kEiAK'
|
||||||
'VXBkYXRlQgoKCF9ncm91cElkQg8KDV9pc0RpcmVjdENoYXRCFwoVX3NlbmRlclByb2ZpbGVDb3'
|
'C2ZvcmNlVXBkYXRlGAQgASgIUgtmb3JjZVVwZGF0ZUIKCghfZ3JvdXBJZEIPCg1faXNEaXJlY3'
|
||||||
'VudGVyQhAKDl9tZXNzYWdlVXBkYXRlQggKBl9tZWRpYUIOCgxfbWVkaWFVcGRhdGVCEAoOX2Nv'
|
'RDaGF0QhcKFV9zZW5kZXJQcm9maWxlQ291bnRlckIQCg5fbWVzc2FnZVVwZGF0ZUIICgZfbWVk'
|
||||||
'bnRhY3RVcGRhdGVCEQoPX2NvbnRhY3RSZXF1ZXN0QgwKCl9mbGFtZVN5bmNCCwoJX3B1c2hLZX'
|
'aWFCDgoMX21lZGlhVXBkYXRlQhAKDl9jb250YWN0VXBkYXRlQhEKD19jb250YWN0UmVxdWVzdE'
|
||||||
'lzQgsKCV9yZWFjdGlvbkIOCgxfdGV4dE1lc3NhZ2VCDgoMX2dyb3VwQ3JlYXRlQgwKCl9ncm91'
|
'IMCgpfZmxhbWVTeW5jQgsKCV9wdXNoS2V5c0ILCglfcmVhY3Rpb25CDgoMX3RleHRNZXNzYWdl'
|
||||||
'cEpvaW5CDgoMX2dyb3VwVXBkYXRlQhcKFV9yZXNlbmRHcm91cFB1YmxpY0tleUIRCg9fZXJyb3'
|
'Qg4KDF9ncm91cENyZWF0ZUIMCgpfZ3JvdXBKb2luQg4KDF9ncm91cFVwZGF0ZUIXChVfcmVzZW'
|
||||||
'JfbWVzc2FnZXNCGgoYX2FkZGl0aW9uYWxfZGF0YV9tZXNzYWdl');
|
'5kR3JvdXBQdWJsaWNLZXlCEQoPX2Vycm9yX21lc3NhZ2VzQhoKGF9hZGRpdGlvbmFsX2RhdGFf'
|
||||||
|
'bWVzc2FnZQ==');
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ message EncryptedContent {
|
||||||
enum Type {
|
enum Type {
|
||||||
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;
|
||||||
}
|
}
|
||||||
Type type = 1;
|
Type type = 1;
|
||||||
string related_receipt_id = 2;
|
string related_receipt_id = 2;
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,15 @@ import 'package:drift/drift.dart' show Value;
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.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/utils/log.dart';
|
||||||
|
|
||||||
Future<void> handleErrorMessage(
|
Future<void> handleErrorMessage(
|
||||||
int fromUserId,
|
int fromUserId,
|
||||||
EncryptedContent_ErrorMessages error,
|
EncryptedContent_ErrorMessages error,
|
||||||
) async {
|
) async {
|
||||||
|
Log.error('Got error from $fromUserId: $error');
|
||||||
|
|
||||||
switch (error.type) {
|
switch (error.type) {
|
||||||
case EncryptedContent_ErrorMessages_Type.UNKNOWN_MESSAGE_TYPE:
|
|
||||||
break;
|
|
||||||
case EncryptedContent_ErrorMessages_Type
|
case EncryptedContent_ErrorMessages_Type
|
||||||
.ERROR_PROCESSING_MESSAGE_CREATED_ACCOUNT_REQUEST_INSTEAD:
|
.ERROR_PROCESSING_MESSAGE_CREATED_ACCOUNT_REQUEST_INSTEAD:
|
||||||
await twonlyDB.receiptsDao.updateReceiptWidthUserId(
|
await twonlyDB.receiptsDao.updateReceiptWidthUserId(
|
||||||
|
|
@ -25,5 +26,8 @@ Future<void> handleErrorMessage(
|
||||||
requested: Value(true),
|
requested: Value(true),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
// ignore: no_default_cases
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,20 +9,18 @@ Future<void> handleMessageUpdate(
|
||||||
) async {
|
) async {
|
||||||
switch (messageUpdate.type) {
|
switch (messageUpdate.type) {
|
||||||
case EncryptedContent_MessageUpdate_Type.OPENED:
|
case EncryptedContent_MessageUpdate_Type.OPENED:
|
||||||
for (final targetMessageId in messageUpdate.multipleTargetMessageIds) {
|
|
||||||
Log.info(
|
Log.info(
|
||||||
'Opened message $targetMessageId',
|
'Opened message ${messageUpdate.multipleTargetMessageIds}',
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
await twonlyDB.messagesDao.handleMessageOpened(
|
await twonlyDB.messagesDao.handleMessagesOpened(
|
||||||
contactId,
|
contactId,
|
||||||
targetMessageId,
|
messageUpdate.multipleTargetMessageIds,
|
||||||
fromTimestamp(messageUpdate.timestamp),
|
fromTimestamp(messageUpdate.timestamp),
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Log.warn(e);
|
Log.warn(e);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
case EncryptedContent_MessageUpdate_Type.DELETE:
|
case EncryptedContent_MessageUpdate_Type.DELETE:
|
||||||
if (!await isSender(contactId, messageUpdate.senderMessageId)) {
|
if (!await isSender(contactId, messageUpdate.senderMessageId)) {
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -375,15 +375,18 @@ Future<void> notifyContactAboutOpeningMessage(
|
||||||
),
|
),
|
||||||
blocking: false,
|
blocking: false,
|
||||||
);
|
);
|
||||||
|
await twonlyDB.batch((batch) {
|
||||||
for (final messageId in messageOtherIds) {
|
for (final messageId in messageOtherIds) {
|
||||||
await twonlyDB.messagesDao.updateMessageId(
|
batch.update(
|
||||||
messageId,
|
twonlyDB.messages,
|
||||||
MessagesCompanion(
|
MessagesCompanion(
|
||||||
openedAt: Value(actionAt),
|
openedAt: Value(actionAt),
|
||||||
openedByAll: Value(actionAt),
|
openedByAll: Value(actionAt),
|
||||||
),
|
),
|
||||||
|
where: (tbl) => tbl.messageId.equals(messageId),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
await updateLastMessageId(contactId, biggestMessageId);
|
await updateLastMessageId(contactId, biggestMessageId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ import 'package:twonly/src/utils/misc.dart';
|
||||||
final lockHandleServerMessage = Mutex();
|
final lockHandleServerMessage = Mutex();
|
||||||
|
|
||||||
Future<void> handleServerMessage(server.ServerToClient msg) async {
|
Future<void> handleServerMessage(server.ServerToClient msg) async {
|
||||||
|
return lockHandleServerMessage.protect(() async {
|
||||||
/// Returns means, that the server can delete the message from the server.
|
/// Returns means, that the server can delete the message from the server.
|
||||||
final ok = client.Response_Ok()..none = true;
|
final ok = client.Response_Ok()..none = true;
|
||||||
var response = client.Response()..ok = ok;
|
var response = client.Response()..ok = ok;
|
||||||
|
|
@ -61,9 +62,11 @@ Future<void> handleServerMessage(server.ServerToClient msg) async {
|
||||||
..response = response;
|
..response = response;
|
||||||
|
|
||||||
await apiService.sendResponse(ClientToServer()..v0 = v0);
|
await apiService.sendResponse(ClientToServer()..v0 = v0);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
DateTime lastPushKeyRequest = clock.now().subtract(const Duration(hours: 1));
|
DateTime lastPushKeyRequest = clock.now().subtract(const Duration(hours: 1));
|
||||||
|
bool alreadyPerformedAnResync = false;
|
||||||
|
|
||||||
Mutex protectReceiptCheck = Mutex();
|
Mutex protectReceiptCheck = Mutex();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ Future<void> handleMediaError(MediaFile media) async {
|
||||||
Future<void> importSignalContactAndCreateRequest(
|
Future<void> importSignalContactAndCreateRequest(
|
||||||
server.Response_UserData userdata,
|
server.Response_UserData userdata,
|
||||||
) async {
|
) async {
|
||||||
if (await createNewSignalSession(userdata)) {
|
if (await processSignalUserData(userdata)) {
|
||||||
// 1. Setup notifications keys with the other user
|
// 1. Setup notifications keys with the other user
|
||||||
await setupNotificationWithUsers(
|
await setupNotificationWithUsers(
|
||||||
forceContact: userdata.userId.toInt(),
|
forceContact: userdata.userId.toInt(),
|
||||||
|
|
|
||||||
|
|
@ -472,7 +472,7 @@ Future<bool> addNewHiddenContact(int contactId) async {
|
||||||
const Value(true), // this will hide the contact in the contact list
|
const Value(true), // this will hide the contact in the contact list
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
await createNewSignalSession(userData);
|
await processSignalUserData(userData);
|
||||||
unawaited(setupNotificationWithUsers(forceContact: contactId));
|
unawaited(setupNotificationWithUsers(forceContact: contactId));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:drift/drift.dart' show Value;
|
import 'package:drift/drift.dart' show Value;
|
||||||
import 'package:ffmpeg_kit_flutter_new/ffmpeg_kit.dart';
|
|
||||||
import 'package:ffmpeg_kit_flutter_new/return_code.dart';
|
|
||||||
import 'package:flutter_image_compress/flutter_image_compress.dart';
|
import 'package:flutter_image_compress/flutter_image_compress.dart';
|
||||||
|
import 'package:pro_video_editor/pro_video_editor.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
|
import 'package:video_compress/video_compress.dart';
|
||||||
|
|
||||||
Future<void> compressImage(
|
Future<void> compressImage(
|
||||||
File sourceFile,
|
File sourceFile,
|
||||||
|
|
@ -69,51 +69,47 @@ Future<void> compressAndOverlayVideo(MediaFileService media) async {
|
||||||
media.ffmpegOutputPath.deleteSync();
|
media.ffmpegOutputPath.deleteSync();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gUser.disableVideoCompression) {
|
|
||||||
media.originalPath.copySync(media.tempPath.path);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var overLayCommand = '';
|
|
||||||
if (media.overlayImagePath.existsSync()) {
|
|
||||||
if (Platform.isAndroid) {
|
|
||||||
overLayCommand =
|
|
||||||
'-i "${media.overlayImagePath.path}" -filter_complex "[1:v]format=yuva420p[ovr_in];[0:v]format=yuv420p[base_in];[ovr_in][base_in]scale2ref=w=rw:h=rh[ovr_out][base_out];[base_out][ovr_out]overlay=0:0"';
|
|
||||||
} else {
|
|
||||||
overLayCommand =
|
|
||||||
'-i "${media.overlayImagePath.path}" -filter_complex "[1:v][0:v]scale2ref=w=ref_w:h=ref_h[ovr][base];[base][ovr]overlay=0:0"';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final stopwatch = Stopwatch()..start();
|
final stopwatch = Stopwatch()..start();
|
||||||
|
|
||||||
var additionalParams = '';
|
try {
|
||||||
|
final task = VideoRenderData(
|
||||||
|
video: EditorVideo.file(media.originalPath),
|
||||||
|
// qualityPreset: VideoQualityPreset.p720High,
|
||||||
|
imageBytes: media.overlayImagePath.readAsBytesSync(),
|
||||||
|
enableAudio: !media.removeAudio,
|
||||||
|
);
|
||||||
|
|
||||||
if (Platform.isAndroid) {
|
final result = await ProVideoEditor.instance.renderVideo(task);
|
||||||
additionalParams += ' -c:v libx264';
|
media.ffmpegOutputPath.writeAsBytesSync(result);
|
||||||
|
|
||||||
|
MediaInfo? mediaInfo;
|
||||||
|
try {
|
||||||
|
mediaInfo = await VideoCompress.compressVideo(
|
||||||
|
media.ffmpegOutputPath.path,
|
||||||
|
quality: VideoQuality.Res640x480Quality,
|
||||||
|
includeAudio: true,
|
||||||
|
);
|
||||||
|
Log.info('Video has now size of ${mediaInfo!.filesize} bytes.');
|
||||||
|
} catch (e) {
|
||||||
|
Log.error('during video compression: $e');
|
||||||
}
|
}
|
||||||
|
|
||||||
var command =
|
if (mediaInfo == null) {
|
||||||
'-i "${media.originalPath.path}" $overLayCommand -map "0:a?" $additionalParams -preset veryfast -crf 28 -c:a aac -b:a 64k "${media.ffmpegOutputPath.path}"';
|
Log.error('Could not compress video using original video.');
|
||||||
|
// as a fall back use the non compressed version
|
||||||
if (media.removeAudio) {
|
|
||||||
command =
|
|
||||||
'-i "${media.originalPath.path}" $overLayCommand $additionalParams -preset veryfast -crf 28 -an "${media.ffmpegOutputPath.path}"';
|
|
||||||
}
|
|
||||||
|
|
||||||
final session = await FFmpegKit.execute(command);
|
|
||||||
final returnCode = await session.getReturnCode();
|
|
||||||
|
|
||||||
if (ReturnCode.isSuccess(returnCode)) {
|
|
||||||
media.ffmpegOutputPath.copySync(media.tempPath.path);
|
media.ffmpegOutputPath.copySync(media.tempPath.path);
|
||||||
|
} else {
|
||||||
|
mediaInfo.file!.copySync(media.tempPath.path);
|
||||||
|
}
|
||||||
|
|
||||||
stopwatch.stop();
|
stopwatch.stop();
|
||||||
Log.info(
|
Log.info(
|
||||||
'It took ${stopwatch.elapsedMilliseconds}ms to compress the video',
|
'It took ${stopwatch.elapsedMilliseconds}ms to compress the video. Reduced from ${media.ffmpegOutputPath.statSync().size} to ${media.tempPath.statSync().size} bytes.',
|
||||||
);
|
);
|
||||||
} else {
|
} catch (e) {
|
||||||
Log.info(command);
|
Log.error(e);
|
||||||
Log.error('Compression failed for the video with exit code $returnCode.');
|
// Log.error('Compression failed for the video with exit code $returnCode.');
|
||||||
Log.error(await session.getAllLogsAsString());
|
// Log.error(await session.getAllLogsAsString());
|
||||||
// This should not happen, but in case "notify" the user that the video was not send... This is absolutely bad, but
|
// This should not happen, but in case "notify" the user that the video was not send... This is absolutely bad, but
|
||||||
// better this way then sending an uncompressed media file which potentially is 100MB big :/
|
// better this way then sending an uncompressed media file which potentially is 100MB big :/
|
||||||
// Hopefully the user will report the strange behavior <3
|
// Hopefully the user will report the strange behavior <3
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:ffmpeg_kit_flutter_new/ffmpeg_kit.dart';
|
import 'dart:ui';
|
||||||
import 'package:ffmpeg_kit_flutter_new/return_code.dart';
|
import 'package:pro_video_editor/pro_video_editor.dart';
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
|
|
||||||
Future<void> createThumbnailsForVideo(
|
Future<void> createThumbnailsForVideo(
|
||||||
|
|
@ -13,22 +13,26 @@ Future<void> createThumbnailsForVideo(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final command =
|
final images = await ProVideoEditor.instance.getThumbnails(
|
||||||
'-y -i "${sourceFile.path}" -ss 00:00:00 -vframes 1 -vf "scale=iw:ih:flags=lanczos" -c:v libwebp -q:v 100 -compression_level 6 "${destinationFile.path}"';
|
ThumbnailConfigs(
|
||||||
|
video: EditorVideo.file(sourceFile),
|
||||||
|
outputFormat: ThumbnailFormat.webp,
|
||||||
|
timestamps: const [
|
||||||
|
Duration.zero,
|
||||||
|
],
|
||||||
|
outputSize: const Size(272, 153),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
final session = await FFmpegKit.execute(command);
|
if (images.isNotEmpty) {
|
||||||
final returnCode = await session.getReturnCode();
|
|
||||||
|
|
||||||
if (ReturnCode.isSuccess(returnCode)) {
|
|
||||||
stopwatch.stop();
|
stopwatch.stop();
|
||||||
|
destinationFile.writeAsBytesSync(images.first);
|
||||||
Log.info(
|
Log.info(
|
||||||
'It took ${stopwatch.elapsedMilliseconds}ms to create the thumbnail.',
|
'It took ${stopwatch.elapsedMilliseconds}ms to create the thumbnail.',
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
Log.info(command);
|
|
||||||
Log.error(
|
Log.error(
|
||||||
'Thumbnail creation failed for the video with exit code $returnCode.',
|
'Thumbnail creation failed for the video with exit code.',
|
||||||
);
|
);
|
||||||
Log.error(await session.getAllLogsAsString());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,12 @@
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
||||||
|
// ignore: implementation_imports
|
||||||
|
import 'package:libsignal_protocol_dart/src/invalid_message_exception.dart';
|
||||||
import 'package:mutex/mutex.dart';
|
import 'package:mutex/mutex.dart';
|
||||||
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
|
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
|
||||||
import 'package:twonly/src/services/signal/consts.signal.dart';
|
import 'package:twonly/src/services/api/messages.dart';
|
||||||
|
import 'package:twonly/src/services/signal/session.signal.dart';
|
||||||
import 'package:twonly/src/services/signal/utils.signal.dart';
|
import 'package:twonly/src/services/signal/utils.signal.dart';
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
|
|
||||||
|
|
@ -17,7 +20,7 @@ Future<CiphertextMessage?> signalEncryptMessage(
|
||||||
return lockingSignalEncryption.protect<CiphertextMessage?>(() async {
|
return lockingSignalEncryption.protect<CiphertextMessage?>(() async {
|
||||||
try {
|
try {
|
||||||
final signalStore = (await getSignalStore())!;
|
final signalStore = (await getSignalStore())!;
|
||||||
final address = SignalProtocolAddress(target.toString(), defaultDeviceId);
|
final address = getSignalAddress(target);
|
||||||
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) {
|
||||||
|
|
@ -27,16 +30,18 @@ Future<CiphertextMessage?> signalEncryptMessage(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool alreadyPerformedAnResync = false;
|
||||||
|
|
||||||
Future<(EncryptedContent?, PlaintextContent_DecryptionErrorMessage_Type?)>
|
Future<(EncryptedContent?, PlaintextContent_DecryptionErrorMessage_Type?)>
|
||||||
signalDecryptMessage(
|
signalDecryptMessage(
|
||||||
int source,
|
int fromUserId,
|
||||||
Uint8List encryptedContentRaw,
|
Uint8List encryptedContentRaw,
|
||||||
int type,
|
int type,
|
||||||
) async {
|
) async {
|
||||||
try {
|
try {
|
||||||
final session = SessionCipher.fromStore(
|
final session = SessionCipher.fromStore(
|
||||||
(await getSignalStore())!,
|
(await getSignalStore())!,
|
||||||
SignalProtocolAddress(source.toString(), defaultDeviceId),
|
getSignalAddress(fromUserId),
|
||||||
);
|
);
|
||||||
|
|
||||||
Uint8List plaintext;
|
Uint8List plaintext;
|
||||||
|
|
@ -59,6 +64,26 @@ Future<(EncryptedContent?, PlaintextContent_DecryptionErrorMessage_Type?)>
|
||||||
} on InvalidKeyIdException catch (e) {
|
} on InvalidKeyIdException catch (e) {
|
||||||
Log.warn(e);
|
Log.warn(e);
|
||||||
return (null, PlaintextContent_DecryptionErrorMessage_Type.PREKEY_UNKNOWN);
|
return (null, PlaintextContent_DecryptionErrorMessage_Type.PREKEY_UNKNOWN);
|
||||||
|
} on InvalidMessageException catch (e) {
|
||||||
|
if (!alreadyPerformedAnResync) {
|
||||||
|
if (await handleSessionResync(fromUserId)) {
|
||||||
|
// This flag prevents from resyncing the session the client received multiple new
|
||||||
|
// messages from the server he could not decrypt
|
||||||
|
alreadyPerformedAnResync = true;
|
||||||
|
|
||||||
|
// This message contains a new PreKeyBundle establishing a new signal session
|
||||||
|
await sendCipherText(
|
||||||
|
fromUserId,
|
||||||
|
EncryptedContent(
|
||||||
|
errorMessages: EncryptedContent_ErrorMessages(
|
||||||
|
type: EncryptedContent_ErrorMessages_Type.SESSION_OUT_OF_SYNC,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Log.warn(e);
|
||||||
|
return (null, PlaintextContent_DecryptionErrorMessage_Type.UNKNOWN);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Log.error(e);
|
Log.error(e);
|
||||||
return (null, PlaintextContent_DecryptionErrorMessage_Type.UNKNOWN);
|
return (null, PlaintextContent_DecryptionErrorMessage_Type.UNKNOWN);
|
||||||
|
|
|
||||||
|
|
@ -6,17 +6,14 @@ import 'package:twonly/src/services/signal/consts.signal.dart';
|
||||||
import 'package:twonly/src/services/signal/utils.signal.dart';
|
import 'package:twonly/src/services/signal/utils.signal.dart';
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
|
|
||||||
Future<bool> createNewSignalSession(Response_UserData userData) async {
|
Future<bool> processSignalUserData(Response_UserData userData) async {
|
||||||
final SignalProtocolStore? signalStore = await getSignalStore();
|
final SignalProtocolStore? signalStore = await getSignalStore();
|
||||||
|
|
||||||
if (signalStore == null) {
|
if (signalStore == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final targetAddress = SignalProtocolAddress(
|
final targetAddress = getSignalAddress(userData.userId.toInt());
|
||||||
userData.userId.toString(),
|
|
||||||
defaultDeviceId,
|
|
||||||
);
|
|
||||||
|
|
||||||
final sessionBuilder = SessionBuilder.fromSignalStore(
|
final sessionBuilder = SessionBuilder.fromSignalStore(
|
||||||
signalStore,
|
signalStore,
|
||||||
|
|
@ -82,30 +79,6 @@ Future<void> deleteSessionWithTarget(int target) async {
|
||||||
await signalStore.sessionStore.deleteSession(address);
|
await signalStore.sessionStore.deleteSession(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Fingerprint?> generateSessionFingerPrint(int target) async {
|
|
||||||
final signalStore = await getSignalStore();
|
|
||||||
if (signalStore == null) return null;
|
|
||||||
try {
|
|
||||||
final targetIdentity = await signalStore
|
|
||||||
.getIdentity(SignalProtocolAddress(target.toString(), defaultDeviceId));
|
|
||||||
if (targetIdentity != null) {
|
|
||||||
final generator = NumericFingerprintGenerator(5200);
|
|
||||||
final localFingerprint = generator.createFor(
|
|
||||||
1,
|
|
||||||
Uint8List.fromList([gUser.userId]),
|
|
||||||
(await signalStore.getIdentityKeyPair()).getPublicKey(),
|
|
||||||
Uint8List.fromList([target]),
|
|
||||||
targetIdentity,
|
|
||||||
);
|
|
||||||
|
|
||||||
return localFingerprint;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
} catch (e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Uint8List?> getPublicKeyFromContact(int contactId) async {
|
Future<Uint8List?> getPublicKeyFromContact(int contactId) async {
|
||||||
final signalStore = await getSignalStore();
|
final signalStore = await getSignalStore();
|
||||||
if (signalStore == null) return null;
|
if (signalStore == null) return null;
|
||||||
|
|
@ -124,3 +97,13 @@ Future<Uint8List?> getPublicKeyFromContact(int contactId) async {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> handleSessionResync(int fromUserId) async {
|
||||||
|
final userData = await apiService.getUserById(fromUserId);
|
||||||
|
if (userData != null) {
|
||||||
|
Log.info('Got new session data from the server to re-sync the session');
|
||||||
|
return processSignalUserData(userData);
|
||||||
|
}
|
||||||
|
Log.info('Could not download userdata from the server.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
||||||
import 'package:twonly/src/database/signal/connect_signal_protocol_store.dart';
|
import 'package:twonly/src/database/signal/connect_signal_protocol_store.dart';
|
||||||
import 'package:twonly/src/model/json/signal_identity.dart';
|
import 'package:twonly/src/model/json/signal_identity.dart';
|
||||||
|
import 'package:twonly/src/services/signal/consts.signal.dart';
|
||||||
import 'package:twonly/src/services/signal/identity.signal.dart';
|
import 'package:twonly/src/services/signal/identity.signal.dart';
|
||||||
|
|
||||||
Future<ConnectSignalProtocolStore?> getSignalStore() async {
|
Future<ConnectSignalProtocolStore?> getSignalStore() async {
|
||||||
|
|
@ -18,3 +19,7 @@ Future<ConnectSignalProtocolStore> getSignalStoreFromIdentity(
|
||||||
signalIdentity.registrationId,
|
signalIdentity.registrationId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SignalProtocolAddress getSignalAddress(int userId) {
|
||||||
|
return SignalProtocolAddress(userId.toString(), defaultDeviceId);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -444,7 +444,7 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
|
|
||||||
// Make a short delay, so the setState does have its effect...
|
// Make a short delay, so the setState does have its effect...
|
||||||
await Future.delayed(const Duration(milliseconds: 10));
|
await Future.delayed(const Duration(milliseconds: 80));
|
||||||
|
|
||||||
final image = await screenshotController.capture(
|
final image = await screenshotController.capture(
|
||||||
pixelRatio: pixelRatio,
|
pixelRatio: pixelRatio,
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,6 @@ class ChatItem {
|
||||||
const ChatItem._({
|
const ChatItem._({
|
||||||
this.message,
|
this.message,
|
||||||
this.date,
|
this.date,
|
||||||
this.lastOpenedPosition,
|
|
||||||
this.groupAction,
|
this.groupAction,
|
||||||
});
|
});
|
||||||
factory ChatItem.date(DateTime date) {
|
factory ChatItem.date(DateTime date) {
|
||||||
|
|
@ -42,20 +41,15 @@ class ChatItem {
|
||||||
factory ChatItem.message(Message message) {
|
factory ChatItem.message(Message message) {
|
||||||
return ChatItem._(message: message);
|
return ChatItem._(message: message);
|
||||||
}
|
}
|
||||||
factory ChatItem.lastOpenedPosition(List<Contact> contacts) {
|
|
||||||
return ChatItem._(lastOpenedPosition: contacts);
|
|
||||||
}
|
|
||||||
factory ChatItem.groupAction(GroupHistory groupAction) {
|
factory ChatItem.groupAction(GroupHistory groupAction) {
|
||||||
return ChatItem._(groupAction: groupAction);
|
return ChatItem._(groupAction: groupAction);
|
||||||
}
|
}
|
||||||
final GroupHistory? groupAction;
|
final GroupHistory? groupAction;
|
||||||
final Message? message;
|
final Message? message;
|
||||||
final DateTime? date;
|
final DateTime? date;
|
||||||
final List<Contact>? lastOpenedPosition;
|
|
||||||
bool get isMessage => message != null;
|
bool get isMessage => message != null;
|
||||||
bool get isDate => date != null;
|
bool get isDate => date != null;
|
||||||
bool get isGroupAction => groupAction != null;
|
bool get isGroupAction => groupAction != null;
|
||||||
bool get isLastOpenedPosition => lastOpenedPosition != null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Displays detailed information about a SampleItem.
|
/// Displays detailed information about a SampleItem.
|
||||||
|
|
@ -75,14 +69,11 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
|
||||||
late StreamSubscription<List<Message>> messageSub;
|
late StreamSubscription<List<Message>> messageSub;
|
||||||
StreamSubscription<List<GroupHistory>>? groupActionsSub;
|
StreamSubscription<List<GroupHistory>>? groupActionsSub;
|
||||||
StreamSubscription<List<Contact>>? contactSub;
|
StreamSubscription<List<Contact>>? contactSub;
|
||||||
StreamSubscription<Future<List<(Message, Contact)>>>?
|
|
||||||
lastOpenedMessageByContactSub;
|
|
||||||
|
|
||||||
Map<int, Contact> userIdToContact = {};
|
Map<int, Contact> userIdToContact = {};
|
||||||
|
|
||||||
List<ChatItem> messages = [];
|
List<ChatItem> messages = [];
|
||||||
List<Message> allMessages = [];
|
List<Message> allMessages = [];
|
||||||
List<(Message, Contact)> lastOpenedMessageByContact = [];
|
|
||||||
List<GroupHistory> groupActions = [];
|
List<GroupHistory> groupActions = [];
|
||||||
List<MemoryItem> galleryItems = [];
|
List<MemoryItem> galleryItems = [];
|
||||||
Message? quotesMessage;
|
Message? quotesMessage;
|
||||||
|
|
@ -105,7 +96,6 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
|
||||||
messageSub.cancel();
|
messageSub.cancel();
|
||||||
contactSub?.cancel();
|
contactSub?.cancel();
|
||||||
groupActionsSub?.cancel();
|
groupActionsSub?.cancel();
|
||||||
lastOpenedMessageByContactSub?.cancel();
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -121,19 +111,10 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!widget.group.isDirectChat) {
|
if (!widget.group.isDirectChat) {
|
||||||
final lastOpenedStream =
|
|
||||||
twonlyDB.messagesDao.watchLastOpenedMessagePerContact(group.groupId);
|
|
||||||
lastOpenedMessageByContactSub =
|
|
||||||
lastOpenedStream.listen((lastActionsFuture) async {
|
|
||||||
final update = await lastActionsFuture;
|
|
||||||
lastOpenedMessageByContact = update;
|
|
||||||
await setMessages(allMessages, update, groupActions);
|
|
||||||
});
|
|
||||||
|
|
||||||
final actionsStream = twonlyDB.groupsDao.watchGroupActions(group.groupId);
|
final actionsStream = twonlyDB.groupsDao.watchGroupActions(group.groupId);
|
||||||
groupActionsSub = actionsStream.listen((update) async {
|
groupActionsSub = actionsStream.listen((update) async {
|
||||||
groupActions = update;
|
groupActions = update;
|
||||||
await setMessages(allMessages, lastOpenedMessageByContact, update);
|
await setMessages(allMessages, update);
|
||||||
});
|
});
|
||||||
|
|
||||||
final contactsStream = twonlyDB.contactsDao.watchAllContacts();
|
final contactsStream = twonlyDB.contactsDao.watchAllContacts();
|
||||||
|
|
@ -147,21 +128,14 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
|
||||||
final msgStream = twonlyDB.messagesDao.watchByGroupId(group.groupId);
|
final msgStream = twonlyDB.messagesDao.watchByGroupId(group.groupId);
|
||||||
messageSub = msgStream.listen((update) async {
|
messageSub = msgStream.listen((update) async {
|
||||||
allMessages = update;
|
allMessages = update;
|
||||||
|
|
||||||
/// In case a message is not open yet the message is updated, which will trigger this watch to be called again.
|
|
||||||
/// So as long as the Mutex is locked just return...
|
|
||||||
if (protectMessageUpdating.isLocked) {
|
|
||||||
// return;
|
|
||||||
}
|
|
||||||
await protectMessageUpdating.protect(() async {
|
await protectMessageUpdating.protect(() async {
|
||||||
await setMessages(update, lastOpenedMessageByContact, groupActions);
|
await setMessages(update, groupActions);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> setMessages(
|
Future<void> setMessages(
|
||||||
List<Message> newMessages,
|
List<Message> newMessages,
|
||||||
List<(Message, Contact)> lastOpenedMessageByContact,
|
|
||||||
List<GroupHistory> groupActions,
|
List<GroupHistory> groupActions,
|
||||||
) async {
|
) async {
|
||||||
await flutterLocalNotificationsPlugin.cancelAll();
|
await flutterLocalNotificationsPlugin.cancelAll();
|
||||||
|
|
@ -172,19 +146,7 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
|
||||||
DateTime? lastDate;
|
DateTime? lastDate;
|
||||||
|
|
||||||
final openedMessages = <int, List<String>>{};
|
final openedMessages = <int, List<String>>{};
|
||||||
final lastOpenedMessageToContact = <String, List<Contact>>{};
|
|
||||||
|
|
||||||
final myLastMessageIndex =
|
|
||||||
newMessages.lastIndexWhere((t) => t.senderId == null);
|
|
||||||
|
|
||||||
for (final opened in lastOpenedMessageByContact) {
|
|
||||||
if (!lastOpenedMessageToContact.containsKey(opened.$1.messageId)) {
|
|
||||||
lastOpenedMessageToContact[opened.$1.messageId] = [opened.$2];
|
|
||||||
} else {
|
|
||||||
lastOpenedMessageToContact[opened.$1.messageId]!.add(opened.$2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var index = 0;
|
|
||||||
var groupHistoryIndex = 0;
|
var groupHistoryIndex = 0;
|
||||||
|
|
||||||
for (final msg in newMessages) {
|
for (final msg in newMessages) {
|
||||||
|
|
@ -199,7 +161,6 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
index += 1;
|
|
||||||
if (msg.type != MessageType.media.name &&
|
if (msg.type != MessageType.media.name &&
|
||||||
msg.senderId != null &&
|
msg.senderId != null &&
|
||||||
msg.openedAt == null) {
|
msg.openedAt == null) {
|
||||||
|
|
@ -221,16 +182,6 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
|
||||||
lastDate = msg.createdAt;
|
lastDate = msg.createdAt;
|
||||||
}
|
}
|
||||||
chatItems.add(ChatItem.message(msg));
|
chatItems.add(ChatItem.message(msg));
|
||||||
|
|
||||||
if (index <= myLastMessageIndex || index == newMessages.length) {
|
|
||||||
if (lastOpenedMessageToContact.containsKey(msg.messageId)) {
|
|
||||||
chatItems.add(
|
|
||||||
ChatItem.lastOpenedPosition(
|
|
||||||
lastOpenedMessageToContact[msg.messageId]!,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (groupHistoryIndex < groupActions.length) {
|
if (groupHistoryIndex < groupActions.length) {
|
||||||
for (var i = groupHistoryIndex; i < groupActions.length; i++) {
|
for (var i = groupHistoryIndex; i < groupActions.length; i++) {
|
||||||
|
|
@ -341,17 +292,6 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
|
||||||
return ChatDateChip(
|
return ChatDateChip(
|
||||||
item: messages[i],
|
item: messages[i],
|
||||||
);
|
);
|
||||||
} else if (messages[i].isLastOpenedPosition) {
|
|
||||||
return Wrap(
|
|
||||||
spacing: 8,
|
|
||||||
alignment: WrapAlignment.center,
|
|
||||||
children: messages[i].lastOpenedPosition!.map((w) {
|
|
||||||
return AvatarIcon(
|
|
||||||
contactId: w.userId,
|
|
||||||
fontSize: 12,
|
|
||||||
);
|
|
||||||
}).toList(),
|
|
||||||
);
|
|
||||||
} else if (messages[i].isGroupAction) {
|
} else if (messages[i].isGroupAction) {
|
||||||
return ChatGroupAction(
|
return ChatGroupAction(
|
||||||
key: Key(messages[i].groupAction!.groupHistoryId),
|
key: Key(messages[i].groupAction!.groupHistoryId),
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
||||||
import 'package:twonly/src/database/tables/messages.table.dart';
|
import 'package:twonly/src/database/tables/messages.table.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/model/memory_item.model.dart';
|
import 'package:twonly/src/model/memory_item.model.dart';
|
||||||
|
import 'package:twonly/src/model/protobuf/client/generated/data.pb.dart';
|
||||||
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart'
|
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart'
|
||||||
hide Message;
|
hide Message;
|
||||||
import 'package:twonly/src/services/api/mediafiles/download.service.dart'
|
import 'package:twonly/src/services/api/mediafiles/download.service.dart'
|
||||||
|
|
@ -13,8 +14,10 @@ import 'package:twonly/src/services/api/mediafiles/download.service.dart'
|
||||||
import 'package:twonly/src/services/api/messages.dart';
|
import 'package:twonly/src/services/api/messages.dart';
|
||||||
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
import 'package:twonly/src/views/chats/chat_messages_components/entries/common.dart';
|
||||||
import 'package:twonly/src/views/chats/chat_messages_components/in_chat_media_viewer.dart';
|
import 'package:twonly/src/views/chats/chat_messages_components/in_chat_media_viewer.dart';
|
||||||
import 'package:twonly/src/views/chats/media_viewer.view.dart';
|
import 'package:twonly/src/views/chats/media_viewer.view.dart';
|
||||||
|
import 'package:twonly/src/views/components/better_text.dart';
|
||||||
|
|
||||||
class ChatMediaEntry extends StatefulWidget {
|
class ChatMediaEntry extends StatefulWidget {
|
||||||
const ChatMediaEntry({
|
const ChatMediaEntry({
|
||||||
|
|
@ -114,7 +117,54 @@ class _ChatMediaEntryState extends State<ChatMediaEntry> {
|
||||||
context,
|
context,
|
||||||
);
|
);
|
||||||
|
|
||||||
return GestureDetector(
|
var imageBorderRadius = BorderRadius.circular(12);
|
||||||
|
|
||||||
|
Widget additionalMessageData = Container();
|
||||||
|
|
||||||
|
final addData = widget.message.additionalMessageData;
|
||||||
|
if (addData != null) {
|
||||||
|
final info =
|
||||||
|
getBubbleInfo(context, widget.message, null, null, null, 200);
|
||||||
|
final data = AdditionalMessageData.fromBuffer(addData);
|
||||||
|
if (data.hasLink() && widget.message.mediaStored) {
|
||||||
|
imageBorderRadius = const BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(12),
|
||||||
|
topRight: Radius.circular(12),
|
||||||
|
bottomLeft: Radius.circular(5),
|
||||||
|
bottomRight: Radius.circular(5),
|
||||||
|
);
|
||||||
|
|
||||||
|
additionalMessageData = Container(
|
||||||
|
constraints: BoxConstraints(
|
||||||
|
maxWidth: MediaQuery.of(context).size.width * 0.8,
|
||||||
|
),
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.only(left: 10, top: 6, bottom: 6, right: 10),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: info.color,
|
||||||
|
borderRadius: const BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(5),
|
||||||
|
topRight: Radius.circular(12),
|
||||||
|
bottomLeft: Radius.circular(12),
|
||||||
|
bottomRight: Radius.circular(12),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
BetterText(text: data.link, textColor: info.textColor),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: widget.message.senderId == null
|
||||||
|
? CrossAxisAlignment.end
|
||||||
|
: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
GestureDetector(
|
||||||
key: reopenMediaFile,
|
key: reopenMediaFile,
|
||||||
onDoubleTap: onDoubleTap,
|
onDoubleTap: onDoubleTap,
|
||||||
onTap: (widget.message.type == MessageType.media.name) ? onTap : null,
|
onTap: (widget.message.type == MessageType.media.name) ? onTap : null,
|
||||||
|
|
@ -127,7 +177,7 @@ class _ChatMediaEntryState extends State<ChatMediaEntry> {
|
||||||
child: Align(
|
child: Align(
|
||||||
alignment: Alignment.centerRight,
|
alignment: Alignment.centerRight,
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: imageBorderRadius,
|
||||||
child: InChatMediaViewer(
|
child: InChatMediaViewer(
|
||||||
message: widget.message,
|
message: widget.message,
|
||||||
group: widget.group,
|
group: widget.group,
|
||||||
|
|
@ -139,6 +189,9 @@ class _ChatMediaEntryState extends State<ChatMediaEntry> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
additionalMessageData,
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
|
||||||
class BetterListTile extends StatelessWidget {
|
class BetterListTile extends StatelessWidget {
|
||||||
const BetterListTile({
|
const BetterListTile({
|
||||||
required this.text,
|
this.text,
|
||||||
|
this.textWidget,
|
||||||
this.onTap,
|
this.onTap,
|
||||||
this.icon,
|
this.icon,
|
||||||
this.leading,
|
this.leading,
|
||||||
|
|
@ -17,7 +18,8 @@ class BetterListTile extends StatelessWidget {
|
||||||
final IconData? icon;
|
final IconData? icon;
|
||||||
final Widget? leading;
|
final Widget? leading;
|
||||||
final Widget? trailing;
|
final Widget? trailing;
|
||||||
final String text;
|
final String? text;
|
||||||
|
final Widget? textWidget;
|
||||||
final Widget? subtitle;
|
final Widget? subtitle;
|
||||||
final Color? color;
|
final Color? color;
|
||||||
final VoidCallback? onTap;
|
final VoidCallback? onTap;
|
||||||
|
|
@ -40,10 +42,12 @@ class BetterListTile extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
trailing: trailing,
|
trailing: trailing,
|
||||||
title: Text(
|
title: text != null
|
||||||
text,
|
? Text(
|
||||||
|
text!,
|
||||||
style: TextStyle(color: color),
|
style: TextStyle(color: color),
|
||||||
),
|
)
|
||||||
|
: textWidget,
|
||||||
subtitle: subtitle,
|
subtitle: subtitle,
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
|
||||||
class SvgIcons {
|
class SvgIcons {
|
||||||
static const String verifiedGreen = 'assets/icons/verified_badge_green.svg';
|
static const String verifiedGreen = 'assets/icons/verified_badge_green.svg';
|
||||||
static const String verifiedYellow = 'assets/icons/verified_badge_yellow.svg';
|
|
||||||
static const String verifiedRed = 'assets/icons/verified_badge_red.svg';
|
static const String verifiedRed = 'assets/icons/verified_badge_red.svg';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ class _VerifiedShieldState extends State<VerifiedShield> {
|
||||||
contact = contacts.first;
|
contact = contacts.first;
|
||||||
}
|
}
|
||||||
setState(() {
|
setState(() {
|
||||||
isVerified = contacts.any((t) => t.verified);
|
isVerified = contacts.every((t) => t.verified);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else if (widget.contact != null) {
|
} else if (widget.contact != null) {
|
||||||
|
|
|
||||||
|
|
@ -242,7 +242,18 @@ class _GroupViewState extends State<GroupView> {
|
||||||
contactId: member.$1.userId,
|
contactId: member.$1.userId,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
),
|
),
|
||||||
text: getContactDisplayName(member.$1, maxLength: 25),
|
textWidget: Row(
|
||||||
|
children: [
|
||||||
|
Text(getContactDisplayName(member.$1, maxLength: 25)),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 5),
|
||||||
|
child: VerifiedShield(
|
||||||
|
key: Key(member.$2.contactId.toString()),
|
||||||
|
contact: member.$1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
trailing: (member.$2.memberState == MemberState.admin)
|
trailing: (member.$2.memberState == MemberState.admin)
|
||||||
? Text(context.lang.admin)
|
? Text(context.lang.admin)
|
||||||
: null,
|
: null,
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,10 @@ import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/services/api/messages.dart';
|
import 'package:twonly/src/services/api/messages.dart';
|
||||||
|
import 'package:twonly/src/services/signal/utils.signal.dart';
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
|
||||||
|
|
@ -26,13 +28,63 @@ class _AutomatedTestingViewState extends State<AutomatedTestingView> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
if (kReleaseMode) return Container();
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text('Automated Testing'),
|
title: const Text('Automated Testing'),
|
||||||
),
|
),
|
||||||
body: ListView(
|
body: ListView(
|
||||||
children: [
|
children: [
|
||||||
if (!kReleaseMode)
|
ListTile(
|
||||||
|
title: const Text('Trigger Signal Out-Of-Sync'),
|
||||||
|
onTap: () async {
|
||||||
|
final username = await showUserNameDialog(context);
|
||||||
|
if (username == null) return;
|
||||||
|
final contacts = await twonlyDB.contactsDao
|
||||||
|
.getContactsByUsername(username.toLowerCase());
|
||||||
|
if (contacts.length != 1) {
|
||||||
|
Log.error('No single user fund');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final userId = contacts.first.userId;
|
||||||
|
|
||||||
|
final group = await twonlyDB.groupsDao.getDirectChat(userId);
|
||||||
|
if (group == null) {
|
||||||
|
Log.error('Target user must have a group!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final sessionStore = await getSignalStore();
|
||||||
|
|
||||||
|
// 1. Store a valid session
|
||||||
|
final originalSession =
|
||||||
|
await sessionStore!.loadSession(getSignalAddress(userId));
|
||||||
|
final serializedSession = originalSession.serialize();
|
||||||
|
|
||||||
|
for (var i = 0; i < 10; i++) {
|
||||||
|
await insertAndSendTextMessage(
|
||||||
|
group.groupId,
|
||||||
|
'DesyncTest_1',
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final corruptedSession =
|
||||||
|
SessionRecord.fromSerialized(serializedSession);
|
||||||
|
await sessionStore.storeSession(
|
||||||
|
getSignalAddress(userId),
|
||||||
|
corruptedSession,
|
||||||
|
);
|
||||||
|
|
||||||
|
await insertAndSendTextMessage(
|
||||||
|
group.groupId,
|
||||||
|
'DesyncTest_2',
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
|
||||||
|
// The other client should res
|
||||||
|
},
|
||||||
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text('Sending a lot of messages.'),
|
title: const Text('Sending a lot of messages.'),
|
||||||
subtitle: Text(lotsOfMessagesStatus),
|
subtitle: Text(lotsOfMessagesStatus),
|
||||||
|
|
|
||||||
|
|
@ -29,14 +29,6 @@ class _DeveloperSettingsViewState extends State<DeveloperSettingsView> {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> toggleVideoCompression() async {
|
|
||||||
await updateUserdata((u) {
|
|
||||||
u.disableVideoCompression = !u.disableVideoCompression;
|
|
||||||
return u;
|
|
||||||
});
|
|
||||||
setState(() {});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
|
@ -75,17 +67,6 @@ class _DeveloperSettingsViewState extends State<DeveloperSettingsView> {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
ListTile(
|
|
||||||
title: const Text('Disable ffmpeg'),
|
|
||||||
subtitle: const Text(
|
|
||||||
'If your smartphone crashes, you can disable ffmpeg. This will prevent your videos from being compressed and NO FILTER will be applied to the video! This is a workaround, until the root-cause in ffmpeg is found.',
|
|
||||||
),
|
|
||||||
onTap: toggleVideoCompression,
|
|
||||||
trailing: Switch(
|
|
||||||
value: gUser.disableVideoCompression,
|
|
||||||
onChanged: (a) => toggleVideoCompression(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (!kReleaseMode)
|
if (!kReleaseMode)
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text('Automated Testing'),
|
title: const Text('Automated Testing'),
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import 'package:flutter/services.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:twonly/src/constants/routes.keys.dart';
|
import 'package:twonly/src/constants/routes.keys.dart';
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/views/components/loader.dart';
|
import 'package:twonly/src/views/components/loader.dart';
|
||||||
|
|
||||||
class DiagnosticsView extends StatefulWidget {
|
class DiagnosticsView extends StatefulWidget {
|
||||||
|
|
@ -150,14 +151,19 @@ class _LogViewerWidgetState extends State<LogViewerWidget> {
|
||||||
}
|
}
|
||||||
|
|
||||||
TextSpan _formatLineSpan(_LogEntry e) {
|
TextSpan _formatLineSpan(_LogEntry e) {
|
||||||
final tsStyle =
|
final tsStyle = TextStyle(
|
||||||
TextStyle(color: Colors.grey.shade500, fontFamily: 'monospace');
|
color: isDarkMode(context) ? Colors.white : Colors.black,
|
||||||
|
fontFamily: 'monospace',
|
||||||
|
);
|
||||||
final levelStyle = TextStyle(
|
final levelStyle = TextStyle(
|
||||||
color: Colors.blueGrey.shade600,
|
color: Colors.blueGrey.shade600,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
fontFamily: 'monospace',
|
fontFamily: 'monospace',
|
||||||
);
|
);
|
||||||
const msgStyle = TextStyle(fontFamily: 'monospace');
|
final msgStyle = TextStyle(
|
||||||
|
color: isDarkMode(context) ? Colors.white : Colors.black,
|
||||||
|
fontFamily: 'monospace',
|
||||||
|
);
|
||||||
|
|
||||||
return TextSpan(
|
return TextSpan(
|
||||||
children: [
|
children: [
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,11 @@ class _VerificationBadeFaqViewState extends State<VerificationBadeFaqView> {
|
||||||
description: context.lang.verificationBadgeGreenDesc,
|
description: context.lang.verificationBadgeGreenDesc,
|
||||||
),
|
),
|
||||||
_buildItem(
|
_buildItem(
|
||||||
icon: const SvgIcon(assetPath: SvgIcons.verifiedYellow, size: 40),
|
icon: const SvgIcon(
|
||||||
|
assetPath: SvgIcons.verifiedGreen,
|
||||||
|
size: 40,
|
||||||
|
color: Color.fromARGB(255, 227, 227, 3),
|
||||||
|
),
|
||||||
description: context.lang.verificationBadgeYellowDesc,
|
description: context.lang.verificationBadgeYellowDesc,
|
||||||
),
|
),
|
||||||
_buildItem(
|
_buildItem(
|
||||||
|
|
|
||||||
32
pubspec.lock
32
pubspec.lock
|
|
@ -456,22 +456,6 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.0"
|
version: "2.2.0"
|
||||||
ffmpeg_kit_flutter_new:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: ffmpeg_kit_flutter_new
|
|
||||||
sha256: d127635f27e93a7f21f0a14ce0a1a148e80919c402dac4a2118d73bfb17ce841
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "4.1.0"
|
|
||||||
ffmpeg_kit_flutter_platform_interface:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: ffmpeg_kit_flutter_platform_interface
|
|
||||||
sha256: addf046ae44e190ad0101b2fde2ad909a3cd08a2a109f6106d2f7048b7abedee
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.2.1"
|
|
||||||
file:
|
file:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -1513,6 +1497,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.3"
|
version: "6.0.3"
|
||||||
|
pro_video_editor:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: pro_video_editor
|
||||||
|
sha256: "0d985f7653c59e2b521d19db49351476eb74eb4001689b33fb8112ab1a9c4330"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.6.1"
|
||||||
protobuf:
|
protobuf:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
@ -1996,6 +1988,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "10.1.0"
|
version: "10.1.0"
|
||||||
|
video_compress:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: video_compress
|
||||||
|
sha256: "31bc5cdb9a02ba666456e5e1907393c28e6e0e972980d7d8d619a7beda0d4f20"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.4"
|
||||||
video_player:
|
video_player:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ description: "twonly, a privacy-friendly way to connect with friends through sec
|
||||||
|
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
|
|
||||||
version: 0.0.94+94
|
version: 0.0.97+97
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.6.0
|
sdk: ^3.6.0
|
||||||
|
|
@ -83,7 +83,6 @@ dependencies:
|
||||||
cached_network_image: ^3.4.1
|
cached_network_image: ^3.4.1
|
||||||
cryptography_flutter_plus: ^2.3.4
|
cryptography_flutter_plus: ^2.3.4
|
||||||
cryptography_plus: ^2.7.0
|
cryptography_plus: ^2.7.0
|
||||||
ffmpeg_kit_flutter_new: ^4.1.0
|
|
||||||
flutter_android_volume_keydown: ^1.0.1
|
flutter_android_volume_keydown: ^1.0.1
|
||||||
flutter_image_compress: ^2.4.0
|
flutter_image_compress: ^2.4.0
|
||||||
flutter_volume_controller: ^1.3.4
|
flutter_volume_controller: ^1.3.4
|
||||||
|
|
@ -113,6 +112,8 @@ dependencies:
|
||||||
flutter_sharing_intent: ^2.0.4
|
flutter_sharing_intent: ^2.0.4
|
||||||
no_screenshot: ^0.3.1
|
no_screenshot: ^0.3.1
|
||||||
google_mlkit_face_detection: ^0.13.1
|
google_mlkit_face_detection: ^0.13.1
|
||||||
|
pro_video_editor: ^1.6.1
|
||||||
|
video_compress: ^3.1.4
|
||||||
|
|
||||||
dependency_overrides:
|
dependency_overrides:
|
||||||
dots_indicator:
|
dots_indicator:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue