display last reaction and compress avatarsvgs

This commit is contained in:
otsmr 2025-10-27 20:14:07 +01:00
parent 3d5fc3e807
commit 350fb899e1
33 changed files with 604 additions and 297 deletions

View file

@ -4,6 +4,7 @@ import 'package:twonly/src/database/tables/contacts.table.dart';
import 'package:twonly/src/database/twonly.db.dart'; import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/database/twonly_database_old.dart' as old; import 'package:twonly/src/database/twonly_database_old.dart' as old;
import 'package:twonly/src/services/notifications/pushkeys.notifications.dart'; import 'package:twonly/src/services/notifications/pushkeys.notifications.dart';
import 'package:twonly/src/utils/log.dart';
part 'contacts.dao.g.dart'; part 'contacts.dao.g.dart';
@ -14,10 +15,20 @@ class ContactsDao extends DatabaseAccessor<TwonlyDB> with _$ContactsDaoMixin {
// ignore: matching_super_parameters // ignore: matching_super_parameters
ContactsDao(super.db); ContactsDao(super.db);
Future<int> insertContact(ContactsCompanion contact) async { Future<int?> insertContact(ContactsCompanion contact) async {
try { try {
return await into(contacts).insert(contact); return await into(contacts).insert(contact);
} catch (e) { } catch (e) {
Log.error(e);
return null;
}
}
Future<int> insertOnConflictUpdate(ContactsCompanion contact) async {
try {
return await into(contacts).insertOnConflictUpdate(contact);
} catch (e) {
Log.error(e);
return 0; return 0;
} }
} }
@ -62,10 +73,12 @@ class ContactsDao extends DatabaseAccessor<TwonlyDB> with _$ContactsDaoMixin {
Stream<List<Contact>> watchNotAcceptedContacts() { Stream<List<Contact>> watchNotAcceptedContacts() {
return (select(contacts) return (select(contacts)
..where( ..where(
(t) => t.accepted.equals(false) & t.blocked.equals(false), (t) =>
t.accepted.equals(false) &
t.blocked.equals(false) &
t.deletedByUser.equals(false),
)) ))
.watch(); .watch();
// return (select(contacts)).watch();
} }
Stream<Contact?> watchContact(int userid) { Stream<Contact?> watchContact(int userid) {
@ -74,7 +87,7 @@ class ContactsDao extends DatabaseAccessor<TwonlyDB> with _$ContactsDaoMixin {
} }
Future<List<Contact>> getAllNotBlockedContacts() { Future<List<Contact>> getAllNotBlockedContacts() {
return (select(contacts)..where((t) => t.blocked.equals(false))).get(); return select(contacts).get();
} }
Stream<int?> watchContactsBlocked() { Stream<int?> watchContactsBlocked() {
@ -89,7 +102,9 @@ class ContactsDao extends DatabaseAccessor<TwonlyDB> with _$ContactsDaoMixin {
final count = contacts.requested.count(distinct: true); final count = contacts.requested.count(distinct: true);
final query = selectOnly(contacts) final query = selectOnly(contacts)
..where( ..where(
contacts.requested.equals(true) & contacts.accepted.equals(true).not(), contacts.requested.equals(true) &
contacts.accepted.equals(false) &
contacts.blocked.equals(false),
) )
..addColumns([count]); ..addColumns([count]);
return query.map((row) => row.read(count)).watchSingle(); return query.map((row) => row.read(count)).watchSingle();
@ -113,7 +128,7 @@ String getContactDisplayName(Contact user) {
} else if (user.displayName != null) { } else if (user.displayName != null) {
name = user.displayName!; name = user.displayName!;
} }
if (user.deleted) { if (user.accountDeleted) {
name = applyStrikethrough(name); name = applyStrikethrough(name);
} }
if (name.length > 12) { if (name.length > 12) {

View file

@ -81,13 +81,13 @@ class GroupsDao extends DatabaseAccessor<TwonlyDB> with _$GroupsDaoMixin {
} }
Future<List<Contact>> getGroupContact(String groupId) async { Future<List<Contact>> getGroupContact(String groupId) async {
final query = select(contacts).join([ final query = (select(contacts).join([
leftOuterJoin( leftOuterJoin(
groupMembers, groupMembers,
groupMembers.contactId.equalsExp(contacts.userId) & groupMembers.contactId.equalsExp(contacts.userId),
groupMembers.groupId.equals(groupId),
), ),
]); ])
..where(groupMembers.groupId.equals(groupId)));
return query.map((row) => row.readTable(contacts)).get(); return query.map((row) => row.readTable(contacts)).get();
} }
@ -101,7 +101,10 @@ class GroupsDao extends DatabaseAccessor<TwonlyDB> with _$GroupsDaoMixin {
} }
Stream<List<Group>> watchGroupsForChatList() { Stream<List<Group>> watchGroupsForChatList() {
return (select(groups)..where((t) => t.archived.equals(false))).watch(); return (select(groups)
..where((t) => t.archived.equals(false))
..orderBy([(t) => OrderingTerm.desc(t.lastMessageExchange)]))
.watch();
} }
Future<Group?> getGroup(String groupId) { Future<Group?> getGroup(String groupId) {
@ -117,7 +120,7 @@ class GroupsDao extends DatabaseAccessor<TwonlyDB> with _$GroupsDaoMixin {
u.lastMessageReceived.isNotNull() & u.lastMessageReceived.isNotNull() &
u.lastMessageSend.isNotNull(), u.lastMessageSend.isNotNull(),
)) ))
.watchSingle() .watchSingleOrNull()
.asyncMap(getFlameCounterFromGroup); .asyncMap(getFlameCounterFromGroup);
} }
@ -126,14 +129,14 @@ class GroupsDao extends DatabaseAccessor<TwonlyDB> with _$GroupsDaoMixin {
} }
Future<Group?> getDirectChat(int userId) async { Future<Group?> getDirectChat(int userId) async {
final query = (select(groups).join([ final query =
((select(groups)..where((t) => t.isDirectChat.equals(true))).join([
leftOuterJoin( leftOuterJoin(
groupMembers, groupMembers,
groupMembers.groupId.equalsExp(groups.groupId) & groupMembers.groupId.equalsExp(groups.groupId),
groupMembers.contactId.equals(userId),
), ),
]) ])
..where(groups.isDirectChat.equals(true))); ..where(groupMembers.contactId.equals(userId)));
return query.map((row) => row.readTable(groups)).getSingleOrNull(); return query.map((row) => row.readTable(groups)).getSingleOrNull();
} }
@ -208,9 +211,23 @@ class GroupsDao extends DatabaseAccessor<TwonlyDB> with _$GroupsDaoMixin {
), ),
); );
} }
Future<void> increaseLastMessageExchange(
String groupId,
DateTime newLastMessage,
) async {
await (update(groups)
..where(
(t) =>
t.groupId.equals(groupId) &
(t.lastMessageExchange.isSmallerThanValue(newLastMessage)),
))
.write(GroupsCompanion(lastMessageExchange: Value(newLastMessage)));
}
} }
int getFlameCounterFromGroup(Group group) { int getFlameCounterFromGroup(Group? group) {
if (group == null) return 0;
if (group.lastMessageSend == null || group.lastMessageReceived == null) { if (group.lastMessageSend == null || group.lastMessageReceived == null) {
return 0; return 0;
} }

View file

@ -32,7 +32,10 @@ class MessagesDao extends DatabaseAccessor<TwonlyDB> with _$MessagesDaoMixin {
Stream<List<Message>> watchMessageNotOpened(String groupId) { Stream<List<Message>> watchMessageNotOpened(String groupId) {
return (select(messages) return (select(messages)
..where((t) => t.openedAt.isNull() & t.groupId.equals(groupId)) ..where((t) =>
t.openedAt.isNull() &
t.groupId.equals(groupId) &
t.isDeletedFromSender.equals(false))
..orderBy([(t) => OrderingTerm.desc(t.createdAt)])) ..orderBy([(t) => OrderingTerm.desc(t.createdAt)]))
.watch(); .watch();
} }
@ -182,7 +185,8 @@ class MessagesDao extends DatabaseAccessor<TwonlyDB> with _$MessagesDaoMixin {
Log.error('Message does not exists or contact is not owner.'); Log.error('Message does not exists or contact is not owner.');
return; return;
} }
if (msg.mediaId != null) { if (msg.mediaId != null && contactId != null) {
// contactId -> When a image is send to multiple and one message is delete the image should be still available...
await (delete(mediaFiles)..where((t) => t.mediaId.equals(msg.mediaId!))) await (delete(mediaFiles)..where((t) => t.mediaId.equals(msg.mediaId!)))
.go(); .go();
@ -326,8 +330,8 @@ class MessagesDao extends DatabaseAccessor<TwonlyDB> with _$MessagesDaoMixin {
Future<void> updateMessageId( Future<void> updateMessageId(
String messageId, String messageId,
MessagesCompanion updatedValues, MessagesCompanion updatedValues,
) { ) async {
return (update(messages)..where((c) => c.messageId.equals(messageId))) await (update(messages)..where((c) => c.messageId.equals(messageId)))
.write(updatedValues); .write(updatedValues);
} }

View file

@ -52,6 +52,24 @@ class ReactionsDao extends DatabaseAccessor<TwonlyDB> with _$ReactionsDaoMixin {
.watch(); .watch();
} }
Stream<Reaction?> watchLastReactions(String groupId) {
final query = (select(reactions)
..orderBy([(t) => OrderingTerm.desc(t.createdAt)]))
.join(
[
innerJoin(
messages,
messages.messageId.equalsExp(reactions.messageId),
useColumns: false,
)
],
)
..where(messages.groupId.equals(groupId))
// ..orderBy([(t) => OrderingTerm.asc(t.createdAt)]))
..limit(1);
return query.map((row) => row.readTable(reactions)).watchSingleOrNull();
}
Stream<List<(Reaction, Contact?)>> watchReactionWithContacts( Stream<List<(Reaction, Contact?)>> watchReactionWithContacts(
String messageId, String messageId,
) { ) {

View file

@ -6,16 +6,19 @@ class Contacts extends Table {
TextColumn get username => text()(); TextColumn get username => text()();
TextColumn get displayName => text().nullable()(); TextColumn get displayName => text().nullable()();
TextColumn get nickName => text().nullable()(); TextColumn get nickName => text().nullable()();
TextColumn get avatarSvg => text().nullable()(); BlobColumn get avatarSvgCompressed => blob().nullable()();
IntColumn get senderProfileCounter => IntColumn get senderProfileCounter =>
integer().withDefault(const Constant(0))(); integer().withDefault(const Constant(0))();
BoolColumn get accepted => boolean().withDefault(const Constant(false))(); BoolColumn get accepted => boolean().withDefault(const Constant(false))();
BoolColumn get deletedByUser =>
boolean().withDefault(const Constant(false))();
BoolColumn get requested => boolean().withDefault(const Constant(false))(); BoolColumn get requested => boolean().withDefault(const Constant(false))();
BoolColumn get blocked => boolean().withDefault(const Constant(false))(); BoolColumn get blocked => boolean().withDefault(const Constant(false))();
BoolColumn get verified => boolean().withDefault(const Constant(false))(); BoolColumn get verified => boolean().withDefault(const Constant(false))();
BoolColumn get deleted => boolean().withDefault(const Constant(false))(); BoolColumn get accountDeleted =>
boolean().withDefault(const Constant(false))();
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)(); DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();

View file

@ -31,12 +31,12 @@ class $ContactsTable extends Contacts with TableInfo<$ContactsTable, Contact> {
late final GeneratedColumn<String> nickName = GeneratedColumn<String>( late final GeneratedColumn<String> nickName = GeneratedColumn<String>(
'nick_name', aliasedName, true, 'nick_name', aliasedName, true,
type: DriftSqlType.string, requiredDuringInsert: false); type: DriftSqlType.string, requiredDuringInsert: false);
static const VerificationMeta _avatarSvgMeta = static const VerificationMeta _avatarSvgCompressedMeta =
const VerificationMeta('avatarSvg'); const VerificationMeta('avatarSvgCompressed');
@override @override
late final GeneratedColumn<String> avatarSvg = GeneratedColumn<String>( late final GeneratedColumn<Uint8List> avatarSvgCompressed =
'avatar_svg', aliasedName, true, GeneratedColumn<Uint8List>('avatar_svg_compressed', aliasedName, true,
type: DriftSqlType.string, requiredDuringInsert: false); type: DriftSqlType.blob, requiredDuringInsert: false);
static const VerificationMeta _senderProfileCounterMeta = static const VerificationMeta _senderProfileCounterMeta =
const VerificationMeta('senderProfileCounter'); const VerificationMeta('senderProfileCounter');
@override @override
@ -55,6 +55,16 @@ class $ContactsTable extends Contacts with TableInfo<$ContactsTable, Contact> {
defaultConstraints: defaultConstraints:
GeneratedColumn.constraintIsAlways('CHECK ("accepted" IN (0, 1))'), GeneratedColumn.constraintIsAlways('CHECK ("accepted" IN (0, 1))'),
defaultValue: const Constant(false)); defaultValue: const Constant(false));
static const VerificationMeta _deletedByUserMeta =
const VerificationMeta('deletedByUser');
@override
late final GeneratedColumn<bool> deletedByUser = GeneratedColumn<bool>(
'deleted_by_user', aliasedName, false,
type: DriftSqlType.bool,
requiredDuringInsert: false,
defaultConstraints: GeneratedColumn.constraintIsAlways(
'CHECK ("deleted_by_user" IN (0, 1))'),
defaultValue: const Constant(false));
static const VerificationMeta _requestedMeta = static const VerificationMeta _requestedMeta =
const VerificationMeta('requested'); const VerificationMeta('requested');
@override @override
@ -85,15 +95,15 @@ class $ContactsTable extends Contacts with TableInfo<$ContactsTable, Contact> {
defaultConstraints: defaultConstraints:
GeneratedColumn.constraintIsAlways('CHECK ("verified" IN (0, 1))'), GeneratedColumn.constraintIsAlways('CHECK ("verified" IN (0, 1))'),
defaultValue: const Constant(false)); defaultValue: const Constant(false));
static const VerificationMeta _deletedMeta = static const VerificationMeta _accountDeletedMeta =
const VerificationMeta('deleted'); const VerificationMeta('accountDeleted');
@override @override
late final GeneratedColumn<bool> deleted = GeneratedColumn<bool>( late final GeneratedColumn<bool> accountDeleted = GeneratedColumn<bool>(
'deleted', aliasedName, false, 'account_deleted', aliasedName, false,
type: DriftSqlType.bool, type: DriftSqlType.bool,
requiredDuringInsert: false, requiredDuringInsert: false,
defaultConstraints: defaultConstraints: GeneratedColumn.constraintIsAlways(
GeneratedColumn.constraintIsAlways('CHECK ("deleted" IN (0, 1))'), 'CHECK ("account_deleted" IN (0, 1))'),
defaultValue: const Constant(false)); defaultValue: const Constant(false));
static const VerificationMeta _createdAtMeta = static const VerificationMeta _createdAtMeta =
const VerificationMeta('createdAt'); const VerificationMeta('createdAt');
@ -109,13 +119,14 @@ class $ContactsTable extends Contacts with TableInfo<$ContactsTable, Contact> {
username, username,
displayName, displayName,
nickName, nickName,
avatarSvg, avatarSvgCompressed,
senderProfileCounter, senderProfileCounter,
accepted, accepted,
deletedByUser,
requested, requested,
blocked, blocked,
verified, verified,
deleted, accountDeleted,
createdAt createdAt
]; ];
@override @override
@ -148,9 +159,11 @@ class $ContactsTable extends Contacts with TableInfo<$ContactsTable, Contact> {
context.handle(_nickNameMeta, context.handle(_nickNameMeta,
nickName.isAcceptableOrUnknown(data['nick_name']!, _nickNameMeta)); nickName.isAcceptableOrUnknown(data['nick_name']!, _nickNameMeta));
} }
if (data.containsKey('avatar_svg')) { if (data.containsKey('avatar_svg_compressed')) {
context.handle(_avatarSvgMeta, context.handle(
avatarSvg.isAcceptableOrUnknown(data['avatar_svg']!, _avatarSvgMeta)); _avatarSvgCompressedMeta,
avatarSvgCompressed.isAcceptableOrUnknown(
data['avatar_svg_compressed']!, _avatarSvgCompressedMeta));
} }
if (data.containsKey('sender_profile_counter')) { if (data.containsKey('sender_profile_counter')) {
context.handle( context.handle(
@ -162,6 +175,12 @@ class $ContactsTable extends Contacts with TableInfo<$ContactsTable, Contact> {
context.handle(_acceptedMeta, context.handle(_acceptedMeta,
accepted.isAcceptableOrUnknown(data['accepted']!, _acceptedMeta)); accepted.isAcceptableOrUnknown(data['accepted']!, _acceptedMeta));
} }
if (data.containsKey('deleted_by_user')) {
context.handle(
_deletedByUserMeta,
deletedByUser.isAcceptableOrUnknown(
data['deleted_by_user']!, _deletedByUserMeta));
}
if (data.containsKey('requested')) { if (data.containsKey('requested')) {
context.handle(_requestedMeta, context.handle(_requestedMeta,
requested.isAcceptableOrUnknown(data['requested']!, _requestedMeta)); requested.isAcceptableOrUnknown(data['requested']!, _requestedMeta));
@ -174,9 +193,11 @@ class $ContactsTable extends Contacts with TableInfo<$ContactsTable, Contact> {
context.handle(_verifiedMeta, context.handle(_verifiedMeta,
verified.isAcceptableOrUnknown(data['verified']!, _verifiedMeta)); verified.isAcceptableOrUnknown(data['verified']!, _verifiedMeta));
} }
if (data.containsKey('deleted')) { if (data.containsKey('account_deleted')) {
context.handle(_deletedMeta, context.handle(
deleted.isAcceptableOrUnknown(data['deleted']!, _deletedMeta)); _accountDeletedMeta,
accountDeleted.isAcceptableOrUnknown(
data['account_deleted']!, _accountDeletedMeta));
} }
if (data.containsKey('created_at')) { if (data.containsKey('created_at')) {
context.handle(_createdAtMeta, context.handle(_createdAtMeta,
@ -199,20 +220,22 @@ class $ContactsTable extends Contacts with TableInfo<$ContactsTable, Contact> {
.read(DriftSqlType.string, data['${effectivePrefix}display_name']), .read(DriftSqlType.string, data['${effectivePrefix}display_name']),
nickName: attachedDatabase.typeMapping nickName: attachedDatabase.typeMapping
.read(DriftSqlType.string, data['${effectivePrefix}nick_name']), .read(DriftSqlType.string, data['${effectivePrefix}nick_name']),
avatarSvg: attachedDatabase.typeMapping avatarSvgCompressed: attachedDatabase.typeMapping.read(
.read(DriftSqlType.string, data['${effectivePrefix}avatar_svg']), DriftSqlType.blob, data['${effectivePrefix}avatar_svg_compressed']),
senderProfileCounter: attachedDatabase.typeMapping.read( senderProfileCounter: attachedDatabase.typeMapping.read(
DriftSqlType.int, data['${effectivePrefix}sender_profile_counter'])!, DriftSqlType.int, data['${effectivePrefix}sender_profile_counter'])!,
accepted: attachedDatabase.typeMapping accepted: attachedDatabase.typeMapping
.read(DriftSqlType.bool, data['${effectivePrefix}accepted'])!, .read(DriftSqlType.bool, data['${effectivePrefix}accepted'])!,
deletedByUser: attachedDatabase.typeMapping
.read(DriftSqlType.bool, data['${effectivePrefix}deleted_by_user'])!,
requested: attachedDatabase.typeMapping requested: attachedDatabase.typeMapping
.read(DriftSqlType.bool, data['${effectivePrefix}requested'])!, .read(DriftSqlType.bool, data['${effectivePrefix}requested'])!,
blocked: attachedDatabase.typeMapping blocked: attachedDatabase.typeMapping
.read(DriftSqlType.bool, data['${effectivePrefix}blocked'])!, .read(DriftSqlType.bool, data['${effectivePrefix}blocked'])!,
verified: attachedDatabase.typeMapping verified: attachedDatabase.typeMapping
.read(DriftSqlType.bool, data['${effectivePrefix}verified'])!, .read(DriftSqlType.bool, data['${effectivePrefix}verified'])!,
deleted: attachedDatabase.typeMapping accountDeleted: attachedDatabase.typeMapping
.read(DriftSqlType.bool, data['${effectivePrefix}deleted'])!, .read(DriftSqlType.bool, data['${effectivePrefix}account_deleted'])!,
createdAt: attachedDatabase.typeMapping createdAt: attachedDatabase.typeMapping
.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!,
); );
@ -229,26 +252,28 @@ class Contact extends DataClass implements Insertable<Contact> {
final String username; final String username;
final String? displayName; final String? displayName;
final String? nickName; final String? nickName;
final String? avatarSvg; final Uint8List? avatarSvgCompressed;
final int senderProfileCounter; final int senderProfileCounter;
final bool accepted; final bool accepted;
final bool deletedByUser;
final bool requested; final bool requested;
final bool blocked; final bool blocked;
final bool verified; final bool verified;
final bool deleted; final bool accountDeleted;
final DateTime createdAt; final DateTime createdAt;
const Contact( const Contact(
{required this.userId, {required this.userId,
required this.username, required this.username,
this.displayName, this.displayName,
this.nickName, this.nickName,
this.avatarSvg, this.avatarSvgCompressed,
required this.senderProfileCounter, required this.senderProfileCounter,
required this.accepted, required this.accepted,
required this.deletedByUser,
required this.requested, required this.requested,
required this.blocked, required this.blocked,
required this.verified, required this.verified,
required this.deleted, required this.accountDeleted,
required this.createdAt}); required this.createdAt});
@override @override
Map<String, Expression> toColumns(bool nullToAbsent) { Map<String, Expression> toColumns(bool nullToAbsent) {
@ -261,15 +286,16 @@ class Contact extends DataClass implements Insertable<Contact> {
if (!nullToAbsent || nickName != null) { if (!nullToAbsent || nickName != null) {
map['nick_name'] = Variable<String>(nickName); map['nick_name'] = Variable<String>(nickName);
} }
if (!nullToAbsent || avatarSvg != null) { if (!nullToAbsent || avatarSvgCompressed != null) {
map['avatar_svg'] = Variable<String>(avatarSvg); map['avatar_svg_compressed'] = Variable<Uint8List>(avatarSvgCompressed);
} }
map['sender_profile_counter'] = Variable<int>(senderProfileCounter); map['sender_profile_counter'] = Variable<int>(senderProfileCounter);
map['accepted'] = Variable<bool>(accepted); map['accepted'] = Variable<bool>(accepted);
map['deleted_by_user'] = Variable<bool>(deletedByUser);
map['requested'] = Variable<bool>(requested); map['requested'] = Variable<bool>(requested);
map['blocked'] = Variable<bool>(blocked); map['blocked'] = Variable<bool>(blocked);
map['verified'] = Variable<bool>(verified); map['verified'] = Variable<bool>(verified);
map['deleted'] = Variable<bool>(deleted); map['account_deleted'] = Variable<bool>(accountDeleted);
map['created_at'] = Variable<DateTime>(createdAt); map['created_at'] = Variable<DateTime>(createdAt);
return map; return map;
} }
@ -284,15 +310,16 @@ class Contact extends DataClass implements Insertable<Contact> {
nickName: nickName == null && nullToAbsent nickName: nickName == null && nullToAbsent
? const Value.absent() ? const Value.absent()
: Value(nickName), : Value(nickName),
avatarSvg: avatarSvg == null && nullToAbsent avatarSvgCompressed: avatarSvgCompressed == null && nullToAbsent
? const Value.absent() ? const Value.absent()
: Value(avatarSvg), : Value(avatarSvgCompressed),
senderProfileCounter: Value(senderProfileCounter), senderProfileCounter: Value(senderProfileCounter),
accepted: Value(accepted), accepted: Value(accepted),
deletedByUser: Value(deletedByUser),
requested: Value(requested), requested: Value(requested),
blocked: Value(blocked), blocked: Value(blocked),
verified: Value(verified), verified: Value(verified),
deleted: Value(deleted), accountDeleted: Value(accountDeleted),
createdAt: Value(createdAt), createdAt: Value(createdAt),
); );
} }
@ -305,14 +332,16 @@ class Contact extends DataClass implements Insertable<Contact> {
username: serializer.fromJson<String>(json['username']), username: serializer.fromJson<String>(json['username']),
displayName: serializer.fromJson<String?>(json['displayName']), displayName: serializer.fromJson<String?>(json['displayName']),
nickName: serializer.fromJson<String?>(json['nickName']), nickName: serializer.fromJson<String?>(json['nickName']),
avatarSvg: serializer.fromJson<String?>(json['avatarSvg']), avatarSvgCompressed:
serializer.fromJson<Uint8List?>(json['avatarSvgCompressed']),
senderProfileCounter: senderProfileCounter:
serializer.fromJson<int>(json['senderProfileCounter']), serializer.fromJson<int>(json['senderProfileCounter']),
accepted: serializer.fromJson<bool>(json['accepted']), accepted: serializer.fromJson<bool>(json['accepted']),
deletedByUser: serializer.fromJson<bool>(json['deletedByUser']),
requested: serializer.fromJson<bool>(json['requested']), requested: serializer.fromJson<bool>(json['requested']),
blocked: serializer.fromJson<bool>(json['blocked']), blocked: serializer.fromJson<bool>(json['blocked']),
verified: serializer.fromJson<bool>(json['verified']), verified: serializer.fromJson<bool>(json['verified']),
deleted: serializer.fromJson<bool>(json['deleted']), accountDeleted: serializer.fromJson<bool>(json['accountDeleted']),
createdAt: serializer.fromJson<DateTime>(json['createdAt']), createdAt: serializer.fromJson<DateTime>(json['createdAt']),
); );
} }
@ -324,13 +353,14 @@ class Contact extends DataClass implements Insertable<Contact> {
'username': serializer.toJson<String>(username), 'username': serializer.toJson<String>(username),
'displayName': serializer.toJson<String?>(displayName), 'displayName': serializer.toJson<String?>(displayName),
'nickName': serializer.toJson<String?>(nickName), 'nickName': serializer.toJson<String?>(nickName),
'avatarSvg': serializer.toJson<String?>(avatarSvg), 'avatarSvgCompressed': serializer.toJson<Uint8List?>(avatarSvgCompressed),
'senderProfileCounter': serializer.toJson<int>(senderProfileCounter), 'senderProfileCounter': serializer.toJson<int>(senderProfileCounter),
'accepted': serializer.toJson<bool>(accepted), 'accepted': serializer.toJson<bool>(accepted),
'deletedByUser': serializer.toJson<bool>(deletedByUser),
'requested': serializer.toJson<bool>(requested), 'requested': serializer.toJson<bool>(requested),
'blocked': serializer.toJson<bool>(blocked), 'blocked': serializer.toJson<bool>(blocked),
'verified': serializer.toJson<bool>(verified), 'verified': serializer.toJson<bool>(verified),
'deleted': serializer.toJson<bool>(deleted), 'accountDeleted': serializer.toJson<bool>(accountDeleted),
'createdAt': serializer.toJson<DateTime>(createdAt), 'createdAt': serializer.toJson<DateTime>(createdAt),
}; };
} }
@ -340,26 +370,30 @@ class Contact extends DataClass implements Insertable<Contact> {
String? username, String? username,
Value<String?> displayName = const Value.absent(), Value<String?> displayName = const Value.absent(),
Value<String?> nickName = const Value.absent(), Value<String?> nickName = const Value.absent(),
Value<String?> avatarSvg = const Value.absent(), Value<Uint8List?> avatarSvgCompressed = const Value.absent(),
int? senderProfileCounter, int? senderProfileCounter,
bool? accepted, bool? accepted,
bool? deletedByUser,
bool? requested, bool? requested,
bool? blocked, bool? blocked,
bool? verified, bool? verified,
bool? deleted, bool? accountDeleted,
DateTime? createdAt}) => DateTime? createdAt}) =>
Contact( Contact(
userId: userId ?? this.userId, userId: userId ?? this.userId,
username: username ?? this.username, username: username ?? this.username,
displayName: displayName.present ? displayName.value : this.displayName, displayName: displayName.present ? displayName.value : this.displayName,
nickName: nickName.present ? nickName.value : this.nickName, nickName: nickName.present ? nickName.value : this.nickName,
avatarSvg: avatarSvg.present ? avatarSvg.value : this.avatarSvg, avatarSvgCompressed: avatarSvgCompressed.present
? avatarSvgCompressed.value
: this.avatarSvgCompressed,
senderProfileCounter: senderProfileCounter ?? this.senderProfileCounter, senderProfileCounter: senderProfileCounter ?? this.senderProfileCounter,
accepted: accepted ?? this.accepted, accepted: accepted ?? this.accepted,
deletedByUser: deletedByUser ?? this.deletedByUser,
requested: requested ?? this.requested, requested: requested ?? this.requested,
blocked: blocked ?? this.blocked, blocked: blocked ?? this.blocked,
verified: verified ?? this.verified, verified: verified ?? this.verified,
deleted: deleted ?? this.deleted, accountDeleted: accountDeleted ?? this.accountDeleted,
createdAt: createdAt ?? this.createdAt, createdAt: createdAt ?? this.createdAt,
); );
Contact copyWithCompanion(ContactsCompanion data) { Contact copyWithCompanion(ContactsCompanion data) {
@ -369,15 +403,22 @@ class Contact extends DataClass implements Insertable<Contact> {
displayName: displayName:
data.displayName.present ? data.displayName.value : this.displayName, data.displayName.present ? data.displayName.value : this.displayName,
nickName: data.nickName.present ? data.nickName.value : this.nickName, nickName: data.nickName.present ? data.nickName.value : this.nickName,
avatarSvg: data.avatarSvg.present ? data.avatarSvg.value : this.avatarSvg, avatarSvgCompressed: data.avatarSvgCompressed.present
? data.avatarSvgCompressed.value
: this.avatarSvgCompressed,
senderProfileCounter: data.senderProfileCounter.present senderProfileCounter: data.senderProfileCounter.present
? data.senderProfileCounter.value ? data.senderProfileCounter.value
: this.senderProfileCounter, : this.senderProfileCounter,
accepted: data.accepted.present ? data.accepted.value : this.accepted, accepted: data.accepted.present ? data.accepted.value : this.accepted,
deletedByUser: data.deletedByUser.present
? data.deletedByUser.value
: this.deletedByUser,
requested: data.requested.present ? data.requested.value : this.requested, requested: data.requested.present ? data.requested.value : this.requested,
blocked: data.blocked.present ? data.blocked.value : this.blocked, blocked: data.blocked.present ? data.blocked.value : this.blocked,
verified: data.verified.present ? data.verified.value : this.verified, verified: data.verified.present ? data.verified.value : this.verified,
deleted: data.deleted.present ? data.deleted.value : this.deleted, accountDeleted: data.accountDeleted.present
? data.accountDeleted.value
: this.accountDeleted,
createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
); );
} }
@ -389,13 +430,14 @@ class Contact extends DataClass implements Insertable<Contact> {
..write('username: $username, ') ..write('username: $username, ')
..write('displayName: $displayName, ') ..write('displayName: $displayName, ')
..write('nickName: $nickName, ') ..write('nickName: $nickName, ')
..write('avatarSvg: $avatarSvg, ') ..write('avatarSvgCompressed: $avatarSvgCompressed, ')
..write('senderProfileCounter: $senderProfileCounter, ') ..write('senderProfileCounter: $senderProfileCounter, ')
..write('accepted: $accepted, ') ..write('accepted: $accepted, ')
..write('deletedByUser: $deletedByUser, ')
..write('requested: $requested, ') ..write('requested: $requested, ')
..write('blocked: $blocked, ') ..write('blocked: $blocked, ')
..write('verified: $verified, ') ..write('verified: $verified, ')
..write('deleted: $deleted, ') ..write('accountDeleted: $accountDeleted, ')
..write('createdAt: $createdAt') ..write('createdAt: $createdAt')
..write(')')) ..write(')'))
.toString(); .toString();
@ -407,13 +449,14 @@ class Contact extends DataClass implements Insertable<Contact> {
username, username,
displayName, displayName,
nickName, nickName,
avatarSvg, $driftBlobEquality.hash(avatarSvgCompressed),
senderProfileCounter, senderProfileCounter,
accepted, accepted,
deletedByUser,
requested, requested,
blocked, blocked,
verified, verified,
deleted, accountDeleted,
createdAt); createdAt);
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
@ -423,13 +466,15 @@ class Contact extends DataClass implements Insertable<Contact> {
other.username == this.username && other.username == this.username &&
other.displayName == this.displayName && other.displayName == this.displayName &&
other.nickName == this.nickName && other.nickName == this.nickName &&
other.avatarSvg == this.avatarSvg && $driftBlobEquality.equals(
other.avatarSvgCompressed, this.avatarSvgCompressed) &&
other.senderProfileCounter == this.senderProfileCounter && other.senderProfileCounter == this.senderProfileCounter &&
other.accepted == this.accepted && other.accepted == this.accepted &&
other.deletedByUser == this.deletedByUser &&
other.requested == this.requested && other.requested == this.requested &&
other.blocked == this.blocked && other.blocked == this.blocked &&
other.verified == this.verified && other.verified == this.verified &&
other.deleted == this.deleted && other.accountDeleted == this.accountDeleted &&
other.createdAt == this.createdAt); other.createdAt == this.createdAt);
} }
@ -438,26 +483,28 @@ class ContactsCompanion extends UpdateCompanion<Contact> {
final Value<String> username; final Value<String> username;
final Value<String?> displayName; final Value<String?> displayName;
final Value<String?> nickName; final Value<String?> nickName;
final Value<String?> avatarSvg; final Value<Uint8List?> avatarSvgCompressed;
final Value<int> senderProfileCounter; final Value<int> senderProfileCounter;
final Value<bool> accepted; final Value<bool> accepted;
final Value<bool> deletedByUser;
final Value<bool> requested; final Value<bool> requested;
final Value<bool> blocked; final Value<bool> blocked;
final Value<bool> verified; final Value<bool> verified;
final Value<bool> deleted; final Value<bool> accountDeleted;
final Value<DateTime> createdAt; final Value<DateTime> createdAt;
const ContactsCompanion({ const ContactsCompanion({
this.userId = const Value.absent(), this.userId = const Value.absent(),
this.username = const Value.absent(), this.username = const Value.absent(),
this.displayName = const Value.absent(), this.displayName = const Value.absent(),
this.nickName = const Value.absent(), this.nickName = const Value.absent(),
this.avatarSvg = const Value.absent(), this.avatarSvgCompressed = const Value.absent(),
this.senderProfileCounter = const Value.absent(), this.senderProfileCounter = const Value.absent(),
this.accepted = const Value.absent(), this.accepted = const Value.absent(),
this.deletedByUser = const Value.absent(),
this.requested = const Value.absent(), this.requested = const Value.absent(),
this.blocked = const Value.absent(), this.blocked = const Value.absent(),
this.verified = const Value.absent(), this.verified = const Value.absent(),
this.deleted = const Value.absent(), this.accountDeleted = const Value.absent(),
this.createdAt = const Value.absent(), this.createdAt = const Value.absent(),
}); });
ContactsCompanion.insert({ ContactsCompanion.insert({
@ -465,13 +512,14 @@ class ContactsCompanion extends UpdateCompanion<Contact> {
required String username, required String username,
this.displayName = const Value.absent(), this.displayName = const Value.absent(),
this.nickName = const Value.absent(), this.nickName = const Value.absent(),
this.avatarSvg = const Value.absent(), this.avatarSvgCompressed = const Value.absent(),
this.senderProfileCounter = const Value.absent(), this.senderProfileCounter = const Value.absent(),
this.accepted = const Value.absent(), this.accepted = const Value.absent(),
this.deletedByUser = const Value.absent(),
this.requested = const Value.absent(), this.requested = const Value.absent(),
this.blocked = const Value.absent(), this.blocked = const Value.absent(),
this.verified = const Value.absent(), this.verified = const Value.absent(),
this.deleted = const Value.absent(), this.accountDeleted = const Value.absent(),
this.createdAt = const Value.absent(), this.createdAt = const Value.absent(),
}) : username = Value(username); }) : username = Value(username);
static Insertable<Contact> custom({ static Insertable<Contact> custom({
@ -479,13 +527,14 @@ class ContactsCompanion extends UpdateCompanion<Contact> {
Expression<String>? username, Expression<String>? username,
Expression<String>? displayName, Expression<String>? displayName,
Expression<String>? nickName, Expression<String>? nickName,
Expression<String>? avatarSvg, Expression<Uint8List>? avatarSvgCompressed,
Expression<int>? senderProfileCounter, Expression<int>? senderProfileCounter,
Expression<bool>? accepted, Expression<bool>? accepted,
Expression<bool>? deletedByUser,
Expression<bool>? requested, Expression<bool>? requested,
Expression<bool>? blocked, Expression<bool>? blocked,
Expression<bool>? verified, Expression<bool>? verified,
Expression<bool>? deleted, Expression<bool>? accountDeleted,
Expression<DateTime>? createdAt, Expression<DateTime>? createdAt,
}) { }) {
return RawValuesInsertable({ return RawValuesInsertable({
@ -493,14 +542,16 @@ class ContactsCompanion extends UpdateCompanion<Contact> {
if (username != null) 'username': username, if (username != null) 'username': username,
if (displayName != null) 'display_name': displayName, if (displayName != null) 'display_name': displayName,
if (nickName != null) 'nick_name': nickName, if (nickName != null) 'nick_name': nickName,
if (avatarSvg != null) 'avatar_svg': avatarSvg, if (avatarSvgCompressed != null)
'avatar_svg_compressed': avatarSvgCompressed,
if (senderProfileCounter != null) if (senderProfileCounter != null)
'sender_profile_counter': senderProfileCounter, 'sender_profile_counter': senderProfileCounter,
if (accepted != null) 'accepted': accepted, if (accepted != null) 'accepted': accepted,
if (deletedByUser != null) 'deleted_by_user': deletedByUser,
if (requested != null) 'requested': requested, if (requested != null) 'requested': requested,
if (blocked != null) 'blocked': blocked, if (blocked != null) 'blocked': blocked,
if (verified != null) 'verified': verified, if (verified != null) 'verified': verified,
if (deleted != null) 'deleted': deleted, if (accountDeleted != null) 'account_deleted': accountDeleted,
if (createdAt != null) 'created_at': createdAt, if (createdAt != null) 'created_at': createdAt,
}); });
} }
@ -510,26 +561,28 @@ class ContactsCompanion extends UpdateCompanion<Contact> {
Value<String>? username, Value<String>? username,
Value<String?>? displayName, Value<String?>? displayName,
Value<String?>? nickName, Value<String?>? nickName,
Value<String?>? avatarSvg, Value<Uint8List?>? avatarSvgCompressed,
Value<int>? senderProfileCounter, Value<int>? senderProfileCounter,
Value<bool>? accepted, Value<bool>? accepted,
Value<bool>? deletedByUser,
Value<bool>? requested, Value<bool>? requested,
Value<bool>? blocked, Value<bool>? blocked,
Value<bool>? verified, Value<bool>? verified,
Value<bool>? deleted, Value<bool>? accountDeleted,
Value<DateTime>? createdAt}) { Value<DateTime>? createdAt}) {
return ContactsCompanion( return ContactsCompanion(
userId: userId ?? this.userId, userId: userId ?? this.userId,
username: username ?? this.username, username: username ?? this.username,
displayName: displayName ?? this.displayName, displayName: displayName ?? this.displayName,
nickName: nickName ?? this.nickName, nickName: nickName ?? this.nickName,
avatarSvg: avatarSvg ?? this.avatarSvg, avatarSvgCompressed: avatarSvgCompressed ?? this.avatarSvgCompressed,
senderProfileCounter: senderProfileCounter ?? this.senderProfileCounter, senderProfileCounter: senderProfileCounter ?? this.senderProfileCounter,
accepted: accepted ?? this.accepted, accepted: accepted ?? this.accepted,
deletedByUser: deletedByUser ?? this.deletedByUser,
requested: requested ?? this.requested, requested: requested ?? this.requested,
blocked: blocked ?? this.blocked, blocked: blocked ?? this.blocked,
verified: verified ?? this.verified, verified: verified ?? this.verified,
deleted: deleted ?? this.deleted, accountDeleted: accountDeleted ?? this.accountDeleted,
createdAt: createdAt ?? this.createdAt, createdAt: createdAt ?? this.createdAt,
); );
} }
@ -549,8 +602,9 @@ class ContactsCompanion extends UpdateCompanion<Contact> {
if (nickName.present) { if (nickName.present) {
map['nick_name'] = Variable<String>(nickName.value); map['nick_name'] = Variable<String>(nickName.value);
} }
if (avatarSvg.present) { if (avatarSvgCompressed.present) {
map['avatar_svg'] = Variable<String>(avatarSvg.value); map['avatar_svg_compressed'] =
Variable<Uint8List>(avatarSvgCompressed.value);
} }
if (senderProfileCounter.present) { if (senderProfileCounter.present) {
map['sender_profile_counter'] = Variable<int>(senderProfileCounter.value); map['sender_profile_counter'] = Variable<int>(senderProfileCounter.value);
@ -558,6 +612,9 @@ class ContactsCompanion extends UpdateCompanion<Contact> {
if (accepted.present) { if (accepted.present) {
map['accepted'] = Variable<bool>(accepted.value); map['accepted'] = Variable<bool>(accepted.value);
} }
if (deletedByUser.present) {
map['deleted_by_user'] = Variable<bool>(deletedByUser.value);
}
if (requested.present) { if (requested.present) {
map['requested'] = Variable<bool>(requested.value); map['requested'] = Variable<bool>(requested.value);
} }
@ -567,8 +624,8 @@ class ContactsCompanion extends UpdateCompanion<Contact> {
if (verified.present) { if (verified.present) {
map['verified'] = Variable<bool>(verified.value); map['verified'] = Variable<bool>(verified.value);
} }
if (deleted.present) { if (accountDeleted.present) {
map['deleted'] = Variable<bool>(deleted.value); map['account_deleted'] = Variable<bool>(accountDeleted.value);
} }
if (createdAt.present) { if (createdAt.present) {
map['created_at'] = Variable<DateTime>(createdAt.value); map['created_at'] = Variable<DateTime>(createdAt.value);
@ -583,13 +640,14 @@ class ContactsCompanion extends UpdateCompanion<Contact> {
..write('username: $username, ') ..write('username: $username, ')
..write('displayName: $displayName, ') ..write('displayName: $displayName, ')
..write('nickName: $nickName, ') ..write('nickName: $nickName, ')
..write('avatarSvg: $avatarSvg, ') ..write('avatarSvgCompressed: $avatarSvgCompressed, ')
..write('senderProfileCounter: $senderProfileCounter, ') ..write('senderProfileCounter: $senderProfileCounter, ')
..write('accepted: $accepted, ') ..write('accepted: $accepted, ')
..write('deletedByUser: $deletedByUser, ')
..write('requested: $requested, ') ..write('requested: $requested, ')
..write('blocked: $blocked, ') ..write('blocked: $blocked, ')
..write('verified: $verified, ') ..write('verified: $verified, ')
..write('deleted: $deleted, ') ..write('accountDeleted: $accountDeleted, ')
..write('createdAt: $createdAt') ..write('createdAt: $createdAt')
..write(')')) ..write(')'))
.toString(); .toString();
@ -6533,13 +6591,14 @@ typedef $$ContactsTableCreateCompanionBuilder = ContactsCompanion Function({
required String username, required String username,
Value<String?> displayName, Value<String?> displayName,
Value<String?> nickName, Value<String?> nickName,
Value<String?> avatarSvg, Value<Uint8List?> avatarSvgCompressed,
Value<int> senderProfileCounter, Value<int> senderProfileCounter,
Value<bool> accepted, Value<bool> accepted,
Value<bool> deletedByUser,
Value<bool> requested, Value<bool> requested,
Value<bool> blocked, Value<bool> blocked,
Value<bool> verified, Value<bool> verified,
Value<bool> deleted, Value<bool> accountDeleted,
Value<DateTime> createdAt, Value<DateTime> createdAt,
}); });
typedef $$ContactsTableUpdateCompanionBuilder = ContactsCompanion Function({ typedef $$ContactsTableUpdateCompanionBuilder = ContactsCompanion Function({
@ -6547,13 +6606,14 @@ typedef $$ContactsTableUpdateCompanionBuilder = ContactsCompanion Function({
Value<String> username, Value<String> username,
Value<String?> displayName, Value<String?> displayName,
Value<String?> nickName, Value<String?> nickName,
Value<String?> avatarSvg, Value<Uint8List?> avatarSvgCompressed,
Value<int> senderProfileCounter, Value<int> senderProfileCounter,
Value<bool> accepted, Value<bool> accepted,
Value<bool> deletedByUser,
Value<bool> requested, Value<bool> requested,
Value<bool> blocked, Value<bool> blocked,
Value<bool> verified, Value<bool> verified,
Value<bool> deleted, Value<bool> accountDeleted,
Value<DateTime> createdAt, Value<DateTime> createdAt,
}); });
@ -6684,8 +6744,9 @@ class $$ContactsTableFilterComposer
ColumnFilters<String> get nickName => $composableBuilder( ColumnFilters<String> get nickName => $composableBuilder(
column: $table.nickName, builder: (column) => ColumnFilters(column)); column: $table.nickName, builder: (column) => ColumnFilters(column));
ColumnFilters<String> get avatarSvg => $composableBuilder( ColumnFilters<Uint8List> get avatarSvgCompressed => $composableBuilder(
column: $table.avatarSvg, builder: (column) => ColumnFilters(column)); column: $table.avatarSvgCompressed,
builder: (column) => ColumnFilters(column));
ColumnFilters<int> get senderProfileCounter => $composableBuilder( ColumnFilters<int> get senderProfileCounter => $composableBuilder(
column: $table.senderProfileCounter, column: $table.senderProfileCounter,
@ -6694,6 +6755,9 @@ class $$ContactsTableFilterComposer
ColumnFilters<bool> get accepted => $composableBuilder( ColumnFilters<bool> get accepted => $composableBuilder(
column: $table.accepted, builder: (column) => ColumnFilters(column)); column: $table.accepted, builder: (column) => ColumnFilters(column));
ColumnFilters<bool> get deletedByUser => $composableBuilder(
column: $table.deletedByUser, builder: (column) => ColumnFilters(column));
ColumnFilters<bool> get requested => $composableBuilder( ColumnFilters<bool> get requested => $composableBuilder(
column: $table.requested, builder: (column) => ColumnFilters(column)); column: $table.requested, builder: (column) => ColumnFilters(column));
@ -6703,8 +6767,9 @@ class $$ContactsTableFilterComposer
ColumnFilters<bool> get verified => $composableBuilder( ColumnFilters<bool> get verified => $composableBuilder(
column: $table.verified, builder: (column) => ColumnFilters(column)); column: $table.verified, builder: (column) => ColumnFilters(column));
ColumnFilters<bool> get deleted => $composableBuilder( ColumnFilters<bool> get accountDeleted => $composableBuilder(
column: $table.deleted, builder: (column) => ColumnFilters(column)); column: $table.accountDeleted,
builder: (column) => ColumnFilters(column));
ColumnFilters<DateTime> get createdAt => $composableBuilder( ColumnFilters<DateTime> get createdAt => $composableBuilder(
column: $table.createdAt, builder: (column) => ColumnFilters(column)); column: $table.createdAt, builder: (column) => ColumnFilters(column));
@ -6861,8 +6926,9 @@ class $$ContactsTableOrderingComposer
ColumnOrderings<String> get nickName => $composableBuilder( ColumnOrderings<String> get nickName => $composableBuilder(
column: $table.nickName, builder: (column) => ColumnOrderings(column)); column: $table.nickName, builder: (column) => ColumnOrderings(column));
ColumnOrderings<String> get avatarSvg => $composableBuilder( ColumnOrderings<Uint8List> get avatarSvgCompressed => $composableBuilder(
column: $table.avatarSvg, builder: (column) => ColumnOrderings(column)); column: $table.avatarSvgCompressed,
builder: (column) => ColumnOrderings(column));
ColumnOrderings<int> get senderProfileCounter => $composableBuilder( ColumnOrderings<int> get senderProfileCounter => $composableBuilder(
column: $table.senderProfileCounter, column: $table.senderProfileCounter,
@ -6871,6 +6937,10 @@ class $$ContactsTableOrderingComposer
ColumnOrderings<bool> get accepted => $composableBuilder( ColumnOrderings<bool> get accepted => $composableBuilder(
column: $table.accepted, builder: (column) => ColumnOrderings(column)); column: $table.accepted, builder: (column) => ColumnOrderings(column));
ColumnOrderings<bool> get deletedByUser => $composableBuilder(
column: $table.deletedByUser,
builder: (column) => ColumnOrderings(column));
ColumnOrderings<bool> get requested => $composableBuilder( ColumnOrderings<bool> get requested => $composableBuilder(
column: $table.requested, builder: (column) => ColumnOrderings(column)); column: $table.requested, builder: (column) => ColumnOrderings(column));
@ -6880,8 +6950,9 @@ class $$ContactsTableOrderingComposer
ColumnOrderings<bool> get verified => $composableBuilder( ColumnOrderings<bool> get verified => $composableBuilder(
column: $table.verified, builder: (column) => ColumnOrderings(column)); column: $table.verified, builder: (column) => ColumnOrderings(column));
ColumnOrderings<bool> get deleted => $composableBuilder( ColumnOrderings<bool> get accountDeleted => $composableBuilder(
column: $table.deleted, builder: (column) => ColumnOrderings(column)); column: $table.accountDeleted,
builder: (column) => ColumnOrderings(column));
ColumnOrderings<DateTime> get createdAt => $composableBuilder( ColumnOrderings<DateTime> get createdAt => $composableBuilder(
column: $table.createdAt, builder: (column) => ColumnOrderings(column)); column: $table.createdAt, builder: (column) => ColumnOrderings(column));
@ -6908,8 +6979,8 @@ class $$ContactsTableAnnotationComposer
GeneratedColumn<String> get nickName => GeneratedColumn<String> get nickName =>
$composableBuilder(column: $table.nickName, builder: (column) => column); $composableBuilder(column: $table.nickName, builder: (column) => column);
GeneratedColumn<String> get avatarSvg => GeneratedColumn<Uint8List> get avatarSvgCompressed => $composableBuilder(
$composableBuilder(column: $table.avatarSvg, builder: (column) => column); column: $table.avatarSvgCompressed, builder: (column) => column);
GeneratedColumn<int> get senderProfileCounter => $composableBuilder( GeneratedColumn<int> get senderProfileCounter => $composableBuilder(
column: $table.senderProfileCounter, builder: (column) => column); column: $table.senderProfileCounter, builder: (column) => column);
@ -6917,6 +6988,9 @@ class $$ContactsTableAnnotationComposer
GeneratedColumn<bool> get accepted => GeneratedColumn<bool> get accepted =>
$composableBuilder(column: $table.accepted, builder: (column) => column); $composableBuilder(column: $table.accepted, builder: (column) => column);
GeneratedColumn<bool> get deletedByUser => $composableBuilder(
column: $table.deletedByUser, builder: (column) => column);
GeneratedColumn<bool> get requested => GeneratedColumn<bool> get requested =>
$composableBuilder(column: $table.requested, builder: (column) => column); $composableBuilder(column: $table.requested, builder: (column) => column);
@ -6926,8 +7000,8 @@ class $$ContactsTableAnnotationComposer
GeneratedColumn<bool> get verified => GeneratedColumn<bool> get verified =>
$composableBuilder(column: $table.verified, builder: (column) => column); $composableBuilder(column: $table.verified, builder: (column) => column);
GeneratedColumn<bool> get deleted => GeneratedColumn<bool> get accountDeleted => $composableBuilder(
$composableBuilder(column: $table.deleted, builder: (column) => column); column: $table.accountDeleted, builder: (column) => column);
GeneratedColumn<DateTime> get createdAt => GeneratedColumn<DateTime> get createdAt =>
$composableBuilder(column: $table.createdAt, builder: (column) => column); $composableBuilder(column: $table.createdAt, builder: (column) => column);
@ -7097,13 +7171,14 @@ class $$ContactsTableTableManager extends RootTableManager<
Value<String> username = const Value.absent(), Value<String> username = const Value.absent(),
Value<String?> displayName = const Value.absent(), Value<String?> displayName = const Value.absent(),
Value<String?> nickName = const Value.absent(), Value<String?> nickName = const Value.absent(),
Value<String?> avatarSvg = const Value.absent(), Value<Uint8List?> avatarSvgCompressed = const Value.absent(),
Value<int> senderProfileCounter = const Value.absent(), Value<int> senderProfileCounter = const Value.absent(),
Value<bool> accepted = const Value.absent(), Value<bool> accepted = const Value.absent(),
Value<bool> deletedByUser = const Value.absent(),
Value<bool> requested = const Value.absent(), Value<bool> requested = const Value.absent(),
Value<bool> blocked = const Value.absent(), Value<bool> blocked = const Value.absent(),
Value<bool> verified = const Value.absent(), Value<bool> verified = const Value.absent(),
Value<bool> deleted = const Value.absent(), Value<bool> accountDeleted = const Value.absent(),
Value<DateTime> createdAt = const Value.absent(), Value<DateTime> createdAt = const Value.absent(),
}) => }) =>
ContactsCompanion( ContactsCompanion(
@ -7111,13 +7186,14 @@ class $$ContactsTableTableManager extends RootTableManager<
username: username, username: username,
displayName: displayName, displayName: displayName,
nickName: nickName, nickName: nickName,
avatarSvg: avatarSvg, avatarSvgCompressed: avatarSvgCompressed,
senderProfileCounter: senderProfileCounter, senderProfileCounter: senderProfileCounter,
accepted: accepted, accepted: accepted,
deletedByUser: deletedByUser,
requested: requested, requested: requested,
blocked: blocked, blocked: blocked,
verified: verified, verified: verified,
deleted: deleted, accountDeleted: accountDeleted,
createdAt: createdAt, createdAt: createdAt,
), ),
createCompanionCallback: ({ createCompanionCallback: ({
@ -7125,13 +7201,14 @@ class $$ContactsTableTableManager extends RootTableManager<
required String username, required String username,
Value<String?> displayName = const Value.absent(), Value<String?> displayName = const Value.absent(),
Value<String?> nickName = const Value.absent(), Value<String?> nickName = const Value.absent(),
Value<String?> avatarSvg = const Value.absent(), Value<Uint8List?> avatarSvgCompressed = const Value.absent(),
Value<int> senderProfileCounter = const Value.absent(), Value<int> senderProfileCounter = const Value.absent(),
Value<bool> accepted = const Value.absent(), Value<bool> accepted = const Value.absent(),
Value<bool> deletedByUser = const Value.absent(),
Value<bool> requested = const Value.absent(), Value<bool> requested = const Value.absent(),
Value<bool> blocked = const Value.absent(), Value<bool> blocked = const Value.absent(),
Value<bool> verified = const Value.absent(), Value<bool> verified = const Value.absent(),
Value<bool> deleted = const Value.absent(), Value<bool> accountDeleted = const Value.absent(),
Value<DateTime> createdAt = const Value.absent(), Value<DateTime> createdAt = const Value.absent(),
}) => }) =>
ContactsCompanion.insert( ContactsCompanion.insert(
@ -7139,13 +7216,14 @@ class $$ContactsTableTableManager extends RootTableManager<
username: username, username: username,
displayName: displayName, displayName: displayName,
nickName: nickName, nickName: nickName,
avatarSvg: avatarSvg, avatarSvgCompressed: avatarSvgCompressed,
senderProfileCounter: senderProfileCounter, senderProfileCounter: senderProfileCounter,
accepted: accepted, accepted: accepted,
deletedByUser: deletedByUser,
requested: requested, requested: requested,
blocked: blocked, blocked: blocked,
verified: verified, verified: verified,
deleted: deleted, accountDeleted: accountDeleted,
createdAt: createdAt, createdAt: createdAt,
), ),
withReferenceMapper: (p0) => p0 withReferenceMapper: (p0) => p0

View file

@ -346,6 +346,7 @@
"tabToRemoveEmoji": "Tippen um zu entfernen", "tabToRemoveEmoji": "Tippen um zu entfernen",
"quotedMessageWasDeleted": "Die zitierte Nachricht wurde gelöscht.", "quotedMessageWasDeleted": "Die zitierte Nachricht wurde gelöscht.",
"messageWasDeleted": "Nachricht wurde gelöscht.", "messageWasDeleted": "Nachricht wurde gelöscht.",
"messageWasDeletedShort": "Gelöscht",
"sent": "Versendet", "sent": "Versendet",
"sentTo": "Zugestellt an", "sentTo": "Zugestellt an",
"received": "Empfangen", "received": "Empfangen",

View file

@ -502,6 +502,7 @@
"tabToRemoveEmoji": "Tab to remove", "tabToRemoveEmoji": "Tab to remove",
"quotedMessageWasDeleted": "The quoted message has been deleted.", "quotedMessageWasDeleted": "The quoted message has been deleted.",
"messageWasDeleted": "Message has been deleted.", "messageWasDeleted": "Message has been deleted.",
"messageWasDeletedShort": "Deleted",
"sent": "Delivered", "sent": "Delivered",
"sentTo": "Delivered to", "sentTo": "Delivered to",
"received": "Received", "received": "Received",

View file

@ -2114,6 +2114,12 @@ abstract class AppLocalizations {
/// **'Message has been deleted.'** /// **'Message has been deleted.'**
String get messageWasDeleted; String get messageWasDeleted;
/// No description provided for @messageWasDeletedShort.
///
/// In en, this message translates to:
/// **'Deleted'**
String get messageWasDeletedShort;
/// No description provided for @sent. /// No description provided for @sent.
/// ///
/// In en, this message translates to: /// In en, this message translates to:

View file

@ -1123,6 +1123,9 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get messageWasDeleted => 'Nachricht wurde gelöscht.'; String get messageWasDeleted => 'Nachricht wurde gelöscht.';
@override
String get messageWasDeletedShort => 'Gelöscht';
@override @override
String get sent => 'Versendet'; String get sent => 'Versendet';

View file

@ -1116,6 +1116,9 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get messageWasDeleted => 'Message has been deleted.'; String get messageWasDeleted => 'Message has been deleted.';
@override
String get messageWasDeletedShort => 'Deleted';
@override @override
String get sent => 'Delivered'; String get sent => 'Delivered';

View file

@ -777,15 +777,15 @@ class EncryptedContent_ContactRequest extends $pb.GeneratedMessage {
class EncryptedContent_ContactUpdate extends $pb.GeneratedMessage { class EncryptedContent_ContactUpdate extends $pb.GeneratedMessage {
factory EncryptedContent_ContactUpdate({ factory EncryptedContent_ContactUpdate({
EncryptedContent_ContactUpdate_Type? type, EncryptedContent_ContactUpdate_Type? type,
$core.String? avatarSvg, $core.List<$core.int>? avatarSvgCompressed,
$core.String? displayName, $core.String? displayName,
}) { }) {
final $result = create(); final $result = create();
if (type != null) { if (type != null) {
$result.type = type; $result.type = type;
} }
if (avatarSvg != null) { if (avatarSvgCompressed != null) {
$result.avatarSvg = avatarSvg; $result.avatarSvgCompressed = avatarSvgCompressed;
} }
if (displayName != null) { if (displayName != null) {
$result.displayName = displayName; $result.displayName = displayName;
@ -798,7 +798,7 @@ class EncryptedContent_ContactUpdate extends $pb.GeneratedMessage {
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'EncryptedContent.ContactUpdate', createEmptyInstance: create) static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'EncryptedContent.ContactUpdate', createEmptyInstance: create)
..e<EncryptedContent_ContactUpdate_Type>(1, _omitFieldNames ? '' : 'type', $pb.PbFieldType.OE, defaultOrMaker: EncryptedContent_ContactUpdate_Type.REQUEST, valueOf: EncryptedContent_ContactUpdate_Type.valueOf, enumValues: EncryptedContent_ContactUpdate_Type.values) ..e<EncryptedContent_ContactUpdate_Type>(1, _omitFieldNames ? '' : 'type', $pb.PbFieldType.OE, defaultOrMaker: EncryptedContent_ContactUpdate_Type.REQUEST, valueOf: EncryptedContent_ContactUpdate_Type.valueOf, enumValues: EncryptedContent_ContactUpdate_Type.values)
..aOS(2, _omitFieldNames ? '' : 'avatarSvg', protoName: 'avatarSvg') ..a<$core.List<$core.int>>(2, _omitFieldNames ? '' : 'avatarSvgCompressed', $pb.PbFieldType.OY, protoName: 'avatarSvgCompressed')
..aOS(3, _omitFieldNames ? '' : 'displayName', protoName: 'displayName') ..aOS(3, _omitFieldNames ? '' : 'displayName', protoName: 'displayName')
..hasRequiredFields = false ..hasRequiredFields = false
; ;
@ -834,13 +834,13 @@ class EncryptedContent_ContactUpdate extends $pb.GeneratedMessage {
void clearType() => clearField(1); void clearType() => clearField(1);
@$pb.TagNumber(2) @$pb.TagNumber(2)
$core.String get avatarSvg => $_getSZ(1); $core.List<$core.int> get avatarSvgCompressed => $_getN(1);
@$pb.TagNumber(2) @$pb.TagNumber(2)
set avatarSvg($core.String v) { $_setString(1, v); } set avatarSvgCompressed($core.List<$core.int> v) { $_setBytes(1, v); }
@$pb.TagNumber(2) @$pb.TagNumber(2)
$core.bool hasAvatarSvg() => $_has(1); $core.bool hasAvatarSvgCompressed() => $_has(1);
@$pb.TagNumber(2) @$pb.TagNumber(2)
void clearAvatarSvg() => clearField(2); void clearAvatarSvgCompressed() => clearField(2);
@$pb.TagNumber(3) @$pb.TagNumber(3)
$core.String get displayName => $_getSZ(2); $core.String get displayName => $_getSZ(2);

View file

@ -260,12 +260,12 @@ const EncryptedContent_ContactUpdate$json = {
'1': 'ContactUpdate', '1': 'ContactUpdate',
'2': [ '2': [
{'1': 'type', '3': 1, '4': 1, '5': 14, '6': '.EncryptedContent.ContactUpdate.Type', '10': 'type'}, {'1': 'type', '3': 1, '4': 1, '5': 14, '6': '.EncryptedContent.ContactUpdate.Type', '10': 'type'},
{'1': 'avatarSvg', '3': 2, '4': 1, '5': 9, '9': 0, '10': 'avatarSvg', '17': true}, {'1': 'avatarSvgCompressed', '3': 2, '4': 1, '5': 12, '9': 0, '10': 'avatarSvgCompressed', '17': true},
{'1': 'displayName', '3': 3, '4': 1, '5': 9, '9': 1, '10': 'displayName', '17': true}, {'1': 'displayName', '3': 3, '4': 1, '5': 9, '9': 1, '10': 'displayName', '17': true},
], ],
'4': [EncryptedContent_ContactUpdate_Type$json], '4': [EncryptedContent_ContactUpdate_Type$json],
'8': [ '8': [
{'1': '_avatarSvg'}, {'1': '_avatarSvgCompressed'},
{'1': '_displayName'}, {'1': '_displayName'},
], ],
}; };
@ -358,19 +358,20 @@ final $typed_data.Uint8List encryptedContentDescriptor = $convert.base64Decode(
'ZXRNZXNzYWdlSWQYAiABKAlSD3RhcmdldE1lc3NhZ2VJZCI2CgRUeXBlEgwKCFJFT1BFTkVEEA' 'ZXRNZXNzYWdlSWQYAiABKAlSD3RhcmdldE1lc3NhZ2VJZCI2CgRUeXBlEgwKCFJFT1BFTkVEEA'
'ASCgoGU1RPUkVEEAESFAoQREVDUllQVElPTl9FUlJPUhACGngKDkNvbnRhY3RSZXF1ZXN0EjkK' 'ASCgoGU1RPUkVEEAESFAoQREVDUllQVElPTl9FUlJPUhACGngKDkNvbnRhY3RSZXF1ZXN0EjkK'
'BHR5cGUYASABKA4yJS5FbmNyeXB0ZWRDb250ZW50LkNvbnRhY3RSZXF1ZXN0LlR5cGVSBHR5cG' 'BHR5cGUYASABKA4yJS5FbmNyeXB0ZWRDb250ZW50LkNvbnRhY3RSZXF1ZXN0LlR5cGVSBHR5cG'
'UiKwoEVHlwZRILCgdSRVFVRVNUEAASCgoGUkVKRUNUEAESCgoGQUNDRVBUEAIa0gEKDUNvbnRh' 'UiKwoEVHlwZRILCgdSRVFVRVNUEAASCgoGUkVKRUNUEAESCgoGQUNDRVBUEAIa8AEKDUNvbnRh'
'Y3RVcGRhdGUSOAoEdHlwZRgBIAEoDjIkLkVuY3J5cHRlZENvbnRlbnQuQ29udGFjdFVwZGF0ZS' 'Y3RVcGRhdGUSOAoEdHlwZRgBIAEoDjIkLkVuY3J5cHRlZENvbnRlbnQuQ29udGFjdFVwZGF0ZS'
'5UeXBlUgR0eXBlEiEKCWF2YXRhclN2ZxgCIAEoCUgAUglhdmF0YXJTdmeIAQESJQoLZGlzcGxh' '5UeXBlUgR0eXBlEjUKE2F2YXRhclN2Z0NvbXByZXNzZWQYAiABKAxIAFITYXZhdGFyU3ZnQ29t'
'eU5hbWUYAyABKAlIAVILZGlzcGxheU5hbWWIAQEiHwoEVHlwZRILCgdSRVFVRVNUEAASCgoGVV' 'cHJlc3NlZIgBARIlCgtkaXNwbGF5TmFtZRgDIAEoCUgBUgtkaXNwbGF5TmFtZYgBASIfCgRUeX'
'BEQVRFEAFCDAoKX2F2YXRhclN2Z0IOCgxfZGlzcGxheU5hbWUa1QEKCFB1c2hLZXlzEjMKBHR5' 'BlEgsKB1JFUVVFU1QQABIKCgZVUERBVEUQAUIWChRfYXZhdGFyU3ZnQ29tcHJlc3NlZEIOCgxf'
'cGUYASABKA4yHy5FbmNyeXB0ZWRDb250ZW50LlB1c2hLZXlzLlR5cGVSBHR5cGUSGQoFa2V5SW' 'ZGlzcGxheU5hbWUa1QEKCFB1c2hLZXlzEjMKBHR5cGUYASABKA4yHy5FbmNyeXB0ZWRDb250ZW'
'QYAiABKANIAFIFa2V5SWSIAQESFQoDa2V5GAMgASgMSAFSA2tleYgBARIhCgljcmVhdGVkQXQY' '50LlB1c2hLZXlzLlR5cGVSBHR5cGUSGQoFa2V5SWQYAiABKANIAFIFa2V5SWSIAQESFQoDa2V5'
'BCABKANIAlIJY3JlYXRlZEF0iAEBIh8KBFR5cGUSCwoHUkVRVUVTVBAAEgoKBlVQREFURRABQg' 'GAMgASgMSAFSA2tleYgBARIhCgljcmVhdGVkQXQYBCABKANIAlIJY3JlYXRlZEF0iAEBIh8KBF'
'gKBl9rZXlJZEIGCgRfa2V5QgwKCl9jcmVhdGVkQXQahwEKCUZsYW1lU3luYxIiCgxmbGFtZUNv' 'R5cGUSCwoHUkVRVUVTVBAAEgoKBlVQREFURRABQggKBl9rZXlJZEIGCgRfa2V5QgwKCl9jcmVh'
'dW50ZXIYASABKANSDGZsYW1lQ291bnRlchI2ChZsYXN0RmxhbWVDb3VudGVyQ2hhbmdlGAIgAS' 'dGVkQXQahwEKCUZsYW1lU3luYxIiCgxmbGFtZUNvdW50ZXIYASABKANSDGZsYW1lQ291bnRlch'
'gDUhZsYXN0RmxhbWVDb3VudGVyQ2hhbmdlEh4KCmJlc3RGcmllbmQYAyABKAhSCmJlc3RGcmll' 'I2ChZsYXN0RmxhbWVDb3VudGVyQ2hhbmdlGAIgASgDUhZsYXN0RmxhbWVDb3VudGVyQ2hhbmdl'
'bmRCCgoIX2dyb3VwSWRCDwoNX2lzRGlyZWN0Q2hhdEIXChVfc2VuZGVyUHJvZmlsZUNvdW50ZX' 'Eh4KCmJlc3RGcmllbmQYAyABKAhSCmJlc3RGcmllbmRCCgoIX2dyb3VwSWRCDwoNX2lzRGlyZW'
'JCEAoOX21lc3NhZ2VVcGRhdGVCCAoGX21lZGlhQg4KDF9tZWRpYVVwZGF0ZUIQCg5fY29udGFj' 'N0Q2hhdEIXChVfc2VuZGVyUHJvZmlsZUNvdW50ZXJCEAoOX21lc3NhZ2VVcGRhdGVCCAoGX21l'
'dFVwZGF0ZUIRCg9fY29udGFjdFJlcXVlc3RCDAoKX2ZsYW1lU3luY0ILCglfcHVzaEtleXNCCw' 'ZGlhQg4KDF9tZWRpYVVwZGF0ZUIQCg5fY29udGFjdFVwZGF0ZUIRCg9fY29udGFjdFJlcXVlc3'
'oJX3JlYWN0aW9uQg4KDF90ZXh0TWVzc2FnZQ=='); 'RCDAoKX2ZsYW1lU3luY0ILCglfcHVzaEtleXNCCwoJX3JlYWN0aW9uQg4KDF90ZXh0TWVzc2Fn'
'ZQ==');

View file

@ -118,7 +118,7 @@ message EncryptedContent {
} }
Type type = 1; Type type = 1;
optional string avatarSvg = 2; optional bytes avatarSvgCompressed = 2;
optional string displayName = 3; optional string displayName = 3;
} }

View file

@ -339,7 +339,7 @@ class ApiService {
await twonlyDB.contactsDao.updateContact( await twonlyDB.contactsDao.updateContact(
contactId, contactId,
ContactsCompanion( ContactsCompanion(
deleted: const Value(true), accountDeleted: const Value(true),
username: Value('${contact.username} (${contact.userId})'), username: Value('${contact.username} (${contact.userId})'),
), ),
); );

View file

@ -52,6 +52,8 @@ Future<void> insertMediaFileInMessagesTable(
type: const Value(MessageType.media), type: const Value(MessageType.media),
), ),
); );
await twonlyDB.groupsDao
.increaseLastMessageExchange(groupId, DateTime.now());
if (message != null) { if (message != null) {
// de-archive contact when sending a new message // de-archive contact when sending a new message
await twonlyDB.groupsDao.updateGroup( await twonlyDB.groupsDao.updateGroup(

View file

@ -1,4 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:fixnum/fixnum.dart'; import 'package:fixnum/fixnum.dart';
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart'; import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
@ -116,7 +118,7 @@ Future<(Uint8List, Uint8List?)?> tryToSendCompleteMessage({
await twonlyDB.receiptsDao.deleteReceipt(receiptId); await twonlyDB.receiptsDao.deleteReceipt(receiptId);
await twonlyDB.contactsDao.updateContact( await twonlyDB.contactsDao.updateContact(
receipt.contactId, receipt.contactId,
const ContactsCompanion(deleted: Value(true)), const ContactsCompanion(accountDeleted: Value(true)),
); );
return null; return null;
} }
@ -195,6 +197,8 @@ Future<void> sendCipherTextToGroup(
) async { ) async {
final groupMembers = await twonlyDB.groupsDao.getGroupMembers(groupId); final groupMembers = await twonlyDB.groupsDao.getGroupMembers(groupId);
await twonlyDB.groupsDao.increaseLastMessageExchange(groupId, DateTime.now());
encryptedContent.groupId = groupId; encryptedContent.groupId = groupId;
for (final groupMember in groupMembers) { for (final groupMember in groupMembers) {
@ -280,7 +284,7 @@ Future<void> notifyContactsAboutProfileChange({int? onlyToContact}) async {
final encryptedContent = pb.EncryptedContent( final encryptedContent = pb.EncryptedContent(
contactUpdate: pb.EncryptedContent_ContactUpdate( contactUpdate: pb.EncryptedContent_ContactUpdate(
type: pb.EncryptedContent_ContactUpdate_Type.UPDATE, type: pb.EncryptedContent_ContactUpdate_Type.UPDATE,
avatarSvg: user.avatarSvg, avatarSvgCompressed: gzip.encode(utf8.encode(user.avatarSvg!)),
displayName: user.displayName, displayName: user.displayName,
), ),
); );

View file

@ -3,6 +3,7 @@ import 'package:drift/drift.dart';
import 'package:hashlib/random.dart'; import 'package:hashlib/random.dart';
import 'package:mutex/mutex.dart'; import 'package:mutex/mutex.dart';
import 'package:twonly/globals.dart'; import 'package:twonly/globals.dart';
import 'package:twonly/src/database/daos/contacts.dao.dart';
import 'package:twonly/src/database/twonly.db.dart' hide Message; import 'package:twonly/src/database/twonly.db.dart' hide Message;
import 'package:twonly/src/model/protobuf/api/websocket/client_to_server.pb.dart' import 'package:twonly/src/model/protobuf/api/websocket/client_to_server.pb.dart'
as client; as client;
@ -20,6 +21,7 @@ import 'package:twonly/src/services/api/server_messages/reaction.server_message.
import 'package:twonly/src/services/api/server_messages/text_message.server_messages.dart'; import 'package:twonly/src/services/api/server_messages/text_message.server_messages.dart';
import 'package:twonly/src/services/signal/encryption.signal.dart'; import 'package:twonly/src/services/signal/encryption.signal.dart';
import 'package:twonly/src/utils/log.dart'; import 'package:twonly/src/utils/log.dart';
import 'package:twonly/src/utils/misc.dart';
final lockHandleServerMessage = Mutex(); final lockHandleServerMessage = Mutex();
@ -28,7 +30,7 @@ Future<void> handleServerMessage(server.ServerToClient msg) async {
final ok = client.Response_Ok()..none = true; final ok = client.Response_Ok()..none = true;
var response = client.Response()..ok = ok; var response = client.Response()..ok = ok;
try { // try {
if (msg.v0.hasRequestNewPreKeys()) { if (msg.v0.hasRequestNewPreKeys()) {
response = await handleRequestNewPreKey(); response = await handleRequestNewPreKey();
} else if (msg.v0.hasNewMessage()) { } else if (msg.v0.hasNewMessage()) {
@ -38,9 +40,9 @@ Future<void> handleServerMessage(server.ServerToClient msg) async {
} else { } else {
Log.error('Unknown server message: $msg'); Log.error('Unknown server message: $msg');
} }
} catch (e) { // } catch (e) {
Log.error(e); // Log.error(e);
} // }
final v0 = client.V0() final v0 = client.V0()
..seq = msg.v0.seq ..seq = msg.v0.seq
@ -93,6 +95,21 @@ Future<void> handleNewMessage(int fromUserId, Uint8List body) async {
final encryptedContentRaw = final encryptedContentRaw =
Uint8List.fromList(message.encryptedContent); Uint8List.fromList(message.encryptedContent);
if (await twonlyDB.contactsDao
.getContactByUserId(fromUserId)
.getSingleOrNull() ==
null) {
/// In case the user does not exists, just create a dummy user which was deleted by the user, so the message
/// can be inserted into the receipts database
await twonlyDB.contactsDao.insertContact(
ContactsCompanion(
userId: Value(fromUserId),
deletedByUser: const Value(true),
username: const Value('[deleted]'),
),
);
}
final responsePlaintextContent = await handleEncryptedMessage( final responsePlaintextContent = await handleEncryptedMessage(
fromUserId, fromUserId,
encryptedContentRaw, encryptedContentRaw,
@ -108,6 +125,7 @@ Future<void> handleNewMessage(int fromUserId, Uint8List body) async {
} else { } else {
response = Message()..type = Message_Type.SENDER_DELIVERY_RECEIPT; response = Message()..type = Message_Type.SENDER_DELIVERY_RECEIPT;
} }
await twonlyDB.receiptsDao.insertReceipt( await twonlyDB.receiptsDao.insertReceipt(
ReceiptsCompanion( ReceiptsCompanion(
receiptId: Value(receiptId), receiptId: Value(receiptId),
@ -189,9 +207,30 @@ Future<PlaintextContent?> handleEncryptedMessage(
/// Verify that the user is (still) in that group... /// Verify that the user is (still) in that group...
if (!await twonlyDB.groupsDao.isContactInGroup(fromUserId, content.groupId)) { if (!await twonlyDB.groupsDao.isContactInGroup(fromUserId, content.groupId)) {
if (getUUIDforDirectChat(gUser.userId, fromUserId) == content.groupId) {
final contact = await twonlyDB.contactsDao
.getContactByUserId(fromUserId)
.getSingleOrNull();
if (contact == null || contact.deletedByUser) {
Log.error(
'User tries to send message to direct chat while the user does not exists !',
);
return null;
}
Log.info(
'Creating new DirectChat between two users',
);
await twonlyDB.groupsDao.createNewDirectChat(
fromUserId,
GroupsCompanion(
groupName: Value(getContactDisplayName(contact)),
),
);
} else {
Log.error('User $fromUserId tried to access group ${content.groupId}.'); Log.error('User $fromUserId tried to access group ${content.groupId}.');
return null; return null;
} }
}
if (content.hasTextMessage()) { if (content.hasTextMessage()) {
await handleTextMessage( await handleTextMessage(

View file

@ -3,6 +3,7 @@ import 'dart:convert';
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:twonly/globals.dart'; import 'package:twonly/globals.dart';
import 'package:twonly/src/database/daos/contacts.dao.dart';
import 'package:twonly/src/database/twonly.db.dart' hide Message; import 'package:twonly/src/database/twonly.db.dart' hide Message;
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart'; import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
import 'package:twonly/src/services/api/messages.dart'; import 'package:twonly/src/services/api/messages.dart';
@ -25,11 +26,12 @@ Future<void> handleContactRequest(
if (username.isSuccess) { if (username.isSuccess) {
// ignore: avoid_dynamic_calls // ignore: avoid_dynamic_calls
final name = username.value.userdata.username as Uint8List; final name = username.value.userdata.username as Uint8List;
await twonlyDB.contactsDao.insertContact( await twonlyDB.contactsDao.insertOnConflictUpdate(
ContactsCompanion( ContactsCompanion(
username: Value(utf8.decode(name)), username: Value(utf8.decode(name)),
userId: Value(fromUserId), userId: Value(fromUserId),
requested: const Value(true), requested: const Value(true),
deletedByUser: const Value(false),
), ),
); );
} }
@ -43,9 +45,25 @@ Future<void> handleContactRequest(
accepted: Value(true), accepted: Value(true),
), ),
); );
final contact = await twonlyDB.contactsDao
.getContactByUserId(fromUserId)
.getSingleOrNull();
await twonlyDB.groupsDao.createNewDirectChat(
fromUserId,
GroupsCompanion(
groupName: Value(getContactDisplayName(contact!)),
),
);
case EncryptedContent_ContactRequest_Type.REJECT: case EncryptedContent_ContactRequest_Type.REJECT:
Log.info('Got a contact reject from $fromUserId'); Log.info('Got a contact reject from $fromUserId');
await twonlyDB.contactsDao.deleteContactByUserId(fromUserId); await twonlyDB.contactsDao.updateContact(
fromUserId,
const ContactsCompanion(
accepted: Value(false),
requested: Value(false),
deletedByUser: Value(true),
),
);
} }
} }
@ -61,13 +79,14 @@ Future<void> handleContactUpdate(
case EncryptedContent_ContactUpdate_Type.UPDATE: case EncryptedContent_ContactUpdate_Type.UPDATE:
Log.info('Got a contact update $fromUserId'); Log.info('Got a contact update $fromUserId');
if (contactUpdate.hasAvatarSvg() && if (contactUpdate.hasAvatarSvgCompressed() &&
contactUpdate.hasDisplayName() && contactUpdate.hasDisplayName() &&
senderProfileCounter != null) { senderProfileCounter != null) {
await twonlyDB.contactsDao.updateContact( await twonlyDB.contactsDao.updateContact(
fromUserId, fromUserId,
ContactsCompanion( ContactsCompanion(
avatarSvg: Value(contactUpdate.avatarSvg), avatarSvgCompressed:
Value(Uint8List.fromList(contactUpdate.avatarSvgCompressed)),
displayName: Value(contactUpdate.displayName), displayName: Value(contactUpdate.displayName),
senderProfileCounter: Value(senderProfileCounter), senderProfileCounter: Value(senderProfileCounter),
), ),

View file

@ -101,6 +101,8 @@ Future<void> handleMedia(
), ),
); );
if (message != null) { if (message != null) {
await twonlyDB.groupsDao
.increaseLastMessageExchange(groupId, fromTimestamp(media.timestamp));
Log.info('Inserted a new media message with ID: ${message.messageId}'); Log.info('Inserted a new media message with ID: ${message.messageId}');
await twonlyDB.groupsDao.incFlameCounter( await twonlyDB.groupsDao.incFlameCounter(
message.groupId, message.groupId,

View file

@ -29,6 +29,10 @@ Future<void> handleTextMessage(
ackByServer: Value(DateTime.now()), ackByServer: Value(DateTime.now()),
), ),
); );
await twonlyDB.groupsDao.increaseLastMessageExchange(
groupId,
fromTimestamp(textMessage.timestamp),
);
if (message != null) { if (message != null) {
Log.info('Inserted a new text message with ID: ${message.messageId}'); Log.info('Inserted a new text message with ID: ${message.messageId}');
} }

View file

@ -12,7 +12,6 @@ import 'package:twonly/src/model/protobuf/api/websocket/server_to_client.pb.dart
import 'package:twonly/src/model/protobuf/client/generated/messages.pbserver.dart' import 'package:twonly/src/model/protobuf/client/generated/messages.pbserver.dart'
hide Message; hide Message;
import 'package:twonly/src/services/api/messages.dart'; import 'package:twonly/src/services/api/messages.dart';
import 'package:twonly/src/services/signal/session.signal.dart';
class Result<T, E> { class Result<T, E> {
Result.error(this.error) : value = null; Result.error(this.error) : value = null;
@ -57,7 +56,7 @@ ClientToServer createClientToServerFromApplicationData(
return ClientToServer()..v0 = v0; return ClientToServer()..v0 = v0;
} }
Future<void> rejectAndDeleteContact(int contactId) async { Future<void> rejectAndHideContact(int contactId) async {
await sendCipherText( await sendCipherText(
contactId, contactId,
EncryptedContent( EncryptedContent(
@ -66,9 +65,14 @@ Future<void> rejectAndDeleteContact(int contactId) async {
), ),
), ),
); );
await twonlyDB.signalDao.deleteAllByContactId(contactId); await twonlyDB.contactsDao.updateContact(
await deleteSessionWithTarget(contactId); contactId,
await twonlyDB.contactsDao.deleteContactByUserId(contactId); const ContactsCompanion(
accepted: Value(false),
requested: Value(false),
deletedByUser: Value(true),
),
);
} }
Future<void> handleMediaError(MediaFile media) async { Future<void> handleMediaError(MediaFile media) async {

View file

@ -9,7 +9,7 @@ Future<void> createThumbnailsForImage(
) async { ) async {
final fileExtension = sourceFile.path.split('.').last.toLowerCase(); final fileExtension = sourceFile.path.split('.').last.toLowerCase();
if (fileExtension != 'png') { if (fileExtension != 'png') {
Log.error('Could not create thumbnail for image. $fileExtension != .png'); Log.error('Could not create thumbnail for image. $fileExtension != png');
return; return;
} }

View file

@ -7,6 +7,7 @@ import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:twonly/globals.dart'; import 'package:twonly/globals.dart';
import 'package:twonly/src/utils/misc.dart';
final StreamController<NotificationResponse> selectNotificationStream = final StreamController<NotificationResponse> selectNotificationStream =
StreamController<NotificationResponse>.broadcast(); StreamController<NotificationResponse>.broadcast();
@ -61,10 +62,11 @@ Future<void> createPushAvatars() async {
final contacts = await twonlyDB.contactsDao.getAllNotBlockedContacts(); final contacts = await twonlyDB.contactsDao.getAllNotBlockedContacts();
for (final contact in contacts) { for (final contact in contacts) {
if (contact.avatarSvg == null) return; if (contact.avatarSvgCompressed == null) return;
final pictureInfo = final avatarSvg = getAvatarSvg(contact.avatarSvgCompressed!);
await vg.loadPicture(SvgStringLoader(contact.avatarSvg!), null);
final pictureInfo = await vg.loadPicture(SvgStringLoader(avatarSvg), null);
final image = await pictureInfo.picture.toImage(300, 300); final image = await pictureInfo.picture.toImage(300, 300);

View file

@ -1,3 +1,5 @@
import 'dart:convert';
import 'dart:io';
import 'dart:math'; import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
@ -372,3 +374,8 @@ String friendlyDateTime(
return '$timePart $datePart'; return '$timePart $datePart';
} }
String getAvatarSvg(Uint8List avatarSvgCompressed) {
final raw = gzip.decode(avatarSvgCompressed);
return utf8.decode(raw);
}

View file

@ -12,7 +12,6 @@ import 'package:twonly/src/services/api/utils.dart';
import 'package:twonly/src/services/notifications/pushkeys.notifications.dart'; import 'package:twonly/src/services/notifications/pushkeys.notifications.dart';
import 'package:twonly/src/services/signal/session.signal.dart'; import 'package:twonly/src/services/signal/session.signal.dart';
import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/utils/storage.dart';
import 'package:twonly/src/views/components/alert_dialog.dart'; import 'package:twonly/src/views/components/alert_dialog.dart';
import 'package:twonly/src/views/components/avatar_icon.component.dart'; import 'package:twonly/src/views/components/avatar_icon.component.dart';
import 'package:twonly/src/views/components/headline.dart'; import 'package:twonly/src/views/components/headline.dart';
@ -49,8 +48,7 @@ class _SearchUsernameView extends State<AddNewUserView> {
} }
Future<void> _addNewUser(BuildContext context) async { Future<void> _addNewUser(BuildContext context) async {
final user = await getUser(); if (gUser.username == searchUserName.text) {
if (user == null || user.username == searchUserName.text || !mounted) {
return; return;
} }
@ -84,11 +82,13 @@ class _SearchUsernameView extends State<AddNewUserView> {
return; return;
} }
final added = await twonlyDB.contactsDao.insertContact( final added = await twonlyDB.contactsDao.insertOnConflictUpdate(
ContactsCompanion( ContactsCompanion(
username: Value(searchUserName.text), username: Value(searchUserName.text),
userId: Value(userdata.userId.toInt()), userId: Value(userdata.userId.toInt()),
requested: const Value(false), requested: const Value(false),
blocked: const Value(false),
deletedByUser: const Value(false),
), ),
); );
@ -223,18 +223,26 @@ class ContactsListView extends StatelessWidget {
child: IconButton( child: IconButton(
icon: const Icon(Icons.close, color: Colors.red), icon: const Icon(Icons.close, color: Colors.red),
onPressed: () async { onPressed: () async {
await rejectAndDeleteContact(contact.userId); await rejectAndHideContact(contact.userId);
}, },
), ),
), ),
IconButton( IconButton(
icon: const Icon(Icons.check, color: Colors.green), icon: const Icon(Icons.check, color: Colors.green),
onPressed: () async { onPressed: () async {
const update = ContactsCompanion( await twonlyDB.contactsDao.updateContact(
contact.userId,
const ContactsCompanion(
accepted: Value(true), accepted: Value(true),
requested: Value(false), requested: Value(false),
),
);
await twonlyDB.groupsDao.createNewDirectChat(
contact.userId,
GroupsCompanion(
groupName: Value(getContactDisplayName(contact)),
),
); );
await twonlyDB.contactsDao.updateContact(contact.userId, update);
await sendCipherText( await sendCipherText(
contact.userId, contact.userId,
EncryptedContent( EncryptedContent(

View file

@ -47,7 +47,7 @@ class _ChatListViewState extends State<ChatListView> {
} }
Future<void> initAsync() async { Future<void> initAsync() async {
final stream = twonlyDB.groupsDao.watchGroups(); final stream = twonlyDB.groupsDao.watchGroupsForChatList();
_contactsSub = stream.listen((groups) { _contactsSub = stream.listen((groups) {
setState(() { setState(() {
_groupsNotPinned = groups.where((x) => !x.pinned).toList(); _groupsNotPinned = groups.where((x) => !x.pinned).toList();

View file

@ -38,7 +38,9 @@ class _UserListItem extends State<GroupListItem> {
late StreamSubscription<List<Message>> messagesNotOpenedStream; late StreamSubscription<List<Message>> messagesNotOpenedStream;
Message? lastMessage; Message? lastMessage;
Reaction? lastReaction;
late StreamSubscription<Message?> lastMessageStream; late StreamSubscription<Message?> lastMessageStream;
late StreamSubscription<Reaction?> lastReactionStream;
late StreamSubscription<List<MediaFile>> lastMediaFilesStream; late StreamSubscription<List<MediaFile>> lastMediaFilesStream;
List<Message> previewMessages = []; List<Message> previewMessages = [];
@ -54,6 +56,7 @@ class _UserListItem extends State<GroupListItem> {
@override @override
void dispose() { void dispose() {
messagesNotOpenedStream.cancel(); messagesNotOpenedStream.cancel();
lastReactionStream.cancel();
lastMessageStream.cancel(); lastMessageStream.cancel();
lastMediaFilesStream.cancel(); lastMediaFilesStream.cancel();
super.dispose(); super.dispose();
@ -68,6 +71,17 @@ class _UserListItem extends State<GroupListItem> {
}); });
}); });
lastReactionStream = twonlyDB.reactionsDao
.watchLastReactions(widget.group.groupId)
.listen((update) {
setState(() {
lastReaction = update;
});
// protectUpdateState.protect(() async {
// await updateState(lastMessage, update, messagesNotOpened);
// });
});
messagesNotOpenedStream = twonlyDB.messagesDao messagesNotOpenedStream = twonlyDB.messagesDao
.watchMessageNotOpened(widget.group.groupId) .watchMessageNotOpened(widget.group.groupId)
.listen((update) { .listen((update) {
@ -141,9 +155,7 @@ class _UserListItem extends State<GroupListItem> {
lastMessage = newLastMessage; lastMessage = newLastMessage;
messagesNotOpened = newMessagesNotOpened; messagesNotOpened = newMessagesNotOpened;
setState(() { if (mounted) setState(() {});
// sets lastMessages, messagesNotOpened and currentMessage
});
} }
Future<void> onTap() async { Future<void> onTap() async {
@ -217,7 +229,11 @@ class _UserListItem extends State<GroupListItem> {
? Text(context.lang.chatsTapToSend) ? Text(context.lang.chatsTapToSend)
: Row( : Row(
children: [ children: [
MessageSendStateIcon(previewMessages, previewMediaFiles), MessageSendStateIcon(
previewMessages,
previewMediaFiles,
lastReaction: lastReaction,
),
const Text(''), const Text(''),
const SizedBox(width: 5), const SizedBox(width: 5),
if (currentMessage != null) if (currentMessage != null)

View file

@ -179,7 +179,7 @@ class _ChatListEntryState extends State<ChatListEntry> {
Message? nextMessage, Message? nextMessage,
bool hasReactions, bool hasReactions,
) { ) {
var bottom = 30.0; var bottom = 20.0;
var top = 0.0; var top = 0.0;
var topLeft = 12.0; var topLeft = 12.0;
@ -189,7 +189,7 @@ class _ChatListEntryState extends State<ChatListEntry> {
if (nextMessage != null) { if (nextMessage != null) {
if (message.senderId == nextMessage.senderId) { if (message.senderId == nextMessage.senderId) {
bottom = 10; bottom = 3;
} }
} }
@ -203,7 +203,7 @@ class _ChatListEntryState extends State<ChatListEntry> {
final combinesWidthNext = combineTextMessageWithNext(message, nextMessage); final combinesWidthNext = combineTextMessageWithNext(message, nextMessage);
if (combinesWidthNext) { if (combinesWidthNext) {
bottom = 1; bottom = 0;
bottomLeft = 5.0; bottomLeft = 5.0;
} }

View file

@ -5,6 +5,7 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:twonly/src/database/tables/mediafiles.table.dart'; import 'package:twonly/src/database/tables/mediafiles.table.dart';
import 'package:twonly/src/database/tables/messages.table.dart'; import 'package:twonly/src/database/tables/messages.table.dart';
import 'package:twonly/src/database/twonly.db.dart'; import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/utils/log.dart';
import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/views/components/animate_icon.dart'; import 'package:twonly/src/views/components/animate_icon.dart';
@ -44,10 +45,12 @@ class MessageSendStateIcon extends StatefulWidget {
this.mediaFiles, { this.mediaFiles, {
super.key, super.key,
this.canBeReopened = false, this.canBeReopened = false,
this.lastReaction,
this.mainAxisAlignment = MainAxisAlignment.end, this.mainAxisAlignment = MainAxisAlignment.end,
}); });
final List<Message> messages; final List<Message> messages;
final List<MediaFile> mediaFiles; final List<MediaFile> mediaFiles;
final Reaction? lastReaction;
final MainAxisAlignment mainAxisAlignment; final MainAxisAlignment mainAxisAlignment;
final bool canBeReopened; final bool canBeReopened;
@ -125,8 +128,8 @@ class _MessageSendStateIconState extends State<MessageSendStateIcon> {
case MessageSendState.received: case MessageSendState.received:
icon = Icon(Icons.square_rounded, size: 14, color: color); icon = Icon(Icons.square_rounded, size: 14, color: color);
text = context.lang.messageSendState_Received; text = context.lang.messageSendState_Received;
if (message.type == MessageType.media) { if (message.type == MessageType.media && mediaFile != null) {
if (mediaFile!.downloadState == DownloadState.pending) { if (mediaFile.downloadState == DownloadState.pending) {
text = context.lang.messageSendState_TapToLoad; text = context.lang.messageSendState_TapToLoad;
} }
if (mediaFile.downloadState == DownloadState.downloading) { if (mediaFile.downloadState == DownloadState.downloading) {
@ -170,6 +173,11 @@ class _MessageSendStateIconState extends State<MessageSendStateIcon> {
} }
} }
if (message.isDeletedFromSender) {
icon = FaIcon(FontAwesomeIcons.trash, size: 10, color: color);
text = context.lang.messageWasDeletedShort;
}
if (hasLoader) { if (hasLoader) {
icons = [icon]; icons = [icon];
break; break;
@ -182,6 +190,41 @@ class _MessageSendStateIconState extends State<MessageSendStateIcon> {
} }
} }
if (widget.lastReaction != null &&
!widget.messages.any((t) => t.openedAt == null)) {
/// No messages are still open, so check if the reaction is the last message received.
if (!widget.messages
.any((m) => m.createdAt.isAfter(widget.lastReaction!.createdAt))) {
if (EmojiAnimation.animatedIcons
.containsKey(widget.lastReaction!.emoji)) {
icons = [
SizedBox(
height: 18,
child: EmojiAnimation(emoji: widget.lastReaction!.emoji),
)
];
} else {
icons = [
SizedBox(
height: 18,
child: Center(
child: Text(
widget.lastReaction!.emoji,
style: const TextStyle(fontSize: 18),
strutStyle: const StrutStyle(
forceStrutHeight: true,
height: 1.6,
),
),
),
)
];
}
// Log.info("DISPLAY REACTION");
}
}
if (icons.isEmpty) return Container(); if (icons.isEmpty) return Container();
var icon = icons[0]; var icon = icons[0];

View file

@ -4,6 +4,7 @@ import 'package:twonly/globals.dart';
import 'package:twonly/src/database/twonly.db.dart'; import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/model/json/userdata.dart'; import 'package:twonly/src/model/json/userdata.dart';
import 'package:twonly/src/utils/log.dart'; import 'package:twonly/src/utils/log.dart';
import 'package:twonly/src/utils/misc.dart';
class AvatarIcon extends StatefulWidget { class AvatarIcon extends StatefulWidget {
const AvatarIcon({ const AvatarIcon({
@ -38,21 +39,21 @@ class _AvatarIconState extends State<AvatarIcon> {
final contacts = final contacts =
await twonlyDB.groupsDao.getGroupContact(widget.group!.groupId); await twonlyDB.groupsDao.getGroupContact(widget.group!.groupId);
if (contacts.length == 1) { if (contacts.length == 1) {
if (contacts.first.avatarSvg != null) { if (contacts.first.avatarSvgCompressed != null) {
avatarSVGs.add(contacts.first.avatarSvg!); avatarSVGs.add(getAvatarSvg(contacts.first.avatarSvgCompressed!));
} }
} else { } else {
for (final contact in contacts) { for (final contact in contacts) {
if (contact.avatarSvg != null) { if (contact.avatarSvgCompressed != null) {
avatarSVGs.add(contact.avatarSvg!); avatarSVGs.add(getAvatarSvg(contact.avatarSvgCompressed!));
} }
} }
} }
// avatarSvg = group!.avatarSvg; // avatarSvg = group!.avatarSvg;
} else if (widget.userData?.avatarSvg != null) { } else if (widget.userData?.avatarSvg != null) {
avatarSVGs.add(widget.userData!.avatarSvg!); avatarSVGs.add(widget.userData!.avatarSvg!);
} else if (widget.contact?.avatarSvg != null) { } else if (widget.contact?.avatarSvgCompressed != null) {
avatarSVGs.add(widget.contact!.avatarSvg!); avatarSVGs.add(getAvatarSvg(widget.contact!.avatarSvgCompressed!));
} }
if (mounted) setState(() {}); if (mounted) setState(() {});
} }

View file

@ -30,7 +30,7 @@ class _ContactViewState extends State<ContactView> {
); );
if (remove) { if (remove) {
// trigger deletion for the other user... // trigger deletion for the other user...
await rejectAndDeleteContact(contact.userId); await rejectAndHideContact(contact.userId);
if (mounted) { if (mounted) {
Navigator.popUntil(context, (route) => route.isFirst); Navigator.popUntil(context, (route) => route.isFirst);
} }

View file

@ -1,3 +1,4 @@
import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -39,26 +40,30 @@ class _DatabaseMigrationViewState extends State<DatabaseMigrationView> {
final oldMessages = await oldDatabase.messages.select().get(); final oldMessages = await oldDatabase.messages.select().get();
for (final oldContact in oldContacts) { for (final oldContact in oldContacts) {
if (oldContact.deleted) continue;
Uint8List? avatarSvg;
if (oldContact.avatarSvg != null) {
avatarSvg =
Uint8List.fromList(gzip.encode(utf8.encode(oldContact.avatarSvg!)));
}
await twonlyDB.contactsDao.insertContact( await twonlyDB.contactsDao.insertContact(
ContactsCompanion( ContactsCompanion(
userId: Value(oldContact.userId), userId: Value(oldContact.userId),
username: Value(oldContact.username), username: Value(oldContact.username),
displayName: Value(oldContact.displayName), displayName: Value(oldContact.displayName),
nickName: Value(oldContact.nickName), nickName: Value(oldContact.nickName),
avatarSvg: Value(oldContact.avatarSvg), avatarSvgCompressed: Value(avatarSvg),
senderProfileCounter: const Value(0), senderProfileCounter: const Value(0),
accepted: Value(oldContact.accepted), accepted: Value(oldContact.accepted),
requested: Value(oldContact.requested), requested: Value(oldContact.requested),
blocked: Value(oldContact.blocked), blocked: Value(oldContact.blocked),
verified: Value(oldContact.verified), verified: Value(oldContact.verified),
deleted: Value(oldContact.deleted),
createdAt: Value(oldContact.createdAt), createdAt: Value(oldContact.createdAt),
), ),
); );
setState(() { setState(() {
_contactsMigrated += 1; _contactsMigrated += 1;
}); });
if (!oldContact.deleted) {
final group = await twonlyDB.groupsDao.createNewDirectChat( final group = await twonlyDB.groupsDao.createNewDirectChat(
oldContact.userId, oldContact.userId,
GroupsCompanion( GroupsCompanion(
@ -144,11 +149,11 @@ class _DatabaseMigrationViewState extends State<DatabaseMigrationView> {
}); });
} }
} }
}
final memoriesPath = Directory( final memoriesPath = Directory(
join((await getApplicationSupportDirectory()).path, 'media', 'memories'), join((await getApplicationSupportDirectory()).path, 'media', 'memories'),
); );
if (memoriesPath.existsSync()) {
final files = memoriesPath.listSync(); final files = memoriesPath.listSync();
for (final file in files) { for (final file in files) {
if (file.path.contains('thumbnail')) continue; if (file.path.contains('thumbnail')) continue;
@ -168,6 +173,7 @@ class _DatabaseMigrationViewState extends State<DatabaseMigrationView> {
_storedMediaFiles += 1; _storedMediaFiles += 1;
}); });
} }
}
final oldContactPreKeys = final oldContactPreKeys =
await oldDatabase.signalContactPreKeys.select().get(); await oldDatabase.signalContactPreKeys.select().get();