diff --git a/lib/src/database/daos/messages.dao.dart b/lib/src/database/daos/messages.dao.dart index 4115de9b..29edbc03 100644 --- a/lib/src/database/daos/messages.dao.dart +++ b/lib/src/database/daos/messages.dao.dart @@ -161,18 +161,6 @@ class MessagesDao extends DatabaseAccessor with _$MessagesDaoMixin { } } - Future openedAllTextMessages(String groupId) { - final updates = MessagesCompanion(openedAt: Value(clock.now())); - return (update(messages)..where( - (t) => - t.groupId.equals(groupId) & - t.senderId.isNotNull() & - t.openedAt.isNull() & - t.type.equals(MessageType.text.name), - )) - .write(updates); - } - Future handleMessageDeletion( int? contactId, String messageId, @@ -184,13 +172,13 @@ class MessagesDao extends DatabaseAccessor with _$MessagesDaoMixin { return; } if (msg.mediaId != null && contactId != null) { - final otherMessagesWithSameMedia = await (select(messages) - ..where( - (t) => - t.mediaId.equals(msg.mediaId!) & - t.messageId.equals(messageId).not(), - )) - .get(); + final otherMessagesWithSameMedia = + await (select(messages)..where( + (t) => + t.mediaId.equals(msg.mediaId!) & + t.messageId.equals(messageId).not(), + )) + .get(); if (otherMessagesWithSameMedia.isEmpty) { await (delete( @@ -210,7 +198,7 @@ class MessagesDao extends DatabaseAccessor with _$MessagesDaoMixin { await (delete( messageHistories, )..where((t) => t.messageId.equals(messageId))).go(); - + await twonlyDB.receiptsDao.deleteReceiptsByMessageId(messageId); await (update(messages)..where( diff --git a/lib/src/visual/views/chats/chat_messages.view.dart b/lib/src/visual/views/chats/chat_messages.view.dart index ab1e59d2..19152bad 100644 --- a/lib/src/visual/views/chats/chat_messages.view.dart +++ b/lib/src/visual/views/chats/chat_messages.view.dart @@ -6,6 +6,7 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:go_router/go_router.dart'; import 'package:mutex/mutex.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; +import 'package:twonly/globals.dart'; import 'package:twonly/locator.dart'; import 'package:twonly/src/constants/routes.keys.dart'; import 'package:twonly/src/database/daos/contacts.dao.dart'; @@ -36,7 +37,7 @@ class ChatMessagesView extends StatefulWidget { State createState() => _ChatMessagesViewState(); } -class _ChatMessagesViewState extends State { +class _ChatMessagesViewState extends State with WidgetsBindingObserver { HashSet alreadyReportedOpened = HashSet(); late StreamSubscription userSub; late StreamSubscription> messageSub; @@ -64,6 +65,7 @@ class _ChatMessagesViewState extends State { void initState() { super.initState(); textFieldFocus = FocusNode(); + WidgetsBinding.instance.addObserver(this); initStreams(); } @@ -74,11 +76,26 @@ class _ChatMessagesViewState extends State { contactSub?.cancel(); groupActionsSub?.cancel(); _nextTypingIndicator?.cancel(); + WidgetsBinding.instance.removeObserver(this); super.dispose(); } Mutex protectMessageUpdating = Mutex(); + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + if (state == AppLifecycleState.resumed) { + protectMessageUpdating.protect(() async { + await setMessages(allMessages, groupActions); + }); + } + } + + bool _isViewActive() { + return !AppState.isAppInBackground && + (ModalRoute.of(context)?.isCurrent ?? false); + } + Future initStreams() async { final groupStream = twonlyDB.groupsDao.watchGroup(widget.groupId); userSub = groupStream.listen((newGroup) { @@ -137,7 +154,9 @@ class _ChatMessagesViewState extends State { List newMessages, List groupActions, ) async { - await flutterLocalNotificationsPlugin.cancelAll(); + if (_isViewActive()) { + await flutterLocalNotificationsPlugin.cancelAll(); + } final chatItems = []; final storedMediaFiles = []; @@ -189,11 +208,13 @@ class _ChatMessagesViewState extends State { } } - for (final contactId in openedMessages.keys) { - await notifyContactAboutOpeningMessage( - contactId, - openedMessages[contactId]!, - ); + if (_isViewActive()) { + for (final contactId in openedMessages.keys) { + await notifyContactAboutOpeningMessage( + contactId, + openedMessages[contactId]!, + ); + } } if (!mounted) return; diff --git a/lib/src/visual/views/chats/media_viewer.view.dart b/lib/src/visual/views/chats/media_viewer.view.dart index dcb200f6..ab2f2332 100644 --- a/lib/src/visual/views/chats/media_viewer.view.dart +++ b/lib/src/visual/views/chats/media_viewer.view.dart @@ -10,6 +10,7 @@ import 'package:lottie/lottie.dart'; import 'package:mutex/mutex.dart'; import 'package:no_screenshot/no_screenshot.dart'; import 'package:photo_view/photo_view.dart'; +import 'package:twonly/globals.dart'; import 'package:twonly/locator.dart'; import 'package:twonly/src/constants/routes.keys.dart'; import 'package:twonly/src/database/daos/contacts.dao.dart'; @@ -46,7 +47,8 @@ class MediaViewerView extends StatefulWidget { State createState() => _MediaViewerViewState(); } -class _MediaViewerViewState extends State { +class _MediaViewerViewState extends State + with WidgetsBindingObserver { Timer? nextMediaTimer; Timer? progressTimer; @@ -87,6 +89,7 @@ class _MediaViewerViewState extends State { if (widget.initialMessage != null) { allMediaFiles = [widget.initialMessage!]; } + WidgetsBinding.instance.addObserver(this); asyncLoadNextMedia(true); } @@ -101,11 +104,28 @@ class _MediaViewerViewState extends State { final tmp = videoController; videoController = null; tmp?.dispose(); + WidgetsBinding.instance.removeObserver(this); super.dispose(); } final Mutex _messageUpdateLock = Mutex(); + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + if (state == AppLifecycleState.resumed) { + _messageUpdateLock.protect(() async { + if (currentMedia == null && allMediaFiles.isNotEmpty) { + await loadCurrentMediaFile(); + } + }); + } + } + + bool _isViewActive() { + return !AppState.isAppInBackground && + (ModalRoute.of(context)?.isCurrent ?? false); + } + Future asyncLoadNextMedia(bool firstRun) async { _subscription = twonlyDB.messagesDao .watchMediaNotOpened(widget.group.groupId) @@ -195,7 +215,9 @@ class _MediaViewerViewState extends State { showSendTextMessageInput = false; }); - unawaited(flutterLocalNotificationsPlugin.cancelAll()); + if (_isViewActive()) { + unawaited(flutterLocalNotificationsPlugin.cancelAll()); + } final stream = twonlyDB.mediaFilesDao.watchMedia( allMediaFiles.first.mediaId!,