diff --git a/.metadata b/.metadata new file mode 100644 index 0000000..fca9f99 --- /dev/null +++ b/.metadata @@ -0,0 +1,30 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "adc901062556672b4138e18a4dc62a4be8f4b3c2" + channel: "stable" + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2 + base_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2 + - platform: macos + create_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2 + base_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/README.md b/README.md index 09663de..02eac22 100644 --- a/README.md +++ b/README.md @@ -8,14 +8,16 @@ This repository contains the complete source code of the [twonly](https://twonly - Offer a Snapchat™ like experience - End-to-End encryption using the [Signal Protocol](https://de.wikipedia.org/wiki/Signal-Protokoll) +- twonly is Open Source and can be downloaded directly from GitHub +- Developed by humans not by AI or Vibe Coding - No email or phone number required to register - Privacy friendly - Everything is stored on the device -- Open-Source +- Backend is exclusively hosted in European -## In work +## Planned -- For Android: Using [UnifiedPush](https://unifiedpush.org/) instead of FCM -- For Android: Reproducible Builds + Publishing F-Droid +- For Android: Optional support for [UnifiedPush](https://unifiedpush.org/) +- For Android: Reproducible Builds - Implementing [Sealed Sender](https://signal.org/blog/sealed-sender/) to minimize metadata ## Security Issues diff --git a/lib/src/database/daos/groups.dao.dart b/lib/src/database/daos/groups.dao.dart index 4f76cc7..b28d460 100644 --- a/lib/src/database/daos/groups.dao.dart +++ b/lib/src/database/daos/groups.dao.dart @@ -8,7 +8,7 @@ import 'package:twonly/src/utils/misc.dart'; part 'groups.dao.g.dart'; -@DriftAccessor(tables: [Groups, GroupMembers]) +@DriftAccessor(tables: [Groups, GroupMembers, GroupHistories]) class GroupsDao extends DatabaseAccessor with _$GroupsDaoMixin { // this constructor is required so that the main database can create an instance // of this object. @@ -37,11 +37,21 @@ class GroupsDao extends DatabaseAccessor with _$GroupsDaoMixin { } Future createNewGroup(GroupsCompanion group) async { - final insertGroup = group.copyWith( - groupId: Value(uuid.v4()), - isGroupAdmin: const Value(true), - ); - return _insertGroup(insertGroup); + return _insertGroup(group); + } + + Future insertGroupMember(GroupMembersCompanion members) async { + await into(groupMembers).insert(members); + } + + Future insertGroupAction(GroupHistoriesCompanion action) async { + var insertAction = action; + if (!action.groupHistoryId.present) { + insertAction = action.copyWith( + groupHistoryId: Value(uuid.v4()), + ); + } + await into(groupHistories).insert(insertAction); } Future createNewDirectChat( @@ -53,6 +63,7 @@ class GroupsDao extends DatabaseAccessor with _$GroupsDaoMixin { groupId: Value(groupIdDirectChat), isDirectChat: const Value(true), isGroupAdmin: const Value(true), + joinedGroup: const Value(true), ); final result = await _insertGroup(insertGroup); @@ -138,6 +149,14 @@ class GroupsDao extends DatabaseAccessor with _$GroupsDaoMixin { return (select(groups)..where((t) => t.isDirectChat.equals(true))).get(); } + Future> getAllNotJoinedGroups() { + return (select(groups) + ..where( + (t) => t.joinedGroup.equals(false) & t.isDirectChat.equals(false), + )) + .get(); + } + Future getDirectChat(int userId) async { final query = ((select(groups)..where((t) => t.isDirectChat.equals(true))).join([ diff --git a/lib/src/database/daos/groups.dao.g.dart b/lib/src/database/daos/groups.dao.g.dart index 3489f8c..4f873ec 100644 --- a/lib/src/database/daos/groups.dao.g.dart +++ b/lib/src/database/daos/groups.dao.g.dart @@ -7,4 +7,5 @@ mixin _$GroupsDaoMixin on DatabaseAccessor { $GroupsTable get groups => attachedDatabase.groups; $ContactsTable get contacts => attachedDatabase.contacts; $GroupMembersTable get groupMembers => attachedDatabase.groupMembers; + $GroupHistoriesTable get groupHistories => attachedDatabase.groupHistories; } diff --git a/lib/src/database/tables/groups.table.dart b/lib/src/database/tables/groups.table.dart index 708622c..e745572 100644 --- a/lib/src/database/tables/groups.table.dart +++ b/lib/src/database/tables/groups.table.dart @@ -1,15 +1,25 @@ import 'package:drift/drift.dart'; import 'package:twonly/src/database/tables/contacts.table.dart'; +const int defaultDeleteMessagesAfterMilliseconds = 1000 * 60 * 60 * 24; + @DataClassName('Group') class Groups extends Table { TextColumn get groupId => text()(); - BoolColumn get isGroupAdmin => boolean()(); - BoolColumn get isDirectChat => boolean()(); + BoolColumn get isGroupAdmin => boolean().withDefault(const Constant(false))(); + BoolColumn get isDirectChat => boolean().withDefault(const Constant(false))(); BoolColumn get pinned => boolean().withDefault(const Constant(false))(); BoolColumn get archived => boolean().withDefault(const Constant(false))(); + BoolColumn get joinedGroup => boolean().withDefault(const Constant(false))(); + BoolColumn get leftGroup => boolean().withDefault(const Constant(false))(); + + IntColumn get stateVersionId => integer().withDefault(const Constant(0))(); + + BlobColumn get stateEncryptionKey => blob().nullable()(); + BlobColumn get myGroupPrivateKey => blob().nullable()(); + TextColumn get groupName => text()(); IntColumn get totalMediaCounter => integer().withDefault(const Constant(0))(); @@ -17,8 +27,8 @@ class Groups extends Table { BoolColumn get alsoBestFriend => boolean().withDefault(const Constant(false))(); - IntColumn get deleteMessagesAfterMilliseconds => - integer().withDefault(const Constant(1000 * 60 * 60 * 24))(); + IntColumn get deleteMessagesAfterMilliseconds => integer() + .withDefault(const Constant(defaultDeleteMessagesAfterMilliseconds))(); DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)(); @@ -39,17 +49,49 @@ class Groups extends Table { Set get primaryKey => {groupId}; } -enum MemberState { invited, accepted, admin } +enum MemberState { normal, admin } @DataClassName('GroupMember') class GroupMembers extends Table { - TextColumn get groupId => text()(); + TextColumn get groupId => + text().references(Groups, #groupId, onDelete: KeyAction.cascade)(); IntColumn get contactId => integer().references(Contacts, #userId)(); TextColumn get memberState => textEnum().nullable()(); + BlobColumn get groupPublicKey => blob().nullable()(); DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)(); @override Set get primaryKey => {groupId, contactId}; } + +enum GroupActionType { + createdGroup, + removedMember, + addMember, + leftGroup, + promoteToAdmin, + demoteToMember, + updatedGroupName, +} + +@DataClassName('GroupHistory') +class GroupHistories extends Table { + TextColumn get groupHistoryId => text()(); + TextColumn get groupId => + text().references(Groups, #groupId, onDelete: KeyAction.cascade)(); + + IntColumn get affectedContactId => + integer().nullable().references(Contacts, #userId)(); + + TextColumn get oldGroupName => text().nullable()(); + TextColumn get newGroupName => text().nullable()(); + + TextColumn get type => textEnum()(); + + DateTimeColumn get actionAt => dateTime().withDefault(currentDateAndTime)(); + + @override + Set get primaryKey => {groupHistoryId}; +} diff --git a/lib/src/database/twonly.db.dart b/lib/src/database/twonly.db.dart index b5681f2..6b02b5c 100644 --- a/lib/src/database/twonly.db.dart +++ b/lib/src/database/twonly.db.dart @@ -44,6 +44,7 @@ part 'twonly.db.g.dart'; SignalContactPreKeys, SignalContactSignedPreKeys, MessageActions, + GroupHistories ], daos: [ MessagesDao, diff --git a/lib/src/database/twonly.db.g.dart b/lib/src/database/twonly.db.g.dart index 57b9870..76dd93a 100644 --- a/lib/src/database/twonly.db.g.dart +++ b/lib/src/database/twonly.db.g.dart @@ -671,18 +671,20 @@ class $GroupsTable extends Groups with TableInfo<$GroupsTable, Group> { late final GeneratedColumn isGroupAdmin = GeneratedColumn( 'is_group_admin', aliasedName, false, type: DriftSqlType.bool, - requiredDuringInsert: true, + requiredDuringInsert: false, defaultConstraints: GeneratedColumn.constraintIsAlways( - 'CHECK ("is_group_admin" IN (0, 1))')); + 'CHECK ("is_group_admin" IN (0, 1))'), + defaultValue: const Constant(false)); static const VerificationMeta _isDirectChatMeta = const VerificationMeta('isDirectChat'); @override late final GeneratedColumn isDirectChat = GeneratedColumn( 'is_direct_chat', aliasedName, false, type: DriftSqlType.bool, - requiredDuringInsert: true, + requiredDuringInsert: false, defaultConstraints: GeneratedColumn.constraintIsAlways( - 'CHECK ("is_direct_chat" IN (0, 1))')); + 'CHECK ("is_direct_chat" IN (0, 1))'), + defaultValue: const Constant(false)); static const VerificationMeta _pinnedMeta = const VerificationMeta('pinned'); @override late final GeneratedColumn pinned = GeneratedColumn( @@ -702,6 +704,46 @@ class $GroupsTable extends Groups with TableInfo<$GroupsTable, Group> { defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("archived" IN (0, 1))'), defaultValue: const Constant(false)); + static const VerificationMeta _joinedGroupMeta = + const VerificationMeta('joinedGroup'); + @override + late final GeneratedColumn joinedGroup = GeneratedColumn( + 'joined_group', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("joined_group" IN (0, 1))'), + defaultValue: const Constant(false)); + static const VerificationMeta _leftGroupMeta = + const VerificationMeta('leftGroup'); + @override + late final GeneratedColumn leftGroup = GeneratedColumn( + 'left_group', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("left_group" IN (0, 1))'), + defaultValue: const Constant(false)); + static const VerificationMeta _stateVersionIdMeta = + const VerificationMeta('stateVersionId'); + @override + late final GeneratedColumn stateVersionId = GeneratedColumn( + 'state_version_id', aliasedName, false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const Constant(0)); + static const VerificationMeta _stateEncryptionKeyMeta = + const VerificationMeta('stateEncryptionKey'); + @override + late final GeneratedColumn stateEncryptionKey = + GeneratedColumn('state_encryption_key', aliasedName, true, + type: DriftSqlType.blob, requiredDuringInsert: false); + static const VerificationMeta _myGroupPrivateKeyMeta = + const VerificationMeta('myGroupPrivateKey'); + @override + late final GeneratedColumn myGroupPrivateKey = + GeneratedColumn('my_group_private_key', aliasedName, true, + type: DriftSqlType.blob, requiredDuringInsert: false); static const VerificationMeta _groupNameMeta = const VerificationMeta('groupName'); @override @@ -734,7 +776,7 @@ class $GroupsTable extends Groups with TableInfo<$GroupsTable, Group> { 'delete_messages_after_milliseconds', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: false, - defaultValue: const Constant(1000 * 60 * 60 * 24)); + defaultValue: const Constant(defaultDeleteMessagesAfterMilliseconds)); static const VerificationMeta _createdAtMeta = const VerificationMeta('createdAt'); @override @@ -804,6 +846,11 @@ class $GroupsTable extends Groups with TableInfo<$GroupsTable, Group> { isDirectChat, pinned, archived, + joinedGroup, + leftGroup, + stateVersionId, + stateEncryptionKey, + myGroupPrivateKey, groupName, totalMediaCounter, alsoBestFriend, @@ -839,16 +886,12 @@ class $GroupsTable extends Groups with TableInfo<$GroupsTable, Group> { _isGroupAdminMeta, isGroupAdmin.isAcceptableOrUnknown( data['is_group_admin']!, _isGroupAdminMeta)); - } else if (isInserting) { - context.missing(_isGroupAdminMeta); } if (data.containsKey('is_direct_chat')) { context.handle( _isDirectChatMeta, isDirectChat.isAcceptableOrUnknown( data['is_direct_chat']!, _isDirectChatMeta)); - } else if (isInserting) { - context.missing(_isDirectChatMeta); } if (data.containsKey('pinned')) { context.handle(_pinnedMeta, @@ -858,6 +901,34 @@ class $GroupsTable extends Groups with TableInfo<$GroupsTable, Group> { context.handle(_archivedMeta, archived.isAcceptableOrUnknown(data['archived']!, _archivedMeta)); } + if (data.containsKey('joined_group')) { + context.handle( + _joinedGroupMeta, + joinedGroup.isAcceptableOrUnknown( + data['joined_group']!, _joinedGroupMeta)); + } + if (data.containsKey('left_group')) { + context.handle(_leftGroupMeta, + leftGroup.isAcceptableOrUnknown(data['left_group']!, _leftGroupMeta)); + } + if (data.containsKey('state_version_id')) { + context.handle( + _stateVersionIdMeta, + stateVersionId.isAcceptableOrUnknown( + data['state_version_id']!, _stateVersionIdMeta)); + } + if (data.containsKey('state_encryption_key')) { + context.handle( + _stateEncryptionKeyMeta, + stateEncryptionKey.isAcceptableOrUnknown( + data['state_encryption_key']!, _stateEncryptionKeyMeta)); + } + if (data.containsKey('my_group_private_key')) { + context.handle( + _myGroupPrivateKeyMeta, + myGroupPrivateKey.isAcceptableOrUnknown( + data['my_group_private_key']!, _myGroupPrivateKeyMeta)); + } if (data.containsKey('group_name')) { context.handle(_groupNameMeta, groupName.isAcceptableOrUnknown(data['group_name']!, _groupNameMeta)); @@ -954,6 +1025,16 @@ class $GroupsTable extends Groups with TableInfo<$GroupsTable, Group> { .read(DriftSqlType.bool, data['${effectivePrefix}pinned'])!, archived: attachedDatabase.typeMapping .read(DriftSqlType.bool, data['${effectivePrefix}archived'])!, + joinedGroup: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}joined_group'])!, + leftGroup: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}left_group'])!, + stateVersionId: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}state_version_id'])!, + stateEncryptionKey: attachedDatabase.typeMapping.read( + DriftSqlType.blob, data['${effectivePrefix}state_encryption_key']), + myGroupPrivateKey: attachedDatabase.typeMapping.read( + DriftSqlType.blob, data['${effectivePrefix}my_group_private_key']), groupName: attachedDatabase.typeMapping .read(DriftSqlType.string, data['${effectivePrefix}group_name'])!, totalMediaCounter: attachedDatabase.typeMapping.read( @@ -1000,6 +1081,11 @@ class Group extends DataClass implements Insertable { final bool isDirectChat; final bool pinned; final bool archived; + final bool joinedGroup; + final bool leftGroup; + final int stateVersionId; + final Uint8List? stateEncryptionKey; + final Uint8List? myGroupPrivateKey; final String groupName; final int totalMediaCounter; final bool alsoBestFriend; @@ -1019,6 +1105,11 @@ class Group extends DataClass implements Insertable { required this.isDirectChat, required this.pinned, required this.archived, + required this.joinedGroup, + required this.leftGroup, + required this.stateVersionId, + this.stateEncryptionKey, + this.myGroupPrivateKey, required this.groupName, required this.totalMediaCounter, required this.alsoBestFriend, @@ -1040,6 +1131,15 @@ class Group extends DataClass implements Insertable { map['is_direct_chat'] = Variable(isDirectChat); map['pinned'] = Variable(pinned); map['archived'] = Variable(archived); + map['joined_group'] = Variable(joinedGroup); + map['left_group'] = Variable(leftGroup); + map['state_version_id'] = Variable(stateVersionId); + if (!nullToAbsent || stateEncryptionKey != null) { + map['state_encryption_key'] = Variable(stateEncryptionKey); + } + if (!nullToAbsent || myGroupPrivateKey != null) { + map['my_group_private_key'] = Variable(myGroupPrivateKey); + } map['group_name'] = Variable(groupName); map['total_media_counter'] = Variable(totalMediaCounter); map['also_best_friend'] = Variable(alsoBestFriend); @@ -1075,6 +1175,15 @@ class Group extends DataClass implements Insertable { isDirectChat: Value(isDirectChat), pinned: Value(pinned), archived: Value(archived), + joinedGroup: Value(joinedGroup), + leftGroup: Value(leftGroup), + stateVersionId: Value(stateVersionId), + stateEncryptionKey: stateEncryptionKey == null && nullToAbsent + ? const Value.absent() + : Value(stateEncryptionKey), + myGroupPrivateKey: myGroupPrivateKey == null && nullToAbsent + ? const Value.absent() + : Value(myGroupPrivateKey), groupName: Value(groupName), totalMediaCounter: Value(totalMediaCounter), alsoBestFriend: Value(alsoBestFriend), @@ -1110,6 +1219,13 @@ class Group extends DataClass implements Insertable { isDirectChat: serializer.fromJson(json['isDirectChat']), pinned: serializer.fromJson(json['pinned']), archived: serializer.fromJson(json['archived']), + joinedGroup: serializer.fromJson(json['joinedGroup']), + leftGroup: serializer.fromJson(json['leftGroup']), + stateVersionId: serializer.fromJson(json['stateVersionId']), + stateEncryptionKey: + serializer.fromJson(json['stateEncryptionKey']), + myGroupPrivateKey: + serializer.fromJson(json['myGroupPrivateKey']), groupName: serializer.fromJson(json['groupName']), totalMediaCounter: serializer.fromJson(json['totalMediaCounter']), alsoBestFriend: serializer.fromJson(json['alsoBestFriend']), @@ -1139,6 +1255,11 @@ class Group extends DataClass implements Insertable { 'isDirectChat': serializer.toJson(isDirectChat), 'pinned': serializer.toJson(pinned), 'archived': serializer.toJson(archived), + 'joinedGroup': serializer.toJson(joinedGroup), + 'leftGroup': serializer.toJson(leftGroup), + 'stateVersionId': serializer.toJson(stateVersionId), + 'stateEncryptionKey': serializer.toJson(stateEncryptionKey), + 'myGroupPrivateKey': serializer.toJson(myGroupPrivateKey), 'groupName': serializer.toJson(groupName), 'totalMediaCounter': serializer.toJson(totalMediaCounter), 'alsoBestFriend': serializer.toJson(alsoBestFriend), @@ -1163,6 +1284,11 @@ class Group extends DataClass implements Insertable { bool? isDirectChat, bool? pinned, bool? archived, + bool? joinedGroup, + bool? leftGroup, + int? stateVersionId, + Value stateEncryptionKey = const Value.absent(), + Value myGroupPrivateKey = const Value.absent(), String? groupName, int? totalMediaCounter, bool? alsoBestFriend, @@ -1182,6 +1308,15 @@ class Group extends DataClass implements Insertable { isDirectChat: isDirectChat ?? this.isDirectChat, pinned: pinned ?? this.pinned, archived: archived ?? this.archived, + joinedGroup: joinedGroup ?? this.joinedGroup, + leftGroup: leftGroup ?? this.leftGroup, + stateVersionId: stateVersionId ?? this.stateVersionId, + stateEncryptionKey: stateEncryptionKey.present + ? stateEncryptionKey.value + : this.stateEncryptionKey, + myGroupPrivateKey: myGroupPrivateKey.present + ? myGroupPrivateKey.value + : this.myGroupPrivateKey, groupName: groupName ?? this.groupName, totalMediaCounter: totalMediaCounter ?? this.totalMediaCounter, alsoBestFriend: alsoBestFriend ?? this.alsoBestFriend, @@ -1217,6 +1352,18 @@ class Group extends DataClass implements Insertable { : this.isDirectChat, pinned: data.pinned.present ? data.pinned.value : this.pinned, archived: data.archived.present ? data.archived.value : this.archived, + joinedGroup: + data.joinedGroup.present ? data.joinedGroup.value : this.joinedGroup, + leftGroup: data.leftGroup.present ? data.leftGroup.value : this.leftGroup, + stateVersionId: data.stateVersionId.present + ? data.stateVersionId.value + : this.stateVersionId, + stateEncryptionKey: data.stateEncryptionKey.present + ? data.stateEncryptionKey.value + : this.stateEncryptionKey, + myGroupPrivateKey: data.myGroupPrivateKey.present + ? data.myGroupPrivateKey.value + : this.myGroupPrivateKey, groupName: data.groupName.present ? data.groupName.value : this.groupName, totalMediaCounter: data.totalMediaCounter.present ? data.totalMediaCounter.value @@ -1264,6 +1411,11 @@ class Group extends DataClass implements Insertable { ..write('isDirectChat: $isDirectChat, ') ..write('pinned: $pinned, ') ..write('archived: $archived, ') + ..write('joinedGroup: $joinedGroup, ') + ..write('leftGroup: $leftGroup, ') + ..write('stateVersionId: $stateVersionId, ') + ..write('stateEncryptionKey: $stateEncryptionKey, ') + ..write('myGroupPrivateKey: $myGroupPrivateKey, ') ..write('groupName: $groupName, ') ..write('totalMediaCounter: $totalMediaCounter, ') ..write('alsoBestFriend: $alsoBestFriend, ') @@ -1283,25 +1435,31 @@ class Group extends DataClass implements Insertable { } @override - int get hashCode => Object.hash( - groupId, - isGroupAdmin, - isDirectChat, - pinned, - archived, - groupName, - totalMediaCounter, - alsoBestFriend, - deleteMessagesAfterMilliseconds, - createdAt, - lastMessageSend, - lastMessageReceived, - lastFlameCounterChange, - lastFlameSync, - flameCounter, - maxFlameCounter, - maxFlameCounterFrom, - lastMessageExchange); + int get hashCode => Object.hashAll([ + groupId, + isGroupAdmin, + isDirectChat, + pinned, + archived, + joinedGroup, + leftGroup, + stateVersionId, + $driftBlobEquality.hash(stateEncryptionKey), + $driftBlobEquality.hash(myGroupPrivateKey), + groupName, + totalMediaCounter, + alsoBestFriend, + deleteMessagesAfterMilliseconds, + createdAt, + lastMessageSend, + lastMessageReceived, + lastFlameCounterChange, + lastFlameSync, + flameCounter, + maxFlameCounter, + maxFlameCounterFrom, + lastMessageExchange + ]); @override bool operator ==(Object other) => identical(this, other) || @@ -1311,6 +1469,13 @@ class Group extends DataClass implements Insertable { other.isDirectChat == this.isDirectChat && other.pinned == this.pinned && other.archived == this.archived && + other.joinedGroup == this.joinedGroup && + other.leftGroup == this.leftGroup && + other.stateVersionId == this.stateVersionId && + $driftBlobEquality.equals( + other.stateEncryptionKey, this.stateEncryptionKey) && + $driftBlobEquality.equals( + other.myGroupPrivateKey, this.myGroupPrivateKey) && other.groupName == this.groupName && other.totalMediaCounter == this.totalMediaCounter && other.alsoBestFriend == this.alsoBestFriend && @@ -1333,6 +1498,11 @@ class GroupsCompanion extends UpdateCompanion { final Value isDirectChat; final Value pinned; final Value archived; + final Value joinedGroup; + final Value leftGroup; + final Value stateVersionId; + final Value stateEncryptionKey; + final Value myGroupPrivateKey; final Value groupName; final Value totalMediaCounter; final Value alsoBestFriend; @@ -1353,6 +1523,11 @@ class GroupsCompanion extends UpdateCompanion { this.isDirectChat = const Value.absent(), this.pinned = const Value.absent(), this.archived = const Value.absent(), + this.joinedGroup = const Value.absent(), + this.leftGroup = const Value.absent(), + this.stateVersionId = const Value.absent(), + this.stateEncryptionKey = const Value.absent(), + this.myGroupPrivateKey = const Value.absent(), this.groupName = const Value.absent(), this.totalMediaCounter = const Value.absent(), this.alsoBestFriend = const Value.absent(), @@ -1370,10 +1545,15 @@ class GroupsCompanion extends UpdateCompanion { }); GroupsCompanion.insert({ required String groupId, - required bool isGroupAdmin, - required bool isDirectChat, + this.isGroupAdmin = const Value.absent(), + this.isDirectChat = const Value.absent(), this.pinned = const Value.absent(), this.archived = const Value.absent(), + this.joinedGroup = const Value.absent(), + this.leftGroup = const Value.absent(), + this.stateVersionId = const Value.absent(), + this.stateEncryptionKey = const Value.absent(), + this.myGroupPrivateKey = const Value.absent(), required String groupName, this.totalMediaCounter = const Value.absent(), this.alsoBestFriend = const Value.absent(), @@ -1389,8 +1569,6 @@ class GroupsCompanion extends UpdateCompanion { this.lastMessageExchange = const Value.absent(), this.rowid = const Value.absent(), }) : groupId = Value(groupId), - isGroupAdmin = Value(isGroupAdmin), - isDirectChat = Value(isDirectChat), groupName = Value(groupName); static Insertable custom({ Expression? groupId, @@ -1398,6 +1576,11 @@ class GroupsCompanion extends UpdateCompanion { Expression? isDirectChat, Expression? pinned, Expression? archived, + Expression? joinedGroup, + Expression? leftGroup, + Expression? stateVersionId, + Expression? stateEncryptionKey, + Expression? myGroupPrivateKey, Expression? groupName, Expression? totalMediaCounter, Expression? alsoBestFriend, @@ -1419,6 +1602,12 @@ class GroupsCompanion extends UpdateCompanion { if (isDirectChat != null) 'is_direct_chat': isDirectChat, if (pinned != null) 'pinned': pinned, if (archived != null) 'archived': archived, + if (joinedGroup != null) 'joined_group': joinedGroup, + if (leftGroup != null) 'left_group': leftGroup, + if (stateVersionId != null) 'state_version_id': stateVersionId, + if (stateEncryptionKey != null) + 'state_encryption_key': stateEncryptionKey, + if (myGroupPrivateKey != null) 'my_group_private_key': myGroupPrivateKey, if (groupName != null) 'group_name': groupName, if (totalMediaCounter != null) 'total_media_counter': totalMediaCounter, if (alsoBestFriend != null) 'also_best_friend': alsoBestFriend, @@ -1447,6 +1636,11 @@ class GroupsCompanion extends UpdateCompanion { Value? isDirectChat, Value? pinned, Value? archived, + Value? joinedGroup, + Value? leftGroup, + Value? stateVersionId, + Value? stateEncryptionKey, + Value? myGroupPrivateKey, Value? groupName, Value? totalMediaCounter, Value? alsoBestFriend, @@ -1467,6 +1661,11 @@ class GroupsCompanion extends UpdateCompanion { isDirectChat: isDirectChat ?? this.isDirectChat, pinned: pinned ?? this.pinned, archived: archived ?? this.archived, + joinedGroup: joinedGroup ?? this.joinedGroup, + leftGroup: leftGroup ?? this.leftGroup, + stateVersionId: stateVersionId ?? this.stateVersionId, + stateEncryptionKey: stateEncryptionKey ?? this.stateEncryptionKey, + myGroupPrivateKey: myGroupPrivateKey ?? this.myGroupPrivateKey, groupName: groupName ?? this.groupName, totalMediaCounter: totalMediaCounter ?? this.totalMediaCounter, alsoBestFriend: alsoBestFriend ?? this.alsoBestFriend, @@ -1504,6 +1703,23 @@ class GroupsCompanion extends UpdateCompanion { if (archived.present) { map['archived'] = Variable(archived.value); } + if (joinedGroup.present) { + map['joined_group'] = Variable(joinedGroup.value); + } + if (leftGroup.present) { + map['left_group'] = Variable(leftGroup.value); + } + if (stateVersionId.present) { + map['state_version_id'] = Variable(stateVersionId.value); + } + if (stateEncryptionKey.present) { + map['state_encryption_key'] = + Variable(stateEncryptionKey.value); + } + if (myGroupPrivateKey.present) { + map['my_group_private_key'] = + Variable(myGroupPrivateKey.value); + } if (groupName.present) { map['group_name'] = Variable(groupName.value); } @@ -1562,6 +1778,11 @@ class GroupsCompanion extends UpdateCompanion { ..write('isDirectChat: $isDirectChat, ') ..write('pinned: $pinned, ') ..write('archived: $archived, ') + ..write('joinedGroup: $joinedGroup, ') + ..write('leftGroup: $leftGroup, ') + ..write('stateVersionId: $stateVersionId, ') + ..write('stateEncryptionKey: $stateEncryptionKey, ') + ..write('myGroupPrivateKey: $myGroupPrivateKey, ') ..write('groupName: $groupName, ') ..write('totalMediaCounter: $totalMediaCounter, ') ..write('alsoBestFriend: $alsoBestFriend, ') @@ -3741,7 +3962,10 @@ class $GroupMembersTable extends GroupMembers @override late final GeneratedColumn groupId = GeneratedColumn( 'group_id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES "groups" (group_id) ON DELETE CASCADE')); static const VerificationMeta _contactIdMeta = const VerificationMeta('contactId'); @override @@ -3757,6 +3981,12 @@ class $GroupMembersTable extends GroupMembers type: DriftSqlType.string, requiredDuringInsert: false) .withConverter( $GroupMembersTable.$convertermemberStaten); + static const VerificationMeta _groupPublicKeyMeta = + const VerificationMeta('groupPublicKey'); + @override + late final GeneratedColumn groupPublicKey = + GeneratedColumn('group_public_key', aliasedName, true, + type: DriftSqlType.blob, requiredDuringInsert: false); static const VerificationMeta _createdAtMeta = const VerificationMeta('createdAt'); @override @@ -3767,7 +3997,7 @@ class $GroupMembersTable extends GroupMembers defaultValue: currentDateAndTime); @override List get $columns => - [groupId, contactId, memberState, createdAt]; + [groupId, contactId, memberState, groupPublicKey, createdAt]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -3790,6 +4020,12 @@ class $GroupMembersTable extends GroupMembers } else if (isInserting) { context.missing(_contactIdMeta); } + if (data.containsKey('group_public_key')) { + context.handle( + _groupPublicKeyMeta, + groupPublicKey.isAcceptableOrUnknown( + data['group_public_key']!, _groupPublicKeyMeta)); + } if (data.containsKey('created_at')) { context.handle(_createdAtMeta, createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); @@ -3810,6 +4046,8 @@ class $GroupMembersTable extends GroupMembers memberState: $GroupMembersTable.$convertermemberStaten.fromSql( attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}member_state'])), + groupPublicKey: attachedDatabase.typeMapping + .read(DriftSqlType.blob, data['${effectivePrefix}group_public_key']), createdAt: attachedDatabase.typeMapping .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, ); @@ -3831,11 +4069,13 @@ class GroupMember extends DataClass implements Insertable { final String groupId; final int contactId; final MemberState? memberState; + final Uint8List? groupPublicKey; final DateTime createdAt; const GroupMember( {required this.groupId, required this.contactId, this.memberState, + this.groupPublicKey, required this.createdAt}); @override Map toColumns(bool nullToAbsent) { @@ -3846,6 +4086,9 @@ class GroupMember extends DataClass implements Insertable { map['member_state'] = Variable( $GroupMembersTable.$convertermemberStaten.toSql(memberState)); } + if (!nullToAbsent || groupPublicKey != null) { + map['group_public_key'] = Variable(groupPublicKey); + } map['created_at'] = Variable(createdAt); return map; } @@ -3857,6 +4100,9 @@ class GroupMember extends DataClass implements Insertable { memberState: memberState == null && nullToAbsent ? const Value.absent() : Value(memberState), + groupPublicKey: groupPublicKey == null && nullToAbsent + ? const Value.absent() + : Value(groupPublicKey), createdAt: Value(createdAt), ); } @@ -3869,6 +4115,7 @@ class GroupMember extends DataClass implements Insertable { contactId: serializer.fromJson(json['contactId']), memberState: $GroupMembersTable.$convertermemberStaten .fromJson(serializer.fromJson(json['memberState'])), + groupPublicKey: serializer.fromJson(json['groupPublicKey']), createdAt: serializer.fromJson(json['createdAt']), ); } @@ -3880,6 +4127,7 @@ class GroupMember extends DataClass implements Insertable { 'contactId': serializer.toJson(contactId), 'memberState': serializer.toJson( $GroupMembersTable.$convertermemberStaten.toJson(memberState)), + 'groupPublicKey': serializer.toJson(groupPublicKey), 'createdAt': serializer.toJson(createdAt), }; } @@ -3888,11 +4136,14 @@ class GroupMember extends DataClass implements Insertable { {String? groupId, int? contactId, Value memberState = const Value.absent(), + Value groupPublicKey = const Value.absent(), DateTime? createdAt}) => GroupMember( groupId: groupId ?? this.groupId, contactId: contactId ?? this.contactId, memberState: memberState.present ? memberState.value : this.memberState, + groupPublicKey: + groupPublicKey.present ? groupPublicKey.value : this.groupPublicKey, createdAt: createdAt ?? this.createdAt, ); GroupMember copyWithCompanion(GroupMembersCompanion data) { @@ -3901,6 +4152,9 @@ class GroupMember extends DataClass implements Insertable { contactId: data.contactId.present ? data.contactId.value : this.contactId, memberState: data.memberState.present ? data.memberState.value : this.memberState, + groupPublicKey: data.groupPublicKey.present + ? data.groupPublicKey.value + : this.groupPublicKey, createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, ); } @@ -3911,13 +4165,15 @@ class GroupMember extends DataClass implements Insertable { ..write('groupId: $groupId, ') ..write('contactId: $contactId, ') ..write('memberState: $memberState, ') + ..write('groupPublicKey: $groupPublicKey, ') ..write('createdAt: $createdAt') ..write(')')) .toString(); } @override - int get hashCode => Object.hash(groupId, contactId, memberState, createdAt); + int get hashCode => Object.hash(groupId, contactId, memberState, + $driftBlobEquality.hash(groupPublicKey), createdAt); @override bool operator ==(Object other) => identical(this, other) || @@ -3925,6 +4181,8 @@ class GroupMember extends DataClass implements Insertable { other.groupId == this.groupId && other.contactId == this.contactId && other.memberState == this.memberState && + $driftBlobEquality.equals( + other.groupPublicKey, this.groupPublicKey) && other.createdAt == this.createdAt); } @@ -3932,12 +4190,14 @@ class GroupMembersCompanion extends UpdateCompanion { final Value groupId; final Value contactId; final Value memberState; + final Value groupPublicKey; final Value createdAt; final Value rowid; const GroupMembersCompanion({ this.groupId = const Value.absent(), this.contactId = const Value.absent(), this.memberState = const Value.absent(), + this.groupPublicKey = const Value.absent(), this.createdAt = const Value.absent(), this.rowid = const Value.absent(), }); @@ -3945,6 +4205,7 @@ class GroupMembersCompanion extends UpdateCompanion { required String groupId, required int contactId, this.memberState = const Value.absent(), + this.groupPublicKey = const Value.absent(), this.createdAt = const Value.absent(), this.rowid = const Value.absent(), }) : groupId = Value(groupId), @@ -3953,6 +4214,7 @@ class GroupMembersCompanion extends UpdateCompanion { Expression? groupId, Expression? contactId, Expression? memberState, + Expression? groupPublicKey, Expression? createdAt, Expression? rowid, }) { @@ -3960,6 +4222,7 @@ class GroupMembersCompanion extends UpdateCompanion { if (groupId != null) 'group_id': groupId, if (contactId != null) 'contact_id': contactId, if (memberState != null) 'member_state': memberState, + if (groupPublicKey != null) 'group_public_key': groupPublicKey, if (createdAt != null) 'created_at': createdAt, if (rowid != null) 'rowid': rowid, }); @@ -3969,12 +4232,14 @@ class GroupMembersCompanion extends UpdateCompanion { {Value? groupId, Value? contactId, Value? memberState, + Value? groupPublicKey, Value? createdAt, Value? rowid}) { return GroupMembersCompanion( groupId: groupId ?? this.groupId, contactId: contactId ?? this.contactId, memberState: memberState ?? this.memberState, + groupPublicKey: groupPublicKey ?? this.groupPublicKey, createdAt: createdAt ?? this.createdAt, rowid: rowid ?? this.rowid, ); @@ -3993,6 +4258,9 @@ class GroupMembersCompanion extends UpdateCompanion { map['member_state'] = Variable( $GroupMembersTable.$convertermemberStaten.toSql(memberState.value)); } + if (groupPublicKey.present) { + map['group_public_key'] = Variable(groupPublicKey.value); + } if (createdAt.present) { map['created_at'] = Variable(createdAt.value); } @@ -4008,6 +4276,7 @@ class GroupMembersCompanion extends UpdateCompanion { ..write('groupId: $groupId, ') ..write('contactId: $contactId, ') ..write('memberState: $memberState, ') + ..write('groupPublicKey: $groupPublicKey, ') ..write('createdAt: $createdAt, ') ..write('rowid: $rowid') ..write(')')) @@ -6590,6 +6859,430 @@ class MessageActionsCompanion extends UpdateCompanion { } } +class $GroupHistoriesTable extends GroupHistories + with TableInfo<$GroupHistoriesTable, GroupHistory> { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + $GroupHistoriesTable(this.attachedDatabase, [this._alias]); + static const VerificationMeta _groupHistoryIdMeta = + const VerificationMeta('groupHistoryId'); + @override + late final GeneratedColumn groupHistoryId = GeneratedColumn( + 'group_history_id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + static const VerificationMeta _groupIdMeta = + const VerificationMeta('groupId'); + @override + late final GeneratedColumn groupId = GeneratedColumn( + 'group_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES "groups" (group_id) ON DELETE CASCADE')); + static const VerificationMeta _affectedContactIdMeta = + const VerificationMeta('affectedContactId'); + @override + late final GeneratedColumn affectedContactId = GeneratedColumn( + 'affected_contact_id', aliasedName, true, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('REFERENCES contacts (user_id)')); + static const VerificationMeta _oldGroupNameMeta = + const VerificationMeta('oldGroupName'); + @override + late final GeneratedColumn oldGroupName = GeneratedColumn( + 'old_group_name', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + static const VerificationMeta _newGroupNameMeta = + const VerificationMeta('newGroupName'); + @override + late final GeneratedColumn newGroupName = GeneratedColumn( + 'new_group_name', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + @override + late final GeneratedColumnWithTypeConverter type = + GeneratedColumn('type', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true) + .withConverter($GroupHistoriesTable.$convertertype); + static const VerificationMeta _actionAtMeta = + const VerificationMeta('actionAt'); + @override + late final GeneratedColumn actionAt = GeneratedColumn( + 'action_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: currentDateAndTime); + @override + List get $columns => [ + groupHistoryId, + groupId, + affectedContactId, + oldGroupName, + newGroupName, + type, + actionAt + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'group_histories'; + @override + VerificationContext validateIntegrity(Insertable instance, + {bool isInserting = false}) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('group_history_id')) { + context.handle( + _groupHistoryIdMeta, + groupHistoryId.isAcceptableOrUnknown( + data['group_history_id']!, _groupHistoryIdMeta)); + } else if (isInserting) { + context.missing(_groupHistoryIdMeta); + } + if (data.containsKey('group_id')) { + context.handle(_groupIdMeta, + groupId.isAcceptableOrUnknown(data['group_id']!, _groupIdMeta)); + } else if (isInserting) { + context.missing(_groupIdMeta); + } + if (data.containsKey('affected_contact_id')) { + context.handle( + _affectedContactIdMeta, + affectedContactId.isAcceptableOrUnknown( + data['affected_contact_id']!, _affectedContactIdMeta)); + } + if (data.containsKey('old_group_name')) { + context.handle( + _oldGroupNameMeta, + oldGroupName.isAcceptableOrUnknown( + data['old_group_name']!, _oldGroupNameMeta)); + } + if (data.containsKey('new_group_name')) { + context.handle( + _newGroupNameMeta, + newGroupName.isAcceptableOrUnknown( + data['new_group_name']!, _newGroupNameMeta)); + } + if (data.containsKey('action_at')) { + context.handle(_actionAtMeta, + actionAt.isAcceptableOrUnknown(data['action_at']!, _actionAtMeta)); + } + return context; + } + + @override + Set get $primaryKey => {groupHistoryId}; + @override + GroupHistory map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return GroupHistory( + groupHistoryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, data['${effectivePrefix}group_history_id'])!, + groupId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}group_id'])!, + affectedContactId: attachedDatabase.typeMapping.read( + DriftSqlType.int, data['${effectivePrefix}affected_contact_id']), + oldGroupName: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}old_group_name']), + newGroupName: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}new_group_name']), + type: $GroupHistoriesTable.$convertertype.fromSql(attachedDatabase + .typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}type'])!), + actionAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}action_at'])!, + ); + } + + @override + $GroupHistoriesTable createAlias(String alias) { + return $GroupHistoriesTable(attachedDatabase, alias); + } + + static JsonTypeConverter2 $convertertype = + const EnumNameConverter(GroupActionType.values); +} + +class GroupHistory extends DataClass implements Insertable { + final String groupHistoryId; + final String groupId; + final int? affectedContactId; + final String? oldGroupName; + final String? newGroupName; + final GroupActionType type; + final DateTime actionAt; + const GroupHistory( + {required this.groupHistoryId, + required this.groupId, + this.affectedContactId, + this.oldGroupName, + this.newGroupName, + required this.type, + required this.actionAt}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['group_history_id'] = Variable(groupHistoryId); + map['group_id'] = Variable(groupId); + if (!nullToAbsent || affectedContactId != null) { + map['affected_contact_id'] = Variable(affectedContactId); + } + if (!nullToAbsent || oldGroupName != null) { + map['old_group_name'] = Variable(oldGroupName); + } + if (!nullToAbsent || newGroupName != null) { + map['new_group_name'] = Variable(newGroupName); + } + { + map['type'] = + Variable($GroupHistoriesTable.$convertertype.toSql(type)); + } + map['action_at'] = Variable(actionAt); + return map; + } + + GroupHistoriesCompanion toCompanion(bool nullToAbsent) { + return GroupHistoriesCompanion( + groupHistoryId: Value(groupHistoryId), + groupId: Value(groupId), + affectedContactId: affectedContactId == null && nullToAbsent + ? const Value.absent() + : Value(affectedContactId), + oldGroupName: oldGroupName == null && nullToAbsent + ? const Value.absent() + : Value(oldGroupName), + newGroupName: newGroupName == null && nullToAbsent + ? const Value.absent() + : Value(newGroupName), + type: Value(type), + actionAt: Value(actionAt), + ); + } + + factory GroupHistory.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return GroupHistory( + groupHistoryId: serializer.fromJson(json['groupHistoryId']), + groupId: serializer.fromJson(json['groupId']), + affectedContactId: serializer.fromJson(json['affectedContactId']), + oldGroupName: serializer.fromJson(json['oldGroupName']), + newGroupName: serializer.fromJson(json['newGroupName']), + type: $GroupHistoriesTable.$convertertype + .fromJson(serializer.fromJson(json['type'])), + actionAt: serializer.fromJson(json['actionAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'groupHistoryId': serializer.toJson(groupHistoryId), + 'groupId': serializer.toJson(groupId), + 'affectedContactId': serializer.toJson(affectedContactId), + 'oldGroupName': serializer.toJson(oldGroupName), + 'newGroupName': serializer.toJson(newGroupName), + 'type': serializer + .toJson($GroupHistoriesTable.$convertertype.toJson(type)), + 'actionAt': serializer.toJson(actionAt), + }; + } + + GroupHistory copyWith( + {String? groupHistoryId, + String? groupId, + Value affectedContactId = const Value.absent(), + Value oldGroupName = const Value.absent(), + Value newGroupName = const Value.absent(), + GroupActionType? type, + DateTime? actionAt}) => + GroupHistory( + groupHistoryId: groupHistoryId ?? this.groupHistoryId, + groupId: groupId ?? this.groupId, + affectedContactId: affectedContactId.present + ? affectedContactId.value + : this.affectedContactId, + oldGroupName: + oldGroupName.present ? oldGroupName.value : this.oldGroupName, + newGroupName: + newGroupName.present ? newGroupName.value : this.newGroupName, + type: type ?? this.type, + actionAt: actionAt ?? this.actionAt, + ); + GroupHistory copyWithCompanion(GroupHistoriesCompanion data) { + return GroupHistory( + groupHistoryId: data.groupHistoryId.present + ? data.groupHistoryId.value + : this.groupHistoryId, + groupId: data.groupId.present ? data.groupId.value : this.groupId, + affectedContactId: data.affectedContactId.present + ? data.affectedContactId.value + : this.affectedContactId, + oldGroupName: data.oldGroupName.present + ? data.oldGroupName.value + : this.oldGroupName, + newGroupName: data.newGroupName.present + ? data.newGroupName.value + : this.newGroupName, + type: data.type.present ? data.type.value : this.type, + actionAt: data.actionAt.present ? data.actionAt.value : this.actionAt, + ); + } + + @override + String toString() { + return (StringBuffer('GroupHistory(') + ..write('groupHistoryId: $groupHistoryId, ') + ..write('groupId: $groupId, ') + ..write('affectedContactId: $affectedContactId, ') + ..write('oldGroupName: $oldGroupName, ') + ..write('newGroupName: $newGroupName, ') + ..write('type: $type, ') + ..write('actionAt: $actionAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(groupHistoryId, groupId, affectedContactId, + oldGroupName, newGroupName, type, actionAt); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is GroupHistory && + other.groupHistoryId == this.groupHistoryId && + other.groupId == this.groupId && + other.affectedContactId == this.affectedContactId && + other.oldGroupName == this.oldGroupName && + other.newGroupName == this.newGroupName && + other.type == this.type && + other.actionAt == this.actionAt); +} + +class GroupHistoriesCompanion extends UpdateCompanion { + final Value groupHistoryId; + final Value groupId; + final Value affectedContactId; + final Value oldGroupName; + final Value newGroupName; + final Value type; + final Value actionAt; + final Value rowid; + const GroupHistoriesCompanion({ + this.groupHistoryId = const Value.absent(), + this.groupId = const Value.absent(), + this.affectedContactId = const Value.absent(), + this.oldGroupName = const Value.absent(), + this.newGroupName = const Value.absent(), + this.type = const Value.absent(), + this.actionAt = const Value.absent(), + this.rowid = const Value.absent(), + }); + GroupHistoriesCompanion.insert({ + required String groupHistoryId, + required String groupId, + this.affectedContactId = const Value.absent(), + this.oldGroupName = const Value.absent(), + this.newGroupName = const Value.absent(), + required GroupActionType type, + this.actionAt = const Value.absent(), + this.rowid = const Value.absent(), + }) : groupHistoryId = Value(groupHistoryId), + groupId = Value(groupId), + type = Value(type); + static Insertable custom({ + Expression? groupHistoryId, + Expression? groupId, + Expression? affectedContactId, + Expression? oldGroupName, + Expression? newGroupName, + Expression? type, + Expression? actionAt, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (groupHistoryId != null) 'group_history_id': groupHistoryId, + if (groupId != null) 'group_id': groupId, + if (affectedContactId != null) 'affected_contact_id': affectedContactId, + if (oldGroupName != null) 'old_group_name': oldGroupName, + if (newGroupName != null) 'new_group_name': newGroupName, + if (type != null) 'type': type, + if (actionAt != null) 'action_at': actionAt, + if (rowid != null) 'rowid': rowid, + }); + } + + GroupHistoriesCompanion copyWith( + {Value? groupHistoryId, + Value? groupId, + Value? affectedContactId, + Value? oldGroupName, + Value? newGroupName, + Value? type, + Value? actionAt, + Value? rowid}) { + return GroupHistoriesCompanion( + groupHistoryId: groupHistoryId ?? this.groupHistoryId, + groupId: groupId ?? this.groupId, + affectedContactId: affectedContactId ?? this.affectedContactId, + oldGroupName: oldGroupName ?? this.oldGroupName, + newGroupName: newGroupName ?? this.newGroupName, + type: type ?? this.type, + actionAt: actionAt ?? this.actionAt, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (groupHistoryId.present) { + map['group_history_id'] = Variable(groupHistoryId.value); + } + if (groupId.present) { + map['group_id'] = Variable(groupId.value); + } + if (affectedContactId.present) { + map['affected_contact_id'] = Variable(affectedContactId.value); + } + if (oldGroupName.present) { + map['old_group_name'] = Variable(oldGroupName.value); + } + if (newGroupName.present) { + map['new_group_name'] = Variable(newGroupName.value); + } + if (type.present) { + map['type'] = Variable( + $GroupHistoriesTable.$convertertype.toSql(type.value)); + } + if (actionAt.present) { + map['action_at'] = Variable(actionAt.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('GroupHistoriesCompanion(') + ..write('groupHistoryId: $groupHistoryId, ') + ..write('groupId: $groupId, ') + ..write('affectedContactId: $affectedContactId, ') + ..write('oldGroupName: $oldGroupName, ') + ..write('newGroupName: $newGroupName, ') + ..write('type: $type, ') + ..write('actionAt: $actionAt, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + abstract class _$TwonlyDB extends GeneratedDatabase { _$TwonlyDB(QueryExecutor e) : super(e); $TwonlyDBManager get managers => $TwonlyDBManager(this); @@ -6617,6 +7310,7 @@ abstract class _$TwonlyDB extends GeneratedDatabase { late final $SignalContactSignedPreKeysTable signalContactSignedPreKeys = $SignalContactSignedPreKeysTable(this); late final $MessageActionsTable messageActions = $MessageActionsTable(this); + late final $GroupHistoriesTable groupHistories = $GroupHistoriesTable(this); late final MessagesDao messagesDao = MessagesDao(this as TwonlyDB); late final ContactsDao contactsDao = ContactsDao(this as TwonlyDB); late final SignalDao signalDao = SignalDao(this as TwonlyDB); @@ -6644,7 +7338,8 @@ abstract class _$TwonlyDB extends GeneratedDatabase { signalSessionStores, signalContactPreKeys, signalContactSignedPreKeys, - messageActions + messageActions, + groupHistories ]; @override StreamQueryUpdateRules get streamUpdateRules => const StreamQueryUpdateRules( @@ -6684,6 +7379,13 @@ abstract class _$TwonlyDB extends GeneratedDatabase { TableUpdate('reactions', kind: UpdateKind.delete), ], ), + WritePropagation( + on: TableUpdateQuery.onTableName('groups', + limitUpdateKind: UpdateKind.delete), + result: [ + TableUpdate('group_members', kind: UpdateKind.delete), + ], + ), WritePropagation( on: TableUpdateQuery.onTableName('contacts', limitUpdateKind: UpdateKind.delete), @@ -6720,6 +7422,13 @@ abstract class _$TwonlyDB extends GeneratedDatabase { TableUpdate('message_actions', kind: UpdateKind.delete), ], ), + WritePropagation( + on: TableUpdateQuery.onTableName('groups', + limitUpdateKind: UpdateKind.delete), + result: [ + TableUpdate('group_histories', kind: UpdateKind.delete), + ], + ), ], ); } @@ -6859,6 +7568,22 @@ final class $$ContactsTableReferences return ProcessedTableManager( manager.$state.copyWith(prefetchedData: cache)); } + + static MultiTypedResultKey<$GroupHistoriesTable, List> + _groupHistoriesRefsTable(_$TwonlyDB db) => + MultiTypedResultKey.fromTable(db.groupHistories, + aliasName: $_aliasNameGenerator( + db.contacts.userId, db.groupHistories.affectedContactId)); + + $$GroupHistoriesTableProcessedTableManager get groupHistoriesRefs { + final manager = $$GroupHistoriesTableTableManager($_db, $_db.groupHistories) + .filter((f) => f.affectedContactId.userId + .sqlEquals($_itemColumn('user_id')!)); + + final cache = $_typedResult.readTableOrNull(_groupHistoriesRefsTable($_db)); + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: cache)); + } } class $$ContactsTableFilterComposer @@ -7041,6 +7766,27 @@ class $$ContactsTableFilterComposer )); return f(composer); } + + Expression groupHistoriesRefs( + Expression Function($$GroupHistoriesTableFilterComposer f) f) { + final $$GroupHistoriesTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.userId, + referencedTable: $db.groupHistories, + getReferencedColumn: (t) => t.affectedContactId, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$GroupHistoriesTableFilterComposer( + $db: $db, + $table: $db.groupHistories, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return f(composer); + } } class $$ContactsTableOrderingComposer @@ -7274,6 +8020,27 @@ class $$ContactsTableAnnotationComposer )); return f(composer); } + + Expression groupHistoriesRefs( + Expression Function($$GroupHistoriesTableAnnotationComposer a) f) { + final $$GroupHistoriesTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.userId, + referencedTable: $db.groupHistories, + getReferencedColumn: (t) => t.affectedContactId, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$GroupHistoriesTableAnnotationComposer( + $db: $db, + $table: $db.groupHistories, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return f(composer); + } } class $$ContactsTableTableManager extends RootTableManager< @@ -7293,7 +8060,8 @@ class $$ContactsTableTableManager extends RootTableManager< bool groupMembersRefs, bool receiptsRefs, bool signalContactPreKeysRefs, - bool signalContactSignedPreKeysRefs})> { + bool signalContactSignedPreKeysRefs, + bool groupHistoriesRefs})> { $$ContactsTableTableManager(_$TwonlyDB db, $ContactsTable table) : super(TableManagerState( db: db, @@ -7374,7 +8142,8 @@ class $$ContactsTableTableManager extends RootTableManager< groupMembersRefs = false, receiptsRefs = false, signalContactPreKeysRefs = false, - signalContactSignedPreKeysRefs = false}) { + signalContactSignedPreKeysRefs = false, + groupHistoriesRefs = false}) { return PrefetchHooks( db: db, explicitlyWatchedTables: [ @@ -7384,7 +8153,8 @@ class $$ContactsTableTableManager extends RootTableManager< if (receiptsRefs) db.receipts, if (signalContactPreKeysRefs) db.signalContactPreKeys, if (signalContactSignedPreKeysRefs) - db.signalContactSignedPreKeys + db.signalContactSignedPreKeys, + if (groupHistoriesRefs) db.groupHistories ], addJoins: null, getPrefetchedDataCallback: (items) async { @@ -7464,6 +8234,19 @@ class $$ContactsTableTableManager extends RootTableManager< referencedItemsForCurrentItem: (item, referencedItems) => referencedItems .where((e) => e.contactId == item.userId), + typedResults: items), + if (groupHistoriesRefs) + await $_getPrefetchedData( + currentTable: table, + referencedTable: $$ContactsTableReferences + ._groupHistoriesRefsTable(db), + managerFromTypedResult: (p0) => + $$ContactsTableReferences(db, table, p0) + .groupHistoriesRefs, + referencedItemsForCurrentItem: + (item, referencedItems) => referencedItems.where( + (e) => e.affectedContactId == item.userId), typedResults: items) ]; }, @@ -7489,13 +8272,19 @@ typedef $$ContactsTableProcessedTableManager = ProcessedTableManager< bool groupMembersRefs, bool receiptsRefs, bool signalContactPreKeysRefs, - bool signalContactSignedPreKeysRefs})>; + bool signalContactSignedPreKeysRefs, + bool groupHistoriesRefs})>; typedef $$GroupsTableCreateCompanionBuilder = GroupsCompanion Function({ required String groupId, - required bool isGroupAdmin, - required bool isDirectChat, + Value isGroupAdmin, + Value isDirectChat, Value pinned, Value archived, + Value joinedGroup, + Value leftGroup, + Value stateVersionId, + Value stateEncryptionKey, + Value myGroupPrivateKey, required String groupName, Value totalMediaCounter, Value alsoBestFriend, @@ -7517,6 +8306,11 @@ typedef $$GroupsTableUpdateCompanionBuilder = GroupsCompanion Function({ Value isDirectChat, Value pinned, Value archived, + Value joinedGroup, + Value leftGroup, + Value stateVersionId, + Value stateEncryptionKey, + Value myGroupPrivateKey, Value groupName, Value totalMediaCounter, Value alsoBestFriend, @@ -7551,6 +8345,38 @@ final class $$GroupsTableReferences return ProcessedTableManager( manager.$state.copyWith(prefetchedData: cache)); } + + static MultiTypedResultKey<$GroupMembersTable, List> + _groupMembersRefsTable(_$TwonlyDB db) => MultiTypedResultKey.fromTable( + db.groupMembers, + aliasName: + $_aliasNameGenerator(db.groups.groupId, db.groupMembers.groupId)); + + $$GroupMembersTableProcessedTableManager get groupMembersRefs { + final manager = $$GroupMembersTableTableManager($_db, $_db.groupMembers) + .filter((f) => + f.groupId.groupId.sqlEquals($_itemColumn('group_id')!)); + + final cache = $_typedResult.readTableOrNull(_groupMembersRefsTable($_db)); + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: cache)); + } + + static MultiTypedResultKey<$GroupHistoriesTable, List> + _groupHistoriesRefsTable(_$TwonlyDB db) => + MultiTypedResultKey.fromTable(db.groupHistories, + aliasName: $_aliasNameGenerator( + db.groups.groupId, db.groupHistories.groupId)); + + $$GroupHistoriesTableProcessedTableManager get groupHistoriesRefs { + final manager = $$GroupHistoriesTableTableManager($_db, $_db.groupHistories) + .filter((f) => + f.groupId.groupId.sqlEquals($_itemColumn('group_id')!)); + + final cache = $_typedResult.readTableOrNull(_groupHistoriesRefsTable($_db)); + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: cache)); + } } class $$GroupsTableFilterComposer extends Composer<_$TwonlyDB, $GroupsTable> { @@ -7576,6 +8402,24 @@ class $$GroupsTableFilterComposer extends Composer<_$TwonlyDB, $GroupsTable> { ColumnFilters get archived => $composableBuilder( column: $table.archived, builder: (column) => ColumnFilters(column)); + ColumnFilters get joinedGroup => $composableBuilder( + column: $table.joinedGroup, builder: (column) => ColumnFilters(column)); + + ColumnFilters get leftGroup => $composableBuilder( + column: $table.leftGroup, builder: (column) => ColumnFilters(column)); + + ColumnFilters get stateVersionId => $composableBuilder( + column: $table.stateVersionId, + builder: (column) => ColumnFilters(column)); + + ColumnFilters get stateEncryptionKey => $composableBuilder( + column: $table.stateEncryptionKey, + builder: (column) => ColumnFilters(column)); + + ColumnFilters get myGroupPrivateKey => $composableBuilder( + column: $table.myGroupPrivateKey, + builder: (column) => ColumnFilters(column)); + ColumnFilters get groupName => $composableBuilder( column: $table.groupName, builder: (column) => ColumnFilters(column)); @@ -7644,6 +8488,48 @@ class $$GroupsTableFilterComposer extends Composer<_$TwonlyDB, $GroupsTable> { )); return f(composer); } + + Expression groupMembersRefs( + Expression Function($$GroupMembersTableFilterComposer f) f) { + final $$GroupMembersTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.groupId, + referencedTable: $db.groupMembers, + getReferencedColumn: (t) => t.groupId, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$GroupMembersTableFilterComposer( + $db: $db, + $table: $db.groupMembers, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return f(composer); + } + + Expression groupHistoriesRefs( + Expression Function($$GroupHistoriesTableFilterComposer f) f) { + final $$GroupHistoriesTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.groupId, + referencedTable: $db.groupHistories, + getReferencedColumn: (t) => t.groupId, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$GroupHistoriesTableFilterComposer( + $db: $db, + $table: $db.groupHistories, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return f(composer); + } } class $$GroupsTableOrderingComposer extends Composer<_$TwonlyDB, $GroupsTable> { @@ -7671,6 +8557,24 @@ class $$GroupsTableOrderingComposer extends Composer<_$TwonlyDB, $GroupsTable> { ColumnOrderings get archived => $composableBuilder( column: $table.archived, builder: (column) => ColumnOrderings(column)); + ColumnOrderings get joinedGroup => $composableBuilder( + column: $table.joinedGroup, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get leftGroup => $composableBuilder( + column: $table.leftGroup, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get stateVersionId => $composableBuilder( + column: $table.stateVersionId, + builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get stateEncryptionKey => $composableBuilder( + column: $table.stateEncryptionKey, + builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get myGroupPrivateKey => $composableBuilder( + column: $table.myGroupPrivateKey, + builder: (column) => ColumnOrderings(column)); + ColumnOrderings get groupName => $composableBuilder( column: $table.groupName, builder: (column) => ColumnOrderings(column)); @@ -7747,6 +8651,21 @@ class $$GroupsTableAnnotationComposer GeneratedColumn get archived => $composableBuilder(column: $table.archived, builder: (column) => column); + GeneratedColumn get joinedGroup => $composableBuilder( + column: $table.joinedGroup, builder: (column) => column); + + GeneratedColumn get leftGroup => + $composableBuilder(column: $table.leftGroup, builder: (column) => column); + + GeneratedColumn get stateVersionId => $composableBuilder( + column: $table.stateVersionId, builder: (column) => column); + + GeneratedColumn get stateEncryptionKey => $composableBuilder( + column: $table.stateEncryptionKey, builder: (column) => column); + + GeneratedColumn get myGroupPrivateKey => $composableBuilder( + column: $table.myGroupPrivateKey, builder: (column) => column); + GeneratedColumn get groupName => $composableBuilder(column: $table.groupName, builder: (column) => column); @@ -7808,6 +8727,48 @@ class $$GroupsTableAnnotationComposer )); return f(composer); } + + Expression groupMembersRefs( + Expression Function($$GroupMembersTableAnnotationComposer a) f) { + final $$GroupMembersTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.groupId, + referencedTable: $db.groupMembers, + getReferencedColumn: (t) => t.groupId, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$GroupMembersTableAnnotationComposer( + $db: $db, + $table: $db.groupMembers, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return f(composer); + } + + Expression groupHistoriesRefs( + Expression Function($$GroupHistoriesTableAnnotationComposer a) f) { + final $$GroupHistoriesTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.groupId, + referencedTable: $db.groupHistories, + getReferencedColumn: (t) => t.groupId, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$GroupHistoriesTableAnnotationComposer( + $db: $db, + $table: $db.groupHistories, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return f(composer); + } } class $$GroupsTableTableManager extends RootTableManager< @@ -7821,7 +8782,8 @@ class $$GroupsTableTableManager extends RootTableManager< $$GroupsTableUpdateCompanionBuilder, (Group, $$GroupsTableReferences), Group, - PrefetchHooks Function({bool messagesRefs})> { + PrefetchHooks Function( + {bool messagesRefs, bool groupMembersRefs, bool groupHistoriesRefs})> { $$GroupsTableTableManager(_$TwonlyDB db, $GroupsTable table) : super(TableManagerState( db: db, @@ -7838,6 +8800,11 @@ class $$GroupsTableTableManager extends RootTableManager< Value isDirectChat = const Value.absent(), Value pinned = const Value.absent(), Value archived = const Value.absent(), + Value joinedGroup = const Value.absent(), + Value leftGroup = const Value.absent(), + Value stateVersionId = const Value.absent(), + Value stateEncryptionKey = const Value.absent(), + Value myGroupPrivateKey = const Value.absent(), Value groupName = const Value.absent(), Value totalMediaCounter = const Value.absent(), Value alsoBestFriend = const Value.absent(), @@ -7859,6 +8826,11 @@ class $$GroupsTableTableManager extends RootTableManager< isDirectChat: isDirectChat, pinned: pinned, archived: archived, + joinedGroup: joinedGroup, + leftGroup: leftGroup, + stateVersionId: stateVersionId, + stateEncryptionKey: stateEncryptionKey, + myGroupPrivateKey: myGroupPrivateKey, groupName: groupName, totalMediaCounter: totalMediaCounter, alsoBestFriend: alsoBestFriend, @@ -7876,10 +8848,15 @@ class $$GroupsTableTableManager extends RootTableManager< ), createCompanionCallback: ({ required String groupId, - required bool isGroupAdmin, - required bool isDirectChat, + Value isGroupAdmin = const Value.absent(), + Value isDirectChat = const Value.absent(), Value pinned = const Value.absent(), Value archived = const Value.absent(), + Value joinedGroup = const Value.absent(), + Value leftGroup = const Value.absent(), + Value stateVersionId = const Value.absent(), + Value stateEncryptionKey = const Value.absent(), + Value myGroupPrivateKey = const Value.absent(), required String groupName, Value totalMediaCounter = const Value.absent(), Value alsoBestFriend = const Value.absent(), @@ -7901,6 +8878,11 @@ class $$GroupsTableTableManager extends RootTableManager< isDirectChat: isDirectChat, pinned: pinned, archived: archived, + joinedGroup: joinedGroup, + leftGroup: leftGroup, + stateVersionId: stateVersionId, + stateEncryptionKey: stateEncryptionKey, + myGroupPrivateKey: myGroupPrivateKey, groupName: groupName, totalMediaCounter: totalMediaCounter, alsoBestFriend: alsoBestFriend, @@ -7920,10 +8902,17 @@ class $$GroupsTableTableManager extends RootTableManager< .map((e) => (e.readTable(table), $$GroupsTableReferences(db, table, e))) .toList(), - prefetchHooksCallback: ({messagesRefs = false}) { + prefetchHooksCallback: ( + {messagesRefs = false, + groupMembersRefs = false, + groupHistoriesRefs = false}) { return PrefetchHooks( db: db, - explicitlyWatchedTables: [if (messagesRefs) db.messages], + explicitlyWatchedTables: [ + if (messagesRefs) db.messages, + if (groupMembersRefs) db.groupMembers, + if (groupHistoriesRefs) db.groupHistories + ], addJoins: null, getPrefetchedDataCallback: (items) async { return [ @@ -7937,6 +8926,31 @@ class $$GroupsTableTableManager extends RootTableManager< referencedItemsForCurrentItem: (item, referencedItems) => referencedItems .where((e) => e.groupId == item.groupId), + typedResults: items), + if (groupMembersRefs) + await $_getPrefetchedData( + currentTable: table, + referencedTable: + $$GroupsTableReferences._groupMembersRefsTable(db), + managerFromTypedResult: (p0) => + $$GroupsTableReferences(db, table, p0) + .groupMembersRefs, + referencedItemsForCurrentItem: + (item, referencedItems) => referencedItems + .where((e) => e.groupId == item.groupId), + typedResults: items), + if (groupHistoriesRefs) + await $_getPrefetchedData( + currentTable: table, + referencedTable: $$GroupsTableReferences + ._groupHistoriesRefsTable(db), + managerFromTypedResult: (p0) => + $$GroupsTableReferences(db, table, p0) + .groupHistoriesRefs, + referencedItemsForCurrentItem: + (item, referencedItems) => referencedItems + .where((e) => e.groupId == item.groupId), typedResults: items) ]; }, @@ -7956,7 +8970,8 @@ typedef $$GroupsTableProcessedTableManager = ProcessedTableManager< $$GroupsTableUpdateCompanionBuilder, (Group, $$GroupsTableReferences), Group, - PrefetchHooks Function({bool messagesRefs})>; + PrefetchHooks Function( + {bool messagesRefs, bool groupMembersRefs, bool groupHistoriesRefs})>; typedef $$MediaFilesTableCreateCompanionBuilder = MediaFilesCompanion Function({ required String mediaId, required MediaType type, @@ -9887,6 +10902,7 @@ typedef $$GroupMembersTableCreateCompanionBuilder = GroupMembersCompanion required String groupId, required int contactId, Value memberState, + Value groupPublicKey, Value createdAt, Value rowid, }); @@ -9895,6 +10911,7 @@ typedef $$GroupMembersTableUpdateCompanionBuilder = GroupMembersCompanion Value groupId, Value contactId, Value memberState, + Value groupPublicKey, Value createdAt, Value rowid, }); @@ -9903,6 +10920,20 @@ final class $$GroupMembersTableReferences extends BaseReferences<_$TwonlyDB, $GroupMembersTable, GroupMember> { $$GroupMembersTableReferences(super.$_db, super.$_table, super.$_typedResult); + static $GroupsTable _groupIdTable(_$TwonlyDB db) => db.groups.createAlias( + $_aliasNameGenerator(db.groupMembers.groupId, db.groups.groupId)); + + $$GroupsTableProcessedTableManager get groupId { + final $_column = $_itemColumn('group_id')!; + + final manager = $$GroupsTableTableManager($_db, $_db.groups) + .filter((f) => f.groupId.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_groupIdTable($_db)); + if (item == null) return manager; + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item])); + } + static $ContactsTable _contactIdTable(_$TwonlyDB db) => db.contacts.createAlias( $_aliasNameGenerator(db.groupMembers.contactId, db.contacts.userId)); @@ -9928,17 +10959,38 @@ class $$GroupMembersTableFilterComposer super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - ColumnFilters get groupId => $composableBuilder( - column: $table.groupId, builder: (column) => ColumnFilters(column)); - ColumnWithTypeConverterFilters get memberState => $composableBuilder( column: $table.memberState, builder: (column) => ColumnWithTypeConverterFilters(column)); + ColumnFilters get groupPublicKey => $composableBuilder( + column: $table.groupPublicKey, + builder: (column) => ColumnFilters(column)); + ColumnFilters get createdAt => $composableBuilder( column: $table.createdAt, builder: (column) => ColumnFilters(column)); + $$GroupsTableFilterComposer get groupId { + final $$GroupsTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.groupId, + referencedTable: $db.groups, + getReferencedColumn: (t) => t.groupId, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$GroupsTableFilterComposer( + $db: $db, + $table: $db.groups, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } + $$ContactsTableFilterComposer get contactId { final $$ContactsTableFilterComposer composer = $composerBuilder( composer: this, @@ -9969,15 +11021,36 @@ class $$GroupMembersTableOrderingComposer super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - ColumnOrderings get groupId => $composableBuilder( - column: $table.groupId, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get memberState => $composableBuilder( column: $table.memberState, builder: (column) => ColumnOrderings(column)); + ColumnOrderings get groupPublicKey => $composableBuilder( + column: $table.groupPublicKey, + builder: (column) => ColumnOrderings(column)); + ColumnOrderings get createdAt => $composableBuilder( column: $table.createdAt, builder: (column) => ColumnOrderings(column)); + $$GroupsTableOrderingComposer get groupId { + final $$GroupsTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.groupId, + referencedTable: $db.groups, + getReferencedColumn: (t) => t.groupId, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$GroupsTableOrderingComposer( + $db: $db, + $table: $db.groups, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } + $$ContactsTableOrderingComposer get contactId { final $$ContactsTableOrderingComposer composer = $composerBuilder( composer: this, @@ -10008,16 +11081,36 @@ class $$GroupMembersTableAnnotationComposer super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - GeneratedColumn get groupId => - $composableBuilder(column: $table.groupId, builder: (column) => column); - GeneratedColumnWithTypeConverter get memberState => $composableBuilder( column: $table.memberState, builder: (column) => column); + GeneratedColumn get groupPublicKey => $composableBuilder( + column: $table.groupPublicKey, builder: (column) => column); + GeneratedColumn get createdAt => $composableBuilder(column: $table.createdAt, builder: (column) => column); + $$GroupsTableAnnotationComposer get groupId { + final $$GroupsTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.groupId, + referencedTable: $db.groups, + getReferencedColumn: (t) => t.groupId, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$GroupsTableAnnotationComposer( + $db: $db, + $table: $db.groups, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } + $$ContactsTableAnnotationComposer get contactId { final $$ContactsTableAnnotationComposer composer = $composerBuilder( composer: this, @@ -10050,7 +11143,7 @@ class $$GroupMembersTableTableManager extends RootTableManager< $$GroupMembersTableUpdateCompanionBuilder, (GroupMember, $$GroupMembersTableReferences), GroupMember, - PrefetchHooks Function({bool contactId})> { + PrefetchHooks Function({bool groupId, bool contactId})> { $$GroupMembersTableTableManager(_$TwonlyDB db, $GroupMembersTable table) : super(TableManagerState( db: db, @@ -10065,6 +11158,7 @@ class $$GroupMembersTableTableManager extends RootTableManager< Value groupId = const Value.absent(), Value contactId = const Value.absent(), Value memberState = const Value.absent(), + Value groupPublicKey = const Value.absent(), Value createdAt = const Value.absent(), Value rowid = const Value.absent(), }) => @@ -10072,6 +11166,7 @@ class $$GroupMembersTableTableManager extends RootTableManager< groupId: groupId, contactId: contactId, memberState: memberState, + groupPublicKey: groupPublicKey, createdAt: createdAt, rowid: rowid, ), @@ -10079,6 +11174,7 @@ class $$GroupMembersTableTableManager extends RootTableManager< required String groupId, required int contactId, Value memberState = const Value.absent(), + Value groupPublicKey = const Value.absent(), Value createdAt = const Value.absent(), Value rowid = const Value.absent(), }) => @@ -10086,6 +11182,7 @@ class $$GroupMembersTableTableManager extends RootTableManager< groupId: groupId, contactId: contactId, memberState: memberState, + groupPublicKey: groupPublicKey, createdAt: createdAt, rowid: rowid, ), @@ -10095,7 +11192,7 @@ class $$GroupMembersTableTableManager extends RootTableManager< $$GroupMembersTableReferences(db, table, e) )) .toList(), - prefetchHooksCallback: ({contactId = false}) { + prefetchHooksCallback: ({groupId = false, contactId = false}) { return PrefetchHooks( db: db, explicitlyWatchedTables: [], @@ -10112,6 +11209,16 @@ class $$GroupMembersTableTableManager extends RootTableManager< dynamic, dynamic, dynamic>>(state) { + if (groupId) { + state = state.withJoin( + currentTable: table, + currentColumn: table.groupId, + referencedTable: + $$GroupMembersTableReferences._groupIdTable(db), + referencedColumn: + $$GroupMembersTableReferences._groupIdTable(db).groupId, + ) as T; + } if (contactId) { state = state.withJoin( currentTable: table, @@ -10145,7 +11252,7 @@ typedef $$GroupMembersTableProcessedTableManager = ProcessedTableManager< $$GroupMembersTableUpdateCompanionBuilder, (GroupMember, $$GroupMembersTableReferences), GroupMember, - PrefetchHooks Function({bool contactId})>; + PrefetchHooks Function({bool groupId, bool contactId})>; typedef $$ReceiptsTableCreateCompanionBuilder = ReceiptsCompanion Function({ required String receiptId, required int contactId, @@ -12102,6 +13209,396 @@ typedef $$MessageActionsTableProcessedTableManager = ProcessedTableManager< (MessageAction, $$MessageActionsTableReferences), MessageAction, PrefetchHooks Function({bool messageId})>; +typedef $$GroupHistoriesTableCreateCompanionBuilder = GroupHistoriesCompanion + Function({ + required String groupHistoryId, + required String groupId, + Value affectedContactId, + Value oldGroupName, + Value newGroupName, + required GroupActionType type, + Value actionAt, + Value rowid, +}); +typedef $$GroupHistoriesTableUpdateCompanionBuilder = GroupHistoriesCompanion + Function({ + Value groupHistoryId, + Value groupId, + Value affectedContactId, + Value oldGroupName, + Value newGroupName, + Value type, + Value actionAt, + Value rowid, +}); + +final class $$GroupHistoriesTableReferences + extends BaseReferences<_$TwonlyDB, $GroupHistoriesTable, GroupHistory> { + $$GroupHistoriesTableReferences( + super.$_db, super.$_table, super.$_typedResult); + + static $GroupsTable _groupIdTable(_$TwonlyDB db) => db.groups.createAlias( + $_aliasNameGenerator(db.groupHistories.groupId, db.groups.groupId)); + + $$GroupsTableProcessedTableManager get groupId { + final $_column = $_itemColumn('group_id')!; + + final manager = $$GroupsTableTableManager($_db, $_db.groups) + .filter((f) => f.groupId.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_groupIdTable($_db)); + if (item == null) return manager; + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item])); + } + + static $ContactsTable _affectedContactIdTable(_$TwonlyDB db) => + db.contacts.createAlias($_aliasNameGenerator( + db.groupHistories.affectedContactId, db.contacts.userId)); + + $$ContactsTableProcessedTableManager? get affectedContactId { + final $_column = $_itemColumn('affected_contact_id'); + if ($_column == null) return null; + final manager = $$ContactsTableTableManager($_db, $_db.contacts) + .filter((f) => f.userId.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_affectedContactIdTable($_db)); + if (item == null) return manager; + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item])); + } +} + +class $$GroupHistoriesTableFilterComposer + extends Composer<_$TwonlyDB, $GroupHistoriesTable> { + $$GroupHistoriesTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnFilters get groupHistoryId => $composableBuilder( + column: $table.groupHistoryId, + builder: (column) => ColumnFilters(column)); + + ColumnFilters get oldGroupName => $composableBuilder( + column: $table.oldGroupName, builder: (column) => ColumnFilters(column)); + + ColumnFilters get newGroupName => $composableBuilder( + column: $table.newGroupName, builder: (column) => ColumnFilters(column)); + + ColumnWithTypeConverterFilters + get type => $composableBuilder( + column: $table.type, + builder: (column) => ColumnWithTypeConverterFilters(column)); + + ColumnFilters get actionAt => $composableBuilder( + column: $table.actionAt, builder: (column) => ColumnFilters(column)); + + $$GroupsTableFilterComposer get groupId { + final $$GroupsTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.groupId, + referencedTable: $db.groups, + getReferencedColumn: (t) => t.groupId, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$GroupsTableFilterComposer( + $db: $db, + $table: $db.groups, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } + + $$ContactsTableFilterComposer get affectedContactId { + final $$ContactsTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.affectedContactId, + referencedTable: $db.contacts, + getReferencedColumn: (t) => t.userId, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$ContactsTableFilterComposer( + $db: $db, + $table: $db.contacts, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } +} + +class $$GroupHistoriesTableOrderingComposer + extends Composer<_$TwonlyDB, $GroupHistoriesTable> { + $$GroupHistoriesTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnOrderings get groupHistoryId => $composableBuilder( + column: $table.groupHistoryId, + builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get oldGroupName => $composableBuilder( + column: $table.oldGroupName, + builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get newGroupName => $composableBuilder( + column: $table.newGroupName, + builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get type => $composableBuilder( + column: $table.type, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get actionAt => $composableBuilder( + column: $table.actionAt, builder: (column) => ColumnOrderings(column)); + + $$GroupsTableOrderingComposer get groupId { + final $$GroupsTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.groupId, + referencedTable: $db.groups, + getReferencedColumn: (t) => t.groupId, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$GroupsTableOrderingComposer( + $db: $db, + $table: $db.groups, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } + + $$ContactsTableOrderingComposer get affectedContactId { + final $$ContactsTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.affectedContactId, + referencedTable: $db.contacts, + getReferencedColumn: (t) => t.userId, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$ContactsTableOrderingComposer( + $db: $db, + $table: $db.contacts, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } +} + +class $$GroupHistoriesTableAnnotationComposer + extends Composer<_$TwonlyDB, $GroupHistoriesTable> { + $$GroupHistoriesTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + GeneratedColumn get groupHistoryId => $composableBuilder( + column: $table.groupHistoryId, builder: (column) => column); + + GeneratedColumn get oldGroupName => $composableBuilder( + column: $table.oldGroupName, builder: (column) => column); + + GeneratedColumn get newGroupName => $composableBuilder( + column: $table.newGroupName, builder: (column) => column); + + GeneratedColumnWithTypeConverter get type => + $composableBuilder(column: $table.type, builder: (column) => column); + + GeneratedColumn get actionAt => + $composableBuilder(column: $table.actionAt, builder: (column) => column); + + $$GroupsTableAnnotationComposer get groupId { + final $$GroupsTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.groupId, + referencedTable: $db.groups, + getReferencedColumn: (t) => t.groupId, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$GroupsTableAnnotationComposer( + $db: $db, + $table: $db.groups, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } + + $$ContactsTableAnnotationComposer get affectedContactId { + final $$ContactsTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.affectedContactId, + referencedTable: $db.contacts, + getReferencedColumn: (t) => t.userId, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$ContactsTableAnnotationComposer( + $db: $db, + $table: $db.contacts, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } +} + +class $$GroupHistoriesTableTableManager extends RootTableManager< + _$TwonlyDB, + $GroupHistoriesTable, + GroupHistory, + $$GroupHistoriesTableFilterComposer, + $$GroupHistoriesTableOrderingComposer, + $$GroupHistoriesTableAnnotationComposer, + $$GroupHistoriesTableCreateCompanionBuilder, + $$GroupHistoriesTableUpdateCompanionBuilder, + (GroupHistory, $$GroupHistoriesTableReferences), + GroupHistory, + PrefetchHooks Function({bool groupId, bool affectedContactId})> { + $$GroupHistoriesTableTableManager(_$TwonlyDB db, $GroupHistoriesTable table) + : super(TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + $$GroupHistoriesTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => + $$GroupHistoriesTableOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => + $$GroupHistoriesTableAnnotationComposer($db: db, $table: table), + updateCompanionCallback: ({ + Value groupHistoryId = const Value.absent(), + Value groupId = const Value.absent(), + Value affectedContactId = const Value.absent(), + Value oldGroupName = const Value.absent(), + Value newGroupName = const Value.absent(), + Value type = const Value.absent(), + Value actionAt = const Value.absent(), + Value rowid = const Value.absent(), + }) => + GroupHistoriesCompanion( + groupHistoryId: groupHistoryId, + groupId: groupId, + affectedContactId: affectedContactId, + oldGroupName: oldGroupName, + newGroupName: newGroupName, + type: type, + actionAt: actionAt, + rowid: rowid, + ), + createCompanionCallback: ({ + required String groupHistoryId, + required String groupId, + Value affectedContactId = const Value.absent(), + Value oldGroupName = const Value.absent(), + Value newGroupName = const Value.absent(), + required GroupActionType type, + Value actionAt = const Value.absent(), + Value rowid = const Value.absent(), + }) => + GroupHistoriesCompanion.insert( + groupHistoryId: groupHistoryId, + groupId: groupId, + affectedContactId: affectedContactId, + oldGroupName: oldGroupName, + newGroupName: newGroupName, + type: type, + actionAt: actionAt, + rowid: rowid, + ), + withReferenceMapper: (p0) => p0 + .map((e) => ( + e.readTable(table), + $$GroupHistoriesTableReferences(db, table, e) + )) + .toList(), + prefetchHooksCallback: ( + {groupId = false, affectedContactId = false}) { + return PrefetchHooks( + db: db, + explicitlyWatchedTables: [], + addJoins: < + T extends TableManagerState< + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic>>(state) { + if (groupId) { + state = state.withJoin( + currentTable: table, + currentColumn: table.groupId, + referencedTable: + $$GroupHistoriesTableReferences._groupIdTable(db), + referencedColumn: $$GroupHistoriesTableReferences + ._groupIdTable(db) + .groupId, + ) as T; + } + if (affectedContactId) { + state = state.withJoin( + currentTable: table, + currentColumn: table.affectedContactId, + referencedTable: $$GroupHistoriesTableReferences + ._affectedContactIdTable(db), + referencedColumn: $$GroupHistoriesTableReferences + ._affectedContactIdTable(db) + .userId, + ) as T; + } + + return state; + }, + getPrefetchedDataCallback: (items) async { + return []; + }, + ); + }, + )); +} + +typedef $$GroupHistoriesTableProcessedTableManager = ProcessedTableManager< + _$TwonlyDB, + $GroupHistoriesTable, + GroupHistory, + $$GroupHistoriesTableFilterComposer, + $$GroupHistoriesTableOrderingComposer, + $$GroupHistoriesTableAnnotationComposer, + $$GroupHistoriesTableCreateCompanionBuilder, + $$GroupHistoriesTableUpdateCompanionBuilder, + (GroupHistory, $$GroupHistoriesTableReferences), + GroupHistory, + PrefetchHooks Function({bool groupId, bool affectedContactId})>; class $TwonlyDBManager { final _$TwonlyDB _db; @@ -12141,4 +13638,6 @@ class $TwonlyDBManager { _db, _db.signalContactSignedPreKeys); $$MessageActionsTableTableManager get messageActions => $$MessageActionsTableTableManager(_db, _db.messageActions); + $$GroupHistoriesTableTableManager get groupHistories => + $$GroupHistoriesTableTableManager(_db, _db.groupHistories); } diff --git a/lib/src/localization/app_de.arb b/lib/src/localization/app_de.arb index 4e91f4c..3c84c47 100644 --- a/lib/src/localization/app_de.arb +++ b/lib/src/localization/app_de.arb @@ -359,5 +359,11 @@ "durationShortSecond": "Sek.", "durationShortMinute": "Min.", "durationShortHour": "Std", - "durationShortDays": "Tagen" + "durationShortDays": "Tagen", + "newGroup": "Neue Gruppe", + "selectMembers": "Mitglieder auswählen", + "selectGroupName": "Gruppennamen wählen", + "groupNameInput": "Gruppennamen", + "groupMembers": "Mitglieder", + "createGroup": "Gruppe erstellen" } \ No newline at end of file diff --git a/lib/src/localization/app_en.arb b/lib/src/localization/app_en.arb index 8b565d0..489cb1c 100644 --- a/lib/src/localization/app_en.arb +++ b/lib/src/localization/app_en.arb @@ -515,5 +515,11 @@ "durationShortSecond": "Sec.", "durationShortMinute": "Min.", "durationShortHour": "Hrs.", - "durationShortDays": "Days" + "durationShortDays": "Days", + "newGroup": "New group", + "selectMembers": "Select members", + "selectGroupName": "Select group name", + "groupNameInput": "Group name", + "groupMembers": "Members", + "createGroup": "Create group" } \ No newline at end of file diff --git a/lib/src/localization/generated/app_localizations.dart b/lib/src/localization/generated/app_localizations.dart index dab9c79..3466610 100644 --- a/lib/src/localization/generated/app_localizations.dart +++ b/lib/src/localization/generated/app_localizations.dart @@ -2197,6 +2197,42 @@ abstract class AppLocalizations { /// In en, this message translates to: /// **'Days'** String get durationShortDays; + + /// No description provided for @newGroup. + /// + /// In en, this message translates to: + /// **'New group'** + String get newGroup; + + /// No description provided for @selectMembers. + /// + /// In en, this message translates to: + /// **'Select members'** + String get selectMembers; + + /// No description provided for @selectGroupName. + /// + /// In en, this message translates to: + /// **'Select group name'** + String get selectGroupName; + + /// No description provided for @groupNameInput. + /// + /// In en, this message translates to: + /// **'Group name'** + String get groupNameInput; + + /// No description provided for @groupMembers. + /// + /// In en, this message translates to: + /// **'Members'** + String get groupMembers; + + /// No description provided for @createGroup. + /// + /// In en, this message translates to: + /// **'Create group'** + String get createGroup; } class _AppLocalizationsDelegate diff --git a/lib/src/localization/generated/app_localizations_de.dart b/lib/src/localization/generated/app_localizations_de.dart index f372df6..3d16a0d 100644 --- a/lib/src/localization/generated/app_localizations_de.dart +++ b/lib/src/localization/generated/app_localizations_de.dart @@ -1165,4 +1165,22 @@ class AppLocalizationsDe extends AppLocalizations { @override String get durationShortDays => 'Tagen'; + + @override + String get newGroup => 'Neue Gruppe'; + + @override + String get selectMembers => 'Mitglieder auswählen'; + + @override + String get selectGroupName => 'Gruppennamen wählen'; + + @override + String get groupNameInput => 'Gruppennamen'; + + @override + String get groupMembers => 'Mitglieder'; + + @override + String get createGroup => 'Gruppe erstellen'; } diff --git a/lib/src/localization/generated/app_localizations_en.dart b/lib/src/localization/generated/app_localizations_en.dart index db0c3cc..1c61b1f 100644 --- a/lib/src/localization/generated/app_localizations_en.dart +++ b/lib/src/localization/generated/app_localizations_en.dart @@ -1158,4 +1158,22 @@ class AppLocalizationsEn extends AppLocalizations { @override String get durationShortDays => 'Days'; + + @override + String get newGroup => 'New group'; + + @override + String get selectMembers => 'Select members'; + + @override + String get selectGroupName => 'Select group name'; + + @override + String get groupNameInput => 'Group name'; + + @override + String get groupMembers => 'Members'; + + @override + String get createGroup => 'Create group'; } diff --git a/lib/src/model/protobuf/api/http/http_requests.pb.dart b/lib/src/model/protobuf/api/http/http_requests.pb.dart index ad378f0..132252d 100644 --- a/lib/src/model/protobuf/api/http/http_requests.pb.dart +++ b/lib/src/model/protobuf/api/http/http_requests.pb.dart @@ -158,6 +158,350 @@ class UploadRequest extends $pb.GeneratedMessage { $core.List get messagesOnSuccess => $_getList(2); } +class UpdateGroupState_UpdateTBS extends $pb.GeneratedMessage { + factory UpdateGroupState_UpdateTBS({ + $fixnum.Int64? versionId, + $core.List<$core.int>? encryptedGroupState, + $core.List<$core.int>? publicKey, + $core.List<$core.int>? removeAdmin, + $core.List<$core.int>? addAdmin, + $core.List<$core.int>? nonce, + }) { + final $result = create(); + if (versionId != null) { + $result.versionId = versionId; + } + if (encryptedGroupState != null) { + $result.encryptedGroupState = encryptedGroupState; + } + if (publicKey != null) { + $result.publicKey = publicKey; + } + if (removeAdmin != null) { + $result.removeAdmin = removeAdmin; + } + if (addAdmin != null) { + $result.addAdmin = addAdmin; + } + if (nonce != null) { + $result.nonce = nonce; + } + return $result; + } + UpdateGroupState_UpdateTBS._() : super(); + factory UpdateGroupState_UpdateTBS.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory UpdateGroupState_UpdateTBS.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'UpdateGroupState.UpdateTBS', package: const $pb.PackageName(_omitMessageNames ? '' : 'http_requests'), createEmptyInstance: create) + ..a<$fixnum.Int64>(1, _omitFieldNames ? '' : 'versionId', $pb.PbFieldType.OU6, defaultOrMaker: $fixnum.Int64.ZERO) + ..a<$core.List<$core.int>>(3, _omitFieldNames ? '' : 'encryptedGroupState', $pb.PbFieldType.OY) + ..a<$core.List<$core.int>>(4, _omitFieldNames ? '' : 'publicKey', $pb.PbFieldType.OY) + ..a<$core.List<$core.int>>(5, _omitFieldNames ? '' : 'removeAdmin', $pb.PbFieldType.OY) + ..a<$core.List<$core.int>>(6, _omitFieldNames ? '' : 'addAdmin', $pb.PbFieldType.OY) + ..a<$core.List<$core.int>>(7, _omitFieldNames ? '' : 'nonce', $pb.PbFieldType.OY) + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + UpdateGroupState_UpdateTBS clone() => UpdateGroupState_UpdateTBS()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + UpdateGroupState_UpdateTBS copyWith(void Function(UpdateGroupState_UpdateTBS) updates) => super.copyWith((message) => updates(message as UpdateGroupState_UpdateTBS)) as UpdateGroupState_UpdateTBS; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static UpdateGroupState_UpdateTBS create() => UpdateGroupState_UpdateTBS._(); + UpdateGroupState_UpdateTBS createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static UpdateGroupState_UpdateTBS getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static UpdateGroupState_UpdateTBS? _defaultInstance; + + @$pb.TagNumber(1) + $fixnum.Int64 get versionId => $_getI64(0); + @$pb.TagNumber(1) + set versionId($fixnum.Int64 v) { $_setInt64(0, v); } + @$pb.TagNumber(1) + $core.bool hasVersionId() => $_has(0); + @$pb.TagNumber(1) + void clearVersionId() => clearField(1); + + @$pb.TagNumber(3) + $core.List<$core.int> get encryptedGroupState => $_getN(1); + @$pb.TagNumber(3) + set encryptedGroupState($core.List<$core.int> v) { $_setBytes(1, v); } + @$pb.TagNumber(3) + $core.bool hasEncryptedGroupState() => $_has(1); + @$pb.TagNumber(3) + void clearEncryptedGroupState() => clearField(3); + + @$pb.TagNumber(4) + $core.List<$core.int> get publicKey => $_getN(2); + @$pb.TagNumber(4) + set publicKey($core.List<$core.int> v) { $_setBytes(2, v); } + @$pb.TagNumber(4) + $core.bool hasPublicKey() => $_has(2); + @$pb.TagNumber(4) + void clearPublicKey() => clearField(4); + + /// public group key + @$pb.TagNumber(5) + $core.List<$core.int> get removeAdmin => $_getN(3); + @$pb.TagNumber(5) + set removeAdmin($core.List<$core.int> v) { $_setBytes(3, v); } + @$pb.TagNumber(5) + $core.bool hasRemoveAdmin() => $_has(3); + @$pb.TagNumber(5) + void clearRemoveAdmin() => clearField(5); + + @$pb.TagNumber(6) + $core.List<$core.int> get addAdmin => $_getN(4); + @$pb.TagNumber(6) + set addAdmin($core.List<$core.int> v) { $_setBytes(4, v); } + @$pb.TagNumber(6) + $core.bool hasAddAdmin() => $_has(4); + @$pb.TagNumber(6) + void clearAddAdmin() => clearField(6); + + @$pb.TagNumber(7) + $core.List<$core.int> get nonce => $_getN(5); + @$pb.TagNumber(7) + set nonce($core.List<$core.int> v) { $_setBytes(5, v); } + @$pb.TagNumber(7) + $core.bool hasNonce() => $_has(5); + @$pb.TagNumber(7) + void clearNonce() => clearField(7); +} + +/// plaintext message send to the server +class UpdateGroupState extends $pb.GeneratedMessage { + factory UpdateGroupState({ + UpdateGroupState_UpdateTBS? update, + $core.List<$core.int>? signature, + }) { + final $result = create(); + if (update != null) { + $result.update = update; + } + if (signature != null) { + $result.signature = signature; + } + return $result; + } + UpdateGroupState._() : super(); + factory UpdateGroupState.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory UpdateGroupState.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'UpdateGroupState', package: const $pb.PackageName(_omitMessageNames ? '' : 'http_requests'), createEmptyInstance: create) + ..aOM(1, _omitFieldNames ? '' : 'update', subBuilder: UpdateGroupState_UpdateTBS.create) + ..a<$core.List<$core.int>>(2, _omitFieldNames ? '' : 'signature', $pb.PbFieldType.OY) + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + UpdateGroupState clone() => UpdateGroupState()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + UpdateGroupState copyWith(void Function(UpdateGroupState) updates) => super.copyWith((message) => updates(message as UpdateGroupState)) as UpdateGroupState; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static UpdateGroupState create() => UpdateGroupState._(); + UpdateGroupState createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static UpdateGroupState getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static UpdateGroupState? _defaultInstance; + + @$pb.TagNumber(1) + UpdateGroupState_UpdateTBS get update => $_getN(0); + @$pb.TagNumber(1) + set update(UpdateGroupState_UpdateTBS v) { setField(1, v); } + @$pb.TagNumber(1) + $core.bool hasUpdate() => $_has(0); + @$pb.TagNumber(1) + void clearUpdate() => clearField(1); + @$pb.TagNumber(1) + UpdateGroupState_UpdateTBS ensureUpdate() => $_ensure(0); + + @$pb.TagNumber(2) + $core.List<$core.int> get signature => $_getN(1); + @$pb.TagNumber(2) + set signature($core.List<$core.int> v) { $_setBytes(1, v); } + @$pb.TagNumber(2) + $core.bool hasSignature() => $_has(1); + @$pb.TagNumber(2) + void clearSignature() => clearField(2); +} + +class NewGroupState extends $pb.GeneratedMessage { + factory NewGroupState({ + $core.String? groupId, + $fixnum.Int64? versionId, + $core.List<$core.int>? encryptedGroupState, + $core.List<$core.int>? publicKey, + }) { + final $result = create(); + if (groupId != null) { + $result.groupId = groupId; + } + if (versionId != null) { + $result.versionId = versionId; + } + if (encryptedGroupState != null) { + $result.encryptedGroupState = encryptedGroupState; + } + if (publicKey != null) { + $result.publicKey = publicKey; + } + return $result; + } + NewGroupState._() : super(); + factory NewGroupState.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory NewGroupState.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'NewGroupState', package: const $pb.PackageName(_omitMessageNames ? '' : 'http_requests'), createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'groupId', protoName: 'groupId') + ..a<$fixnum.Int64>(2, _omitFieldNames ? '' : 'versionId', $pb.PbFieldType.OU6, protoName: 'versionId', defaultOrMaker: $fixnum.Int64.ZERO) + ..a<$core.List<$core.int>>(4, _omitFieldNames ? '' : 'encryptedGroupState', $pb.PbFieldType.OY) + ..a<$core.List<$core.int>>(5, _omitFieldNames ? '' : 'publicKey', $pb.PbFieldType.OY) + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + NewGroupState clone() => NewGroupState()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + NewGroupState copyWith(void Function(NewGroupState) updates) => super.copyWith((message) => updates(message as NewGroupState)) as NewGroupState; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static NewGroupState create() => NewGroupState._(); + NewGroupState createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static NewGroupState getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static NewGroupState? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get groupId => $_getSZ(0); + @$pb.TagNumber(1) + set groupId($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasGroupId() => $_has(0); + @$pb.TagNumber(1) + void clearGroupId() => clearField(1); + + @$pb.TagNumber(2) + $fixnum.Int64 get versionId => $_getI64(1); + @$pb.TagNumber(2) + set versionId($fixnum.Int64 v) { $_setInt64(1, v); } + @$pb.TagNumber(2) + $core.bool hasVersionId() => $_has(1); + @$pb.TagNumber(2) + void clearVersionId() => clearField(2); + + @$pb.TagNumber(4) + $core.List<$core.int> get encryptedGroupState => $_getN(2); + @$pb.TagNumber(4) + set encryptedGroupState($core.List<$core.int> v) { $_setBytes(2, v); } + @$pb.TagNumber(4) + $core.bool hasEncryptedGroupState() => $_has(2); + @$pb.TagNumber(4) + void clearEncryptedGroupState() => clearField(4); + + @$pb.TagNumber(5) + $core.List<$core.int> get publicKey => $_getN(3); + @$pb.TagNumber(5) + set publicKey($core.List<$core.int> v) { $_setBytes(3, v); } + @$pb.TagNumber(5) + $core.bool hasPublicKey() => $_has(3); + @$pb.TagNumber(5) + void clearPublicKey() => clearField(5); +} + +class GroupState extends $pb.GeneratedMessage { + factory GroupState({ + $fixnum.Int64? versionId, + $core.List<$core.int>? encryptedGroupState, + }) { + final $result = create(); + if (versionId != null) { + $result.versionId = versionId; + } + if (encryptedGroupState != null) { + $result.encryptedGroupState = encryptedGroupState; + } + return $result; + } + GroupState._() : super(); + factory GroupState.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory GroupState.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'GroupState', package: const $pb.PackageName(_omitMessageNames ? '' : 'http_requests'), createEmptyInstance: create) + ..a<$fixnum.Int64>(1, _omitFieldNames ? '' : 'versionId', $pb.PbFieldType.OU6, protoName: 'versionId', defaultOrMaker: $fixnum.Int64.ZERO) + ..a<$core.List<$core.int>>(3, _omitFieldNames ? '' : 'encryptedGroupState', $pb.PbFieldType.OY) + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + GroupState clone() => GroupState()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + GroupState copyWith(void Function(GroupState) updates) => super.copyWith((message) => updates(message as GroupState)) as GroupState; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static GroupState create() => GroupState._(); + GroupState createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static GroupState getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static GroupState? _defaultInstance; + + @$pb.TagNumber(1) + $fixnum.Int64 get versionId => $_getI64(0); + @$pb.TagNumber(1) + set versionId($fixnum.Int64 v) { $_setInt64(0, v); } + @$pb.TagNumber(1) + $core.bool hasVersionId() => $_has(0); + @$pb.TagNumber(1) + void clearVersionId() => clearField(1); + + @$pb.TagNumber(3) + $core.List<$core.int> get encryptedGroupState => $_getN(1); + @$pb.TagNumber(3) + set encryptedGroupState($core.List<$core.int> v) { $_setBytes(1, v); } + @$pb.TagNumber(3) + $core.bool hasEncryptedGroupState() => $_has(1); + @$pb.TagNumber(3) + void clearEncryptedGroupState() => clearField(3); +} + const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/lib/src/model/protobuf/api/http/http_requests.pbjson.dart b/lib/src/model/protobuf/api/http/http_requests.pbjson.dart index d4c43f8..be8ef01 100644 --- a/lib/src/model/protobuf/api/http/http_requests.pbjson.dart +++ b/lib/src/model/protobuf/api/http/http_requests.pbjson.dart @@ -48,3 +48,71 @@ final $typed_data.Uint8List uploadRequestDescriptor = $convert.base64Decode( 'c3VjY2VzcxgDIAMoCzIaLmh0dHBfcmVxdWVzdHMuVGV4dE1lc3NhZ2VSEW1lc3NhZ2VzT25TdW' 'NjZXNz'); +@$core.Deprecated('Use updateGroupStateDescriptor instead') +const UpdateGroupState$json = { + '1': 'UpdateGroupState', + '2': [ + {'1': 'update', '3': 1, '4': 1, '5': 11, '6': '.http_requests.UpdateGroupState.UpdateTBS', '10': 'update'}, + {'1': 'signature', '3': 2, '4': 1, '5': 12, '10': 'signature'}, + ], + '3': [UpdateGroupState_UpdateTBS$json], +}; + +@$core.Deprecated('Use updateGroupStateDescriptor instead') +const UpdateGroupState_UpdateTBS$json = { + '1': 'UpdateTBS', + '2': [ + {'1': 'version_id', '3': 1, '4': 1, '5': 4, '10': 'versionId'}, + {'1': 'encrypted_group_state', '3': 3, '4': 1, '5': 12, '10': 'encryptedGroupState'}, + {'1': 'public_key', '3': 4, '4': 1, '5': 12, '10': 'publicKey'}, + {'1': 'remove_admin', '3': 5, '4': 1, '5': 12, '9': 0, '10': 'removeAdmin', '17': true}, + {'1': 'add_admin', '3': 6, '4': 1, '5': 12, '9': 1, '10': 'addAdmin', '17': true}, + {'1': 'nonce', '3': 7, '4': 1, '5': 12, '10': 'nonce'}, + ], + '8': [ + {'1': '_remove_admin'}, + {'1': '_add_admin'}, + ], +}; + +/// Descriptor for `UpdateGroupState`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List updateGroupStateDescriptor = $convert.base64Decode( + 'ChBVcGRhdGVHcm91cFN0YXRlEkEKBnVwZGF0ZRgBIAEoCzIpLmh0dHBfcmVxdWVzdHMuVXBkYX' + 'RlR3JvdXBTdGF0ZS5VcGRhdGVUQlNSBnVwZGF0ZRIcCglzaWduYXR1cmUYAiABKAxSCXNpZ25h' + 'dHVyZRr8AQoJVXBkYXRlVEJTEh0KCnZlcnNpb25faWQYASABKARSCXZlcnNpb25JZBIyChVlbm' + 'NyeXB0ZWRfZ3JvdXBfc3RhdGUYAyABKAxSE2VuY3J5cHRlZEdyb3VwU3RhdGUSHQoKcHVibGlj' + 'X2tleRgEIAEoDFIJcHVibGljS2V5EiYKDHJlbW92ZV9hZG1pbhgFIAEoDEgAUgtyZW1vdmVBZG' + '1pbogBARIgCglhZGRfYWRtaW4YBiABKAxIAVIIYWRkQWRtaW6IAQESFAoFbm9uY2UYByABKAxS' + 'BW5vbmNlQg8KDV9yZW1vdmVfYWRtaW5CDAoKX2FkZF9hZG1pbg=='); + +@$core.Deprecated('Use newGroupStateDescriptor instead') +const NewGroupState$json = { + '1': 'NewGroupState', + '2': [ + {'1': 'groupId', '3': 1, '4': 1, '5': 9, '10': 'groupId'}, + {'1': 'versionId', '3': 2, '4': 1, '5': 4, '10': 'versionId'}, + {'1': 'encrypted_group_state', '3': 4, '4': 1, '5': 12, '10': 'encryptedGroupState'}, + {'1': 'public_key', '3': 5, '4': 1, '5': 12, '10': 'publicKey'}, + ], +}; + +/// Descriptor for `NewGroupState`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List newGroupStateDescriptor = $convert.base64Decode( + 'Cg1OZXdHcm91cFN0YXRlEhgKB2dyb3VwSWQYASABKAlSB2dyb3VwSWQSHAoJdmVyc2lvbklkGA' + 'IgASgEUgl2ZXJzaW9uSWQSMgoVZW5jcnlwdGVkX2dyb3VwX3N0YXRlGAQgASgMUhNlbmNyeXB0' + 'ZWRHcm91cFN0YXRlEh0KCnB1YmxpY19rZXkYBSABKAxSCXB1YmxpY0tleQ=='); + +@$core.Deprecated('Use groupStateDescriptor instead') +const GroupState$json = { + '1': 'GroupState', + '2': [ + {'1': 'versionId', '3': 1, '4': 1, '5': 4, '10': 'versionId'}, + {'1': 'encrypted_group_state', '3': 3, '4': 1, '5': 12, '10': 'encryptedGroupState'}, + ], +}; + +/// Descriptor for `GroupState`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List groupStateDescriptor = $convert.base64Decode( + 'CgpHcm91cFN0YXRlEhwKCXZlcnNpb25JZBgBIAEoBFIJdmVyc2lvbklkEjIKFWVuY3J5cHRlZF' + '9ncm91cF9zdGF0ZRgDIAEoDFITZW5jcnlwdGVkR3JvdXBTdGF0ZQ=='); + diff --git a/lib/src/model/protobuf/api/websocket/client_to_server.pb.dart b/lib/src/model/protobuf/api/websocket/client_to_server.pb.dart index 5d4ff99..2535881 100644 --- a/lib/src/model/protobuf/api/websocket/client_to_server.pb.dart +++ b/lib/src/model/protobuf/api/websocket/client_to_server.pb.dart @@ -206,6 +206,7 @@ class Handshake_Register extends $pb.GeneratedMessage { $fixnum.Int64? signedPrekeyId, $fixnum.Int64? registrationId, $core.bool? isIos, + $core.String? langCode, }) { final $result = create(); if (username != null) { @@ -232,6 +233,9 @@ class Handshake_Register extends $pb.GeneratedMessage { if (isIos != null) { $result.isIos = isIos; } + if (langCode != null) { + $result.langCode = langCode; + } return $result; } Handshake_Register._() : super(); @@ -247,6 +251,7 @@ class Handshake_Register extends $pb.GeneratedMessage { ..aInt64(6, _omitFieldNames ? '' : 'signedPrekeyId') ..aInt64(7, _omitFieldNames ? '' : 'registrationId') ..aOB(8, _omitFieldNames ? '' : 'isIos') + ..aOS(9, _omitFieldNames ? '' : 'langCode') ..hasRequiredFields = false ; @@ -342,6 +347,15 @@ class Handshake_Register extends $pb.GeneratedMessage { $core.bool hasIsIos() => $_has(7); @$pb.TagNumber(8) void clearIsIos() => clearField(8); + + @$pb.TagNumber(9) + $core.String get langCode => $_getSZ(8); + @$pb.TagNumber(9) + set langCode($core.String v) { $_setString(8, v); } + @$pb.TagNumber(9) + $core.bool hasLangCode() => $_has(8); + @$pb.TagNumber(9) + void clearLangCode() => clearField(9); } class Handshake_GetAuthChallenge extends $pb.GeneratedMessage { diff --git a/lib/src/model/protobuf/api/websocket/client_to_server.pbjson.dart b/lib/src/model/protobuf/api/websocket/client_to_server.pbjson.dart index 7c05b1b..edb5470 100644 --- a/lib/src/model/protobuf/api/websocket/client_to_server.pbjson.dart +++ b/lib/src/model/protobuf/api/websocket/client_to_server.pbjson.dart @@ -77,11 +77,11 @@ const Handshake_Register$json = { {'1': 'signed_prekey_signature', '3': 5, '4': 1, '5': 12, '10': 'signedPrekeySignature'}, {'1': 'signed_prekey_id', '3': 6, '4': 1, '5': 3, '10': 'signedPrekeyId'}, {'1': 'registration_id', '3': 7, '4': 1, '5': 3, '10': 'registrationId'}, - {'1': 'is_ios', '3': 8, '4': 1, '5': 8, '9': 1, '10': 'isIos', '17': true}, + {'1': 'is_ios', '3': 8, '4': 1, '5': 8, '10': 'isIos'}, + {'1': 'lang_code', '3': 9, '4': 1, '5': 9, '10': 'langCode'}, ], '8': [ {'1': '_invite_code'}, - {'1': '_is_ios'}, ], }; @@ -121,19 +121,19 @@ final $typed_data.Uint8List handshakeDescriptor = $convert.base64Decode( 'ZW50X3RvX3NlcnZlci5IYW5kc2hha2UuR2V0QXV0aENoYWxsZW5nZUgAUhBnZXRhdXRoY2hhbG' 'xlbmdlEk4KDGdldGF1dGh0b2tlbhgDIAEoCzIoLmNsaWVudF90b19zZXJ2ZXIuSGFuZHNoYWtl' 'LkdldEF1dGhUb2tlbkgAUgxnZXRhdXRodG9rZW4STgoMYXV0aGVudGljYXRlGAQgASgLMiguY2' - 'xpZW50X3RvX3NlcnZlci5IYW5kc2hha2UuQXV0aGVudGljYXRlSABSDGF1dGhlbnRpY2F0ZRrj' + 'xpZW50X3RvX3NlcnZlci5IYW5kc2hha2UuQXV0aGVudGljYXRlSABSDGF1dGhlbnRpY2F0ZRrw' 'AgoIUmVnaXN0ZXISGgoIdXNlcm5hbWUYASABKAlSCHVzZXJuYW1lEiQKC2ludml0ZV9jb2RlGA' 'IgASgJSABSCmludml0ZUNvZGWIAQESLgoTcHVibGljX2lkZW50aXR5X2tleRgDIAEoDFIRcHVi' 'bGljSWRlbnRpdHlLZXkSIwoNc2lnbmVkX3ByZWtleRgEIAEoDFIMc2lnbmVkUHJla2V5EjYKF3' 'NpZ25lZF9wcmVrZXlfc2lnbmF0dXJlGAUgASgMUhVzaWduZWRQcmVrZXlTaWduYXR1cmUSKAoQ' 'c2lnbmVkX3ByZWtleV9pZBgGIAEoA1IOc2lnbmVkUHJla2V5SWQSJwoPcmVnaXN0cmF0aW9uX2' - 'lkGAcgASgDUg5yZWdpc3RyYXRpb25JZBIaCgZpc19pb3MYCCABKAhIAVIFaXNJb3OIAQFCDgoM' - 'X2ludml0ZV9jb2RlQgkKB19pc19pb3MaEgoQR2V0QXV0aENoYWxsZW5nZRpDCgxHZXRBdXRoVG' - '9rZW4SFwoHdXNlcl9pZBgBIAEoA1IGdXNlcklkEhoKCHJlc3BvbnNlGAIgASgMUghyZXNwb25z' - 'ZRqsAQoMQXV0aGVudGljYXRlEhcKB3VzZXJfaWQYASABKANSBnVzZXJJZBIdCgphdXRoX3Rva2' - 'VuGAIgASgMUglhdXRoVG9rZW4SJAoLYXBwX3ZlcnNpb24YAyABKAlIAFIKYXBwVmVyc2lvbogB' - 'ARIgCglkZXZpY2VfaWQYBCABKANIAVIIZGV2aWNlSWSIAQFCDgoMX2FwcF92ZXJzaW9uQgwKCl' - '9kZXZpY2VfaWRCCwoJSGFuZHNoYWtl'); + 'lkGAcgASgDUg5yZWdpc3RyYXRpb25JZBIVCgZpc19pb3MYCCABKAhSBWlzSW9zEhsKCWxhbmdf' + 'Y29kZRgJIAEoCVIIbGFuZ0NvZGVCDgoMX2ludml0ZV9jb2RlGhIKEEdldEF1dGhDaGFsbGVuZ2' + 'UaQwoMR2V0QXV0aFRva2VuEhcKB3VzZXJfaWQYASABKANSBnVzZXJJZBIaCghyZXNwb25zZRgC' + 'IAEoDFIIcmVzcG9uc2UarAEKDEF1dGhlbnRpY2F0ZRIXCgd1c2VyX2lkGAEgASgDUgZ1c2VySW' + 'QSHQoKYXV0aF90b2tlbhgCIAEoDFIJYXV0aFRva2VuEiQKC2FwcF92ZXJzaW9uGAMgASgJSABS' + 'CmFwcFZlcnNpb26IAQESIAoJZGV2aWNlX2lkGAQgASgDSAFSCGRldmljZUlkiAEBQg4KDF9hcH' + 'BfdmVyc2lvbkIMCgpfZGV2aWNlX2lkQgsKCUhhbmRzaGFrZQ=='); @$core.Deprecated('Use applicationDataDescriptor instead') const ApplicationData$json = { diff --git a/lib/src/model/protobuf/client/generated/groups.pb.dart b/lib/src/model/protobuf/client/generated/groups.pb.dart new file mode 100644 index 0000000..e3d50f5 --- /dev/null +++ b/lib/src/model/protobuf/client/generated/groups.pb.dart @@ -0,0 +1,192 @@ +// +// Generated code. Do not modify. +// source: groups.proto +// +// @dart = 2.12 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_final_fields +// ignore_for_file: unnecessary_import, unnecessary_this, unused_import + +import 'dart:core' as $core; + +import 'package:fixnum/fixnum.dart' as $fixnum; +import 'package:protobuf/protobuf.dart' as $pb; + +/// Stored encrypted on the server in the members columns. +class EncryptedGroupState extends $pb.GeneratedMessage { + factory EncryptedGroupState({ + $core.Iterable<$fixnum.Int64>? memberIds, + $core.Iterable<$fixnum.Int64>? adminIds, + $core.String? groupName, + $fixnum.Int64? deleteMessagesAfterMilliseconds, + $core.List<$core.int>? padding, + }) { + final $result = create(); + if (memberIds != null) { + $result.memberIds.addAll(memberIds); + } + if (adminIds != null) { + $result.adminIds.addAll(adminIds); + } + if (groupName != null) { + $result.groupName = groupName; + } + if (deleteMessagesAfterMilliseconds != null) { + $result.deleteMessagesAfterMilliseconds = deleteMessagesAfterMilliseconds; + } + if (padding != null) { + $result.padding = padding; + } + return $result; + } + EncryptedGroupState._() : super(); + factory EncryptedGroupState.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory EncryptedGroupState.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'EncryptedGroupState', createEmptyInstance: create) + ..p<$fixnum.Int64>(1, _omitFieldNames ? '' : 'memberIds', $pb.PbFieldType.K6, protoName: 'memberIds') + ..p<$fixnum.Int64>(2, _omitFieldNames ? '' : 'adminIds', $pb.PbFieldType.K6, protoName: 'adminIds') + ..aOS(3, _omitFieldNames ? '' : 'groupName', protoName: 'groupName') + ..aInt64(4, _omitFieldNames ? '' : 'deleteMessagesAfterMilliseconds', protoName: 'deleteMessagesAfterMilliseconds') + ..a<$core.List<$core.int>>(5, _omitFieldNames ? '' : 'padding', $pb.PbFieldType.OY) + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + EncryptedGroupState clone() => EncryptedGroupState()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + EncryptedGroupState copyWith(void Function(EncryptedGroupState) updates) => super.copyWith((message) => updates(message as EncryptedGroupState)) as EncryptedGroupState; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static EncryptedGroupState create() => EncryptedGroupState._(); + EncryptedGroupState createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static EncryptedGroupState getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static EncryptedGroupState? _defaultInstance; + + @$pb.TagNumber(1) + $core.List<$fixnum.Int64> get memberIds => $_getList(0); + + @$pb.TagNumber(2) + $core.List<$fixnum.Int64> get adminIds => $_getList(1); + + @$pb.TagNumber(3) + $core.String get groupName => $_getSZ(2); + @$pb.TagNumber(3) + set groupName($core.String v) { $_setString(2, v); } + @$pb.TagNumber(3) + $core.bool hasGroupName() => $_has(2); + @$pb.TagNumber(3) + void clearGroupName() => clearField(3); + + @$pb.TagNumber(4) + $fixnum.Int64 get deleteMessagesAfterMilliseconds => $_getI64(3); + @$pb.TagNumber(4) + set deleteMessagesAfterMilliseconds($fixnum.Int64 v) { $_setInt64(3, v); } + @$pb.TagNumber(4) + $core.bool hasDeleteMessagesAfterMilliseconds() => $_has(3); + @$pb.TagNumber(4) + void clearDeleteMessagesAfterMilliseconds() => clearField(4); + + @$pb.TagNumber(5) + $core.List<$core.int> get padding => $_getN(4); + @$pb.TagNumber(5) + set padding($core.List<$core.int> v) { $_setBytes(4, v); } + @$pb.TagNumber(5) + $core.bool hasPadding() => $_has(4); + @$pb.TagNumber(5) + void clearPadding() => clearField(5); +} + +class EncryptedGroupStateEnvelop extends $pb.GeneratedMessage { + factory EncryptedGroupStateEnvelop({ + $core.List<$core.int>? nonce, + $core.List<$core.int>? encryptedGroupState, + $core.List<$core.int>? mac, + }) { + final $result = create(); + if (nonce != null) { + $result.nonce = nonce; + } + if (encryptedGroupState != null) { + $result.encryptedGroupState = encryptedGroupState; + } + if (mac != null) { + $result.mac = mac; + } + return $result; + } + EncryptedGroupStateEnvelop._() : super(); + factory EncryptedGroupStateEnvelop.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory EncryptedGroupStateEnvelop.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'EncryptedGroupStateEnvelop', createEmptyInstance: create) + ..a<$core.List<$core.int>>(1, _omitFieldNames ? '' : 'nonce', $pb.PbFieldType.OY) + ..a<$core.List<$core.int>>(2, _omitFieldNames ? '' : 'encryptedGroupState', $pb.PbFieldType.OY, protoName: 'encryptedGroupState') + ..a<$core.List<$core.int>>(3, _omitFieldNames ? '' : 'mac', $pb.PbFieldType.OY) + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + EncryptedGroupStateEnvelop clone() => EncryptedGroupStateEnvelop()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + EncryptedGroupStateEnvelop copyWith(void Function(EncryptedGroupStateEnvelop) updates) => super.copyWith((message) => updates(message as EncryptedGroupStateEnvelop)) as EncryptedGroupStateEnvelop; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static EncryptedGroupStateEnvelop create() => EncryptedGroupStateEnvelop._(); + EncryptedGroupStateEnvelop createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static EncryptedGroupStateEnvelop getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static EncryptedGroupStateEnvelop? _defaultInstance; + + @$pb.TagNumber(1) + $core.List<$core.int> get nonce => $_getN(0); + @$pb.TagNumber(1) + set nonce($core.List<$core.int> v) { $_setBytes(0, v); } + @$pb.TagNumber(1) + $core.bool hasNonce() => $_has(0); + @$pb.TagNumber(1) + void clearNonce() => clearField(1); + + @$pb.TagNumber(2) + $core.List<$core.int> get encryptedGroupState => $_getN(1); + @$pb.TagNumber(2) + set encryptedGroupState($core.List<$core.int> v) { $_setBytes(1, v); } + @$pb.TagNumber(2) + $core.bool hasEncryptedGroupState() => $_has(1); + @$pb.TagNumber(2) + void clearEncryptedGroupState() => clearField(2); + + @$pb.TagNumber(3) + $core.List<$core.int> get mac => $_getN(2); + @$pb.TagNumber(3) + set mac($core.List<$core.int> v) { $_setBytes(2, v); } + @$pb.TagNumber(3) + $core.bool hasMac() => $_has(2); + @$pb.TagNumber(3) + void clearMac() => clearField(3); +} + + +const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); +const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/lib/src/model/protobuf/client/generated/groups.pbenum.dart b/lib/src/model/protobuf/client/generated/groups.pbenum.dart new file mode 100644 index 0000000..c03fb19 --- /dev/null +++ b/lib/src/model/protobuf/client/generated/groups.pbenum.dart @@ -0,0 +1,11 @@ +// +// Generated code. Do not modify. +// source: groups.proto +// +// @dart = 2.12 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_final_fields +// ignore_for_file: unnecessary_import, unnecessary_this, unused_import + diff --git a/lib/src/model/protobuf/client/generated/groups.pbjson.dart b/lib/src/model/protobuf/client/generated/groups.pbjson.dart new file mode 100644 index 0000000..97fd3f8 --- /dev/null +++ b/lib/src/model/protobuf/client/generated/groups.pbjson.dart @@ -0,0 +1,54 @@ +// +// Generated code. Do not modify. +// source: groups.proto +// +// @dart = 2.12 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_final_fields +// ignore_for_file: unnecessary_import, unnecessary_this, unused_import + +import 'dart:convert' as $convert; +import 'dart:core' as $core; +import 'dart:typed_data' as $typed_data; + +@$core.Deprecated('Use encryptedGroupStateDescriptor instead') +const EncryptedGroupState$json = { + '1': 'EncryptedGroupState', + '2': [ + {'1': 'memberIds', '3': 1, '4': 3, '5': 3, '10': 'memberIds'}, + {'1': 'adminIds', '3': 2, '4': 3, '5': 3, '10': 'adminIds'}, + {'1': 'groupName', '3': 3, '4': 1, '5': 9, '10': 'groupName'}, + {'1': 'deleteMessagesAfterMilliseconds', '3': 4, '4': 1, '5': 3, '9': 0, '10': 'deleteMessagesAfterMilliseconds', '17': true}, + {'1': 'padding', '3': 5, '4': 1, '5': 12, '10': 'padding'}, + ], + '8': [ + {'1': '_deleteMessagesAfterMilliseconds'}, + ], +}; + +/// Descriptor for `EncryptedGroupState`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List encryptedGroupStateDescriptor = $convert.base64Decode( + 'ChNFbmNyeXB0ZWRHcm91cFN0YXRlEhwKCW1lbWJlcklkcxgBIAMoA1IJbWVtYmVySWRzEhoKCG' + 'FkbWluSWRzGAIgAygDUghhZG1pbklkcxIcCglncm91cE5hbWUYAyABKAlSCWdyb3VwTmFtZRJN' + 'Ch9kZWxldGVNZXNzYWdlc0FmdGVyTWlsbGlzZWNvbmRzGAQgASgDSABSH2RlbGV0ZU1lc3NhZ2' + 'VzQWZ0ZXJNaWxsaXNlY29uZHOIAQESGAoHcGFkZGluZxgFIAEoDFIHcGFkZGluZ0IiCiBfZGVs' + 'ZXRlTWVzc2FnZXNBZnRlck1pbGxpc2Vjb25kcw=='); + +@$core.Deprecated('Use encryptedGroupStateEnvelopDescriptor instead') +const EncryptedGroupStateEnvelop$json = { + '1': 'EncryptedGroupStateEnvelop', + '2': [ + {'1': 'nonce', '3': 1, '4': 1, '5': 12, '10': 'nonce'}, + {'1': 'encryptedGroupState', '3': 2, '4': 1, '5': 12, '10': 'encryptedGroupState'}, + {'1': 'mac', '3': 3, '4': 1, '5': 12, '10': 'mac'}, + ], +}; + +/// Descriptor for `EncryptedGroupStateEnvelop`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List encryptedGroupStateEnvelopDescriptor = $convert.base64Decode( + 'ChpFbmNyeXB0ZWRHcm91cFN0YXRlRW52ZWxvcBIUCgVub25jZRgBIAEoDFIFbm9uY2USMAoTZW' + '5jcnlwdGVkR3JvdXBTdGF0ZRgCIAEoDFITZW5jcnlwdGVkR3JvdXBTdGF0ZRIQCgNtYWMYAyAB' + 'KAxSA21hYw=='); + diff --git a/lib/src/model/protobuf/client/generated/groups.pbserver.dart b/lib/src/model/protobuf/client/generated/groups.pbserver.dart new file mode 100644 index 0000000..087f85c --- /dev/null +++ b/lib/src/model/protobuf/client/generated/groups.pbserver.dart @@ -0,0 +1,14 @@ +// +// Generated code. Do not modify. +// source: groups.proto +// +// @dart = 2.12 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names +// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_final_fields +// ignore_for_file: unnecessary_import, unnecessary_this, unused_import + +export 'groups.pb.dart'; + diff --git a/lib/src/model/protobuf/client/generated/messages.pb.dart b/lib/src/model/protobuf/client/generated/messages.pb.dart index 6a41b3f..029b405 100644 --- a/lib/src/model/protobuf/client/generated/messages.pb.dart +++ b/lib/src/model/protobuf/client/generated/messages.pb.dart @@ -214,6 +214,200 @@ class PlaintextContent extends $pb.GeneratedMessage { PlaintextContent_DecryptionErrorMessage ensureDecryptionErrorMessage() => $_ensure(0); } +class EncryptedContent_GroupCreate extends $pb.GeneratedMessage { + factory EncryptedContent_GroupCreate({ + $core.List<$core.int>? stateKey, + $core.List<$core.int>? groupPublicKey, + }) { + final $result = create(); + if (stateKey != null) { + $result.stateKey = stateKey; + } + if (groupPublicKey != null) { + $result.groupPublicKey = groupPublicKey; + } + return $result; + } + EncryptedContent_GroupCreate._() : super(); + factory EncryptedContent_GroupCreate.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory EncryptedContent_GroupCreate.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'EncryptedContent.GroupCreate', createEmptyInstance: create) + ..a<$core.List<$core.int>>(3, _omitFieldNames ? '' : 'stateKey', $pb.PbFieldType.OY, protoName: 'stateKey') + ..a<$core.List<$core.int>>(4, _omitFieldNames ? '' : 'groupPublicKey', $pb.PbFieldType.OY, protoName: 'groupPublicKey') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + EncryptedContent_GroupCreate clone() => EncryptedContent_GroupCreate()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + EncryptedContent_GroupCreate copyWith(void Function(EncryptedContent_GroupCreate) updates) => super.copyWith((message) => updates(message as EncryptedContent_GroupCreate)) as EncryptedContent_GroupCreate; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static EncryptedContent_GroupCreate create() => EncryptedContent_GroupCreate._(); + EncryptedContent_GroupCreate createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static EncryptedContent_GroupCreate getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static EncryptedContent_GroupCreate? _defaultInstance; + + /// key for the state stored on the server + @$pb.TagNumber(3) + $core.List<$core.int> get stateKey => $_getN(0); + @$pb.TagNumber(3) + set stateKey($core.List<$core.int> v) { $_setBytes(0, v); } + @$pb.TagNumber(3) + $core.bool hasStateKey() => $_has(0); + @$pb.TagNumber(3) + void clearStateKey() => clearField(3); + + @$pb.TagNumber(4) + $core.List<$core.int> get groupPublicKey => $_getN(1); + @$pb.TagNumber(4) + set groupPublicKey($core.List<$core.int> v) { $_setBytes(1, v); } + @$pb.TagNumber(4) + $core.bool hasGroupPublicKey() => $_has(1); + @$pb.TagNumber(4) + void clearGroupPublicKey() => clearField(4); +} + +class EncryptedContent_GroupJoin extends $pb.GeneratedMessage { + factory EncryptedContent_GroupJoin({ + $core.List<$core.int>? groupPublicKey, + }) { + final $result = create(); + if (groupPublicKey != null) { + $result.groupPublicKey = groupPublicKey; + } + return $result; + } + EncryptedContent_GroupJoin._() : super(); + factory EncryptedContent_GroupJoin.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory EncryptedContent_GroupJoin.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'EncryptedContent.GroupJoin', createEmptyInstance: create) + ..a<$core.List<$core.int>>(4, _omitFieldNames ? '' : 'groupPublicKey', $pb.PbFieldType.OY, protoName: 'groupPublicKey') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + EncryptedContent_GroupJoin clone() => EncryptedContent_GroupJoin()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + EncryptedContent_GroupJoin copyWith(void Function(EncryptedContent_GroupJoin) updates) => super.copyWith((message) => updates(message as EncryptedContent_GroupJoin)) as EncryptedContent_GroupJoin; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static EncryptedContent_GroupJoin create() => EncryptedContent_GroupJoin._(); + EncryptedContent_GroupJoin createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static EncryptedContent_GroupJoin getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static EncryptedContent_GroupJoin? _defaultInstance; + + /// key for the state stored on the server + @$pb.TagNumber(4) + $core.List<$core.int> get groupPublicKey => $_getN(0); + @$pb.TagNumber(4) + set groupPublicKey($core.List<$core.int> v) { $_setBytes(0, v); } + @$pb.TagNumber(4) + $core.bool hasGroupPublicKey() => $_has(0); + @$pb.TagNumber(4) + void clearGroupPublicKey() => clearField(4); +} + +class EncryptedContent_GroupUpdate extends $pb.GeneratedMessage { + factory EncryptedContent_GroupUpdate({ + $core.String? groupActionType, + $fixnum.Int64? affectedContactId, + $core.String? newGroupName, + }) { + final $result = create(); + if (groupActionType != null) { + $result.groupActionType = groupActionType; + } + if (affectedContactId != null) { + $result.affectedContactId = affectedContactId; + } + if (newGroupName != null) { + $result.newGroupName = newGroupName; + } + return $result; + } + EncryptedContent_GroupUpdate._() : super(); + factory EncryptedContent_GroupUpdate.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory EncryptedContent_GroupUpdate.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'EncryptedContent.GroupUpdate', createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'groupActionType', protoName: 'groupActionType') + ..aInt64(2, _omitFieldNames ? '' : 'affectedContactId', protoName: 'affectedContactId') + ..aOS(3, _omitFieldNames ? '' : 'newGroupName', protoName: 'newGroupName') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + EncryptedContent_GroupUpdate clone() => EncryptedContent_GroupUpdate()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + EncryptedContent_GroupUpdate copyWith(void Function(EncryptedContent_GroupUpdate) updates) => super.copyWith((message) => updates(message as EncryptedContent_GroupUpdate)) as EncryptedContent_GroupUpdate; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static EncryptedContent_GroupUpdate create() => EncryptedContent_GroupUpdate._(); + EncryptedContent_GroupUpdate createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static EncryptedContent_GroupUpdate getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static EncryptedContent_GroupUpdate? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get groupActionType => $_getSZ(0); + @$pb.TagNumber(1) + set groupActionType($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasGroupActionType() => $_has(0); + @$pb.TagNumber(1) + void clearGroupActionType() => clearField(1); + + @$pb.TagNumber(2) + $fixnum.Int64 get affectedContactId => $_getI64(1); + @$pb.TagNumber(2) + set affectedContactId($fixnum.Int64 v) { $_setInt64(1, v); } + @$pb.TagNumber(2) + $core.bool hasAffectedContactId() => $_has(1); + @$pb.TagNumber(2) + void clearAffectedContactId() => clearField(2); + + @$pb.TagNumber(3) + $core.String get newGroupName => $_getSZ(2); + @$pb.TagNumber(3) + set newGroupName($core.String v) { $_setString(2, v); } + @$pb.TagNumber(3) + $core.bool hasNewGroupName() => $_has(2); + @$pb.TagNumber(3) + void clearNewGroupName() => clearField(3); +} + class EncryptedContent_TextMessage extends $pb.GeneratedMessage { factory EncryptedContent_TextMessage({ $core.String? senderMessageId, @@ -1036,6 +1230,9 @@ class EncryptedContent extends $pb.GeneratedMessage { EncryptedContent_PushKeys? pushKeys, EncryptedContent_Reaction? reaction, EncryptedContent_TextMessage? textMessage, + EncryptedContent_GroupCreate? groupCreate, + EncryptedContent_GroupJoin? groupJoin, + EncryptedContent_GroupUpdate? groupUpdate, }) { final $result = create(); if (groupId != null) { @@ -1074,6 +1271,15 @@ class EncryptedContent extends $pb.GeneratedMessage { if (textMessage != null) { $result.textMessage = textMessage; } + if (groupCreate != null) { + $result.groupCreate = groupCreate; + } + if (groupJoin != null) { + $result.groupJoin = groupJoin; + } + if (groupUpdate != null) { + $result.groupUpdate = groupUpdate; + } return $result; } EncryptedContent._() : super(); @@ -1093,6 +1299,9 @@ class EncryptedContent extends $pb.GeneratedMessage { ..aOM(11, _omitFieldNames ? '' : 'pushKeys', protoName: 'pushKeys', subBuilder: EncryptedContent_PushKeys.create) ..aOM(12, _omitFieldNames ? '' : 'reaction', subBuilder: EncryptedContent_Reaction.create) ..aOM(13, _omitFieldNames ? '' : 'textMessage', protoName: 'textMessage', subBuilder: EncryptedContent_TextMessage.create) + ..aOM(14, _omitFieldNames ? '' : 'groupCreate', protoName: 'groupCreate', subBuilder: EncryptedContent_GroupCreate.create) + ..aOM(15, _omitFieldNames ? '' : 'groupJoin', protoName: 'groupJoin', subBuilder: EncryptedContent_GroupJoin.create) + ..aOM(16, _omitFieldNames ? '' : 'groupUpdate', protoName: 'groupUpdate', subBuilder: EncryptedContent_GroupUpdate.create) ..hasRequiredFields = false ; @@ -1243,6 +1452,39 @@ class EncryptedContent extends $pb.GeneratedMessage { void clearTextMessage() => clearField(13); @$pb.TagNumber(13) EncryptedContent_TextMessage ensureTextMessage() => $_ensure(11); + + @$pb.TagNumber(14) + EncryptedContent_GroupCreate get groupCreate => $_getN(12); + @$pb.TagNumber(14) + set groupCreate(EncryptedContent_GroupCreate v) { setField(14, v); } + @$pb.TagNumber(14) + $core.bool hasGroupCreate() => $_has(12); + @$pb.TagNumber(14) + void clearGroupCreate() => clearField(14); + @$pb.TagNumber(14) + EncryptedContent_GroupCreate ensureGroupCreate() => $_ensure(12); + + @$pb.TagNumber(15) + EncryptedContent_GroupJoin get groupJoin => $_getN(13); + @$pb.TagNumber(15) + set groupJoin(EncryptedContent_GroupJoin v) { setField(15, v); } + @$pb.TagNumber(15) + $core.bool hasGroupJoin() => $_has(13); + @$pb.TagNumber(15) + void clearGroupJoin() => clearField(15); + @$pb.TagNumber(15) + EncryptedContent_GroupJoin ensureGroupJoin() => $_ensure(13); + + @$pb.TagNumber(16) + EncryptedContent_GroupUpdate get groupUpdate => $_getN(14); + @$pb.TagNumber(16) + set groupUpdate(EncryptedContent_GroupUpdate v) { setField(16, v); } + @$pb.TagNumber(16) + $core.bool hasGroupUpdate() => $_has(14); + @$pb.TagNumber(16) + void clearGroupUpdate() => clearField(16); + @$pb.TagNumber(16) + EncryptedContent_GroupUpdate ensureGroupUpdate() => $_ensure(14); } diff --git a/lib/src/model/protobuf/client/generated/messages.pbjson.dart b/lib/src/model/protobuf/client/generated/messages.pbjson.dart index 4300965..1896c58 100644 --- a/lib/src/model/protobuf/client/generated/messages.pbjson.dart +++ b/lib/src/model/protobuf/client/generated/messages.pbjson.dart @@ -106,8 +106,11 @@ const EncryptedContent$json = { {'1': 'pushKeys', '3': 11, '4': 1, '5': 11, '6': '.EncryptedContent.PushKeys', '9': 9, '10': 'pushKeys', '17': true}, {'1': 'reaction', '3': 12, '4': 1, '5': 11, '6': '.EncryptedContent.Reaction', '9': 10, '10': 'reaction', '17': true}, {'1': 'textMessage', '3': 13, '4': 1, '5': 11, '6': '.EncryptedContent.TextMessage', '9': 11, '10': 'textMessage', '17': true}, + {'1': 'groupCreate', '3': 14, '4': 1, '5': 11, '6': '.EncryptedContent.GroupCreate', '9': 12, '10': 'groupCreate', '17': true}, + {'1': 'groupJoin', '3': 15, '4': 1, '5': 11, '6': '.EncryptedContent.GroupJoin', '9': 13, '10': 'groupJoin', '17': true}, + {'1': 'groupUpdate', '3': 16, '4': 1, '5': 11, '6': '.EncryptedContent.GroupUpdate', '9': 14, '10': 'groupUpdate', '17': true}, ], - '3': [EncryptedContent_TextMessage$json, EncryptedContent_Reaction$json, EncryptedContent_MessageUpdate$json, EncryptedContent_Media$json, EncryptedContent_MediaUpdate$json, EncryptedContent_ContactRequest$json, EncryptedContent_ContactUpdate$json, EncryptedContent_PushKeys$json, EncryptedContent_FlameSync$json], + '3': [EncryptedContent_GroupCreate$json, EncryptedContent_GroupJoin$json, EncryptedContent_GroupUpdate$json, EncryptedContent_TextMessage$json, EncryptedContent_Reaction$json, EncryptedContent_MessageUpdate$json, EncryptedContent_Media$json, EncryptedContent_MediaUpdate$json, EncryptedContent_ContactRequest$json, EncryptedContent_ContactUpdate$json, EncryptedContent_PushKeys$json, EncryptedContent_FlameSync$json], '8': [ {'1': '_groupId'}, {'1': '_isDirectChat'}, @@ -121,6 +124,40 @@ const EncryptedContent$json = { {'1': '_pushKeys'}, {'1': '_reaction'}, {'1': '_textMessage'}, + {'1': '_groupCreate'}, + {'1': '_groupJoin'}, + {'1': '_groupUpdate'}, + ], +}; + +@$core.Deprecated('Use encryptedContentDescriptor instead') +const EncryptedContent_GroupCreate$json = { + '1': 'GroupCreate', + '2': [ + {'1': 'stateKey', '3': 3, '4': 1, '5': 12, '10': 'stateKey'}, + {'1': 'groupPublicKey', '3': 4, '4': 1, '5': 12, '10': 'groupPublicKey'}, + ], +}; + +@$core.Deprecated('Use encryptedContentDescriptor instead') +const EncryptedContent_GroupJoin$json = { + '1': 'GroupJoin', + '2': [ + {'1': 'groupPublicKey', '3': 4, '4': 1, '5': 12, '10': 'groupPublicKey'}, + ], +}; + +@$core.Deprecated('Use encryptedContentDescriptor instead') +const EncryptedContent_GroupUpdate$json = { + '1': 'GroupUpdate', + '2': [ + {'1': 'groupActionType', '3': 1, '4': 1, '5': 9, '10': 'groupActionType'}, + {'1': 'affectedContactId', '3': 2, '4': 1, '5': 3, '9': 0, '10': 'affectedContactId', '17': true}, + {'1': 'newGroupName', '3': 3, '4': 1, '5': 9, '9': 1, '10': 'newGroupName', '17': true}, + ], + '8': [ + {'1': '_affectedContactId'}, + {'1': '_newGroupName'}, ], }; @@ -326,47 +363,57 @@ final $typed_data.Uint8List encryptedContentDescriptor = $convert.base64Decode( 'bWVTeW5jiAEBEjsKCHB1c2hLZXlzGAsgASgLMhouRW5jcnlwdGVkQ29udGVudC5QdXNoS2V5c0' 'gJUghwdXNoS2V5c4gBARI7CghyZWFjdGlvbhgMIAEoCzIaLkVuY3J5cHRlZENvbnRlbnQuUmVh' 'Y3Rpb25IClIIcmVhY3Rpb26IAQESRAoLdGV4dE1lc3NhZ2UYDSABKAsyHS5FbmNyeXB0ZWRDb2' - '50ZW50LlRleHRNZXNzYWdlSAtSC3RleHRNZXNzYWdliAEBGqkBCgtUZXh0TWVzc2FnZRIoCg9z' - 'ZW5kZXJNZXNzYWdlSWQYASABKAlSD3NlbmRlck1lc3NhZ2VJZBISCgR0ZXh0GAIgASgJUgR0ZX' - 'h0EhwKCXRpbWVzdGFtcBgDIAEoA1IJdGltZXN0YW1wEisKDnF1b3RlTWVzc2FnZUlkGAQgASgJ' - 'SABSDnF1b3RlTWVzc2FnZUlkiAEBQhEKD19xdW90ZU1lc3NhZ2VJZBpiCghSZWFjdGlvbhIoCg' - '90YXJnZXRNZXNzYWdlSWQYASABKAlSD3RhcmdldE1lc3NhZ2VJZBIUCgVlbW9qaRgCIAEoCVIF' - 'ZW1vamkSFgoGcmVtb3ZlGAMgASgIUgZyZW1vdmUatwIKDU1lc3NhZ2VVcGRhdGUSOAoEdHlwZR' - 'gBIAEoDjIkLkVuY3J5cHRlZENvbnRlbnQuTWVzc2FnZVVwZGF0ZS5UeXBlUgR0eXBlEi0KD3Nl' - 'bmRlck1lc3NhZ2VJZBgCIAEoCUgAUg9zZW5kZXJNZXNzYWdlSWSIAQESOgoYbXVsdGlwbGVUYX' - 'JnZXRNZXNzYWdlSWRzGAMgAygJUhhtdWx0aXBsZVRhcmdldE1lc3NhZ2VJZHMSFwoEdGV4dBgE' - 'IAEoCUgBUgR0ZXh0iAEBEhwKCXRpbWVzdGFtcBgFIAEoA1IJdGltZXN0YW1wIi0KBFR5cGUSCg' - 'oGREVMRVRFEAASDQoJRURJVF9URVhUEAESCgoGT1BFTkVEEAJCEgoQX3NlbmRlck1lc3NhZ2VJ' - 'ZEIHCgVfdGV4dBqMBQoFTWVkaWESKAoPc2VuZGVyTWVzc2FnZUlkGAEgASgJUg9zZW5kZXJNZX' - 'NzYWdlSWQSMAoEdHlwZRgCIAEoDjIcLkVuY3J5cHRlZENvbnRlbnQuTWVkaWEuVHlwZVIEdHlw' - 'ZRJDChpkaXNwbGF5TGltaXRJbk1pbGxpc2Vjb25kcxgDIAEoA0gAUhpkaXNwbGF5TGltaXRJbk' - '1pbGxpc2Vjb25kc4gBARI2ChZyZXF1aXJlc0F1dGhlbnRpY2F0aW9uGAQgASgIUhZyZXF1aXJl' - 'c0F1dGhlbnRpY2F0aW9uEhwKCXRpbWVzdGFtcBgFIAEoA1IJdGltZXN0YW1wEisKDnF1b3RlTW' - 'Vzc2FnZUlkGAYgASgJSAFSDnF1b3RlTWVzc2FnZUlkiAEBEikKDWRvd25sb2FkVG9rZW4YByAB' - 'KAxIAlINZG93bmxvYWRUb2tlbogBARIpCg1lbmNyeXB0aW9uS2V5GAggASgMSANSDWVuY3J5cH' - 'Rpb25LZXmIAQESKQoNZW5jcnlwdGlvbk1hYxgJIAEoDEgEUg1lbmNyeXB0aW9uTWFjiAEBEi0K' - 'D2VuY3J5cHRpb25Ob25jZRgKIAEoDEgFUg9lbmNyeXB0aW9uTm9uY2WIAQEiMwoEVHlwZRIMCg' - 'hSRVVQTE9BRBAAEgkKBUlNQUdFEAESCQoFVklERU8QAhIHCgNHSUYQA0IdChtfZGlzcGxheUxp' - 'bWl0SW5NaWxsaXNlY29uZHNCEQoPX3F1b3RlTWVzc2FnZUlkQhAKDl9kb3dubG9hZFRva2VuQh' - 'AKDl9lbmNyeXB0aW9uS2V5QhAKDl9lbmNyeXB0aW9uTWFjQhIKEF9lbmNyeXB0aW9uTm9uY2Ua' - 'pwEKC01lZGlhVXBkYXRlEjYKBHR5cGUYASABKA4yIi5FbmNyeXB0ZWRDb250ZW50Lk1lZGlhVX' - 'BkYXRlLlR5cGVSBHR5cGUSKAoPdGFyZ2V0TWVzc2FnZUlkGAIgASgJUg90YXJnZXRNZXNzYWdl' - 'SWQiNgoEVHlwZRIMCghSRU9QRU5FRBAAEgoKBlNUT1JFRBABEhQKEERFQ1JZUFRJT05fRVJST1' - 'IQAhp4Cg5Db250YWN0UmVxdWVzdBI5CgR0eXBlGAEgASgOMiUuRW5jcnlwdGVkQ29udGVudC5D' - 'b250YWN0UmVxdWVzdC5UeXBlUgR0eXBlIisKBFR5cGUSCwoHUkVRVUVTVBAAEgoKBlJFSkVDVB' - 'ABEgoKBkFDQ0VQVBACGvABCg1Db250YWN0VXBkYXRlEjgKBHR5cGUYASABKA4yJC5FbmNyeXB0' - 'ZWRDb250ZW50LkNvbnRhY3RVcGRhdGUuVHlwZVIEdHlwZRI1ChNhdmF0YXJTdmdDb21wcmVzc2' - 'VkGAIgASgMSABSE2F2YXRhclN2Z0NvbXByZXNzZWSIAQESJQoLZGlzcGxheU5hbWUYAyABKAlI' - 'AVILZGlzcGxheU5hbWWIAQEiHwoEVHlwZRILCgdSRVFVRVNUEAASCgoGVVBEQVRFEAFCFgoUX2' - 'F2YXRhclN2Z0NvbXByZXNzZWRCDgoMX2Rpc3BsYXlOYW1lGtUBCghQdXNoS2V5cxIzCgR0eXBl' - 'GAEgASgOMh8uRW5jcnlwdGVkQ29udGVudC5QdXNoS2V5cy5UeXBlUgR0eXBlEhkKBWtleUlkGA' - 'IgASgDSABSBWtleUlkiAEBEhUKA2tleRgDIAEoDEgBUgNrZXmIAQESIQoJY3JlYXRlZEF0GAQg' - 'ASgDSAJSCWNyZWF0ZWRBdIgBASIfCgRUeXBlEgsKB1JFUVVFU1QQABIKCgZVUERBVEUQAUIICg' - 'Zfa2V5SWRCBgoEX2tleUIMCgpfY3JlYXRlZEF0GocBCglGbGFtZVN5bmMSIgoMZmxhbWVDb3Vu' - 'dGVyGAEgASgDUgxmbGFtZUNvdW50ZXISNgoWbGFzdEZsYW1lQ291bnRlckNoYW5nZRgCIAEoA1' - 'IWbGFzdEZsYW1lQ291bnRlckNoYW5nZRIeCgpiZXN0RnJpZW5kGAMgASgIUgpiZXN0RnJpZW5k' - 'QgoKCF9ncm91cElkQg8KDV9pc0RpcmVjdENoYXRCFwoVX3NlbmRlclByb2ZpbGVDb3VudGVyQh' - 'AKDl9tZXNzYWdlVXBkYXRlQggKBl9tZWRpYUIOCgxfbWVkaWFVcGRhdGVCEAoOX2NvbnRhY3RV' - 'cGRhdGVCEQoPX2NvbnRhY3RSZXF1ZXN0QgwKCl9mbGFtZVN5bmNCCwoJX3B1c2hLZXlzQgsKCV' - '9yZWFjdGlvbkIOCgxfdGV4dE1lc3NhZ2U='); + '50ZW50LlRleHRNZXNzYWdlSAtSC3RleHRNZXNzYWdliAEBEkQKC2dyb3VwQ3JlYXRlGA4gASgL' + 'Mh0uRW5jcnlwdGVkQ29udGVudC5Hcm91cENyZWF0ZUgMUgtncm91cENyZWF0ZYgBARI+Cglncm' + '91cEpvaW4YDyABKAsyGy5FbmNyeXB0ZWRDb250ZW50Lkdyb3VwSm9pbkgNUglncm91cEpvaW6I' + 'AQESRAoLZ3JvdXBVcGRhdGUYECABKAsyHS5FbmNyeXB0ZWRDb250ZW50Lkdyb3VwVXBkYXRlSA' + '5SC2dyb3VwVXBkYXRliAEBGlEKC0dyb3VwQ3JlYXRlEhoKCHN0YXRlS2V5GAMgASgMUghzdGF0' + 'ZUtleRImCg5ncm91cFB1YmxpY0tleRgEIAEoDFIOZ3JvdXBQdWJsaWNLZXkaMwoJR3JvdXBKb2' + 'luEiYKDmdyb3VwUHVibGljS2V5GAQgASgMUg5ncm91cFB1YmxpY0tleRq6AQoLR3JvdXBVcGRh' + 'dGUSKAoPZ3JvdXBBY3Rpb25UeXBlGAEgASgJUg9ncm91cEFjdGlvblR5cGUSMQoRYWZmZWN0ZW' + 'RDb250YWN0SWQYAiABKANIAFIRYWZmZWN0ZWRDb250YWN0SWSIAQESJwoMbmV3R3JvdXBOYW1l' + 'GAMgASgJSAFSDG5ld0dyb3VwTmFtZYgBAUIUChJfYWZmZWN0ZWRDb250YWN0SWRCDwoNX25ld0' + 'dyb3VwTmFtZRqpAQoLVGV4dE1lc3NhZ2USKAoPc2VuZGVyTWVzc2FnZUlkGAEgASgJUg9zZW5k' + 'ZXJNZXNzYWdlSWQSEgoEdGV4dBgCIAEoCVIEdGV4dBIcCgl0aW1lc3RhbXAYAyABKANSCXRpbW' + 'VzdGFtcBIrCg5xdW90ZU1lc3NhZ2VJZBgEIAEoCUgAUg5xdW90ZU1lc3NhZ2VJZIgBAUIRCg9f' + 'cXVvdGVNZXNzYWdlSWQaYgoIUmVhY3Rpb24SKAoPdGFyZ2V0TWVzc2FnZUlkGAEgASgJUg90YX' + 'JnZXRNZXNzYWdlSWQSFAoFZW1vamkYAiABKAlSBWVtb2ppEhYKBnJlbW92ZRgDIAEoCFIGcmVt' + 'b3ZlGrcCCg1NZXNzYWdlVXBkYXRlEjgKBHR5cGUYASABKA4yJC5FbmNyeXB0ZWRDb250ZW50Lk' + '1lc3NhZ2VVcGRhdGUuVHlwZVIEdHlwZRItCg9zZW5kZXJNZXNzYWdlSWQYAiABKAlIAFIPc2Vu' + 'ZGVyTWVzc2FnZUlkiAEBEjoKGG11bHRpcGxlVGFyZ2V0TWVzc2FnZUlkcxgDIAMoCVIYbXVsdG' + 'lwbGVUYXJnZXRNZXNzYWdlSWRzEhcKBHRleHQYBCABKAlIAVIEdGV4dIgBARIcCgl0aW1lc3Rh' + 'bXAYBSABKANSCXRpbWVzdGFtcCItCgRUeXBlEgoKBkRFTEVURRAAEg0KCUVESVRfVEVYVBABEg' + 'oKBk9QRU5FRBACQhIKEF9zZW5kZXJNZXNzYWdlSWRCBwoFX3RleHQajAUKBU1lZGlhEigKD3Nl' + 'bmRlck1lc3NhZ2VJZBgBIAEoCVIPc2VuZGVyTWVzc2FnZUlkEjAKBHR5cGUYAiABKA4yHC5Fbm' + 'NyeXB0ZWRDb250ZW50Lk1lZGlhLlR5cGVSBHR5cGUSQwoaZGlzcGxheUxpbWl0SW5NaWxsaXNl' + 'Y29uZHMYAyABKANIAFIaZGlzcGxheUxpbWl0SW5NaWxsaXNlY29uZHOIAQESNgoWcmVxdWlyZX' + 'NBdXRoZW50aWNhdGlvbhgEIAEoCFIWcmVxdWlyZXNBdXRoZW50aWNhdGlvbhIcCgl0aW1lc3Rh' + 'bXAYBSABKANSCXRpbWVzdGFtcBIrCg5xdW90ZU1lc3NhZ2VJZBgGIAEoCUgBUg5xdW90ZU1lc3' + 'NhZ2VJZIgBARIpCg1kb3dubG9hZFRva2VuGAcgASgMSAJSDWRvd25sb2FkVG9rZW6IAQESKQoN' + 'ZW5jcnlwdGlvbktleRgIIAEoDEgDUg1lbmNyeXB0aW9uS2V5iAEBEikKDWVuY3J5cHRpb25NYW' + 'MYCSABKAxIBFINZW5jcnlwdGlvbk1hY4gBARItCg9lbmNyeXB0aW9uTm9uY2UYCiABKAxIBVIP' + 'ZW5jcnlwdGlvbk5vbmNliAEBIjMKBFR5cGUSDAoIUkVVUExPQUQQABIJCgVJTUFHRRABEgkKBV' + 'ZJREVPEAISBwoDR0lGEANCHQobX2Rpc3BsYXlMaW1pdEluTWlsbGlzZWNvbmRzQhEKD19xdW90' + 'ZU1lc3NhZ2VJZEIQCg5fZG93bmxvYWRUb2tlbkIQCg5fZW5jcnlwdGlvbktleUIQCg5fZW5jcn' + 'lwdGlvbk1hY0ISChBfZW5jcnlwdGlvbk5vbmNlGqcBCgtNZWRpYVVwZGF0ZRI2CgR0eXBlGAEg' + 'ASgOMiIuRW5jcnlwdGVkQ29udGVudC5NZWRpYVVwZGF0ZS5UeXBlUgR0eXBlEigKD3RhcmdldE' + '1lc3NhZ2VJZBgCIAEoCVIPdGFyZ2V0TWVzc2FnZUlkIjYKBFR5cGUSDAoIUkVPUEVORUQQABIK' + 'CgZTVE9SRUQQARIUChBERUNSWVBUSU9OX0VSUk9SEAIaeAoOQ29udGFjdFJlcXVlc3QSOQoEdH' + 'lwZRgBIAEoDjIlLkVuY3J5cHRlZENvbnRlbnQuQ29udGFjdFJlcXVlc3QuVHlwZVIEdHlwZSIr' + 'CgRUeXBlEgsKB1JFUVVFU1QQABIKCgZSRUpFQ1QQARIKCgZBQ0NFUFQQAhrwAQoNQ29udGFjdF' + 'VwZGF0ZRI4CgR0eXBlGAEgASgOMiQuRW5jcnlwdGVkQ29udGVudC5Db250YWN0VXBkYXRlLlR5' + 'cGVSBHR5cGUSNQoTYXZhdGFyU3ZnQ29tcHJlc3NlZBgCIAEoDEgAUhNhdmF0YXJTdmdDb21wcm' + 'Vzc2VkiAEBEiUKC2Rpc3BsYXlOYW1lGAMgASgJSAFSC2Rpc3BsYXlOYW1liAEBIh8KBFR5cGUS' + 'CwoHUkVRVUVTVBAAEgoKBlVQREFURRABQhYKFF9hdmF0YXJTdmdDb21wcmVzc2VkQg4KDF9kaX' + 'NwbGF5TmFtZRrVAQoIUHVzaEtleXMSMwoEdHlwZRgBIAEoDjIfLkVuY3J5cHRlZENvbnRlbnQu' + 'UHVzaEtleXMuVHlwZVIEdHlwZRIZCgVrZXlJZBgCIAEoA0gAUgVrZXlJZIgBARIVCgNrZXkYAy' + 'ABKAxIAVIDa2V5iAEBEiEKCWNyZWF0ZWRBdBgEIAEoA0gCUgljcmVhdGVkQXSIAQEiHwoEVHlw' + 'ZRILCgdSRVFVRVNUEAASCgoGVVBEQVRFEAFCCAoGX2tleUlkQgYKBF9rZXlCDAoKX2NyZWF0ZW' + 'RBdBqHAQoJRmxhbWVTeW5jEiIKDGZsYW1lQ291bnRlchgBIAEoA1IMZmxhbWVDb3VudGVyEjYK' + 'Fmxhc3RGbGFtZUNvdW50ZXJDaGFuZ2UYAiABKANSFmxhc3RGbGFtZUNvdW50ZXJDaGFuZ2USHg' + 'oKYmVzdEZyaWVuZBgDIAEoCFIKYmVzdEZyaWVuZEIKCghfZ3JvdXBJZEIPCg1faXNEaXJlY3RD' + 'aGF0QhcKFV9zZW5kZXJQcm9maWxlQ291bnRlckIQCg5fbWVzc2FnZVVwZGF0ZUIICgZfbWVkaW' + 'FCDgoMX21lZGlhVXBkYXRlQhAKDl9jb250YWN0VXBkYXRlQhEKD19jb250YWN0UmVxdWVzdEIM' + 'CgpfZmxhbWVTeW5jQgsKCV9wdXNoS2V5c0ILCglfcmVhY3Rpb25CDgoMX3RleHRNZXNzYWdlQg' + '4KDF9ncm91cENyZWF0ZUIMCgpfZ3JvdXBKb2luQg4KDF9ncm91cFVwZGF0ZQ=='); diff --git a/lib/src/model/protobuf/client/groups.proto b/lib/src/model/protobuf/client/groups.proto index 140d754..813563c 100644 --- a/lib/src/model/protobuf/client/groups.proto +++ b/lib/src/model/protobuf/client/groups.proto @@ -1,10 +1,16 @@ syntax = "proto3"; // Stored encrypted on the server in the members columns. -message GroupState { +message EncryptedGroupState { repeated int64 memberIds = 1; repeated int64 adminIds = 2; string groupName = 3; optional int64 deleteMessagesAfterMilliseconds = 4; - bytes _padding = 5; + bytes padding = 5; +} + +message EncryptedGroupStateEnvelop { + bytes nonce = 1; + bytes encryptedGroupState = 2; + bytes mac = 3; } \ No newline at end of file diff --git a/lib/src/model/protobuf/client/messages.proto b/lib/src/model/protobuf/client/messages.proto index 29dc4e1..a4ab602 100644 --- a/lib/src/model/protobuf/client/messages.proto +++ b/lib/src/model/protobuf/client/messages.proto @@ -44,31 +44,26 @@ message EncryptedContent { optional PushKeys pushKeys = 11; optional Reaction reaction = 12; optional TextMessage textMessage = 13; - optional NewGroup newGroup = 14; - optional JoinGroup joinGroup = 15; + optional GroupCreate groupCreate = 14; + optional GroupJoin groupJoin = 15; optional GroupUpdate groupUpdate = 16; - message NewGroup { + message GroupCreate { // key for the state stored on the server - string groupId = 1; - string stateId = 2; bytes stateKey = 3; bytes groupPublicKey = 4; } - message JoinGroup { + message GroupJoin { // key for the state stored on the server - string groupId = 1; bytes groupPublicKey = 4; } message GroupUpdate { - optional int64 removedAdmin = 1; - optional int64 removedUser = 2; - optional int64 groupName = 3; - optional int64 deleteMessagesAfterMilliseconds = 4; - bytes stateKey = 5; + string groupActionType = 1; // GroupActionType.name + optional int64 affectedContactId = 2; + optional string newGroupName = 3; } message TextMessage { diff --git a/lib/src/services/api.service.dart b/lib/src/services/api.service.dart index 7de2f52..4c40c3b 100644 --- a/lib/src/services/api.service.dart +++ b/lib/src/services/api.service.dart @@ -5,6 +5,7 @@ import 'dart:collection'; import 'dart:convert'; import 'dart:io'; import 'dart:math'; +import 'dart:ui' as ui; import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:drift/drift.dart'; @@ -283,6 +284,10 @@ class ApiService { request.v0.seq = seq; final requestBytes = request.writeToBuffer(); + Log.info( + 'Sending ${requestBytes.length} bytes to the server via WebSocket.', + ); + if (ensureRetransmission) { await addToRetransmissionBuffer(seq, requestBytes); } @@ -467,6 +472,7 @@ class ApiService { ..signedPrekey = signedPreKey.getKeyPair().publicKey.serialize() ..signedPrekeySignature = signedPreKey.signature ..signedPrekeyId = Int64(signedPreKey.id) + ..langCode = ui.PlatformDispatcher.instance.locale.languageCode ..isIos = Platform.isIOS; if (inviteCode != null && inviteCode != '') { diff --git a/lib/src/services/api/server_messages/contact.server_messages.dart b/lib/src/services/api/client2client/contact.c2c.dart similarity index 100% rename from lib/src/services/api/server_messages/contact.server_messages.dart rename to lib/src/services/api/client2client/contact.c2c.dart diff --git a/lib/src/services/api/client2client/groups.c2c.dart b/lib/src/services/api/client2client/groups.c2c.dart new file mode 100644 index 0000000..0f7a040 --- /dev/null +++ b/lib/src/services/api/client2client/groups.c2c.dart @@ -0,0 +1,78 @@ +import 'dart:async'; + +import 'package:drift/drift.dart'; +import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart'; +import 'package:twonly/globals.dart'; +import 'package:twonly/src/database/tables/groups.table.dart'; +import 'package:twonly/src/database/twonly.db.dart'; +import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart'; +import 'package:twonly/src/services/group.services.dart'; +import 'package:twonly/src/utils/log.dart'; + +Future handleGroupCreate( + int fromUserId, + String groupId, + EncryptedContent_GroupCreate newGroup, +) async { + // 1. Store the new group -> e.g. store the stateKey and groupPublicKey + // 2. Call function that should fetch all jobs + // 1. This function is also called in the main function, in case the state stored on the server could not be loaded + // 2. This function will also send the GroupJoin to all members -> so they get there public key + // 3. Finished + + final myGroupKey = generateIdentityKeyPair(); + + // Group state is joinedGroup -> As the current state has not yet been downloaded. + final group = await twonlyDB.groupsDao.createNewGroup( + GroupsCompanion( + groupId: Value(groupId), + stateVersionId: const Value(1), + stateEncryptionKey: Value(Uint8List.fromList(newGroup.stateKey)), + myGroupPrivateKey: Value(myGroupKey.getPrivateKey().serialize()), + ), + ); + + if (group == null) { + Log.error( + 'Could not create new group. Probably because the group already existed.', + ); + return; + } + + final user = await twonlyDB.contactsDao + .getContactByUserId(fromUserId) + .getSingleOrNull(); + if (user == null) { + // Only contacts can invite other contacts, so this can (via the UI) not happen. + Log.error( + 'User is not a contact. Aborting.', + ); + return; + } + + await twonlyDB.groupsDao.insertGroupMember( + GroupMembersCompanion( + groupId: Value(groupId), + contactId: Value(fromUserId), + memberState: const Value( + MemberState.admin, // is the group creator, so must be admin... + ), + groupPublicKey: Value(Uint8List.fromList(newGroup.groupPublicKey)), + ), + ); + + // can be done in the background -> websocket message can be ACK + unawaited(fetchGroupStatesForUnjoinedGroups()); +} + +Future handleGroupUpdate( + int fromUserId, + String groupId, + EncryptedContent_GroupUpdate update, +) async {} + +Future handleGroupJoin( + int fromUserId, + String groupId, + EncryptedContent_GroupJoin join, +) async {} diff --git a/lib/src/services/api/server_messages/media.server_messages.dart b/lib/src/services/api/client2client/media.c2c.dart similarity index 100% rename from lib/src/services/api/server_messages/media.server_messages.dart rename to lib/src/services/api/client2client/media.c2c.dart diff --git a/lib/src/services/api/server_messages/messages.server_messages.dart b/lib/src/services/api/client2client/messages.c2c.dart similarity index 100% rename from lib/src/services/api/server_messages/messages.server_messages.dart rename to lib/src/services/api/client2client/messages.c2c.dart diff --git a/lib/src/services/api/server_messages/prekeys.server_messages.dart b/lib/src/services/api/client2client/prekeys.c2c.dart similarity index 100% rename from lib/src/services/api/server_messages/prekeys.server_messages.dart rename to lib/src/services/api/client2client/prekeys.c2c.dart diff --git a/lib/src/services/api/server_messages/pushkeys.server_messages.dart b/lib/src/services/api/client2client/pushkeys.c2c.dart similarity index 100% rename from lib/src/services/api/server_messages/pushkeys.server_messages.dart rename to lib/src/services/api/client2client/pushkeys.c2c.dart diff --git a/lib/src/services/api/server_messages/reaction.server_message.dart b/lib/src/services/api/client2client/reaction.c2c.dart similarity index 100% rename from lib/src/services/api/server_messages/reaction.server_message.dart rename to lib/src/services/api/client2client/reaction.c2c.dart diff --git a/lib/src/services/api/server_messages/text_message.server_messages.dart b/lib/src/services/api/client2client/text_message.c2c.dart similarity index 100% rename from lib/src/services/api/server_messages/text_message.server_messages.dart rename to lib/src/services/api/client2client/text_message.c2c.dart diff --git a/lib/src/services/api/server_messages.dart b/lib/src/services/api/server_messages.dart index e8e6ad6..e9c8ddc 100644 --- a/lib/src/services/api/server_messages.dart +++ b/lib/src/services/api/server_messages.dart @@ -11,14 +11,15 @@ import 'package:twonly/src/model/protobuf/api/websocket/client_to_server.pb.dart import 'package:twonly/src/model/protobuf/api/websocket/server_to_client.pb.dart' as server; import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart'; +import 'package:twonly/src/services/api/client2client/groups.c2c.dart'; import 'package:twonly/src/services/api/messages.dart'; -import 'package:twonly/src/services/api/server_messages/contact.server_messages.dart'; -import 'package:twonly/src/services/api/server_messages/media.server_messages.dart'; -import 'package:twonly/src/services/api/server_messages/messages.server_messages.dart'; -import 'package:twonly/src/services/api/server_messages/prekeys.server_messages.dart'; -import 'package:twonly/src/services/api/server_messages/pushkeys.server_messages.dart'; -import 'package:twonly/src/services/api/server_messages/reaction.server_message.dart'; -import 'package:twonly/src/services/api/server_messages/text_message.server_messages.dart'; +import 'package:twonly/src/services/api/client2client/contact.c2c.dart'; +import 'package:twonly/src/services/api/client2client/media.c2c.dart'; +import 'package:twonly/src/services/api/client2client/messages.c2c.dart'; +import 'package:twonly/src/services/api/client2client/prekeys.c2c.dart'; +import 'package:twonly/src/services/api/client2client/pushkeys.c2c.dart'; +import 'package:twonly/src/services/api/client2client/reaction.c2c.dart'; +import 'package:twonly/src/services/api/client2client/text_message.c2c.dart'; import 'package:twonly/src/services/signal/encryption.signal.dart'; import 'package:twonly/src/utils/log.dart'; import 'package:twonly/src/utils/misc.dart'; @@ -36,7 +37,7 @@ Future handleServerMessage(server.ServerToClient msg) async { } else if (msg.v0.hasNewMessage()) { final body = Uint8List.fromList(msg.v0.newMessage.body); final fromUserId = msg.v0.newMessage.fromUserId.toInt(); - await handleNewMessage(fromUserId, body); + await handleClient2ClientMessage(fromUserId, body); } else { Log.error('Unknown server message: $msg'); } @@ -55,7 +56,7 @@ DateTime lastPushKeyRequest = DateTime.now().subtract(const Duration(hours: 1)); Mutex protectReceiptCheck = Mutex(); -Future handleNewMessage(int fromUserId, Uint8List body) async { +Future handleClient2ClientMessage(int fromUserId, Uint8List body) async { final message = Message.fromBuffer(body); final receiptId = message.receiptId; @@ -205,6 +206,33 @@ Future handleEncryptedMessage( return null; } + if (content.hasGroupUpdate()) { + await handleGroupUpdate( + fromUserId, + content.groupId, + content.groupUpdate, + ); + return null; + } + + if (content.hasGroupCreate()) { + await handleGroupCreate( + fromUserId, + content.groupId, + content.groupCreate, + ); + return null; + } + + if (content.hasGroupJoin()) { + await handleGroupJoin( + fromUserId, + content.groupId, + content.groupJoin, + ); + return null; + } + /// Verify that the user is (still) in that group... if (!await twonlyDB.groupsDao.isContactInGroup(fromUserId, content.groupId)) { if (getUUIDforDirectChat(gUser.userId, fromUserId) == content.groupId) { diff --git a/lib/src/services/group.services.dart b/lib/src/services/group.services.dart new file mode 100644 index 0000000..c144208 --- /dev/null +++ b/lib/src/services/group.services.dart @@ -0,0 +1,179 @@ +import 'dart:math'; +import 'package:cryptography_flutter_plus/cryptography_flutter_plus.dart'; +import 'package:cryptography_plus/cryptography_plus.dart'; +import 'package:drift/drift.dart' show Value; +import 'package:fixnum/fixnum.dart'; +import 'package:hashlib/random.dart'; +import 'package:http/http.dart' as http; +import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart'; +import 'package:twonly/globals.dart'; +import 'package:twonly/src/database/tables/groups.table.dart'; +import 'package:twonly/src/database/twonly.db.dart'; +import 'package:twonly/src/model/protobuf/api/http/http_requests.pb.dart'; +import 'package:twonly/src/model/protobuf/client/generated/groups.pb.dart'; +import 'package:twonly/src/model/protobuf/client/generated/messages.pbserver.dart'; +import 'package:twonly/src/services/api/messages.dart'; +import 'package:twonly/src/utils/log.dart'; +import 'package:twonly/src/utils/misc.dart'; + +String getGroupStateUrl() { + return 'http${apiService.apiSecure}://${apiService.apiHost}/api/group/state'; +} + +Future createNewGroup(String groupName, List members) async { + // First: Upload new State to the server..... + // if (groupName) return; + + final groupId = uuid.v4(); + + final memberIds = members.map((x) => Int64(x.userId)).toList(); + + final groupState = EncryptedGroupState( + memberIds: memberIds, + adminIds: [Int64(gUser.userId)], + groupName: groupName, + deleteMessagesAfterMilliseconds: + Int64(defaultDeleteMessagesAfterMilliseconds), + padding: List.generate(Random().nextInt(80), (_) => 0), + ); + + final stateEncryptionKey = getRandomUint8List(32); + final chacha20 = FlutterChacha20.poly1305Aead(); + final encryptionNonce = chacha20.newNonce(); + + final secretBox = await chacha20.encrypt( + groupState.writeToBuffer(), + secretKey: SecretKey(stateEncryptionKey), + nonce: encryptionNonce, + ); + + final encryptedGroupState = EncryptedGroupStateEnvelop( + nonce: encryptionNonce, + encryptedGroupState: secretBox.cipherText, + mac: secretBox.mac.bytes, + ); + + final myGroupKey = generateIdentityKeyPair(); + + { + // Upload the group state, if this fails, the group can not be created. + + final newGroupState = NewGroupState( + groupId: groupId, + versionId: Int64(1), + encryptedGroupState: encryptedGroupState.writeToBuffer(), + publicKey: myGroupKey.getPublicKey().serialize(), + ); + + final response = await http + .post( + Uri.parse(getGroupStateUrl()), + body: newGroupState.writeToBuffer(), + ) + .timeout(const Duration(seconds: 10)); + + if (response.statusCode != 200) { + Log.error( + 'Could not upload group state. Got status code ${response.statusCode} from server.', + ); + return false; + } + } + + final group = await twonlyDB.groupsDao.createNewGroup( + GroupsCompanion( + groupId: Value(groupId), + groupName: Value(groupName), + isGroupAdmin: const Value(true), + stateEncryptionKey: Value(stateEncryptionKey), + stateVersionId: const Value(1), + myGroupPrivateKey: Value(myGroupKey.getPrivateKey().serialize()), + ), + ); + + if (group == null) { + Log.error('Could not insert group into database.'); + return false; + } + + Log.info('Created new group: ${group.groupId}'); + + for (final member in members) { + await twonlyDB.groupsDao.insertGroupMember( + GroupMembersCompanion( + groupId: Value(group.groupId), + contactId: Value(member.userId), + memberState: const Value(MemberState.normal), + ), + ); + } + + await twonlyDB.groupsDao.insertGroupAction( + GroupHistoriesCompanion( + groupId: Value(groupId), + type: const Value(GroupActionType.createdGroup), + ), + ); + + // Notify members about the new group :) + + await sendCipherTextToGroup( + group.groupId, + EncryptedContent( + groupCreate: EncryptedContent_GroupCreate( + stateKey: stateEncryptionKey, + groupPublicKey: myGroupKey.getPublicKey().serialize(), + ), + ), + null, + ); + + return true; +} + +Future fetchGroupStatesForUnjoinedGroups() async { + final groups = await twonlyDB.groupsDao.getAllNotJoinedGroups(); + + for (final group in groups) {} +} + +Future fetchGroupState(Group group) async { + try { + final response = await http + .get( + Uri.parse('${getGroupStateUrl()}/${group.groupId}'), + ) + .timeout(const Duration(seconds: 10)); + + if (response.statusCode != 200) { + Log.error( + 'Could not load group state. Got status code ${response.statusCode} from server.', + ); + return null; + } + + final groupStateServer = GroupState.fromBuffer(response.bodyBytes); + final envelope = EncryptedGroupStateEnvelop.fromBuffer( + groupStateServer.encryptedGroupState); + final chacha20 = FlutterChacha20.poly1305Aead(); + + final secretBox = SecretBox(envelope.encryptedGroupState, + nonce: envelope.nonce, mac: Mac(envelope.mac)); + + final encryptedGroupStateRaw = await chacha20.decrypt(secretBox, + secretKey: SecretKey(group.stateEncryptionKey!)); + + final encryptedGroupState = + EncryptedGroupState.fromBuffer(encryptedGroupStateRaw); + + encryptedGroupState.adminIds; + encryptedGroupState.memberIds; + encryptedGroupState.groupName; + encryptedGroupState.deleteMessagesAfterMilliseconds; + encryptedGroupState.deleteMessagesAfterMilliseconds; + groupStateServer.versionId; + } catch (e) { + Log.error(e); + return null; + } +} diff --git a/lib/src/views/chats/add_new_user.view.dart b/lib/src/views/chats/add_new_user.view.dart index 7ab04b5..c24ffec 100644 --- a/lib/src/views/chats/add_new_user.view.dart +++ b/lib/src/views/chats/add_new_user.view.dart @@ -277,9 +277,8 @@ class ContactsListView extends StatelessWidget { itemCount: contacts.length, itemBuilder: (context, index) { final contact = contacts[index]; - final displayName = getContactDisplayName(contact); return ListTile( - title: Text(displayName), + title: Text(substringBy(contact.username, 25)), leading: AvatarIcon(contact: contact), trailing: Row( mainAxisSize: MainAxisSize.min, diff --git a/lib/src/views/chats/start_new_chat.view.dart b/lib/src/views/chats/start_new_chat.view.dart index c1670a6..046e492 100644 --- a/lib/src/views/chats/start_new_chat.view.dart +++ b/lib/src/views/chats/start_new_chat.view.dart @@ -11,6 +11,7 @@ import 'package:twonly/src/views/chats/chat_messages.view.dart'; import 'package:twonly/src/views/components/avatar_icon.component.dart'; import 'package:twonly/src/views/components/flame.dart'; import 'package:twonly/src/views/components/user_context_menu.component.dart'; +import 'package:twonly/src/views/groups/group_create_select_members.view.dart'; class StartNewChatView extends StatefulWidget { const StartNewChatView({super.key}); @@ -116,11 +117,10 @@ class UserList extends StatelessWidget { Widget build(BuildContext context) { return ListView.builder( restorationId: 'new_message_users_list', - itemCount: users.length + 2, + itemCount: users.length + 3, itemBuilder: (BuildContext context, int i) { - if (i == 0) { + if (i == 1) { return ListTile( - key: const Key('add_new_contact'), title: Text(context.lang.startNewChatNewContact), leading: const CircleAvatar( child: FaIcon( @@ -138,10 +138,29 @@ class UserList extends StatelessWidget { }, ); } - if (i == 1) { + if (i == 0) { + return ListTile( + title: Text(context.lang.newGroup), + leading: const CircleAvatar( + child: FaIcon( + FontAwesomeIcons.userGroup, + size: 13, + ), + ), + onTap: () async { + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const GroupCreateSelectMembersView(), + ), + ); + }, + ); + } + if (i == 2) { return const Divider(); } - final user = users[i - 2]; + final user = users[i - 3]; return UserContextMenu( key: Key(user.userId.toString()), contact: user, diff --git a/lib/src/views/groups/group_create_select_group_name.view.dart b/lib/src/views/groups/group_create_select_group_name.view.dart new file mode 100644 index 0000000..7c4b81c --- /dev/null +++ b/lib/src/views/groups/group_create_select_group_name.view.dart @@ -0,0 +1,128 @@ +import 'package:flutter/material.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:twonly/src/database/daos/contacts.dao.dart'; +import 'package:twonly/src/database/twonly.db.dart'; +import 'package:twonly/src/services/group.services.dart'; +import 'package:twonly/src/utils/misc.dart'; +import 'package:twonly/src/views/components/avatar_icon.component.dart'; +import 'package:twonly/src/views/components/flame.dart'; +import 'package:twonly/src/views/components/user_context_menu.component.dart'; + +class GroupCreateSelectGroupNameView extends StatefulWidget { + const GroupCreateSelectGroupNameView({ + required this.selectedUsers, + super.key, + }); + + final List selectedUsers; + + @override + State createState() => + _GroupCreateSelectGroupNameViewState(); +} + +class _GroupCreateSelectGroupNameViewState + extends State { + final TextEditingController textFieldGroupName = TextEditingController(); + + bool _isLoading = false; + + Future _createNewGroup() async { + setState(() { + _isLoading = true; + }); + + final wasSuccess = + await createNewGroup(textFieldGroupName.text, widget.selectedUsers); + if (wasSuccess) { + // POP + } + + setState(() { + _isLoading = false; + }); + } + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () => FocusScope.of(context).unfocus(), + child: Scaffold( + appBar: AppBar( + title: Text(context.lang.selectGroupName), + ), + floatingActionButton: FilledButton.icon( + onPressed: (textFieldGroupName.text.isEmpty || _isLoading) + ? null + : _createNewGroup, + label: Text(context.lang.createGroup), + icon: _isLoading + ? const SizedBox( + width: 15, + height: 15, + child: CircularProgressIndicator( + strokeWidth: 1, + ), + ) + : const FaIcon(FontAwesomeIcons.penToSquare), + ), + body: SafeArea( + child: Padding( + padding: + const EdgeInsets.only(bottom: 40, left: 10, top: 20, right: 10), + child: Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10), + child: TextField( + onChanged: (_) async { + setState(() {}); + }, + autofocus: true, + controller: textFieldGroupName, + decoration: getInputDecoration( + context, + context.lang.groupNameInput, + ), + ), + ), + const SizedBox(height: 10), + ListTile( + title: Text(context.lang.groupMembers), + ), + Expanded( + child: ListView.builder( + restorationId: 'new_message_users_list', + itemCount: widget.selectedUsers.length, + itemBuilder: (BuildContext context, int i) { + final user = widget.selectedUsers[i]; + return UserContextMenu( + key: GlobalKey(), + contact: user, + child: ListTile( + title: Row( + children: [ + Text(getContactDisplayName(user)), + FlameCounterWidget( + contactId: user.userId, + prefix: true, + ), + ], + ), + leading: AvatarIcon( + contact: user, + fontSize: 13, + ), + ), + ); + }, + ), + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/src/views/groups/group_create_select_members.view.dart b/lib/src/views/groups/group_create_select_members.view.dart new file mode 100644 index 0000000..2340746 --- /dev/null +++ b/lib/src/views/groups/group_create_select_members.view.dart @@ -0,0 +1,250 @@ +import 'dart:async'; +import 'dart:collection'; +import 'package:flutter/material.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:twonly/globals.dart'; +import 'package:twonly/src/database/daos/contacts.dao.dart'; +import 'package:twonly/src/database/twonly.db.dart'; +import 'package:twonly/src/utils/misc.dart'; +import 'package:twonly/src/views/components/avatar_icon.component.dart'; +import 'package:twonly/src/views/components/flame.dart'; +import 'package:twonly/src/views/components/user_context_menu.component.dart'; +import 'package:twonly/src/views/groups/group_create_select_group_name.view.dart'; + +class GroupCreateSelectMembersView extends StatefulWidget { + const GroupCreateSelectMembersView({super.key}); + @override + State createState() => _StartNewChatView(); +} + +class _StartNewChatView extends State { + List contacts = []; + List allContacts = []; + final TextEditingController searchUserName = TextEditingController(); + late StreamSubscription> contactSub; + + final HashSet selectedUsers = HashSet(); + + @override + void initState() { + super.initState(); + + final stream = twonlyDB.contactsDao.watchAllAcceptedContacts(); + + contactSub = stream.listen((update) async { + update.sort( + (a, b) => getContactDisplayName(a).compareTo(getContactDisplayName(b)), + ); + setState(() { + allContacts = update; + }); + await filterUsers(); + }); + } + + @override + void dispose() { + unawaited(contactSub.cancel()); + super.dispose(); + } + + Future filterUsers() async { + if (searchUserName.value.text.isEmpty) { + setState(() { + contacts = allContacts; + }); + return; + } + final usersFiltered = allContacts + .where( + (user) => getContactDisplayName(user) + .toLowerCase() + .contains(searchUserName.value.text.toLowerCase()), + ) + .toList(); + setState(() { + contacts = usersFiltered; + }); + } + + void toggleSelectedUser(int userId) { + if (!selectedUsers.contains(userId)) { + selectedUsers.add(userId); + } else { + selectedUsers.remove(userId); + } + setState(() {}); + } + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () => FocusScope.of(context).unfocus(), + child: Scaffold( + appBar: AppBar( + title: Text(context.lang.selectMembers), + ), + floatingActionButton: FilledButton.icon( + onPressed: selectedUsers.isEmpty + ? null + : () async { + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) => GroupCreateSelectGroupNameView( + selectedUsers: allContacts + .where((t) => selectedUsers.contains(t.userId)) + .toList(), + ), + ), + ); + }, + label: Text(context.lang.next), + icon: const FaIcon(FontAwesomeIcons.penToSquare), + ), + body: SafeArea( + child: Padding( + padding: + const EdgeInsets.only(bottom: 40, left: 10, top: 20, right: 10), + child: Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10), + child: TextField( + onChanged: (_) async { + await filterUsers(); + }, + controller: searchUserName, + decoration: getInputDecoration( + context, + context.lang.shareImageSearchAllContacts, + ), + ), + ), + const SizedBox(height: 10), + Expanded( + child: ListView.builder( + restorationId: 'new_message_users_list', + itemCount: + contacts.length + (selectedUsers.isEmpty ? 0 : 2), + itemBuilder: (BuildContext context, int i) { + if (selectedUsers.isNotEmpty) { + final selected = selectedUsers.toList(); + if (i == 0) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 18), + constraints: const BoxConstraints( + maxHeight: 150, + ), + child: SingleChildScrollView( + child: LayoutBuilder( + builder: (context, constraints) { + // Wrap will use the available width from constraints.maxWidth + return Wrap( + spacing: 8, + children: selected.map((w) { + return _Chip( + contact: allContacts + .firstWhere((t) => t.userId == w), + onTap: toggleSelectedUser, + ); + }).toList(), + ); + }), + ), + ); + } + if (i == 1) { + return const Divider(); + } + i -= 2; + } + final user = contacts[i]; + return UserContextMenu( + key: GlobalKey(), + contact: user, + child: ListTile( + title: Row( + children: [ + Text(getContactDisplayName(user)), + FlameCounterWidget( + contactId: user.userId, + prefix: true, + ), + ], + ), + leading: AvatarIcon( + contact: user, + fontSize: 13, + ), + trailing: Checkbox( + value: selectedUsers.contains(user.userId), + side: WidgetStateBorderSide.resolveWith( + (states) { + if (states.contains(WidgetState.selected)) { + return const BorderSide(width: 0); + } + return BorderSide( + color: Theme.of(context).colorScheme.outline, + ); + }, + ), + onChanged: (bool? value) { + toggleSelectedUser(user.userId); + }, + ), + onTap: () { + toggleSelectedUser(user.userId); + }, + ), + ); + }, + ), + ), + ], + ), + ), + ), + ), + ); + } +} + +class _Chip extends StatelessWidget { + const _Chip({ + required this.contact, + required this.onTap, + }); + final Contact contact; + final void Function(int) onTap; + + @override + Widget build(BuildContext context) { + return Chip( + key: GlobalKey(), + avatar: AvatarIcon( + contact: contact, + fontSize: 10, + ), + label: GestureDetector( + onTap: () => onTap(contact.userId), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + getContactDisplayName(contact), + style: const TextStyle(fontSize: 14), + overflow: TextOverflow.ellipsis, + ), + const SizedBox(width: 15), + const FaIcon( + FontAwesomeIcons.xmark, + color: Colors.grey, + size: 12, + ) + ], + ), + ), + ); + } +} diff --git a/scripts/generate_proto.sh b/scripts/generate_proto.sh index 3824b77..b842220 100755 --- a/scripts/generate_proto.sh +++ b/scripts/generate_proto.sh @@ -13,20 +13,19 @@ CLIENT_DIR="./lib/src/model/protobuf/client/" protoc --proto_path="$CLIENT_DIR" --dart_out="$GENERATED_DIR" "backup.proto" protoc --proto_path="$CLIENT_DIR" --dart_out="$GENERATED_DIR" "messages.proto" +protoc --proto_path="$CLIENT_DIR" --dart_out="$GENERATED_DIR" "groups.proto" protoc --proto_path="$CLIENT_DIR" --dart_out="$GENERATED_DIR" "push_notification.proto" protoc --proto_path="$CLIENT_DIR" --swift_out="./ios/NotificationService/" "push_notification.proto" -exit - # Definitions for the Server API -SRC_DIR="../twonly-server/twonly/src/" +SRC_DIR="../twonly-server/twonly-api/src/" DST_DIR="$(pwd)/lib/src/model/protobuf/" -mkdir $DST_DIR +mkdir $DST_DIR &>/dev/null ORIGINAL_DIR=$(pwd) @@ -37,9 +36,7 @@ cd "$SRC_DIR" || { for proto_file in "api/"**/*.proto; do if [[ -f "$proto_file" ]]; then - # Run the protoc command protoc --proto_path="." --dart_out="$DST_DIR" "$proto_file" - echo "Processed: $proto_file" else echo "No .proto files found in $SRC_DIR" fi @@ -50,4 +47,4 @@ cd "$ORIGINAL_DIR" || { exit 1 } -echo "Finished processing .proto files." \ No newline at end of file +echo "Finished processing .proto files :)" \ No newline at end of file