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 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> 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> 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> watchLastMessage(int contactId) { return (select(messages) ..where((t) => t.contactId.equals(contactId)) ..orderBy([(t) => OrderingTerm.desc(t.sendAt)]) ..limit(1)) .watch(); } Stream> 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 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 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> 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> 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> getAllStoredMediaFiles() { return (select(messages) ..where((t) => t.mediaStored.equals(true)) ..orderBy([(t) => OrderingTerm.desc(t.sendAt)])) .watch(); } Future> 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 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 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 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 updateMessageByOtherUser( int userId, int messageId, MessagesCompanion updatedValues, ) { return (update(messages) ..where( (c) => c.contactId.equals(userId) & c.messageId.equals(messageId), )) .write(updatedValues); } Future updateMessageByOtherMessageId( int userId, int messageOtherId, MessagesCompanion updatedValues, ) { return (update(messages) ..where( (c) => c.contactId.equals(userId) & c.messageOtherId.equals(messageOtherId), )) .write(updatedValues); } Future updateMessageByMessageId( int messageId, MessagesCompanion updatedValues, ) { return (update(messages)..where((c) => c.messageId.equals(messageId))) .write(updatedValues); } Future 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 deleteMessagesByContactId(int contactId) { return (delete(messages) ..where( (t) => t.contactId.equals(contactId) & t.mediaStored.equals(false), )) .go(); } Future deleteMessagesByContactIdAndOtherMessageId( int contactId, int messageOtherId, ) { return (delete(messages) ..where( (t) => t.contactId.equals(contactId) & t.messageOtherId.equals(messageOtherId), )) .go(); } Future deleteMessagesByMessageId(int messageId) { return (delete(messages)..where((t) => t.messageId.equals(messageId))).go(); } Future deleteAllMessagesByContactId(int contactId) { return (delete(messages)..where((t) => t.contactId.equals(contactId))).go(); } Future 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 getMessageByMessageId(int messageId) { return select(messages)..where((t) => t.messageId.equals(messageId)); } Future> getMessagesByMediaUploadId(int mediaUploadId) async { return (select(messages) ..where((t) => t.mediaUploadId.equals(mediaUploadId))) .get(); } SingleOrNullSelectable getMessageByOtherMessageId( int fromUserId, int messageId, ) { return select(messages) ..where( (t) => t.messageOtherId.equals(messageId) & t.contactId.equals(fromUserId), ); } SingleOrNullSelectable getMessageByIdAndContactId( int fromUserId, int messageId, ) { return select(messages) ..where( (t) => t.messageId.equals(messageId) & t.contactId.equals(fromUserId), ); } }