From cb595004e450a1a02befd7afe5ec8202108401a6 Mon Sep 17 00:00:00 2001 From: otsmr Date: Mon, 31 Mar 2025 22:58:58 +0200 Subject: [PATCH] fix #89 --- lib/src/database/daos/messages_dao.dart | 4 + lib/src/localization/app_de.arb | 2 + lib/src/localization/app_en.arb | 2 + lib/src/views/chats/chat_list_view.dart | 168 +++++++++++++----------- lib/src/views/contact/contact_view.dart | 18 +++ 5 files changed, 115 insertions(+), 79 deletions(-) diff --git a/lib/src/database/daos/messages_dao.dart b/lib/src/database/daos/messages_dao.dart index 65de60c..d0ef2d4 100644 --- a/lib/src/database/daos/messages_dao.dart +++ b/lib/src/database/daos/messages_dao.dart @@ -144,6 +144,10 @@ class MessagesDao extends DatabaseAccessor return (delete(messages)..where((t) => t.messageId.equals(messageId))).go(); } + Future deleteMessagesByContactId(int contactId) { + return (delete(messages)..where((t) => t.contactId.equals(contactId))).go(); + } + Future containsOtherMessageId( int fromUserId, int messageOtherId) async { final query = select(messages) diff --git a/lib/src/localization/app_de.arb b/lib/src/localization/app_de.arb index 1b5db41..66ea768 100644 --- a/lib/src/localization/app_de.arb +++ b/lib/src/localization/app_de.arb @@ -83,6 +83,8 @@ "contactNickname": "Spitzname", "contactNicknameNew": "Neuer Spitzname", "contactBlock": "Blockieren", + "deleteAllContactMessages": "Alle Nachrichten löschen", + "deleteAllContactMessagesBody": "Dadurch werden alle Nachrichten in deinem Chat mit {username} gelöscht. Dies löscht NICHT die auf dem Gerät von {username} gespeicherten Nachrichten!", "contactBlockTitle": "Blockiere {username}", "contactBlockBody": "Ein blockierter Benutzer kann dir keine Nachrichten mehr senden, und sein Profil ist nicht mehr sichtbar. Um die Blockierung eines Benutzers aufzuheben, navigiere einfach zu Einstellungen > Datenschutz > Blockierte Benutzer.", "undo": "Rückgängig", diff --git a/lib/src/localization/app_en.arb b/lib/src/localization/app_en.arb index d9c572b..2e9f8df 100644 --- a/lib/src/localization/app_en.arb +++ b/lib/src/localization/app_en.arb @@ -121,6 +121,8 @@ "contactVerifyNumberLongDesc": "To verify the end-to-end encryption with {username}, compare the numbers with their device. The person can also scan your code with their device.", "contactNickname": "Nickname", "contactNicknameNew": "New nickname", + "deleteAllContactMessages": "Delete all messages", + "deleteAllContactMessagesBody": "This will remove all messages in your chat with {username}. This will NOT delete the messages stored at {username}s device!", "contactBlock": "Block", "contactBlockTitle": "Block {username}", "contactBlockBody": "A blocked user will no longer be able to send you messages and their profile will be hidden from view. To unblock a user, simply navigate to Settings > Privacy > Blocked Users.", diff --git a/lib/src/views/chats/chat_list_view.dart b/lib/src/views/chats/chat_list_view.dart index b798aa4..d18d362 100644 --- a/lib/src/views/chats/chat_list_view.dart +++ b/lib/src/views/chats/chat_list_view.dart @@ -1,6 +1,5 @@ import 'dart:async'; import 'dart:convert'; - import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:provider/provider.dart'; import 'package:twonly/globals.dart'; @@ -121,6 +120,7 @@ class _ChatListViewState extends State { return RefreshIndicator( onRefresh: () async { + await apiProvider.close(() {}); await apiProvider.connect(); await Future.delayed(Duration(seconds: 1)); }, @@ -130,6 +130,7 @@ class _ChatListViewState extends State { itemBuilder: (BuildContext context, int index) { final user = contacts[index]; return UserListItem( + key: ValueKey(user.userId), user: user, maxTotalMediaCounter: maxTotalMediaCounter, ); @@ -158,14 +159,80 @@ class _UserListItem extends State { MessageSendState state = MessageSendState.send; Message? currentMessage; + List messagesNotOpened = []; + late StreamSubscription> messagesNotOpenedStream; + + List lastMessages = []; + late StreamSubscription> lastMessageStream; + Timer? updateTime; @override void initState() { super.initState(); + initStreams(); lastUpdateTime(); } + void initStreams() { + lastMessageStream = twonlyDatabase.messagesDao + .watchLastMessage(widget.user.userId) + .listen((update) { + updateState(update, messagesNotOpened); + }); + + messagesNotOpenedStream = twonlyDatabase.messagesDao + .watchMessageNotOpened(widget.user.userId) + .listen((update) { + updateState(lastMessages, update); + }); + } + + void updateState( + List newLastMessages, + List newMessagesNotOpened, + ) { + if (newLastMessages.isEmpty) { + // there are no messages at all + currentMessage = null; + } else if (newMessagesNotOpened.isEmpty) { + // there are no not opened messages show just the last message in the table + currentMessage = newLastMessages.first; + } else { + // filter first for received messages + final receivedMessages = + newMessagesNotOpened.where((x) => x.messageOtherId != null).toList(); + + if (receivedMessages.isNotEmpty) { + // There are received messages + final mediaMessages = + receivedMessages.where((x) => x.kind == MessageKind.media); + + if (mediaMessages.isNotEmpty) { + currentMessage = mediaMessages.first; + } else { + currentMessage = receivedMessages.first; + } + } else { + // The not opened message was send + final mediaMessages = + newMessagesNotOpened.where((x) => x.kind == MessageKind.media); + + if (mediaMessages.isNotEmpty) { + currentMessage = mediaMessages.first; + } else { + currentMessage = newMessagesNotOpened.first; + } + } + } + + lastMessages = newLastMessages; + messagesNotOpened = newMessagesNotOpened; + setState(() { + // sets lastMessages, messagesNotOpened and currentMessage + }); + } + void lastUpdateTime() { // Change the color every 200 milliseconds updateTime = Timer.periodic(Duration(milliseconds: 200), (timer) { @@ -185,6 +252,8 @@ class _UserListItem extends State { void dispose() { updateTime?.cancel(); super.dispose(); + messagesNotOpenedStream.cancel(); + lastMessageStream.cancel(); } @override @@ -195,85 +264,26 @@ class _UserListItem extends State { contact: widget.user, child: ListTile( title: Text(getContactDisplayName(widget.user)), - subtitle: StreamBuilder( - stream: - twonlyDatabase.messagesDao.watchLastMessage(widget.user.userId), - builder: (context, lastMessageSnapshot) { - if (!lastMessageSnapshot.hasData) { - return Container(); - } - if (lastMessageSnapshot.data!.isEmpty) { - return Text(context.lang.chatsTapToSend); - } - final lastMessage = lastMessageSnapshot.data!.first; - return StreamBuilder( - stream: twonlyDatabase.messagesDao - .watchMessageNotOpened(widget.user.userId), - builder: (context, notOpenedMessagesSnapshot) { - if (!lastMessageSnapshot.hasData) { - return Container(); - } - - var lastMessages = [lastMessage]; - if (notOpenedMessagesSnapshot.data != null && - notOpenedMessagesSnapshot.data!.isNotEmpty) { - // filter first for only received messages - var notOpenedMessages = notOpenedMessagesSnapshot.data!; - - lastMessages = notOpenedMessages - .where((x) => x.messageOtherId != null) - .toList(); - - // For send images show only one - if (lastMessages.isEmpty) { - var media = notOpenedMessages - .where((x) => x.kind == MessageKind.media); - - if (media.isNotEmpty) { - currentMessage = media.first; - lastMessages = [currentMessage!]; - } else { - currentMessage = notOpenedMessages.first; - lastMessages = [currentMessage!]; - } - } else { - // there are multiple messages received - - var media = - lastMessages.where((x) => x.kind == MessageKind.media); - - if (media.isNotEmpty) { - currentMessage = media.first; - } else { - currentMessage = lastMessages.first; - } - } - } else { - currentMessage = lastMessage; - } - - return Row( - children: [ - MessageSendStateIcon(lastMessages), - Text("•"), - const SizedBox(width: 5), - Text( - formatDuration(lastMessageInSeconds), - style: TextStyle(fontSize: 12), + subtitle: (currentMessage == null) + ? Text(context.lang.chatsTapToSend) + : Row( + children: [ + MessageSendStateIcon(lastMessages), + Text("•"), + const SizedBox(width: 5), + Text( + formatDuration(lastMessageInSeconds), + style: TextStyle(fontSize: 12), + ), + if (flameCounter > 0) + FlameCounterWidget( + widget.user, + flameCounter, + widget.maxTotalMediaCounter, + prefix: true, ), - if (flameCounter > 0) - FlameCounterWidget( - widget.user, - flameCounter, - widget.maxTotalMediaCounter, - prefix: true, - ), - ], - ); - }, - ); - }, - ), + ], + ), leading: ContactAvatar(contact: widget.user), onTap: () { if (currentMessage == null) { diff --git a/lib/src/views/contact/contact_view.dart b/lib/src/views/contact/contact_view.dart index 291bce6..8870aa3 100644 --- a/lib/src/views/contact/contact_view.dart +++ b/lib/src/views/contact/contact_view.dart @@ -94,6 +94,24 @@ class _ContactViewState extends State { )); }, ), + BetterListTile( + icon: FontAwesomeIcons.trashCan, + text: context.lang.deleteAllContactMessages, + onTap: () async { + bool block = await showAlertDialog( + context, + context.lang.deleteAllContactMessages, + context.lang.deleteAllContactMessagesBody( + getContactDisplayName(contact)), + ); + if (block) { + if (context.mounted) { + await twonlyDatabase.messagesDao + .deleteMessagesByContactId(contact.userId); + } + } + }, + ), BetterListTile( icon: FontAwesomeIcons.ban, color: Colors.red,