starting with #227

This commit is contained in:
otsmr 2025-10-19 02:45:17 +02:00
parent 90e6aa6598
commit a4ccefec75
131 changed files with 14355 additions and 1435 deletions

6
.gitmodules vendored
View file

@ -1,9 +1,3 @@
[submodule "dependencies/flutter_secure_storage"]
path = dependencies/flutter_secure_storage
url = https://github.com/juliansteenbakker/flutter_secure_storage
[submodule "dependencies/flutter_zxing"]
path = dependencies/flutter_zxing
url = https://github.com/khoren93/flutter_zxing.git
[submodule "dependencies/flutter-pie-menu"]
path = dependencies/flutter-pie-menu
url = https://github.com/otsmr/flutter-pie-menu.git

View file

@ -10,4 +10,5 @@ targets:
drift_dev:
options:
databases:
twonly_database: lib/src/database/twonly_database.dart
twonly_db: lib/src/database/twonly.db.dart
twonly_database: lib/src/database/twonly_database_old.dart

@ -1 +0,0 @@
Subproject commit 22df3f2ab9ad71db60526668578a5309b3cc84ef

@ -1 +0,0 @@
Subproject commit 71b75a36f35f2ce945998e20c6c6aa1820babfc6

View file

@ -103,7 +103,7 @@ enum PushKind: SwiftProtobuf.Enum, Swift.CaseIterable {
}
struct EncryptedPushNotification: @unchecked Sendable {
struct EncryptedPushNotification: Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
@ -195,7 +195,7 @@ struct PushUser: Sendable {
fileprivate var _lastMessageID: Int64? = nil
}
struct PushKey: @unchecked Sendable {
struct PushKey: Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
@ -214,32 +214,12 @@ struct PushKey: @unchecked Sendable {
// MARK: - Code below here is support for the SwiftProtobuf runtime.
extension PushKind: SwiftProtobuf._ProtoNameProviding {
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
0: .same(proto: "reaction"),
1: .same(proto: "response"),
2: .same(proto: "text"),
3: .same(proto: "video"),
4: .same(proto: "twonly"),
5: .same(proto: "image"),
6: .same(proto: "contactRequest"),
7: .same(proto: "acceptRequest"),
8: .same(proto: "storedMediaFile"),
9: .same(proto: "testNotification"),
10: .same(proto: "reopenedMedia"),
11: .same(proto: "reactionToVideo"),
12: .same(proto: "reactionToText"),
13: .same(proto: "reactionToImage"),
]
static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0reaction\0\u{1}response\0\u{1}text\0\u{1}video\0\u{1}twonly\0\u{1}image\0\u{1}contactRequest\0\u{1}acceptRequest\0\u{1}storedMediaFile\0\u{1}testNotification\0\u{1}reopenedMedia\0\u{1}reactionToVideo\0\u{1}reactionToText\0\u{1}reactionToImage\0")
}
extension EncryptedPushNotification: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = "EncryptedPushNotification"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "keyId"),
2: .same(proto: "nonce"),
3: .same(proto: "ciphertext"),
4: .same(proto: "mac"),
]
static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}keyId\0\u{1}nonce\0\u{1}ciphertext\0\u{1}mac\0")
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
@ -284,11 +264,7 @@ extension EncryptedPushNotification: SwiftProtobuf.Message, SwiftProtobuf._Messa
extension PushNotification: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = "PushNotification"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "kind"),
2: .same(proto: "messageId"),
3: .same(proto: "reactionContent"),
]
static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}kind\0\u{1}messageId\0\u{1}reactionContent\0")
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
@ -332,9 +308,7 @@ extension PushNotification: SwiftProtobuf.Message, SwiftProtobuf._MessageImpleme
extension PushUsers: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = "PushUsers"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "users"),
]
static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}users\0")
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
@ -364,13 +338,7 @@ extension PushUsers: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementation
extension PushUser: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = "PushUser"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "userId"),
2: .same(proto: "displayName"),
3: .same(proto: "blocked"),
4: .same(proto: "lastMessageId"),
5: .same(proto: "pushKeys"),
]
static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}userId\0\u{1}displayName\0\u{1}blocked\0\u{1}lastMessageId\0\u{1}pushKeys\0")
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
@ -424,11 +392,7 @@ extension PushUser: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB
extension PushKey: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = "PushKey"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "id"),
2: .same(proto: "key"),
3: .same(proto: "createdAtUnixTimestamp"),
]
static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}id\0\u{1}key\0\u{1}createdAtUnixTimestamp\0")
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {

View file

@ -1,11 +1,11 @@
import 'package:camera/camera.dart';
import 'package:twonly/src/database/twonly_database.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/services/api.service.dart';
late ApiService apiService;
// uses for background notification
late TwonlyDatabase twonlyDB;
late TwonlyDB twonlyDB;
List<CameraDescription> gCameras = <CameraDescription>[];

View file

@ -5,7 +5,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/database/twonly_database.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/providers/connection.provider.dart';
import 'package:twonly/src/providers/image_editor.provider.dart';
import 'package:twonly/src/providers/settings.provider.dart';
@ -43,18 +43,18 @@ void main() async {
gCameras = await availableCameras();
apiService = ApiService();
twonlyDB = TwonlyDatabase();
twonlyDB = TwonlyDB();
await twonlyDB.messagesDao.resetPendingDownloadState();
await twonlyDB.messagesDao.handleMediaFilesOlderThan30Days();
await twonlyDB.messageRetransmissionDao.purgeOldRetransmissions();
await twonlyDB.signalDao.purgeOutDatedPreKeys();
// await twonlyDB.messagesDao.resetPendingDownloadState();
// await twonlyDB.messagesDao.handleMediaFilesOlderThan30Days();
// await twonlyDB.messageRetransmissionDao.purgeOldRetransmissions();
// await twonlyDB.signalDao.purgeOutDatedPreKeys();
// Purge media files in the background
unawaited(purgeReceivedMediaFiles());
unawaited(purgeSendMediaFiles());
// unawaited(purgeReceivedMediaFiles());
// unawaited(purgeSendMediaFiles());
unawaited(performTwonlySafeBackup());
// unawaited(performTwonlySafeBackup());
await initFileDownloader();

View file

@ -1,13 +1,12 @@
import 'package:drift/drift.dart';
import 'package:twonly/src/database/tables/contacts_table.dart';
import 'package:twonly/src/database/twonly_database.dart';
import 'package:twonly/src/database/tables/contacts.table.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/services/notifications/pushkeys.notifications.dart';
part 'contacts_dao.g.dart';
part 'contacts.dao.g.dart';
@DriftAccessor(tables: [Contacts])
class ContactsDao extends DatabaseAccessor<TwonlyDatabase>
with _$ContactsDaoMixin {
class ContactsDao extends DatabaseAccessor<TwonlyDB> with _$ContactsDaoMixin {
// this constructor is required so that the main database can create an instance
// of this object.
// ignore: matching_super_parameters
@ -135,42 +134,39 @@ class ContactsDao extends DatabaseAccessor<TwonlyDatabase>
.watchSingleOrNull();
}
Stream<List<Contact>> watchContactsForShareView() {
return (select(contacts)
..where(
(t) =>
t.accepted.equals(true) &
t.blocked.equals(false) &
t.deleted.equals(false),
)
..orderBy([(t) => OrderingTerm.desc(t.lastMessageExchange)]))
.watch();
}
// Stream<List<Contact>> watchContactsForShareView() {
// return (select(contacts)
// ..where(
// (t) =>
// t.accepted.equals(true) &
// t.blocked.equals(false) &
// t.deleted.equals(false),
// )
// ..orderBy([(t) => OrderingTerm.desc(t.lastMessageExchange)]))
// .watch();
// }
Stream<List<Contact>> watchContactsForStartNewChat() {
return (select(contacts)
..where((t) => t.accepted.equals(true) & t.blocked.equals(false))
..orderBy([(t) => OrderingTerm.desc(t.lastMessageExchange)]))
.watch();
}
// Stream<List<Contact>> watchContactsForStartNewChat() {
// return (select(contacts)
// ..where((t) => t.accepted.equals(true) & t.blocked.equals(false))
// ..orderBy([(t) => OrderingTerm.desc(t.lastMessageExchange)]))
// .watch();
// }
Stream<List<Contact>> watchContactsForChatList() {
return (select(contacts)
..where(
(t) =>
t.accepted.equals(true) &
t.blocked.equals(false) &
t.archived.equals(false),
)
..orderBy([(t) => OrderingTerm.desc(t.lastMessageExchange)]))
.watch();
}
// Stream<List<Contact>> watchContactsForChatList() {
// return (select(contacts)
// ..where(
// (t) =>
// t.accepted.equals(true) &
// t.blocked.equals(false) &
// t.archived.equals(false),
// )
// ..orderBy([(t) => OrderingTerm.desc(t.lastMessageExchange)]))
// .watch();
// }
Future<List<Contact>> getAllNotBlockedContacts() {
return (select(contacts)
..where((t) => t.blocked.equals(false))
..orderBy([(t) => OrderingTerm.desc(t.lastMessageExchange)]))
.get();
return (select(contacts)..where((t) => t.blocked.equals(false))).get();
}
Stream<int?> watchContactsBlocked() {

View file

@ -1,8 +1,8 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'contacts_dao.dart';
part of 'contacts.dao.dart';
// ignore_for_file: type=lint
mixin _$ContactsDaoMixin on DatabaseAccessor<TwonlyDatabase> {
mixin _$ContactsDaoMixin on DatabaseAccessor<TwonlyDB> {
$ContactsTable get contacts => attachedDatabase.contacts;
}

View file

@ -0,0 +1,21 @@
import 'package:drift/drift.dart';
import 'package:twonly/src/database/tables/groups.table.dart';
import 'package:twonly/src/database/twonly.db.dart';
part 'groups.dao.g.dart';
@DriftAccessor(tables: [Groups, GroupMembers])
class GroupsDao extends DatabaseAccessor<TwonlyDB> with _$GroupsDaoMixin {
// this constructor is required so that the main database can create an instance
// of this object.
// ignore: matching_super_parameters
GroupsDao(super.db);
Future<bool> isContactInGroup(int contactId, String groupId) async {
final entry = await (select(groupMembers)
..where(
(t) => t.contactId.equals(contactId) & t.groupId.equals(groupId)))
.getSingleOrNull();
return entry != null;
}
}

View file

@ -0,0 +1,10 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'groups.dao.dart';
// ignore_for_file: type=lint
mixin _$GroupsDaoMixin on DatabaseAccessor<TwonlyDB> {
$GroupsTable get groups => attachedDatabase.groups;
$ContactsTable get contacts => attachedDatabase.contacts;
$GroupMembersTable get groupMembers => attachedDatabase.groupMembers;
}

View file

@ -1,50 +0,0 @@
import 'package:drift/drift.dart';
import 'package:twonly/src/database/tables/media_uploads_table.dart';
import 'package:twonly/src/database/twonly_database.dart';
import 'package:twonly/src/utils/log.dart';
part 'media_uploads_dao.g.dart';
@DriftAccessor(tables: [MediaUploads])
class MediaUploadsDao extends DatabaseAccessor<TwonlyDatabase>
with _$MediaUploadsDaoMixin {
// ignore: matching_super_parameters
MediaUploadsDao(super.db);
Future<List<MediaUpload>> getMediaUploadsForRetry() {
return (select(mediaUploads)
..where(
(t) => t.state.equals(UploadState.receiverNotified.name).not(),
))
.get();
}
Future<int> updateMediaUpload(
int mediaUploadId,
MediaUploadsCompanion updatedValues,
) {
return (update(mediaUploads)
..where((c) => c.mediaUploadId.equals(mediaUploadId)))
.write(updatedValues);
}
Future<int?> insertMediaUpload(MediaUploadsCompanion values) async {
try {
return await into(mediaUploads).insert(values);
} catch (e) {
Log.error('Error while inserting media upload: $e');
return null;
}
}
Future<void> deleteMediaUpload(int mediaUploadId) {
return (delete(mediaUploads)
..where((t) => t.mediaUploadId.equals(mediaUploadId)))
.go();
}
SingleOrNullSelectable<MediaUpload> getMediaUploadById(int mediaUploadId) {
return select(mediaUploads)
..where((t) => t.mediaUploadId.equals(mediaUploadId));
}
}

View file

@ -1,8 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'media_uploads_dao.dart';
// ignore_for_file: type=lint
mixin _$MediaUploadsDaoMixin on DatabaseAccessor<TwonlyDatabase> {
$MediaUploadsTable get mediaUploads => attachedDatabase.mediaUploads;
}

View file

@ -1,123 +0,0 @@
import 'package:drift/drift.dart';
import 'package:twonly/src/database/tables/message_retransmissions.dart';
import 'package:twonly/src/database/twonly_database.dart';
import 'package:twonly/src/utils/log.dart';
part 'message_retransmissions.dao.g.dart';
@DriftAccessor(tables: [MessageRetransmissions])
class MessageRetransmissionDao extends DatabaseAccessor<TwonlyDatabase>
with _$MessageRetransmissionDaoMixin {
// this constructor is required so that the main database can create an instance
// of this object.
// ignore: matching_super_parameters
MessageRetransmissionDao(super.db);
Future<int?> insertRetransmission(
MessageRetransmissionsCompanion message,
) async {
try {
return await into(messageRetransmissions).insert(message);
} catch (e) {
Log.error('Error while inserting message for retransmission: $e');
return null;
}
}
Future<void> purgeOldRetransmissions() async {
// delete entries older than two weeks
await (delete(messageRetransmissions)
..where(
(t) => (t.acknowledgeByServerAt.isSmallerThanValue(
DateTime.now().subtract(
const Duration(days: 25),
),
)),
))
.go();
}
Future<List<int>> getRetransmitAbleMessages() async {
final countDeleted = await (delete(messageRetransmissions)
..where(
(t) =>
t.encryptedHash.isNull() & t.acknowledgeByServerAt.isNotNull(),
))
.go();
if (countDeleted > 0) {
Log.info('Deleted $countDeleted faulty retransmissions');
}
return (await (select(messageRetransmissions)
..where((t) => t.acknowledgeByServerAt.isNull()))
.get())
.map((msg) => msg.retransmissionId)
.toList();
}
SingleOrNullSelectable<MessageRetransmission> getRetransmissionById(
int retransmissionId,
) {
return select(messageRetransmissions)
..where((t) => t.retransmissionId.equals(retransmissionId));
}
Stream<List<MessageRetransmission>> watchAllMessages() {
return (select(messageRetransmissions)
..orderBy([(t) => OrderingTerm.asc(t.retransmissionId)]))
.watch();
}
Future<void> updateRetransmission(
int retransmissionId,
MessageRetransmissionsCompanion updatedValues,
) {
return (update(messageRetransmissions)
..where((c) => c.retransmissionId.equals(retransmissionId)))
.write(updatedValues);
}
Future<int> resetAckStatusFor(int fromUserId, Uint8List encryptedHash) async {
return ((update(messageRetransmissions))
..where(
(m) =>
m.contactId.equals(fromUserId) &
m.encryptedHash.equals(encryptedHash),
))
.write(
const MessageRetransmissionsCompanion(
acknowledgeByServerAt: Value(null),
),
);
}
Future<MessageRetransmission?> getRetransmissionFromHash(
int fromUserId,
Uint8List encryptedHash,
) async {
return ((select(messageRetransmissions))
..where(
(m) =>
m.contactId.equals(fromUserId) &
m.encryptedHash.equals(encryptedHash),
))
.getSingleOrNull();
}
Future<void> deleteRetransmissionById(int retransmissionId) {
return (delete(messageRetransmissions)
..where((t) => t.retransmissionId.equals(retransmissionId)))
.go();
}
Future<void> clearRetransmissionTable() {
return delete(messageRetransmissions).go();
}
Future<void> deleteRetransmissionByMessageId(int messageId) {
return (delete(messageRetransmissions)
..where((t) => t.messageId.equals(messageId)))
.go();
}
}

View file

@ -1,11 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'message_retransmissions.dao.dart';
// ignore_for_file: type=lint
mixin _$MessageRetransmissionDaoMixin on DatabaseAccessor<TwonlyDatabase> {
$ContactsTable get contacts => attachedDatabase.contacts;
$MessagesTable get messages => attachedDatabase.messages;
$MessageRetransmissionsTable get messageRetransmissions =>
attachedDatabase.messageRetransmissions;
}

View file

@ -0,0 +1,377 @@
import 'package:drift/drift.dart';
import 'package:twonly/src/database/tables/contacts.table.dart';
import 'package:twonly/src/database/tables/mediafiles.table.dart';
import 'package:twonly/src/database/tables/messages.table.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/services/mediafile.service.dart';
part 'messages.dao.g.dart';
@DriftAccessor(tables: [Messages, Contacts, MediaFiles, MessageHistories])
class MessagesDao extends DatabaseAccessor<TwonlyDB> with _$MessagesDaoMixin {
// this constructor is required so that the main database can create an instance
// of this object.
// ignore: matching_super_parameters
MessagesDao(super.db);
// Stream<List<Message>> watchMessageNotOpened(int contactId) {
// return (select(messages)
// ..where(
// (t) =>
// t.openedAt.isNull() &
// t.contactId.equals(contactId) &
// t.errorWhileSending.equals(false),
// )
// ..orderBy([(t) => OrderingTerm.desc(t.sendAt)]))
// .watch();
// }
// Stream<List<Message>> watchMediaMessageNotOpened(int contactId) {
// return (select(messages)
// ..where(
// (t) =>
// t.openedAt.isNull() &
// t.contactId.equals(contactId) &
// t.errorWhileSending.equals(false) &
// t.messageOtherId.isNotNull() &
// t.kind.equals(MessageKind.media.name),
// )
// ..orderBy([(t) => OrderingTerm.asc(t.sendAt)]))
// .watch();
// }
// Stream<List<Message>> watchLastMessage(int contactId) {
// return (select(messages)
// ..where((t) => t.contactId.equals(contactId))
// ..orderBy([(t) => OrderingTerm.desc(t.sendAt)])
// ..limit(1))
// .watch();
// }
// Stream<List<Message>> watchAllMessagesFrom(int contactId) {
// return (select(messages)
// ..where(
// (t) =>
// t.contactId.equals(contactId) &
// t.contentJson.isNotNull() &
// (t.openedAt.isNull() |
// t.mediaStored.equals(true) |
// t.openedAt.isBiggerThanValue(
// DateTime.now().subtract(const Duration(days: 1)),
// )),
// )
// ..orderBy([(t) => OrderingTerm.asc(t.sendAt)]))
// .watch();
// }
// Future<void> removeOldMessages() {
// return (update(messages)
// ..where(
// (t) =>
// (t.openedAt.isSmallerThanValue(
// DateTime.now().subtract(const Duration(days: 1)),
// ) |
// (t.sendAt.isSmallerThanValue(
// DateTime.now().subtract(const Duration(days: 3)),
// ) &
// t.errorWhileSending.equals(true))) &
// t.kind.equals(MessageKind.textMessage.name),
// ))
// .write(const MessagesCompanion(contentJson: Value(null)));
// }
// Future<void> handleMediaFilesOlderThan30Days() {
// /// media files will be deleted by the server after 30 days, so delete them here also
// return (update(messages)
// ..where(
// (t) => (t.kind.equals(MessageKind.media.name) &
// t.openedAt.isNull() &
// t.messageOtherId.isNull() &
// (t.sendAt.isSmallerThanValue(
// DateTime.now().subtract(
// const Duration(days: 30),
// ),
// ))),
// ))
// .write(const MessagesCompanion(errorWhileSending: Value(true)));
// }
// Future<List<Message>> getAllMessagesPendingDownloading() {
// return (select(messages)
// ..where(
// (t) =>
// t.downloadState.equals(DownloadState.downloaded.index).not() &
// t.messageOtherId.isNotNull() &
// t.errorWhileSending.equals(false) &
// t.kind.equals(MessageKind.media.name),
// ))
// .get();
// }
// Future<List<Message>> getAllNonACKMessagesFromUser() {
// return (select(messages)
// ..where(
// (t) =>
// t.acknowledgeByUser.equals(false) &
// t.messageOtherId.isNull() &
// t.errorWhileSending.equals(false) &
// t.sendAt.isBiggerThanValue(
// DateTime.now().subtract(const Duration(minutes: 10)),
// ),
// ))
// .get();
// }
// Stream<List<Message>> getAllStoredMediaFiles() {
// return (select(messages)
// ..where((t) => t.mediaStored.equals(true))
// ..orderBy([(t) => OrderingTerm.desc(t.sendAt)]))
// .watch();
// }
// Future<List<Message>> getAllMessagesPendingUpload() {
// return (select(messages)
// ..where(
// (t) =>
// t.acknowledgeByServer.equals(false) &
// t.messageOtherId.isNull() &
// t.mediaUploadId.isNotNull() &
// t.downloadState.equals(DownloadState.pending.index) &
// t.errorWhileSending.equals(false) &
// t.kind.equals(MessageKind.media.name),
// ))
// .get();
// }
// Future<void> openedAllNonMediaMessages(int contactId) {
// final updates = MessagesCompanion(openedAt: Value(DateTime.now()));
// return (update(messages)
// ..where(
// (t) =>
// t.contactId.equals(contactId) &
// t.messageOtherId.isNotNull() &
// t.openedAt.isNull() &
// t.kind.equals(MessageKind.media.name).not(),
// ))
// .write(updates);
// }
// Future<void> resetPendingDownloadState() {
// // All media files in the downloading state are reset to the pending state
// // When the app is used in mobile network, they will not be downloaded at the start
// // if they are not yet downloaded...
// const updates =
// MessagesCompanion(downloadState: Value(DownloadState.pending));
// return (update(messages)
// ..where(
// (t) =>
// t.messageOtherId.isNotNull() &
// t.downloadState.equals(DownloadState.downloading.index) &
// t.kind.equals(MessageKind.media.name),
// ))
// .write(updates);
// }
Future<void> handleMessageDeletion(
int contactId,
String messageId,
DateTime timestamp,
) async {
final msg = await getMessageById(messageId).getSingleOrNull();
if (msg == null || msg.senderId != contactId) return;
if (msg.mediaId != null) {
await (delete(mediaFiles)..where((t) => t.mediaId.equals(msg.mediaId!)))
.go();
await removeMediaFile(msg.mediaId!);
}
await (delete(messageHistories)
..where((t) => t.messageId.equals(messageId)))
.go();
await (update(messages)
..where(
(t) => t.messageId.equals(messageId) & t.senderId.equals(contactId),
))
.write(
MessagesCompanion(
isDeletedFromSender: const Value(true),
content: const Value(null),
modifiedAt: Value(timestamp),
mediaId: const Value(null),
),
);
}
Future<void> handleTextEdit(
int contactId,
String messageId,
String text,
DateTime timestamp,
) async {
final msg = await getMessageById(messageId).getSingleOrNull();
if (msg == null || msg.content == null || msg.senderId == contactId) {
return;
}
await into(messageHistories).insert(
MessageHistoriesCompanion(
messageId: Value(messageId),
content: Value(msg.content),
),
);
await (update(messages)
..where(
(t) => t.messageId.equals(messageId) & t.senderId.equals(contactId),
))
.write(
MessagesCompanion(
content: Value(text),
modifiedAt: Value(timestamp),
),
);
}
Future<void> handleMessageOpened(
String groupId,
String messageId,
DateTime timestamp,
) async {
final msg = await getMessageById(messageId).getSingleOrNull();
if (msg == null) return;
await (update(messages)
..where(
(t) =>
t.groupId.equals(groupId) &
t.messageId.equals(messageId) &
t.senderId.isNull(),
))
.write(
MessagesCompanion(
openedAt: Value(timestamp),
openedByCounter: Value(msg.openedByCounter + 1),
),
);
}
// Future<void> updateMessageByOtherUser(
// int userId,
// int messageId,
// MessagesCompanion updatedValues,
// ) {
// return (update(messages)
// ..where(
// (c) => c.contactId.equals(userId) & c.messageId.equals(messageId),
// ))
// .write(updatedValues);
// }
// Future<void> updateMessageByOtherMessageId(
// int userId,
// int messageOtherId,
// MessagesCompanion updatedValues,
// ) {
// return (update(messages)
// ..where(
// (c) =>
// c.contactId.equals(userId) &
// c.messageOtherId.equals(messageOtherId),
// ))
// .write(updatedValues);
// }
// Future<void> updateMessageByMessageId(
// int messageId,
// MessagesCompanion updatedValues,
// ) {
// return (update(messages)..where((c) => c.messageId.equals(messageId)))
// .write(updatedValues);
// }
// Future<int?> insertMessage(MessagesCompanion message) async {
// try {
// await (update(contacts)
// ..where(
// (c) => c.userId.equals(message.contactId.value),
// ))
// .write(ContactsCompanion(lastMessageExchange: Value(DateTime.now())));
// return await into(messages).insert(message);
// } catch (e) {
// Log.error('Error while inserting message: $e');
// return null;
// }
// }
// Future<void> deleteMessagesByContactId(int contactId) {
// return (delete(messages)
// ..where(
// (t) => t.contactId.equals(contactId) & t.mediaStored.equals(false),
// ))
// .go();
// }
// Future<void> deleteMessagesByContactIdAndOtherMessageId(
// int contactId,
// int messageOtherId,
// ) {
// return (delete(messages)
// ..where(
// (t) =>
// t.contactId.equals(contactId) &
// t.messageOtherId.equals(messageOtherId),
// ))
// .go();
// }
// Future<void> deleteMessagesByMessageId(int messageId) {
// return (delete(messages)..where((t) => t.messageId.equals(messageId))).go();
// }
// Future<void> deleteAllMessagesByContactId(int contactId) {
// return (delete(messages)..where((t) => t.contactId.equals(contactId))).go();
// }
// Future<bool> containsOtherMessageId(
// int fromUserId,
// int messageOtherId,
// ) async {
// final query = select(messages)
// ..where(
// (t) =>
// t.messageOtherId.equals(messageOtherId) &
// t.contactId.equals(fromUserId),
// );
// final entry = await query.get();
// return entry.isNotEmpty;
// }
SingleOrNullSelectable<Message> getMessageById(String messageId) {
return select(messages)..where((t) => t.messageId.equals(messageId));
}
// Future<List<Message>> getMessagesByMediaUploadId(int mediaUploadId) async {
// return (select(messages)
// ..where((t) => t.mediaUploadId.equals(mediaUploadId)))
// .get();
// }
// SingleOrNullSelectable<Message> getMessageByOtherMessageId(
// int fromUserId,
// int messageId,
// ) {
// return select(messages)
// ..where(
// (t) =>
// t.messageOtherId.equals(messageId) & t.contactId.equals(fromUserId),
// );
// }
// SingleOrNullSelectable<Message> getMessageByIdAndContactId(
// int fromUserId,
// int messageId,
// ) {
// return select(messages)
// ..where(
// (t) => t.messageId.equals(messageId) & t.contactId.equals(fromUserId),
// );
// }
}

View file

@ -0,0 +1,12 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'messages.dao.dart';
// ignore_for_file: type=lint
mixin _$MessagesDaoMixin on DatabaseAccessor<TwonlyDB> {
$ContactsTable get contacts => attachedDatabase.contacts;
$MediaFilesTable get mediaFiles => attachedDatabase.mediaFiles;
$MessagesTable get messages => attachedDatabase.messages;
$MessageHistoriesTable get messageHistories =>
attachedDatabase.messageHistories;
}

View file

@ -1,311 +0,0 @@
import 'package:drift/drift.dart';
import 'package:twonly/src/database/tables/contacts_table.dart';
import 'package:twonly/src/database/tables/messages_table.dart';
import 'package:twonly/src/database/twonly_database.dart';
import 'package:twonly/src/utils/log.dart';
part 'messages_dao.g.dart';
@DriftAccessor(tables: [Messages, Contacts])
class MessagesDao extends DatabaseAccessor<TwonlyDatabase>
with _$MessagesDaoMixin {
// this constructor is required so that the main database can create an instance
// of this object.
// ignore: matching_super_parameters
MessagesDao(super.db);
Stream<List<Message>> watchMessageNotOpened(int contactId) {
return (select(messages)
..where(
(t) =>
t.openedAt.isNull() &
t.contactId.equals(contactId) &
t.errorWhileSending.equals(false),
)
..orderBy([(t) => OrderingTerm.desc(t.sendAt)]))
.watch();
}
Stream<List<Message>> watchMediaMessageNotOpened(int contactId) {
return (select(messages)
..where(
(t) =>
t.openedAt.isNull() &
t.contactId.equals(contactId) &
t.errorWhileSending.equals(false) &
t.messageOtherId.isNotNull() &
t.kind.equals(MessageKind.media.name),
)
..orderBy([(t) => OrderingTerm.asc(t.sendAt)]))
.watch();
}
Stream<List<Message>> watchLastMessage(int contactId) {
return (select(messages)
..where((t) => t.contactId.equals(contactId))
..orderBy([(t) => OrderingTerm.desc(t.sendAt)])
..limit(1))
.watch();
}
Stream<List<Message>> watchAllMessagesFrom(int contactId) {
return (select(messages)
..where(
(t) =>
t.contactId.equals(contactId) &
t.contentJson.isNotNull() &
(t.openedAt.isNull() |
t.mediaStored.equals(true) |
t.openedAt.isBiggerThanValue(
DateTime.now().subtract(const Duration(days: 1)),
)),
)
..orderBy([(t) => OrderingTerm.asc(t.sendAt)]))
.watch();
}
Future<void> removeOldMessages() {
return (update(messages)
..where(
(t) =>
(t.openedAt.isSmallerThanValue(
DateTime.now().subtract(const Duration(days: 1)),
) |
(t.sendAt.isSmallerThanValue(
DateTime.now().subtract(const Duration(days: 3)),
) &
t.errorWhileSending.equals(true))) &
t.kind.equals(MessageKind.textMessage.name),
))
.write(const MessagesCompanion(contentJson: Value(null)));
}
Future<void> handleMediaFilesOlderThan30Days() {
/// media files will be deleted by the server after 30 days, so delete them here also
return (update(messages)
..where(
(t) => (t.kind.equals(MessageKind.media.name) &
t.openedAt.isNull() &
t.messageOtherId.isNull() &
(t.sendAt.isSmallerThanValue(
DateTime.now().subtract(
const Duration(days: 30),
),
))),
))
.write(const MessagesCompanion(errorWhileSending: Value(true)));
}
Future<List<Message>> getAllMessagesPendingDownloading() {
return (select(messages)
..where(
(t) =>
t.downloadState.equals(DownloadState.downloaded.index).not() &
t.messageOtherId.isNotNull() &
t.errorWhileSending.equals(false) &
t.kind.equals(MessageKind.media.name),
))
.get();
}
Future<List<Message>> getAllNonACKMessagesFromUser() {
return (select(messages)
..where(
(t) =>
t.acknowledgeByUser.equals(false) &
t.messageOtherId.isNull() &
t.errorWhileSending.equals(false) &
t.sendAt.isBiggerThanValue(
DateTime.now().subtract(const Duration(minutes: 10)),
),
))
.get();
}
Stream<List<Message>> getAllStoredMediaFiles() {
return (select(messages)
..where((t) => t.mediaStored.equals(true))
..orderBy([(t) => OrderingTerm.desc(t.sendAt)]))
.watch();
}
Future<List<Message>> getAllMessagesPendingUpload() {
return (select(messages)
..where(
(t) =>
t.acknowledgeByServer.equals(false) &
t.messageOtherId.isNull() &
t.mediaUploadId.isNotNull() &
t.downloadState.equals(DownloadState.pending.index) &
t.errorWhileSending.equals(false) &
t.kind.equals(MessageKind.media.name),
))
.get();
}
Future<void> openedAllNonMediaMessages(int contactId) {
final updates = MessagesCompanion(openedAt: Value(DateTime.now()));
return (update(messages)
..where(
(t) =>
t.contactId.equals(contactId) &
t.messageOtherId.isNotNull() &
t.openedAt.isNull() &
t.kind.equals(MessageKind.media.name).not(),
))
.write(updates);
}
Future<void> resetPendingDownloadState() {
// All media files in the downloading state are reset to the pending state
// When the app is used in mobile network, they will not be downloaded at the start
// if they are not yet downloaded...
const updates =
MessagesCompanion(downloadState: Value(DownloadState.pending));
return (update(messages)
..where(
(t) =>
t.messageOtherId.isNotNull() &
t.downloadState.equals(DownloadState.downloading.index) &
t.kind.equals(MessageKind.media.name),
))
.write(updates);
}
Future<void> openedAllNonMediaMessagesFromOtherUser(int contactId) {
final updates = MessagesCompanion(openedAt: Value(DateTime.now()));
return (update(messages)
..where(
(t) =>
t.contactId.equals(contactId) &
t.messageOtherId
.isNull() & // only mark messages open that where send
t.openedAt.isNull() &
t.kind.equals(MessageKind.media.name).not(),
))
.write(updates);
}
Future<void> updateMessageByOtherUser(
int userId,
int messageId,
MessagesCompanion updatedValues,
) {
return (update(messages)
..where(
(c) => c.contactId.equals(userId) & c.messageId.equals(messageId),
))
.write(updatedValues);
}
Future<void> updateMessageByOtherMessageId(
int userId,
int messageOtherId,
MessagesCompanion updatedValues,
) {
return (update(messages)
..where(
(c) =>
c.contactId.equals(userId) &
c.messageOtherId.equals(messageOtherId),
))
.write(updatedValues);
}
Future<void> updateMessageByMessageId(
int messageId,
MessagesCompanion updatedValues,
) {
return (update(messages)..where((c) => c.messageId.equals(messageId)))
.write(updatedValues);
}
Future<int?> insertMessage(MessagesCompanion message) async {
try {
await (update(contacts)
..where(
(c) => c.userId.equals(message.contactId.value),
))
.write(ContactsCompanion(lastMessageExchange: Value(DateTime.now())));
return await into(messages).insert(message);
} catch (e) {
Log.error('Error while inserting message: $e');
return null;
}
}
Future<void> deleteMessagesByContactId(int contactId) {
return (delete(messages)
..where(
(t) => t.contactId.equals(contactId) & t.mediaStored.equals(false),
))
.go();
}
Future<void> deleteMessagesByContactIdAndOtherMessageId(
int contactId,
int messageOtherId,
) {
return (delete(messages)
..where(
(t) =>
t.contactId.equals(contactId) &
t.messageOtherId.equals(messageOtherId),
))
.go();
}
Future<void> deleteMessagesByMessageId(int messageId) {
return (delete(messages)..where((t) => t.messageId.equals(messageId))).go();
}
Future<void> deleteAllMessagesByContactId(int contactId) {
return (delete(messages)..where((t) => t.contactId.equals(contactId))).go();
}
Future<bool> containsOtherMessageId(
int fromUserId,
int messageOtherId,
) async {
final query = select(messages)
..where(
(t) =>
t.messageOtherId.equals(messageOtherId) &
t.contactId.equals(fromUserId),
);
final entry = await query.get();
return entry.isNotEmpty;
}
SingleOrNullSelectable<Message> getMessageByMessageId(int messageId) {
return select(messages)..where((t) => t.messageId.equals(messageId));
}
Future<List<Message>> getMessagesByMediaUploadId(int mediaUploadId) async {
return (select(messages)
..where((t) => t.mediaUploadId.equals(mediaUploadId)))
.get();
}
SingleOrNullSelectable<Message> getMessageByOtherMessageId(
int fromUserId,
int messageId,
) {
return select(messages)
..where(
(t) =>
t.messageOtherId.equals(messageId) & t.contactId.equals(fromUserId),
);
}
SingleOrNullSelectable<Message> getMessageByIdAndContactId(
int fromUserId,
int messageId,
) {
return select(messages)
..where(
(t) => t.messageId.equals(messageId) & t.contactId.equals(fromUserId),
);
}
}

View file

@ -1,9 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'messages_dao.dart';
// ignore_for_file: type=lint
mixin _$MessagesDaoMixin on DatabaseAccessor<TwonlyDatabase> {
$ContactsTable get contacts => attachedDatabase.contacts;
$MessagesTable get messages => attachedDatabase.messages;
}

View file

@ -0,0 +1,44 @@
import 'package:drift/drift.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/database/tables/reactions.table.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/utils/log.dart';
part 'reactions.dao.g.dart';
@DriftAccessor(tables: [Reactions])
class ReactionsDao extends DatabaseAccessor<TwonlyDB> with _$ReactionsDaoMixin {
// this constructor is required so that the main database can create an instance
// of this object.
// ignore: matching_super_parameters
ReactionsDao(super.db);
Future<void> updateReaction(
int contactId,
String messageId,
String groupId,
String? emoji,
) async {
final msg =
await twonlyDB.messagesDao.getMessageById(messageId).getSingleOrNull();
if (msg == null || msg.groupId != groupId) return;
try {
await (delete(reactions)
..where(
(t) =>
t.senderId.equals(contactId) & t.messageId.equals(messageId),
))
.go();
if (emoji != null) {
await into(reactions).insert(ReactionsCompanion(
messageId: Value(messageId),
emoji: Value(emoji),
senderId: Value(contactId),
));
}
} catch (e) {
Log.error(e);
}
}
}

View file

@ -0,0 +1,11 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'reactions.dao.dart';
// ignore_for_file: type=lint
mixin _$ReactionsDaoMixin on DatabaseAccessor<TwonlyDB> {
$ContactsTable get contacts => attachedDatabase.contacts;
$MediaFilesTable get mediaFiles => attachedDatabase.mediaFiles;
$MessagesTable get messages => attachedDatabase.messages;
$ReactionsTable get reactions => attachedDatabase.reactions;
}

View file

@ -0,0 +1,52 @@
import 'package:drift/drift.dart';
import 'package:twonly/src/database/tables/messages.table.dart';
import 'package:twonly/src/database/tables/receipts.table.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/utils/log.dart';
part 'receipts.dao.g.dart';
@DriftAccessor(tables: [Receipts, Messages])
class ReceiptsDao extends DatabaseAccessor<TwonlyDB> with _$ReceiptsDaoMixin {
// this constructor is required so that the main database can create an instance
// of this object.
// ignore: matching_super_parameters
ReceiptsDao(super.db);
Future<void> confirmReceipt(String receiptId, int fromUserId) async {
final receipt = await (select(receipts)
..where((t) =>
t.receiptId.equals(receiptId) & t.contactId.equals(fromUserId)))
.getSingleOrNull();
if (receipt == null) return;
if (receipt.messageId != null) {
await (update(messages)
..where((t) => t.messageId.equals(receipt.messageId!)))
.write(
const MessagesCompanion(
acknowledgeByUser: Value(true),
),
);
}
await (delete(receipts)
..where(
(t) =>
t.receiptId.equals(receiptId) & t.contactId.equals(fromUserId),
))
.go();
}
Future<Receipt?> insertReceipt(ReceiptsCompanion entry) async {
try {
final id = await into(receipts).insert(entry);
return await (select(receipts)..where((t) => t.rowId.equals(id)))
.getSingle();
} catch (e) {
Log.error(e);
return null;
}
}
}

View file

@ -0,0 +1,11 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'receipts.dao.dart';
// ignore_for_file: type=lint
mixin _$ReceiptsDaoMixin on DatabaseAccessor<TwonlyDB> {
$ContactsTable get contacts => attachedDatabase.contacts;
$MediaFilesTable get mediaFiles => attachedDatabase.mediaFiles;
$MessagesTable get messages => attachedDatabase.messages;
$ReceiptsTable get receipts => attachedDatabase.receipts;
}

View file

@ -1,11 +1,11 @@
import 'package:drift/drift.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/database/tables/signal_contact_prekey_table.dart';
import 'package:twonly/src/database/tables/signal_contact_signed_prekey_table.dart';
import 'package:twonly/src/database/twonly_database.dart';
import 'package:twonly/src/database/tables/signal_contact_prekey.table.dart';
import 'package:twonly/src/database/tables/signal_contact_signed_prekey.table.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/utils/log.dart';
part 'signal_dao.g.dart';
part 'signal.dao.g.dart';
@DriftAccessor(
tables: [
@ -13,7 +13,7 @@ part 'signal_dao.g.dart';
SignalContactSignedPreKeys,
],
)
class SignalDao extends DatabaseAccessor<TwonlyDatabase> with _$SignalDaoMixin {
class SignalDao extends DatabaseAccessor<TwonlyDB> with _$SignalDaoMixin {
// this constructor is required so that the main database can create an instance
// of this object.
// ignore: matching_super_parameters

View file

@ -1,9 +1,10 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'signal_dao.dart';
part of 'signal.dao.dart';
// ignore_for_file: type=lint
mixin _$SignalDaoMixin on DatabaseAccessor<TwonlyDatabase> {
mixin _$SignalDaoMixin on DatabaseAccessor<TwonlyDB> {
$ContactsTable get contacts => attachedDatabase.contacts;
$SignalContactPreKeysTable get signalContactPreKeys =>
attachedDatabase.signalContactPreKeys;
$SignalContactSignedPreKeysTable get signalContactSignedPreKeys =>

View file

@ -2,7 +2,7 @@ import 'package:collection/collection.dart';
import 'package:drift/drift.dart';
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/database/twonly_database.dart';
import 'package:twonly/src/database/twonly.db.dart';
class ConnectIdentityKeyStore extends IdentityKeyStore {
ConnectIdentityKeyStore(this.identityKeyPair, this.localRegistrationId);

View file

@ -1,7 +1,7 @@
import 'package:drift/drift.dart';
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/database/twonly_database.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/utils/log.dart';
class ConnectPreKeyStore extends PreKeyStore {

View file

@ -1,7 +1,7 @@
import 'package:drift/drift.dart';
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/database/twonly_database.dart';
import 'package:twonly/src/database/twonly.db.dart';
class ConnectSenderKeyStore extends SenderKeyStore {
@override

View file

@ -1,7 +1,7 @@
import 'package:drift/drift.dart';
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/database/twonly_database.dart';
import 'package:twonly/src/database/twonly.db.dart';
class ConnectSessionStore extends SessionStore {
@override

View file

@ -0,0 +1,41 @@
import 'package:drift/drift.dart';
class Contacts extends Table {
IntColumn get userId => integer()();
TextColumn get username => text()();
TextColumn get displayName => text().nullable()();
TextColumn get nickName => text().nullable()();
TextColumn get avatarSvg => text().nullable()();
IntColumn get senderProfileCounter =>
integer().withDefault(const Constant(0))();
BoolColumn get accepted => boolean().withDefault(const Constant(false))();
BoolColumn get requested => boolean().withDefault(const Constant(false))();
BoolColumn get hidden => boolean().withDefault(const Constant(false))();
BoolColumn get blocked => boolean().withDefault(const Constant(false))();
BoolColumn get verified => boolean().withDefault(const Constant(false))();
BoolColumn get archived => boolean().withDefault(const Constant(false))();
BoolColumn get deleted => boolean().withDefault(const Constant(false))();
BoolColumn get alsoBestFriend =>
boolean().withDefault(const Constant(false))();
IntColumn get deleteMessagesAfterXMinutes =>
integer().withDefault(const Constant(60 * 24))();
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
IntColumn get totalMediaCounter => integer().withDefault(const Constant(0))();
DateTimeColumn get lastMessageSend => dateTime().nullable()();
DateTimeColumn get lastMessageReceived => dateTime().nullable()();
DateTimeColumn get lastFlameCounterChange => dateTime().nullable()();
DateTimeColumn get lastFlameSync => dateTime().nullable()();
IntColumn get flameCounter => integer().withDefault(const Constant(0))();
@override
Set<Column> get primaryKey => {userId};
}

View file

@ -0,0 +1,34 @@
import 'package:drift/drift.dart';
import 'package:hashlib/random.dart';
import 'package:twonly/src/database/tables/contacts.table.dart';
@DataClassName('Group')
class Groups extends Table {
TextColumn get groupId => text().clientDefault(() => uuid.v4())();
BoolColumn get isGroupAdmin => boolean()();
BoolColumn get isGroupOfTwo => boolean()();
BoolColumn get pinned => boolean().withDefault(const Constant(false))();
DateTimeColumn get lastMessageExchange =>
dateTime().withDefault(currentDateAndTime)();
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
@override
Set<Column> get primaryKey => {groupId};
}
enum MemberState { invited, accepted, admin }
@DataClassName('GroupMember')
class GroupMembers extends Table {
TextColumn get groupId => text()();
IntColumn get contactId => integer().references(Contacts, #userId)();
TextColumn get memberState => textEnum<MemberState>().nullable()();
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
@override
Set<Column> get primaryKey => {groupId, contactId};
}

View file

@ -0,0 +1,48 @@
import 'package:drift/drift.dart';
import 'package:hashlib/random.dart';
enum MediaType {
image,
video,
gif,
}
enum UploadState {
pending,
readyToUpload,
uploadTaskStarted,
receiverNotified,
}
enum DownloadState {
pending,
}
@DataClassName('MediaFile')
class MediaFiles extends Table {
TextColumn get mediaId => text().clientDefault(() => uuid.v4())();
TextColumn get type => textEnum<MediaType>()();
TextColumn get uploadState => textEnum<UploadState>().nullable()();
TextColumn get downloadState => textEnum<DownloadState>().nullable()();
BoolColumn get requiresAuthentication => boolean()();
BoolColumn get reopenByContact =>
boolean().withDefault(const Constant(false))();
BoolColumn get storedByContact =>
boolean().withDefault(const Constant(false))();
IntColumn get displayLimitInMilliseconds => integer().nullable()();
BlobColumn get downloadToken => blob().nullable()();
BlobColumn get encryptionKey => blob().nullable()();
BlobColumn get encryptionMac => blob().nullable()();
BlobColumn get encryptionNonce => blob().nullable()();
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
@override
Set<Column> get primaryKey => {mediaId};
}

View file

@ -0,0 +1,52 @@
import 'package:drift/drift.dart';
import 'package:twonly/src/database/tables/contacts.table.dart';
import 'package:twonly/src/database/tables/mediafiles.table.dart';
@DataClassName('Message')
class Messages extends Table {
TextColumn get groupId => text()();
TextColumn get messageId => text()();
// in case senderId is null, it was send by user itself
IntColumn get senderId =>
integer().nullable().references(Contacts, #userId)();
TextColumn get content => text().nullable()();
TextColumn get mediaId =>
text().nullable().references(MediaFiles, #mediaId)();
TextColumn get quotesMessageId =>
text().nullable().references(Messages, #messageId)();
BoolColumn get isDeletedFromSender =>
boolean().withDefault(const Constant(false))();
BoolColumn get isEdited => boolean().withDefault(const Constant(false))();
BoolColumn get acknowledgeByUser =>
boolean().withDefault(const Constant(false))();
BoolColumn get acknowledgeByServer =>
boolean().withDefault(const Constant(false))();
IntColumn get openedByCounter => integer().withDefault(const Constant(0))();
DateTimeColumn get openedAt => dateTime().nullable()();
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
DateTimeColumn get modifiedAt =>
dateTime().nullable().withDefault(currentDateAndTime)();
@override
Set<Column> get primaryKey => {messageId};
}
@DataClassName('MessageHistory')
class MessageHistories extends Table {
TextColumn get messageId =>
text().references(Messages, #messageId, onDelete: KeyAction.cascade)();
TextColumn get content => text().nullable()();
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
@override
Set<Column> get primaryKey => {messageId, createdAt};
}

View file

@ -0,0 +1,21 @@
import 'package:drift/drift.dart';
import 'package:twonly/src/database/tables/contacts.table.dart';
import 'package:twonly/src/database/tables/messages.table.dart';
@DataClassName('Reaction')
class Reactions extends Table {
TextColumn get messageId =>
text().references(Messages, #messageId, onDelete: KeyAction.cascade)();
TextColumn get emoji => text()();
// in case senderId is null, it was send by user itself
IntColumn get senderId => integer()
.nullable()
.references(Contacts, #userId, onDelete: KeyAction.cascade)();
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
@override
Set<Column> get primaryKey => {messageId, senderId, createdAt};
}

View file

@ -0,0 +1,30 @@
import 'package:drift/drift.dart';
import 'package:hashlib/random.dart';
import 'package:twonly/src/database/tables/contacts.table.dart';
import 'package:twonly/src/database/tables/messages.table.dart';
@DataClassName('Receipt')
class Receipts extends Table {
TextColumn get receiptId => text().clientDefault(() => uuid.v4())();
IntColumn get contactId =>
integer().references(Contacts, #userId, onDelete: KeyAction.cascade)();
// in case a message is deleted, it should be also deleted from the receipts table
TextColumn get messageId => text()
.nullable()
.references(Messages, #messageId, onDelete: KeyAction.cascade)();
BlobColumn get message => blob()();
BoolColumn get contactWillSendsReceipt =>
boolean().withDefault(const Constant(true))();
IntColumn get retryCount => integer().withDefault(const Constant(0))();
DateTimeColumn get lastRetry => dateTime().nullable()();
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
@override
Set<Column> get primaryKey => {receiptId};
}

View file

@ -0,0 +1,13 @@
import 'package:drift/drift.dart';
import 'package:twonly/src/database/tables/contacts.table.dart';
@DataClassName('SignalContactPreKey')
class SignalContactPreKeys extends Table {
IntColumn get contactId =>
integer().references(Contacts, #userId, onDelete: KeyAction.cascade)();
IntColumn get preKeyId => integer()();
BlobColumn get preKey => blob()();
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
@override
Set<Column> get primaryKey => {contactId, preKeyId};
}

View file

@ -0,0 +1,14 @@
import 'package:drift/drift.dart';
import 'package:twonly/src/database/tables/contacts.table.dart';
@DataClassName('SignalContactSignedPreKey')
class SignalContactSignedPreKeys extends Table {
IntColumn get contactId =>
integer().references(Contacts, #userId, onDelete: KeyAction.cascade)();
IntColumn get signedPreKeyId => integer()();
BlobColumn get signedPreKey => blob()();
BlobColumn get signedPreKeySignature => blob()();
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
@override
Set<Column> get primaryKey => {contactId};
}

View file

@ -1,6 +1,6 @@
import 'package:drift/drift.dart';
import 'package:twonly/src/database/tables/contacts_table.dart';
import 'package:twonly/src/database/tables/messages_table.dart';
import 'package:twonly/src/database/tables_old/contacts_table.dart';
import 'package:twonly/src/database/tables_old/messages_table.dart';
@DataClassName('MessageRetransmission')
class MessageRetransmissions extends Table {

View file

@ -1,5 +1,5 @@
import 'package:drift/drift.dart';
import 'package:twonly/src/database/tables/contacts_table.dart';
import 'package:twonly/src/database/tables_old/contacts_table.dart';
enum MessageKind {
textMessage,

View file

@ -0,0 +1,12 @@
import 'package:drift/drift.dart';
@DataClassName('SignalIdentityKeyStore')
class SignalIdentityKeyStores extends Table {
IntColumn get deviceId => integer()();
TextColumn get name => text()();
BlobColumn get identityKey => blob()();
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
@override
Set<Column> get primaryKey => {deviceId, name};
}

View file

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

View file

@ -0,0 +1,10 @@
import 'package:drift/drift.dart';
@DataClassName('SignalSenderKeyStore')
class SignalSenderKeyStores extends Table {
TextColumn get senderKeyName => text()();
BlobColumn get senderKey => blob()();
@override
Set<Column> get primaryKey => {senderKeyName};
}

View file

@ -0,0 +1,12 @@
import 'package:drift/drift.dart';
@DataClassName('SignalSessionStore')
class SignalSessionStores extends Table {
IntColumn get deviceId => integer()();
TextColumn get name => text()();
BlobColumn get sessionRecord => blob()();
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
@override
Set<Column> get primaryKey => {deviceId, name};
}

View file

@ -0,0 +1,124 @@
import 'package:drift/drift.dart';
import 'package:drift_flutter/drift_flutter.dart'
show DriftNativeOptions, driftDatabase;
import 'package:path_provider/path_provider.dart';
import 'package:twonly/src/database/daos/contacts.dao.dart';
import 'package:twonly/src/database/daos/groups.dao.dart';
import 'package:twonly/src/database/daos/messages.dao.dart';
import 'package:twonly/src/database/daos/reactions.dao.dart';
import 'package:twonly/src/database/daos/receipts.dao.dart';
import 'package:twonly/src/database/daos/signal.dao.dart';
import 'package:twonly/src/database/tables/contacts.table.dart';
import 'package:twonly/src/database/tables/groups.table.dart';
import 'package:twonly/src/database/tables/mediafiles.table.dart';
import 'package:twonly/src/database/tables/messages.table.dart';
import 'package:twonly/src/database/tables/reactions.table.dart';
import 'package:twonly/src/database/tables/receipts.table.dart';
import 'package:twonly/src/database/tables/signal_contact_prekey.table.dart';
import 'package:twonly/src/database/tables/signal_contact_signed_prekey.table.dart';
import 'package:twonly/src/database/tables/signal_identity_key_store.table.dart';
import 'package:twonly/src/database/tables/signal_pre_key_store.table.dart';
import 'package:twonly/src/database/tables/signal_sender_key_store.table.dart';
import 'package:twonly/src/database/tables/signal_session_store.table.dart';
import 'package:twonly/src/utils/log.dart';
part 'twonly.db.g.dart';
// You can then create a database class that includes this table
@DriftDatabase(
tables: [
Contacts,
Messages,
MessageHistories,
MediaFiles,
Reactions,
Groups,
GroupMembers,
Receipts,
SignalIdentityKeyStores,
SignalPreKeyStores,
SignalSenderKeyStores,
SignalSessionStores,
SignalContactPreKeys,
SignalContactSignedPreKeys,
],
daos: [
MessagesDao,
ContactsDao,
SignalDao,
ReceiptsDao,
GroupsDao,
ReactionsDao
],
)
class TwonlyDB extends _$TwonlyDB {
TwonlyDB([QueryExecutor? e])
: super(
e ?? _openConnection(),
);
// ignore: matching_super_parameters
TwonlyDB.forTesting(DatabaseConnection super.connection);
@override
int get schemaVersion => 1;
static QueryExecutor _openConnection() {
return driftDatabase(
name: 'twonly',
native: const DriftNativeOptions(
databaseDirectory: getApplicationSupportDirectory,
),
);
}
@override
MigrationStrategy get migration {
return MigrationStrategy(
beforeOpen: (details) async {
await customStatement('PRAGMA foreign_keys = ON');
},
// onUpgrade: stepByStep(),
);
}
void markUpdated() {
notifyUpdates({TableUpdate.onTable(messages, kind: UpdateKind.update)});
notifyUpdates({TableUpdate.onTable(contacts, kind: UpdateKind.update)});
}
Future<void> printTableSizes() async {
final result = await customSelect(
'SELECT name, SUM(pgsize) as size FROM dbstat GROUP BY name',
).get();
for (final row in result) {
final tableName = row.read<String>('name');
final tableSize = row.read<String>('size');
Log.info('Table: $tableName, Size: $tableSize bytes');
}
}
Future<void> deleteDataForTwonlySafe() async {
// await delete(messages).go();
// await delete(messageRetransmissions).go();
// await delete(mediaUploads).go();
// await update(contacts).write(
// const ContactsCompanion(
// avatarSvg: Value(null),
// myAvatarCounter: Value(0),
// ),
// );
// await delete(signalContactPreKeys).go();
// await delete(signalContactSignedPreKeys).go();
// await (delete(signalPreKeyStores)
// ..where(
// (t) => (t.createdAt.isSmallerThanValue(
// DateTime.now().subtract(
// const Duration(days: 25),
// ),
// )),
// ))
// .go();
}
}

File diff suppressed because it is too large Load diff

View file

@ -2,25 +2,20 @@ import 'package:drift/drift.dart';
import 'package:drift_flutter/drift_flutter.dart'
show DriftNativeOptions, driftDatabase;
import 'package:path_provider/path_provider.dart';
import 'package:twonly/src/database/daos/contacts_dao.dart';
import 'package:twonly/src/database/daos/media_uploads_dao.dart';
import 'package:twonly/src/database/daos/message_retransmissions.dao.dart';
import 'package:twonly/src/database/daos/messages_dao.dart';
import 'package:twonly/src/database/daos/signal_dao.dart';
import 'package:twonly/src/database/tables/contacts_table.dart';
import 'package:twonly/src/database/tables/media_uploads_table.dart';
import 'package:twonly/src/database/tables/message_retransmissions.dart';
import 'package:twonly/src/database/tables/messages_table.dart';
import 'package:twonly/src/database/tables/signal_contact_prekey_table.dart';
import 'package:twonly/src/database/tables/signal_contact_signed_prekey_table.dart';
import 'package:twonly/src/database/tables/signal_identity_key_store_table.dart';
import 'package:twonly/src/database/tables/signal_pre_key_store_table.dart';
import 'package:twonly/src/database/tables/signal_sender_key_store_table.dart';
import 'package:twonly/src/database/tables/signal_session_store_table.dart';
import 'package:twonly/src/database/twonly_database.steps.dart';
import 'package:twonly/src/database/tables_old/contacts_table.dart';
import 'package:twonly/src/database/tables_old/media_uploads_table.dart';
import 'package:twonly/src/database/tables_old/message_retransmissions.dart';
import 'package:twonly/src/database/tables_old/messages_table.dart';
import 'package:twonly/src/database/tables_old/signal_contact_prekey_table.dart';
import 'package:twonly/src/database/tables_old/signal_contact_signed_prekey_table.dart';
import 'package:twonly/src/database/tables_old/signal_identity_key_store_table.dart';
import 'package:twonly/src/database/tables_old/signal_pre_key_store_table.dart';
import 'package:twonly/src/database/tables_old/signal_sender_key_store_table.dart';
import 'package:twonly/src/database/tables_old/signal_session_store_table.dart';
import 'package:twonly/src/database/twonly_database_old.steps.dart';
import 'package:twonly/src/utils/log.dart';
part 'twonly_database.g.dart';
part 'twonly_database_old.g.dart';
// You can then create a database class that includes this table
@DriftDatabase(
@ -36,22 +31,16 @@ part 'twonly_database.g.dart';
SignalContactSignedPreKeys,
MessageRetransmissions,
],
daos: [
MessagesDao,
ContactsDao,
MediaUploadsDao,
SignalDao,
MessageRetransmissionDao,
],
daos: [],
)
class TwonlyDatabase extends _$TwonlyDatabase {
TwonlyDatabase([QueryExecutor? e])
class TwonlyDatabaseOld extends _$TwonlyDatabaseOld {
TwonlyDatabaseOld([QueryExecutor? e])
: super(
e ?? _openConnection(),
);
// ignore: matching_super_parameters
TwonlyDatabase.forTesting(DatabaseConnection super.connection);
TwonlyDatabaseOld.forTesting(DatabaseConnection super.connection);
@override
int get schemaVersion => 17;

View file

@ -1,6 +1,6 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'twonly_database.dart';
part of 'twonly_database_old.dart';
// ignore_for_file: type=lint
class $ContactsTable extends Contacts with TableInfo<$ContactsTable, Contact> {
@ -4443,9 +4443,9 @@ class MessageRetransmissionsCompanion
}
}
abstract class _$TwonlyDatabase extends GeneratedDatabase {
_$TwonlyDatabase(QueryExecutor e) : super(e);
$TwonlyDatabaseManager get managers => $TwonlyDatabaseManager(this);
abstract class _$TwonlyDatabaseOld extends GeneratedDatabase {
_$TwonlyDatabaseOld(QueryExecutor e) : super(e);
$TwonlyDatabaseOldManager get managers => $TwonlyDatabaseOldManager(this);
late final $ContactsTable contacts = $ContactsTable(this);
late final $MessagesTable messages = $MessagesTable(this);
late final $MediaUploadsTable mediaUploads = $MediaUploadsTable(this);
@ -4463,13 +4463,6 @@ abstract class _$TwonlyDatabase extends GeneratedDatabase {
$SignalContactSignedPreKeysTable(this);
late final $MessageRetransmissionsTable messageRetransmissions =
$MessageRetransmissionsTable(this);
late final MessagesDao messagesDao = MessagesDao(this as TwonlyDatabase);
late final ContactsDao contactsDao = ContactsDao(this as TwonlyDatabase);
late final MediaUploadsDao mediaUploadsDao =
MediaUploadsDao(this as TwonlyDatabase);
late final SignalDao signalDao = SignalDao(this as TwonlyDatabase);
late final MessageRetransmissionDao messageRetransmissionDao =
MessageRetransmissionDao(this as TwonlyDatabase);
@override
Iterable<TableInfo<Table, Object?>> get allTables =>
allSchemaEntities.whereType<TableInfo<Table, Object?>>();
@ -4559,11 +4552,11 @@ typedef $$ContactsTableUpdateCompanionBuilder = ContactsCompanion Function({
});
final class $$ContactsTableReferences
extends BaseReferences<_$TwonlyDatabase, $ContactsTable, Contact> {
extends BaseReferences<_$TwonlyDatabaseOld, $ContactsTable, Contact> {
$$ContactsTableReferences(super.$_db, super.$_table, super.$_typedResult);
static MultiTypedResultKey<$MessagesTable, List<Message>> _messagesRefsTable(
_$TwonlyDatabase db) =>
_$TwonlyDatabaseOld db) =>
MultiTypedResultKey.fromTable(db.messages,
aliasName:
$_aliasNameGenerator(db.contacts.userId, db.messages.contactId));
@ -4579,7 +4572,7 @@ final class $$ContactsTableReferences
static MultiTypedResultKey<$MessageRetransmissionsTable,
List<MessageRetransmission>> _messageRetransmissionsRefsTable(
_$TwonlyDatabase db) =>
_$TwonlyDatabaseOld db) =>
MultiTypedResultKey.fromTable(db.messageRetransmissions,
aliasName: $_aliasNameGenerator(
db.contacts.userId, db.messageRetransmissions.contactId));
@ -4599,7 +4592,7 @@ final class $$ContactsTableReferences
}
class $$ContactsTableFilterComposer
extends Composer<_$TwonlyDatabase, $ContactsTable> {
extends Composer<_$TwonlyDatabaseOld, $ContactsTable> {
$$ContactsTableFilterComposer({
required super.$db,
required super.$table,
@ -4730,7 +4723,7 @@ class $$ContactsTableFilterComposer
}
class $$ContactsTableOrderingComposer
extends Composer<_$TwonlyDatabase, $ContactsTable> {
extends Composer<_$TwonlyDatabaseOld, $ContactsTable> {
$$ContactsTableOrderingComposer({
required super.$db,
required super.$table,
@ -4819,7 +4812,7 @@ class $$ContactsTableOrderingComposer
}
class $$ContactsTableAnnotationComposer
extends Composer<_$TwonlyDatabase, $ContactsTable> {
extends Composer<_$TwonlyDatabaseOld, $ContactsTable> {
$$ContactsTableAnnotationComposer({
required super.$db,
required super.$table,
@ -4942,7 +4935,7 @@ class $$ContactsTableAnnotationComposer
}
class $$ContactsTableTableManager extends RootTableManager<
_$TwonlyDatabase,
_$TwonlyDatabaseOld,
$ContactsTable,
Contact,
$$ContactsTableFilterComposer,
@ -4954,7 +4947,7 @@ class $$ContactsTableTableManager extends RootTableManager<
Contact,
PrefetchHooks Function(
{bool messagesRefs, bool messageRetransmissionsRefs})> {
$$ContactsTableTableManager(_$TwonlyDatabase db, $ContactsTable table)
$$ContactsTableTableManager(_$TwonlyDatabaseOld db, $ContactsTable table)
: super(TableManagerState(
db: db,
table: table,
@ -5112,7 +5105,7 @@ class $$ContactsTableTableManager extends RootTableManager<
}
typedef $$ContactsTableProcessedTableManager = ProcessedTableManager<
_$TwonlyDatabase,
_$TwonlyDatabaseOld,
$ContactsTable,
Contact,
$$ContactsTableFilterComposer,
@ -5166,10 +5159,10 @@ typedef $$MessagesTableUpdateCompanionBuilder = MessagesCompanion Function({
});
final class $$MessagesTableReferences
extends BaseReferences<_$TwonlyDatabase, $MessagesTable, Message> {
extends BaseReferences<_$TwonlyDatabaseOld, $MessagesTable, Message> {
$$MessagesTableReferences(super.$_db, super.$_table, super.$_typedResult);
static $ContactsTable _contactIdTable(_$TwonlyDatabase db) =>
static $ContactsTable _contactIdTable(_$TwonlyDatabaseOld db) =>
db.contacts.createAlias(
$_aliasNameGenerator(db.messages.contactId, db.contacts.userId));
@ -5186,7 +5179,7 @@ final class $$MessagesTableReferences
static MultiTypedResultKey<$MessageRetransmissionsTable,
List<MessageRetransmission>> _messageRetransmissionsRefsTable(
_$TwonlyDatabase db) =>
_$TwonlyDatabaseOld db) =>
MultiTypedResultKey.fromTable(db.messageRetransmissions,
aliasName: $_aliasNameGenerator(
db.messages.messageId, db.messageRetransmissions.messageId));
@ -5206,7 +5199,7 @@ final class $$MessagesTableReferences
}
class $$MessagesTableFilterComposer
extends Composer<_$TwonlyDatabase, $MessagesTable> {
extends Composer<_$TwonlyDatabaseOld, $MessagesTable> {
$$MessagesTableFilterComposer({
required super.$db,
required super.$table,
@ -5324,7 +5317,7 @@ class $$MessagesTableFilterComposer
}
class $$MessagesTableOrderingComposer
extends Composer<_$TwonlyDatabase, $MessagesTable> {
extends Composer<_$TwonlyDatabaseOld, $MessagesTable> {
$$MessagesTableOrderingComposer({
required super.$db,
required super.$table,
@ -5415,7 +5408,7 @@ class $$MessagesTableOrderingComposer
}
class $$MessagesTableAnnotationComposer
extends Composer<_$TwonlyDatabase, $MessagesTable> {
extends Composer<_$TwonlyDatabaseOld, $MessagesTable> {
$$MessagesTableAnnotationComposer({
required super.$db,
required super.$table,
@ -5521,7 +5514,7 @@ class $$MessagesTableAnnotationComposer
}
class $$MessagesTableTableManager extends RootTableManager<
_$TwonlyDatabase,
_$TwonlyDatabaseOld,
$MessagesTable,
Message,
$$MessagesTableFilterComposer,
@ -5532,7 +5525,7 @@ class $$MessagesTableTableManager extends RootTableManager<
(Message, $$MessagesTableReferences),
Message,
PrefetchHooks Function({bool contactId, bool messageRetransmissionsRefs})> {
$$MessagesTableTableManager(_$TwonlyDatabase db, $MessagesTable table)
$$MessagesTableTableManager(_$TwonlyDatabaseOld db, $MessagesTable table)
: super(TableManagerState(
db: db,
table: table,
@ -5684,7 +5677,7 @@ class $$MessagesTableTableManager extends RootTableManager<
}
typedef $$MessagesTableProcessedTableManager = ProcessedTableManager<
_$TwonlyDatabase,
_$TwonlyDatabaseOld,
$MessagesTable,
Message,
$$MessagesTableFilterComposer,
@ -5713,7 +5706,7 @@ typedef $$MediaUploadsTableUpdateCompanionBuilder = MediaUploadsCompanion
});
class $$MediaUploadsTableFilterComposer
extends Composer<_$TwonlyDatabase, $MediaUploadsTable> {
extends Composer<_$TwonlyDatabaseOld, $MediaUploadsTable> {
$$MediaUploadsTableFilterComposer({
required super.$db,
required super.$table,
@ -5748,7 +5741,7 @@ class $$MediaUploadsTableFilterComposer
}
class $$MediaUploadsTableOrderingComposer
extends Composer<_$TwonlyDatabase, $MediaUploadsTable> {
extends Composer<_$TwonlyDatabaseOld, $MediaUploadsTable> {
$$MediaUploadsTableOrderingComposer({
required super.$db,
required super.$table,
@ -5775,7 +5768,7 @@ class $$MediaUploadsTableOrderingComposer
}
class $$MediaUploadsTableAnnotationComposer
extends Composer<_$TwonlyDatabase, $MediaUploadsTable> {
extends Composer<_$TwonlyDatabaseOld, $MediaUploadsTable> {
$$MediaUploadsTableAnnotationComposer({
required super.$db,
required super.$table,
@ -5802,7 +5795,7 @@ class $$MediaUploadsTableAnnotationComposer
}
class $$MediaUploadsTableTableManager extends RootTableManager<
_$TwonlyDatabase,
_$TwonlyDatabaseOld,
$MediaUploadsTable,
MediaUpload,
$$MediaUploadsTableFilterComposer,
@ -5812,11 +5805,12 @@ class $$MediaUploadsTableTableManager extends RootTableManager<
$$MediaUploadsTableUpdateCompanionBuilder,
(
MediaUpload,
BaseReferences<_$TwonlyDatabase, $MediaUploadsTable, MediaUpload>
BaseReferences<_$TwonlyDatabaseOld, $MediaUploadsTable, MediaUpload>
),
MediaUpload,
PrefetchHooks Function()> {
$$MediaUploadsTableTableManager(_$TwonlyDatabase db, $MediaUploadsTable table)
$$MediaUploadsTableTableManager(
_$TwonlyDatabaseOld db, $MediaUploadsTable table)
: super(TableManagerState(
db: db,
table: table,
@ -5862,7 +5856,7 @@ class $$MediaUploadsTableTableManager extends RootTableManager<
}
typedef $$MediaUploadsTableProcessedTableManager = ProcessedTableManager<
_$TwonlyDatabase,
_$TwonlyDatabaseOld,
$MediaUploadsTable,
MediaUpload,
$$MediaUploadsTableFilterComposer,
@ -5872,7 +5866,7 @@ typedef $$MediaUploadsTableProcessedTableManager = ProcessedTableManager<
$$MediaUploadsTableUpdateCompanionBuilder,
(
MediaUpload,
BaseReferences<_$TwonlyDatabase, $MediaUploadsTable, MediaUpload>
BaseReferences<_$TwonlyDatabaseOld, $MediaUploadsTable, MediaUpload>
),
MediaUpload,
PrefetchHooks Function()>;
@ -5894,7 +5888,7 @@ typedef $$SignalIdentityKeyStoresTableUpdateCompanionBuilder
});
class $$SignalIdentityKeyStoresTableFilterComposer
extends Composer<_$TwonlyDatabase, $SignalIdentityKeyStoresTable> {
extends Composer<_$TwonlyDatabaseOld, $SignalIdentityKeyStoresTable> {
$$SignalIdentityKeyStoresTableFilterComposer({
required super.$db,
required super.$table,
@ -5916,7 +5910,7 @@ class $$SignalIdentityKeyStoresTableFilterComposer
}
class $$SignalIdentityKeyStoresTableOrderingComposer
extends Composer<_$TwonlyDatabase, $SignalIdentityKeyStoresTable> {
extends Composer<_$TwonlyDatabaseOld, $SignalIdentityKeyStoresTable> {
$$SignalIdentityKeyStoresTableOrderingComposer({
required super.$db,
required super.$table,
@ -5938,7 +5932,7 @@ class $$SignalIdentityKeyStoresTableOrderingComposer
}
class $$SignalIdentityKeyStoresTableAnnotationComposer
extends Composer<_$TwonlyDatabase, $SignalIdentityKeyStoresTable> {
extends Composer<_$TwonlyDatabaseOld, $SignalIdentityKeyStoresTable> {
$$SignalIdentityKeyStoresTableAnnotationComposer({
required super.$db,
required super.$table,
@ -5960,7 +5954,7 @@ class $$SignalIdentityKeyStoresTableAnnotationComposer
}
class $$SignalIdentityKeyStoresTableTableManager extends RootTableManager<
_$TwonlyDatabase,
_$TwonlyDatabaseOld,
$SignalIdentityKeyStoresTable,
SignalIdentityKeyStore,
$$SignalIdentityKeyStoresTableFilterComposer,
@ -5970,13 +5964,13 @@ class $$SignalIdentityKeyStoresTableTableManager extends RootTableManager<
$$SignalIdentityKeyStoresTableUpdateCompanionBuilder,
(
SignalIdentityKeyStore,
BaseReferences<_$TwonlyDatabase, $SignalIdentityKeyStoresTable,
BaseReferences<_$TwonlyDatabaseOld, $SignalIdentityKeyStoresTable,
SignalIdentityKeyStore>
),
SignalIdentityKeyStore,
PrefetchHooks Function()> {
$$SignalIdentityKeyStoresTableTableManager(
_$TwonlyDatabase db, $SignalIdentityKeyStoresTable table)
_$TwonlyDatabaseOld db, $SignalIdentityKeyStoresTable table)
: super(TableManagerState(
db: db,
table: table,
@ -6026,7 +6020,7 @@ class $$SignalIdentityKeyStoresTableTableManager extends RootTableManager<
typedef $$SignalIdentityKeyStoresTableProcessedTableManager
= ProcessedTableManager<
_$TwonlyDatabase,
_$TwonlyDatabaseOld,
$SignalIdentityKeyStoresTable,
SignalIdentityKeyStore,
$$SignalIdentityKeyStoresTableFilterComposer,
@ -6036,7 +6030,7 @@ typedef $$SignalIdentityKeyStoresTableProcessedTableManager
$$SignalIdentityKeyStoresTableUpdateCompanionBuilder,
(
SignalIdentityKeyStore,
BaseReferences<_$TwonlyDatabase, $SignalIdentityKeyStoresTable,
BaseReferences<_$TwonlyDatabaseOld, $SignalIdentityKeyStoresTable,
SignalIdentityKeyStore>
),
SignalIdentityKeyStore,
@ -6055,7 +6049,7 @@ typedef $$SignalPreKeyStoresTableUpdateCompanionBuilder
});
class $$SignalPreKeyStoresTableFilterComposer
extends Composer<_$TwonlyDatabase, $SignalPreKeyStoresTable> {
extends Composer<_$TwonlyDatabaseOld, $SignalPreKeyStoresTable> {
$$SignalPreKeyStoresTableFilterComposer({
required super.$db,
required super.$table,
@ -6074,7 +6068,7 @@ class $$SignalPreKeyStoresTableFilterComposer
}
class $$SignalPreKeyStoresTableOrderingComposer
extends Composer<_$TwonlyDatabase, $SignalPreKeyStoresTable> {
extends Composer<_$TwonlyDatabaseOld, $SignalPreKeyStoresTable> {
$$SignalPreKeyStoresTableOrderingComposer({
required super.$db,
required super.$table,
@ -6093,7 +6087,7 @@ class $$SignalPreKeyStoresTableOrderingComposer
}
class $$SignalPreKeyStoresTableAnnotationComposer
extends Composer<_$TwonlyDatabase, $SignalPreKeyStoresTable> {
extends Composer<_$TwonlyDatabaseOld, $SignalPreKeyStoresTable> {
$$SignalPreKeyStoresTableAnnotationComposer({
required super.$db,
required super.$table,
@ -6112,7 +6106,7 @@ class $$SignalPreKeyStoresTableAnnotationComposer
}
class $$SignalPreKeyStoresTableTableManager extends RootTableManager<
_$TwonlyDatabase,
_$TwonlyDatabaseOld,
$SignalPreKeyStoresTable,
SignalPreKeyStore,
$$SignalPreKeyStoresTableFilterComposer,
@ -6122,13 +6116,13 @@ class $$SignalPreKeyStoresTableTableManager extends RootTableManager<
$$SignalPreKeyStoresTableUpdateCompanionBuilder,
(
SignalPreKeyStore,
BaseReferences<_$TwonlyDatabase, $SignalPreKeyStoresTable,
BaseReferences<_$TwonlyDatabaseOld, $SignalPreKeyStoresTable,
SignalPreKeyStore>
),
SignalPreKeyStore,
PrefetchHooks Function()> {
$$SignalPreKeyStoresTableTableManager(
_$TwonlyDatabase db, $SignalPreKeyStoresTable table)
_$TwonlyDatabaseOld db, $SignalPreKeyStoresTable table)
: super(TableManagerState(
db: db,
table: table,
@ -6167,7 +6161,7 @@ class $$SignalPreKeyStoresTableTableManager extends RootTableManager<
}
typedef $$SignalPreKeyStoresTableProcessedTableManager = ProcessedTableManager<
_$TwonlyDatabase,
_$TwonlyDatabaseOld,
$SignalPreKeyStoresTable,
SignalPreKeyStore,
$$SignalPreKeyStoresTableFilterComposer,
@ -6177,7 +6171,7 @@ typedef $$SignalPreKeyStoresTableProcessedTableManager = ProcessedTableManager<
$$SignalPreKeyStoresTableUpdateCompanionBuilder,
(
SignalPreKeyStore,
BaseReferences<_$TwonlyDatabase, $SignalPreKeyStoresTable,
BaseReferences<_$TwonlyDatabaseOld, $SignalPreKeyStoresTable,
SignalPreKeyStore>
),
SignalPreKeyStore,
@ -6196,7 +6190,7 @@ typedef $$SignalSenderKeyStoresTableUpdateCompanionBuilder
});
class $$SignalSenderKeyStoresTableFilterComposer
extends Composer<_$TwonlyDatabase, $SignalSenderKeyStoresTable> {
extends Composer<_$TwonlyDatabaseOld, $SignalSenderKeyStoresTable> {
$$SignalSenderKeyStoresTableFilterComposer({
required super.$db,
required super.$table,
@ -6212,7 +6206,7 @@ class $$SignalSenderKeyStoresTableFilterComposer
}
class $$SignalSenderKeyStoresTableOrderingComposer
extends Composer<_$TwonlyDatabase, $SignalSenderKeyStoresTable> {
extends Composer<_$TwonlyDatabaseOld, $SignalSenderKeyStoresTable> {
$$SignalSenderKeyStoresTableOrderingComposer({
required super.$db,
required super.$table,
@ -6229,7 +6223,7 @@ class $$SignalSenderKeyStoresTableOrderingComposer
}
class $$SignalSenderKeyStoresTableAnnotationComposer
extends Composer<_$TwonlyDatabase, $SignalSenderKeyStoresTable> {
extends Composer<_$TwonlyDatabaseOld, $SignalSenderKeyStoresTable> {
$$SignalSenderKeyStoresTableAnnotationComposer({
required super.$db,
required super.$table,
@ -6245,7 +6239,7 @@ class $$SignalSenderKeyStoresTableAnnotationComposer
}
class $$SignalSenderKeyStoresTableTableManager extends RootTableManager<
_$TwonlyDatabase,
_$TwonlyDatabaseOld,
$SignalSenderKeyStoresTable,
SignalSenderKeyStore,
$$SignalSenderKeyStoresTableFilterComposer,
@ -6255,13 +6249,13 @@ class $$SignalSenderKeyStoresTableTableManager extends RootTableManager<
$$SignalSenderKeyStoresTableUpdateCompanionBuilder,
(
SignalSenderKeyStore,
BaseReferences<_$TwonlyDatabase, $SignalSenderKeyStoresTable,
BaseReferences<_$TwonlyDatabaseOld, $SignalSenderKeyStoresTable,
SignalSenderKeyStore>
),
SignalSenderKeyStore,
PrefetchHooks Function()> {
$$SignalSenderKeyStoresTableTableManager(
_$TwonlyDatabase db, $SignalSenderKeyStoresTable table)
_$TwonlyDatabaseOld db, $SignalSenderKeyStoresTable table)
: super(TableManagerState(
db: db,
table: table,
@ -6303,7 +6297,7 @@ class $$SignalSenderKeyStoresTableTableManager extends RootTableManager<
typedef $$SignalSenderKeyStoresTableProcessedTableManager
= ProcessedTableManager<
_$TwonlyDatabase,
_$TwonlyDatabaseOld,
$SignalSenderKeyStoresTable,
SignalSenderKeyStore,
$$SignalSenderKeyStoresTableFilterComposer,
@ -6313,7 +6307,7 @@ typedef $$SignalSenderKeyStoresTableProcessedTableManager
$$SignalSenderKeyStoresTableUpdateCompanionBuilder,
(
SignalSenderKeyStore,
BaseReferences<_$TwonlyDatabase, $SignalSenderKeyStoresTable,
BaseReferences<_$TwonlyDatabaseOld, $SignalSenderKeyStoresTable,
SignalSenderKeyStore>
),
SignalSenderKeyStore,
@ -6336,7 +6330,7 @@ typedef $$SignalSessionStoresTableUpdateCompanionBuilder
});
class $$SignalSessionStoresTableFilterComposer
extends Composer<_$TwonlyDatabase, $SignalSessionStoresTable> {
extends Composer<_$TwonlyDatabaseOld, $SignalSessionStoresTable> {
$$SignalSessionStoresTableFilterComposer({
required super.$db,
required super.$table,
@ -6358,7 +6352,7 @@ class $$SignalSessionStoresTableFilterComposer
}
class $$SignalSessionStoresTableOrderingComposer
extends Composer<_$TwonlyDatabase, $SignalSessionStoresTable> {
extends Composer<_$TwonlyDatabaseOld, $SignalSessionStoresTable> {
$$SignalSessionStoresTableOrderingComposer({
required super.$db,
required super.$table,
@ -6381,7 +6375,7 @@ class $$SignalSessionStoresTableOrderingComposer
}
class $$SignalSessionStoresTableAnnotationComposer
extends Composer<_$TwonlyDatabase, $SignalSessionStoresTable> {
extends Composer<_$TwonlyDatabaseOld, $SignalSessionStoresTable> {
$$SignalSessionStoresTableAnnotationComposer({
required super.$db,
required super.$table,
@ -6403,7 +6397,7 @@ class $$SignalSessionStoresTableAnnotationComposer
}
class $$SignalSessionStoresTableTableManager extends RootTableManager<
_$TwonlyDatabase,
_$TwonlyDatabaseOld,
$SignalSessionStoresTable,
SignalSessionStore,
$$SignalSessionStoresTableFilterComposer,
@ -6413,13 +6407,13 @@ class $$SignalSessionStoresTableTableManager extends RootTableManager<
$$SignalSessionStoresTableUpdateCompanionBuilder,
(
SignalSessionStore,
BaseReferences<_$TwonlyDatabase, $SignalSessionStoresTable,
BaseReferences<_$TwonlyDatabaseOld, $SignalSessionStoresTable,
SignalSessionStore>
),
SignalSessionStore,
PrefetchHooks Function()> {
$$SignalSessionStoresTableTableManager(
_$TwonlyDatabase db, $SignalSessionStoresTable table)
_$TwonlyDatabaseOld db, $SignalSessionStoresTable table)
: super(TableManagerState(
db: db,
table: table,
@ -6467,7 +6461,7 @@ class $$SignalSessionStoresTableTableManager extends RootTableManager<
}
typedef $$SignalSessionStoresTableProcessedTableManager = ProcessedTableManager<
_$TwonlyDatabase,
_$TwonlyDatabaseOld,
$SignalSessionStoresTable,
SignalSessionStore,
$$SignalSessionStoresTableFilterComposer,
@ -6477,7 +6471,7 @@ typedef $$SignalSessionStoresTableProcessedTableManager = ProcessedTableManager<
$$SignalSessionStoresTableUpdateCompanionBuilder,
(
SignalSessionStore,
BaseReferences<_$TwonlyDatabase, $SignalSessionStoresTable,
BaseReferences<_$TwonlyDatabaseOld, $SignalSessionStoresTable,
SignalSessionStore>
),
SignalSessionStore,
@ -6500,7 +6494,7 @@ typedef $$SignalContactPreKeysTableUpdateCompanionBuilder
});
class $$SignalContactPreKeysTableFilterComposer
extends Composer<_$TwonlyDatabase, $SignalContactPreKeysTable> {
extends Composer<_$TwonlyDatabaseOld, $SignalContactPreKeysTable> {
$$SignalContactPreKeysTableFilterComposer({
required super.$db,
required super.$table,
@ -6522,7 +6516,7 @@ class $$SignalContactPreKeysTableFilterComposer
}
class $$SignalContactPreKeysTableOrderingComposer
extends Composer<_$TwonlyDatabase, $SignalContactPreKeysTable> {
extends Composer<_$TwonlyDatabaseOld, $SignalContactPreKeysTable> {
$$SignalContactPreKeysTableOrderingComposer({
required super.$db,
required super.$table,
@ -6544,7 +6538,7 @@ class $$SignalContactPreKeysTableOrderingComposer
}
class $$SignalContactPreKeysTableAnnotationComposer
extends Composer<_$TwonlyDatabase, $SignalContactPreKeysTable> {
extends Composer<_$TwonlyDatabaseOld, $SignalContactPreKeysTable> {
$$SignalContactPreKeysTableAnnotationComposer({
required super.$db,
required super.$table,
@ -6566,7 +6560,7 @@ class $$SignalContactPreKeysTableAnnotationComposer
}
class $$SignalContactPreKeysTableTableManager extends RootTableManager<
_$TwonlyDatabase,
_$TwonlyDatabaseOld,
$SignalContactPreKeysTable,
SignalContactPreKey,
$$SignalContactPreKeysTableFilterComposer,
@ -6576,13 +6570,13 @@ class $$SignalContactPreKeysTableTableManager extends RootTableManager<
$$SignalContactPreKeysTableUpdateCompanionBuilder,
(
SignalContactPreKey,
BaseReferences<_$TwonlyDatabase, $SignalContactPreKeysTable,
BaseReferences<_$TwonlyDatabaseOld, $SignalContactPreKeysTable,
SignalContactPreKey>
),
SignalContactPreKey,
PrefetchHooks Function()> {
$$SignalContactPreKeysTableTableManager(
_$TwonlyDatabase db, $SignalContactPreKeysTable table)
_$TwonlyDatabaseOld db, $SignalContactPreKeysTable table)
: super(TableManagerState(
db: db,
table: table,
@ -6631,7 +6625,7 @@ class $$SignalContactPreKeysTableTableManager extends RootTableManager<
typedef $$SignalContactPreKeysTableProcessedTableManager
= ProcessedTableManager<
_$TwonlyDatabase,
_$TwonlyDatabaseOld,
$SignalContactPreKeysTable,
SignalContactPreKey,
$$SignalContactPreKeysTableFilterComposer,
@ -6641,7 +6635,7 @@ typedef $$SignalContactPreKeysTableProcessedTableManager
$$SignalContactPreKeysTableUpdateCompanionBuilder,
(
SignalContactPreKey,
BaseReferences<_$TwonlyDatabase, $SignalContactPreKeysTable,
BaseReferences<_$TwonlyDatabaseOld, $SignalContactPreKeysTable,
SignalContactPreKey>
),
SignalContactPreKey,
@ -6664,7 +6658,7 @@ typedef $$SignalContactSignedPreKeysTableUpdateCompanionBuilder
});
class $$SignalContactSignedPreKeysTableFilterComposer
extends Composer<_$TwonlyDatabase, $SignalContactSignedPreKeysTable> {
extends Composer<_$TwonlyDatabaseOld, $SignalContactSignedPreKeysTable> {
$$SignalContactSignedPreKeysTableFilterComposer({
required super.$db,
required super.$table,
@ -6691,7 +6685,7 @@ class $$SignalContactSignedPreKeysTableFilterComposer
}
class $$SignalContactSignedPreKeysTableOrderingComposer
extends Composer<_$TwonlyDatabase, $SignalContactSignedPreKeysTable> {
extends Composer<_$TwonlyDatabaseOld, $SignalContactSignedPreKeysTable> {
$$SignalContactSignedPreKeysTableOrderingComposer({
required super.$db,
required super.$table,
@ -6719,7 +6713,7 @@ class $$SignalContactSignedPreKeysTableOrderingComposer
}
class $$SignalContactSignedPreKeysTableAnnotationComposer
extends Composer<_$TwonlyDatabase, $SignalContactSignedPreKeysTable> {
extends Composer<_$TwonlyDatabaseOld, $SignalContactSignedPreKeysTable> {
$$SignalContactSignedPreKeysTableAnnotationComposer({
required super.$db,
required super.$table,
@ -6744,7 +6738,7 @@ class $$SignalContactSignedPreKeysTableAnnotationComposer
}
class $$SignalContactSignedPreKeysTableTableManager extends RootTableManager<
_$TwonlyDatabase,
_$TwonlyDatabaseOld,
$SignalContactSignedPreKeysTable,
SignalContactSignedPreKey,
$$SignalContactSignedPreKeysTableFilterComposer,
@ -6754,13 +6748,13 @@ class $$SignalContactSignedPreKeysTableTableManager extends RootTableManager<
$$SignalContactSignedPreKeysTableUpdateCompanionBuilder,
(
SignalContactSignedPreKey,
BaseReferences<_$TwonlyDatabase, $SignalContactSignedPreKeysTable,
BaseReferences<_$TwonlyDatabaseOld, $SignalContactSignedPreKeysTable,
SignalContactSignedPreKey>
),
SignalContactSignedPreKey,
PrefetchHooks Function()> {
$$SignalContactSignedPreKeysTableTableManager(
_$TwonlyDatabase db, $SignalContactSignedPreKeysTable table)
_$TwonlyDatabaseOld db, $SignalContactSignedPreKeysTable table)
: super(TableManagerState(
db: db,
table: table,
@ -6810,7 +6804,7 @@ class $$SignalContactSignedPreKeysTableTableManager extends RootTableManager<
typedef $$SignalContactSignedPreKeysTableProcessedTableManager
= ProcessedTableManager<
_$TwonlyDatabase,
_$TwonlyDatabaseOld,
$SignalContactSignedPreKeysTable,
SignalContactSignedPreKey,
$$SignalContactSignedPreKeysTableFilterComposer,
@ -6820,7 +6814,7 @@ typedef $$SignalContactSignedPreKeysTableProcessedTableManager
$$SignalContactSignedPreKeysTableUpdateCompanionBuilder,
(
SignalContactSignedPreKey,
BaseReferences<_$TwonlyDatabase, $SignalContactSignedPreKeysTable,
BaseReferences<_$TwonlyDatabaseOld, $SignalContactSignedPreKeysTable,
SignalContactSignedPreKey>
),
SignalContactSignedPreKey,
@ -6851,11 +6845,11 @@ typedef $$MessageRetransmissionsTableUpdateCompanionBuilder
});
final class $$MessageRetransmissionsTableReferences extends BaseReferences<
_$TwonlyDatabase, $MessageRetransmissionsTable, MessageRetransmission> {
_$TwonlyDatabaseOld, $MessageRetransmissionsTable, MessageRetransmission> {
$$MessageRetransmissionsTableReferences(
super.$_db, super.$_table, super.$_typedResult);
static $ContactsTable _contactIdTable(_$TwonlyDatabase db) =>
static $ContactsTable _contactIdTable(_$TwonlyDatabaseOld db) =>
db.contacts.createAlias($_aliasNameGenerator(
db.messageRetransmissions.contactId, db.contacts.userId));
@ -6870,7 +6864,7 @@ final class $$MessageRetransmissionsTableReferences extends BaseReferences<
manager.$state.copyWith(prefetchedData: [item]));
}
static $MessagesTable _messageIdTable(_$TwonlyDatabase db) =>
static $MessagesTable _messageIdTable(_$TwonlyDatabaseOld db) =>
db.messages.createAlias($_aliasNameGenerator(
db.messageRetransmissions.messageId, db.messages.messageId));
@ -6887,7 +6881,7 @@ final class $$MessageRetransmissionsTableReferences extends BaseReferences<
}
class $$MessageRetransmissionsTableFilterComposer
extends Composer<_$TwonlyDatabase, $MessageRetransmissionsTable> {
extends Composer<_$TwonlyDatabaseOld, $MessageRetransmissionsTable> {
$$MessageRetransmissionsTableFilterComposer({
required super.$db,
required super.$table,
@ -6961,7 +6955,7 @@ class $$MessageRetransmissionsTableFilterComposer
}
class $$MessageRetransmissionsTableOrderingComposer
extends Composer<_$TwonlyDatabase, $MessageRetransmissionsTable> {
extends Composer<_$TwonlyDatabaseOld, $MessageRetransmissionsTable> {
$$MessageRetransmissionsTableOrderingComposer({
required super.$db,
required super.$table,
@ -7036,7 +7030,7 @@ class $$MessageRetransmissionsTableOrderingComposer
}
class $$MessageRetransmissionsTableAnnotationComposer
extends Composer<_$TwonlyDatabase, $MessageRetransmissionsTable> {
extends Composer<_$TwonlyDatabaseOld, $MessageRetransmissionsTable> {
$$MessageRetransmissionsTableAnnotationComposer({
required super.$db,
required super.$table,
@ -7107,7 +7101,7 @@ class $$MessageRetransmissionsTableAnnotationComposer
}
class $$MessageRetransmissionsTableTableManager extends RootTableManager<
_$TwonlyDatabase,
_$TwonlyDatabaseOld,
$MessageRetransmissionsTable,
MessageRetransmission,
$$MessageRetransmissionsTableFilterComposer,
@ -7119,7 +7113,7 @@ class $$MessageRetransmissionsTableTableManager extends RootTableManager<
MessageRetransmission,
PrefetchHooks Function({bool contactId, bool messageId})> {
$$MessageRetransmissionsTableTableManager(
_$TwonlyDatabase db, $MessageRetransmissionsTable table)
_$TwonlyDatabaseOld db, $MessageRetransmissionsTable table)
: super(TableManagerState(
db: db,
table: table,
@ -7234,7 +7228,7 @@ class $$MessageRetransmissionsTableTableManager extends RootTableManager<
typedef $$MessageRetransmissionsTableProcessedTableManager
= ProcessedTableManager<
_$TwonlyDatabase,
_$TwonlyDatabaseOld,
$MessageRetransmissionsTable,
MessageRetransmission,
$$MessageRetransmissionsTableFilterComposer,
@ -7246,9 +7240,9 @@ typedef $$MessageRetransmissionsTableProcessedTableManager
MessageRetransmission,
PrefetchHooks Function({bool contactId, bool messageId})>;
class $TwonlyDatabaseManager {
final _$TwonlyDatabase _db;
$TwonlyDatabaseManager(this._db);
class $TwonlyDatabaseOldManager {
final _$TwonlyDatabaseOld _db;
$TwonlyDatabaseOldManager(this._db);
$$ContactsTableTableManager get contacts =>
$$ContactsTableTableManager(_db, _db.contacts);
$$MessagesTableTableManager get messages =>

View file

@ -1,7 +1,7 @@
// ignore_for_file: strict_raw_type, prefer_constructors_over_static_methods
import 'package:flutter/material.dart';
import 'package:twonly/src/database/tables/messages_table.dart';
import 'package:twonly/src/database/tables_old/messages_table.dart';
import 'package:twonly/src/utils/misc.dart';
Color getMessageColorFromType(MessageContent content, BuildContext context) {

View file

@ -3,8 +3,8 @@ import 'dart:io';
import 'package:drift/drift.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/database/twonly_database.dart';
import 'package:twonly/src/model/json/message.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/model/json/message_old.dart';
import 'package:twonly/src/services/api/media_upload.dart' as send;
import 'package:twonly/src/services/thumbnail.service.dart';

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,149 @@
//
// Generated code. Do not modify.
// source: messages.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:protobuf/protobuf.dart' as $pb;
class Message_Type extends $pb.ProtobufEnum {
static const Message_Type SENDER_DELIVERY_RECEIPT = Message_Type._(0, _omitEnumNames ? '' : 'SENDER_DELIVERY_RECEIPT');
static const Message_Type PLAINTEXT_CONTENT = Message_Type._(1, _omitEnumNames ? '' : 'PLAINTEXT_CONTENT');
static const Message_Type CIPHERTEXT = Message_Type._(2, _omitEnumNames ? '' : 'CIPHERTEXT');
static const Message_Type PREKEY_BUNDLE = Message_Type._(3, _omitEnumNames ? '' : 'PREKEY_BUNDLE');
static const $core.List<Message_Type> values = <Message_Type> [
SENDER_DELIVERY_RECEIPT,
PLAINTEXT_CONTENT,
CIPHERTEXT,
PREKEY_BUNDLE,
];
static final $core.Map<$core.int, Message_Type> _byValue = $pb.ProtobufEnum.initByValue(values);
static Message_Type? valueOf($core.int value) => _byValue[value];
const Message_Type._($core.int v, $core.String n) : super(v, n);
}
class PlaintextContent_DecryptionErrorMessage_Type extends $pb.ProtobufEnum {
static const PlaintextContent_DecryptionErrorMessage_Type UNKNOWN = PlaintextContent_DecryptionErrorMessage_Type._(0, _omitEnumNames ? '' : 'UNKNOWN');
static const PlaintextContent_DecryptionErrorMessage_Type PREKEY_UNKNOWN = PlaintextContent_DecryptionErrorMessage_Type._(1, _omitEnumNames ? '' : 'PREKEY_UNKNOWN');
static const $core.List<PlaintextContent_DecryptionErrorMessage_Type> values = <PlaintextContent_DecryptionErrorMessage_Type> [
UNKNOWN,
PREKEY_UNKNOWN,
];
static final $core.Map<$core.int, PlaintextContent_DecryptionErrorMessage_Type> _byValue = $pb.ProtobufEnum.initByValue(values);
static PlaintextContent_DecryptionErrorMessage_Type? valueOf($core.int value) => _byValue[value];
const PlaintextContent_DecryptionErrorMessage_Type._($core.int v, $core.String n) : super(v, n);
}
class EncryptedContent_MessageUpdate_Type extends $pb.ProtobufEnum {
static const EncryptedContent_MessageUpdate_Type DELETE = EncryptedContent_MessageUpdate_Type._(0, _omitEnumNames ? '' : 'DELETE');
static const EncryptedContent_MessageUpdate_Type EDIT_TEXT = EncryptedContent_MessageUpdate_Type._(1, _omitEnumNames ? '' : 'EDIT_TEXT');
static const EncryptedContent_MessageUpdate_Type OPENED = EncryptedContent_MessageUpdate_Type._(2, _omitEnumNames ? '' : 'OPENED');
static const $core.List<EncryptedContent_MessageUpdate_Type> values = <EncryptedContent_MessageUpdate_Type> [
DELETE,
EDIT_TEXT,
OPENED,
];
static final $core.Map<$core.int, EncryptedContent_MessageUpdate_Type> _byValue = $pb.ProtobufEnum.initByValue(values);
static EncryptedContent_MessageUpdate_Type? valueOf($core.int value) => _byValue[value];
const EncryptedContent_MessageUpdate_Type._($core.int v, $core.String n) : super(v, n);
}
class EncryptedContent_Media_Type extends $pb.ProtobufEnum {
static const EncryptedContent_Media_Type IMAGE = EncryptedContent_Media_Type._(0, _omitEnumNames ? '' : 'IMAGE');
static const EncryptedContent_Media_Type VIDEO = EncryptedContent_Media_Type._(1, _omitEnumNames ? '' : 'VIDEO');
static const EncryptedContent_Media_Type GIF = EncryptedContent_Media_Type._(2, _omitEnumNames ? '' : 'GIF');
static const $core.List<EncryptedContent_Media_Type> values = <EncryptedContent_Media_Type> [
IMAGE,
VIDEO,
GIF,
];
static final $core.Map<$core.int, EncryptedContent_Media_Type> _byValue = $pb.ProtobufEnum.initByValue(values);
static EncryptedContent_Media_Type? valueOf($core.int value) => _byValue[value];
const EncryptedContent_Media_Type._($core.int v, $core.String n) : super(v, n);
}
class EncryptedContent_MediaUpdate_Type extends $pb.ProtobufEnum {
static const EncryptedContent_MediaUpdate_Type REOPENED = EncryptedContent_MediaUpdate_Type._(0, _omitEnumNames ? '' : 'REOPENED');
static const EncryptedContent_MediaUpdate_Type STORED = EncryptedContent_MediaUpdate_Type._(1, _omitEnumNames ? '' : 'STORED');
static const EncryptedContent_MediaUpdate_Type DECRYPTION_ERROR = EncryptedContent_MediaUpdate_Type._(2, _omitEnumNames ? '' : 'DECRYPTION_ERROR');
static const $core.List<EncryptedContent_MediaUpdate_Type> values = <EncryptedContent_MediaUpdate_Type> [
REOPENED,
STORED,
DECRYPTION_ERROR,
];
static final $core.Map<$core.int, EncryptedContent_MediaUpdate_Type> _byValue = $pb.ProtobufEnum.initByValue(values);
static EncryptedContent_MediaUpdate_Type? valueOf($core.int value) => _byValue[value];
const EncryptedContent_MediaUpdate_Type._($core.int v, $core.String n) : super(v, n);
}
class EncryptedContent_ContactRequest_Type extends $pb.ProtobufEnum {
static const EncryptedContent_ContactRequest_Type REQUEST = EncryptedContent_ContactRequest_Type._(0, _omitEnumNames ? '' : 'REQUEST');
static const EncryptedContent_ContactRequest_Type REJECT = EncryptedContent_ContactRequest_Type._(1, _omitEnumNames ? '' : 'REJECT');
static const EncryptedContent_ContactRequest_Type ACCEPT = EncryptedContent_ContactRequest_Type._(2, _omitEnumNames ? '' : 'ACCEPT');
static const $core.List<EncryptedContent_ContactRequest_Type> values = <EncryptedContent_ContactRequest_Type> [
REQUEST,
REJECT,
ACCEPT,
];
static final $core.Map<$core.int, EncryptedContent_ContactRequest_Type> _byValue = $pb.ProtobufEnum.initByValue(values);
static EncryptedContent_ContactRequest_Type? valueOf($core.int value) => _byValue[value];
const EncryptedContent_ContactRequest_Type._($core.int v, $core.String n) : super(v, n);
}
class EncryptedContent_ContactUpdate_Type extends $pb.ProtobufEnum {
static const EncryptedContent_ContactUpdate_Type REQUEST = EncryptedContent_ContactUpdate_Type._(0, _omitEnumNames ? '' : 'REQUEST');
static const EncryptedContent_ContactUpdate_Type UPDATE = EncryptedContent_ContactUpdate_Type._(1, _omitEnumNames ? '' : 'UPDATE');
static const $core.List<EncryptedContent_ContactUpdate_Type> values = <EncryptedContent_ContactUpdate_Type> [
REQUEST,
UPDATE,
];
static final $core.Map<$core.int, EncryptedContent_ContactUpdate_Type> _byValue = $pb.ProtobufEnum.initByValue(values);
static EncryptedContent_ContactUpdate_Type? valueOf($core.int value) => _byValue[value];
const EncryptedContent_ContactUpdate_Type._($core.int v, $core.String n) : super(v, n);
}
class EncryptedContent_PushKeys_Type extends $pb.ProtobufEnum {
static const EncryptedContent_PushKeys_Type REQUEST = EncryptedContent_PushKeys_Type._(0, _omitEnumNames ? '' : 'REQUEST');
static const EncryptedContent_PushKeys_Type UPDATE = EncryptedContent_PushKeys_Type._(1, _omitEnumNames ? '' : 'UPDATE');
static const $core.List<EncryptedContent_PushKeys_Type> values = <EncryptedContent_PushKeys_Type> [
REQUEST,
UPDATE,
];
static final $core.Map<$core.int, EncryptedContent_PushKeys_Type> _byValue = $pb.ProtobufEnum.initByValue(values);
static EncryptedContent_PushKeys_Type? valueOf($core.int value) => _byValue[value];
const EncryptedContent_PushKeys_Type._($core.int v, $core.String n) : super(v, n);
}
const _omitEnumNames = $core.bool.fromEnvironment('protobuf.omit_enum_names');

View file

@ -0,0 +1,358 @@
//
// Generated code. Do not modify.
// source: messages.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 messageDescriptor instead')
const Message$json = {
'1': 'Message',
'2': [
{'1': 'type', '3': 1, '4': 1, '5': 14, '6': '.Message.Type', '10': 'type'},
{'1': 'receiptId', '3': 2, '4': 1, '5': 9, '10': 'receiptId'},
{'1': 'encryptedContent', '3': 3, '4': 1, '5': 12, '9': 0, '10': 'encryptedContent', '17': true},
{'1': 'plaintextContent', '3': 4, '4': 1, '5': 11, '6': '.PlaintextContent', '9': 1, '10': 'plaintextContent', '17': true},
],
'4': [Message_Type$json],
'8': [
{'1': '_encryptedContent'},
{'1': '_plaintextContent'},
],
};
@$core.Deprecated('Use messageDescriptor instead')
const Message_Type$json = {
'1': 'Type',
'2': [
{'1': 'SENDER_DELIVERY_RECEIPT', '2': 0},
{'1': 'PLAINTEXT_CONTENT', '2': 1},
{'1': 'CIPHERTEXT', '2': 2},
{'1': 'PREKEY_BUNDLE', '2': 3},
],
};
/// Descriptor for `Message`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List messageDescriptor = $convert.base64Decode(
'CgdNZXNzYWdlEiEKBHR5cGUYASABKA4yDS5NZXNzYWdlLlR5cGVSBHR5cGUSHAoJcmVjZWlwdE'
'lkGAIgASgJUglyZWNlaXB0SWQSLwoQZW5jcnlwdGVkQ29udGVudBgDIAEoDEgAUhBlbmNyeXB0'
'ZWRDb250ZW50iAEBEkIKEHBsYWludGV4dENvbnRlbnQYBCABKAsyES5QbGFpbnRleHRDb250ZW'
'50SAFSEHBsYWludGV4dENvbnRlbnSIAQEiXQoEVHlwZRIbChdTRU5ERVJfREVMSVZFUllfUkVD'
'RUlQVBAAEhUKEVBMQUlOVEVYVF9DT05URU5UEAESDgoKQ0lQSEVSVEVYVBACEhEKDVBSRUtFWV'
'9CVU5ETEUQA0ITChFfZW5jcnlwdGVkQ29udGVudEITChFfcGxhaW50ZXh0Q29udGVudA==');
@$core.Deprecated('Use plaintextContentDescriptor instead')
const PlaintextContent$json = {
'1': 'PlaintextContent',
'2': [
{'1': 'decryptionErrorMessage', '3': 1, '4': 1, '5': 11, '6': '.PlaintextContent.DecryptionErrorMessage', '9': 0, '10': 'decryptionErrorMessage', '17': true},
],
'3': [PlaintextContent_DecryptionErrorMessage$json],
'8': [
{'1': '_decryptionErrorMessage'},
],
};
@$core.Deprecated('Use plaintextContentDescriptor instead')
const PlaintextContent_DecryptionErrorMessage$json = {
'1': 'DecryptionErrorMessage',
'2': [
{'1': 'type', '3': 1, '4': 1, '5': 14, '6': '.PlaintextContent.DecryptionErrorMessage.Type', '10': 'type'},
],
'4': [PlaintextContent_DecryptionErrorMessage_Type$json],
};
@$core.Deprecated('Use plaintextContentDescriptor instead')
const PlaintextContent_DecryptionErrorMessage_Type$json = {
'1': 'Type',
'2': [
{'1': 'UNKNOWN', '2': 0},
{'1': 'PREKEY_UNKNOWN', '2': 1},
],
};
/// Descriptor for `PlaintextContent`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List plaintextContentDescriptor = $convert.base64Decode(
'ChBQbGFpbnRleHRDb250ZW50EmUKFmRlY3J5cHRpb25FcnJvck1lc3NhZ2UYASABKAsyKC5QbG'
'FpbnRleHRDb250ZW50LkRlY3J5cHRpb25FcnJvck1lc3NhZ2VIAFIWZGVjcnlwdGlvbkVycm9y'
'TWVzc2FnZYgBARqEAQoWRGVjcnlwdGlvbkVycm9yTWVzc2FnZRJBCgR0eXBlGAEgASgOMi0uUG'
'xhaW50ZXh0Q29udGVudC5EZWNyeXB0aW9uRXJyb3JNZXNzYWdlLlR5cGVSBHR5cGUiJwoEVHlw'
'ZRILCgdVTktOT1dOEAASEgoOUFJFS0VZX1VOS05PV04QAUIZChdfZGVjcnlwdGlvbkVycm9yTW'
'Vzc2FnZQ==');
@$core.Deprecated('Use encryptedContentDescriptor instead')
const EncryptedContent$json = {
'1': 'EncryptedContent',
'2': [
{'1': 'groupId', '3': 2, '4': 1, '5': 9, '9': 0, '10': 'groupId', '17': true},
{'1': 'senderProfileCounter', '3': 3, '4': 1, '5': 3, '9': 1, '10': 'senderProfileCounter', '17': true},
{'1': 'textMessage', '3': 4, '4': 1, '5': 11, '6': '.EncryptedContent.TextMessage', '9': 2, '10': 'textMessage', '17': true},
{'1': 'messageUpdate', '3': 5, '4': 1, '5': 11, '6': '.EncryptedContent.MessageUpdate', '9': 3, '10': 'messageUpdate', '17': true},
{'1': 'media', '3': 6, '4': 1, '5': 11, '6': '.EncryptedContent.Media', '9': 4, '10': 'media', '17': true},
{'1': 'mediaUpdate', '3': 7, '4': 1, '5': 11, '6': '.EncryptedContent.MediaUpdate', '9': 5, '10': 'mediaUpdate', '17': true},
{'1': 'contactUpdate', '3': 8, '4': 1, '5': 11, '6': '.EncryptedContent.ContactUpdate', '9': 6, '10': 'contactUpdate', '17': true},
{'1': 'contactRequest', '3': 9, '4': 1, '5': 11, '6': '.EncryptedContent.ContactRequest', '9': 7, '10': 'contactRequest', '17': true},
{'1': 'flameSync', '3': 10, '4': 1, '5': 11, '6': '.EncryptedContent.FlameSync', '9': 8, '10': 'flameSync', '17': true},
{'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},
],
'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],
'8': [
{'1': '_groupId'},
{'1': '_senderProfileCounter'},
{'1': '_textMessage'},
{'1': '_messageUpdate'},
{'1': '_media'},
{'1': '_mediaUpdate'},
{'1': '_contactUpdate'},
{'1': '_contactRequest'},
{'1': '_flameSync'},
{'1': '_pushKeys'},
{'1': '_reaction'},
],
};
@$core.Deprecated('Use encryptedContentDescriptor instead')
const EncryptedContent_TextMessage$json = {
'1': 'TextMessage',
'2': [
{'1': 'senderMessageId', '3': 1, '4': 1, '5': 9, '10': 'senderMessageId'},
{'1': 'text', '3': 2, '4': 1, '5': 9, '10': 'text'},
{'1': 'quoteMessageId', '3': 3, '4': 1, '5': 9, '9': 0, '10': 'quoteMessageId', '17': true},
],
'8': [
{'1': '_quoteMessageId'},
],
};
@$core.Deprecated('Use encryptedContentDescriptor instead')
const EncryptedContent_Reaction$json = {
'1': 'Reaction',
'2': [
{'1': 'targetMessageId', '3': 1, '4': 1, '5': 9, '10': 'targetMessageId'},
{'1': 'emoji', '3': 2, '4': 1, '5': 9, '9': 0, '10': 'emoji', '17': true},
{'1': 'remove', '3': 3, '4': 1, '5': 8, '9': 1, '10': 'remove', '17': true},
],
'8': [
{'1': '_emoji'},
{'1': '_remove'},
],
};
@$core.Deprecated('Use encryptedContentDescriptor instead')
const EncryptedContent_MessageUpdate$json = {
'1': 'MessageUpdate',
'2': [
{'1': 'type', '3': 1, '4': 1, '5': 14, '6': '.EncryptedContent.MessageUpdate.Type', '10': 'type'},
{'1': 'senderMessageId', '3': 2, '4': 1, '5': 9, '10': 'senderMessageId'},
{'1': 'text', '3': 3, '4': 1, '5': 9, '9': 0, '10': 'text', '17': true},
{'1': 'timestamp', '3': 4, '4': 1, '5': 3, '9': 1, '10': 'timestamp', '17': true},
],
'4': [EncryptedContent_MessageUpdate_Type$json],
'8': [
{'1': '_text'},
{'1': '_timestamp'},
],
};
@$core.Deprecated('Use encryptedContentDescriptor instead')
const EncryptedContent_MessageUpdate_Type$json = {
'1': 'Type',
'2': [
{'1': 'DELETE', '2': 0},
{'1': 'EDIT_TEXT', '2': 1},
{'1': 'OPENED', '2': 2},
],
};
@$core.Deprecated('Use encryptedContentDescriptor instead')
const EncryptedContent_Media$json = {
'1': 'Media',
'2': [
{'1': 'senderMessageId', '3': 1, '4': 1, '5': 9, '10': 'senderMessageId'},
{'1': 'type', '3': 2, '4': 1, '5': 14, '6': '.EncryptedContent.Media.Type', '10': 'type'},
{'1': 'displayLimitInMilliseconds', '3': 3, '4': 1, '5': 3, '9': 0, '10': 'displayLimitInMilliseconds', '17': true},
{'1': 'requiresAuthentication', '3': 4, '4': 1, '5': 8, '10': 'requiresAuthentication'},
{'1': 'downloadToken', '3': 5, '4': 1, '5': 12, '9': 1, '10': 'downloadToken', '17': true},
{'1': 'encryptionKey', '3': 6, '4': 1, '5': 12, '9': 2, '10': 'encryptionKey', '17': true},
{'1': 'encryptionMac', '3': 7, '4': 1, '5': 12, '9': 3, '10': 'encryptionMac', '17': true},
{'1': 'encryptionNonce', '3': 8, '4': 1, '5': 12, '9': 4, '10': 'encryptionNonce', '17': true},
],
'4': [EncryptedContent_Media_Type$json],
'8': [
{'1': '_displayLimitInMilliseconds'},
{'1': '_downloadToken'},
{'1': '_encryptionKey'},
{'1': '_encryptionMac'},
{'1': '_encryptionNonce'},
],
};
@$core.Deprecated('Use encryptedContentDescriptor instead')
const EncryptedContent_Media_Type$json = {
'1': 'Type',
'2': [
{'1': 'IMAGE', '2': 0},
{'1': 'VIDEO', '2': 1},
{'1': 'GIF', '2': 2},
],
};
@$core.Deprecated('Use encryptedContentDescriptor instead')
const EncryptedContent_MediaUpdate$json = {
'1': 'MediaUpdate',
'2': [
{'1': 'type', '3': 1, '4': 1, '5': 14, '6': '.EncryptedContent.MediaUpdate.Type', '10': 'type'},
{'1': 'targetMessageId', '3': 2, '4': 1, '5': 9, '10': 'targetMessageId'},
],
'4': [EncryptedContent_MediaUpdate_Type$json],
};
@$core.Deprecated('Use encryptedContentDescriptor instead')
const EncryptedContent_MediaUpdate_Type$json = {
'1': 'Type',
'2': [
{'1': 'REOPENED', '2': 0},
{'1': 'STORED', '2': 1},
{'1': 'DECRYPTION_ERROR', '2': 2},
],
};
@$core.Deprecated('Use encryptedContentDescriptor instead')
const EncryptedContent_ContactRequest$json = {
'1': 'ContactRequest',
'2': [
{'1': 'type', '3': 1, '4': 1, '5': 14, '6': '.EncryptedContent.ContactRequest.Type', '10': 'type'},
],
'4': [EncryptedContent_ContactRequest_Type$json],
};
@$core.Deprecated('Use encryptedContentDescriptor instead')
const EncryptedContent_ContactRequest_Type$json = {
'1': 'Type',
'2': [
{'1': 'REQUEST', '2': 0},
{'1': 'REJECT', '2': 1},
{'1': 'ACCEPT', '2': 2},
],
};
@$core.Deprecated('Use encryptedContentDescriptor instead')
const EncryptedContent_ContactUpdate$json = {
'1': 'ContactUpdate',
'2': [
{'1': 'type', '3': 1, '4': 1, '5': 14, '6': '.EncryptedContent.ContactUpdate.Type', '10': 'type'},
{'1': 'avatarSvg', '3': 2, '4': 1, '5': 9, '9': 0, '10': 'avatarSvg', '17': true},
{'1': 'displayName', '3': 3, '4': 1, '5': 9, '9': 1, '10': 'displayName', '17': true},
],
'4': [EncryptedContent_ContactUpdate_Type$json],
'8': [
{'1': '_avatarSvg'},
{'1': '_displayName'},
],
};
@$core.Deprecated('Use encryptedContentDescriptor instead')
const EncryptedContent_ContactUpdate_Type$json = {
'1': 'Type',
'2': [
{'1': 'REQUEST', '2': 0},
{'1': 'UPDATE', '2': 1},
],
};
@$core.Deprecated('Use encryptedContentDescriptor instead')
const EncryptedContent_PushKeys$json = {
'1': 'PushKeys',
'2': [
{'1': 'type', '3': 1, '4': 1, '5': 14, '6': '.EncryptedContent.PushKeys.Type', '10': 'type'},
{'1': 'keyId', '3': 2, '4': 1, '5': 3, '9': 0, '10': 'keyId', '17': true},
{'1': 'key', '3': 3, '4': 1, '5': 12, '9': 1, '10': 'key', '17': true},
],
'4': [EncryptedContent_PushKeys_Type$json],
'8': [
{'1': '_keyId'},
{'1': '_key'},
],
};
@$core.Deprecated('Use encryptedContentDescriptor instead')
const EncryptedContent_PushKeys_Type$json = {
'1': 'Type',
'2': [
{'1': 'REQUEST', '2': 0},
{'1': 'UPDATE', '2': 1},
],
};
@$core.Deprecated('Use encryptedContentDescriptor instead')
const EncryptedContent_FlameSync$json = {
'1': 'FlameSync',
'2': [
{'1': 'flameCounter', '3': 1, '4': 1, '5': 3, '10': 'flameCounter'},
{'1': 'lastFlameCounterChange', '3': 2, '4': 1, '5': 3, '10': 'lastFlameCounterChange'},
{'1': 'bestFriend', '3': 3, '4': 1, '5': 8, '10': 'bestFriend'},
],
};
/// Descriptor for `EncryptedContent`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List encryptedContentDescriptor = $convert.base64Decode(
'ChBFbmNyeXB0ZWRDb250ZW50Eh0KB2dyb3VwSWQYAiABKAlIAFIHZ3JvdXBJZIgBARI3ChRzZW'
'5kZXJQcm9maWxlQ291bnRlchgDIAEoA0gBUhRzZW5kZXJQcm9maWxlQ291bnRlcogBARJECgt0'
'ZXh0TWVzc2FnZRgEIAEoCzIdLkVuY3J5cHRlZENvbnRlbnQuVGV4dE1lc3NhZ2VIAlILdGV4dE'
'1lc3NhZ2WIAQESSgoNbWVzc2FnZVVwZGF0ZRgFIAEoCzIfLkVuY3J5cHRlZENvbnRlbnQuTWVz'
'c2FnZVVwZGF0ZUgDUg1tZXNzYWdlVXBkYXRliAEBEjIKBW1lZGlhGAYgASgLMhcuRW5jcnlwdG'
'VkQ29udGVudC5NZWRpYUgEUgVtZWRpYYgBARJECgttZWRpYVVwZGF0ZRgHIAEoCzIdLkVuY3J5'
'cHRlZENvbnRlbnQuTWVkaWFVcGRhdGVIBVILbWVkaWFVcGRhdGWIAQESSgoNY29udGFjdFVwZG'
'F0ZRgIIAEoCzIfLkVuY3J5cHRlZENvbnRlbnQuQ29udGFjdFVwZGF0ZUgGUg1jb250YWN0VXBk'
'YXRliAEBEk0KDmNvbnRhY3RSZXF1ZXN0GAkgASgLMiAuRW5jcnlwdGVkQ29udGVudC5Db250YW'
'N0UmVxdWVzdEgHUg5jb250YWN0UmVxdWVzdIgBARI+CglmbGFtZVN5bmMYCiABKAsyGy5FbmNy'
'eXB0ZWRDb250ZW50LkZsYW1lU3luY0gIUglmbGFtZVN5bmOIAQESOwoIcHVzaEtleXMYCyABKA'
'syGi5FbmNyeXB0ZWRDb250ZW50LlB1c2hLZXlzSAlSCHB1c2hLZXlziAEBEjsKCHJlYWN0aW9u'
'GAwgASgLMhouRW5jcnlwdGVkQ29udGVudC5SZWFjdGlvbkgKUghyZWFjdGlvbogBARqLAQoLVG'
'V4dE1lc3NhZ2USKAoPc2VuZGVyTWVzc2FnZUlkGAEgASgJUg9zZW5kZXJNZXNzYWdlSWQSEgoE'
'dGV4dBgCIAEoCVIEdGV4dBIrCg5xdW90ZU1lc3NhZ2VJZBgDIAEoCUgAUg5xdW90ZU1lc3NhZ2'
'VJZIgBAUIRCg9fcXVvdGVNZXNzYWdlSWQagQEKCFJlYWN0aW9uEigKD3RhcmdldE1lc3NhZ2VJ'
'ZBgBIAEoCVIPdGFyZ2V0TWVzc2FnZUlkEhkKBWVtb2ppGAIgASgJSABSBWVtb2ppiAEBEhsKBn'
'JlbW92ZRgDIAEoCEgBUgZyZW1vdmWIAQFCCAoGX2Vtb2ppQgkKB19yZW1vdmUa9QEKDU1lc3Nh'
'Z2VVcGRhdGUSOAoEdHlwZRgBIAEoDjIkLkVuY3J5cHRlZENvbnRlbnQuTWVzc2FnZVVwZGF0ZS'
'5UeXBlUgR0eXBlEigKD3NlbmRlck1lc3NhZ2VJZBgCIAEoCVIPc2VuZGVyTWVzc2FnZUlkEhcK'
'BHRleHQYAyABKAlIAFIEdGV4dIgBARIhCgl0aW1lc3RhbXAYBCABKANIAVIJdGltZXN0YW1wiA'
'EBIi0KBFR5cGUSCgoGREVMRVRFEAASDQoJRURJVF9URVhUEAESCgoGT1BFTkVEEAJCBwoFX3Rl'
'eHRCDAoKX3RpbWVzdGFtcBqgBAoFTWVkaWESKAoPc2VuZGVyTWVzc2FnZUlkGAEgASgJUg9zZW'
'5kZXJNZXNzYWdlSWQSMAoEdHlwZRgCIAEoDjIcLkVuY3J5cHRlZENvbnRlbnQuTWVkaWEuVHlw'
'ZVIEdHlwZRJDChpkaXNwbGF5TGltaXRJbk1pbGxpc2Vjb25kcxgDIAEoA0gAUhpkaXNwbGF5TG'
'ltaXRJbk1pbGxpc2Vjb25kc4gBARI2ChZyZXF1aXJlc0F1dGhlbnRpY2F0aW9uGAQgASgIUhZy'
'ZXF1aXJlc0F1dGhlbnRpY2F0aW9uEikKDWRvd25sb2FkVG9rZW4YBSABKAxIAVINZG93bmxvYW'
'RUb2tlbogBARIpCg1lbmNyeXB0aW9uS2V5GAYgASgMSAJSDWVuY3J5cHRpb25LZXmIAQESKQoN'
'ZW5jcnlwdGlvbk1hYxgHIAEoDEgDUg1lbmNyeXB0aW9uTWFjiAEBEi0KD2VuY3J5cHRpb25Ob2'
'5jZRgIIAEoDEgEUg9lbmNyeXB0aW9uTm9uY2WIAQEiJQoEVHlwZRIJCgVJTUFHRRAAEgkKBVZJ'
'REVPEAESBwoDR0lGEAJCHQobX2Rpc3BsYXlMaW1pdEluTWlsbGlzZWNvbmRzQhAKDl9kb3dubG'
'9hZFRva2VuQhAKDl9lbmNyeXB0aW9uS2V5QhAKDl9lbmNyeXB0aW9uTWFjQhIKEF9lbmNyeXB0'
'aW9uTm9uY2UapwEKC01lZGlhVXBkYXRlEjYKBHR5cGUYASABKA4yIi5FbmNyeXB0ZWRDb250ZW'
'50Lk1lZGlhVXBkYXRlLlR5cGVSBHR5cGUSKAoPdGFyZ2V0TWVzc2FnZUlkGAIgASgJUg90YXJn'
'ZXRNZXNzYWdlSWQiNgoEVHlwZRIMCghSRU9QRU5FRBAAEgoKBlNUT1JFRBABEhQKEERFQ1JZUF'
'RJT05fRVJST1IQAhp4Cg5Db250YWN0UmVxdWVzdBI5CgR0eXBlGAEgASgOMiUuRW5jcnlwdGVk'
'Q29udGVudC5Db250YWN0UmVxdWVzdC5UeXBlUgR0eXBlIisKBFR5cGUSCwoHUkVRVUVTVBAAEg'
'oKBlJFSkVDVBABEgoKBkFDQ0VQVBACGtIBCg1Db250YWN0VXBkYXRlEjgKBHR5cGUYASABKA4y'
'JC5FbmNyeXB0ZWRDb250ZW50LkNvbnRhY3RVcGRhdGUuVHlwZVIEdHlwZRIhCglhdmF0YXJTdm'
'cYAiABKAlIAFIJYXZhdGFyU3ZniAEBEiUKC2Rpc3BsYXlOYW1lGAMgASgJSAFSC2Rpc3BsYXlO'
'YW1liAEBIh8KBFR5cGUSCwoHUkVRVUVTVBAAEgoKBlVQREFURRABQgwKCl9hdmF0YXJTdmdCDg'
'oMX2Rpc3BsYXlOYW1lGqQBCghQdXNoS2V5cxIzCgR0eXBlGAEgASgOMh8uRW5jcnlwdGVkQ29u'
'dGVudC5QdXNoS2V5cy5UeXBlUgR0eXBlEhkKBWtleUlkGAIgASgDSABSBWtleUlkiAEBEhUKA2'
'tleRgDIAEoDEgBUgNrZXmIAQEiHwoEVHlwZRILCgdSRVFVRVNUEAASCgoGVVBEQVRFEAFCCAoG'
'X2tleUlkQgYKBF9rZXkahwEKCUZsYW1lU3luYxIiCgxmbGFtZUNvdW50ZXIYASABKANSDGZsYW'
'1lQ291bnRlchI2ChZsYXN0RmxhbWVDb3VudGVyQ2hhbmdlGAIgASgDUhZsYXN0RmxhbWVDb3Vu'
'dGVyQ2hhbmdlEh4KCmJlc3RGcmllbmQYAyABKAhSCmJlc3RGcmllbmRCCgoIX2dyb3VwSWRCFw'
'oVX3NlbmRlclByb2ZpbGVDb3VudGVyQg4KDF90ZXh0TWVzc2FnZUIQCg5fbWVzc2FnZVVwZGF0'
'ZUIICgZfbWVkaWFCDgoMX21lZGlhVXBkYXRlQhAKDl9jb250YWN0VXBkYXRlQhEKD19jb250YW'
'N0UmVxdWVzdEIMCgpfZmxhbWVTeW5jQgsKCV9wdXNoS2V5c0ILCglfcmVhY3Rpb24=');

View file

@ -0,0 +1,14 @@
//
// Generated code. Do not modify.
// source: messages.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 'messages.pb.dart';

View file

@ -0,0 +1,135 @@
syntax = "proto3";
message Message {
enum Type {
SENDER_DELIVERY_RECEIPT = 0;
PLAINTEXT_CONTENT = 1;
CIPHERTEXT = 2;
PREKEY_BUNDLE = 3;
}
Type type = 1;
string receiptId = 2;
optional bytes encryptedContent = 3;
optional PlaintextContent plaintextContent = 4;
}
message PlaintextContent {
optional DecryptionErrorMessage decryptionErrorMessage = 1;
message DecryptionErrorMessage {
enum Type {
UNKNOWN = 0;
PREKEY_UNKNOWN = 1;
}
Type type = 1;
}
}
message EncryptedContent {
optional string groupId = 2;
/// This can be added, so the receiver can check weather he is up to date with the current profile
optional int64 senderProfileCounter = 3;
optional TextMessage textMessage = 4;
optional MessageUpdate messageUpdate = 5;
optional Media media = 6;
optional MediaUpdate mediaUpdate = 7;
optional ContactUpdate contactUpdate = 8;
optional ContactRequest contactRequest = 9;
optional FlameSync flameSync = 10;
optional PushKeys pushKeys = 11;
optional Reaction reaction = 12;
message TextMessage {
string senderMessageId = 1;
string text = 2;
optional string quoteMessageId = 3;
}
message Reaction {
string targetMessageId = 1;
optional string emoji = 2;
optional bool remove = 3;
}
message MessageUpdate {
enum Type {
DELETE = 0;
EDIT_TEXT = 1;
OPENED = 2;
}
Type type = 1;
string senderMessageId = 2;
optional string text = 3;
optional int64 timestamp = 4;
}
message Media {
enum Type {
IMAGE = 0;
VIDEO = 1;
GIF = 2;
}
string senderMessageId = 1;
Type type = 2;
optional int64 displayLimitInMilliseconds = 3;
bool requiresAuthentication = 4;
optional bytes downloadToken = 5;
optional bytes encryptionKey = 6;
optional bytes encryptionMac = 7;
optional bytes encryptionNonce = 8;
}
message MediaUpdate {
enum Type {
REOPENED = 0;
STORED = 1;
DECRYPTION_ERROR = 2;
}
Type type = 1;
string targetMessageId = 2;
}
message ContactRequest {
enum Type {
REQUEST = 0;
REJECT = 1;
ACCEPT = 2;
}
Type type = 1;
}
message ContactUpdate {
enum Type {
REQUEST = 0;
UPDATE = 1;
}
Type type = 1;
optional string avatarSvg = 2;
optional string displayName = 3;
}
message PushKeys {
enum Type {
REQUEST = 0;
UPDATE = 1;
}
Type type = 1;
optional int64 keyId = 2;
optional bytes key = 3;
}
message FlameSync {
int64 flameCounter = 1;
int64 lastFlameCounterChange = 2;
bool bestFriend = 3;
}
}

View file

@ -17,7 +17,7 @@ import 'package:mutex/mutex.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/constants/secure_storage_keys.dart';
import 'package:twonly/src/database/twonly_database.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/model/protobuf/api/websocket/client_to_server.pbserver.dart';
import 'package:twonly/src/model/protobuf/api/websocket/error.pb.dart';
import 'package:twonly/src/model/protobuf/api/websocket/server_to_client.pb.dart'

View file

@ -13,8 +13,8 @@ import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/database/tables/messages_table.dart';
import 'package:twonly/src/database/twonly_database.dart';
import 'package:twonly/src/model/json/message.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/model/json/message_old.dart';
import 'package:twonly/src/services/api/media_upload.dart';
import 'package:twonly/src/services/api/utils.dart';
import 'package:twonly/src/utils/log.dart';

View file

@ -19,8 +19,8 @@ import 'package:twonly/globals.dart';
import 'package:twonly/src/constants/secure_storage_keys.dart';
import 'package:twonly/src/database/tables/media_uploads_table.dart';
import 'package:twonly/src/database/tables/messages_table.dart';
import 'package:twonly/src/database/twonly_database.dart';
import 'package:twonly/src/model/json/message.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/model/json/message_old.dart';
import 'package:twonly/src/model/protobuf/api/http/http_requests.pb.dart';
import 'package:twonly/src/model/protobuf/api/websocket/error.pb.dart';
import 'package:twonly/src/model/protobuf/push_notification/push_notification.pbserver.dart';

View file

@ -8,9 +8,11 @@ import 'package:fixnum/fixnum.dart';
import 'package:mutex/mutex.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/database/tables/messages_table.dart';
import 'package:twonly/src/database/twonly_database.dart';
import 'package:twonly/src/model/json/message.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/model/json/message_old.dart';
import 'package:twonly/src/model/protobuf/api/websocket/error.pb.dart';
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart'
as pb;
import 'package:twonly/src/model/protobuf/push_notification/push_notification.pb.dart';
import 'package:twonly/src/services/api/server_messages.dart'
show messageGetsAck;
@ -36,12 +38,15 @@ Future<void> tryTransmitMessages() async {
});
}
Future<void> sendRetransmitMessage(int retransId) async {
Future<void> tryToSendCompleteMessage(String receiptId) async {
try {
final retrans = await twonlyDB.messageRetransmissionDao
.getRetransmissionById(retransId)
.getSingleOrNull();
/// SET THE Message().receiptID !!!!!!!
/// ALSO THE encryptedContent is NOT YET ENCRYPTED!
if (retrans == null) {
Log.error('$retransId not found in database');
return;
@ -156,97 +161,74 @@ Future<void> sendRetransmitMessage(int retransId) async {
}
}
// encrypts and stores the message and then sends it in the background
Future<void> encryptAndSendMessageAsync(
int? messageId,
int userId,
MessageJson msg, {
PushNotification? pushNotification,
}) async {
Uint8List? pushData;
if (pushNotification != null) {
pushData = await getPushData(userId, pushNotification);
}
final retransId =
await twonlyDB.messageRetransmissionDao.insertRetransmission(
MessageRetransmissionsCompanion(
contactId: Value(userId),
messageId: Value(messageId),
plaintextContent: Value(Uint8List(0)),
pushData: Value(pushData),
),
);
if (retransId == null) {
Log.error('Could not insert the message into the retransmission database');
return;
}
msg.retransId = retransId;
final plaintextContent =
Uint8List.fromList(gzip.encode(utf8.encode(jsonEncode(msg.toJson()))));
await twonlyDB.messageRetransmissionDao.updateRetransmission(
retransId,
MessageRetransmissionsCompanion(
plaintextContent: Value(plaintextContent),
),
);
// this can now be done in the background...
unawaited(sendRetransmitMessage(retransId));
}
Future<void> sendTextMessage(
int target,
TextMessageContent content,
PushNotification? pushNotification,
Future<void> sendCipherText(
int contactId,
pb.EncryptedContent encryptedContent,
) async {
final messageSendAt = DateTime.now();
DateTime? openedAt;
final response = pb.Message()
..type = pb.Message_Type.CIPHERTEXT
..encryptedContent = encryptedContent.writeToBuffer();
if (pushNotification != null && pushNotification.hasReactionContent()) {
openedAt = DateTime.now();
}
final messageId = await twonlyDB.messagesDao.insertMessage(
MessagesCompanion(
contactId: Value(target),
kind: const Value(MessageKind.textMessage),
sendAt: Value(messageSendAt),
responseToOtherMessageId: Value(content.responseToMessageId),
responseToMessageId: Value(content.responseToOtherMessageId),
downloadState: const Value(DownloadState.downloaded),
openedAt: Value(openedAt),
contentJson: Value(
jsonEncode(content.toJson()),
),
final receipt = await twonlyDB.receiptsDao.insertReceipt(
ReceiptsCompanion(
contactId: Value(contactId),
message: Value(response.writeToBuffer()),
),
);
if (messageId == null) return;
if (pushNotification != null && !pushNotification.hasReactionContent()) {
pushNotification.messageId = Int64(messageId);
if (receipt != null) {
await tryToSendCompleteMessage(receipt.receiptId);
}
final msg = MessageJson(
kind: MessageKind.textMessage,
messageSenderId: messageId,
content: content,
timestamp: messageSendAt,
);
await encryptAndSendMessageAsync(
messageId,
target,
msg,
pushNotification: pushNotification,
);
}
// Future<void> sendTextMessage(
// int target,
// TextMessageContent content,
// PushNotification? pushNotification,
// ) async {
// final messageSendAt = DateTime.now();
// DateTime? openedAt;
// if (pushNotification != null && pushNotification.hasReactionContent()) {
// openedAt = DateTime.now();
// }
// final messageId = await twonlyDB.messagesDao.insertMessage(
// MessagesCompanion(
// contactId: Value(target),
// kind: const Value(MessageKind.textMessage),
// sendAt: Value(messageSendAt),
// responseToOtherMessageId: Value(content.responseToMessageId),
// responseToMessageId: Value(content.responseToOtherMessageId),
// downloadState: const Value(DownloadState.downloaded),
// openedAt: Value(openedAt),
// contentJson: Value(
// jsonEncode(content.toJson()),
// ),
// ),
// );
// if (messageId == null) return;
// if (pushNotification != null && !pushNotification.hasReactionContent()) {
// pushNotification.messageId = Int64(messageId);
// }
// final msg = MessageJson(
// kind: MessageKind.textMessage,
// messageSenderId: messageId,
// content: content,
// timestamp: messageSendAt,
// );
// await encryptAndSendMessageAsync(
// messageId,
// target,
// msg,
// pushNotification: pushNotification,
// );
// }
Future<void> notifyContactAboutOpeningMessage(
int fromUserId,
List<int> messageOtherIds,
@ -269,33 +251,25 @@ Future<void> notifyContactAboutOpeningMessage(
await updateLastMessageId(fromUserId, biggestMessageId);
}
Future<void> notifyContactsAboutProfileChange() async {
final contacts = await twonlyDB.contactsDao.getAllNotBlockedContacts();
Future<void> notifyContactsAboutProfileChange({int? onlyToContact}) async {
final user = await getUser();
if (user == null) return;
if (user.avatarSvg == null) return;
for (final contact in contacts) {
if (contact.myAvatarCounter < user.avatarCounter) {
await twonlyDB.contactsDao.updateContact(
contact.userId,
ContactsCompanion(
myAvatarCounter: Value(user.avatarCounter),
),
);
await encryptAndSendMessageAsync(
null,
contact.userId,
MessageJson(
kind: MessageKind.profileChange,
content: ProfileContent(
avatarSvg: user.avatarSvg!,
displayName: user.displayName,
),
timestamp: DateTime.now(),
),
);
final encryptedContent = pb.EncryptedContent()
..contactUpdate = (pb.EncryptedContent_ContactUpdate()
..type = pb.EncryptedContent_ContactUpdate_Type.UPDATE
..avatarSvg = user.avatarSvg!
..displayName = user.displayName);
if (onlyToContact != null) {
await sendCipherText(onlyToContact, encryptedContent);
return;
}
final contacts = await twonlyDB.contactsDao.getAllNotBlockedContacts();
for (final contact in contacts) {
await sendCipherText(contact.userId, encryptedContent);
}
}

View file

@ -1,42 +1,31 @@
// ignore_for_file: avoid_dynamic_calls
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:cryptography_plus/cryptography_plus.dart';
import 'package:drift/drift.dart';
import 'package:fixnum/fixnum.dart';
import 'package:mutex/mutex.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/database/tables/media_uploads_table.dart';
import 'package:twonly/src/database/tables/messages_table.dart';
import 'package:twonly/src/database/twonly_database.dart';
import 'package:twonly/src/model/json/message.dart';
import 'package:twonly/src/database/twonly.db.dart' hide Message;
import 'package:twonly/src/model/protobuf/api/websocket/client_to_server.pb.dart'
as client;
import 'package:twonly/src/model/protobuf/api/websocket/client_to_server.pb.dart';
import 'package:twonly/src/model/protobuf/api/websocket/error.pb.dart';
import 'package:twonly/src/model/protobuf/api/websocket/server_to_client.pb.dart'
as server;
import 'package:twonly/src/services/api/media_download.dart';
import 'package:twonly/src/services/api/media_upload.dart';
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
import 'package:twonly/src/services/api/messages.dart';
import 'package:twonly/src/services/api/utils.dart';
import 'package:twonly/src/services/notifications/pushkeys.notifications.dart';
import 'package:twonly/src/services/notifications/setup.notifications.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/signal/encryption.signal.dart';
import 'package:twonly/src/services/signal/identity.signal.dart';
import 'package:twonly/src/services/thumbnail.service.dart';
import 'package:twonly/src/utils/log.dart';
import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/views/components/animate_icon.dart';
final lockHandleServerMessage = Mutex();
Future<void> handleServerMessage(server.ServerToClient msg) async {
// return lockHandleServerMessage.protect(() async {
client.Response? response;
/// Returns means, that the server can delete the message from the server.
final ok = client.Response_Ok()..none = true;
var response = client.Response()..ok = ok;
try {
if (msg.v0.hasRequestNewPreKeys()) {
@ -44,13 +33,12 @@ Future<void> 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();
response = await handleNewMessage(fromUserId, body);
await handleNewMessage(fromUserId, body);
} else {
Log.error('Got a unknown message from the server: $msg');
response = client.Response()..error = ErrorCode.InternalError;
Log.error('Unknown server message: $msg');
}
} catch (e) {
response = client.Response()..error = ErrorCode.InternalError;
Log.error(e);
}
final v0 = client.V0()
@ -58,435 +46,159 @@ Future<void> handleServerMessage(server.ServerToClient msg) async {
..response = response;
await apiService.sendResponse(ClientToServer()..v0 = v0);
// });
}
DateTime lastSignalDecryptMessage =
DateTime.now().subtract(const Duration(hours: 1));
DateTime lastPushKeyRequest = DateTime.now().subtract(const Duration(hours: 1));
bool messageGetsAck(MessageKind kind) {
return kind != MessageKind.pushKey && kind != MessageKind.ack;
}
Future<void> handleNewMessage(int fromUserId, Uint8List body) async {
final message = Message.fromBuffer(body);
final receiptId = message.receiptId;
Future<client.Response> handleNewMessage(int fromUserId, Uint8List body) async {
final message = await signalDecryptMessage(fromUserId, body);
if (message == null) {
final encryptedHash = (await Sha256().hash(body)).bytes;
await encryptAndSendMessageAsync(
null,
fromUserId,
MessageJson(
kind: MessageKind.signalDecryptError,
content: SignalDecryptErrorContent(encryptedHash: encryptedHash),
timestamp: DateTime.now(),
),
switch (message.type) {
case Message_Type.SENDER_DELIVERY_RECEIPT:
Log.info('Got delivery receipt for $receiptId!');
await twonlyDB.receiptsDao.confirmReceipt(receiptId, fromUserId);
case Message_Type.PLAINTEXT_CONTENT:
if (message.hasPlaintextContent() &&
message.plaintextContent.hasDecryptionErrorMessage()) {
Log.info(
'Got decryption error: ${message.plaintextContent.decryptionErrorMessage.type} for $receiptId',
);
Log.error('Could not decrypt others message!');
// Message is not valid, so server can delete it
final ok = client.Response_Ok()..none = true;
return client.Response()..ok = ok;
await tryToSendCompleteMessage(receiptId);
}
client.Response? result;
case Message_Type.CIPHERTEXT:
case Message_Type.PREKEY_BUNDLE:
if (message.hasEncryptedContent()) {
final encryptedContentRaw =
Uint8List.fromList(message.encryptedContent);
Log.info('Got: ${message.kind} from $fromUserId');
switch (message.kind) {
case MessageKind.ack:
final content = message.content;
if (content is AckContent) {
if (content.messageIdToAck != null) {
const update = MessagesCompanion(
acknowledgeByUser: Value(true),
errorWhileSending: Value(false),
);
await twonlyDB.messagesDao.updateMessageByOtherUser(
final responsePlaintextContent = await handleEncryptedMessage(
fromUserId,
content.messageIdToAck!,
update,
encryptedContentRaw,
message.type,
);
}
await twonlyDB.messageRetransmissionDao
.deleteRetransmissionById(content.retransIdToAck);
}
case MessageKind.signalDecryptError:
Log.error(
'Got signal decrypt error from other user! Sending it again.',
);
final content = message.content;
if (content is SignalDecryptErrorContent) {
final hash = Uint8List.fromList(content.encryptedHash);
await twonlyDB.messageRetransmissionDao.resetAckStatusFor(
fromUserId,
hash,
);
final message = await twonlyDB.messageRetransmissionDao
.getRetransmissionFromHash(fromUserId, hash);
if (message != null) {
unawaited(sendRetransmitMessage(message.retransmissionId));
Message response;
if (responsePlaintextContent != null) {
response = Message()
..receiptId = receiptId
..type = Message_Type.PLAINTEXT_CONTENT
..plaintextContent = responsePlaintextContent;
Log.error('Sending decryption error ($receiptId)');
} else {
Log.error('Could not find message to retransmit!');
response = Message()..type = Message_Type.SENDER_DELIVERY_RECEIPT;
}
}
case MessageKind.contactRequest:
await handleContactRequest(fromUserId, message);
case MessageKind.flameSync:
final contact = await twonlyDB.contactsDao
.getContactByUserId(fromUserId)
.getSingleOrNull();
if (contact != null && contact.lastFlameCounterChange != null) {
final content = message.content;
if (content is FlameSyncContent) {
var updates = ContactsCompanion(
alsoBestFriend: Value(content.bestFriend),
);
if (isToday(contact.lastFlameCounterChange!) &&
isToday(content.lastFlameCounterChange)) {
if (content.flameCounter > contact.flameCounter) {
updates = ContactsCompanion(
flameCounter: Value(content.flameCounter),
);
}
}
await twonlyDB.contactsDao.updateContact(fromUserId, updates);
}
}
case MessageKind.receiveMediaError:
if (message.messageReceiverId != null) {
final openedMessage = await twonlyDB.messagesDao
.getMessageByIdAndContactId(fromUserId, message.messageReceiverId!)
.getSingleOrNull();
if (openedMessage != null) {
/// message found
/// checks if
/// 1. this was a media upload
/// 2. the media was not already retransmitted
/// 3. the media was send in the last two days
if (openedMessage.mediaUploadId != null &&
openedMessage.mediaRetransmissionState ==
MediaRetransmitting.none &&
openedMessage.sendAt
.isAfter(DateTime.now().subtract(const Duration(days: 2)))) {
// reset the media upload state to pending,
// this will cause the media to be re-encrypted again
await twonlyDB.mediaUploadsDao.updateMediaUpload(
openedMessage.mediaUploadId!,
const MediaUploadsCompanion(
state: Value(
UploadState.pending,
),
),
);
// reset the message upload so the upload will be done again
await twonlyDB.messagesDao.updateMessageByOtherUser(
fromUserId,
message.messageReceiverId!,
const MessagesCompanion(
downloadState: Value(DownloadState.pending),
mediaRetransmissionState:
Value(MediaRetransmitting.retransmitted),
),
);
unawaited(retryMediaUpload(false));
} else {
await twonlyDB.messagesDao.updateMessageByOtherUser(
fromUserId,
message.messageReceiverId!,
const MessagesCompanion(
errorWhileSending: Value(true),
),
);
}
}
}
case MessageKind.opened:
if (message.messageReceiverId != null) {
final update = MessagesCompanion(
openedAt: Value(message.timestamp),
errorWhileSending: const Value(false),
);
await twonlyDB.messagesDao.updateMessageByOtherUser(
fromUserId,
message.messageReceiverId!,
update,
);
final openedMessage = await twonlyDB.messagesDao
.getMessageByMessageId(message.messageReceiverId!)
.getSingleOrNull();
if (openedMessage != null &&
openedMessage.kind == MessageKind.textMessage) {
await twonlyDB.messagesDao.openedAllNonMediaMessagesFromOtherUser(
fromUserId,
);
}
}
case MessageKind.rejectRequest:
await deleteContact(fromUserId);
case MessageKind.acceptRequest:
const update = ContactsCompanion(accepted: Value(true));
await twonlyDB.contactsDao.updateContact(fromUserId, update);
unawaited(notifyContactsAboutProfileChange());
case MessageKind.profileChange:
final content = message.content;
if (content is ProfileContent) {
final update = ContactsCompanion(
avatarSvg: Value(content.avatarSvg),
displayName: Value(content.displayName),
);
await twonlyDB.contactsDao.updateContact(fromUserId, update);
}
unawaited(createPushAvatars());
case MessageKind.requestPushKey:
if (lastPushKeyRequest
.isBefore(DateTime.now().subtract(const Duration(seconds: 60)))) {
lastPushKeyRequest = DateTime.now();
unawaited(setupNotificationWithUsers(forceContact: fromUserId));
}
case MessageKind.pushKey:
if (message.content != null) {
final pushKey = message.content!;
if (pushKey is PushKeyContent) {
await handleNewPushKey(fromUserId, pushKey);
}
}
// ignore: no_default_cases
default:
if (message.messageSenderId == null) {
Log.error('Messageid not defined $message');
} else if ([
MessageKind.textMessage,
MessageKind.media,
MessageKind.storedMediaFile,
MessageKind.reopenedMedia,
].contains(message.kind)) {
result = await handleMediaOrTextMessage(fromUserId, message);
} else {
Log.error('Got unknown MessageKind $message');
}
}
if (messageGetsAck(message.kind) && message.retransId != null) {
Log.info('Sending ACK for ${message.kind}');
/// ACK every message
await encryptAndSendMessageAsync(
null,
fromUserId,
MessageJson(
kind: MessageKind.ack,
content: AckContent(
messageIdToAck: message.messageSenderId,
retransIdToAck: message.retransId!,
),
timestamp: DateTime.now(),
),
);
}
if (result != null) {
return result;
}
final ok = client.Response_Ok()..none = true;
return client.Response()..ok = ok;
}
Future<client.Response?> handleMediaOrTextMessage(
int fromUserId,
MessageJson message,
) async {
if (message.kind == MessageKind.storedMediaFile) {
if (message.messageReceiverId != null) {
/// stored media file just updates the message
await twonlyDB.messagesDao.updateMessageByOtherUser(
fromUserId,
message.messageReceiverId!,
const MessagesCompanion(
mediaStored: Value(true),
errorWhileSending: Value(false),
),
);
final msg = await twonlyDB.messagesDao
.getMessageByIdAndContactId(
fromUserId,
message.messageReceiverId!,
)
.getSingleOrNull();
if (msg != null && msg.mediaUploadId != null) {
final filePath = await getMediaFilePath(msg.mediaUploadId, 'send');
if (filePath.contains('mp4')) {
unawaited(createThumbnailsForVideo(File(filePath)));
} else {
unawaited(createThumbnailsForImage(File(filePath)));
}
}
}
} else if (message.content != null) {
final content = message.content!;
// when a message is received doubled ignore it...
final openedMessage = await twonlyDB.messagesDao
.getMessageByOtherMessageId(fromUserId, message.messageSenderId!)
.getSingleOrNull();
if (openedMessage != null) {
if (openedMessage.errorWhileSending) {
await twonlyDB.messagesDao
.deleteMessagesByMessageId(openedMessage.messageId);
} else {
Log.error(
'Got a duplicated message from other user: ${message.messageSenderId!}',
);
final ok = client.Response_Ok()..none = true;
return client.Response()..ok = ok;
}
}
int? responseToMessageId;
int? responseToOtherMessageId;
int? messageId;
var acknowledgeByUser = false;
DateTime? openedAt;
if (message.kind == MessageKind.reopenedMedia) {
acknowledgeByUser = true;
openedAt = DateTime.now();
}
if (content is TextMessageContent) {
responseToMessageId = content.responseToMessageId;
responseToOtherMessageId = content.responseToOtherMessageId;
if (responseToMessageId != null || responseToOtherMessageId != null) {
// reactions are shown in the notification directly...
if (isEmoji(content.text)) {
openedAt = DateTime.now();
}
}
}
if (content is ReopenedMediaFileContent) {
responseToMessageId = content.messageId;
}
if (responseToMessageId != null) {
await twonlyDB.messagesDao.updateMessageByOtherUser(
fromUserId,
responseToMessageId,
MessagesCompanion(
errorWhileSending: const Value(false),
openedAt: Value(
DateTime.now(),
), // when a user reacted to the media file, it should be marked as opened
),
);
}
final contentJson = jsonEncode(content.toJson());
final update = MessagesCompanion(
await twonlyDB.receiptsDao.insertReceipt(
ReceiptsCompanion(
receiptId: Value(receiptId),
contactId: Value(fromUserId),
kind: Value(message.kind),
messageOtherId: Value(message.messageSenderId),
contentJson: Value(contentJson),
acknowledgeByServer: const Value(true),
acknowledgeByUser: Value(acknowledgeByUser),
responseToMessageId: Value(responseToMessageId),
responseToOtherMessageId: Value(responseToOtherMessageId),
openedAt: Value(openedAt),
downloadState: Value(
message.kind == MessageKind.media
? DownloadState.pending
: DownloadState.downloaded,
message: Value(response.writeToBuffer()),
contactWillSendsReceipt: const Value(false),
),
sendAt: Value(message.timestamp),
);
messageId = await twonlyDB.messagesDao.insertMessage(
update,
);
if (messageId == null) {
Log.error('could not insert message into db');
return client.Response()..error = ErrorCode.InternalError;
await tryToSendCompleteMessage(receiptId);
}
}
}
Log.info('Inserted a new message with id: $messageId');
if (message.kind == MessageKind.media) {
await twonlyDB.contactsDao.incFlameCounter(
Future<PlaintextContent?> handleEncryptedMessage(
int fromUserId,
Uint8List encryptedContentRaw,
Message_Type messageType,
) async {
final (content, decryptionErrorType) = await signalDecryptMessage(
fromUserId,
true,
message.timestamp,
encryptedContentRaw,
messageType as int,
);
final msg = await twonlyDB.messagesDao
.getMessageByMessageId(messageId)
.getSingleOrNull();
if (msg != null) {
unawaited(startDownloadMedia(msg, false));
}
}
} else {
Log.error('Content is not defined $message');
if (content == null) {
return PlaintextContent()
..decryptionErrorMessage = (PlaintextContent_DecryptionErrorMessage()
..type = decryptionErrorType!);
}
// unarchive contact when receiving a new message
await twonlyDB.contactsDao.updateContact(
final senderProfileCounter = await checkForProfileUpdate(fromUserId, content);
if (content.hasContactRequest()) {
await handleContactRequest(fromUserId, content.contactRequest);
return null;
}
if (content.hasContactUpdate()) {
await handleContactUpdate(
fromUserId,
const ContactsCompanion(
archived: Value(false),
),
content.contactUpdate,
senderProfileCounter,
);
return null;
}
Future<client.Response> handleRequestNewPreKey() async {
final localPreKeys = await signalGetPreKeys();
final prekeysList = <client.Response_PreKey>[];
for (var i = 0; i < localPreKeys.length; i++) {
prekeysList.add(
client.Response_PreKey()
..id = Int64(localPreKeys[i].id)
..prekey = localPreKeys[i].getKeyPair().publicKey.serialize(),
);
}
final prekeys = client.Response_Prekeys(prekeys: prekeysList);
final ok = client.Response_Ok()..prekeys = prekeys;
return client.Response()..ok = ok;
}
Future<void> handleContactRequest(
int fromUserId,
MessageJson message,
) async {
// request the username by the server so an attacker can not
// forge the displayed username in the contact request
final username = await apiService.getUsername(fromUserId);
if (username.isSuccess) {
final name = username.value.userdata.username as Uint8List;
await twonlyDB.contactsDao.insertContact(
ContactsCompanion(
username: Value(utf8.decode(name)),
userId: Value(fromUserId),
requested: const Value(true),
),
);
if (content.hasFlameSync()) {
await handleFlameSync(fromUserId, content.flameSync);
return null;
}
await setupNotificationWithUsers();
if (content.hasPushKeys()) {
await handlePushKey(fromUserId, content.pushKeys);
return null;
}
if (!content.hasGroupId()) {
return null;
}
/// Verify that the user is (still) in that group...
if (!await twonlyDB.groupsDao.isContactInGroup(fromUserId, content.groupId)) {
Log.error('User $fromUserId tried to access group ${content.groupId}.');
return null;
}
if (content.hasMessageUpdate()) {
await handleMessageUpdate(
fromUserId,
content.groupId,
content.messageUpdate,
);
return null;
}
if (content.hasTextMessage()) {
await handleTextMessage(
fromUserId,
content.groupId,
content.textMessage,
);
return null;
}
if (content.hasReaction()) {
await handleReaction(
fromUserId,
content.groupId,
content.reaction,
);
return null;
}
if (content.hasMedia()) {
await handleMedia(
fromUserId,
content.groupId,
content.media,
);
return null;
}
if (content.hasMediaUpdate()) {
await handleMediaUpdate(
fromUserId,
content.groupId,
content.mediaUpdate,
);
return null;
}
return null;
}

View file

@ -0,0 +1,121 @@
import 'dart:async';
import 'dart:convert';
import 'package:drift/drift.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/database/twonly.db.dart' hide Message;
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
import 'package:twonly/src/services/api/messages.dart';
import 'package:twonly/src/services/api/utils.dart';
import 'package:twonly/src/services/notifications/pushkeys.notifications.dart';
import 'package:twonly/src/services/notifications/setup.notifications.dart';
import 'package:twonly/src/utils/misc.dart';
Future<void> handleContactRequest(
int fromUserId,
EncryptedContent_ContactRequest contactRequest,
) async {
switch (contactRequest.type) {
case EncryptedContent_ContactRequest_Type.REQUEST:
// Request the username by the server so an attacker can not
// forge the displayed username in the contact request
final username = await apiService.getUsername(fromUserId);
if (username.isSuccess) {
// ignore: avoid_dynamic_calls
final name = username.value.userdata.username as Uint8List;
await twonlyDB.contactsDao.insertContact(
ContactsCompanion(
username: Value(utf8.decode(name)),
userId: Value(fromUserId),
requested: const Value(true),
),
);
}
await setupNotificationWithUsers();
case EncryptedContent_ContactRequest_Type.ACCEPT:
await twonlyDB.contactsDao.updateContact(
fromUserId,
const ContactsCompanion(
requested: Value(false),
accepted: Value(true),
),
);
case EncryptedContent_ContactRequest_Type.REJECT:
await twonlyDB.contactsDao.deleteContactByUserId(fromUserId);
}
}
Future<void> handleContactUpdate(
int fromUserId,
EncryptedContent_ContactUpdate contactUpdate,
int? senderProfileCounter) async {
switch (contactUpdate.type) {
case EncryptedContent_ContactUpdate_Type.REQUEST:
await notifyContactsAboutProfileChange(onlyToContact: fromUserId);
case EncryptedContent_ContactUpdate_Type.UPDATE:
if (contactUpdate.hasAvatarSvg() &&
contactUpdate.hasDisplayName() &&
senderProfileCounter != null) {
await twonlyDB.contactsDao.updateContact(
fromUserId,
ContactsCompanion(
avatarSvg: Value(contactUpdate.avatarSvg),
displayName: Value(contactUpdate.displayName),
senderProfileCounter: Value(senderProfileCounter),
),
);
unawaited(createPushAvatars());
}
}
}
Future<void> handleFlameSync(
int contactId,
EncryptedContent_FlameSync flameSync,
) async {
final contact = await twonlyDB.contactsDao
.getContactByUserId(contactId)
.getSingleOrNull();
if (contact == null || contact.lastFlameCounterChange != null) return;
var updates = ContactsCompanion(
alsoBestFriend: Value(flameSync.bestFriend),
);
if (isToday(contact.lastFlameCounterChange!) &&
isToday(fromTimestamp(flameSync.lastFlameCounterChange))) {
if (flameSync.flameCounter > contact.flameCounter) {
updates = ContactsCompanion(
flameCounter: Value(flameSync.flameCounter.toInt()),
);
}
}
await twonlyDB.contactsDao.updateContact(contactId, updates);
}
Future<int?> checkForProfileUpdate(
int fromUserId,
EncryptedContent content,
) async {
int? senderProfileCounter;
if (content.hasSenderProfileCounter() && !content.hasContactUpdate()) {
senderProfileCounter = content.senderProfileCounter.toInt();
final contact = await twonlyDB.contactsDao
.getContactByUserId(fromUserId)
.getSingleOrNull();
if (contact != null) {
if (contact.senderProfileCounter < senderProfileCounter) {
await sendCipherText(
fromUserId,
EncryptedContent()
..contactUpdate = (EncryptedContent_ContactUpdate()
..type = EncryptedContent_ContactUpdate_Type.REQUEST),
);
}
}
}
return senderProfileCounter;
}

View file

@ -0,0 +1,92 @@
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
Future<void> handleMedia(int fromUserId, String groupId, EncryptedContent_Media media) async {
TODO
}
Future<void> handleMediaUpdate(int fromUserId, String groupId, EncryptedContent_MediaUpdate mediaUpdate) async {
TODO
// switch (message.kind) {
// case MessageKind.receiveMediaError:
// if (message.messageReceiverId != null) {
// final openedMessage = await twonlyDB.messagesDao
// .getMessageByIdAndContactId(fromUserId, message.messageReceiverId!)
// .getSingleOrNull();
// if (openedMessage != null) {
// /// message found
// /// checks if
// /// 1. this was a media upload
// /// 2. the media was not already retransmitted
// /// 3. the media was send in the last two days
// if (openedMessage.mediaUploadId != null &&
// openedMessage.mediaRetransmissionState ==
// MediaRetransmitting.none &&
// openedMessage.sendAt
// .isAfter(DateTime.now().subtract(const Duration(days: 2)))) {
// // reset the media upload state to pending,
// // this will cause the media to be re-encrypted again
// await twonlyDB.mediaUploadsDao.updateMediaUpload(
// openedMessage.mediaUploadId!,
// const MediaUploadsCompanion(
// state: Value(
// UploadState.pending,
// ),
// ),
// );
// // reset the message upload so the upload will be done again
// await twonlyDB.messagesDao.updateMessageByOtherUser(
// fromUserId,
// message.messageReceiverId!,
// const MessagesCompanion(
// downloadState: Value(DownloadState.pending),
// mediaRetransmissionState:
// Value(MediaRetransmitting.retransmitted),
// ),
// );
// unawaited(retryMediaUpload(false));
// } else {
// await twonlyDB.messagesDao.updateMessageByOtherUser(
// fromUserId,
// message.messageReceiverId!,
// const MessagesCompanion(
// errorWhileSending: Value(true),
// ),
// );
// }
// }
// }
if (message.kind == MessageKind.storedMediaFile) {
if (message.messageReceiverId != null) {
/// stored media file just updates the message
await twonlyDB.messagesDao.updateMessageByOtherUser(
fromUserId,
message.messageReceiverId!,
const MessagesCompanion(
mediaStored: Value(true),
errorWhileSending: Value(false),
),
);
final msg = await twonlyDB.messagesDao
.getMessageByIdAndContactId(
fromUserId,
message.messageReceiverId!,
)
.getSingleOrNull();
if (msg != null && msg.mediaUploadId != null) {
final filePath = await getMediaFilePath(msg.mediaUploadId, 'send');
if (filePath.contains('mp4')) {
unawaited(createThumbnailsForVideo(File(filePath)));
} else {
unawaited(createThumbnailsForImage(File(filePath)));
}
}
}
} else if (message.content != null) {}
}

View file

@ -0,0 +1,35 @@
import 'package:twonly/globals.dart';
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
import 'package:twonly/src/services/api/utils.dart';
import 'package:twonly/src/utils/log.dart';
Future<void> handleMessageUpdate(
int contactId,
String groupId,
EncryptedContent_MessageUpdate messageUpdate,
) async {
switch (messageUpdate.type) {
case EncryptedContent_MessageUpdate_Type.OPENED:
Log.info('Opened message ${messageUpdate.senderMessageId}');
await twonlyDB.messagesDao.handleMessageOpened(
groupId,
messageUpdate.senderMessageId,
fromTimestamp(messageUpdate.timestamp),
);
case EncryptedContent_MessageUpdate_Type.DELETE:
Log.info('Delete message ${messageUpdate.senderMessageId}');
await twonlyDB.messagesDao.handleMessageDeletion(
contactId,
messageUpdate.senderMessageId,
fromTimestamp(messageUpdate.timestamp),
);
case EncryptedContent_MessageUpdate_Type.EDIT_TEXT:
Log.info('Edit message ${messageUpdate.senderMessageId}');
await twonlyDB.messagesDao.handleTextEdit(
contactId,
messageUpdate.senderMessageId,
messageUpdate.text,
fromTimestamp(messageUpdate.timestamp),
);
}
}

View file

@ -0,0 +1,20 @@
import 'package:fixnum/fixnum.dart';
import 'package:twonly/src/model/protobuf/api/websocket/client_to_server.pb.dart'
as client;
import 'package:twonly/src/services/signal/identity.signal.dart';
Future<client.Response> handleRequestNewPreKey() async {
final localPreKeys = await signalGetPreKeys();
final prekeysList = <client.Response_PreKey>[];
for (var i = 0; i < localPreKeys.length; i++) {
prekeysList.add(
client.Response_PreKey()
..id = Int64(localPreKeys[i].id)
..prekey = localPreKeys[i].getKeyPair().publicKey.serialize(),
);
}
final prekeys = client.Response_Prekeys(prekeys: prekeysList);
final ok = client.Response_Ok()..prekeys = prekeys;
return client.Response()..ok = ok;
}

View file

@ -0,0 +1,23 @@
import 'dart:async';
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
import 'package:twonly/src/services/notifications/pushkeys.notifications.dart';
DateTime lastPushKeyRequest = DateTime.now().subtract(const Duration(hours: 1));
Future<void> handlePushKey(
int contactId,
EncryptedContent_PushKeys pushKeys,
) async {
switch (pushKeys.type) {
case EncryptedContent_PushKeys_Type.REQUEST:
if (lastPushKeyRequest
.isBefore(DateTime.now().subtract(const Duration(seconds: 60)))) {
lastPushKeyRequest = DateTime.now();
unawaited(setupNotificationWithUsers(forceContact: contactId));
}
case EncryptedContent_PushKeys_Type.UPDATE:
await handleNewPushKey(contactId, pushKeys.keyId.toInt(), pushKeys.key);
}
}

View file

@ -0,0 +1,25 @@
import 'package:twonly/globals.dart';
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
Future<void> handleReaction(
int fromUserId,
String groupId,
EncryptedContent_Reaction reaction,
) async {
if (reaction.hasRemove()) {
if (reaction.remove) {
await twonlyDB.reactionsDao
.updateReaction(fromUserId, reaction.targetMessageId, groupId, null);
}
return;
}
if (reaction.hasEmoji()) {
await twonlyDB.reactionsDao.updateReaction(
fromUserId,
reaction.targetMessageId,
groupId,
reaction.emoji,
);
return;
}
}

View file

@ -0,0 +1,125 @@
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
Future<void> handleTextMessage(
int fromUserId,
String groupId,
EncryptedContent_TextMessage textMessage,
) async {
TODO
// final content = message.content!;
// // when a message is received doubled ignore it...
// final openedMessage = await twonlyDB.messagesDao
// .getMessageByOtherMessageId(fromUserId, message.messageSenderId!)
// .getSingleOrNull();
// if (openedMessage != null) {
// if (openedMessage.errorWhileSending) {
// await twonlyDB.messagesDao
// .deleteMessagesByMessageId(openedMessage.messageId);
// } else {
// Log.error(
// 'Got a duplicated message from other user: ${message.messageSenderId!}',
// );
// final ok = client.Response_Ok()..none = true;
// return client.Response()..ok = ok;
// }
// }
// int? responseToMessageId;
// int? responseToOtherMessageId;
// int? messageId;
// var acknowledgeByUser = false;
// DateTime? openedAt;
// if (message.kind == MessageKind.reopenedMedia) {
// acknowledgeByUser = true;
// openedAt = DateTime.now();
// }
// if (content is TextMessageContent) {
// responseToMessageId = content.responseToMessageId;
// responseToOtherMessageId = content.responseToOtherMessageId;
// if (responseToMessageId != null || responseToOtherMessageId != null) {
// // reactions are shown in the notification directly...
// if (isEmoji(content.text)) {
// openedAt = DateTime.now();
// }
// }
// }
// if (content is ReopenedMediaFileContent) {
// responseToMessageId = content.messageId;
// }
// if (responseToMessageId != null) {
// await twonlyDB.messagesDao.updateMessageByOtherUser(
// fromUserId,
// responseToMessageId,
// MessagesCompanion(
// errorWhileSending: const Value(false),
// openedAt: Value(
// DateTime.now(),
// ), // when a user reacted to the media file, it should be marked as opened
// ),
// );
// }
// final contentJson = jsonEncode(content.toJson());
// final update = MessagesCompanion(
// contactId: Value(fromUserId),
// kind: Value(message.kind),
// messageOtherId: Value(message.messageSenderId),
// contentJson: Value(contentJson),
// acknowledgeByServer: const Value(true),
// acknowledgeByUser: Value(acknowledgeByUser),
// responseToMessageId: Value(responseToMessageId),
// responseToOtherMessageId: Value(responseToOtherMessageId),
// openedAt: Value(openedAt),
// downloadState: Value(
// message.kind == MessageKind.media
// ? DownloadState.pending
// : DownloadState.downloaded,
// ),
// sendAt: Value(message.timestamp),
// );
// messageId = await twonlyDB.messagesDao.insertMessage(
// update,
// );
// if (messageId == null) {
// Log.error('could not insert message into db');
// return client.Response()..error = ErrorCode.InternalError;
// }
// Log.info('Inserted a new message with id: $messageId');
// if (message.kind == MessageKind.media) {
// await twonlyDB.contactsDao.incFlameCounter(
// fromUserId,
// true,
// message.timestamp,
// );
// final msg = await twonlyDB.messagesDao
// .getMessageByMessageId(messageId)
// .getSingleOrNull();
// if (msg != null) {
// unawaited(startDownloadMedia(msg, false));
// }
// }
// } else {
// Log.error('Content is not defined $message');
// }
// // unarchive contact when receiving a new message
// await twonlyDB.contactsDao.updateContact(
// fromUserId,
// const ContactsCompanion(
// archived: Value(false),
// ),
// );
// return null;
}

View file

@ -2,8 +2,8 @@ import 'package:drift/drift.dart';
import 'package:fixnum/fixnum.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/database/tables/messages_table.dart';
import 'package:twonly/src/database/twonly_database.dart';
import 'package:twonly/src/model/json/message.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/model/json/message_old.dart';
import 'package:twonly/src/model/protobuf/api/websocket/client_to_server.pb.dart'
as client;
import 'package:twonly/src/model/protobuf/api/websocket/client_to_server.pbserver.dart';
@ -24,6 +24,10 @@ class Result<T, E> {
bool get isError => error != null;
}
DateTime fromTimestamp(Int64 timeStamp) {
return DateTime.fromMillisecondsSinceEpoch(timeStamp.toInt() * 1000);
}
// ignore: strict_raw_type
Result asResult(server.ServerToClient? msg) {
if (msg == null) {

View file

@ -1,10 +1,10 @@
import 'package:collection/collection.dart';
import 'package:drift/drift.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/database/daos/contacts_dao.dart';
import 'package:twonly/src/database/daos/contacts.dao.dart';
import 'package:twonly/src/database/tables/messages_table.dart';
import 'package:twonly/src/database/twonly_database.dart';
import 'package:twonly/src/model/json/message.dart' as my;
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/model/json/message_old.dart' as my;
import 'package:twonly/src/services/api/messages.dart';
import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/utils/storage.dart';

View file

@ -0,0 +1,5 @@
import 'package:twonly/src/utils/log.dart';
Future<void> removeMediaFile(String mediaId) async {
Log.error('TODO removeMediaFile: $mediaId');
}

View file

@ -9,10 +9,10 @@ import 'package:flutter/services.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/constants/secure_storage_keys.dart';
import 'package:twonly/src/database/daos/contacts_dao.dart';
import 'package:twonly/src/database/daos/contacts.dao.dart';
import 'package:twonly/src/database/tables/messages_table.dart';
import 'package:twonly/src/database/twonly_database.dart';
import 'package:twonly/src/model/json/message.dart' as my;
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/model/json/message_old.dart' as my;
import 'package:twonly/src/model/protobuf/push_notification/push_notification.pb.dart';
import 'package:twonly/src/services/api/messages.dart';
import 'package:twonly/src/utils/log.dart';
@ -144,7 +144,7 @@ Future<void> updatePushUser(Contact contact) async {
await setPushKeys(SecureStorageKeys.receivingPushKeys, pushKeys);
}
Future<void> handleNewPushKey(int fromUserId, my.PushKeyContent pushKey) async {
Future<void> handleNewPushKey(int fromUserId, int keyId, List<int> key) async {
final pushKeys = await getPushKeys(SecureStorageKeys.sendingPushKeys);
var pushUser = pushKeys.firstWhereOrNull((x) => x.userId == fromUserId);

View file

@ -1,10 +1,8 @@
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
import 'package:mutex/mutex.dart';
import 'package:twonly/src/model/json/message.dart';
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
import 'package:twonly/src/services/signal/consts.signal.dart';
import 'package:twonly/src/services/signal/prekeys.signal.dart';
import 'package:twonly/src/services/signal/utils.signal.dart';
@ -92,42 +90,40 @@ Future<Uint8List?> signalEncryptMessage(
});
}
Future<MessageJson?> signalDecryptMessage(int source, Uint8List msg) async {
Future<(EncryptedContent?, PlaintextContent_DecryptionErrorMessage_Type?)>
signalDecryptMessage(
int source,
Uint8List encryptedContentRaw,
int type,
) async {
try {
final signalStore = (await getSignalStore())!;
final session = SessionCipher.fromStore(
signalStore,
(await getSignalStore())!,
SignalProtocolAddress(source.toString(), defaultDeviceId),
);
final msgs = removeLastXBytes(msg, 4);
if (msgs == null) {
Log.error('Message requires at least 4 bytes.');
return null;
}
final body = msgs[0];
final type = bytesToInt(msgs[1]);
Uint8List plaintext;
if (type == CiphertextMessage.prekeyType) {
final pre = PreKeySignalMessage(body);
plaintext = await session.decrypt(pre);
} else if (type == CiphertextMessage.whisperType) {
final signalMsg = SignalMessage.fromSerialized(body);
plaintext = await session.decryptFromSignal(signalMsg);
} else {
Log.error('Type not known: $type');
return null;
}
return MessageJson.fromJson(
jsonDecode(
utf8.decode(
gzip.decode(plaintext),
),
) as Map<String, dynamic>,
switch (type) {
case CiphertextMessage.prekeyType:
plaintext = await session.decrypt(
PreKeySignalMessage(encryptedContentRaw),
);
case CiphertextMessage.whisperType:
plaintext = await session.decryptFromSignal(
SignalMessage.fromSerialized(encryptedContentRaw),
);
default:
Log.error('Unknown Message Decryption Type: $type');
return (null, PlaintextContent_DecryptionErrorMessage_Type.UNKNOWN);
}
return (EncryptedContent.fromBuffer(plaintext), null);
} on InvalidKeyIdException catch (e) {
Log.error(e);
return (null, PlaintextContent_DecryptionErrorMessage_Type.PREKEY_UNKNOWN);
} catch (e) {
Log.error(e.toString());
return null;
Log.error(e);
return (null, PlaintextContent_DecryptionErrorMessage_Type.UNKNOWN);
}
}

View file

@ -3,7 +3,7 @@ import 'dart:async';
import 'package:drift/drift.dart';
import 'package:mutex/mutex.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/database/twonly_database.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/model/protobuf/api/websocket/server_to_client.pb.dart'
as server;
import 'package:twonly/src/utils/log.dart';

View file

@ -11,7 +11,7 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:twonly/src/constants/secure_storage_keys.dart';
import 'package:twonly/src/database/twonly_database.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/model/json/userdata.dart';
import 'package:twonly/src/model/protobuf/backup/backup.pb.dart';
import 'package:twonly/src/services/api/media_upload.dart';

View file

@ -11,7 +11,7 @@ import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:twonly/src/constants/secure_storage_keys.dart';
import 'package:twonly/src/database/tables/messages_table.dart';
import 'package:twonly/src/database/twonly_database.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/model/json/userdata.dart';
import 'package:twonly/src/model/protobuf/backup/backup.pb.dart';
import 'package:twonly/src/services/twonly_safe/common.twonly_safe.dart';

View file

@ -9,9 +9,9 @@ import 'package:gal/gal.dart';
import 'package:intl/intl.dart';
import 'package:local_auth/local_auth.dart';
import 'package:provider/provider.dart';
import 'package:twonly/src/database/twonly_database.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/localization/generated/app_localizations.dart';
import 'package:twonly/src/model/json/message.dart';
import 'package:twonly/src/model/json/message_old.dart';
import 'package:twonly/src/model/protobuf/api/websocket/error.pb.dart';
import 'package:twonly/src/providers/settings.provider.dart';
import 'package:twonly/src/utils/log.dart';

View file

@ -9,8 +9,8 @@ import 'package:image_picker/image_picker.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:screenshot/screenshot.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/database/daos/contacts_dao.dart';
import 'package:twonly/src/database/twonly_database.dart';
import 'package:twonly/src/database/daos/contacts.dao.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/utils/log.dart';
import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/utils/storage.dart';
@ -308,7 +308,7 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
imageBytes: imageBytes,
sharedFromGallery: sharedFromGallery,
sendTo: widget.sendTo,
mirrorVideo: isFront && Platform.isAndroid,
mirrorVideo: isFront && Platform.isAndroid && false,
useHighQuality: true,
),
transitionsBuilder: (context, animation, secondaryAnimation, child) {

View file

@ -3,7 +3,7 @@ import 'dart:async';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:screenshot/screenshot.dart';
import 'package:twonly/src/database/twonly_database.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/views/camera/camera_preview_components/camera_preview.dart';
import 'package:twonly/src/views/camera/camera_preview_controller_view.dart';

View file

@ -4,8 +4,8 @@ import 'dart:collection';
import 'package:flutter/material.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/database/daos/contacts_dao.dart';
import 'package:twonly/src/database/twonly_database.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/flame.dart';
import 'package:twonly/src/views/components/headline.dart';

View file

@ -8,8 +8,8 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:screenshot/screenshot.dart';
import 'package:twonly/src/database/daos/contacts_dao.dart';
import 'package:twonly/src/database/twonly_database.dart';
import 'package:twonly/src/database/daos/contacts.dao.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/services/api/media_upload.dart';
import 'package:twonly/src/utils/log.dart';
import 'package:twonly/src/utils/misc.dart';

View file

@ -7,8 +7,8 @@ import 'dart:typed_data';
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_database.dart';
import 'package:twonly/src/database/daos/contacts.dao.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/services/api/media_upload.dart';
import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/views/camera/share_image_components/best_friends_selector.dart';

View file

@ -5,10 +5,10 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.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/daos/contacts.dao.dart';
import 'package:twonly/src/database/tables/messages_table.dart';
import 'package:twonly/src/database/twonly_database.dart';
import 'package:twonly/src/model/json/message.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/model/json/message_old.dart';
import 'package:twonly/src/model/protobuf/push_notification/push_notification.pbserver.dart';
import 'package:twonly/src/services/api/messages.dart';
import 'package:twonly/src/services/api/utils.dart';

Some files were not shown because too many files have changed in this diff Show more