mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-05-25 15:12:13 +00:00
fix: messages where opene even if the app is in the background
This commit is contained in:
parent
5b5140ec7c
commit
a93187c86d
3 changed files with 60 additions and 29 deletions
|
|
@ -161,18 +161,6 @@ class MessagesDao extends DatabaseAccessor<TwonlyDB> with _$MessagesDaoMixin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> 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<void> handleMessageDeletion(
|
Future<void> handleMessageDeletion(
|
||||||
int? contactId,
|
int? contactId,
|
||||||
String messageId,
|
String messageId,
|
||||||
|
|
@ -184,8 +172,8 @@ class MessagesDao extends DatabaseAccessor<TwonlyDB> with _$MessagesDaoMixin {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (msg.mediaId != null && contactId != null) {
|
if (msg.mediaId != null && contactId != null) {
|
||||||
final otherMessagesWithSameMedia = await (select(messages)
|
final otherMessagesWithSameMedia =
|
||||||
..where(
|
await (select(messages)..where(
|
||||||
(t) =>
|
(t) =>
|
||||||
t.mediaId.equals(msg.mediaId!) &
|
t.mediaId.equals(msg.mediaId!) &
|
||||||
t.messageId.equals(messageId).not(),
|
t.messageId.equals(messageId).not(),
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:mutex/mutex.dart';
|
import 'package:mutex/mutex.dart';
|
||||||
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
||||||
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/locator.dart';
|
import 'package:twonly/locator.dart';
|
||||||
import 'package:twonly/src/constants/routes.keys.dart';
|
import 'package:twonly/src/constants/routes.keys.dart';
|
||||||
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
||||||
|
|
@ -36,7 +37,7 @@ class ChatMessagesView extends StatefulWidget {
|
||||||
State<ChatMessagesView> createState() => _ChatMessagesViewState();
|
State<ChatMessagesView> createState() => _ChatMessagesViewState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ChatMessagesViewState extends State<ChatMessagesView> {
|
class _ChatMessagesViewState extends State<ChatMessagesView> with WidgetsBindingObserver {
|
||||||
HashSet<int> alreadyReportedOpened = HashSet<int>();
|
HashSet<int> alreadyReportedOpened = HashSet<int>();
|
||||||
late StreamSubscription<Group?> userSub;
|
late StreamSubscription<Group?> userSub;
|
||||||
late StreamSubscription<List<Message>> messageSub;
|
late StreamSubscription<List<Message>> messageSub;
|
||||||
|
|
@ -64,6 +65,7 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
textFieldFocus = FocusNode();
|
textFieldFocus = FocusNode();
|
||||||
|
WidgetsBinding.instance.addObserver(this);
|
||||||
initStreams();
|
initStreams();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -74,11 +76,26 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
|
||||||
contactSub?.cancel();
|
contactSub?.cancel();
|
||||||
groupActionsSub?.cancel();
|
groupActionsSub?.cancel();
|
||||||
_nextTypingIndicator?.cancel();
|
_nextTypingIndicator?.cancel();
|
||||||
|
WidgetsBinding.instance.removeObserver(this);
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
Mutex protectMessageUpdating = Mutex();
|
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<void> initStreams() async {
|
Future<void> initStreams() async {
|
||||||
final groupStream = twonlyDB.groupsDao.watchGroup(widget.groupId);
|
final groupStream = twonlyDB.groupsDao.watchGroup(widget.groupId);
|
||||||
userSub = groupStream.listen((newGroup) {
|
userSub = groupStream.listen((newGroup) {
|
||||||
|
|
@ -137,7 +154,9 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
|
||||||
List<Message> newMessages,
|
List<Message> newMessages,
|
||||||
List<GroupHistory> groupActions,
|
List<GroupHistory> groupActions,
|
||||||
) async {
|
) async {
|
||||||
|
if (_isViewActive()) {
|
||||||
await flutterLocalNotificationsPlugin.cancelAll();
|
await flutterLocalNotificationsPlugin.cancelAll();
|
||||||
|
}
|
||||||
|
|
||||||
final chatItems = <ChatItem>[];
|
final chatItems = <ChatItem>[];
|
||||||
final storedMediaFiles = <Message>[];
|
final storedMediaFiles = <Message>[];
|
||||||
|
|
@ -189,12 +208,14 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_isViewActive()) {
|
||||||
for (final contactId in openedMessages.keys) {
|
for (final contactId in openedMessages.keys) {
|
||||||
await notifyContactAboutOpeningMessage(
|
await notifyContactAboutOpeningMessage(
|
||||||
contactId,
|
contactId,
|
||||||
openedMessages[contactId]!,
|
openedMessages[contactId]!,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
setState(() {
|
setState(() {
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import 'package:lottie/lottie.dart';
|
||||||
import 'package:mutex/mutex.dart';
|
import 'package:mutex/mutex.dart';
|
||||||
import 'package:no_screenshot/no_screenshot.dart';
|
import 'package:no_screenshot/no_screenshot.dart';
|
||||||
import 'package:photo_view/photo_view.dart';
|
import 'package:photo_view/photo_view.dart';
|
||||||
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/locator.dart';
|
import 'package:twonly/locator.dart';
|
||||||
import 'package:twonly/src/constants/routes.keys.dart';
|
import 'package:twonly/src/constants/routes.keys.dart';
|
||||||
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
||||||
|
|
@ -46,7 +47,8 @@ class MediaViewerView extends StatefulWidget {
|
||||||
State<MediaViewerView> createState() => _MediaViewerViewState();
|
State<MediaViewerView> createState() => _MediaViewerViewState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MediaViewerViewState extends State<MediaViewerView> {
|
class _MediaViewerViewState extends State<MediaViewerView>
|
||||||
|
with WidgetsBindingObserver {
|
||||||
Timer? nextMediaTimer;
|
Timer? nextMediaTimer;
|
||||||
Timer? progressTimer;
|
Timer? progressTimer;
|
||||||
|
|
||||||
|
|
@ -87,6 +89,7 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
||||||
if (widget.initialMessage != null) {
|
if (widget.initialMessage != null) {
|
||||||
allMediaFiles = [widget.initialMessage!];
|
allMediaFiles = [widget.initialMessage!];
|
||||||
}
|
}
|
||||||
|
WidgetsBinding.instance.addObserver(this);
|
||||||
|
|
||||||
asyncLoadNextMedia(true);
|
asyncLoadNextMedia(true);
|
||||||
}
|
}
|
||||||
|
|
@ -101,11 +104,28 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
||||||
final tmp = videoController;
|
final tmp = videoController;
|
||||||
videoController = null;
|
videoController = null;
|
||||||
tmp?.dispose();
|
tmp?.dispose();
|
||||||
|
WidgetsBinding.instance.removeObserver(this);
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
final Mutex _messageUpdateLock = Mutex();
|
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<void> asyncLoadNextMedia(bool firstRun) async {
|
Future<void> asyncLoadNextMedia(bool firstRun) async {
|
||||||
_subscription = twonlyDB.messagesDao
|
_subscription = twonlyDB.messagesDao
|
||||||
.watchMediaNotOpened(widget.group.groupId)
|
.watchMediaNotOpened(widget.group.groupId)
|
||||||
|
|
@ -195,7 +215,9 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
||||||
showSendTextMessageInput = false;
|
showSendTextMessageInput = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (_isViewActive()) {
|
||||||
unawaited(flutterLocalNotificationsPlugin.cancelAll());
|
unawaited(flutterLocalNotificationsPlugin.cancelAll());
|
||||||
|
}
|
||||||
|
|
||||||
final stream = twonlyDB.mediaFilesDao.watchMedia(
|
final stream = twonlyDB.mediaFilesDao.watchMedia(
|
||||||
allMediaFiles.first.mediaId!,
|
allMediaFiles.first.mediaId!,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue