mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 11:18:41 +00:00
fix #207
This commit is contained in:
parent
4108d9a798
commit
80746b5139
29 changed files with 4852 additions and 390 deletions
271
.vscode/launch.json
vendored
Normal file
271
.vscode/launch.json
vendored
Normal file
|
|
@ -0,0 +1,271 @@
|
||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "twonly-app",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "twonly-app (profile mode)",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"flutterMode": "profile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "twonly-app (release mode)",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"flutterMode": "release"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "flutter_secure_storage",
|
||||||
|
"cwd": "dependencies/flutter_secure_storage",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "flutter_zxing",
|
||||||
|
"cwd": "dependencies/flutter_zxing",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "flutter_zxing (profile mode)",
|
||||||
|
"cwd": "dependencies/flutter_zxing",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"flutterMode": "profile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "flutter_zxing (release mode)",
|
||||||
|
"cwd": "dependencies/flutter_zxing",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"flutterMode": "release"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "flutter_secure_storage",
|
||||||
|
"cwd": "dependencies/flutter_secure_storage/flutter_secure_storage",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "flutter_secure_storage (profile mode)",
|
||||||
|
"cwd": "dependencies/flutter_secure_storage/flutter_secure_storage",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"flutterMode": "profile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "flutter_secure_storage (release mode)",
|
||||||
|
"cwd": "dependencies/flutter_secure_storage/flutter_secure_storage",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"flutterMode": "release"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "flutter_secure_storage_darwin",
|
||||||
|
"cwd": "dependencies/flutter_secure_storage/flutter_secure_storage_darwin",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "flutter_secure_storage_darwin (profile mode)",
|
||||||
|
"cwd": "dependencies/flutter_secure_storage/flutter_secure_storage_darwin",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"flutterMode": "profile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "flutter_secure_storage_darwin (release mode)",
|
||||||
|
"cwd": "dependencies/flutter_secure_storage/flutter_secure_storage_darwin",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"flutterMode": "release"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "flutter_secure_storage_linux",
|
||||||
|
"cwd": "dependencies/flutter_secure_storage/flutter_secure_storage_linux",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "flutter_secure_storage_linux (profile mode)",
|
||||||
|
"cwd": "dependencies/flutter_secure_storage/flutter_secure_storage_linux",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"flutterMode": "profile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "flutter_secure_storage_linux (release mode)",
|
||||||
|
"cwd": "dependencies/flutter_secure_storage/flutter_secure_storage_linux",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"flutterMode": "release"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "flutter_secure_storage_platform_interface",
|
||||||
|
"cwd": "dependencies/flutter_secure_storage/flutter_secure_storage_platform_interface",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "flutter_secure_storage_platform_interface (profile mode)",
|
||||||
|
"cwd": "dependencies/flutter_secure_storage/flutter_secure_storage_platform_interface",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"flutterMode": "profile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "flutter_secure_storage_platform_interface (release mode)",
|
||||||
|
"cwd": "dependencies/flutter_secure_storage/flutter_secure_storage_platform_interface",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"flutterMode": "release"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "flutter_secure_storage_web",
|
||||||
|
"cwd": "dependencies/flutter_secure_storage/flutter_secure_storage_web",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "flutter_secure_storage_web (profile mode)",
|
||||||
|
"cwd": "dependencies/flutter_secure_storage/flutter_secure_storage_web",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"flutterMode": "profile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "flutter_secure_storage_web (release mode)",
|
||||||
|
"cwd": "dependencies/flutter_secure_storage/flutter_secure_storage_web",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"flutterMode": "release"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "flutter_secure_storage_windows",
|
||||||
|
"cwd": "dependencies/flutter_secure_storage/flutter_secure_storage_windows",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "flutter_secure_storage_windows (profile mode)",
|
||||||
|
"cwd": "dependencies/flutter_secure_storage/flutter_secure_storage_windows",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"flutterMode": "profile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "flutter_secure_storage_windows (release mode)",
|
||||||
|
"cwd": "dependencies/flutter_secure_storage/flutter_secure_storage_windows",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"flutterMode": "release"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "example",
|
||||||
|
"cwd": "dependencies/flutter_zxing/example",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "example (profile mode)",
|
||||||
|
"cwd": "dependencies/flutter_zxing/example",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"flutterMode": "profile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "example (release mode)",
|
||||||
|
"cwd": "dependencies/flutter_zxing/example",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"flutterMode": "release"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "zxscanner",
|
||||||
|
"cwd": "dependencies/flutter_zxing/zxscanner",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "zxscanner (profile mode)",
|
||||||
|
"cwd": "dependencies/flutter_zxing/zxscanner",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"flutterMode": "profile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "zxscanner (release mode)",
|
||||||
|
"cwd": "dependencies/flutter_zxing/zxscanner",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"flutterMode": "release"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "flutter_secure_storage_macos",
|
||||||
|
"cwd": "dependencies/flutter_secure_storage/archived_packages/flutter_secure_storage_macos",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "flutter_secure_storage_macos (profile mode)",
|
||||||
|
"cwd": "dependencies/flutter_secure_storage/archived_packages/flutter_secure_storage_macos",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"flutterMode": "profile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "flutter_secure_storage_macos (release mode)",
|
||||||
|
"cwd": "dependencies/flutter_secure_storage/archived_packages/flutter_secure_storage_macos",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"flutterMode": "release"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "example",
|
||||||
|
"cwd": "dependencies/flutter_secure_storage/flutter_secure_storage/example",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "example (profile mode)",
|
||||||
|
"cwd": "dependencies/flutter_secure_storage/flutter_secure_storage/example",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"flutterMode": "profile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "example (release mode)",
|
||||||
|
"cwd": "dependencies/flutter_secure_storage/flutter_secure_storage/example",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"flutterMode": "release"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "example",
|
||||||
|
"cwd": "dependencies/flutter_secure_storage/flutter_secure_storage_windows/example",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "example (profile mode)",
|
||||||
|
"cwd": "dependencies/flutter_secure_storage/flutter_secure_storage_windows/example",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"flutterMode": "profile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "example (release mode)",
|
||||||
|
"cwd": "dependencies/flutter_secure_storage/flutter_secure_storage_windows/example",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"flutterMode": "release"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
1
drift_schemas/twonly_database/drift_schema_v12.json
Normal file
1
drift_schemas/twonly_database/drift_schema_v12.json
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -44,6 +44,7 @@ void main() async {
|
||||||
twonlyDB = TwonlyDatabase();
|
twonlyDB = TwonlyDatabase();
|
||||||
await twonlyDB.messagesDao.resetPendingDownloadState();
|
await twonlyDB.messagesDao.resetPendingDownloadState();
|
||||||
await twonlyDB.messagesDao.handleMediaFilesOlderThan7Days();
|
await twonlyDB.messagesDao.handleMediaFilesOlderThan7Days();
|
||||||
|
await twonlyDB.signalDao.purgeOutDatedPreKeys();
|
||||||
|
|
||||||
// purge media files in the background
|
// purge media files in the background
|
||||||
purgeReceivedMediaFiles();
|
purgeReceivedMediaFiles();
|
||||||
|
|
|
||||||
|
|
@ -36,9 +36,34 @@ class MessageRetransmissionDao extends DatabaseAccessor<TwonlyDatabase>
|
||||||
..where((t) => t.retransmissionId.equals(retransmissionId));
|
..where((t) => t.retransmissionId.equals(retransmissionId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future updateRetransmission(
|
||||||
|
int retransmissionId,
|
||||||
|
MessageRetransmissionsCompanion updatedValues,
|
||||||
|
) {
|
||||||
|
return (update(messageRetransmissions)
|
||||||
|
..where((c) => c.retransmissionId.equals(retransmissionId)))
|
||||||
|
.write(updatedValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future resetAckStatusForAllMessages() {
|
||||||
|
return ((update(messageRetransmissions))
|
||||||
|
..where((m) => m.willNotGetACKByUser.equals(false)))
|
||||||
|
.write(
|
||||||
|
MessageRetransmissionsCompanion(
|
||||||
|
acknowledgeByServerAt: Value(null),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Future deleteRetransmissionById(int retransmissionId) {
|
Future deleteRetransmissionById(int retransmissionId) {
|
||||||
return (delete(messageRetransmissions)
|
return (delete(messageRetransmissions)
|
||||||
..where((t) => t.retransmissionId.equals(retransmissionId)))
|
..where((t) => t.retransmissionId.equals(retransmissionId)))
|
||||||
.go();
|
.go();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future deleteRetransmissionByMessageId(int messageId) {
|
||||||
|
return (delete(messageRetransmissions)
|
||||||
|
..where((t) => t.messageId.equals(messageId)))
|
||||||
|
.go();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,16 @@
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/database/tables/signal_contact_prekey_table.dart';
|
import 'package:twonly/src/database/tables/signal_contact_prekey_table.dart';
|
||||||
import 'package:twonly/src/database/tables/signal_contact_signed_prekey_table.dart';
|
import 'package:twonly/src/database/tables/signal_contact_signed_prekey_table.dart';
|
||||||
import 'package:twonly/src/database/twonly_database.dart';
|
import 'package:twonly/src/database/twonly_database.dart';
|
||||||
|
import 'package:twonly/src/utils/log.dart';
|
||||||
|
|
||||||
part 'signal_dao.g.dart';
|
part 'signal_dao.g.dart';
|
||||||
|
|
||||||
@DriftAccessor(tables: [SignalContactPreKeys, SignalContactSignedPreKeys])
|
@DriftAccessor(tables: [
|
||||||
|
SignalContactPreKeys,
|
||||||
|
SignalContactSignedPreKeys,
|
||||||
|
])
|
||||||
class SignalDao extends DatabaseAccessor<TwonlyDatabase> with _$SignalDaoMixin {
|
class SignalDao extends DatabaseAccessor<TwonlyDatabase> with _$SignalDaoMixin {
|
||||||
// this constructor is required so that the main database can create an instance
|
// this constructor is required so that the main database can create an instance
|
||||||
// of this object.
|
// of this object.
|
||||||
|
|
@ -19,6 +24,12 @@ class SignalDao extends DatabaseAccessor<TwonlyDatabase> with _$SignalDaoMixin {
|
||||||
.go();
|
.go();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future deleteAllPreKeysByContactId(int contactId) async {
|
||||||
|
await (delete(signalContactPreKeys)
|
||||||
|
..where((t) => t.contactId.equals(contactId)))
|
||||||
|
.go();
|
||||||
|
}
|
||||||
|
|
||||||
// 1: Count the number of pre-keys by contact ID
|
// 1: Count the number of pre-keys by contact ID
|
||||||
Future<int> countPreKeysByContactId(int contactId) {
|
Future<int> countPreKeysByContactId(int contactId) {
|
||||||
return (select(signalContactPreKeys)
|
return (select(signalContactPreKeys)
|
||||||
|
|
@ -49,9 +60,13 @@ class SignalDao extends DatabaseAccessor<TwonlyDatabase> with _$SignalDaoMixin {
|
||||||
// 3: Insert multiple pre-keys
|
// 3: Insert multiple pre-keys
|
||||||
Future<void> insertPreKeys(
|
Future<void> insertPreKeys(
|
||||||
List<SignalContactPreKeysCompanion> preKeys) async {
|
List<SignalContactPreKeysCompanion> preKeys) async {
|
||||||
await batch((batch) {
|
for (final preKey in preKeys) {
|
||||||
batch.insertAll(signalContactPreKeys, preKeys);
|
try {
|
||||||
});
|
into(signalContactPreKeys).insert(preKey);
|
||||||
|
} catch (e) {
|
||||||
|
Log.error("$e");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4: Get signed pre-key by contact ID
|
// 4: Get signed pre-key by contact ID
|
||||||
|
|
@ -64,12 +79,28 @@ class SignalDao extends DatabaseAccessor<TwonlyDatabase> with _$SignalDaoMixin {
|
||||||
// 5: Insert or update signed pre-key by contact ID
|
// 5: Insert or update signed pre-key by contact ID
|
||||||
Future<void> insertOrUpdateSignedPreKeyByContactId(
|
Future<void> insertOrUpdateSignedPreKeyByContactId(
|
||||||
SignalContactSignedPreKeysCompanion signedPreKey) async {
|
SignalContactSignedPreKeysCompanion signedPreKey) async {
|
||||||
final existingKey =
|
await (delete(signalContactSignedPreKeys)
|
||||||
await getSignedPreKeyByContactId(signedPreKey.contactId.value);
|
..where((t) => t.contactId.equals(signedPreKey.contactId.value)))
|
||||||
if (existingKey != null) {
|
.go();
|
||||||
await update(signalContactSignedPreKeys).replace(signedPreKey);
|
|
||||||
} else {
|
|
||||||
await into(signalContactSignedPreKeys).insert(signedPreKey);
|
await into(signalContactSignedPreKeys).insert(signedPreKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> purgeOutDatedPreKeys() async {
|
||||||
|
// other pre keys are valid 25 days
|
||||||
|
await (delete(signalContactSignedPreKeys)
|
||||||
|
..where((t) => (t.createdAt.isSmallerThanValue(
|
||||||
|
DateTime.now().subtract(
|
||||||
|
Duration(days: 25),
|
||||||
|
),
|
||||||
|
))))
|
||||||
|
.go();
|
||||||
|
// own pre keys are valid for 40 days
|
||||||
|
await (delete(twonlyDB.signalPreKeyStores)
|
||||||
|
..where((t) => (t.createdAt.isSmallerThanValue(
|
||||||
|
DateTime.now().subtract(
|
||||||
|
Duration(days: 40),
|
||||||
|
),
|
||||||
|
))))
|
||||||
|
.go();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,25 +32,6 @@ class ConnectPreKeyStore extends PreKeyStore {
|
||||||
.go();
|
.go();
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<int?> getNextPreKeyId() async {
|
|
||||||
try {
|
|
||||||
String tableName = twonlyDB.signalPreKeyStores.actualTableName;
|
|
||||||
String columnName = twonlyDB.signalPreKeyStores.preKeyId.name;
|
|
||||||
|
|
||||||
final result = await twonlyDB
|
|
||||||
.customSelect('SELECT MAX($columnName) AS max_id FROM $tableName')
|
|
||||||
.get();
|
|
||||||
int? count = result.first.read<int?>('max_id');
|
|
||||||
if (count == null) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return count + 1;
|
|
||||||
} catch (e) {
|
|
||||||
Log.error("$e");
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> storePreKey(int preKeyId, PreKeyRecord record) async {
|
Future<void> storePreKey(int preKeyId, PreKeyRecord record) async {
|
||||||
final preKeyCompanion = SignalPreKeyStoresCompanion(
|
final preKeyCompanion = SignalPreKeyStoresCompanion(
|
||||||
|
|
|
||||||
|
|
@ -22,24 +22,6 @@ class ConnectSignedPreKeyStore extends SignedPreKeyStore {
|
||||||
return store;
|
return store;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<int> getNextKeyId() async {
|
|
||||||
final storage = FlutterSecureStorage();
|
|
||||||
final storeSerialized = await storage.read(
|
|
||||||
key: SecureStorageKeys.signalSignedPreKey,
|
|
||||||
);
|
|
||||||
if (storeSerialized == null) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
final storeHashMap = json.decode(storeSerialized);
|
|
||||||
var maxKeyId = 0;
|
|
||||||
for (final item in storeHashMap) {
|
|
||||||
if (maxKeyId < item[0]) {
|
|
||||||
maxKeyId = item[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return maxKeyId + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future safeStore(HashMap<int, Uint8List> store) async {
|
Future safeStore(HashMap<int, Uint8List> store) async {
|
||||||
final storage = FlutterSecureStorage();
|
final storage = FlutterSecureStorage();
|
||||||
var storeHashMap = [];
|
var storeHashMap = [];
|
||||||
|
|
|
||||||
|
|
@ -15,5 +15,8 @@ class MessageRetransmissions extends Table {
|
||||||
BlobColumn get plaintextContent => blob()();
|
BlobColumn get plaintextContent => blob()();
|
||||||
BlobColumn get pushData => blob().nullable()();
|
BlobColumn get pushData => blob().nullable()();
|
||||||
|
|
||||||
|
BoolColumn get willNotGetACKByUser =>
|
||||||
|
boolean().withDefault(Constant(false))();
|
||||||
|
|
||||||
DateTimeColumn get acknowledgeByServerAt => dateTime().nullable()();
|
DateTimeColumn get acknowledgeByServerAt => dateTime().nullable()();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ enum MessageKind {
|
||||||
opened,
|
opened,
|
||||||
ack,
|
ack,
|
||||||
pushKey,
|
pushKey,
|
||||||
|
requestPushKey,
|
||||||
receiveMediaError,
|
receiveMediaError,
|
||||||
signalDecryptError
|
signalDecryptError
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ class TwonlyDatabase extends _$TwonlyDatabase {
|
||||||
TwonlyDatabase.forTesting(DatabaseConnection super.connection);
|
TwonlyDatabase.forTesting(DatabaseConnection super.connection);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get schemaVersion => 11;
|
int get schemaVersion => 12;
|
||||||
|
|
||||||
static QueryExecutor _openConnection() {
|
static QueryExecutor _openConnection() {
|
||||||
return driftDatabase(
|
return driftDatabase(
|
||||||
|
|
@ -121,6 +121,10 @@ class TwonlyDatabase extends _$TwonlyDatabase {
|
||||||
from10To11: (m, schema) async {
|
from10To11: (m, schema) async {
|
||||||
m.createTable(messageRetransmissions);
|
m.createTable(messageRetransmissions);
|
||||||
},
|
},
|
||||||
|
from11To12: (m, schema) async {
|
||||||
|
m.addColumn(schema.messageRetransmissions,
|
||||||
|
schema.messageRetransmissions.willNotGetACKByUser);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -135,6 +139,12 @@ class TwonlyDatabase extends _$TwonlyDatabase {
|
||||||
await delete(messageRetransmissions).go();
|
await delete(messageRetransmissions).go();
|
||||||
await delete(mediaDownloads).go();
|
await delete(mediaDownloads).go();
|
||||||
await delete(mediaUploads).go();
|
await delete(mediaUploads).go();
|
||||||
|
await update(contacts).write(
|
||||||
|
ContactsCompanion(
|
||||||
|
avatarSvg: Value(null),
|
||||||
|
myAvatarCounter: Value(0),
|
||||||
|
),
|
||||||
|
);
|
||||||
await delete(signalContactPreKeys).go();
|
await delete(signalContactPreKeys).go();
|
||||||
await delete(signalContactSignedPreKeys).go();
|
await delete(signalContactSignedPreKeys).go();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4225,6 +4225,16 @@ class $MessageRetransmissionsTable extends MessageRetransmissions
|
||||||
late final GeneratedColumn<Uint8List> pushData = GeneratedColumn<Uint8List>(
|
late final GeneratedColumn<Uint8List> pushData = GeneratedColumn<Uint8List>(
|
||||||
'push_data', aliasedName, true,
|
'push_data', aliasedName, true,
|
||||||
type: DriftSqlType.blob, requiredDuringInsert: false);
|
type: DriftSqlType.blob, requiredDuringInsert: false);
|
||||||
|
static const VerificationMeta _willNotGetACKByUserMeta =
|
||||||
|
const VerificationMeta('willNotGetACKByUser');
|
||||||
|
@override
|
||||||
|
late final GeneratedColumn<bool> willNotGetACKByUser = GeneratedColumn<bool>(
|
||||||
|
'will_not_get_a_c_k_by_user', aliasedName, false,
|
||||||
|
type: DriftSqlType.bool,
|
||||||
|
requiredDuringInsert: false,
|
||||||
|
defaultConstraints: GeneratedColumn.constraintIsAlways(
|
||||||
|
'CHECK ("will_not_get_a_c_k_by_user" IN (0, 1))'),
|
||||||
|
defaultValue: Constant(false));
|
||||||
static const VerificationMeta _acknowledgeByServerAtMeta =
|
static const VerificationMeta _acknowledgeByServerAtMeta =
|
||||||
const VerificationMeta('acknowledgeByServerAt');
|
const VerificationMeta('acknowledgeByServerAt');
|
||||||
@override
|
@override
|
||||||
|
|
@ -4238,6 +4248,7 @@ class $MessageRetransmissionsTable extends MessageRetransmissions
|
||||||
messageId,
|
messageId,
|
||||||
plaintextContent,
|
plaintextContent,
|
||||||
pushData,
|
pushData,
|
||||||
|
willNotGetACKByUser,
|
||||||
acknowledgeByServerAt
|
acknowledgeByServerAt
|
||||||
];
|
];
|
||||||
@override
|
@override
|
||||||
|
|
@ -4279,6 +4290,12 @@ class $MessageRetransmissionsTable extends MessageRetransmissions
|
||||||
context.handle(_pushDataMeta,
|
context.handle(_pushDataMeta,
|
||||||
pushData.isAcceptableOrUnknown(data['push_data']!, _pushDataMeta));
|
pushData.isAcceptableOrUnknown(data['push_data']!, _pushDataMeta));
|
||||||
}
|
}
|
||||||
|
if (data.containsKey('will_not_get_a_c_k_by_user')) {
|
||||||
|
context.handle(
|
||||||
|
_willNotGetACKByUserMeta,
|
||||||
|
willNotGetACKByUser.isAcceptableOrUnknown(
|
||||||
|
data['will_not_get_a_c_k_by_user']!, _willNotGetACKByUserMeta));
|
||||||
|
}
|
||||||
if (data.containsKey('acknowledge_by_server_at')) {
|
if (data.containsKey('acknowledge_by_server_at')) {
|
||||||
context.handle(
|
context.handle(
|
||||||
_acknowledgeByServerAtMeta,
|
_acknowledgeByServerAtMeta,
|
||||||
|
|
@ -4304,6 +4321,8 @@ class $MessageRetransmissionsTable extends MessageRetransmissions
|
||||||
DriftSqlType.blob, data['${effectivePrefix}plaintext_content'])!,
|
DriftSqlType.blob, data['${effectivePrefix}plaintext_content'])!,
|
||||||
pushData: attachedDatabase.typeMapping
|
pushData: attachedDatabase.typeMapping
|
||||||
.read(DriftSqlType.blob, data['${effectivePrefix}push_data']),
|
.read(DriftSqlType.blob, data['${effectivePrefix}push_data']),
|
||||||
|
willNotGetACKByUser: attachedDatabase.typeMapping.read(DriftSqlType.bool,
|
||||||
|
data['${effectivePrefix}will_not_get_a_c_k_by_user'])!,
|
||||||
acknowledgeByServerAt: attachedDatabase.typeMapping.read(
|
acknowledgeByServerAt: attachedDatabase.typeMapping.read(
|
||||||
DriftSqlType.dateTime,
|
DriftSqlType.dateTime,
|
||||||
data['${effectivePrefix}acknowledge_by_server_at']),
|
data['${effectivePrefix}acknowledge_by_server_at']),
|
||||||
|
|
@ -4323,6 +4342,7 @@ class MessageRetransmission extends DataClass
|
||||||
final int? messageId;
|
final int? messageId;
|
||||||
final Uint8List plaintextContent;
|
final Uint8List plaintextContent;
|
||||||
final Uint8List? pushData;
|
final Uint8List? pushData;
|
||||||
|
final bool willNotGetACKByUser;
|
||||||
final DateTime? acknowledgeByServerAt;
|
final DateTime? acknowledgeByServerAt;
|
||||||
const MessageRetransmission(
|
const MessageRetransmission(
|
||||||
{required this.retransmissionId,
|
{required this.retransmissionId,
|
||||||
|
|
@ -4330,6 +4350,7 @@ class MessageRetransmission extends DataClass
|
||||||
this.messageId,
|
this.messageId,
|
||||||
required this.plaintextContent,
|
required this.plaintextContent,
|
||||||
this.pushData,
|
this.pushData,
|
||||||
|
required this.willNotGetACKByUser,
|
||||||
this.acknowledgeByServerAt});
|
this.acknowledgeByServerAt});
|
||||||
@override
|
@override
|
||||||
Map<String, Expression> toColumns(bool nullToAbsent) {
|
Map<String, Expression> toColumns(bool nullToAbsent) {
|
||||||
|
|
@ -4343,6 +4364,7 @@ class MessageRetransmission extends DataClass
|
||||||
if (!nullToAbsent || pushData != null) {
|
if (!nullToAbsent || pushData != null) {
|
||||||
map['push_data'] = Variable<Uint8List>(pushData);
|
map['push_data'] = Variable<Uint8List>(pushData);
|
||||||
}
|
}
|
||||||
|
map['will_not_get_a_c_k_by_user'] = Variable<bool>(willNotGetACKByUser);
|
||||||
if (!nullToAbsent || acknowledgeByServerAt != null) {
|
if (!nullToAbsent || acknowledgeByServerAt != null) {
|
||||||
map['acknowledge_by_server_at'] =
|
map['acknowledge_by_server_at'] =
|
||||||
Variable<DateTime>(acknowledgeByServerAt);
|
Variable<DateTime>(acknowledgeByServerAt);
|
||||||
|
|
@ -4361,6 +4383,7 @@ class MessageRetransmission extends DataClass
|
||||||
pushData: pushData == null && nullToAbsent
|
pushData: pushData == null && nullToAbsent
|
||||||
? const Value.absent()
|
? const Value.absent()
|
||||||
: Value(pushData),
|
: Value(pushData),
|
||||||
|
willNotGetACKByUser: Value(willNotGetACKByUser),
|
||||||
acknowledgeByServerAt: acknowledgeByServerAt == null && nullToAbsent
|
acknowledgeByServerAt: acknowledgeByServerAt == null && nullToAbsent
|
||||||
? const Value.absent()
|
? const Value.absent()
|
||||||
: Value(acknowledgeByServerAt),
|
: Value(acknowledgeByServerAt),
|
||||||
|
|
@ -4377,6 +4400,8 @@ class MessageRetransmission extends DataClass
|
||||||
plaintextContent:
|
plaintextContent:
|
||||||
serializer.fromJson<Uint8List>(json['plaintextContent']),
|
serializer.fromJson<Uint8List>(json['plaintextContent']),
|
||||||
pushData: serializer.fromJson<Uint8List?>(json['pushData']),
|
pushData: serializer.fromJson<Uint8List?>(json['pushData']),
|
||||||
|
willNotGetACKByUser:
|
||||||
|
serializer.fromJson<bool>(json['willNotGetACKByUser']),
|
||||||
acknowledgeByServerAt:
|
acknowledgeByServerAt:
|
||||||
serializer.fromJson<DateTime?>(json['acknowledgeByServerAt']),
|
serializer.fromJson<DateTime?>(json['acknowledgeByServerAt']),
|
||||||
);
|
);
|
||||||
|
|
@ -4390,6 +4415,7 @@ class MessageRetransmission extends DataClass
|
||||||
'messageId': serializer.toJson<int?>(messageId),
|
'messageId': serializer.toJson<int?>(messageId),
|
||||||
'plaintextContent': serializer.toJson<Uint8List>(plaintextContent),
|
'plaintextContent': serializer.toJson<Uint8List>(plaintextContent),
|
||||||
'pushData': serializer.toJson<Uint8List?>(pushData),
|
'pushData': serializer.toJson<Uint8List?>(pushData),
|
||||||
|
'willNotGetACKByUser': serializer.toJson<bool>(willNotGetACKByUser),
|
||||||
'acknowledgeByServerAt':
|
'acknowledgeByServerAt':
|
||||||
serializer.toJson<DateTime?>(acknowledgeByServerAt),
|
serializer.toJson<DateTime?>(acknowledgeByServerAt),
|
||||||
};
|
};
|
||||||
|
|
@ -4401,6 +4427,7 @@ class MessageRetransmission extends DataClass
|
||||||
Value<int?> messageId = const Value.absent(),
|
Value<int?> messageId = const Value.absent(),
|
||||||
Uint8List? plaintextContent,
|
Uint8List? plaintextContent,
|
||||||
Value<Uint8List?> pushData = const Value.absent(),
|
Value<Uint8List?> pushData = const Value.absent(),
|
||||||
|
bool? willNotGetACKByUser,
|
||||||
Value<DateTime?> acknowledgeByServerAt = const Value.absent()}) =>
|
Value<DateTime?> acknowledgeByServerAt = const Value.absent()}) =>
|
||||||
MessageRetransmission(
|
MessageRetransmission(
|
||||||
retransmissionId: retransmissionId ?? this.retransmissionId,
|
retransmissionId: retransmissionId ?? this.retransmissionId,
|
||||||
|
|
@ -4408,6 +4435,7 @@ class MessageRetransmission extends DataClass
|
||||||
messageId: messageId.present ? messageId.value : this.messageId,
|
messageId: messageId.present ? messageId.value : this.messageId,
|
||||||
plaintextContent: plaintextContent ?? this.plaintextContent,
|
plaintextContent: plaintextContent ?? this.plaintextContent,
|
||||||
pushData: pushData.present ? pushData.value : this.pushData,
|
pushData: pushData.present ? pushData.value : this.pushData,
|
||||||
|
willNotGetACKByUser: willNotGetACKByUser ?? this.willNotGetACKByUser,
|
||||||
acknowledgeByServerAt: acknowledgeByServerAt.present
|
acknowledgeByServerAt: acknowledgeByServerAt.present
|
||||||
? acknowledgeByServerAt.value
|
? acknowledgeByServerAt.value
|
||||||
: this.acknowledgeByServerAt,
|
: this.acknowledgeByServerAt,
|
||||||
|
|
@ -4424,6 +4452,9 @@ class MessageRetransmission extends DataClass
|
||||||
? data.plaintextContent.value
|
? data.plaintextContent.value
|
||||||
: this.plaintextContent,
|
: this.plaintextContent,
|
||||||
pushData: data.pushData.present ? data.pushData.value : this.pushData,
|
pushData: data.pushData.present ? data.pushData.value : this.pushData,
|
||||||
|
willNotGetACKByUser: data.willNotGetACKByUser.present
|
||||||
|
? data.willNotGetACKByUser.value
|
||||||
|
: this.willNotGetACKByUser,
|
||||||
acknowledgeByServerAt: data.acknowledgeByServerAt.present
|
acknowledgeByServerAt: data.acknowledgeByServerAt.present
|
||||||
? data.acknowledgeByServerAt.value
|
? data.acknowledgeByServerAt.value
|
||||||
: this.acknowledgeByServerAt,
|
: this.acknowledgeByServerAt,
|
||||||
|
|
@ -4438,6 +4469,7 @@ class MessageRetransmission extends DataClass
|
||||||
..write('messageId: $messageId, ')
|
..write('messageId: $messageId, ')
|
||||||
..write('plaintextContent: $plaintextContent, ')
|
..write('plaintextContent: $plaintextContent, ')
|
||||||
..write('pushData: $pushData, ')
|
..write('pushData: $pushData, ')
|
||||||
|
..write('willNotGetACKByUser: $willNotGetACKByUser, ')
|
||||||
..write('acknowledgeByServerAt: $acknowledgeByServerAt')
|
..write('acknowledgeByServerAt: $acknowledgeByServerAt')
|
||||||
..write(')'))
|
..write(')'))
|
||||||
.toString();
|
.toString();
|
||||||
|
|
@ -4450,6 +4482,7 @@ class MessageRetransmission extends DataClass
|
||||||
messageId,
|
messageId,
|
||||||
$driftBlobEquality.hash(plaintextContent),
|
$driftBlobEquality.hash(plaintextContent),
|
||||||
$driftBlobEquality.hash(pushData),
|
$driftBlobEquality.hash(pushData),
|
||||||
|
willNotGetACKByUser,
|
||||||
acknowledgeByServerAt);
|
acknowledgeByServerAt);
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) =>
|
bool operator ==(Object other) =>
|
||||||
|
|
@ -4461,6 +4494,7 @@ class MessageRetransmission extends DataClass
|
||||||
$driftBlobEquality.equals(
|
$driftBlobEquality.equals(
|
||||||
other.plaintextContent, this.plaintextContent) &&
|
other.plaintextContent, this.plaintextContent) &&
|
||||||
$driftBlobEquality.equals(other.pushData, this.pushData) &&
|
$driftBlobEquality.equals(other.pushData, this.pushData) &&
|
||||||
|
other.willNotGetACKByUser == this.willNotGetACKByUser &&
|
||||||
other.acknowledgeByServerAt == this.acknowledgeByServerAt);
|
other.acknowledgeByServerAt == this.acknowledgeByServerAt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4471,6 +4505,7 @@ class MessageRetransmissionsCompanion
|
||||||
final Value<int?> messageId;
|
final Value<int?> messageId;
|
||||||
final Value<Uint8List> plaintextContent;
|
final Value<Uint8List> plaintextContent;
|
||||||
final Value<Uint8List?> pushData;
|
final Value<Uint8List?> pushData;
|
||||||
|
final Value<bool> willNotGetACKByUser;
|
||||||
final Value<DateTime?> acknowledgeByServerAt;
|
final Value<DateTime?> acknowledgeByServerAt;
|
||||||
const MessageRetransmissionsCompanion({
|
const MessageRetransmissionsCompanion({
|
||||||
this.retransmissionId = const Value.absent(),
|
this.retransmissionId = const Value.absent(),
|
||||||
|
|
@ -4478,6 +4513,7 @@ class MessageRetransmissionsCompanion
|
||||||
this.messageId = const Value.absent(),
|
this.messageId = const Value.absent(),
|
||||||
this.plaintextContent = const Value.absent(),
|
this.plaintextContent = const Value.absent(),
|
||||||
this.pushData = const Value.absent(),
|
this.pushData = const Value.absent(),
|
||||||
|
this.willNotGetACKByUser = const Value.absent(),
|
||||||
this.acknowledgeByServerAt = const Value.absent(),
|
this.acknowledgeByServerAt = const Value.absent(),
|
||||||
});
|
});
|
||||||
MessageRetransmissionsCompanion.insert({
|
MessageRetransmissionsCompanion.insert({
|
||||||
|
|
@ -4486,6 +4522,7 @@ class MessageRetransmissionsCompanion
|
||||||
this.messageId = const Value.absent(),
|
this.messageId = const Value.absent(),
|
||||||
required Uint8List plaintextContent,
|
required Uint8List plaintextContent,
|
||||||
this.pushData = const Value.absent(),
|
this.pushData = const Value.absent(),
|
||||||
|
this.willNotGetACKByUser = const Value.absent(),
|
||||||
this.acknowledgeByServerAt = const Value.absent(),
|
this.acknowledgeByServerAt = const Value.absent(),
|
||||||
}) : contactId = Value(contactId),
|
}) : contactId = Value(contactId),
|
||||||
plaintextContent = Value(plaintextContent);
|
plaintextContent = Value(plaintextContent);
|
||||||
|
|
@ -4495,6 +4532,7 @@ class MessageRetransmissionsCompanion
|
||||||
Expression<int>? messageId,
|
Expression<int>? messageId,
|
||||||
Expression<Uint8List>? plaintextContent,
|
Expression<Uint8List>? plaintextContent,
|
||||||
Expression<Uint8List>? pushData,
|
Expression<Uint8List>? pushData,
|
||||||
|
Expression<bool>? willNotGetACKByUser,
|
||||||
Expression<DateTime>? acknowledgeByServerAt,
|
Expression<DateTime>? acknowledgeByServerAt,
|
||||||
}) {
|
}) {
|
||||||
return RawValuesInsertable({
|
return RawValuesInsertable({
|
||||||
|
|
@ -4503,6 +4541,8 @@ class MessageRetransmissionsCompanion
|
||||||
if (messageId != null) 'message_id': messageId,
|
if (messageId != null) 'message_id': messageId,
|
||||||
if (plaintextContent != null) 'plaintext_content': plaintextContent,
|
if (plaintextContent != null) 'plaintext_content': plaintextContent,
|
||||||
if (pushData != null) 'push_data': pushData,
|
if (pushData != null) 'push_data': pushData,
|
||||||
|
if (willNotGetACKByUser != null)
|
||||||
|
'will_not_get_a_c_k_by_user': willNotGetACKByUser,
|
||||||
if (acknowledgeByServerAt != null)
|
if (acknowledgeByServerAt != null)
|
||||||
'acknowledge_by_server_at': acknowledgeByServerAt,
|
'acknowledge_by_server_at': acknowledgeByServerAt,
|
||||||
});
|
});
|
||||||
|
|
@ -4514,6 +4554,7 @@ class MessageRetransmissionsCompanion
|
||||||
Value<int?>? messageId,
|
Value<int?>? messageId,
|
||||||
Value<Uint8List>? plaintextContent,
|
Value<Uint8List>? plaintextContent,
|
||||||
Value<Uint8List?>? pushData,
|
Value<Uint8List?>? pushData,
|
||||||
|
Value<bool>? willNotGetACKByUser,
|
||||||
Value<DateTime?>? acknowledgeByServerAt}) {
|
Value<DateTime?>? acknowledgeByServerAt}) {
|
||||||
return MessageRetransmissionsCompanion(
|
return MessageRetransmissionsCompanion(
|
||||||
retransmissionId: retransmissionId ?? this.retransmissionId,
|
retransmissionId: retransmissionId ?? this.retransmissionId,
|
||||||
|
|
@ -4521,6 +4562,7 @@ class MessageRetransmissionsCompanion
|
||||||
messageId: messageId ?? this.messageId,
|
messageId: messageId ?? this.messageId,
|
||||||
plaintextContent: plaintextContent ?? this.plaintextContent,
|
plaintextContent: plaintextContent ?? this.plaintextContent,
|
||||||
pushData: pushData ?? this.pushData,
|
pushData: pushData ?? this.pushData,
|
||||||
|
willNotGetACKByUser: willNotGetACKByUser ?? this.willNotGetACKByUser,
|
||||||
acknowledgeByServerAt:
|
acknowledgeByServerAt:
|
||||||
acknowledgeByServerAt ?? this.acknowledgeByServerAt,
|
acknowledgeByServerAt ?? this.acknowledgeByServerAt,
|
||||||
);
|
);
|
||||||
|
|
@ -4544,6 +4586,10 @@ class MessageRetransmissionsCompanion
|
||||||
if (pushData.present) {
|
if (pushData.present) {
|
||||||
map['push_data'] = Variable<Uint8List>(pushData.value);
|
map['push_data'] = Variable<Uint8List>(pushData.value);
|
||||||
}
|
}
|
||||||
|
if (willNotGetACKByUser.present) {
|
||||||
|
map['will_not_get_a_c_k_by_user'] =
|
||||||
|
Variable<bool>(willNotGetACKByUser.value);
|
||||||
|
}
|
||||||
if (acknowledgeByServerAt.present) {
|
if (acknowledgeByServerAt.present) {
|
||||||
map['acknowledge_by_server_at'] =
|
map['acknowledge_by_server_at'] =
|
||||||
Variable<DateTime>(acknowledgeByServerAt.value);
|
Variable<DateTime>(acknowledgeByServerAt.value);
|
||||||
|
|
@ -4559,6 +4605,7 @@ class MessageRetransmissionsCompanion
|
||||||
..write('messageId: $messageId, ')
|
..write('messageId: $messageId, ')
|
||||||
..write('plaintextContent: $plaintextContent, ')
|
..write('plaintextContent: $plaintextContent, ')
|
||||||
..write('pushData: $pushData, ')
|
..write('pushData: $pushData, ')
|
||||||
|
..write('willNotGetACKByUser: $willNotGetACKByUser, ')
|
||||||
..write('acknowledgeByServerAt: $acknowledgeByServerAt')
|
..write('acknowledgeByServerAt: $acknowledgeByServerAt')
|
||||||
..write(')'))
|
..write(')'))
|
||||||
.toString();
|
.toString();
|
||||||
|
|
@ -7107,6 +7154,7 @@ typedef $$MessageRetransmissionsTableCreateCompanionBuilder
|
||||||
Value<int?> messageId,
|
Value<int?> messageId,
|
||||||
required Uint8List plaintextContent,
|
required Uint8List plaintextContent,
|
||||||
Value<Uint8List?> pushData,
|
Value<Uint8List?> pushData,
|
||||||
|
Value<bool> willNotGetACKByUser,
|
||||||
Value<DateTime?> acknowledgeByServerAt,
|
Value<DateTime?> acknowledgeByServerAt,
|
||||||
});
|
});
|
||||||
typedef $$MessageRetransmissionsTableUpdateCompanionBuilder
|
typedef $$MessageRetransmissionsTableUpdateCompanionBuilder
|
||||||
|
|
@ -7116,6 +7164,7 @@ typedef $$MessageRetransmissionsTableUpdateCompanionBuilder
|
||||||
Value<int?> messageId,
|
Value<int?> messageId,
|
||||||
Value<Uint8List> plaintextContent,
|
Value<Uint8List> plaintextContent,
|
||||||
Value<Uint8List?> pushData,
|
Value<Uint8List?> pushData,
|
||||||
|
Value<bool> willNotGetACKByUser,
|
||||||
Value<DateTime?> acknowledgeByServerAt,
|
Value<DateTime?> acknowledgeByServerAt,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -7175,6 +7224,10 @@ class $$MessageRetransmissionsTableFilterComposer
|
||||||
ColumnFilters<Uint8List> get pushData => $composableBuilder(
|
ColumnFilters<Uint8List> get pushData => $composableBuilder(
|
||||||
column: $table.pushData, builder: (column) => ColumnFilters(column));
|
column: $table.pushData, builder: (column) => ColumnFilters(column));
|
||||||
|
|
||||||
|
ColumnFilters<bool> get willNotGetACKByUser => $composableBuilder(
|
||||||
|
column: $table.willNotGetACKByUser,
|
||||||
|
builder: (column) => ColumnFilters(column));
|
||||||
|
|
||||||
ColumnFilters<DateTime> get acknowledgeByServerAt => $composableBuilder(
|
ColumnFilters<DateTime> get acknowledgeByServerAt => $composableBuilder(
|
||||||
column: $table.acknowledgeByServerAt,
|
column: $table.acknowledgeByServerAt,
|
||||||
builder: (column) => ColumnFilters(column));
|
builder: (column) => ColumnFilters(column));
|
||||||
|
|
@ -7240,6 +7293,10 @@ class $$MessageRetransmissionsTableOrderingComposer
|
||||||
ColumnOrderings<Uint8List> get pushData => $composableBuilder(
|
ColumnOrderings<Uint8List> get pushData => $composableBuilder(
|
||||||
column: $table.pushData, builder: (column) => ColumnOrderings(column));
|
column: $table.pushData, builder: (column) => ColumnOrderings(column));
|
||||||
|
|
||||||
|
ColumnOrderings<bool> get willNotGetACKByUser => $composableBuilder(
|
||||||
|
column: $table.willNotGetACKByUser,
|
||||||
|
builder: (column) => ColumnOrderings(column));
|
||||||
|
|
||||||
ColumnOrderings<DateTime> get acknowledgeByServerAt => $composableBuilder(
|
ColumnOrderings<DateTime> get acknowledgeByServerAt => $composableBuilder(
|
||||||
column: $table.acknowledgeByServerAt,
|
column: $table.acknowledgeByServerAt,
|
||||||
builder: (column) => ColumnOrderings(column));
|
builder: (column) => ColumnOrderings(column));
|
||||||
|
|
@ -7303,6 +7360,9 @@ class $$MessageRetransmissionsTableAnnotationComposer
|
||||||
GeneratedColumn<Uint8List> get pushData =>
|
GeneratedColumn<Uint8List> get pushData =>
|
||||||
$composableBuilder(column: $table.pushData, builder: (column) => column);
|
$composableBuilder(column: $table.pushData, builder: (column) => column);
|
||||||
|
|
||||||
|
GeneratedColumn<bool> get willNotGetACKByUser => $composableBuilder(
|
||||||
|
column: $table.willNotGetACKByUser, builder: (column) => column);
|
||||||
|
|
||||||
GeneratedColumn<DateTime> get acknowledgeByServerAt => $composableBuilder(
|
GeneratedColumn<DateTime> get acknowledgeByServerAt => $composableBuilder(
|
||||||
column: $table.acknowledgeByServerAt, builder: (column) => column);
|
column: $table.acknowledgeByServerAt, builder: (column) => column);
|
||||||
|
|
||||||
|
|
@ -7379,6 +7439,7 @@ class $$MessageRetransmissionsTableTableManager extends RootTableManager<
|
||||||
Value<int?> messageId = const Value.absent(),
|
Value<int?> messageId = const Value.absent(),
|
||||||
Value<Uint8List> plaintextContent = const Value.absent(),
|
Value<Uint8List> plaintextContent = const Value.absent(),
|
||||||
Value<Uint8List?> pushData = const Value.absent(),
|
Value<Uint8List?> pushData = const Value.absent(),
|
||||||
|
Value<bool> willNotGetACKByUser = const Value.absent(),
|
||||||
Value<DateTime?> acknowledgeByServerAt = const Value.absent(),
|
Value<DateTime?> acknowledgeByServerAt = const Value.absent(),
|
||||||
}) =>
|
}) =>
|
||||||
MessageRetransmissionsCompanion(
|
MessageRetransmissionsCompanion(
|
||||||
|
|
@ -7387,6 +7448,7 @@ class $$MessageRetransmissionsTableTableManager extends RootTableManager<
|
||||||
messageId: messageId,
|
messageId: messageId,
|
||||||
plaintextContent: plaintextContent,
|
plaintextContent: plaintextContent,
|
||||||
pushData: pushData,
|
pushData: pushData,
|
||||||
|
willNotGetACKByUser: willNotGetACKByUser,
|
||||||
acknowledgeByServerAt: acknowledgeByServerAt,
|
acknowledgeByServerAt: acknowledgeByServerAt,
|
||||||
),
|
),
|
||||||
createCompanionCallback: ({
|
createCompanionCallback: ({
|
||||||
|
|
@ -7395,6 +7457,7 @@ class $$MessageRetransmissionsTableTableManager extends RootTableManager<
|
||||||
Value<int?> messageId = const Value.absent(),
|
Value<int?> messageId = const Value.absent(),
|
||||||
required Uint8List plaintextContent,
|
required Uint8List plaintextContent,
|
||||||
Value<Uint8List?> pushData = const Value.absent(),
|
Value<Uint8List?> pushData = const Value.absent(),
|
||||||
|
Value<bool> willNotGetACKByUser = const Value.absent(),
|
||||||
Value<DateTime?> acknowledgeByServerAt = const Value.absent(),
|
Value<DateTime?> acknowledgeByServerAt = const Value.absent(),
|
||||||
}) =>
|
}) =>
|
||||||
MessageRetransmissionsCompanion.insert(
|
MessageRetransmissionsCompanion.insert(
|
||||||
|
|
@ -7403,6 +7466,7 @@ class $$MessageRetransmissionsTableTableManager extends RootTableManager<
|
||||||
messageId: messageId,
|
messageId: messageId,
|
||||||
plaintextContent: plaintextContent,
|
plaintextContent: plaintextContent,
|
||||||
pushData: pushData,
|
pushData: pushData,
|
||||||
|
willNotGetACKByUser: willNotGetACKByUser,
|
||||||
acknowledgeByServerAt: acknowledgeByServerAt,
|
acknowledgeByServerAt: acknowledgeByServerAt,
|
||||||
),
|
),
|
||||||
withReferenceMapper: (p0) => p0
|
withReferenceMapper: (p0) => p0
|
||||||
|
|
|
||||||
|
|
@ -2465,6 +2465,263 @@ i1.GeneratedColumn<i2.Uint8List> _column_66(String aliasedName) =>
|
||||||
i1.GeneratedColumn<DateTime> _column_67(String aliasedName) =>
|
i1.GeneratedColumn<DateTime> _column_67(String aliasedName) =>
|
||||||
i1.GeneratedColumn<DateTime>('acknowledge_by_server_at', aliasedName, true,
|
i1.GeneratedColumn<DateTime>('acknowledge_by_server_at', aliasedName, true,
|
||||||
type: i1.DriftSqlType.dateTime);
|
type: i1.DriftSqlType.dateTime);
|
||||||
|
|
||||||
|
final class Schema12 extends i0.VersionedSchema {
|
||||||
|
Schema12({required super.database}) : super(version: 12);
|
||||||
|
@override
|
||||||
|
late final List<i1.DatabaseSchemaEntity> entities = [
|
||||||
|
contacts,
|
||||||
|
messages,
|
||||||
|
mediaUploads,
|
||||||
|
mediaDownloads,
|
||||||
|
signalIdentityKeyStores,
|
||||||
|
signalPreKeyStores,
|
||||||
|
signalSenderKeyStores,
|
||||||
|
signalSessionStores,
|
||||||
|
signalContactPreKeys,
|
||||||
|
signalContactSignedPreKeys,
|
||||||
|
messageRetransmissions,
|
||||||
|
];
|
||||||
|
late final Shape13 contacts = Shape13(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'contacts',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [
|
||||||
|
'PRIMARY KEY(user_id)',
|
||||||
|
],
|
||||||
|
columns: [
|
||||||
|
_column_0,
|
||||||
|
_column_1,
|
||||||
|
_column_2,
|
||||||
|
_column_3,
|
||||||
|
_column_4,
|
||||||
|
_column_5,
|
||||||
|
_column_6,
|
||||||
|
_column_7,
|
||||||
|
_column_8,
|
||||||
|
_column_9,
|
||||||
|
_column_39,
|
||||||
|
_column_53,
|
||||||
|
_column_57,
|
||||||
|
_column_54,
|
||||||
|
_column_40,
|
||||||
|
_column_10,
|
||||||
|
_column_11,
|
||||||
|
_column_12,
|
||||||
|
_column_13,
|
||||||
|
_column_14,
|
||||||
|
_column_55,
|
||||||
|
_column_15,
|
||||||
|
_column_16,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
late final Shape10 messages = Shape10(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'messages',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [],
|
||||||
|
columns: [
|
||||||
|
_column_17,
|
||||||
|
_column_18,
|
||||||
|
_column_19,
|
||||||
|
_column_48,
|
||||||
|
_column_49,
|
||||||
|
_column_20,
|
||||||
|
_column_21,
|
||||||
|
_column_22,
|
||||||
|
_column_52,
|
||||||
|
_column_23,
|
||||||
|
_column_24,
|
||||||
|
_column_25,
|
||||||
|
_column_26,
|
||||||
|
_column_27,
|
||||||
|
_column_28,
|
||||||
|
_column_29,
|
||||||
|
_column_30,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
late final Shape7 mediaUploads = Shape7(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'media_uploads',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [],
|
||||||
|
columns: [
|
||||||
|
_column_41,
|
||||||
|
_column_42,
|
||||||
|
_column_56,
|
||||||
|
_column_44,
|
||||||
|
_column_45,
|
||||||
|
_column_46,
|
||||||
|
_column_47,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
late final Shape9 mediaDownloads = Shape9(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'media_downloads',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [],
|
||||||
|
columns: [
|
||||||
|
_column_50,
|
||||||
|
_column_51,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
late final Shape2 signalIdentityKeyStores = Shape2(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'signal_identity_key_stores',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [
|
||||||
|
'PRIMARY KEY(device_id, name)',
|
||||||
|
],
|
||||||
|
columns: [
|
||||||
|
_column_31,
|
||||||
|
_column_32,
|
||||||
|
_column_33,
|
||||||
|
_column_10,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
late final Shape3 signalPreKeyStores = Shape3(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'signal_pre_key_stores',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [
|
||||||
|
'PRIMARY KEY(pre_key_id)',
|
||||||
|
],
|
||||||
|
columns: [
|
||||||
|
_column_34,
|
||||||
|
_column_35,
|
||||||
|
_column_10,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
late final Shape4 signalSenderKeyStores = Shape4(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'signal_sender_key_stores',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [
|
||||||
|
'PRIMARY KEY(sender_key_name)',
|
||||||
|
],
|
||||||
|
columns: [
|
||||||
|
_column_36,
|
||||||
|
_column_37,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
late final Shape5 signalSessionStores = Shape5(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'signal_session_stores',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [
|
||||||
|
'PRIMARY KEY(device_id, name)',
|
||||||
|
],
|
||||||
|
columns: [
|
||||||
|
_column_31,
|
||||||
|
_column_32,
|
||||||
|
_column_38,
|
||||||
|
_column_10,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
late final Shape14 signalContactPreKeys = Shape14(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'signal_contact_pre_keys',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [
|
||||||
|
'PRIMARY KEY(contact_id, pre_key_id)',
|
||||||
|
],
|
||||||
|
columns: [
|
||||||
|
_column_58,
|
||||||
|
_column_34,
|
||||||
|
_column_35,
|
||||||
|
_column_10,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
late final Shape15 signalContactSignedPreKeys = Shape15(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'signal_contact_signed_pre_keys',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [
|
||||||
|
'PRIMARY KEY(contact_id)',
|
||||||
|
],
|
||||||
|
columns: [
|
||||||
|
_column_58,
|
||||||
|
_column_59,
|
||||||
|
_column_60,
|
||||||
|
_column_61,
|
||||||
|
_column_10,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
late final Shape17 messageRetransmissions = Shape17(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'message_retransmissions',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [],
|
||||||
|
columns: [
|
||||||
|
_column_62,
|
||||||
|
_column_63,
|
||||||
|
_column_64,
|
||||||
|
_column_65,
|
||||||
|
_column_66,
|
||||||
|
_column_68,
|
||||||
|
_column_67,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Shape17 extends i0.VersionedTable {
|
||||||
|
Shape17({required super.source, required super.alias}) : super.aliased();
|
||||||
|
i1.GeneratedColumn<int> get retransmissionId =>
|
||||||
|
columnsByName['retransmission_id']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<int> get contactId =>
|
||||||
|
columnsByName['contact_id']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<int> get messageId =>
|
||||||
|
columnsByName['message_id']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<i2.Uint8List> get plaintextContent =>
|
||||||
|
columnsByName['plaintext_content']! as i1.GeneratedColumn<i2.Uint8List>;
|
||||||
|
i1.GeneratedColumn<i2.Uint8List> get pushData =>
|
||||||
|
columnsByName['push_data']! as i1.GeneratedColumn<i2.Uint8List>;
|
||||||
|
i1.GeneratedColumn<bool> get willNotGetACKByUser =>
|
||||||
|
columnsByName['will_not_get_a_c_k_by_user']! as i1.GeneratedColumn<bool>;
|
||||||
|
i1.GeneratedColumn<DateTime> get acknowledgeByServerAt =>
|
||||||
|
columnsByName['acknowledge_by_server_at']!
|
||||||
|
as i1.GeneratedColumn<DateTime>;
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.GeneratedColumn<bool> _column_68(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<bool>('will_not_get_a_c_k_by_user', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.bool,
|
||||||
|
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
||||||
|
'CHECK ("will_not_get_a_c_k_by_user" IN (0, 1))'),
|
||||||
|
defaultValue: const CustomExpression('0'));
|
||||||
i0.MigrationStepWithVersion migrationSteps({
|
i0.MigrationStepWithVersion migrationSteps({
|
||||||
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
|
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
|
||||||
required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3,
|
required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3,
|
||||||
|
|
@ -2476,6 +2733,7 @@ i0.MigrationStepWithVersion migrationSteps({
|
||||||
required Future<void> Function(i1.Migrator m, Schema9 schema) from8To9,
|
required Future<void> Function(i1.Migrator m, Schema9 schema) from8To9,
|
||||||
required Future<void> Function(i1.Migrator m, Schema10 schema) from9To10,
|
required Future<void> Function(i1.Migrator m, Schema10 schema) from9To10,
|
||||||
required Future<void> Function(i1.Migrator m, Schema11 schema) from10To11,
|
required Future<void> Function(i1.Migrator m, Schema11 schema) from10To11,
|
||||||
|
required Future<void> Function(i1.Migrator m, Schema12 schema) from11To12,
|
||||||
}) {
|
}) {
|
||||||
return (currentVersion, database) async {
|
return (currentVersion, database) async {
|
||||||
switch (currentVersion) {
|
switch (currentVersion) {
|
||||||
|
|
@ -2529,6 +2787,11 @@ i0.MigrationStepWithVersion migrationSteps({
|
||||||
final migrator = i1.Migrator(database, schema);
|
final migrator = i1.Migrator(database, schema);
|
||||||
await from10To11(migrator, schema);
|
await from10To11(migrator, schema);
|
||||||
return 11;
|
return 11;
|
||||||
|
case 11:
|
||||||
|
final schema = Schema12(database: database);
|
||||||
|
final migrator = i1.Migrator(database, schema);
|
||||||
|
await from11To12(migrator, schema);
|
||||||
|
return 12;
|
||||||
default:
|
default:
|
||||||
throw ArgumentError.value('Unknown migration from $currentVersion');
|
throw ArgumentError.value('Unknown migration from $currentVersion');
|
||||||
}
|
}
|
||||||
|
|
@ -2546,6 +2809,7 @@ i1.OnUpgrade stepByStep({
|
||||||
required Future<void> Function(i1.Migrator m, Schema9 schema) from8To9,
|
required Future<void> Function(i1.Migrator m, Schema9 schema) from8To9,
|
||||||
required Future<void> Function(i1.Migrator m, Schema10 schema) from9To10,
|
required Future<void> Function(i1.Migrator m, Schema10 schema) from9To10,
|
||||||
required Future<void> Function(i1.Migrator m, Schema11 schema) from10To11,
|
required Future<void> Function(i1.Migrator m, Schema11 schema) from10To11,
|
||||||
|
required Future<void> Function(i1.Migrator m, Schema12 schema) from11To12,
|
||||||
}) =>
|
}) =>
|
||||||
i0.VersionedSchema.stepByStepHelper(
|
i0.VersionedSchema.stepByStepHelper(
|
||||||
step: migrationSteps(
|
step: migrationSteps(
|
||||||
|
|
@ -2559,4 +2823,5 @@ i1.OnUpgrade stepByStep({
|
||||||
from8To9: from8To9,
|
from8To9: from8To9,
|
||||||
from9To10: from9To10,
|
from9To10: from9To10,
|
||||||
from10To11: from10To11,
|
from10To11: from10To11,
|
||||||
|
from11To12: from11To12,
|
||||||
));
|
));
|
||||||
|
|
|
||||||
|
|
@ -37,11 +37,13 @@ class MessageJson {
|
||||||
final MessageKind kind;
|
final MessageKind kind;
|
||||||
final MessageContent? content;
|
final MessageContent? content;
|
||||||
final int? messageId;
|
final int? messageId;
|
||||||
|
int? retransId;
|
||||||
DateTime timestamp;
|
DateTime timestamp;
|
||||||
|
|
||||||
MessageJson({
|
MessageJson({
|
||||||
required this.kind,
|
required this.kind,
|
||||||
this.messageId,
|
this.messageId,
|
||||||
|
this.retransId,
|
||||||
required this.content,
|
required this.content,
|
||||||
required this.timestamp,
|
required this.timestamp,
|
||||||
});
|
});
|
||||||
|
|
@ -57,6 +59,7 @@ class MessageJson {
|
||||||
return MessageJson(
|
return MessageJson(
|
||||||
kind: kind,
|
kind: kind,
|
||||||
messageId: (json['messageId'] as num?)?.toInt(),
|
messageId: (json['messageId'] as num?)?.toInt(),
|
||||||
|
retransId: (json['retransId'] as num?)?.toInt(),
|
||||||
content: MessageContent.fromJson(
|
content: MessageContent.fromJson(
|
||||||
kind, json['content'] as Map<String, dynamic>),
|
kind, json['content'] as Map<String, dynamic>),
|
||||||
timestamp: DateTime.fromMillisecondsSinceEpoch(json['timestamp']),
|
timestamp: DateTime.fromMillisecondsSinceEpoch(json['timestamp']),
|
||||||
|
|
@ -67,6 +70,7 @@ class MessageJson {
|
||||||
'kind': kind.name,
|
'kind': kind.name,
|
||||||
'content': content?.toJson(),
|
'content': content?.toJson(),
|
||||||
'messageId': messageId,
|
'messageId': messageId,
|
||||||
|
'retransId': retransId,
|
||||||
'timestamp': timestamp.toUtc().millisecondsSinceEpoch,
|
'timestamp': timestamp.toUtc().millisecondsSinceEpoch,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -90,6 +94,8 @@ class MessageContent {
|
||||||
return ReopenedMediaFileContent.fromJson(json);
|
return ReopenedMediaFileContent.fromJson(json);
|
||||||
case MessageKind.flameSync:
|
case MessageKind.flameSync:
|
||||||
return FlameSyncContent.fromJson(json);
|
return FlameSyncContent.fromJson(json);
|
||||||
|
case MessageKind.ack:
|
||||||
|
return AckContent.fromJson(json);
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -216,6 +222,27 @@ class ReopenedMediaFileContent extends MessageContent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class AckContent extends MessageContent {
|
||||||
|
int? messageIdToAck;
|
||||||
|
int retransIdToAck;
|
||||||
|
AckContent({required this.messageIdToAck, required this.retransIdToAck});
|
||||||
|
|
||||||
|
static AckContent fromJson(Map json) {
|
||||||
|
return AckContent(
|
||||||
|
messageIdToAck: json['messageIdToAck'],
|
||||||
|
retransIdToAck: json['retransIdToAck'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map toJson() {
|
||||||
|
return {
|
||||||
|
'messageIdToAck': messageIdToAck,
|
||||||
|
'retransIdToAck': retransIdToAck,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ProfileContent extends MessageContent {
|
class ProfileContent extends MessageContent {
|
||||||
String avatarSvg;
|
String avatarSvg;
|
||||||
String displayName;
|
String displayName;
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,14 @@ class UserData {
|
||||||
|
|
||||||
DateTime? signalLastSignedPreKeyUpdated;
|
DateTime? signalLastSignedPreKeyUpdated;
|
||||||
|
|
||||||
|
// -- Custom DATA --
|
||||||
|
|
||||||
|
@JsonKey(defaultValue: 100_000)
|
||||||
|
int currentPreKeyIndexStart = 100_000;
|
||||||
|
|
||||||
|
@JsonKey(defaultValue: 100_000)
|
||||||
|
int currentSignedPreKeyIndexStart = 100_000;
|
||||||
|
|
||||||
// --- BACKUP ---
|
// --- BACKUP ---
|
||||||
|
|
||||||
DateTime? nextTimeToShowBackupNotice;
|
DateTime? nextTimeToShowBackupNotice;
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,10 @@ UserData _$UserDataFromJson(Map<String, dynamic> json) => UserData(
|
||||||
json['signalLastSignedPreKeyUpdated'] == null
|
json['signalLastSignedPreKeyUpdated'] == null
|
||||||
? null
|
? null
|
||||||
: DateTime.parse(json['signalLastSignedPreKeyUpdated'] as String)
|
: DateTime.parse(json['signalLastSignedPreKeyUpdated'] as String)
|
||||||
|
..currentPreKeyIndexStart =
|
||||||
|
(json['currentPreKeyIndexStart'] as num?)?.toInt() ?? 100000
|
||||||
|
..currentSignedPreKeyIndexStart =
|
||||||
|
(json['currentSignedPreKeyIndexStart'] as num?)?.toInt() ?? 100000
|
||||||
..nextTimeToShowBackupNotice = json['nextTimeToShowBackupNotice'] == null
|
..nextTimeToShowBackupNotice = json['nextTimeToShowBackupNotice'] == null
|
||||||
? null
|
? null
|
||||||
: DateTime.parse(json['nextTimeToShowBackupNotice'] as String)
|
: DateTime.parse(json['nextTimeToShowBackupNotice'] as String)
|
||||||
|
|
@ -83,6 +87,8 @@ Map<String, dynamic> _$UserDataToJson(UserData instance) => <String, dynamic>{
|
||||||
'myBestFriendContactId': instance.myBestFriendContactId,
|
'myBestFriendContactId': instance.myBestFriendContactId,
|
||||||
'signalLastSignedPreKeyUpdated':
|
'signalLastSignedPreKeyUpdated':
|
||||||
instance.signalLastSignedPreKeyUpdated?.toIso8601String(),
|
instance.signalLastSignedPreKeyUpdated?.toIso8601String(),
|
||||||
|
'currentPreKeyIndexStart': instance.currentPreKeyIndexStart,
|
||||||
|
'currentSignedPreKeyIndexStart': instance.currentSignedPreKeyIndexStart,
|
||||||
'nextTimeToShowBackupNotice':
|
'nextTimeToShowBackupNotice':
|
||||||
instance.nextTimeToShowBackupNotice?.toIso8601String(),
|
instance.nextTimeToShowBackupNotice?.toIso8601String(),
|
||||||
'backupServer': instance.backupServer,
|
'backupServer': instance.backupServer,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:cryptography_plus/cryptography_plus.dart';
|
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/database/twonly_database.dart';
|
import 'package:twonly/src/database/twonly_database.dart';
|
||||||
|
|
@ -9,60 +8,23 @@ import 'package:twonly/src/database/tables/messages_table.dart';
|
||||||
import 'package:twonly/src/model/json/message.dart';
|
import 'package:twonly/src/model/json/message.dart';
|
||||||
import 'package:twonly/src/model/json/userdata.dart';
|
import 'package:twonly/src/model/json/userdata.dart';
|
||||||
import 'package:twonly/src/model/protobuf/api/websocket/error.pb.dart';
|
import 'package:twonly/src/model/protobuf/api/websocket/error.pb.dart';
|
||||||
import 'package:twonly/src/services/api/media_upload.dart';
|
|
||||||
import 'package:twonly/src/services/api/utils.dart';
|
import 'package:twonly/src/services/api/utils.dart';
|
||||||
import 'package:twonly/src/services/signal/encryption.signal.dart';
|
import 'package:twonly/src/services/signal/encryption.signal.dart';
|
||||||
import 'package:twonly/src/services/notification.service.dart';
|
import 'package:twonly/src/services/notification.service.dart';
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
import 'package:twonly/src/utils/storage.dart';
|
import 'package:twonly/src/utils/storage.dart';
|
||||||
|
|
||||||
class DirtyResendingItem {
|
|
||||||
DirtyResendingItem({required this.gotLastAck});
|
|
||||||
DateTime gotLastAck;
|
|
||||||
Timer? timer;
|
|
||||||
}
|
|
||||||
|
|
||||||
class DirtyResending {
|
|
||||||
static final Map<int, DirtyResendingItem> _gotLastAck = {};
|
|
||||||
|
|
||||||
static Future gotAckFromUser(int contactID) async {
|
|
||||||
_gotLastAck[contactID]?.timer?.cancel();
|
|
||||||
|
|
||||||
_gotLastAck[contactID] = DirtyResendingItem(gotLastAck: DateTime.now());
|
|
||||||
_gotLastAck[contactID]?.timer = Timer(Duration(seconds: 10), () async {
|
|
||||||
_gotLastAck.remove(contactID);
|
|
||||||
_handleNonACKMessagesForUser(contactID);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future _handleNonACKMessagesForUser(int contactID) async {
|
|
||||||
final List<Message> toResendMessages =
|
|
||||||
await twonlyDB.messagesDao.getAllNonACKMessagesFromUser();
|
|
||||||
|
|
||||||
for (final Message message in toResendMessages) {
|
|
||||||
Log.info("Got newer ACKs from user ${message.messageId}");
|
|
||||||
await twonlyDB.messagesDao.updateMessageByMessageId(
|
|
||||||
message.messageId,
|
|
||||||
MessagesCompanion(
|
|
||||||
errorWhileSending: Value(true),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future handleOlderNonAckMessages() async {}
|
|
||||||
|
|
||||||
Future tryTransmitMessages() async {
|
Future tryTransmitMessages() async {
|
||||||
final retransIds =
|
final retransIds =
|
||||||
await twonlyDB.messageRetransmissionDao.getRetransmitAbleMessages();
|
await twonlyDB.messageRetransmissionDao.getRetransmitAbleMessages();
|
||||||
|
|
||||||
if (retransIds.isEmpty) return;
|
|
||||||
|
|
||||||
Log.info("Retransmitting ${retransIds.length} text messages");
|
Log.info("Retransmitting ${retransIds.length} text messages");
|
||||||
|
|
||||||
|
if (retransIds.isEmpty) return;
|
||||||
|
|
||||||
for (final retransId in retransIds) {
|
for (final retransId in retransIds) {
|
||||||
sendRetransmitMessage(retransId);
|
sendRetransmitMessage(retransId);
|
||||||
|
//twonlyDB.messageRetransmissionDao.deleteRetransmissionById(retransId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -100,10 +62,6 @@ Future sendRetransmitMessage(int retransId) async {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var hash = uint8ListToHex(
|
|
||||||
Uint8List.fromList((await Sha256().hash(encryptedBytes)).bytes));
|
|
||||||
Log.info("Sending message: ${hash.substring(0, 10)}");
|
|
||||||
|
|
||||||
Result resp = await apiService.sendTextMessage(
|
Result resp = await apiService.sendTextMessage(
|
||||||
retrans.contactId,
|
retrans.contactId,
|
||||||
encryptedBytes,
|
encryptedBytes,
|
||||||
|
|
@ -142,13 +100,23 @@ Future sendRetransmitMessage(int retransId) async {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!retry) {
|
if (!retry) {
|
||||||
await twonlyDB.messageRetransmissionDao.deleteRetransmissionById(retransId);
|
if (retrans.willNotGetACKByUser) {
|
||||||
|
await twonlyDB.messageRetransmissionDao
|
||||||
|
.deleteRetransmissionById(retransId);
|
||||||
|
} else {
|
||||||
|
await twonlyDB.messageRetransmissionDao.updateRetransmission(
|
||||||
|
retransId,
|
||||||
|
MessageRetransmissionsCompanion(
|
||||||
|
acknowledgeByServerAt: Value(DateTime.now()),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// encrypts and stores the message and then sends it in the background
|
// encrypts and stores the message and then sends it in the background
|
||||||
Future encryptAndSendMessageAsync(int? messageId, int userId, MessageJson msg,
|
Future encryptAndSendMessageAsync(int? messageId, int userId, MessageJson msg,
|
||||||
{PushKind? pushKind}) async {
|
{PushKind? pushKind, bool willNotGetACKByUser = false}) async {
|
||||||
if (gIsDemoUser) {
|
if (gIsDemoUser) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -158,15 +126,13 @@ Future encryptAndSendMessageAsync(int? messageId, int userId, MessageJson msg,
|
||||||
pushData = await getPushData(userId, pushKind);
|
pushData = await getPushData(userId, pushKind);
|
||||||
}
|
}
|
||||||
|
|
||||||
Uint8List plaintextContent =
|
|
||||||
Uint8List.fromList(gzip.encode(utf8.encode(jsonEncode(msg.toJson()))));
|
|
||||||
|
|
||||||
int? retransId = await twonlyDB.messageRetransmissionDao.insertRetransmission(
|
int? retransId = await twonlyDB.messageRetransmissionDao.insertRetransmission(
|
||||||
MessageRetransmissionsCompanion(
|
MessageRetransmissionsCompanion(
|
||||||
contactId: Value(userId),
|
contactId: Value(userId),
|
||||||
messageId: Value(messageId),
|
messageId: Value(messageId),
|
||||||
plaintextContent: Value(plaintextContent),
|
plaintextContent: Value(Uint8List(0)),
|
||||||
pushData: Value(pushData),
|
pushData: Value(pushData),
|
||||||
|
willNotGetACKByUser: Value(willNotGetACKByUser),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -175,6 +141,16 @@ Future encryptAndSendMessageAsync(int? messageId, int userId, MessageJson msg,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
msg.retransId = retransId;
|
||||||
|
|
||||||
|
Uint8List plaintextContent =
|
||||||
|
Uint8List.fromList(gzip.encode(utf8.encode(jsonEncode(msg.toJson()))));
|
||||||
|
|
||||||
|
await twonlyDB.messageRetransmissionDao.updateRetransmission(
|
||||||
|
retransId,
|
||||||
|
MessageRetransmissionsCompanion(
|
||||||
|
plaintextContent: Value(plaintextContent)));
|
||||||
|
|
||||||
// this can now be done in the background...
|
// this can now be done in the background...
|
||||||
sendRetransmitMessage(retransId);
|
sendRetransmitMessage(retransId);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:cryptography_plus/cryptography_plus.dart';
|
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:fixnum/fixnum.dart';
|
import 'package:fixnum/fixnum.dart';
|
||||||
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
||||||
|
|
@ -14,13 +13,13 @@ import 'package:twonly/src/model/protobuf/api/websocket/client_to_server.pbserve
|
||||||
import 'package:twonly/src/model/protobuf/api/websocket/error.pb.dart';
|
import 'package:twonly/src/model/protobuf/api/websocket/error.pb.dart';
|
||||||
import 'package:twonly/src/model/protobuf/api/websocket/server_to_client.pb.dart'
|
import 'package:twonly/src/model/protobuf/api/websocket/server_to_client.pb.dart'
|
||||||
as server;
|
as server;
|
||||||
import 'package:twonly/src/services/api/media_upload.dart';
|
|
||||||
import 'package:twonly/src/services/api/messages.dart';
|
import 'package:twonly/src/services/api/messages.dart';
|
||||||
import 'package:twonly/src/services/api/utils.dart';
|
import 'package:twonly/src/services/api/utils.dart';
|
||||||
import 'package:twonly/src/services/api/media_download.dart';
|
import 'package:twonly/src/services/api/media_download.dart';
|
||||||
import 'package:twonly/src/services/notification.service.dart';
|
import 'package:twonly/src/services/notification.service.dart';
|
||||||
import 'package:twonly/src/services/signal/encryption.signal.dart';
|
import 'package:twonly/src/services/signal/encryption.signal.dart';
|
||||||
import 'package:twonly/src/services/signal/identity.signal.dart';
|
import 'package:twonly/src/services/signal/identity.signal.dart';
|
||||||
|
import 'package:twonly/src/services/signal/prekeys.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';
|
||||||
|
|
||||||
|
|
@ -36,9 +35,6 @@ Future handleServerMessage(server.ServerToClient msg) async {
|
||||||
} else if (msg.v0.hasNewMessage()) {
|
} else if (msg.v0.hasNewMessage()) {
|
||||||
Uint8List body = Uint8List.fromList(msg.v0.newMessage.body);
|
Uint8List body = Uint8List.fromList(msg.v0.newMessage.body);
|
||||||
int fromUserId = msg.v0.newMessage.fromUserId.toInt();
|
int fromUserId = msg.v0.newMessage.fromUserId.toInt();
|
||||||
var hash = uint8ListToHex(Uint8List.fromList(
|
|
||||||
(await Sha256().hash(msg.v0.newMessage.body)).bytes));
|
|
||||||
Log.info("Got new message from server: ${hash.substring(0, 10)}");
|
|
||||||
response = await handleNewMessage(fromUserId, body);
|
response = await handleNewMessage(fromUserId, body);
|
||||||
} else {
|
} else {
|
||||||
Log.error("Got a new message from the server: $msg");
|
Log.error("Got a new message from the server: $msg");
|
||||||
|
|
@ -56,9 +52,24 @@ Future handleServerMessage(server.ServerToClient msg) async {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DateTime lastSignalDecryptMessage = DateTime.now().subtract(Duration(hours: 1));
|
||||||
|
DateTime lastPushKeyRequest = DateTime.now().subtract(Duration(hours: 1));
|
||||||
|
|
||||||
Future<client.Response> handleNewMessage(int fromUserId, Uint8List body) async {
|
Future<client.Response> handleNewMessage(int fromUserId, Uint8List body) async {
|
||||||
MessageJson? message = await signalDecryptMessage(fromUserId, body);
|
MessageJson? message = await signalDecryptMessage(fromUserId, body);
|
||||||
if (message == null) {
|
if (message == null) {
|
||||||
|
await encryptAndSendMessageAsync(
|
||||||
|
null,
|
||||||
|
fromUserId,
|
||||||
|
MessageJson(
|
||||||
|
kind: MessageKind.signalDecryptError,
|
||||||
|
content: MessageContent(),
|
||||||
|
timestamp: DateTime.now(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Log.error("Could not decrypt others message!");
|
||||||
|
|
||||||
// Message is not valid, so server can delete it
|
// Message is not valid, so server can delete it
|
||||||
var ok = client.Response_Ok()..none = true;
|
var ok = client.Response_Ok()..none = true;
|
||||||
return client.Response()..ok = ok;
|
return client.Response()..ok = ok;
|
||||||
|
|
@ -66,7 +77,58 @@ Future<client.Response> handleNewMessage(int fromUserId, Uint8List body) async {
|
||||||
|
|
||||||
Log.info("Got: ${message.kind}");
|
Log.info("Got: ${message.kind}");
|
||||||
|
|
||||||
|
if (message.kind != MessageKind.ack && message.retransId != null) {
|
||||||
|
Log.info("Sending ACK for ${message.kind}");
|
||||||
|
|
||||||
|
/// ACK every message
|
||||||
|
await encryptAndSendMessageAsync(
|
||||||
|
null,
|
||||||
|
fromUserId,
|
||||||
|
MessageJson(
|
||||||
|
kind: MessageKind.ack,
|
||||||
|
messageId: null,
|
||||||
|
content: AckContent(
|
||||||
|
messageIdToAck: message.messageId,
|
||||||
|
retransIdToAck: message.retransId!),
|
||||||
|
timestamp: DateTime.now(),
|
||||||
|
),
|
||||||
|
willNotGetACKByUser: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
switch (message.kind) {
|
switch (message.kind) {
|
||||||
|
case MessageKind.ack:
|
||||||
|
final content = message.content;
|
||||||
|
if (content is AckContent) {
|
||||||
|
if (content.messageIdToAck != null) {
|
||||||
|
final update = MessagesCompanion(
|
||||||
|
acknowledgeByUser: Value(true),
|
||||||
|
errorWhileSending: Value(false),
|
||||||
|
);
|
||||||
|
await twonlyDB.messagesDao.updateMessageByOtherUser(
|
||||||
|
fromUserId,
|
||||||
|
content.messageIdToAck!,
|
||||||
|
update,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await twonlyDB.messageRetransmissionDao
|
||||||
|
.deleteRetransmissionById(content.retransIdToAck);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MessageKind.signalDecryptError:
|
||||||
|
if (lastSignalDecryptMessage
|
||||||
|
.isBefore(DateTime.now().subtract(Duration(seconds: 60)))) {
|
||||||
|
Log.error(
|
||||||
|
"Got signal decrypt error from other user! Sending all non ACK messages again.");
|
||||||
|
lastSignalDecryptMessage = DateTime.now();
|
||||||
|
await twonlyDB.signalDao.deleteAllPreKeysByContactId(fromUserId);
|
||||||
|
await requestNewPrekeysForContact(fromUserId);
|
||||||
|
await twonlyDB.messageRetransmissionDao.resetAckStatusForAllMessages();
|
||||||
|
tryTransmitMessages();
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
case MessageKind.contactRequest:
|
case MessageKind.contactRequest:
|
||||||
return handleContactRequest(fromUserId, message);
|
return handleContactRequest(fromUserId, message);
|
||||||
|
|
||||||
|
|
@ -148,21 +210,12 @@ Future<client.Response> handleNewMessage(int fromUserId, Uint8List body) async {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MessageKind.ack:
|
case MessageKind.requestPushKey:
|
||||||
final update = MessagesCompanion(
|
if (lastPushKeyRequest
|
||||||
acknowledgeByUser: Value(true),
|
.isBefore(DateTime.now().subtract(Duration(seconds: 60)))) {
|
||||||
errorWhileSending: Value(false),
|
lastPushKeyRequest = DateTime.now();
|
||||||
);
|
setupNotificationWithUsers(force: true);
|
||||||
await twonlyDB.messagesDao.updateMessageByOtherUser(
|
}
|
||||||
fromUserId,
|
|
||||||
message.messageId!,
|
|
||||||
update,
|
|
||||||
);
|
|
||||||
|
|
||||||
// search for older messages, that where not yet ack by the other party
|
|
||||||
DirtyResending.gotAckFromUser(fromUserId);
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MessageKind.pushKey:
|
case MessageKind.pushKey:
|
||||||
if (message.content != null) {
|
if (message.content != null) {
|
||||||
|
|
@ -277,17 +330,6 @@ Future<client.Response> handleNewMessage(int fromUserId, Uint8List body) async {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await encryptAndSendMessageAsync(
|
|
||||||
null,
|
|
||||||
fromUserId,
|
|
||||||
MessageJson(
|
|
||||||
kind: MessageKind.ack,
|
|
||||||
messageId: message.messageId!,
|
|
||||||
content: MessageContent(),
|
|
||||||
timestamp: DateTime.now(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// unarchive contact when receiving a new message
|
// unarchive contact when receiving a new message
|
||||||
await twonlyDB.contactsDao.updateContact(
|
await twonlyDB.contactsDao.updateContact(
|
||||||
fromUserId,
|
fromUserId,
|
||||||
|
|
|
||||||
|
|
@ -254,6 +254,15 @@ Future<Uint8List?> getPushData(int toUserId, PushKind kind) async {
|
||||||
// this will be enforced after every app uses this system... :/
|
// this will be enforced after every app uses this system... :/
|
||||||
// return null;
|
// return null;
|
||||||
Log.error("Using insecure key as the receiver does not send a push key!");
|
Log.error("Using insecure key as the receiver does not send a push key!");
|
||||||
|
await encryptAndSendMessageAsync(
|
||||||
|
null,
|
||||||
|
toUserId,
|
||||||
|
my.MessageJson(
|
||||||
|
kind: MessageKind.requestPushKey,
|
||||||
|
content: my.MessageContent(),
|
||||||
|
timestamp: DateTime.now(),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/constants/secure_storage_keys.dart';
|
import 'package:twonly/src/constants/secure_storage_keys.dart';
|
||||||
import 'package:twonly/src/database/signal/connect_pre_key_store.dart';
|
|
||||||
import 'package:twonly/src/model/json/signal_identity.dart';
|
import 'package:twonly/src/model/json/signal_identity.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/userdata.dart';
|
import 'package:twonly/src/model/json/userdata.dart';
|
||||||
|
|
@ -59,9 +58,14 @@ Future signalHandleNewServerConnection() async {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<PreKeyRecord>> signalGetPreKeys() async {
|
Future<List<PreKeyRecord>> signalGetPreKeys() async {
|
||||||
int? start = await ConnectPreKeyStore.getNextPreKeyId();
|
final user = await getUser();
|
||||||
if (start == null) return [];
|
if (user == null) return [];
|
||||||
print(start);
|
|
||||||
|
int start = user.currentPreKeyIndexStart;
|
||||||
|
await updateUserdata((user) {
|
||||||
|
user.currentPreKeyIndexStart += 200;
|
||||||
|
return user;
|
||||||
|
});
|
||||||
final preKeys = generatePreKeys(start, 200);
|
final preKeys = generatePreKeys(start, 200);
|
||||||
final signalStore = await getSignalStore();
|
final signalStore = await getSignalStore();
|
||||||
if (signalStore == null) return [];
|
if (signalStore == null) return [];
|
||||||
|
|
@ -123,11 +127,17 @@ Future createIfNotExistsSignalIdentity() async {
|
||||||
|
|
||||||
Future<SignedPreKeyRecord?> _getNewSignalSignedPreKey() async {
|
Future<SignedPreKeyRecord?> _getNewSignalSignedPreKey() async {
|
||||||
var identityKeyPair = await getSignalIdentityKeyPair();
|
var identityKeyPair = await getSignalIdentityKeyPair();
|
||||||
if (identityKeyPair == null) return null;
|
final user = await getUser();
|
||||||
final signalStore = await getSignalStore();
|
final signalStore = await getSignalStore();
|
||||||
if (signalStore == null) return null;
|
if (identityKeyPair == null || signalStore == null || user == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
int signedPreKeyId = await signalStore.signedPreKeyStore.getNextKeyId();
|
int signedPreKeyId = user.currentSignedPreKeyIndexStart;
|
||||||
|
await updateUserdata((user) {
|
||||||
|
user.currentSignedPreKeyIndexStart += 1;
|
||||||
|
return user;
|
||||||
|
});
|
||||||
|
|
||||||
final SignedPreKeyRecord signedPreKey = generateSignedPreKey(
|
final SignedPreKeyRecord signedPreKey = generateSignedPreKey(
|
||||||
identityKeyPair,
|
identityKeyPair,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
import 'package:mutex/mutex.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/database/twonly_database.dart';
|
import 'package:twonly/src/database/twonly_database.dart';
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
|
|
@ -18,10 +19,22 @@ class OtherPreKeys {
|
||||||
final List<int> signedPreKeySignature;
|
final List<int> signedPreKeySignature;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Mutex requestNewKeys = Mutex();
|
||||||
|
DateTime lastPreKeyRequest = DateTime.now().subtract(Duration(hours: 1));
|
||||||
|
DateTime lastSignedPreKeyRequest = DateTime.now().subtract(Duration(hours: 1));
|
||||||
|
|
||||||
Future requestNewPrekeysForContact(int contactId) async {
|
Future requestNewPrekeysForContact(int contactId) async {
|
||||||
|
if (lastPreKeyRequest
|
||||||
|
.isAfter(DateTime.now().subtract(Duration(seconds: 60)))) {
|
||||||
|
Log.info("last pre request was 60s before");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
lastPreKeyRequest = DateTime.now();
|
||||||
|
requestNewKeys.protect(() async {
|
||||||
final otherKeys = await apiService.getPreKeysByUserId(contactId);
|
final otherKeys = await apiService.getPreKeysByUserId(contactId);
|
||||||
if (otherKeys != null) {
|
if (otherKeys != null) {
|
||||||
Log.info("got fresh pre keys from other $contactId!");
|
Log.info(
|
||||||
|
"got fresh ${otherKeys.preKeys.length} pre keys from other $contactId!");
|
||||||
final preKeys = otherKeys.preKeys
|
final preKeys = otherKeys.preKeys
|
||||||
.map(
|
.map(
|
||||||
(preKey) => SignalContactPreKeysCompanion(
|
(preKey) => SignalContactPreKeysCompanion(
|
||||||
|
|
@ -35,20 +48,26 @@ Future requestNewPrekeysForContact(int contactId) async {
|
||||||
} else {
|
} else {
|
||||||
Log.error("could not load new pre keys for user $contactId");
|
Log.error("could not load new pre keys for user $contactId");
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<SignalContactPreKey?> getPreKeyByContactId(int contactId) async {
|
Future<SignalContactPreKey?> getPreKeyByContactId(int contactId) async {
|
||||||
int count = await twonlyDB.signalDao.countPreKeysByContactId(contactId);
|
int count = await twonlyDB.signalDao.countPreKeysByContactId(contactId);
|
||||||
if (count < 10) {
|
if (count < 10) {
|
||||||
Log.info(
|
Log.info("Requesting new prekeys: $count < 10");
|
||||||
"There are $count < 10 prekeys for $contactId. Loading fresh once from the server.",
|
|
||||||
);
|
|
||||||
requestNewPrekeysForContact(contactId);
|
requestNewPrekeysForContact(contactId);
|
||||||
}
|
}
|
||||||
return twonlyDB.signalDao.popPreKeyByContactId(contactId);
|
return twonlyDB.signalDao.popPreKeyByContactId(contactId);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future requestNewSignedPreKeyForContact(int contactId) async {
|
Future requestNewSignedPreKeyForContact(int contactId) async {
|
||||||
|
if (lastSignedPreKeyRequest
|
||||||
|
.isAfter(DateTime.now().subtract(Duration(seconds: 60)))) {
|
||||||
|
Log.info("last signed pre request was 60s before");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
lastSignedPreKeyRequest = DateTime.now();
|
||||||
|
await requestNewKeys.protect(() async {
|
||||||
final signedPreKey = await apiService.getSignedKeyByUserId(contactId);
|
final signedPreKey = await apiService.getSignedKeyByUserId(contactId);
|
||||||
if (signedPreKey != null) {
|
if (signedPreKey != null) {
|
||||||
Log.info("got fresh signed pre keys from other $contactId!");
|
Log.info("got fresh signed pre keys from other $contactId!");
|
||||||
|
|
@ -63,6 +82,7 @@ Future requestNewSignedPreKeyForContact(int contactId) async {
|
||||||
} else {
|
} else {
|
||||||
Log.error("could not load new signed pre key for user $contactId");
|
Log.error("could not load new signed pre key for user $contactId");
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<SignalContactSignedPreKey?> getSignedPreKeyByContactId(
|
Future<SignalContactSignedPreKey?> getSignedPreKeyByContactId(
|
||||||
|
|
|
||||||
|
|
@ -1,212 +0,0 @@
|
||||||
import 'dart:convert';
|
|
||||||
import 'dart:io';
|
|
||||||
import 'package:background_downloader/background_downloader.dart';
|
|
||||||
import 'package:cryptography_plus/cryptography_plus.dart';
|
|
||||||
import 'package:drift/drift.dart';
|
|
||||||
import 'package:drift_flutter/drift_flutter.dart';
|
|
||||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
|
||||||
import 'package:path/path.dart';
|
|
||||||
import 'package:path_provider/path_provider.dart';
|
|
||||||
import 'package:twonly/src/constants/secure_storage_keys.dart';
|
|
||||||
import 'package:twonly/src/database/twonly_database.dart';
|
|
||||||
import 'package:twonly/src/model/json/userdata.dart';
|
|
||||||
import 'package:twonly/src/model/protobuf/backup/backup.pb.dart';
|
|
||||||
import 'package:twonly/src/services/api/media_upload.dart';
|
|
||||||
import 'package:twonly/src/services/twonly_safe/common.twonly_safe.dart';
|
|
||||||
import 'package:twonly/src/utils/log.dart';
|
|
||||||
import 'package:twonly/src/utils/storage.dart';
|
|
||||||
import 'package:twonly/src/views/settings/backup/backup.view.dart';
|
|
||||||
|
|
||||||
Future performTwonlySafeBackup({bool force = false}) async {
|
|
||||||
final user = await getUser();
|
|
||||||
|
|
||||||
if (user == null || user.twonlySafeBackup == null || user.isDemoUser) {
|
|
||||||
Log.warn("perform twonly safe backup was called while it is disabled");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user.twonlySafeBackup!.backupUploadState ==
|
|
||||||
LastBackupUploadState.pending) {
|
|
||||||
Log.warn("Backup upload is already pending.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DateTime? lastUpdateTime = user.twonlySafeBackup!.lastBackupDone;
|
|
||||||
if (!force && lastUpdateTime != null) {
|
|
||||||
if (lastUpdateTime.isAfter(DateTime.now().subtract(Duration(days: 1)))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.info("Starting new twonly Safe-Backup.");
|
|
||||||
|
|
||||||
final baseDir = (await getApplicationSupportDirectory()).path;
|
|
||||||
|
|
||||||
final backupDir = Directory(join(baseDir, "backup_twonly_safe/"));
|
|
||||||
await backupDir.create(recursive: true);
|
|
||||||
|
|
||||||
final backupDatabaseFile =
|
|
||||||
File(join(backupDir.path, "twonly_database.backup.sqlite"));
|
|
||||||
|
|
||||||
// copy database
|
|
||||||
final originalDatabase = File(join(baseDir, "twonly_database.sqlite"));
|
|
||||||
await originalDatabase.copy(backupDatabaseFile.path);
|
|
||||||
|
|
||||||
driftRuntimeOptions.dontWarnAboutMultipleDatabases = true;
|
|
||||||
final backupDB = TwonlyDatabase(
|
|
||||||
driftDatabase(
|
|
||||||
name: "twonly_database.backup",
|
|
||||||
native: DriftNativeOptions(
|
|
||||||
databaseDirectory: () async {
|
|
||||||
return backupDir;
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
await backupDB.deleteDataForTwonlySafe();
|
|
||||||
|
|
||||||
var secureStorageBackup = {};
|
|
||||||
final storage = FlutterSecureStorage();
|
|
||||||
secureStorageBackup[SecureStorageKeys.signalIdentity] =
|
|
||||||
await storage.read(key: SecureStorageKeys.signalIdentity);
|
|
||||||
secureStorageBackup[SecureStorageKeys.signalSignedPreKey] =
|
|
||||||
await storage.read(key: SecureStorageKeys.signalSignedPreKey);
|
|
||||||
|
|
||||||
var userBackup = await getUser();
|
|
||||||
if (userBackup == null) return;
|
|
||||||
// FILTER settings which should not be in the backup
|
|
||||||
userBackup.twonlySafeBackup = null;
|
|
||||||
userBackup.lastImageSend = null;
|
|
||||||
userBackup.todaysImageCounter = null;
|
|
||||||
userBackup.lastPlanBallance = "";
|
|
||||||
userBackup.additionalUserInvites = "";
|
|
||||||
userBackup.signalLastSignedPreKeyUpdated = null;
|
|
||||||
|
|
||||||
secureStorageBackup[SecureStorageKeys.userData] = jsonEncode(userBackup);
|
|
||||||
|
|
||||||
// Compress and convert backup data
|
|
||||||
|
|
||||||
final twonlyDatabaseBytes = await backupDatabaseFile.readAsBytes();
|
|
||||||
await backupDatabaseFile.delete();
|
|
||||||
|
|
||||||
final backupProto = TwonlySafeBackupContent(
|
|
||||||
secureStorageJson: jsonEncode(secureStorageBackup),
|
|
||||||
twonlyDatabase: twonlyDatabaseBytes,
|
|
||||||
);
|
|
||||||
|
|
||||||
final backupBytes = gzip.encode(backupProto.writeToBuffer());
|
|
||||||
|
|
||||||
final backupHash = uint8ListToHex((await Sha256().hash(backupBytes)).bytes);
|
|
||||||
|
|
||||||
if (user.twonlySafeBackup!.lastBackupDone == null ||
|
|
||||||
user.twonlySafeBackup!.lastBackupDone!
|
|
||||||
.isAfter(DateTime.now().subtract(Duration(days: 90)))) {
|
|
||||||
force = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
final lastHash =
|
|
||||||
await storage.read(key: SecureStorageKeys.twonlySafeLastBackupHash);
|
|
||||||
|
|
||||||
if (lastHash != null && !force) {
|
|
||||||
if (backupHash == lastHash) {
|
|
||||||
Log.info("Since last backup nothing has changed.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await storage.write(
|
|
||||||
key: SecureStorageKeys.twonlySafeLastBackupHash,
|
|
||||||
value: backupHash,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Encrypt backup data
|
|
||||||
|
|
||||||
final xchacha20 = Xchacha20.poly1305Aead();
|
|
||||||
final nonce = xchacha20.newNonce();
|
|
||||||
|
|
||||||
final secretBox = await xchacha20.encrypt(
|
|
||||||
backupBytes,
|
|
||||||
secretKey: SecretKey(user.twonlySafeBackup!.encryptionKey),
|
|
||||||
nonce: nonce,
|
|
||||||
);
|
|
||||||
|
|
||||||
final encryptedBackupBytes = (TwonlySafeBackupEncrypted(
|
|
||||||
mac: secretBox.mac.bytes,
|
|
||||||
nonce: nonce,
|
|
||||||
cipherText: secretBox.cipherText,
|
|
||||||
)).writeToBuffer();
|
|
||||||
|
|
||||||
Log.info("Backup files created.");
|
|
||||||
|
|
||||||
var encryptedBackupBytesFile =
|
|
||||||
File(join(backupDir.path, "twonly_safe.backup"));
|
|
||||||
|
|
||||||
await encryptedBackupBytesFile.writeAsBytes(encryptedBackupBytes);
|
|
||||||
|
|
||||||
Log.info(
|
|
||||||
"Create twonly Safe backup with a size of ${encryptedBackupBytes.length} bytes.");
|
|
||||||
|
|
||||||
if (user.backupServer != null) {
|
|
||||||
if (encryptedBackupBytes.length > user.backupServer!.maxBackupBytes) {
|
|
||||||
Log.error("Backup is to big for the alternative backup server.");
|
|
||||||
await updateUserdata((user) {
|
|
||||||
user.twonlySafeBackup!.backupUploadState = LastBackupUploadState.failed;
|
|
||||||
return user;
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final task = UploadTask.fromFile(
|
|
||||||
taskId: "backup",
|
|
||||||
file: encryptedBackupBytesFile,
|
|
||||||
httpRequestMethod: "PUT",
|
|
||||||
url: (await getTwonlySafeBackupUrl())!,
|
|
||||||
requiresWiFi: true,
|
|
||||||
priority: 5,
|
|
||||||
retries: 2,
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/octet-stream",
|
|
||||||
},
|
|
||||||
);
|
|
||||||
if (await FileDownloader().enqueue(task)) {
|
|
||||||
Log.info("Starting upload from twonly Safe backup.");
|
|
||||||
await updateUserdata((user) {
|
|
||||||
user.twonlySafeBackup!.backupUploadState = LastBackupUploadState.pending;
|
|
||||||
user.twonlySafeBackup!.lastBackupDone = DateTime.now();
|
|
||||||
user.twonlySafeBackup!.lastBackupSize = encryptedBackupBytes.length;
|
|
||||||
return user;
|
|
||||||
});
|
|
||||||
gUpdateBackupView();
|
|
||||||
} else {
|
|
||||||
Log.error("Error starting UploadTask for twonly Safe.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future handleBackupStatusUpdate(TaskStatusUpdate update) async {
|
|
||||||
if (update.status == TaskStatus.failed ||
|
|
||||||
update.status == TaskStatus.canceled) {
|
|
||||||
Log.error(
|
|
||||||
"twonly Safe upload failed. ${update.responseStatusCode} ${update.responseBody} ${update.responseHeaders} ${update.exception}");
|
|
||||||
await updateUserdata((user) {
|
|
||||||
if (user.twonlySafeBackup != null) {
|
|
||||||
user.twonlySafeBackup!.backupUploadState = LastBackupUploadState.failed;
|
|
||||||
}
|
|
||||||
return user;
|
|
||||||
});
|
|
||||||
} else if (update.status == TaskStatus.complete) {
|
|
||||||
Log.error(
|
|
||||||
"twonly Safe uploaded with status code ${update.responseStatusCode}");
|
|
||||||
await updateUserdata((user) {
|
|
||||||
if (user.twonlySafeBackup != null) {
|
|
||||||
user.twonlySafeBackup!.backupUploadState =
|
|
||||||
LastBackupUploadState.success;
|
|
||||||
}
|
|
||||||
return user;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
Log.info("Backup is in state: ${update.status}");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
gUpdateBackupView();
|
|
||||||
}
|
|
||||||
|
|
@ -21,7 +21,7 @@ Future performTwonlySafeBackup({bool force = false}) async {
|
||||||
final user = await getUser();
|
final user = await getUser();
|
||||||
|
|
||||||
if (user == null || user.twonlySafeBackup == null || user.isDemoUser) {
|
if (user == null || user.twonlySafeBackup == null || user.isDemoUser) {
|
||||||
// Log.warn("perform twonly safe backup was called while it is disabled");
|
Log.warn("perform twonly safe backup was called while it is disabled");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -38,7 +38,7 @@ Future performTwonlySafeBackup({bool force = false}) async {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.info("Starting new twonly Safe-Backup.");
|
Log.info("Starting new twonly Safe-Backup!");
|
||||||
|
|
||||||
final baseDir = (await getApplicationSupportDirectory()).path;
|
final baseDir = (await getApplicationSupportDirectory()).path;
|
||||||
|
|
||||||
|
|
@ -48,6 +48,9 @@ Future performTwonlySafeBackup({bool force = false}) async {
|
||||||
final backupDatabaseFile =
|
final backupDatabaseFile =
|
||||||
File(join(backupDir.path, "twonly_database.backup.sqlite"));
|
File(join(backupDir.path, "twonly_database.backup.sqlite"));
|
||||||
|
|
||||||
|
final backupDatabaseFileCleaned =
|
||||||
|
File(join(backupDir.path, "twonly_database.backup.cleaned.sqlite"));
|
||||||
|
|
||||||
// copy database
|
// copy database
|
||||||
final originalDatabase = File(join(baseDir, "twonly_database.sqlite"));
|
final originalDatabase = File(join(baseDir, "twonly_database.sqlite"));
|
||||||
await originalDatabase.copy(backupDatabaseFile.path);
|
await originalDatabase.copy(backupDatabaseFile.path);
|
||||||
|
|
@ -66,6 +69,10 @@ Future performTwonlySafeBackup({bool force = false}) async {
|
||||||
|
|
||||||
await backupDB.deleteDataForTwonlySafe();
|
await backupDB.deleteDataForTwonlySafe();
|
||||||
|
|
||||||
|
await backupDB
|
||||||
|
.customStatement('VACUUM INTO ?', [backupDatabaseFileCleaned.path]);
|
||||||
|
backupDB.close();
|
||||||
|
|
||||||
var secureStorageBackup = {};
|
var secureStorageBackup = {};
|
||||||
final storage = FlutterSecureStorage();
|
final storage = FlutterSecureStorage();
|
||||||
secureStorageBackup[SecureStorageKeys.signalIdentity] =
|
secureStorageBackup[SecureStorageKeys.signalIdentity] =
|
||||||
|
|
@ -87,8 +94,11 @@ Future performTwonlySafeBackup({bool force = false}) async {
|
||||||
|
|
||||||
// Compress and convert backup data
|
// Compress and convert backup data
|
||||||
|
|
||||||
final twonlyDatabaseBytes = await backupDatabaseFile.readAsBytes();
|
final twonlyDatabaseBytes = await backupDatabaseFileCleaned.readAsBytes();
|
||||||
await backupDatabaseFile.delete();
|
await backupDatabaseFile.delete();
|
||||||
|
await backupDatabaseFileCleaned.delete();
|
||||||
|
|
||||||
|
print("twonlyDatabaseBytes = ${twonlyDatabaseBytes.lengthInBytes}");
|
||||||
|
|
||||||
final backupProto = TwonlySafeBackupContent(
|
final backupProto = TwonlySafeBackupContent(
|
||||||
secureStorageJson: jsonEncode(secureStorageBackup),
|
secureStorageJson: jsonEncode(secureStorageBackup),
|
||||||
|
|
@ -163,8 +173,8 @@ Future performTwonlySafeBackup({bool force = false}) async {
|
||||||
httpRequestMethod: "PUT",
|
httpRequestMethod: "PUT",
|
||||||
url: (await getTwonlySafeBackupUrl())!,
|
url: (await getTwonlySafeBackupUrl())!,
|
||||||
requiresWiFi: true,
|
requiresWiFi: true,
|
||||||
post: 'binary',
|
|
||||||
priority: 5,
|
priority: 5,
|
||||||
|
post: 'binary',
|
||||||
retries: 2,
|
retries: 2,
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/octet-stream",
|
"Content-Type": "application/octet-stream",
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ import 'package:http/http.dart' as http;
|
||||||
import 'package:path/path.dart';
|
import 'package:path/path.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:twonly/src/constants/secure_storage_keys.dart';
|
import 'package:twonly/src/constants/secure_storage_keys.dart';
|
||||||
|
import 'package:twonly/src/database/tables/messages_table.dart';
|
||||||
|
import 'package:twonly/src/database/twonly_database.dart';
|
||||||
import 'package:twonly/src/model/json/userdata.dart';
|
import 'package:twonly/src/model/json/userdata.dart';
|
||||||
import 'package:twonly/src/model/protobuf/backup/backup.pb.dart';
|
import 'package:twonly/src/model/protobuf/backup/backup.pb.dart';
|
||||||
import 'package:twonly/src/services/twonly_safe/common.twonly_safe.dart';
|
import 'package:twonly/src/services/twonly_safe/common.twonly_safe.dart';
|
||||||
|
|
@ -85,6 +87,40 @@ Future handleBackupData(
|
||||||
final originalDatabase = File(join(baseDir, "twonly_database.sqlite"));
|
final originalDatabase = File(join(baseDir, "twonly_database.sqlite"));
|
||||||
await originalDatabase.writeAsBytes(backupContent.twonlyDatabase);
|
await originalDatabase.writeAsBytes(backupContent.twonlyDatabase);
|
||||||
|
|
||||||
|
/// When restoring the last message ID must be increased otherwise
|
||||||
|
/// receivers would mark them as duplicates as they where already
|
||||||
|
/// send.
|
||||||
|
final database = TwonlyDatabase();
|
||||||
|
var lastMessageSend = 0;
|
||||||
|
int? randomUserId;
|
||||||
|
|
||||||
|
final contacts = await database.contactsDao.getAllNotBlockedContacts();
|
||||||
|
for (final contact in contacts) {
|
||||||
|
randomUserId = contact.userId;
|
||||||
|
final days = DateTime.now().difference(contact.lastMessageExchange).inDays;
|
||||||
|
if (days < lastMessageSend) {
|
||||||
|
lastMessageSend = days;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (randomUserId != null) {
|
||||||
|
// for each day add 400 message ids
|
||||||
|
var dummyMessagesCounter = (lastMessageSend + 1) * 400;
|
||||||
|
Log.info(
|
||||||
|
"Creating $dummyMessagesCounter dummy messages to increase message counter as last message was $lastMessageSend days ago.");
|
||||||
|
for (var i = 0; i < dummyMessagesCounter; i++) {
|
||||||
|
await database.messagesDao.insertMessage(
|
||||||
|
MessagesCompanion(
|
||||||
|
contactId: Value(randomUserId),
|
||||||
|
kind: Value(MessageKind.ack),
|
||||||
|
acknowledgeByServer: Value(true),
|
||||||
|
errorWhileSending: Value(true),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await database.messagesDao.deleteAllMessagesByContactId(randomUserId);
|
||||||
|
}
|
||||||
|
|
||||||
final storage = FlutterSecureStorage();
|
final storage = FlutterSecureStorage();
|
||||||
|
|
||||||
final secureStorage = jsonDecode(backupContent.secureStorageJson);
|
final secureStorage = jsonDecode(backupContent.secureStorageJson);
|
||||||
|
|
|
||||||
|
|
@ -288,7 +288,7 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
|
||||||
TextMessageContent? content = TextMessageContent.fromJson(
|
TextMessageContent? content = TextMessageContent.fromJson(
|
||||||
jsonDecode(messages[index].contentJson!));
|
jsonDecode(messages[index].contentJson!));
|
||||||
if (EmojiAnimation.supported(content.text)) {
|
if (EmojiAnimation.supported(content.text)) {
|
||||||
size = 95;
|
size = 99;
|
||||||
} else {
|
} else {
|
||||||
size = 11 +
|
size = 11 +
|
||||||
calculateNumberOfLines(content.text,
|
calculateNumberOfLines(content.text,
|
||||||
|
|
|
||||||
|
|
@ -218,7 +218,7 @@ class _RegisterViewState extends State<RegisterView> {
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
},
|
},
|
||||||
label: Text("Restore identity"),
|
label: Text(context.lang.twonlySafeRecoverBtn),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ class _BackupViewState extends State<BackupView> {
|
||||||
String backupStatus(LastBackupUploadState status) {
|
String backupStatus(LastBackupUploadState status) {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case LastBackupUploadState.none:
|
case LastBackupUploadState.none:
|
||||||
return '';
|
return context.lang.backupPending;
|
||||||
case LastBackupUploadState.pending:
|
case LastBackupUploadState.pending:
|
||||||
return context.lang.backupPending;
|
return context.lang.backupPending;
|
||||||
case LastBackupUploadState.failed:
|
case LastBackupUploadState.failed:
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,9 @@ class _TwonlyIdentityBackupViewState extends State<TwonlyIdentityBackupView> {
|
||||||
final TextEditingController repeatedPasswordCtrl = TextEditingController();
|
final TextEditingController repeatedPasswordCtrl = TextEditingController();
|
||||||
|
|
||||||
Future onPressedEnableTwonlySafe() async {
|
Future onPressedEnableTwonlySafe() async {
|
||||||
if (!mounted) return;
|
setState(() {
|
||||||
|
isLoading = true;
|
||||||
|
});
|
||||||
if (!await isSecurePassword(passwordCtrl.text)) {
|
if (!await isSecurePassword(passwordCtrl.text)) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
bool ignore = await showAlertDialog(
|
bool ignore = await showAlertDialog(
|
||||||
|
|
@ -33,6 +35,11 @@ class _TwonlyIdentityBackupViewState extends State<TwonlyIdentityBackupView> {
|
||||||
customOk: context.lang.backupInsecurePasswordCancel,
|
customOk: context.lang.backupInsecurePasswordCancel,
|
||||||
);
|
);
|
||||||
if (ignore) {
|
if (ignore) {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
isLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -119,6 +126,7 @@ class _TwonlyIdentityBackupViewState extends State<TwonlyIdentityBackupView> {
|
||||||
child: Text(
|
child: Text(
|
||||||
context.lang.backupPasswordRequirement,
|
context.lang.backupPasswordRequirement,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
color: ((passwordCtrl.text.length < 8 &&
|
color: ((passwordCtrl.text.length < 8 &&
|
||||||
passwordCtrl.text.isNotEmpty))
|
passwordCtrl.text.isNotEmpty))
|
||||||
? Colors.red
|
? Colors.red
|
||||||
|
|
@ -143,6 +151,7 @@ class _TwonlyIdentityBackupViewState extends State<TwonlyIdentityBackupView> {
|
||||||
child: Text(
|
child: Text(
|
||||||
context.lang.passwordRepeatedNotEqual,
|
context.lang.passwordRepeatedNotEqual,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
color: (passwordCtrl.text != repeatedPasswordCtrl.text &&
|
color: (passwordCtrl.text != repeatedPasswordCtrl.text &&
|
||||||
repeatedPasswordCtrl.text.isNotEmpty)
|
repeatedPasswordCtrl.text.isNotEmpty)
|
||||||
? Colors.red
|
? Colors.red
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import 'schema_v8.dart' as v8;
|
||||||
import 'schema_v9.dart' as v9;
|
import 'schema_v9.dart' as v9;
|
||||||
import 'schema_v10.dart' as v10;
|
import 'schema_v10.dart' as v10;
|
||||||
import 'schema_v11.dart' as v11;
|
import 'schema_v11.dart' as v11;
|
||||||
|
import 'schema_v12.dart' as v12;
|
||||||
|
|
||||||
class GeneratedHelper implements SchemaInstantiationHelper {
|
class GeneratedHelper implements SchemaInstantiationHelper {
|
||||||
@override
|
@override
|
||||||
|
|
@ -41,10 +42,12 @@ class GeneratedHelper implements SchemaInstantiationHelper {
|
||||||
return v10.DatabaseAtV10(db);
|
return v10.DatabaseAtV10(db);
|
||||||
case 11:
|
case 11:
|
||||||
return v11.DatabaseAtV11(db);
|
return v11.DatabaseAtV11(db);
|
||||||
|
case 12:
|
||||||
|
return v12.DatabaseAtV12(db);
|
||||||
default:
|
default:
|
||||||
throw MissingSchemaException(version, versions);
|
throw MissingSchemaException(version, versions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const versions = const [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
|
static const versions = const [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
3883
test/drift/twonly_database/generated/schema_v12.dart
Normal file
3883
test/drift/twonly_database/generated/schema_v12.dart
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue