mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 09:28:41 +00:00
finally fixing #44
This commit is contained in:
parent
e69870ea14
commit
0d2533e9a6
15 changed files with 5086 additions and 72 deletions
1
drift_schemas/twonly_database/drift_schema_v10.json
Normal file
1
drift_schemas/twonly_database/drift_schema_v10.json
Normal file
File diff suppressed because one or more lines are too long
67
lib/src/database/daos/signal_dao.dart
Normal file
67
lib/src/database/daos/signal_dao.dart
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
import 'package:drift/drift.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/twonly_database.dart';
|
||||||
|
|
||||||
|
part 'signal_dao.g.dart';
|
||||||
|
|
||||||
|
@DriftAccessor(tables: [SignalContactPreKeys, SignalContactSignedPreKeys])
|
||||||
|
class SignalDao extends DatabaseAccessor<TwonlyDatabase> with _$SignalDaoMixin {
|
||||||
|
// this constructor is required so that the main database can create an instance
|
||||||
|
// of this object.
|
||||||
|
SignalDao(super.db);
|
||||||
|
|
||||||
|
// 1: Count the number of pre-keys by contact ID
|
||||||
|
Future<int> countPreKeysByContactId(int contactId) {
|
||||||
|
return (select(signalContactPreKeys)
|
||||||
|
..where((tbl) => tbl.contactId.equals(contactId)))
|
||||||
|
.get()
|
||||||
|
.then((rows) => rows.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2: Pop a pre-key by contact ID
|
||||||
|
Future<SignalContactPreKey?> popPreKeyByContactId(int contactId) async {
|
||||||
|
final preKey =
|
||||||
|
await ((select(signalContactPreKeys)..where((tbl) => tbl.contactId.equals(contactId)))
|
||||||
|
..limit(1))
|
||||||
|
.getSingleOrNull();
|
||||||
|
|
||||||
|
if (preKey != null) {
|
||||||
|
// remove the pre key...
|
||||||
|
await (delete(signalContactPreKeys)
|
||||||
|
..where((tbl) =>
|
||||||
|
tbl.contactId.equals(contactId) &
|
||||||
|
tbl.preKeyId.equals(preKey.preKeyId)))
|
||||||
|
.go();
|
||||||
|
return preKey;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3: Insert multiple pre-keys
|
||||||
|
Future<void> insertPreKeys(
|
||||||
|
List<SignalContactPreKeysCompanion> preKeys) async {
|
||||||
|
await batch((batch) {
|
||||||
|
batch.insertAll(signalContactPreKeys, preKeys);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4: Get signed pre-key by contact ID
|
||||||
|
Future<SignalContactSignedPreKey?> getSignedPreKeyByContactId(int contactId) {
|
||||||
|
return (select(signalContactSignedPreKeys)
|
||||||
|
..where((tbl) => tbl.contactId.equals(contactId)))
|
||||||
|
.getSingleOrNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5: Insert or update signed pre-key by contact ID
|
||||||
|
Future<void> insertOrUpdateSignedPreKeyByContactId(
|
||||||
|
SignalContactSignedPreKeysCompanion signedPreKey) async {
|
||||||
|
final existingKey =
|
||||||
|
await getSignedPreKeyByContactId(signedPreKey.contactId.value);
|
||||||
|
if (existingKey != null) {
|
||||||
|
await update(signalContactSignedPreKeys).replace(signedPreKey);
|
||||||
|
} else {
|
||||||
|
await into(signalContactSignedPreKeys).insert(signedPreKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
lib/src/database/daos/signal_dao.g.dart
Normal file
11
lib/src/database/daos/signal_dao.g.dart
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'signal_dao.dart';
|
||||||
|
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
mixin _$SignalDaoMixin on DatabaseAccessor<TwonlyDatabase> {
|
||||||
|
$SignalContactPreKeysTable get signalContactPreKeys =>
|
||||||
|
attachedDatabase.signalContactPreKeys;
|
||||||
|
$SignalContactSignedPreKeysTable get signalContactSignedPreKeys =>
|
||||||
|
attachedDatabase.signalContactSignedPreKeys;
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@ import 'package:logging/logging.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
||||||
import 'package:twonly/src/database/twonly_database.dart';
|
import 'package:twonly/src/database/twonly_database.dart';
|
||||||
|
import 'package:twonly/src/utils/log.dart';
|
||||||
|
|
||||||
class ConnectPreKeyStore extends PreKeyStore {
|
class ConnectPreKeyStore extends PreKeyStore {
|
||||||
@override
|
@override
|
||||||
|
|
@ -32,6 +33,25 @@ 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(
|
||||||
|
|
|
||||||
11
lib/src/database/tables/signal_contact_prekey_table.dart
Normal file
11
lib/src/database/tables/signal_contact_prekey_table.dart
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
@DataClassName('SignalContactPreKey')
|
||||||
|
class SignalContactPreKeys extends Table {
|
||||||
|
IntColumn get contactId => integer()();
|
||||||
|
IntColumn get preKeyId => integer()();
|
||||||
|
BlobColumn get preKey => blob()();
|
||||||
|
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
|
||||||
|
@override
|
||||||
|
Set<Column> get primaryKey => {contactId, preKeyId};
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
@DataClassName('SignalContactSignedPreKey')
|
||||||
|
class SignalContactSignedPreKeys extends Table {
|
||||||
|
IntColumn get contactId => integer()();
|
||||||
|
IntColumn get signedPreKeyId => integer()();
|
||||||
|
BlobColumn get signedPreKey => blob()();
|
||||||
|
BlobColumn get signedPreKeySignature => blob()();
|
||||||
|
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
|
||||||
|
@override
|
||||||
|
Set<Column> get primaryKey => {contactId};
|
||||||
|
}
|
||||||
|
|
@ -6,10 +6,13 @@ import 'package:twonly/src/database/daos/contacts_dao.dart';
|
||||||
import 'package:twonly/src/database/daos/media_downloads_dao.dart';
|
import 'package:twonly/src/database/daos/media_downloads_dao.dart';
|
||||||
import 'package:twonly/src/database/daos/media_uploads_dao.dart';
|
import 'package:twonly/src/database/daos/media_uploads_dao.dart';
|
||||||
import 'package:twonly/src/database/daos/messages_dao.dart';
|
import 'package:twonly/src/database/daos/messages_dao.dart';
|
||||||
|
import 'package:twonly/src/database/daos/signal_dao.dart';
|
||||||
import 'package:twonly/src/database/tables/contacts_table.dart';
|
import 'package:twonly/src/database/tables/contacts_table.dart';
|
||||||
import 'package:twonly/src/database/tables/media_download_table.dart';
|
import 'package:twonly/src/database/tables/media_download_table.dart';
|
||||||
import 'package:twonly/src/database/tables/media_uploads_table.dart';
|
import 'package:twonly/src/database/tables/media_uploads_table.dart';
|
||||||
import 'package:twonly/src/database/tables/messages_table.dart';
|
import 'package:twonly/src/database/tables/messages_table.dart';
|
||||||
|
import 'package:twonly/src/database/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_identity_key_store_table.dart';
|
import 'package:twonly/src/database/tables/signal_identity_key_store_table.dart';
|
||||||
import 'package:twonly/src/database/tables/signal_pre_key_store_table.dart';
|
import 'package:twonly/src/database/tables/signal_pre_key_store_table.dart';
|
||||||
import 'package:twonly/src/database/tables/signal_sender_key_store_table.dart';
|
import 'package:twonly/src/database/tables/signal_sender_key_store_table.dart';
|
||||||
|
|
@ -27,12 +30,15 @@ part 'twonly_database.g.dart';
|
||||||
SignalIdentityKeyStores,
|
SignalIdentityKeyStores,
|
||||||
SignalPreKeyStores,
|
SignalPreKeyStores,
|
||||||
SignalSenderKeyStores,
|
SignalSenderKeyStores,
|
||||||
SignalSessionStores
|
SignalSessionStores,
|
||||||
|
SignalContactPreKeys,
|
||||||
|
SignalContactSignedPreKeys
|
||||||
], daos: [
|
], daos: [
|
||||||
MessagesDao,
|
MessagesDao,
|
||||||
ContactsDao,
|
ContactsDao,
|
||||||
MediaUploadsDao,
|
MediaUploadsDao,
|
||||||
MediaDownloadsDao,
|
MediaDownloadsDao,
|
||||||
|
SignalDao
|
||||||
])
|
])
|
||||||
class TwonlyDatabase extends _$TwonlyDatabase {
|
class TwonlyDatabase extends _$TwonlyDatabase {
|
||||||
TwonlyDatabase([QueryExecutor? e])
|
TwonlyDatabase([QueryExecutor? e])
|
||||||
|
|
@ -43,7 +49,7 @@ class TwonlyDatabase extends _$TwonlyDatabase {
|
||||||
TwonlyDatabase.forTesting(DatabaseConnection super.connection);
|
TwonlyDatabase.forTesting(DatabaseConnection super.connection);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get schemaVersion => 9;
|
int get schemaVersion => 10;
|
||||||
|
|
||||||
static QueryExecutor _openConnection() {
|
static QueryExecutor _openConnection() {
|
||||||
return driftDatabase(
|
return driftDatabase(
|
||||||
|
|
@ -57,34 +63,54 @@ class TwonlyDatabase extends _$TwonlyDatabase {
|
||||||
@override
|
@override
|
||||||
MigrationStrategy get migration {
|
MigrationStrategy get migration {
|
||||||
return MigrationStrategy(
|
return MigrationStrategy(
|
||||||
onUpgrade: stepByStep(from1To2: (m, schema) async {
|
onUpgrade: stepByStep(
|
||||||
m.addColumn(schema.messages, schema.messages.errorWhileSending);
|
from1To2: (m, schema) async {
|
||||||
}, from2To3: (m, schema) async {
|
m.addColumn(schema.messages, schema.messages.errorWhileSending);
|
||||||
m.addColumn(schema.contacts, schema.contacts.archived);
|
},
|
||||||
m.addColumn(
|
from2To3: (m, schema) async {
|
||||||
schema.contacts, schema.contacts.deleteMessagesAfterXMinutes);
|
m.addColumn(schema.contacts, schema.contacts.archived);
|
||||||
}, from3To4: (m, schema) async {
|
m.addColumn(
|
||||||
m.createTable(mediaUploads);
|
schema.contacts, schema.contacts.deleteMessagesAfterXMinutes);
|
||||||
}, from4To5: (m, schema) async {
|
},
|
||||||
m.createTable(mediaDownloads);
|
from3To4: (m, schema) async {
|
||||||
m.addColumn(schema.messages, schema.messages.mediaDownloadId);
|
m.createTable(mediaUploads);
|
||||||
m.addColumn(schema.messages, schema.messages.mediaUploadId);
|
await m.alterTable(TableMigration(
|
||||||
}, from5To6: (m, schema) async {
|
schema.mediaUploads,
|
||||||
m.addColumn(schema.messages, schema.messages.mediaStored);
|
columnTransformer: {
|
||||||
}, from6To7: (m, schema) async {
|
schema.mediaUploads.metadata:
|
||||||
m.addColumn(schema.contacts, schema.contacts.pinned);
|
schema.mediaUploads.metadata.cast<String>(),
|
||||||
}, from7To8: (m, schema) async {
|
},
|
||||||
m.addColumn(schema.contacts, schema.contacts.alsoBestFriend);
|
));
|
||||||
m.addColumn(schema.contacts, schema.contacts.lastFlameSync);
|
},
|
||||||
}, from8To9: (m, schema) async {
|
from4To5: (m, schema) async {
|
||||||
await m.alterTable(TableMigration(
|
m.createTable(mediaDownloads);
|
||||||
schema.mediaUploads,
|
m.addColumn(schema.messages, schema.messages.mediaDownloadId);
|
||||||
columnTransformer: {
|
m.addColumn(schema.messages, schema.messages.mediaUploadId);
|
||||||
schema.mediaUploads.metadata:
|
},
|
||||||
schema.mediaUploads.metadata.cast<String>(),
|
from5To6: (m, schema) async {
|
||||||
},
|
m.addColumn(schema.messages, schema.messages.mediaStored);
|
||||||
));
|
},
|
||||||
}),
|
from6To7: (m, schema) async {
|
||||||
|
m.addColumn(schema.contacts, schema.contacts.pinned);
|
||||||
|
},
|
||||||
|
from7To8: (m, schema) async {
|
||||||
|
m.addColumn(schema.contacts, schema.contacts.alsoBestFriend);
|
||||||
|
m.addColumn(schema.contacts, schema.contacts.lastFlameSync);
|
||||||
|
},
|
||||||
|
from8To9: (m, schema) async {
|
||||||
|
await m.alterTable(TableMigration(
|
||||||
|
schema.mediaUploads,
|
||||||
|
columnTransformer: {
|
||||||
|
schema.mediaUploads.metadata:
|
||||||
|
schema.mediaUploads.metadata.cast<String>(),
|
||||||
|
},
|
||||||
|
));
|
||||||
|
},
|
||||||
|
from9To10: (m, schema) async {
|
||||||
|
m.createTable(signalContactPreKeys);
|
||||||
|
m.createTable(signalContactSignedPreKeys);
|
||||||
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1879,6 +1879,259 @@ final class Schema9 extends i0.VersionedSchema {
|
||||||
i1.GeneratedColumn<String> _column_56(String aliasedName) =>
|
i1.GeneratedColumn<String> _column_56(String aliasedName) =>
|
||||||
i1.GeneratedColumn<String>('metadata', aliasedName, true,
|
i1.GeneratedColumn<String>('metadata', aliasedName, true,
|
||||||
type: i1.DriftSqlType.string);
|
type: i1.DriftSqlType.string);
|
||||||
|
|
||||||
|
final class Schema10 extends i0.VersionedSchema {
|
||||||
|
Schema10({required super.database}) : super(version: 10);
|
||||||
|
@override
|
||||||
|
late final List<i1.DatabaseSchemaEntity> entities = [
|
||||||
|
contacts,
|
||||||
|
messages,
|
||||||
|
mediaUploads,
|
||||||
|
mediaDownloads,
|
||||||
|
signalIdentityKeyStores,
|
||||||
|
signalPreKeyStores,
|
||||||
|
signalSenderKeyStores,
|
||||||
|
signalSessionStores,
|
||||||
|
signalContactPreKeys,
|
||||||
|
signalContactSignedPreKeys,
|
||||||
|
];
|
||||||
|
late final Shape12 contacts = Shape12(
|
||||||
|
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_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 Shape13 signalContactPreKeys = Shape13(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'signal_contact_pre_keys',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [
|
||||||
|
'PRIMARY KEY(contact_id, pre_key_id)',
|
||||||
|
],
|
||||||
|
columns: [
|
||||||
|
_column_57,
|
||||||
|
_column_34,
|
||||||
|
_column_35,
|
||||||
|
_column_10,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
late final Shape14 signalContactSignedPreKeys = Shape14(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'signal_contact_signed_pre_keys',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [
|
||||||
|
'PRIMARY KEY(contact_id, signed_pre_key_id)',
|
||||||
|
],
|
||||||
|
columns: [
|
||||||
|
_column_57,
|
||||||
|
_column_58,
|
||||||
|
_column_59,
|
||||||
|
_column_60,
|
||||||
|
_column_10,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Shape13 extends i0.VersionedTable {
|
||||||
|
Shape13({required super.source, required super.alias}) : super.aliased();
|
||||||
|
i1.GeneratedColumn<int> get contactId =>
|
||||||
|
columnsByName['contact_id']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<int> get preKeyId =>
|
||||||
|
columnsByName['pre_key_id']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<i2.Uint8List> get preKey =>
|
||||||
|
columnsByName['pre_key']! as i1.GeneratedColumn<i2.Uint8List>;
|
||||||
|
i1.GeneratedColumn<DateTime> get createdAt =>
|
||||||
|
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.GeneratedColumn<int> _column_57(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<int>('contact_id', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.int);
|
||||||
|
|
||||||
|
class Shape14 extends i0.VersionedTable {
|
||||||
|
Shape14({required super.source, required super.alias}) : super.aliased();
|
||||||
|
i1.GeneratedColumn<int> get contactId =>
|
||||||
|
columnsByName['contact_id']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<int> get signedPreKeyId =>
|
||||||
|
columnsByName['signed_pre_key_id']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<i2.Uint8List> get signedPreKey =>
|
||||||
|
columnsByName['signed_pre_key']! as i1.GeneratedColumn<i2.Uint8List>;
|
||||||
|
i1.GeneratedColumn<i2.Uint8List> get signedPreKeySignature =>
|
||||||
|
columnsByName['signed_pre_key_signature']!
|
||||||
|
as i1.GeneratedColumn<i2.Uint8List>;
|
||||||
|
i1.GeneratedColumn<DateTime> get createdAt =>
|
||||||
|
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.GeneratedColumn<int> _column_58(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<int>('signed_pre_key_id', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.int);
|
||||||
|
i1.GeneratedColumn<i2.Uint8List> _column_59(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<i2.Uint8List>('signed_pre_key', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.blob);
|
||||||
|
i1.GeneratedColumn<i2.Uint8List> _column_60(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<i2.Uint8List>(
|
||||||
|
'signed_pre_key_signature', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.blob);
|
||||||
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,
|
||||||
|
|
@ -1888,6 +2141,7 @@ i0.MigrationStepWithVersion migrationSteps({
|
||||||
required Future<void> Function(i1.Migrator m, Schema7 schema) from6To7,
|
required Future<void> Function(i1.Migrator m, Schema7 schema) from6To7,
|
||||||
required Future<void> Function(i1.Migrator m, Schema8 schema) from7To8,
|
required Future<void> Function(i1.Migrator m, Schema8 schema) from7To8,
|
||||||
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,
|
||||||
}) {
|
}) {
|
||||||
return (currentVersion, database) async {
|
return (currentVersion, database) async {
|
||||||
switch (currentVersion) {
|
switch (currentVersion) {
|
||||||
|
|
@ -1931,6 +2185,11 @@ i0.MigrationStepWithVersion migrationSteps({
|
||||||
final migrator = i1.Migrator(database, schema);
|
final migrator = i1.Migrator(database, schema);
|
||||||
await from8To9(migrator, schema);
|
await from8To9(migrator, schema);
|
||||||
return 9;
|
return 9;
|
||||||
|
case 9:
|
||||||
|
final schema = Schema10(database: database);
|
||||||
|
final migrator = i1.Migrator(database, schema);
|
||||||
|
await from9To10(migrator, schema);
|
||||||
|
return 10;
|
||||||
default:
|
default:
|
||||||
throw ArgumentError.value('Unknown migration from $currentVersion');
|
throw ArgumentError.value('Unknown migration from $currentVersion');
|
||||||
}
|
}
|
||||||
|
|
@ -1946,6 +2205,7 @@ i1.OnUpgrade stepByStep({
|
||||||
required Future<void> Function(i1.Migrator m, Schema7 schema) from6To7,
|
required Future<void> Function(i1.Migrator m, Schema7 schema) from6To7,
|
||||||
required Future<void> Function(i1.Migrator m, Schema8 schema) from7To8,
|
required Future<void> Function(i1.Migrator m, Schema8 schema) from7To8,
|
||||||
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,
|
||||||
}) =>
|
}) =>
|
||||||
i0.VersionedSchema.stepByStepHelper(
|
i0.VersionedSchema.stepByStepHelper(
|
||||||
step: migrationSteps(
|
step: migrationSteps(
|
||||||
|
|
@ -1957,4 +2217,5 @@ i1.OnUpgrade stepByStep({
|
||||||
from6To7: from6To7,
|
from6To7: from6To7,
|
||||||
from7To8: from7To8,
|
from7To8: from7To8,
|
||||||
from8To9: from8To9,
|
from8To9: from8To9,
|
||||||
|
from9To10: from9To10,
|
||||||
));
|
));
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import 'package:twonly/src/services/api/utils.dart';
|
||||||
import 'package:twonly/src/services/api/media_received.dart';
|
import 'package:twonly/src/services/api/media_received.dart';
|
||||||
import 'package:twonly/src/services/api/media_send.dart';
|
import 'package:twonly/src/services/api/media_send.dart';
|
||||||
import 'package:twonly/src/services/api/server_messages.dart';
|
import 'package:twonly/src/services/api/server_messages.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/utils.signal.dart';
|
import 'package:twonly/src/services/signal/utils.signal.dart';
|
||||||
import 'package:twonly/src/utils/hive.dart';
|
import 'package:twonly/src/utils/hive.dart';
|
||||||
|
|
@ -559,6 +560,44 @@ class ApiService {
|
||||||
return await sendRequestSync(req);
|
return await sendRequestSync(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Response_SignedPreKey?> getSignedKeyByUserId(int userId) async {
|
||||||
|
var get = ApplicationData_GetSignedPreKeyByUserId()..userId = Int64(userId);
|
||||||
|
var appData = ApplicationData()..getsignedprekeybyuserid = get;
|
||||||
|
var req = createClientToServerFromApplicationData(appData);
|
||||||
|
Result res = await sendRequestSync(req);
|
||||||
|
if (res.isSuccess) {
|
||||||
|
server.Response_Ok ok = res.value;
|
||||||
|
if (ok.hasSignedprekey()) {
|
||||||
|
return ok.signedprekey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<OtherPreKeys?> getPreKeysByUserId(int userId) async {
|
||||||
|
var get = ApplicationData_GetPrekeysByUserId()..userId = Int64(userId);
|
||||||
|
var appData = ApplicationData()..getprekeysbyuserid = get;
|
||||||
|
var req = createClientToServerFromApplicationData(appData);
|
||||||
|
Result res = await sendRequestSync(req);
|
||||||
|
if (res.isSuccess) {
|
||||||
|
server.Response_Ok ok = res.value;
|
||||||
|
if (ok.hasUserdata()) {
|
||||||
|
server.Response_UserData data = ok.userdata;
|
||||||
|
if (data.hasSignedPrekey() &&
|
||||||
|
data.hasSignedPrekeyId() &&
|
||||||
|
data.hasSignedPrekeySignature()) {
|
||||||
|
return OtherPreKeys(
|
||||||
|
preKeys: ok.userdata.prekeys,
|
||||||
|
signedPreKey: data.signedPrekey,
|
||||||
|
signedPreKeyId: data.signedPrekeyId.toInt(),
|
||||||
|
signedPreKeySignature: data.signedPrekeySignature,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
Future<Result> sendTextMessage(
|
Future<Result> sendTextMessage(
|
||||||
int target, Uint8List msg, List<int>? pushData) async {
|
int target, Uint8List msg, List<int>? pushData) async {
|
||||||
var testMessage = ApplicationData_TextMessage()
|
var testMessage = ApplicationData_TextMessage()
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,31 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
import 'package:drift/drift.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/src/database/twonly_database.dart';
|
||||||
import 'package:twonly/src/model/json/message.dart';
|
import 'package:twonly/src/model/json/message.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/services/signal/consts.signal.dart';
|
import 'package:twonly/src/services/signal/consts.signal.dart';
|
||||||
import 'package:twonly/src/services/signal/utils.signal.dart';
|
import 'package:twonly/src/services/signal/utils.signal.dart';
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
import 'package:twonly/src/model/protobuf/api/server_to_client.pb.dart'
|
||||||
|
as server;
|
||||||
|
|
||||||
|
class OtherPreKeys {
|
||||||
|
OtherPreKeys({
|
||||||
|
required this.preKeys,
|
||||||
|
required this.signedPreKey,
|
||||||
|
required this.signedPreKeyId,
|
||||||
|
required this.signedPreKeySignature,
|
||||||
|
});
|
||||||
|
final List<server.Response_PreKey> preKeys;
|
||||||
|
final int signedPreKeyId;
|
||||||
|
final List<int> signedPreKey;
|
||||||
|
final List<int> signedPreKeySignature;
|
||||||
|
}
|
||||||
|
|
||||||
// Future<void> deleteSession(String userId) async {
|
// Future<void> deleteSession(String userId) async {
|
||||||
// await mixinSignalProtocolStore.sessionStore.sessionDao
|
// await mixinSignalProtocolStore.sessionStore.sessionDao
|
||||||
|
|
@ -31,36 +49,72 @@ import 'package:twonly/src/utils/misc.dart';
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// Future<bool> checkSignalSession(String recipientId, String sessionId) async {
|
Future requestNewPrekeysForContact(int contactId) async {
|
||||||
// final contains = await signalProtocol.containsSession(
|
final otherKeys = await apiService.getPreKeysByUserId(contactId);
|
||||||
// recipientId,
|
if (otherKeys != null) {
|
||||||
// deviceId: sessionId.getDeviceId(),
|
Log.info("got fresh pre keys from other $contactId!");
|
||||||
// );
|
final preKeys = otherKeys.preKeys
|
||||||
// if (!contains) {
|
.map(
|
||||||
// final requestKeys = <BlazeMessageParamSession>[
|
(preKey) => SignalContactPreKeysCompanion(
|
||||||
// BlazeMessageParamSession(userId: recipientId, sessionId: sessionId),
|
contactId: Value(contactId),
|
||||||
// ];
|
preKey: Value(Uint8List.fromList(preKey.prekey)),
|
||||||
// final blazeMessage = createConsumeSessionSignalKeys(
|
preKeyId: Value(preKey.id.toInt()),
|
||||||
// createConsumeSignalKeysParam(requestKeys),
|
),
|
||||||
// );
|
)
|
||||||
// final data = (await signalKeysChannel(blazeMessage))?.data;
|
.toList();
|
||||||
// if (data == null) {
|
await twonlyDB.signalDao.insertPreKeys(preKeys);
|
||||||
// return false;
|
} else {
|
||||||
// }
|
Log.error("could not load new pre keys for user $contactId");
|
||||||
// final keys = List<SignalKey>.from(
|
}
|
||||||
// (data as List<dynamic>).map(
|
}
|
||||||
// (e) => SignalKey.fromJson(e as Map<String, dynamic>),
|
|
||||||
// ),
|
Future<SignalContactPreKey?> getPreKeyByContactId(int contactId) async {
|
||||||
// );
|
int count = await twonlyDB.signalDao.countPreKeysByContactId(contactId);
|
||||||
// if (keys.isNotEmpty) {
|
if (count < 10) {
|
||||||
// final preKeyBundle = keys.first.createPreKeyBundle();
|
Log.info(
|
||||||
// await signalProtocol.processSession(recipientId, preKeyBundle);
|
"There are $count < 10 prekeys for $contactId. Loading fresh once from the server.",
|
||||||
// } else {
|
);
|
||||||
// return false;
|
requestNewPrekeysForContact(contactId);
|
||||||
// }
|
}
|
||||||
// }
|
return twonlyDB.signalDao.popPreKeyByContactId(contactId);
|
||||||
// return true;
|
}
|
||||||
// }
|
|
||||||
|
Future requestNewSignedPreKeyForContact(int contactId) async {
|
||||||
|
final signedPreKey = await apiService.getSignedKeyByUserId(contactId);
|
||||||
|
if (signedPreKey != null) {
|
||||||
|
Log.info("got fresh signed pre keys from other $contactId!");
|
||||||
|
await twonlyDB.signalDao.insertOrUpdateSignedPreKeyByContactId(
|
||||||
|
SignalContactSignedPreKeysCompanion(
|
||||||
|
contactId: Value(contactId),
|
||||||
|
signedPreKey: Value(Uint8List.fromList(signedPreKey.signedPrekey)),
|
||||||
|
signedPreKeySignature:
|
||||||
|
Value(Uint8List.fromList(signedPreKey.signedPrekeySignature)),
|
||||||
|
signedPreKeyId: Value(signedPreKey.signedPrekeyId.toInt()),
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
Log.error("could not load new signed pre key for user $contactId");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<SignalContactSignedPreKey?> getSignedPreKeyByContactId(
|
||||||
|
int contactId,
|
||||||
|
) async {
|
||||||
|
SignalContactSignedPreKey? signedPreKey =
|
||||||
|
await twonlyDB.signalDao.getSignedPreKeyByContactId(contactId);
|
||||||
|
|
||||||
|
if (signedPreKey != null) {
|
||||||
|
DateTime fortyEightHoursAgo = DateTime.now().subtract(Duration(hours: 48));
|
||||||
|
bool isOlderThan48Hours =
|
||||||
|
(signedPreKey.createdAt).isBefore(fortyEightHoursAgo);
|
||||||
|
if (isOlderThan48Hours) {
|
||||||
|
requestNewSignedPreKeyForContact(contactId);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
requestNewSignedPreKeyForContact(contactId);
|
||||||
|
Log.error("Contact $contactId does not have a signed pre key!");
|
||||||
|
}
|
||||||
|
return signedPreKey;
|
||||||
|
}
|
||||||
|
|
||||||
Future<Uint8List?> signalEncryptMessage(int target, MessageJson msg) async {
|
Future<Uint8List?> signalEncryptMessage(int target, MessageJson msg) async {
|
||||||
try {
|
try {
|
||||||
|
|
@ -69,17 +123,62 @@ Future<Uint8List?> signalEncryptMessage(int target, MessageJson msg) async {
|
||||||
|
|
||||||
SessionCipher session = SessionCipher.fromStore(signalStore, address);
|
SessionCipher session = SessionCipher.fromStore(signalStore, address);
|
||||||
|
|
||||||
final SessionRecord sessionRecord =
|
SignalContactPreKey? preKey = await getPreKeyByContactId(target);
|
||||||
await signalStore.sessionStore.loadSession(address);
|
SignalContactSignedPreKey? signedPreKey = await getSignedPreKeyByContactId(
|
||||||
|
target,
|
||||||
|
);
|
||||||
|
|
||||||
if (!sessionRecord.sessionState.hasUnacknowledgedPreKeyMessage()) {
|
if (signedPreKey != null) {
|
||||||
Log.info("There are now pre keys any more... load new...");
|
SessionBuilder sessionBuilder = SessionBuilder.fromSignalStore(
|
||||||
|
signalStore,
|
||||||
|
address,
|
||||||
|
);
|
||||||
|
|
||||||
|
ECPublicKey? tempPrePublicKey;
|
||||||
|
|
||||||
|
if (preKey != null) {
|
||||||
|
tempPrePublicKey = Curve.decodePoint(
|
||||||
|
DjbECPublicKey(
|
||||||
|
Uint8List.fromList(preKey.preKey),
|
||||||
|
).serialize(),
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ECPublicKey? tempSignedPreKeyPublic = Curve.decodePoint(
|
||||||
|
DjbECPublicKey(Uint8List.fromList(signedPreKey.signedPreKey))
|
||||||
|
.serialize(),
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
|
||||||
|
Uint8List? tempSignedPreKeySignature = Uint8List.fromList(
|
||||||
|
signedPreKey.signedPreKeySignature,
|
||||||
|
);
|
||||||
|
|
||||||
|
final IdentityKey? tempIdentityKey =
|
||||||
|
await signalStore.getIdentity(address);
|
||||||
|
if (tempIdentityKey != null) {
|
||||||
|
PreKeyBundle preKeyBundle = PreKeyBundle(
|
||||||
|
target,
|
||||||
|
defaultDeviceId,
|
||||||
|
preKey?.preKeyId,
|
||||||
|
tempPrePublicKey,
|
||||||
|
signedPreKey.signedPreKeyId,
|
||||||
|
tempSignedPreKeyPublic,
|
||||||
|
tempSignedPreKeySignature,
|
||||||
|
tempIdentityKey,
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await sessionBuilder.processPreKeyBundle(preKeyBundle);
|
||||||
|
} catch (e) {
|
||||||
|
Log.error("could not process pre key bundle: $e");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.error("did not get the identity of the remote address");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// sessionRecord.sessionState.sign
|
|
||||||
|
|
||||||
// session.
|
|
||||||
|
|
||||||
final ciphertext = await session.encrypt(
|
final ciphertext = await session.encrypt(
|
||||||
Uint8List.fromList(gzip.encode(utf8.encode(jsonEncode(msg.toJson())))),
|
Uint8List.fromList(gzip.encode(utf8.encode(jsonEncode(msg.toJson())))),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ 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';
|
||||||
|
|
@ -56,7 +57,10 @@ Future signalHandleNewServerConnection() async {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<PreKeyRecord>> signalGetPreKeys() async {
|
Future<List<PreKeyRecord>> signalGetPreKeys() async {
|
||||||
final preKeys = generatePreKeys(0, 200);
|
int? start = await ConnectPreKeyStore.getNextPreKeyId();
|
||||||
|
if (start == null) return [];
|
||||||
|
print(start);
|
||||||
|
final preKeys = generatePreKeys(start, 200);
|
||||||
final signalStore = await getSignalStore();
|
final signalStore = await getSignalStore();
|
||||||
if (signalStore == null) return [];
|
if (signalStore == null) return [];
|
||||||
for (final p in preKeys) {
|
for (final p in preKeys) {
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ Future<void> _writeLogToFile(LogRecord record) async {
|
||||||
|
|
||||||
// Prepare the log message
|
// Prepare the log message
|
||||||
final logMessage =
|
final logMessage =
|
||||||
'${DateTime.now()} ${record.level.name} [twonly] ${record.loggerName} > ${record.message}\n';
|
'${DateTime.now().toString().split(".")[0]} ${record.level.name} [twonly] ${record.loggerName} > ${record.message}\n';
|
||||||
|
|
||||||
// Append the log message to the file
|
// Append the log message to the file
|
||||||
await logFile.writeAsString(logMessage, mode: FileMode.append);
|
await logFile.writeAsString(logMessage, mode: FileMode.append);
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import 'schema_v6.dart' as v6;
|
||||||
import 'schema_v7.dart' as v7;
|
import 'schema_v7.dart' as v7;
|
||||||
import 'schema_v8.dart' as v8;
|
import 'schema_v8.dart' as v8;
|
||||||
import 'schema_v9.dart' as v9;
|
import 'schema_v9.dart' as v9;
|
||||||
|
import 'schema_v10.dart' as v10;
|
||||||
|
|
||||||
class GeneratedHelper implements SchemaInstantiationHelper {
|
class GeneratedHelper implements SchemaInstantiationHelper {
|
||||||
@override
|
@override
|
||||||
|
|
@ -35,10 +36,12 @@ class GeneratedHelper implements SchemaInstantiationHelper {
|
||||||
return v8.DatabaseAtV8(db);
|
return v8.DatabaseAtV8(db);
|
||||||
case 9:
|
case 9:
|
||||||
return v9.DatabaseAtV9(db);
|
return v9.DatabaseAtV9(db);
|
||||||
|
case 10:
|
||||||
|
return v10.DatabaseAtV10(db);
|
||||||
default:
|
default:
|
||||||
throw MissingSchemaException(version, versions);
|
throw MissingSchemaException(version, versions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const versions = const [1, 2, 3, 4, 5, 6, 7, 8, 9];
|
static const versions = const [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
3495
test/drift/twonly_database/generated/schema_v10.dart
Normal file
3495
test/drift/twonly_database/generated/schema_v10.dart
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue