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 allowErrorTrackingViaSentry = 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);
}
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) {
assert(
AppState.latestAppVersionId == 114,
AppState.latestAppVersionId == 115,
'Forgot to update the target version in runMigrations() after incrementing AppState.latestAppVersionId.',
);
assert(

File diff suppressed because it is too large Load diff

View file

@ -1,10 +1,11 @@
import 'dart:collection';
import 'dart:convert';
import 'dart:typed_data';
import 'package:drift/drift.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/database/twonly.db.dart';
import 'package:twonly/src/utils/log.dart';
import 'package:twonly/src/utils/secure_storage.dart';
Future<HashMap<int, Uint8List>> getSignalSignedPreKeyStoreOld() async {
@ -26,25 +27,25 @@ Future<HashMap<int, Uint8List>> getSignalSignedPreKeyStoreOld() async {
class SignalSignedPreKeyStore extends SignedPreKeyStore {
@override
Future<SignedPreKeyRecord> loadSignedPreKey(int signedPreKeyId) async {
final store = await RustKeyManager.loadSignedPrekey(
signedPreKeyId: signedPreKeyId,
);
if (store == null) {
final record = await (twonlyDB.select(
twonlyDB.signalSignedPreKeyStores,
)..where((tbl) => tbl.signedPreKeyId.equals(signedPreKeyId))).get();
if (record.isEmpty) {
throw InvalidKeyIdException(
'No such signed prekey record! $signedPreKeyId',
);
}
return SignedPreKeyRecord.fromSerialized(store);
return SignedPreKeyRecord.fromSerialized(record.first.signedPreKey);
}
@override
Future<List<SignedPreKeyRecord>> loadSignedPreKeys() async {
final store = await RustKeyManager.loadSignedPrekeys();
final results = <SignedPreKeyRecord>[];
for (final serialized in store.values) {
results.add(SignedPreKeyRecord.fromSerialized(serialized));
}
return results;
final records = await twonlyDB
.select(twonlyDB.signalSignedPreKeyStores)
.get();
return records
.map((r) => SignedPreKeyRecord.fromSerialized(r.signedPreKey))
.toList();
}
@override
@ -52,21 +53,32 @@ class SignalSignedPreKeyStore extends SignedPreKeyStore {
int signedPreKeyId,
SignedPreKeyRecord record,
) async {
await RustKeyManager.storeSignedPrekey(
signedPreKeyId: signedPreKeyId,
record: record.serialize(),
final companion = SignalSignedPreKeyStoresCompanion(
signedPreKeyId: Value(signedPreKeyId),
signedPreKey: Value(record.serialize()),
);
try {
await twonlyDB
.into(twonlyDB.signalSignedPreKeyStores)
.insert(companion, mode: InsertMode.insertOrReplace);
} catch (e) {
Log.error('$e');
}
}
@override
Future<bool> containsSignedPreKey(int signedPreKeyId) async =>
await RustKeyManager.loadSignedPrekey(
signedPreKeyId: signedPreKeyId,
) !=
null;
Future<bool> containsSignedPreKey(int signedPreKeyId) async {
final record = await (twonlyDB.select(
twonlyDB.signalSignedPreKeyStores,
)..where((tbl) => tbl.signedPreKeyId.equals(signedPreKeyId))).get();
return record.isNotEmpty;
}
@override
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_sender_key_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/twonly.db.steps.dart';
import 'package:twonly/src/utils/log.dart';
@ -45,6 +46,7 @@ part 'twonly.db.g.dart';
SignalPreKeyStores,
SignalSenderKeyStores,
SignalSessionStores,
SignalSignedPreKeyStores,
MessageActions,
GroupHistories,
KeyVerifications,
@ -79,7 +81,7 @@ class TwonlyDB extends _$TwonlyDB {
TwonlyDB.forTesting(DatabaseConnection super.connection);
@override
int get schemaVersion => 14;
int get schemaVersion => 15;
static QueryExecutor _openConnection() {
return driftDatabase(
@ -206,6 +208,9 @@ class TwonlyDB extends _$TwonlyDB {
schema.mediaFiles.hasCropAnalyzed,
);
},
from14To15: (m, schema) async {
await m.createTable(schema.signalSignedPreKeyStores);
},
)(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
with TableInfo<$MessageActionsTable, MessageAction> {
@override
@ -12118,6 +12397,8 @@ abstract class _$TwonlyDB extends GeneratedDatabase {
$SignalSenderKeyStoresTable(this);
late final $SignalSessionStoresTable signalSessionStores =
$SignalSessionStoresTable(this);
late final $SignalSignedPreKeyStoresTable signalSignedPreKeyStores =
$SignalSignedPreKeyStoresTable(this);
late final $MessageActionsTable messageActions = $MessageActionsTable(this);
late final $GroupHistoriesTable groupHistories = $GroupHistoriesTable(this);
late final $KeyVerificationsTable keyVerifications = $KeyVerificationsTable(
@ -12170,6 +12451,7 @@ abstract class _$TwonlyDB extends GeneratedDatabase {
signalPreKeyStores,
signalSenderKeyStores,
signalSessionStores,
signalSignedPreKeyStores,
messageActions,
groupHistories,
keyVerifications,
@ -19540,6 +19822,185 @@ typedef $$SignalSessionStoresTableProcessedTableManager =
SignalSessionStore,
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 =
MessageActionsCompanion Function({
required String messageId,
@ -23335,6 +23796,11 @@ class $TwonlyDBManager {
$$SignalSenderKeyStoresTableTableManager(_db, _db.signalSenderKeyStores);
$$SignalSessionStoresTableTableManager get signalSessionStores =>
$$SignalSessionStoresTableTableManager(_db, _db.signalSessionStores);
$$SignalSignedPreKeyStoresTableTableManager get signalSignedPreKeyStores =>
$$SignalSignedPreKeyStoresTableTableManager(
_db,
_db.signalSignedPreKeyStores,
);
$$MessageActionsTableTableManager get messageActions =>
$$MessageActionsTableTableManager(_db, _db.messageActions);
$$GroupHistoriesTableTableManager get groupHistories =>

View file

@ -7561,6 +7561,477 @@ i1.GeneratedColumn<String> _column_241(String aliasedName) =>
type: i1.DriftSqlType.string,
$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({
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
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, Schema13 schema) from12To13,
required Future<void> Function(i1.Migrator m, Schema14 schema) from13To14,
required Future<void> Function(i1.Migrator m, Schema15 schema) from14To15,
}) {
return (currentVersion, database) async {
switch (currentVersion) {
@ -7643,6 +8115,11 @@ i0.MigrationStepWithVersion migrationSteps({
final migrator = i1.Migrator(database, schema);
await from13To14(migrator, schema);
return 14;
case 14:
final schema = Schema15(database: database);
final migrator = i1.Migrator(database, schema);
await from14To15(migrator, schema);
return 15;
default:
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, Schema13 schema) from12To13,
required Future<void> Function(i1.Migrator m, Schema14 schema) from13To14,
required Future<void> Function(i1.Migrator m, Schema15 schema) from14To15,
}) => i0.VersionedSchema.stepByStepHelper(
step: migrationSteps(
from1To2: from1To2,
@ -7678,5 +8156,6 @@ i1.OnUpgrade stepByStep({
from11To12: from11To12,
from12To13: from12To13,
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:twonly/core/bridge/wrapper/key_manager.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/services/signal/consts.signal.dart';
import 'package:twonly/src/services/signal/protocol_state.signal.dart';
@ -93,19 +94,38 @@ Future<Uint8List> getUserPublicKey() async {
Future<void> createIfNotExistsSignalIdentity() async {
// 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 registrationId = generateRegistrationId(true);
final signedPreKey = generateSignedPreKey(identityKeyPair, defaultDeviceId);
final signedPreKeyStore = <int, Uint8List>{};
signedPreKeyStore[signedPreKey.id] = signedPreKey.serialize();
await SignalSignedPreKeyStore().storeSignedPreKey(
signedPreKey.id,
signedPreKey,
);
await RustKeyManager.importSignalIdentity(
identityKeyPairStructure: identityKeyPair.serialize(),
registrationId: registrationId,
signedPreKeyStore: signedPreKeyStore,
signedPreKeyStore: const {},
);
}

View file

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

View file

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

View file

@ -430,9 +430,7 @@ class MemoriesViewState extends State<MemoriesView> {
),
),
),
SliverPadding(
padding: const EdgeInsets.symmetric(horizontal: 4),
sliver: SliverGrid(
SliverGrid(
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4,
@ -444,8 +442,7 @@ class MemoriesViewState extends State<MemoriesView> {
(context, idx) {
final globalIndex = orderedByMonth[month]![idx];
final item = state.galleryItems[globalIndex];
final mediaId =
item.mediaService.mediaFile.mediaId;
final mediaId = item.mediaService.mediaFile.mediaId;
final isSelected = _selectedMediaIds.contains(
mediaId,
);
@ -463,7 +460,6 @@ class MemoriesViewState extends State<MemoriesView> {
childCount: orderedByMonth[month]!.length,
),
),
),
],
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_v13.dart' as v13;
import 'schema_v14.dart' as v14;
import 'schema_v15.dart' as v15;
class GeneratedHelper implements SchemaInstantiationHelper {
@override
@ -51,10 +52,28 @@ class GeneratedHelper implements SchemaInstantiationHelper {
return v13.DatabaseAtV13(db);
case 14:
return v14.DatabaseAtV14(db);
case 15:
return v15.DatabaseAtV15(db);
default:
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