move signal pre key store into the database
Some checks failed
Flutter analyze & test / flutter_analyze_and_test (push) Has been cancelled

This commit is contained in:
otsmr 2026-05-14 14:38:00 +02:00
parent ed0b7160b9
commit 4b9180c3c7
14 changed files with 5050 additions and 108 deletions

View file

@ -32,6 +32,6 @@ class AppState {
static bool isInBackgroundTask = false; static bool isInBackgroundTask = false;
static bool allowErrorTrackingViaSentry = false; static bool allowErrorTrackingViaSentry = false;
static bool gotMessageFromServer = false; static bool gotMessageFromServer = false;
static int latestAppVersionId = 114; static int latestAppVersionId = 115;
} }

View file

@ -267,9 +267,36 @@ Future<void> runMigrations() async {
} }
await UserService.update((u) => u.appVersion = 114); await UserService.update((u) => u.appVersion = 114);
} }
if (userService.currentUser.appVersion < 115) {
var migrationSuccess = true;
try {
final rustStore = await RustKeyManager.loadSignedPrekeys();
for (final entry in rustStore.entries) {
final companion = SignalSignedPreKeyStoresCompanion(
signedPreKeyId: Value(entry.key),
signedPreKey: Value(entry.value),
);
await twonlyDB
.into(twonlyDB.signalSignedPreKeyStores)
.insert(
companion,
mode: InsertMode.insertOrReplace,
);
await RustKeyManager.removeSignedPrekey(signedPreKeyId: entry.key);
}
} catch (e) {
Log.error('Failed to migrate signed prekeys to Drift: $e');
migrationSuccess = false;
}
if (migrationSuccess) {
await UserService.update((u) => u.appVersion = 115);
}
}
if (kDebugMode) { if (kDebugMode) {
assert( assert(
AppState.latestAppVersionId == 114, AppState.latestAppVersionId == 115,
'Forgot to update the target version in runMigrations() after incrementing AppState.latestAppVersionId.', 'Forgot to update the target version in runMigrations() after incrementing AppState.latestAppVersionId.',
); );
assert( assert(

File diff suppressed because it is too large Load diff

View file

@ -1,10 +1,11 @@
import 'dart:collection'; import 'dart:collection';
import 'dart:convert'; import 'dart:convert';
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/core/bridge/wrapper/key_manager.dart'; import 'package:twonly/locator.dart';
import 'package:twonly/src/constants/secure_storage.keys.dart'; import 'package:twonly/src/constants/secure_storage.keys.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/utils/log.dart';
import 'package:twonly/src/utils/secure_storage.dart'; import 'package:twonly/src/utils/secure_storage.dart';
Future<HashMap<int, Uint8List>> getSignalSignedPreKeyStoreOld() async { Future<HashMap<int, Uint8List>> getSignalSignedPreKeyStoreOld() async {
@ -26,25 +27,25 @@ Future<HashMap<int, Uint8List>> getSignalSignedPreKeyStoreOld() async {
class SignalSignedPreKeyStore extends SignedPreKeyStore { class SignalSignedPreKeyStore extends SignedPreKeyStore {
@override @override
Future<SignedPreKeyRecord> loadSignedPreKey(int signedPreKeyId) async { Future<SignedPreKeyRecord> loadSignedPreKey(int signedPreKeyId) async {
final store = await RustKeyManager.loadSignedPrekey( final record = await (twonlyDB.select(
signedPreKeyId: signedPreKeyId, twonlyDB.signalSignedPreKeyStores,
); )..where((tbl) => tbl.signedPreKeyId.equals(signedPreKeyId))).get();
if (store == null) { if (record.isEmpty) {
throw InvalidKeyIdException( throw InvalidKeyIdException(
'No such signed prekey record! $signedPreKeyId', 'No such signed prekey record! $signedPreKeyId',
); );
} }
return SignedPreKeyRecord.fromSerialized(store); return SignedPreKeyRecord.fromSerialized(record.first.signedPreKey);
} }
@override @override
Future<List<SignedPreKeyRecord>> loadSignedPreKeys() async { Future<List<SignedPreKeyRecord>> loadSignedPreKeys() async {
final store = await RustKeyManager.loadSignedPrekeys(); final records = await twonlyDB
final results = <SignedPreKeyRecord>[]; .select(twonlyDB.signalSignedPreKeyStores)
for (final serialized in store.values) { .get();
results.add(SignedPreKeyRecord.fromSerialized(serialized)); return records
} .map((r) => SignedPreKeyRecord.fromSerialized(r.signedPreKey))
return results; .toList();
} }
@override @override
@ -52,21 +53,32 @@ class SignalSignedPreKeyStore extends SignedPreKeyStore {
int signedPreKeyId, int signedPreKeyId,
SignedPreKeyRecord record, SignedPreKeyRecord record,
) async { ) async {
await RustKeyManager.storeSignedPrekey( final companion = SignalSignedPreKeyStoresCompanion(
signedPreKeyId: signedPreKeyId, signedPreKeyId: Value(signedPreKeyId),
record: record.serialize(), signedPreKey: Value(record.serialize()),
); );
try {
await twonlyDB
.into(twonlyDB.signalSignedPreKeyStores)
.insert(companion, mode: InsertMode.insertOrReplace);
} catch (e) {
Log.error('$e');
}
} }
@override @override
Future<bool> containsSignedPreKey(int signedPreKeyId) async => Future<bool> containsSignedPreKey(int signedPreKeyId) async {
await RustKeyManager.loadSignedPrekey( final record = await (twonlyDB.select(
signedPreKeyId: signedPreKeyId, twonlyDB.signalSignedPreKeyStores,
) != )..where((tbl) => tbl.signedPreKeyId.equals(signedPreKeyId))).get();
null; return record.isNotEmpty;
}
@override @override
Future<void> removeSignedPreKey(int signedPreKeyId) async { Future<void> removeSignedPreKey(int signedPreKeyId) async {
await RustKeyManager.removeSignedPrekey(signedPreKeyId: signedPreKeyId); await (twonlyDB.delete(
twonlyDB.signalSignedPreKeyStores,
)..where((tbl) => tbl.signedPreKeyId.equals(signedPreKeyId))).go();
} }
} }

View file

@ -0,0 +1,11 @@
import 'package:drift/drift.dart';
@DataClassName('SignalSignedPreKeyStore')
class SignalSignedPreKeyStores extends Table {
IntColumn get signedPreKeyId => integer()();
BlobColumn get signedPreKey => blob()();
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
@override
Set<Column> get primaryKey => {signedPreKeyId};
}

View file

@ -23,6 +23,7 @@ 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';
import 'package:twonly/src/database/tables/signal_session_store.table.dart'; import 'package:twonly/src/database/tables/signal_session_store.table.dart';
import 'package:twonly/src/database/tables/signal_signed_pre_key_store.table.dart';
import 'package:twonly/src/database/tables/user_discovery.table.dart'; import 'package:twonly/src/database/tables/user_discovery.table.dart';
import 'package:twonly/src/database/twonly.db.steps.dart'; import 'package:twonly/src/database/twonly.db.steps.dart';
import 'package:twonly/src/utils/log.dart'; import 'package:twonly/src/utils/log.dart';
@ -45,6 +46,7 @@ part 'twonly.db.g.dart';
SignalPreKeyStores, SignalPreKeyStores,
SignalSenderKeyStores, SignalSenderKeyStores,
SignalSessionStores, SignalSessionStores,
SignalSignedPreKeyStores,
MessageActions, MessageActions,
GroupHistories, GroupHistories,
KeyVerifications, KeyVerifications,
@ -79,7 +81,7 @@ class TwonlyDB extends _$TwonlyDB {
TwonlyDB.forTesting(DatabaseConnection super.connection); TwonlyDB.forTesting(DatabaseConnection super.connection);
@override @override
int get schemaVersion => 14; int get schemaVersion => 15;
static QueryExecutor _openConnection() { static QueryExecutor _openConnection() {
return driftDatabase( return driftDatabase(
@ -206,6 +208,9 @@ class TwonlyDB extends _$TwonlyDB {
schema.mediaFiles.hasCropAnalyzed, schema.mediaFiles.hasCropAnalyzed,
); );
}, },
from14To15: (m, schema) async {
await m.createTable(schema.signalSignedPreKeyStores);
},
)(m, from, to); )(m, from, to);
}, },
); );

View file

@ -8316,6 +8316,285 @@ class SignalSessionStoresCompanion extends UpdateCompanion<SignalSessionStore> {
} }
} }
class $SignalSignedPreKeyStoresTable extends SignalSignedPreKeyStores
with TableInfo<$SignalSignedPreKeyStoresTable, SignalSignedPreKeyStore> {
@override
final GeneratedDatabase attachedDatabase;
final String? _alias;
$SignalSignedPreKeyStoresTable(this.attachedDatabase, [this._alias]);
static const VerificationMeta _signedPreKeyIdMeta = const VerificationMeta(
'signedPreKeyId',
);
@override
late final GeneratedColumn<int> signedPreKeyId = GeneratedColumn<int>(
'signed_pre_key_id',
aliasedName,
false,
type: DriftSqlType.int,
requiredDuringInsert: false,
);
static const VerificationMeta _signedPreKeyMeta = const VerificationMeta(
'signedPreKey',
);
@override
late final GeneratedColumn<Uint8List> signedPreKey =
GeneratedColumn<Uint8List>(
'signed_pre_key',
aliasedName,
false,
type: DriftSqlType.blob,
requiredDuringInsert: true,
);
static const VerificationMeta _createdAtMeta = const VerificationMeta(
'createdAt',
);
@override
late final GeneratedColumn<DateTime> createdAt = GeneratedColumn<DateTime>(
'created_at',
aliasedName,
false,
type: DriftSqlType.dateTime,
requiredDuringInsert: false,
defaultValue: currentDateAndTime,
);
@override
List<GeneratedColumn> get $columns => [
signedPreKeyId,
signedPreKey,
createdAt,
];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'signal_signed_pre_key_stores';
@override
VerificationContext validateIntegrity(
Insertable<SignalSignedPreKeyStore> instance, {
bool isInserting = false,
}) {
final context = VerificationContext();
final data = instance.toColumns(true);
if (data.containsKey('signed_pre_key_id')) {
context.handle(
_signedPreKeyIdMeta,
signedPreKeyId.isAcceptableOrUnknown(
data['signed_pre_key_id']!,
_signedPreKeyIdMeta,
),
);
}
if (data.containsKey('signed_pre_key')) {
context.handle(
_signedPreKeyMeta,
signedPreKey.isAcceptableOrUnknown(
data['signed_pre_key']!,
_signedPreKeyMeta,
),
);
} else if (isInserting) {
context.missing(_signedPreKeyMeta);
}
if (data.containsKey('created_at')) {
context.handle(
_createdAtMeta,
createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta),
);
}
return context;
}
@override
Set<GeneratedColumn> get $primaryKey => {signedPreKeyId};
@override
SignalSignedPreKeyStore map(
Map<String, dynamic> data, {
String? tablePrefix,
}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return SignalSignedPreKeyStore(
signedPreKeyId: attachedDatabase.typeMapping.read(
DriftSqlType.int,
data['${effectivePrefix}signed_pre_key_id'],
)!,
signedPreKey: attachedDatabase.typeMapping.read(
DriftSqlType.blob,
data['${effectivePrefix}signed_pre_key'],
)!,
createdAt: attachedDatabase.typeMapping.read(
DriftSqlType.dateTime,
data['${effectivePrefix}created_at'],
)!,
);
}
@override
$SignalSignedPreKeyStoresTable createAlias(String alias) {
return $SignalSignedPreKeyStoresTable(attachedDatabase, alias);
}
}
class SignalSignedPreKeyStore extends DataClass
implements Insertable<SignalSignedPreKeyStore> {
final int signedPreKeyId;
final Uint8List signedPreKey;
final DateTime createdAt;
const SignalSignedPreKeyStore({
required this.signedPreKeyId,
required this.signedPreKey,
required this.createdAt,
});
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
map['signed_pre_key_id'] = Variable<int>(signedPreKeyId);
map['signed_pre_key'] = Variable<Uint8List>(signedPreKey);
map['created_at'] = Variable<DateTime>(createdAt);
return map;
}
SignalSignedPreKeyStoresCompanion toCompanion(bool nullToAbsent) {
return SignalSignedPreKeyStoresCompanion(
signedPreKeyId: Value(signedPreKeyId),
signedPreKey: Value(signedPreKey),
createdAt: Value(createdAt),
);
}
factory SignalSignedPreKeyStore.fromJson(
Map<String, dynamic> json, {
ValueSerializer? serializer,
}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return SignalSignedPreKeyStore(
signedPreKeyId: serializer.fromJson<int>(json['signedPreKeyId']),
signedPreKey: serializer.fromJson<Uint8List>(json['signedPreKey']),
createdAt: serializer.fromJson<DateTime>(json['createdAt']),
);
}
@override
Map<String, dynamic> toJson({ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'signedPreKeyId': serializer.toJson<int>(signedPreKeyId),
'signedPreKey': serializer.toJson<Uint8List>(signedPreKey),
'createdAt': serializer.toJson<DateTime>(createdAt),
};
}
SignalSignedPreKeyStore copyWith({
int? signedPreKeyId,
Uint8List? signedPreKey,
DateTime? createdAt,
}) => SignalSignedPreKeyStore(
signedPreKeyId: signedPreKeyId ?? this.signedPreKeyId,
signedPreKey: signedPreKey ?? this.signedPreKey,
createdAt: createdAt ?? this.createdAt,
);
SignalSignedPreKeyStore copyWithCompanion(
SignalSignedPreKeyStoresCompanion data,
) {
return SignalSignedPreKeyStore(
signedPreKeyId: data.signedPreKeyId.present
? data.signedPreKeyId.value
: this.signedPreKeyId,
signedPreKey: data.signedPreKey.present
? data.signedPreKey.value
: this.signedPreKey,
createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
);
}
@override
String toString() {
return (StringBuffer('SignalSignedPreKeyStore(')
..write('signedPreKeyId: $signedPreKeyId, ')
..write('signedPreKey: $signedPreKey, ')
..write('createdAt: $createdAt')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(
signedPreKeyId,
$driftBlobEquality.hash(signedPreKey),
createdAt,
);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is SignalSignedPreKeyStore &&
other.signedPreKeyId == this.signedPreKeyId &&
$driftBlobEquality.equals(other.signedPreKey, this.signedPreKey) &&
other.createdAt == this.createdAt);
}
class SignalSignedPreKeyStoresCompanion
extends UpdateCompanion<SignalSignedPreKeyStore> {
final Value<int> signedPreKeyId;
final Value<Uint8List> signedPreKey;
final Value<DateTime> createdAt;
const SignalSignedPreKeyStoresCompanion({
this.signedPreKeyId = const Value.absent(),
this.signedPreKey = const Value.absent(),
this.createdAt = const Value.absent(),
});
SignalSignedPreKeyStoresCompanion.insert({
this.signedPreKeyId = const Value.absent(),
required Uint8List signedPreKey,
this.createdAt = const Value.absent(),
}) : signedPreKey = Value(signedPreKey);
static Insertable<SignalSignedPreKeyStore> custom({
Expression<int>? signedPreKeyId,
Expression<Uint8List>? signedPreKey,
Expression<DateTime>? createdAt,
}) {
return RawValuesInsertable({
if (signedPreKeyId != null) 'signed_pre_key_id': signedPreKeyId,
if (signedPreKey != null) 'signed_pre_key': signedPreKey,
if (createdAt != null) 'created_at': createdAt,
});
}
SignalSignedPreKeyStoresCompanion copyWith({
Value<int>? signedPreKeyId,
Value<Uint8List>? signedPreKey,
Value<DateTime>? createdAt,
}) {
return SignalSignedPreKeyStoresCompanion(
signedPreKeyId: signedPreKeyId ?? this.signedPreKeyId,
signedPreKey: signedPreKey ?? this.signedPreKey,
createdAt: createdAt ?? this.createdAt,
);
}
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
if (signedPreKeyId.present) {
map['signed_pre_key_id'] = Variable<int>(signedPreKeyId.value);
}
if (signedPreKey.present) {
map['signed_pre_key'] = Variable<Uint8List>(signedPreKey.value);
}
if (createdAt.present) {
map['created_at'] = Variable<DateTime>(createdAt.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('SignalSignedPreKeyStoresCompanion(')
..write('signedPreKeyId: $signedPreKeyId, ')
..write('signedPreKey: $signedPreKey, ')
..write('createdAt: $createdAt')
..write(')'))
.toString();
}
}
class $MessageActionsTable extends MessageActions class $MessageActionsTable extends MessageActions
with TableInfo<$MessageActionsTable, MessageAction> { with TableInfo<$MessageActionsTable, MessageAction> {
@override @override
@ -12118,6 +12397,8 @@ abstract class _$TwonlyDB extends GeneratedDatabase {
$SignalSenderKeyStoresTable(this); $SignalSenderKeyStoresTable(this);
late final $SignalSessionStoresTable signalSessionStores = late final $SignalSessionStoresTable signalSessionStores =
$SignalSessionStoresTable(this); $SignalSessionStoresTable(this);
late final $SignalSignedPreKeyStoresTable signalSignedPreKeyStores =
$SignalSignedPreKeyStoresTable(this);
late final $MessageActionsTable messageActions = $MessageActionsTable(this); late final $MessageActionsTable messageActions = $MessageActionsTable(this);
late final $GroupHistoriesTable groupHistories = $GroupHistoriesTable(this); late final $GroupHistoriesTable groupHistories = $GroupHistoriesTable(this);
late final $KeyVerificationsTable keyVerifications = $KeyVerificationsTable( late final $KeyVerificationsTable keyVerifications = $KeyVerificationsTable(
@ -12170,6 +12451,7 @@ abstract class _$TwonlyDB extends GeneratedDatabase {
signalPreKeyStores, signalPreKeyStores,
signalSenderKeyStores, signalSenderKeyStores,
signalSessionStores, signalSessionStores,
signalSignedPreKeyStores,
messageActions, messageActions,
groupHistories, groupHistories,
keyVerifications, keyVerifications,
@ -19540,6 +19822,185 @@ typedef $$SignalSessionStoresTableProcessedTableManager =
SignalSessionStore, SignalSessionStore,
PrefetchHooks Function() PrefetchHooks Function()
>; >;
typedef $$SignalSignedPreKeyStoresTableCreateCompanionBuilder =
SignalSignedPreKeyStoresCompanion Function({
Value<int> signedPreKeyId,
required Uint8List signedPreKey,
Value<DateTime> createdAt,
});
typedef $$SignalSignedPreKeyStoresTableUpdateCompanionBuilder =
SignalSignedPreKeyStoresCompanion Function({
Value<int> signedPreKeyId,
Value<Uint8List> signedPreKey,
Value<DateTime> createdAt,
});
class $$SignalSignedPreKeyStoresTableFilterComposer
extends Composer<_$TwonlyDB, $SignalSignedPreKeyStoresTable> {
$$SignalSignedPreKeyStoresTableFilterComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
ColumnFilters<int> get signedPreKeyId => $composableBuilder(
column: $table.signedPreKeyId,
builder: (column) => ColumnFilters(column),
);
ColumnFilters<Uint8List> get signedPreKey => $composableBuilder(
column: $table.signedPreKey,
builder: (column) => ColumnFilters(column),
);
ColumnFilters<DateTime> get createdAt => $composableBuilder(
column: $table.createdAt,
builder: (column) => ColumnFilters(column),
);
}
class $$SignalSignedPreKeyStoresTableOrderingComposer
extends Composer<_$TwonlyDB, $SignalSignedPreKeyStoresTable> {
$$SignalSignedPreKeyStoresTableOrderingComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
ColumnOrderings<int> get signedPreKeyId => $composableBuilder(
column: $table.signedPreKeyId,
builder: (column) => ColumnOrderings(column),
);
ColumnOrderings<Uint8List> get signedPreKey => $composableBuilder(
column: $table.signedPreKey,
builder: (column) => ColumnOrderings(column),
);
ColumnOrderings<DateTime> get createdAt => $composableBuilder(
column: $table.createdAt,
builder: (column) => ColumnOrderings(column),
);
}
class $$SignalSignedPreKeyStoresTableAnnotationComposer
extends Composer<_$TwonlyDB, $SignalSignedPreKeyStoresTable> {
$$SignalSignedPreKeyStoresTableAnnotationComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
GeneratedColumn<int> get signedPreKeyId => $composableBuilder(
column: $table.signedPreKeyId,
builder: (column) => column,
);
GeneratedColumn<Uint8List> get signedPreKey => $composableBuilder(
column: $table.signedPreKey,
builder: (column) => column,
);
GeneratedColumn<DateTime> get createdAt =>
$composableBuilder(column: $table.createdAt, builder: (column) => column);
}
class $$SignalSignedPreKeyStoresTableTableManager
extends
RootTableManager<
_$TwonlyDB,
$SignalSignedPreKeyStoresTable,
SignalSignedPreKeyStore,
$$SignalSignedPreKeyStoresTableFilterComposer,
$$SignalSignedPreKeyStoresTableOrderingComposer,
$$SignalSignedPreKeyStoresTableAnnotationComposer,
$$SignalSignedPreKeyStoresTableCreateCompanionBuilder,
$$SignalSignedPreKeyStoresTableUpdateCompanionBuilder,
(
SignalSignedPreKeyStore,
BaseReferences<
_$TwonlyDB,
$SignalSignedPreKeyStoresTable,
SignalSignedPreKeyStore
>,
),
SignalSignedPreKeyStore,
PrefetchHooks Function()
> {
$$SignalSignedPreKeyStoresTableTableManager(
_$TwonlyDB db,
$SignalSignedPreKeyStoresTable table,
) : super(
TableManagerState(
db: db,
table: table,
createFilteringComposer: () =>
$$SignalSignedPreKeyStoresTableFilterComposer(
$db: db,
$table: table,
),
createOrderingComposer: () =>
$$SignalSignedPreKeyStoresTableOrderingComposer(
$db: db,
$table: table,
),
createComputedFieldComposer: () =>
$$SignalSignedPreKeyStoresTableAnnotationComposer(
$db: db,
$table: table,
),
updateCompanionCallback:
({
Value<int> signedPreKeyId = const Value.absent(),
Value<Uint8List> signedPreKey = const Value.absent(),
Value<DateTime> createdAt = const Value.absent(),
}) => SignalSignedPreKeyStoresCompanion(
signedPreKeyId: signedPreKeyId,
signedPreKey: signedPreKey,
createdAt: createdAt,
),
createCompanionCallback:
({
Value<int> signedPreKeyId = const Value.absent(),
required Uint8List signedPreKey,
Value<DateTime> createdAt = const Value.absent(),
}) => SignalSignedPreKeyStoresCompanion.insert(
signedPreKeyId: signedPreKeyId,
signedPreKey: signedPreKey,
createdAt: createdAt,
),
withReferenceMapper: (p0) => p0
.map((e) => (e.readTable(table), BaseReferences(db, table, e)))
.toList(),
prefetchHooksCallback: null,
),
);
}
typedef $$SignalSignedPreKeyStoresTableProcessedTableManager =
ProcessedTableManager<
_$TwonlyDB,
$SignalSignedPreKeyStoresTable,
SignalSignedPreKeyStore,
$$SignalSignedPreKeyStoresTableFilterComposer,
$$SignalSignedPreKeyStoresTableOrderingComposer,
$$SignalSignedPreKeyStoresTableAnnotationComposer,
$$SignalSignedPreKeyStoresTableCreateCompanionBuilder,
$$SignalSignedPreKeyStoresTableUpdateCompanionBuilder,
(
SignalSignedPreKeyStore,
BaseReferences<
_$TwonlyDB,
$SignalSignedPreKeyStoresTable,
SignalSignedPreKeyStore
>,
),
SignalSignedPreKeyStore,
PrefetchHooks Function()
>;
typedef $$MessageActionsTableCreateCompanionBuilder = typedef $$MessageActionsTableCreateCompanionBuilder =
MessageActionsCompanion Function({ MessageActionsCompanion Function({
required String messageId, required String messageId,
@ -23335,6 +23796,11 @@ class $TwonlyDBManager {
$$SignalSenderKeyStoresTableTableManager(_db, _db.signalSenderKeyStores); $$SignalSenderKeyStoresTableTableManager(_db, _db.signalSenderKeyStores);
$$SignalSessionStoresTableTableManager get signalSessionStores => $$SignalSessionStoresTableTableManager get signalSessionStores =>
$$SignalSessionStoresTableTableManager(_db, _db.signalSessionStores); $$SignalSessionStoresTableTableManager(_db, _db.signalSessionStores);
$$SignalSignedPreKeyStoresTableTableManager get signalSignedPreKeyStores =>
$$SignalSignedPreKeyStoresTableTableManager(
_db,
_db.signalSignedPreKeyStores,
);
$$MessageActionsTableTableManager get messageActions => $$MessageActionsTableTableManager get messageActions =>
$$MessageActionsTableTableManager(_db, _db.messageActions); $$MessageActionsTableTableManager(_db, _db.messageActions);
$$GroupHistoriesTableTableManager get groupHistories => $$GroupHistoriesTableTableManager get groupHistories =>

View file

@ -7561,6 +7561,477 @@ i1.GeneratedColumn<String> _column_241(String aliasedName) =>
type: i1.DriftSqlType.string, type: i1.DriftSqlType.string,
$customConstraints: 'NULL', $customConstraints: 'NULL',
); );
final class Schema15 extends i0.VersionedSchema {
Schema15({required super.database}) : super(version: 15);
@override
late final List<i1.DatabaseSchemaEntity> entities = [
contacts,
groups,
mediaFiles,
messages,
messageHistories,
reactions,
groupMembers,
receipts,
receivedReceipts,
signalIdentityKeyStores,
signalPreKeyStores,
signalSenderKeyStores,
signalSessionStores,
signalSignedPreKeyStores,
messageActions,
groupHistories,
keyVerifications,
verificationTokens,
userDiscoveryAnnouncedUsers,
userDiscoveryUserRelations,
userDiscoveryOtherPromotions,
userDiscoveryOwnPromotions,
userDiscoveryShares,
shortcuts,
shortcutMembers,
];
late final Shape39 contacts = Shape39(
source: i0.VersionedTable(
entityName: 'contacts',
withoutRowId: false,
isStrict: false,
tableConstraints: ['PRIMARY KEY(user_id)'],
columns: [
_column_106,
_column_107,
_column_108,
_column_109,
_column_110,
_column_111,
_column_112,
_column_113,
_column_114,
_column_115,
_column_116,
_column_117,
_column_118,
_column_211,
_column_212,
_column_213,
_column_214,
_column_215,
],
attachedDatabase: database,
),
alias: null,
);
late final Shape23 groups = Shape23(
source: i0.VersionedTable(
entityName: 'groups',
withoutRowId: false,
isStrict: false,
tableConstraints: ['PRIMARY KEY(group_id)'],
columns: [
_column_119,
_column_120,
_column_121,
_column_122,
_column_123,
_column_124,
_column_125,
_column_126,
_column_127,
_column_128,
_column_129,
_column_130,
_column_131,
_column_132,
_column_133,
_column_134,
_column_118,
_column_135,
_column_136,
_column_137,
_column_138,
_column_139,
_column_140,
_column_141,
_column_142,
],
attachedDatabase: database,
),
alias: null,
);
late final Shape49 mediaFiles = Shape49(
source: i0.VersionedTable(
entityName: 'media_files',
withoutRowId: false,
isStrict: false,
tableConstraints: ['PRIMARY KEY(media_id)'],
columns: [
_column_143,
_column_144,
_column_145,
_column_146,
_column_147,
_column_148,
_column_149,
_column_239,
_column_240,
_column_207,
_column_150,
_column_151,
_column_152,
_column_153,
_column_154,
_column_155,
_column_156,
_column_157,
_column_118,
_column_241,
],
attachedDatabase: database,
),
alias: null,
);
late final Shape25 messages = Shape25(
source: i0.VersionedTable(
entityName: 'messages',
withoutRowId: false,
isStrict: false,
tableConstraints: ['PRIMARY KEY(message_id)'],
columns: [
_column_158,
_column_159,
_column_160,
_column_144,
_column_161,
_column_162,
_column_163,
_column_164,
_column_165,
_column_153,
_column_166,
_column_167,
_column_168,
_column_169,
_column_118,
_column_170,
_column_171,
_column_172,
],
attachedDatabase: database,
),
alias: null,
);
late final Shape26 messageHistories = Shape26(
source: i0.VersionedTable(
entityName: 'message_histories',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [
_column_173,
_column_174,
_column_175,
_column_161,
_column_118,
],
attachedDatabase: database,
),
alias: null,
);
late final Shape27 reactions = Shape27(
source: i0.VersionedTable(
entityName: 'reactions',
withoutRowId: false,
isStrict: false,
tableConstraints: ['PRIMARY KEY(message_id, sender_id, emoji)'],
columns: [_column_174, _column_176, _column_177, _column_118],
attachedDatabase: database,
),
alias: null,
);
late final Shape38 groupMembers = Shape38(
source: i0.VersionedTable(
entityName: 'group_members',
withoutRowId: false,
isStrict: false,
tableConstraints: ['PRIMARY KEY(group_id, contact_id)'],
columns: [
_column_158,
_column_178,
_column_179,
_column_180,
_column_209,
_column_210,
_column_181,
_column_118,
],
attachedDatabase: database,
),
alias: null,
);
late final Shape37 receipts = Shape37(
source: i0.VersionedTable(
entityName: 'receipts',
withoutRowId: false,
isStrict: false,
tableConstraints: ['PRIMARY KEY(receipt_id)'],
columns: [
_column_182,
_column_183,
_column_184,
_column_185,
_column_186,
_column_208,
_column_187,
_column_188,
_column_189,
_column_190,
_column_191,
_column_118,
],
attachedDatabase: database,
),
alias: null,
);
late final Shape30 receivedReceipts = Shape30(
source: i0.VersionedTable(
entityName: 'received_receipts',
withoutRowId: false,
isStrict: false,
tableConstraints: ['PRIMARY KEY(receipt_id)'],
columns: [_column_182, _column_118],
attachedDatabase: database,
),
alias: null,
);
late final Shape31 signalIdentityKeyStores = Shape31(
source: i0.VersionedTable(
entityName: 'signal_identity_key_stores',
withoutRowId: false,
isStrict: false,
tableConstraints: ['PRIMARY KEY(device_id, name)'],
columns: [_column_192, _column_193, _column_194, _column_118],
attachedDatabase: database,
),
alias: null,
);
late final Shape32 signalPreKeyStores = Shape32(
source: i0.VersionedTable(
entityName: 'signal_pre_key_stores',
withoutRowId: false,
isStrict: false,
tableConstraints: ['PRIMARY KEY(pre_key_id)'],
columns: [_column_195, _column_196, _column_118],
attachedDatabase: database,
),
alias: null,
);
late final Shape11 signalSenderKeyStores = Shape11(
source: i0.VersionedTable(
entityName: 'signal_sender_key_stores',
withoutRowId: false,
isStrict: false,
tableConstraints: ['PRIMARY KEY(sender_key_name)'],
columns: [_column_197, _column_198],
attachedDatabase: database,
),
alias: null,
);
late final Shape33 signalSessionStores = Shape33(
source: i0.VersionedTable(
entityName: 'signal_session_stores',
withoutRowId: false,
isStrict: false,
tableConstraints: ['PRIMARY KEY(device_id, name)'],
columns: [_column_192, _column_193, _column_199, _column_118],
attachedDatabase: database,
),
alias: null,
);
late final Shape50 signalSignedPreKeyStores = Shape50(
source: i0.VersionedTable(
entityName: 'signal_signed_pre_key_stores',
withoutRowId: false,
isStrict: false,
tableConstraints: ['PRIMARY KEY(signed_pre_key_id)'],
columns: [_column_242, _column_243, _column_118],
attachedDatabase: database,
),
alias: null,
);
late final Shape34 messageActions = Shape34(
source: i0.VersionedTable(
entityName: 'message_actions',
withoutRowId: false,
isStrict: false,
tableConstraints: ['PRIMARY KEY(message_id, contact_id, type)'],
columns: [_column_174, _column_183, _column_144, _column_200],
attachedDatabase: database,
),
alias: null,
);
late final Shape35 groupHistories = Shape35(
source: i0.VersionedTable(
entityName: 'group_histories',
withoutRowId: false,
isStrict: false,
tableConstraints: ['PRIMARY KEY(group_history_id)'],
columns: [
_column_201,
_column_158,
_column_202,
_column_203,
_column_204,
_column_205,
_column_206,
_column_144,
_column_200,
],
attachedDatabase: database,
),
alias: null,
);
late final Shape40 keyVerifications = Shape40(
source: i0.VersionedTable(
entityName: 'key_verifications',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [_column_216, _column_183, _column_144, _column_118],
attachedDatabase: database,
),
alias: null,
);
late final Shape41 verificationTokens = Shape41(
source: i0.VersionedTable(
entityName: 'verification_tokens',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [_column_217, _column_218, _column_118],
attachedDatabase: database,
),
alias: null,
);
late final Shape42 userDiscoveryAnnouncedUsers = Shape42(
source: i0.VersionedTable(
entityName: 'user_discovery_announced_users',
withoutRowId: false,
isStrict: false,
tableConstraints: ['PRIMARY KEY(announced_user_id)'],
columns: [
_column_219,
_column_220,
_column_221,
_column_222,
_column_223,
_column_224,
],
attachedDatabase: database,
),
alias: null,
);
late final Shape43 userDiscoveryUserRelations = Shape43(
source: i0.VersionedTable(
entityName: 'user_discovery_user_relations',
withoutRowId: false,
isStrict: false,
tableConstraints: ['PRIMARY KEY(announced_user_id, from_contact_id)'],
columns: [_column_225, _column_226, _column_227],
attachedDatabase: database,
),
alias: null,
);
late final Shape44 userDiscoveryOtherPromotions = Shape44(
source: i0.VersionedTable(
entityName: 'user_discovery_other_promotions',
withoutRowId: false,
isStrict: false,
tableConstraints: ['PRIMARY KEY(from_contact_id, public_id)'],
columns: [
_column_226,
_column_228,
_column_229,
_column_230,
_column_231,
_column_227,
],
attachedDatabase: database,
),
alias: null,
);
late final Shape45 userDiscoveryOwnPromotions = Shape45(
source: i0.VersionedTable(
entityName: 'user_discovery_own_promotions',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [_column_232, _column_183, _column_233],
attachedDatabase: database,
),
alias: null,
);
late final Shape46 userDiscoveryShares = Shape46(
source: i0.VersionedTable(
entityName: 'user_discovery_shares',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [_column_234, _column_235, _column_175],
attachedDatabase: database,
),
alias: null,
);
late final Shape47 shortcuts = Shape47(
source: i0.VersionedTable(
entityName: 'shortcuts',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [_column_173, _column_236, _column_237],
attachedDatabase: database,
),
alias: null,
);
late final Shape48 shortcutMembers = Shape48(
source: i0.VersionedTable(
entityName: 'shortcut_members',
withoutRowId: false,
isStrict: false,
tableConstraints: ['PRIMARY KEY(shortcut_id, group_id)'],
columns: [_column_238, _column_158],
attachedDatabase: database,
),
alias: null,
);
}
class Shape50 extends i0.VersionedTable {
Shape50({required super.source, required super.alias}) : super.aliased();
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<int> get createdAt =>
columnsByName['created_at']! as i1.GeneratedColumn<int>;
}
i1.GeneratedColumn<int> _column_242(String aliasedName) =>
i1.GeneratedColumn<int>(
'signed_pre_key_id',
aliasedName,
false,
type: i1.DriftSqlType.int,
$customConstraints: 'NOT NULL',
);
i1.GeneratedColumn<i2.Uint8List> _column_243(String aliasedName) =>
i1.GeneratedColumn<i2.Uint8List>(
'signed_pre_key',
aliasedName,
false,
type: i1.DriftSqlType.blob,
$customConstraints: 'NOT NULL',
);
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,
@ -7575,6 +8046,7 @@ i0.MigrationStepWithVersion migrationSteps({
required Future<void> Function(i1.Migrator m, Schema12 schema) from11To12, required Future<void> Function(i1.Migrator m, Schema12 schema) from11To12,
required Future<void> Function(i1.Migrator m, Schema13 schema) from12To13, required Future<void> Function(i1.Migrator m, Schema13 schema) from12To13,
required Future<void> Function(i1.Migrator m, Schema14 schema) from13To14, required Future<void> Function(i1.Migrator m, Schema14 schema) from13To14,
required Future<void> Function(i1.Migrator m, Schema15 schema) from14To15,
}) { }) {
return (currentVersion, database) async { return (currentVersion, database) async {
switch (currentVersion) { switch (currentVersion) {
@ -7643,6 +8115,11 @@ i0.MigrationStepWithVersion migrationSteps({
final migrator = i1.Migrator(database, schema); final migrator = i1.Migrator(database, schema);
await from13To14(migrator, schema); await from13To14(migrator, schema);
return 14; return 14;
case 14:
final schema = Schema15(database: database);
final migrator = i1.Migrator(database, schema);
await from14To15(migrator, schema);
return 15;
default: default:
throw ArgumentError.value('Unknown migration from $currentVersion'); throw ArgumentError.value('Unknown migration from $currentVersion');
} }
@ -7663,6 +8140,7 @@ i1.OnUpgrade stepByStep({
required Future<void> Function(i1.Migrator m, Schema12 schema) from11To12, required Future<void> Function(i1.Migrator m, Schema12 schema) from11To12,
required Future<void> Function(i1.Migrator m, Schema13 schema) from12To13, required Future<void> Function(i1.Migrator m, Schema13 schema) from12To13,
required Future<void> Function(i1.Migrator m, Schema14 schema) from13To14, required Future<void> Function(i1.Migrator m, Schema14 schema) from13To14,
required Future<void> Function(i1.Migrator m, Schema15 schema) from14To15,
}) => i0.VersionedSchema.stepByStepHelper( }) => i0.VersionedSchema.stepByStepHelper(
step: migrationSteps( step: migrationSteps(
from1To2: from1To2, from1To2: from1To2,
@ -7678,5 +8156,6 @@ i1.OnUpgrade stepByStep({
from11To12: from11To12, from11To12: from11To12,
from12To13: from12To13, from12To13: from12To13,
from13To14: from13To14, from13To14: from13To14,
from14To15: from14To15,
), ),
); );

View file

@ -3,6 +3,7 @@ import 'package:clock/clock.dart';
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart'; import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
import 'package:twonly/core/bridge/wrapper/key_manager.dart'; import 'package:twonly/core/bridge/wrapper/key_manager.dart';
import 'package:twonly/locator.dart'; import 'package:twonly/locator.dart';
import 'package:twonly/src/database/signal/signal_signed_pre_key_store.dart';
import 'package:twonly/src/model/json/signal_identity.model.dart'; import 'package:twonly/src/model/json/signal_identity.model.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/protocol_state.signal.dart'; import 'package:twonly/src/services/signal/protocol_state.signal.dart';
@ -93,19 +94,38 @@ Future<Uint8List> getUserPublicKey() async {
Future<void> createIfNotExistsSignalIdentity() async { Future<void> createIfNotExistsSignalIdentity() async {
// check if identity already exists // check if identity already exists
if (await getSignalIdentity() != null) return; final existingIdentity = await getSignalIdentity();
if (existingIdentity != null) {
final store = await getSignalStoreFromIdentity(existingIdentity);
final keys = await store.loadSignedPreKeys();
if (keys.isEmpty) {
Log.warn(
'Signal identity exists but signed prekeys are missing. Generating a new one.',
);
final keyPair = await store.getIdentityKeyPair();
final signedPreKey = generateSignedPreKey(keyPair, defaultDeviceId);
await SignalSignedPreKeyStore().storeSignedPreKey(
signedPreKey.id,
signedPreKey,
);
}
return;
}
final identityKeyPair = generateIdentityKeyPair(); final identityKeyPair = generateIdentityKeyPair();
final registrationId = generateRegistrationId(true); final registrationId = generateRegistrationId(true);
final signedPreKey = generateSignedPreKey(identityKeyPair, defaultDeviceId); final signedPreKey = generateSignedPreKey(identityKeyPair, defaultDeviceId);
final signedPreKeyStore = <int, Uint8List>{};
signedPreKeyStore[signedPreKey.id] = signedPreKey.serialize(); await SignalSignedPreKeyStore().storeSignedPreKey(
signedPreKey.id,
signedPreKey,
);
await RustKeyManager.importSignalIdentity( await RustKeyManager.importSignalIdentity(
identityKeyPairStructure: identityKeyPair.serialize(), identityKeyPairStructure: identityKeyPair.serialize(),
registrationId: registrationId, registrationId: registrationId,
signedPreKeyStore: signedPreKeyStore, signedPreKeyStore: const {},
); );
} }

View file

@ -146,11 +146,6 @@ class MainCameraController {
); );
try { try {
await cameraController?.initialize(); await cameraController?.initialize();
} catch (e) {
Log.error(e);
cameraController = null; // ensure uninitialized controller is not reused
return;
}
await cameraController?.startImageStream(_processCameraImage); await cameraController?.startImageStream(_processCameraImage);
await cameraController?.setZoomLevel(selectedCameraDetails.scaleFactor); await cameraController?.setZoomLevel(selectedCameraDetails.scaleFactor);
if (userService.currentUser.videoStabilizationEnabled && !kDebugMode) { if (userService.currentUser.videoStabilizationEnabled && !kDebugMode) {
@ -158,6 +153,10 @@ class MainCameraController {
VideoStabilizationMode.level1, VideoStabilizationMode.level1,
); );
} }
} catch (e) {
cameraController = null;
return;
}
} else { } else {
try { try {
if (!isVideoRecording) { if (!isVideoRecording) {
@ -179,6 +178,7 @@ class MainCameraController {
} }
} }
try {
await cameraController?.lockCaptureOrientation( await cameraController?.lockCaptureOrientation(
DeviceOrientation.portraitUp, DeviceOrientation.portraitUp,
); );
@ -202,6 +202,11 @@ class MainCameraController {
setFilter(FaceFilterType.none); setFilter(FaceFilterType.none);
zoomButtonKey = GlobalKey(); zoomButtonKey = GlobalKey();
setState(); setState();
} catch (e) {
Log.error(e);
cameraController = null;
return;
}
} }
Future<void> onDoubleTap() async { Future<void> onDoubleTap() async {

View file

@ -14,11 +14,13 @@ class MemoriesFlashbackBannerComp extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (lastYears.isEmpty) return const SliverToBoxAdapter(child: SizedBox.shrink()); if (lastYears.isEmpty) {
return const SliverToBoxAdapter(child: SizedBox.shrink());
}
return SliverToBoxAdapter( return SliverToBoxAdapter(
child: Padding( child: Padding(
padding: const EdgeInsets.fromLTRB(16, 8, 16, 16), padding: const EdgeInsets.fromLTRB(0, 8, 0, 16),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -27,9 +29,13 @@ class MemoriesFlashbackBannerComp extends StatelessWidget {
child: ListView.separated( child: ListView.separated(
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
physics: const BouncingScrollPhysics(), physics: const BouncingScrollPhysics(),
itemCount: lastYears.length, itemCount: lastYears.length + 1,
separatorBuilder: (context, _) => const SizedBox(width: 12), separatorBuilder: (context, _) => const SizedBox(width: 12),
itemBuilder: (context, idx) { itemBuilder: (context, idx) {
if (idx == 0) {
return const SizedBox.shrink();
}
idx -= 1;
final entry = lastYears.entries.elementAt(idx); final entry = lastYears.entries.elementAt(idx);
final years = entry.key; final years = entry.key;
final items = entry.value; final items = entry.value;

View file

@ -430,9 +430,7 @@ class MemoriesViewState extends State<MemoriesView> {
), ),
), ),
), ),
SliverPadding( SliverGrid(
padding: const EdgeInsets.symmetric(horizontal: 4),
sliver: SliverGrid(
gridDelegate: gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount( const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4, crossAxisCount: 4,
@ -444,8 +442,7 @@ class MemoriesViewState extends State<MemoriesView> {
(context, idx) { (context, idx) {
final globalIndex = orderedByMonth[month]![idx]; final globalIndex = orderedByMonth[month]![idx];
final item = state.galleryItems[globalIndex]; final item = state.galleryItems[globalIndex];
final mediaId = final mediaId = item.mediaService.mediaFile.mediaId;
item.mediaService.mediaFile.mediaId;
final isSelected = _selectedMediaIds.contains( final isSelected = _selectedMediaIds.contains(
mediaId, mediaId,
); );
@ -463,7 +460,6 @@ class MemoriesViewState extends State<MemoriesView> {
childCount: orderedByMonth[month]!.length, childCount: orderedByMonth[month]!.length,
), ),
), ),
),
], ],
const SliverPadding(padding: EdgeInsets.only(bottom: 32)), const SliverPadding(padding: EdgeInsets.only(bottom: 32)),
], ],

View file

@ -18,6 +18,7 @@ import 'schema_v11.dart' as v11;
import 'schema_v12.dart' as v12; import 'schema_v12.dart' as v12;
import 'schema_v13.dart' as v13; import 'schema_v13.dart' as v13;
import 'schema_v14.dart' as v14; import 'schema_v14.dart' as v14;
import 'schema_v15.dart' as v15;
class GeneratedHelper implements SchemaInstantiationHelper { class GeneratedHelper implements SchemaInstantiationHelper {
@override @override
@ -51,10 +52,28 @@ class GeneratedHelper implements SchemaInstantiationHelper {
return v13.DatabaseAtV13(db); return v13.DatabaseAtV13(db);
case 14: case 14:
return v14.DatabaseAtV14(db); return v14.DatabaseAtV14(db);
case 15:
return v15.DatabaseAtV15(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, 12, 13, 14]; static const versions = const [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
];
} }

File diff suppressed because it is too large Load diff