This commit is contained in:
otsmr 2025-06-01 16:39:14 +02:00
parent 86d87b84ac
commit 190b16ec9a
27 changed files with 4013 additions and 158 deletions

File diff suppressed because one or more lines are too long

View file

@ -34,8 +34,10 @@ void main() async {
apiService = ApiService();
twonlyDB = TwonlyDatabase();
await twonlyDB.messagesDao.resetPendingDownloadState();
await purgeReceivedMediaFiles();
await purgeSendMediaFiles();
// purge media files in the background
purgeReceivedMediaFiles();
purgeSendMediaFiles();
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);

View file

@ -123,12 +123,22 @@ class ContactsDao extends DatabaseAccessor<TwonlyDatabase>
// return (select(contacts)).watch();
}
Stream<Contact> watchContact(int userid) {
Stream<Contact?> watchContact(int userid) {
return (select(contacts)..where((t) => t.userId.equals(userid)))
.watchSingle();
.watchSingleOrNull();
}
Stream<List<Contact>> watchContactsForShareView() {
return (select(contacts)
..where((t) =>
t.accepted.equals(true) &
t.blocked.equals(false) &
t.deleted.equals(false))
..orderBy([(t) => OrderingTerm.desc(t.lastMessageExchange)]))
.watch();
}
Stream<List<Contact>> watchContactsForStartNewChat() {
return (select(contacts)
..where((t) => t.accepted.equals(true) & t.blocked.equals(false))
..orderBy([(t) => OrderingTerm.desc(t.lastMessageExchange)]))
@ -190,13 +200,20 @@ class ContactsDao extends DatabaseAccessor<TwonlyDatabase>
}
String getContactDisplayName(Contact user) {
if (user.nickName != null) {
return user.nickName!;
String name = user.username;
if (user.nickName != null && user.nickName != "") {
name = user.nickName!;
} else if (user.displayName != null) {
name = user.displayName!;
}
if (user.displayName != null) {
return user.displayName!;
if (user.deleted) {
name = applyStrikethrough(name);
}
return user.username;
return name;
}
String applyStrikethrough(String text) {
return text.split('').map((char) => '$char\u0336').join('');
}
int getFlameCounterFromContact(Contact contact) {

View file

@ -186,6 +186,10 @@ class MessagesDao extends DatabaseAccessor<TwonlyDatabase>
.go();
}
Future deleteAllMessagesByContactId(int contactId) {
return (delete(messages)..where((t) => t.contactId.equals(contactId))).go();
}
Future<bool> containsOtherMessageId(
int fromUserId, int messageOtherId) async {
final query = select(messages)

View file

@ -11,6 +11,15 @@ class SignalDao extends DatabaseAccessor<TwonlyDatabase> with _$SignalDaoMixin {
// of this object.
SignalDao(super.db);
Future deleteAllByContactId(int contactId) async {
await (delete(signalContactPreKeys)
..where((t) => t.contactId.equals(contactId)))
.go();
await (delete(signalContactSignedPreKeys)
..where((t) => t.contactId.equals(contactId)))
.go();
}
// 1: Count the number of pre-keys by contact ID
Future<int> countPreKeysByContactId(int contactId) {
return (select(signalContactPreKeys)

View file

@ -16,6 +16,8 @@ class Contacts extends Table {
BoolColumn get verified => boolean().withDefault(Constant(false))();
BoolColumn get archived => boolean().withDefault(Constant(false))();
BoolColumn get pinned => boolean().withDefault(Constant(false))();
BoolColumn get deleted => boolean().withDefault(Constant(false))();
BoolColumn get alsoBestFriend => boolean().withDefault(Constant(false))();
IntColumn get deleteMessagesAfterXMinutes =>

View file

@ -63,6 +63,9 @@ class TwonlyDatabase extends _$TwonlyDatabase {
@override
MigrationStrategy get migration {
return MigrationStrategy(
beforeOpen: (details) async {
await customStatement('PRAGMA foreign_keys = ON');
},
onUpgrade: stepByStep(
from1To2: (m, schema) async {
m.addColumn(schema.messages, schema.messages.errorWhileSending);
@ -109,6 +112,7 @@ class TwonlyDatabase extends _$TwonlyDatabase {
from9To10: (m, schema) async {
m.createTable(signalContactPreKeys);
m.createTable(signalContactSignedPreKeys);
m.addColumn(schema.contacts, schema.contacts.deleted);
},
),
);

View file

@ -106,6 +106,16 @@ class $ContactsTable extends Contacts with TableInfo<$ContactsTable, Contact> {
defaultConstraints:
GeneratedColumn.constraintIsAlways('CHECK ("pinned" IN (0, 1))'),
defaultValue: Constant(false));
static const VerificationMeta _deletedMeta =
const VerificationMeta('deleted');
@override
late final GeneratedColumn<bool> deleted = GeneratedColumn<bool>(
'deleted', aliasedName, false,
type: DriftSqlType.bool,
requiredDuringInsert: false,
defaultConstraints:
GeneratedColumn.constraintIsAlways('CHECK ("deleted" IN (0, 1))'),
defaultValue: Constant(false));
static const VerificationMeta _alsoBestFriendMeta =
const VerificationMeta('alsoBestFriend');
@override
@ -195,6 +205,7 @@ class $ContactsTable extends Contacts with TableInfo<$ContactsTable, Contact> {
verified,
archived,
pinned,
deleted,
alsoBestFriend,
deleteMessagesAfterXMinutes,
createdAt,
@ -270,6 +281,10 @@ class $ContactsTable extends Contacts with TableInfo<$ContactsTable, Contact> {
context.handle(_pinnedMeta,
pinned.isAcceptableOrUnknown(data['pinned']!, _pinnedMeta));
}
if (data.containsKey('deleted')) {
context.handle(_deletedMeta,
deleted.isAcceptableOrUnknown(data['deleted']!, _deletedMeta));
}
if (data.containsKey('also_best_friend')) {
context.handle(
_alsoBestFriendMeta,
@ -362,6 +377,8 @@ class $ContactsTable extends Contacts with TableInfo<$ContactsTable, Contact> {
.read(DriftSqlType.bool, data['${effectivePrefix}archived'])!,
pinned: attachedDatabase.typeMapping
.read(DriftSqlType.bool, data['${effectivePrefix}pinned'])!,
deleted: attachedDatabase.typeMapping
.read(DriftSqlType.bool, data['${effectivePrefix}deleted'])!,
alsoBestFriend: attachedDatabase.typeMapping
.read(DriftSqlType.bool, data['${effectivePrefix}also_best_friend'])!,
deleteMessagesAfterXMinutes: attachedDatabase.typeMapping.read(
@ -408,6 +425,7 @@ class Contact extends DataClass implements Insertable<Contact> {
final bool verified;
final bool archived;
final bool pinned;
final bool deleted;
final bool alsoBestFriend;
final int deleteMessagesAfterXMinutes;
final DateTime createdAt;
@ -431,6 +449,7 @@ class Contact extends DataClass implements Insertable<Contact> {
required this.verified,
required this.archived,
required this.pinned,
required this.deleted,
required this.alsoBestFriend,
required this.deleteMessagesAfterXMinutes,
required this.createdAt,
@ -462,6 +481,7 @@ class Contact extends DataClass implements Insertable<Contact> {
map['verified'] = Variable<bool>(verified);
map['archived'] = Variable<bool>(archived);
map['pinned'] = Variable<bool>(pinned);
map['deleted'] = Variable<bool>(deleted);
map['also_best_friend'] = Variable<bool>(alsoBestFriend);
map['delete_messages_after_x_minutes'] =
Variable<int>(deleteMessagesAfterXMinutes);
@ -505,6 +525,7 @@ class Contact extends DataClass implements Insertable<Contact> {
verified: Value(verified),
archived: Value(archived),
pinned: Value(pinned),
deleted: Value(deleted),
alsoBestFriend: Value(alsoBestFriend),
deleteMessagesAfterXMinutes: Value(deleteMessagesAfterXMinutes),
createdAt: Value(createdAt),
@ -542,6 +563,7 @@ class Contact extends DataClass implements Insertable<Contact> {
verified: serializer.fromJson<bool>(json['verified']),
archived: serializer.fromJson<bool>(json['archived']),
pinned: serializer.fromJson<bool>(json['pinned']),
deleted: serializer.fromJson<bool>(json['deleted']),
alsoBestFriend: serializer.fromJson<bool>(json['alsoBestFriend']),
deleteMessagesAfterXMinutes:
serializer.fromJson<int>(json['deleteMessagesAfterXMinutes']),
@ -574,6 +596,7 @@ class Contact extends DataClass implements Insertable<Contact> {
'verified': serializer.toJson<bool>(verified),
'archived': serializer.toJson<bool>(archived),
'pinned': serializer.toJson<bool>(pinned),
'deleted': serializer.toJson<bool>(deleted),
'alsoBestFriend': serializer.toJson<bool>(alsoBestFriend),
'deleteMessagesAfterXMinutes':
serializer.toJson<int>(deleteMessagesAfterXMinutes),
@ -602,6 +625,7 @@ class Contact extends DataClass implements Insertable<Contact> {
bool? verified,
bool? archived,
bool? pinned,
bool? deleted,
bool? alsoBestFriend,
int? deleteMessagesAfterXMinutes,
DateTime? createdAt,
@ -625,6 +649,7 @@ class Contact extends DataClass implements Insertable<Contact> {
verified: verified ?? this.verified,
archived: archived ?? this.archived,
pinned: pinned ?? this.pinned,
deleted: deleted ?? this.deleted,
alsoBestFriend: alsoBestFriend ?? this.alsoBestFriend,
deleteMessagesAfterXMinutes:
deleteMessagesAfterXMinutes ?? this.deleteMessagesAfterXMinutes,
@ -661,6 +686,7 @@ class Contact extends DataClass implements Insertable<Contact> {
verified: data.verified.present ? data.verified.value : this.verified,
archived: data.archived.present ? data.archived.value : this.archived,
pinned: data.pinned.present ? data.pinned.value : this.pinned,
deleted: data.deleted.present ? data.deleted.value : this.deleted,
alsoBestFriend: data.alsoBestFriend.present
? data.alsoBestFriend.value
: this.alsoBestFriend,
@ -707,6 +733,7 @@ class Contact extends DataClass implements Insertable<Contact> {
..write('verified: $verified, ')
..write('archived: $archived, ')
..write('pinned: $pinned, ')
..write('deleted: $deleted, ')
..write('alsoBestFriend: $alsoBestFriend, ')
..write('deleteMessagesAfterXMinutes: $deleteMessagesAfterXMinutes, ')
..write('createdAt: $createdAt, ')
@ -735,6 +762,7 @@ class Contact extends DataClass implements Insertable<Contact> {
verified,
archived,
pinned,
deleted,
alsoBestFriend,
deleteMessagesAfterXMinutes,
createdAt,
@ -762,6 +790,7 @@ class Contact extends DataClass implements Insertable<Contact> {
other.verified == this.verified &&
other.archived == this.archived &&
other.pinned == this.pinned &&
other.deleted == this.deleted &&
other.alsoBestFriend == this.alsoBestFriend &&
other.deleteMessagesAfterXMinutes ==
this.deleteMessagesAfterXMinutes &&
@ -788,6 +817,7 @@ class ContactsCompanion extends UpdateCompanion<Contact> {
final Value<bool> verified;
final Value<bool> archived;
final Value<bool> pinned;
final Value<bool> deleted;
final Value<bool> alsoBestFriend;
final Value<int> deleteMessagesAfterXMinutes;
final Value<DateTime> createdAt;
@ -811,6 +841,7 @@ class ContactsCompanion extends UpdateCompanion<Contact> {
this.verified = const Value.absent(),
this.archived = const Value.absent(),
this.pinned = const Value.absent(),
this.deleted = const Value.absent(),
this.alsoBestFriend = const Value.absent(),
this.deleteMessagesAfterXMinutes = const Value.absent(),
this.createdAt = const Value.absent(),
@ -835,6 +866,7 @@ class ContactsCompanion extends UpdateCompanion<Contact> {
this.verified = const Value.absent(),
this.archived = const Value.absent(),
this.pinned = const Value.absent(),
this.deleted = const Value.absent(),
this.alsoBestFriend = const Value.absent(),
this.deleteMessagesAfterXMinutes = const Value.absent(),
this.createdAt = const Value.absent(),
@ -859,6 +891,7 @@ class ContactsCompanion extends UpdateCompanion<Contact> {
Expression<bool>? verified,
Expression<bool>? archived,
Expression<bool>? pinned,
Expression<bool>? deleted,
Expression<bool>? alsoBestFriend,
Expression<int>? deleteMessagesAfterXMinutes,
Expression<DateTime>? createdAt,
@ -883,6 +916,7 @@ class ContactsCompanion extends UpdateCompanion<Contact> {
if (verified != null) 'verified': verified,
if (archived != null) 'archived': archived,
if (pinned != null) 'pinned': pinned,
if (deleted != null) 'deleted': deleted,
if (alsoBestFriend != null) 'also_best_friend': alsoBestFriend,
if (deleteMessagesAfterXMinutes != null)
'delete_messages_after_x_minutes': deleteMessagesAfterXMinutes,
@ -913,6 +947,7 @@ class ContactsCompanion extends UpdateCompanion<Contact> {
Value<bool>? verified,
Value<bool>? archived,
Value<bool>? pinned,
Value<bool>? deleted,
Value<bool>? alsoBestFriend,
Value<int>? deleteMessagesAfterXMinutes,
Value<DateTime>? createdAt,
@ -936,6 +971,7 @@ class ContactsCompanion extends UpdateCompanion<Contact> {
verified: verified ?? this.verified,
archived: archived ?? this.archived,
pinned: pinned ?? this.pinned,
deleted: deleted ?? this.deleted,
alsoBestFriend: alsoBestFriend ?? this.alsoBestFriend,
deleteMessagesAfterXMinutes:
deleteMessagesAfterXMinutes ?? this.deleteMessagesAfterXMinutes,
@ -990,6 +1026,9 @@ class ContactsCompanion extends UpdateCompanion<Contact> {
if (pinned.present) {
map['pinned'] = Variable<bool>(pinned.value);
}
if (deleted.present) {
map['deleted'] = Variable<bool>(deleted.value);
}
if (alsoBestFriend.present) {
map['also_best_friend'] = Variable<bool>(alsoBestFriend.value);
}
@ -1042,6 +1081,7 @@ class ContactsCompanion extends UpdateCompanion<Contact> {
..write('verified: $verified, ')
..write('archived: $archived, ')
..write('pinned: $pinned, ')
..write('deleted: $deleted, ')
..write('alsoBestFriend: $alsoBestFriend, ')
..write('deleteMessagesAfterXMinutes: $deleteMessagesAfterXMinutes, ')
..write('createdAt: $createdAt, ')
@ -4196,6 +4236,7 @@ typedef $$ContactsTableCreateCompanionBuilder = ContactsCompanion Function({
Value<bool> verified,
Value<bool> archived,
Value<bool> pinned,
Value<bool> deleted,
Value<bool> alsoBestFriend,
Value<int> deleteMessagesAfterXMinutes,
Value<DateTime> createdAt,
@ -4220,6 +4261,7 @@ typedef $$ContactsTableUpdateCompanionBuilder = ContactsCompanion Function({
Value<bool> verified,
Value<bool> archived,
Value<bool> pinned,
Value<bool> deleted,
Value<bool> alsoBestFriend,
Value<int> deleteMessagesAfterXMinutes,
Value<DateTime> createdAt,
@ -4298,6 +4340,9 @@ class $$ContactsTableFilterComposer
ColumnFilters<bool> get pinned => $composableBuilder(
column: $table.pinned, builder: (column) => ColumnFilters(column));
ColumnFilters<bool> get deleted => $composableBuilder(
column: $table.deleted, builder: (column) => ColumnFilters(column));
ColumnFilters<bool> get alsoBestFriend => $composableBuilder(
column: $table.alsoBestFriend,
builder: (column) => ColumnFilters(column));
@ -4403,6 +4448,9 @@ class $$ContactsTableOrderingComposer
ColumnOrderings<bool> get pinned => $composableBuilder(
column: $table.pinned, builder: (column) => ColumnOrderings(column));
ColumnOrderings<bool> get deleted => $composableBuilder(
column: $table.deleted, builder: (column) => ColumnOrderings(column));
ColumnOrderings<bool> get alsoBestFriend => $composableBuilder(
column: $table.alsoBestFriend,
builder: (column) => ColumnOrderings(column));
@ -4488,6 +4536,9 @@ class $$ContactsTableAnnotationComposer
GeneratedColumn<bool> get pinned =>
$composableBuilder(column: $table.pinned, builder: (column) => column);
GeneratedColumn<bool> get deleted =>
$composableBuilder(column: $table.deleted, builder: (column) => column);
GeneratedColumn<bool> get alsoBestFriend => $composableBuilder(
column: $table.alsoBestFriend, builder: (column) => column);
@ -4575,6 +4626,7 @@ class $$ContactsTableTableManager extends RootTableManager<
Value<bool> verified = const Value.absent(),
Value<bool> archived = const Value.absent(),
Value<bool> pinned = const Value.absent(),
Value<bool> deleted = const Value.absent(),
Value<bool> alsoBestFriend = const Value.absent(),
Value<int> deleteMessagesAfterXMinutes = const Value.absent(),
Value<DateTime> createdAt = const Value.absent(),
@ -4599,6 +4651,7 @@ class $$ContactsTableTableManager extends RootTableManager<
verified: verified,
archived: archived,
pinned: pinned,
deleted: deleted,
alsoBestFriend: alsoBestFriend,
deleteMessagesAfterXMinutes: deleteMessagesAfterXMinutes,
createdAt: createdAt,
@ -4623,6 +4676,7 @@ class $$ContactsTableTableManager extends RootTableManager<
Value<bool> verified = const Value.absent(),
Value<bool> archived = const Value.absent(),
Value<bool> pinned = const Value.absent(),
Value<bool> deleted = const Value.absent(),
Value<bool> alsoBestFriend = const Value.absent(),
Value<int> deleteMessagesAfterXMinutes = const Value.absent(),
Value<DateTime> createdAt = const Value.absent(),
@ -4647,6 +4701,7 @@ class $$ContactsTableTableManager extends RootTableManager<
verified: verified,
archived: archived,
pinned: pinned,
deleted: deleted,
alsoBestFriend: alsoBestFriend,
deleteMessagesAfterXMinutes: deleteMessagesAfterXMinutes,
createdAt: createdAt,

View file

@ -1895,7 +1895,7 @@ final class Schema10 extends i0.VersionedSchema {
signalContactPreKeys,
signalContactSignedPreKeys,
];
late final Shape12 contacts = Shape12(
late final Shape13 contacts = Shape13(
source: i0.VersionedTable(
entityName: 'contacts',
withoutRowId: false,
@ -1916,6 +1916,7 @@ final class Schema10 extends i0.VersionedSchema {
_column_9,
_column_39,
_column_53,
_column_57,
_column_54,
_column_40,
_column_10,
@ -2054,7 +2055,7 @@ final class Schema10 extends i0.VersionedSchema {
attachedDatabase: database,
),
alias: null);
late final Shape13 signalContactPreKeys = Shape13(
late final Shape14 signalContactPreKeys = Shape14(
source: i0.VersionedTable(
entityName: 'signal_contact_pre_keys',
withoutRowId: false,
@ -2063,7 +2064,7 @@ final class Schema10 extends i0.VersionedSchema {
'PRIMARY KEY(contact_id, pre_key_id)',
],
columns: [
_column_57,
_column_58,
_column_34,
_column_35,
_column_10,
@ -2071,19 +2072,19 @@ final class Schema10 extends i0.VersionedSchema {
attachedDatabase: database,
),
alias: null);
late final Shape14 signalContactSignedPreKeys = Shape14(
late final Shape15 signalContactSignedPreKeys = Shape15(
source: i0.VersionedTable(
entityName: 'signal_contact_signed_pre_keys',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(contact_id, signed_pre_key_id)',
'PRIMARY KEY(contact_id)',
],
columns: [
_column_57,
_column_58,
_column_59,
_column_60,
_column_61,
_column_10,
],
attachedDatabase: database,
@ -2093,6 +2094,65 @@ final class Schema10 extends i0.VersionedSchema {
class Shape13 extends i0.VersionedTable {
Shape13({required super.source, required super.alias}) : super.aliased();
i1.GeneratedColumn<int> get userId =>
columnsByName['user_id']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<String> get username =>
columnsByName['username']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get displayName =>
columnsByName['display_name']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get nickName =>
columnsByName['nick_name']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get avatarSvg =>
columnsByName['avatar_svg']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<int> get myAvatarCounter =>
columnsByName['my_avatar_counter']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<bool> get accepted =>
columnsByName['accepted']! as i1.GeneratedColumn<bool>;
i1.GeneratedColumn<bool> get requested =>
columnsByName['requested']! as i1.GeneratedColumn<bool>;
i1.GeneratedColumn<bool> get blocked =>
columnsByName['blocked']! as i1.GeneratedColumn<bool>;
i1.GeneratedColumn<bool> get verified =>
columnsByName['verified']! as i1.GeneratedColumn<bool>;
i1.GeneratedColumn<bool> get archived =>
columnsByName['archived']! as i1.GeneratedColumn<bool>;
i1.GeneratedColumn<bool> get pinned =>
columnsByName['pinned']! as i1.GeneratedColumn<bool>;
i1.GeneratedColumn<bool> get deleted =>
columnsByName['deleted']! as i1.GeneratedColumn<bool>;
i1.GeneratedColumn<bool> get alsoBestFriend =>
columnsByName['also_best_friend']! as i1.GeneratedColumn<bool>;
i1.GeneratedColumn<int> get deleteMessagesAfterXMinutes =>
columnsByName['delete_messages_after_x_minutes']!
as i1.GeneratedColumn<int>;
i1.GeneratedColumn<DateTime> get createdAt =>
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
i1.GeneratedColumn<int> get totalMediaCounter =>
columnsByName['total_media_counter']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<DateTime> get lastMessageSend =>
columnsByName['last_message_send']! as i1.GeneratedColumn<DateTime>;
i1.GeneratedColumn<DateTime> get lastMessageReceived =>
columnsByName['last_message_received']! as i1.GeneratedColumn<DateTime>;
i1.GeneratedColumn<DateTime> get lastFlameCounterChange =>
columnsByName['last_flame_counter_change']!
as i1.GeneratedColumn<DateTime>;
i1.GeneratedColumn<DateTime> get lastFlameSync =>
columnsByName['last_flame_sync']! as i1.GeneratedColumn<DateTime>;
i1.GeneratedColumn<DateTime> get lastMessageExchange =>
columnsByName['last_message_exchange']! as i1.GeneratedColumn<DateTime>;
i1.GeneratedColumn<int> get flameCounter =>
columnsByName['flame_counter']! as i1.GeneratedColumn<int>;
}
i1.GeneratedColumn<bool> _column_57(String aliasedName) =>
i1.GeneratedColumn<bool>('deleted', aliasedName, false,
type: i1.DriftSqlType.bool,
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
'CHECK ("deleted" IN (0, 1))'),
defaultValue: const CustomExpression('0'));
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 preKeyId =>
@ -2103,12 +2163,12 @@ class Shape13 extends i0.VersionedTable {
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
}
i1.GeneratedColumn<int> _column_57(String aliasedName) =>
i1.GeneratedColumn<int> _column_58(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();
class Shape15 extends i0.VersionedTable {
Shape15({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 =>
@ -2122,13 +2182,13 @@ class Shape14 extends i0.VersionedTable {
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
}
i1.GeneratedColumn<int> _column_58(String aliasedName) =>
i1.GeneratedColumn<int> _column_59(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> _column_60(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> _column_61(String aliasedName) =>
i1.GeneratedColumn<i2.Uint8List>(
'signed_pre_key_signature', aliasedName, false,
type: i1.DriftSqlType.blob);

View file

@ -64,6 +64,7 @@
"chatListViewSearchUserNameBtn": "Füge deinen ersten twonly-Kontakt hinzu!",
"chatListViewSendFirstTwonly": "Sende dein erstes twonly!",
"chatListDetailInput": "Nachricht eingeben",
"userDeletedAccount": "Der Nutzer hat sein Konto gelöscht.",
"contextMenuVerifyUser": "Verifizieren",
"@contextMenuVerifyUser": {},
"contextMenuArchiveUser": "Archivieren",
@ -141,7 +142,10 @@
"contactNickname": "Spitzname",
"contactNicknameNew": "Neuer Spitzname",
"contactBlock": "Blockieren",
"deleteAllContactMessages": "Alle Nachrichten löschen",
"contactRemove": "Benutzer löschen",
"contactRemoveTitle": "{username} löschen?",
"contactRemoveBody": "Entferne den Benutzer und lösche den Chat sowie alle zugehörigen Mediendateien dauerhaft. Dadurch wird auch DEIN KONTO VON DEM TELEFON DEINES KONTAKTS gelöscht.",
"deleteAllContactMessages": "Textnachrichten löschen",
"deleteAllContactMessagesBody": "Dadurch werden alle Nachrichten, ausgenommen gespeicherte Mediendateien, in deinem Chat mit {username} gelöscht. Dies löscht NICHT die auf dem Gerät von {username} gespeicherten Nachrichten!",
"contactBlockTitle": "Blockiere {username}",
"contactBlockBody": "Ein blockierter Benutzer kann dir keine Nachrichten mehr senden, und sein Profil ist nicht mehr sichtbar. Um die Blockierung eines Benutzers aufzuheben, navigiere einfach zu Einstellungen > Datenschutz > Blockierte Benutzer.",

View file

@ -115,6 +115,7 @@
"@chatListViewSendFirstTwonly": {},
"chatListDetailInput": "Type a message",
"@chatListDetailInput": {},
"userDeletedAccount": "The user has deleted its account.",
"contextMenuVerifyUser": "Verify",
"@contextMenuVerifyUser": {},
"contextMenuArchiveUser": "Archive",
@ -250,7 +251,7 @@
"@contactNickname": {},
"contactNicknameNew": "New nickname",
"@contactNicknameNew": {},
"deleteAllContactMessages": "Delete all messages",
"deleteAllContactMessages": "Delete all text-messages",
"@deleteAllContactMessages": {},
"deleteAllContactMessagesBody": "This will remove all messages, except stored media files, in your chat with {username}. This will NOT delete the messages stored at {username}s device!",
"@deleteAllContactMessagesBody": {
@ -268,6 +269,9 @@
},
"contactBlockBody": "A blocked user will no longer be able to send you messages and their profile will be hidden from view. To unblock a user, simply navigate to Settings > Privacy > Blocked Users.",
"@contactBlockBody": {},
"contactRemove": "Remove user",
"contactRemoveTitle": "Remove {username}",
"contactRemoveBody": "Remove the user and permanently delete the chat and all associated media files. This will also delete YOUR ACCOUNT FROM YOUR CONTACT'S PHONE.",
"undo": "Undo",
"@undo": {},
"redo": "Redo",

View file

@ -446,6 +446,12 @@ abstract class AppLocalizations {
/// **'Type a message'**
String get chatListDetailInput;
/// No description provided for @userDeletedAccount.
///
/// In en, this message translates to:
/// **'The user has deleted its account.'**
String get userDeletedAccount;
/// No description provided for @contextMenuVerifyUser.
///
/// In en, this message translates to:
@ -845,7 +851,7 @@ abstract class AppLocalizations {
/// No description provided for @deleteAllContactMessages.
///
/// In en, this message translates to:
/// **'Delete all messages'**
/// **'Delete all text-messages'**
String get deleteAllContactMessages;
/// No description provided for @deleteAllContactMessagesBody.
@ -872,6 +878,24 @@ abstract class AppLocalizations {
/// **'A blocked user will no longer be able to send you messages and their profile will be hidden from view. To unblock a user, simply navigate to Settings > Privacy > Blocked Users.'**
String get contactBlockBody;
/// No description provided for @contactRemove.
///
/// In en, this message translates to:
/// **'Remove user'**
String get contactRemove;
/// No description provided for @contactRemoveTitle.
///
/// In en, this message translates to:
/// **'Remove {username}'**
String contactRemoveTitle(Object username);
/// No description provided for @contactRemoveBody.
///
/// In en, this message translates to:
/// **'Remove the user and permanently delete the chat and all associated media files. This will also delete YOUR ACCOUNT FROM YOUR CONTACT\'S PHONE.'**
String get contactRemoveBody;
/// No description provided for @undo.
///
/// In en, this message translates to:

View file

@ -203,6 +203,9 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get chatListDetailInput => 'Nachricht eingeben';
@override
String get userDeletedAccount => 'Der Nutzer hat sein Konto gelöscht.';
@override
String get contextMenuVerifyUser => 'Verifizieren';
@ -419,7 +422,7 @@ class AppLocalizationsDe extends AppLocalizations {
String get contactNicknameNew => 'Neuer Spitzname';
@override
String get deleteAllContactMessages => 'Alle Nachrichten löschen';
String get deleteAllContactMessages => 'Textnachrichten löschen';
@override
String deleteAllContactMessagesBody(Object username) {
@ -438,6 +441,18 @@ class AppLocalizationsDe extends AppLocalizations {
String get contactBlockBody =>
'Ein blockierter Benutzer kann dir keine Nachrichten mehr senden, und sein Profil ist nicht mehr sichtbar. Um die Blockierung eines Benutzers aufzuheben, navigiere einfach zu Einstellungen > Datenschutz > Blockierte Benutzer.';
@override
String get contactRemove => 'Benutzer löschen';
@override
String contactRemoveTitle(Object username) {
return '$username löschen?';
}
@override
String get contactRemoveBody =>
'Entferne den Benutzer und lösche den Chat sowie alle zugehörigen Mediendateien dauerhaft. Dadurch wird auch DEIN KONTO VON DEM TELEFON DEINES KONTAKTS gelöscht.';
@override
String get undo => 'Rückgängig';

View file

@ -201,6 +201,9 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get chatListDetailInput => 'Type a message';
@override
String get userDeletedAccount => 'The user has deleted its account.';
@override
String get contextMenuVerifyUser => 'Verify';
@ -414,7 +417,7 @@ class AppLocalizationsEn extends AppLocalizations {
String get contactNicknameNew => 'New nickname';
@override
String get deleteAllContactMessages => 'Delete all messages';
String get deleteAllContactMessages => 'Delete all text-messages';
@override
String deleteAllContactMessagesBody(Object username) {
@ -433,6 +436,18 @@ class AppLocalizationsEn extends AppLocalizations {
String get contactBlockBody =>
'A blocked user will no longer be able to send you messages and their profile will be hidden from view. To unblock a user, simply navigate to Settings > Privacy > Blocked Users.';
@override
String get contactRemove => 'Remove user';
@override
String contactRemoveTitle(Object username) {
return 'Remove $username';
}
@override
String get contactRemoveBody =>
'Remove the user and permanently delete the chat and all associated media files. This will also delete YOUR ACCOUNT FROM YOUR CONTACT\'S PHONE.';
@override
String get undo => 'Undo';

View file

@ -3,6 +3,7 @@ import 'dart:collection';
import 'dart:convert';
import 'dart:io';
import 'dart:math';
import 'package:drift/drift.dart';
import 'package:fixnum/fixnum.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
@ -10,6 +11,7 @@ import 'package:mutex/mutex.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/app.dart';
import 'package:twonly/src/database/twonly_database.dart';
import 'package:twonly/src/model/json/userdata.dart';
import 'package:twonly/src/model/protobuf/api/client_to_server.pbserver.dart';
import 'package:twonly/src/model/protobuf/api/error.pb.dart';
@ -221,8 +223,12 @@ class ApiService {
box.put("rawbytes-to-retransmit", retransmit);
}
Future<Result> sendRequestSync(ClientToServer request,
{bool authenticated = true, bool ensureRetransmission = false}) async {
Future<Result> sendRequestSync(
ClientToServer request, {
bool authenticated = true,
bool ensureRetransmission = false,
int? contactId,
}) async {
var seq = Int64(Random().nextInt(4294967296));
while (messagesV0.containsKey(seq)) {
seq = Int64(Random().nextInt(4294967296));
@ -263,6 +269,11 @@ class ApiService {
}
}
}
if (res.error == ErrorCode.UserIdNotFound && contactId != null) {
Log.error("Contact deleted their account $contactId.");
await twonlyDB.contactsDao
.updateContact(contactId, ContactsCompanion(deleted: Value(true)));
}
}
return res;
}
@ -393,7 +404,7 @@ class ApiService {
var get = ApplicationData_GetUserById()..userId = Int64(userId);
var appData = ApplicationData()..getuserbyid = get;
var req = createClientToServerFromApplicationData(appData);
return await sendRequestSync(req);
return await sendRequestSync(req, contactId: userId);
}
Future<Result> getUploadToken(int recipientsCount) async {
@ -504,7 +515,7 @@ class ApiService {
var get = ApplicationData_RemoveAdditionalUser()..userId = userId;
var appData = ApplicationData()..removeadditionaluser = get;
var req = createClientToServerFromApplicationData(appData);
return await sendRequestSync(req);
return await sendRequestSync(req, contactId: userId.toInt());
}
Future<Result> buyVoucher(int valueInCents) async {
@ -571,7 +582,7 @@ class ApiService {
var get = ApplicationData_GetSignedPreKeyByUserId()..userId = Int64(userId);
var appData = ApplicationData()..getsignedprekeybyuserid = get;
var req = createClientToServerFromApplicationData(appData);
Result res = await sendRequestSync(req);
Result res = await sendRequestSync(req, contactId: userId);
if (res.isSuccess) {
server.Response_Ok ok = res.value;
if (ok.hasSignedprekey()) {
@ -585,7 +596,7 @@ class ApiService {
var get = ApplicationData_GetPrekeysByUserId()..userId = Int64(userId);
var appData = ApplicationData()..getprekeysbyuserid = get;
var req = createClientToServerFromApplicationData(appData);
Result res = await sendRequestSync(req);
Result res = await sendRequestSync(req, contactId: userId);
if (res.isSuccess) {
server.Response_Ok ok = res.value;
if (ok.hasUserdata()) {
@ -617,7 +628,6 @@ class ApiService {
var appData = ApplicationData()..textmessage = testMessage;
var req = createClientToServerFromApplicationData(appData);
return await sendRequestSync(req);
return await sendRequestSync(req, contactId: target);
}
}

View file

@ -8,6 +8,7 @@ import 'package:twonly/src/database/twonly_database.dart';
import 'package:twonly/src/database/tables/messages_table.dart';
import 'package:twonly/src/model/json/message.dart';
import 'package:twonly/src/model/json/userdata.dart';
import 'package:twonly/src/model/protobuf/api/error.pb.dart';
import 'package:twonly/src/services/api/utils.dart';
import 'package:twonly/src/services/signal/encryption.signal.dart';
import 'package:twonly/src/utils/hive.dart';
@ -114,19 +115,37 @@ Future<Result> sendRetransmitMessage(
Result resp =
await apiService.sendTextMessage(msg.userId, msg.bytes, msg.pushData);
bool retry = true;
if (resp.isError) {
if (resp.error == ErrorCode.UserIdNotFound) {
retry = false;
if (msg.messageId != null) {
await twonlyDB.messagesDao.updateMessageByMessageId(
msg.messageId!,
MessagesCompanion(errorWhileSending: Value(true)),
);
}
}
}
if (resp.isSuccess) {
if (msg.messageId != null) {
retry = false;
await twonlyDB.messagesDao.updateMessageByMessageId(
msg.messageId!,
MessagesCompanion(acknowledgeByServer: Value(true)),
);
}
}
if (!retry) {
{
var retransmit = await getAllMessagesForRetransmitting();
retransmit.remove(stateId);
Box box = await getMediaStorage();
box.put("messages-to-retransmit", jsonEncode(retransmit));
}
if (msg.messageId != null) {
await twonlyDB.messagesDao.updateMessageByMessageId(
msg.messageId!,
MessagesCompanion(acknowledgeByServer: Value(true)),
);
}
}
return resp;
}

View file

@ -107,7 +107,7 @@ Future<client.Response> handleNewMessage(int fromUserId, Uint8List body) async {
break;
case MessageKind.rejectRequest:
await twonlyDB.contactsDao.deleteContactByUserId(fromUserId);
await deleteContact(fromUserId);
break;
case MessageKind.acceptRequest:

View file

@ -1,10 +1,15 @@
import 'package:fixnum/fixnum.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/database/tables/messages_table.dart';
import 'package:twonly/src/model/json/message.dart';
import 'package:twonly/src/model/protobuf/api/client_to_server.pb.dart'
as client;
import 'package:twonly/src/model/protobuf/api/client_to_server.pbserver.dart';
import 'package:twonly/src/model/protobuf/api/error.pb.dart';
import 'package:twonly/src/model/protobuf/api/server_to_client.pb.dart'
as server;
import 'package:twonly/src/services/api/messages.dart';
import 'package:twonly/src/services/signal/session.signal.dart';
class Result<T, E> {
final T? value;
@ -42,3 +47,22 @@ ClientToServer createClientToServerFromApplicationData(
..applicationdata = applicationData;
return ClientToServer()..v0 = v0;
}
Future deleteContact(int contactId) async {
await twonlyDB.messagesDao.deleteAllMessagesByContactId(contactId);
await twonlyDB.signalDao.deleteAllByContactId(contactId);
await deleteSessionWithTarget(contactId);
await twonlyDB.contactsDao.deleteContactByUserId(contactId);
}
Future rejectUser(int contactId) async {
await encryptAndSendMessageAsync(
null,
contactId,
MessageJson(
kind: MessageKind.rejectRequest,
timestamp: DateTime.now(),
content: MessageContent(),
),
);
}

View file

@ -77,6 +77,13 @@ Future<bool> createNewSignalSession(Response_UserData userData) async {
}
}
Future deleteSessionWithTarget(int target) async {
ConnectSignalProtocolStore? signalStore = await getSignalStore();
if (signalStore == null) return;
final address = SignalProtocolAddress(target.toString(), defaultDeviceId);
await signalStore.sessionStore.deleteSession(address);
}
Future<Fingerprint?> generateSessionFingerPrint(int target) async {
ConnectSignalProtocolStore? signalStore = await getSignalStore();
UserData? user = await getUser();

View file

@ -5,6 +5,7 @@ import 'package:flutter/services.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:provider/provider.dart';
import 'package:twonly/src/providers/connection.provider.dart';
import 'package:twonly/src/services/api/utils.dart';
import 'package:twonly/src/services/signal/session.signal.dart';
import 'package:twonly/src/views/components/alert_dialog.dart';
import 'package:twonly/src/database/daos/contacts_dao.dart';
@ -262,16 +263,8 @@ class _ContactsListViewState extends State<ContactsListView> {
child: IconButton(
icon: Icon(Icons.close, color: Colors.red),
onPressed: () async {
await twonlyDB.contactsDao.deleteContactByUserId(contact.userId);
await encryptAndSendMessageAsync(
null,
contact.userId,
MessageJson(
kind: MessageKind.rejectRequest,
timestamp: DateTime.now(),
content: MessageContent(),
),
);
rejectUser(contact.userId);
await deleteContact(contact.userId);
},
),
),

View file

@ -363,7 +363,9 @@ class _UserListItem extends State<UserListItem> {
title: Text(
getContactDisplayName(widget.user),
),
subtitle: (currentMessage == null)
subtitle: (widget.user.deleted)
? Text(context.lang.userDeletedAccount)
: (currentMessage == null)
? Text(context.lang.chatsTapToSend)
: Row(
children: [
@ -383,7 +385,9 @@ class _UserListItem extends State<UserListItem> {
],
),
leading: ContactAvatar(contact: widget.user),
trailing: IconButton(
trailing: (widget.user.deleted)
? null
: IconButton(
onPressed: () {
Navigator.push(context, MaterialPageRoute(
builder: (context) {

View file

@ -42,7 +42,7 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
HashSet<int> alreadyReportedOpened = HashSet<int>();
late Contact user;
String currentInputText = "";
late StreamSubscription<Contact> userSub;
late StreamSubscription<Contact?> userSub;
late StreamSubscription<List<Message>> messageSub;
List<Message> messages = [];
List<MemoryItem> galleryItems = [];
@ -75,9 +75,10 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
Future initStreams() async {
await twonlyDB.messagesDao.removeOldMessages();
Stream<Contact> contact =
Stream<Contact?> contact =
twonlyDB.contactsDao.watchContact(widget.contact.userId);
userSub = contact.listen((contact) {
if (contact == null) return;
setState(() {
user = contact;
});
@ -336,7 +337,7 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
},
),
),
if (responseToMessage != null)
if (responseToMessage != null && !user.deleted)
Container(
padding: const EdgeInsets.only(
bottom: 00,
@ -369,7 +370,9 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
top: 10,
),
child: Row(
children: [
children: (user.deleted)
? []
: [
Expanded(
child: TextField(
controller: newMessageController,

View file

@ -30,7 +30,7 @@ class _StartNewChatView extends State<StartNewChatView> {
super.initState();
Stream<List<Contact>> stream =
twonlyDB.contactsDao.watchContactsForShareView();
twonlyDB.contactsDao.watchContactsForStartNewChat();
contactSub = stream.listen((update) {
update.sort((a, b) =>

View file

@ -1,6 +1,7 @@
import 'package:drift/drift.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/services/api/utils.dart';
import 'package:twonly/src/views/components/alert_dialog.dart';
import 'package:twonly/src/views/components/better_list_title.dart';
import 'package:twonly/src/views/components/flame.dart';
@ -22,6 +23,39 @@ class ContactView extends StatefulWidget {
}
class _ContactViewState extends State<ContactView> {
Future handleUserRemoveRequest(Contact contact) async {
bool remove = await showAlertDialog(
context,
context.lang.contactRemoveTitle(getContactDisplayName(contact)),
context.lang.contactRemoveBody,
);
if (remove) {
// trigger deletion for the other user...
rejectUser(contact.userId);
await deleteContact(contact.userId);
if (mounted) {
Navigator.popUntil(context, (route) => route.isFirst);
}
}
}
Future handleUserBlockRequest(Contact contact) async {
bool block = await showAlertDialog(
context,
context.lang.contactBlockTitle(getContactDisplayName(contact)),
context.lang.contactBlockBody,
);
if (block) {
final update = ContactsCompanion(blocked: Value(true));
if (context.mounted) {
await twonlyDB.contactsDao.updateContact(contact.userId, update);
}
if (context.mounted) {
Navigator.popUntil(context, (route) => route.isFirst);
}
}
}
@override
Widget build(BuildContext context) {
Stream<Contact?> contact = twonlyDB.contactsDao
@ -93,7 +127,8 @@ class _ContactViewState extends State<ContactView> {
},
),
BetterListTile(
icon: FontAwesomeIcons.trashCan,
icon: FontAwesomeIcons.eraser,
iconSize: 16,
text: context.lang.deleteAllContactMessages,
onTap: () async {
bool block = await showAlertDialog(
@ -114,24 +149,14 @@ class _ContactViewState extends State<ContactView> {
icon: FontAwesomeIcons.ban,
color: Colors.red,
text: context.lang.contactBlock,
onTap: () async {
bool block = await showAlertDialog(
context,
context.lang
.contactBlockTitle(getContactDisplayName(contact)),
context.lang.contactBlockBody,
);
if (block) {
final update = ContactsCompanion(blocked: Value(true));
if (context.mounted) {
await twonlyDB.contactsDao
.updateContact(contact.userId, update);
}
if (context.mounted) {
Navigator.popUntil(context, (route) => route.isFirst);
}
}
},
onTap: () => handleUserBlockRequest(contact),
),
BetterListTile(
icon: FontAwesomeIcons.userMinus,
iconSize: 16,
color: Colors.red,
text: context.lang.contactRemove,
onTap: () => handleUserRemoveRequest(contact),
),
],
);

View file

@ -95,6 +95,15 @@ class _AccountViewState extends State<AccountView> {
.settingsAccountDeleteAccountWithBallance(
formattedBallance!))
: Text(context.lang.settingsAccountDeleteAccountNoBallance),
onLongPress: (kDebugMode)
? () async {
await deleteLocalUserData();
Restart.restartApp(
notificationTitle: 'Account successfully deleted',
notificationBody: 'Click here to open the app again',
);
}
: null,
onTap: (formattedBallance == null)
? null
: () async {

View file

@ -72,6 +72,13 @@ class Contacts extends Table with TableInfo<Contacts, ContactsData> {
defaultConstraints:
GeneratedColumn.constraintIsAlways('CHECK ("pinned" IN (0, 1))'),
defaultValue: const CustomExpression('0'));
late final GeneratedColumn<bool> deleted = GeneratedColumn<bool>(
'deleted', aliasedName, false,
type: DriftSqlType.bool,
requiredDuringInsert: false,
defaultConstraints:
GeneratedColumn.constraintIsAlways('CHECK ("deleted" IN (0, 1))'),
defaultValue: const CustomExpression('0'));
late final GeneratedColumn<bool> alsoBestFriend = GeneratedColumn<bool>(
'also_best_friend', aliasedName, false,
type: DriftSqlType.bool,
@ -133,6 +140,7 @@ class Contacts extends Table with TableInfo<Contacts, ContactsData> {
verified,
archived,
pinned,
deleted,
alsoBestFriend,
deleteMessagesAfterXMinutes,
createdAt,
@ -179,6 +187,8 @@ class Contacts extends Table with TableInfo<Contacts, ContactsData> {
.read(DriftSqlType.bool, data['${effectivePrefix}archived'])!,
pinned: attachedDatabase.typeMapping
.read(DriftSqlType.bool, data['${effectivePrefix}pinned'])!,
deleted: attachedDatabase.typeMapping
.read(DriftSqlType.bool, data['${effectivePrefix}deleted'])!,
alsoBestFriend: attachedDatabase.typeMapping
.read(DriftSqlType.bool, data['${effectivePrefix}also_best_friend'])!,
deleteMessagesAfterXMinutes: attachedDatabase.typeMapping.read(
@ -225,6 +235,7 @@ class ContactsData extends DataClass implements Insertable<ContactsData> {
final bool verified;
final bool archived;
final bool pinned;
final bool deleted;
final bool alsoBestFriend;
final int deleteMessagesAfterXMinutes;
final DateTime createdAt;
@ -248,6 +259,7 @@ class ContactsData extends DataClass implements Insertable<ContactsData> {
required this.verified,
required this.archived,
required this.pinned,
required this.deleted,
required this.alsoBestFriend,
required this.deleteMessagesAfterXMinutes,
required this.createdAt,
@ -279,6 +291,7 @@ class ContactsData extends DataClass implements Insertable<ContactsData> {
map['verified'] = Variable<bool>(verified);
map['archived'] = Variable<bool>(archived);
map['pinned'] = Variable<bool>(pinned);
map['deleted'] = Variable<bool>(deleted);
map['also_best_friend'] = Variable<bool>(alsoBestFriend);
map['delete_messages_after_x_minutes'] =
Variable<int>(deleteMessagesAfterXMinutes);
@ -322,6 +335,7 @@ class ContactsData extends DataClass implements Insertable<ContactsData> {
verified: Value(verified),
archived: Value(archived),
pinned: Value(pinned),
deleted: Value(deleted),
alsoBestFriend: Value(alsoBestFriend),
deleteMessagesAfterXMinutes: Value(deleteMessagesAfterXMinutes),
createdAt: Value(createdAt),
@ -359,6 +373,7 @@ class ContactsData extends DataClass implements Insertable<ContactsData> {
verified: serializer.fromJson<bool>(json['verified']),
archived: serializer.fromJson<bool>(json['archived']),
pinned: serializer.fromJson<bool>(json['pinned']),
deleted: serializer.fromJson<bool>(json['deleted']),
alsoBestFriend: serializer.fromJson<bool>(json['alsoBestFriend']),
deleteMessagesAfterXMinutes:
serializer.fromJson<int>(json['deleteMessagesAfterXMinutes']),
@ -391,6 +406,7 @@ class ContactsData extends DataClass implements Insertable<ContactsData> {
'verified': serializer.toJson<bool>(verified),
'archived': serializer.toJson<bool>(archived),
'pinned': serializer.toJson<bool>(pinned),
'deleted': serializer.toJson<bool>(deleted),
'alsoBestFriend': serializer.toJson<bool>(alsoBestFriend),
'deleteMessagesAfterXMinutes':
serializer.toJson<int>(deleteMessagesAfterXMinutes),
@ -419,6 +435,7 @@ class ContactsData extends DataClass implements Insertable<ContactsData> {
bool? verified,
bool? archived,
bool? pinned,
bool? deleted,
bool? alsoBestFriend,
int? deleteMessagesAfterXMinutes,
DateTime? createdAt,
@ -442,6 +459,7 @@ class ContactsData extends DataClass implements Insertable<ContactsData> {
verified: verified ?? this.verified,
archived: archived ?? this.archived,
pinned: pinned ?? this.pinned,
deleted: deleted ?? this.deleted,
alsoBestFriend: alsoBestFriend ?? this.alsoBestFriend,
deleteMessagesAfterXMinutes:
deleteMessagesAfterXMinutes ?? this.deleteMessagesAfterXMinutes,
@ -478,6 +496,7 @@ class ContactsData extends DataClass implements Insertable<ContactsData> {
verified: data.verified.present ? data.verified.value : this.verified,
archived: data.archived.present ? data.archived.value : this.archived,
pinned: data.pinned.present ? data.pinned.value : this.pinned,
deleted: data.deleted.present ? data.deleted.value : this.deleted,
alsoBestFriend: data.alsoBestFriend.present
? data.alsoBestFriend.value
: this.alsoBestFriend,
@ -524,6 +543,7 @@ class ContactsData extends DataClass implements Insertable<ContactsData> {
..write('verified: $verified, ')
..write('archived: $archived, ')
..write('pinned: $pinned, ')
..write('deleted: $deleted, ')
..write('alsoBestFriend: $alsoBestFriend, ')
..write('deleteMessagesAfterXMinutes: $deleteMessagesAfterXMinutes, ')
..write('createdAt: $createdAt, ')
@ -552,6 +572,7 @@ class ContactsData extends DataClass implements Insertable<ContactsData> {
verified,
archived,
pinned,
deleted,
alsoBestFriend,
deleteMessagesAfterXMinutes,
createdAt,
@ -579,6 +600,7 @@ class ContactsData extends DataClass implements Insertable<ContactsData> {
other.verified == this.verified &&
other.archived == this.archived &&
other.pinned == this.pinned &&
other.deleted == this.deleted &&
other.alsoBestFriend == this.alsoBestFriend &&
other.deleteMessagesAfterXMinutes ==
this.deleteMessagesAfterXMinutes &&
@ -605,6 +627,7 @@ class ContactsCompanion extends UpdateCompanion<ContactsData> {
final Value<bool> verified;
final Value<bool> archived;
final Value<bool> pinned;
final Value<bool> deleted;
final Value<bool> alsoBestFriend;
final Value<int> deleteMessagesAfterXMinutes;
final Value<DateTime> createdAt;
@ -628,6 +651,7 @@ class ContactsCompanion extends UpdateCompanion<ContactsData> {
this.verified = const Value.absent(),
this.archived = const Value.absent(),
this.pinned = const Value.absent(),
this.deleted = const Value.absent(),
this.alsoBestFriend = const Value.absent(),
this.deleteMessagesAfterXMinutes = const Value.absent(),
this.createdAt = const Value.absent(),
@ -652,6 +676,7 @@ class ContactsCompanion extends UpdateCompanion<ContactsData> {
this.verified = const Value.absent(),
this.archived = const Value.absent(),
this.pinned = const Value.absent(),
this.deleted = const Value.absent(),
this.alsoBestFriend = const Value.absent(),
this.deleteMessagesAfterXMinutes = const Value.absent(),
this.createdAt = const Value.absent(),
@ -676,6 +701,7 @@ class ContactsCompanion extends UpdateCompanion<ContactsData> {
Expression<bool>? verified,
Expression<bool>? archived,
Expression<bool>? pinned,
Expression<bool>? deleted,
Expression<bool>? alsoBestFriend,
Expression<int>? deleteMessagesAfterXMinutes,
Expression<DateTime>? createdAt,
@ -700,6 +726,7 @@ class ContactsCompanion extends UpdateCompanion<ContactsData> {
if (verified != null) 'verified': verified,
if (archived != null) 'archived': archived,
if (pinned != null) 'pinned': pinned,
if (deleted != null) 'deleted': deleted,
if (alsoBestFriend != null) 'also_best_friend': alsoBestFriend,
if (deleteMessagesAfterXMinutes != null)
'delete_messages_after_x_minutes': deleteMessagesAfterXMinutes,
@ -730,6 +757,7 @@ class ContactsCompanion extends UpdateCompanion<ContactsData> {
Value<bool>? verified,
Value<bool>? archived,
Value<bool>? pinned,
Value<bool>? deleted,
Value<bool>? alsoBestFriend,
Value<int>? deleteMessagesAfterXMinutes,
Value<DateTime>? createdAt,
@ -753,6 +781,7 @@ class ContactsCompanion extends UpdateCompanion<ContactsData> {
verified: verified ?? this.verified,
archived: archived ?? this.archived,
pinned: pinned ?? this.pinned,
deleted: deleted ?? this.deleted,
alsoBestFriend: alsoBestFriend ?? this.alsoBestFriend,
deleteMessagesAfterXMinutes:
deleteMessagesAfterXMinutes ?? this.deleteMessagesAfterXMinutes,
@ -807,6 +836,9 @@ class ContactsCompanion extends UpdateCompanion<ContactsData> {
if (pinned.present) {
map['pinned'] = Variable<bool>(pinned.value);
}
if (deleted.present) {
map['deleted'] = Variable<bool>(deleted.value);
}
if (alsoBestFriend.present) {
map['also_best_friend'] = Variable<bool>(alsoBestFriend.value);
}
@ -859,6 +891,7 @@ class ContactsCompanion extends UpdateCompanion<ContactsData> {
..write('verified: $verified, ')
..write('archived: $archived, ')
..write('pinned: $pinned, ')
..write('deleted: $deleted, ')
..write('alsoBestFriend: $alsoBestFriend, ')
..write('deleteMessagesAfterXMinutes: $deleteMessagesAfterXMinutes, ')
..write('createdAt: $createdAt, ')
@ -3176,7 +3209,7 @@ class SignalContactSignedPreKeys extends Table
SignalContactSignedPreKeys(this.attachedDatabase, [this._alias]);
late final GeneratedColumn<int> contactId = GeneratedColumn<int>(
'contact_id', aliasedName, false,
type: DriftSqlType.int, requiredDuringInsert: true);
type: DriftSqlType.int, requiredDuringInsert: false);
late final GeneratedColumn<int> signedPreKeyId = GeneratedColumn<int>(
'signed_pre_key_id', aliasedName, false,
type: DriftSqlType.int, requiredDuringInsert: true);
@ -3206,7 +3239,7 @@ class SignalContactSignedPreKeys extends Table
String get actualTableName => $name;
static const String $name = 'signal_contact_signed_pre_keys';
@override
Set<GeneratedColumn> get $primaryKey => {contactId, signedPreKeyId};
Set<GeneratedColumn> get $primaryKey => {contactId};
@override
SignalContactSignedPreKeysData map(Map<String, dynamic> data,
{String? tablePrefix}) {
@ -3361,24 +3394,20 @@ class SignalContactSignedPreKeysCompanion
final Value<Uint8List> signedPreKey;
final Value<Uint8List> signedPreKeySignature;
final Value<DateTime> createdAt;
final Value<int> rowid;
const SignalContactSignedPreKeysCompanion({
this.contactId = const Value.absent(),
this.signedPreKeyId = const Value.absent(),
this.signedPreKey = const Value.absent(),
this.signedPreKeySignature = const Value.absent(),
this.createdAt = const Value.absent(),
this.rowid = const Value.absent(),
});
SignalContactSignedPreKeysCompanion.insert({
required int contactId,
this.contactId = const Value.absent(),
required int signedPreKeyId,
required Uint8List signedPreKey,
required Uint8List signedPreKeySignature,
this.createdAt = const Value.absent(),
this.rowid = const Value.absent(),
}) : contactId = Value(contactId),
signedPreKeyId = Value(signedPreKeyId),
}) : signedPreKeyId = Value(signedPreKeyId),
signedPreKey = Value(signedPreKey),
signedPreKeySignature = Value(signedPreKeySignature);
static Insertable<SignalContactSignedPreKeysData> custom({
@ -3387,7 +3416,6 @@ class SignalContactSignedPreKeysCompanion
Expression<Uint8List>? signedPreKey,
Expression<Uint8List>? signedPreKeySignature,
Expression<DateTime>? createdAt,
Expression<int>? rowid,
}) {
return RawValuesInsertable({
if (contactId != null) 'contact_id': contactId,
@ -3396,7 +3424,6 @@ class SignalContactSignedPreKeysCompanion
if (signedPreKeySignature != null)
'signed_pre_key_signature': signedPreKeySignature,
if (createdAt != null) 'created_at': createdAt,
if (rowid != null) 'rowid': rowid,
});
}
@ -3405,8 +3432,7 @@ class SignalContactSignedPreKeysCompanion
Value<int>? signedPreKeyId,
Value<Uint8List>? signedPreKey,
Value<Uint8List>? signedPreKeySignature,
Value<DateTime>? createdAt,
Value<int>? rowid}) {
Value<DateTime>? createdAt}) {
return SignalContactSignedPreKeysCompanion(
contactId: contactId ?? this.contactId,
signedPreKeyId: signedPreKeyId ?? this.signedPreKeyId,
@ -3414,7 +3440,6 @@ class SignalContactSignedPreKeysCompanion
signedPreKeySignature:
signedPreKeySignature ?? this.signedPreKeySignature,
createdAt: createdAt ?? this.createdAt,
rowid: rowid ?? this.rowid,
);
}
@ -3437,9 +3462,6 @@ class SignalContactSignedPreKeysCompanion
if (createdAt.present) {
map['created_at'] = Variable<DateTime>(createdAt.value);
}
if (rowid.present) {
map['rowid'] = Variable<int>(rowid.value);
}
return map;
}
@ -3450,8 +3472,7 @@ class SignalContactSignedPreKeysCompanion
..write('signedPreKeyId: $signedPreKeyId, ')
..write('signedPreKey: $signedPreKey, ')
..write('signedPreKeySignature: $signedPreKeySignature, ')
..write('createdAt: $createdAt, ')
..write('rowid: $rowid')
..write('createdAt: $createdAt')
..write(')'))
.toString();
}

File diff suppressed because it is too large Load diff