From 42330594bb9a02793d3fc66a1d3dbb86ef9b92da Mon Sep 17 00:00:00 2001 From: otsmr Date: Tue, 8 Apr 2025 16:42:42 +0200 Subject: [PATCH] hide users from search user name view --- lib/src/components/user_context_menu.dart | 2 +- lib/src/database/daos/contacts_dao.dart | 4 +- lib/src/localization/app_de.arb | 8 + lib/src/localization/app_en.arb | 8 + .../views/chats/chat_item_details_view.dart | 149 ++++++++------- lib/src/views/chats/chat_list_view.dart | 18 +- lib/src/views/chats/media_viewer_view.dart | 2 +- lib/src/views/chats/search_username_view.dart | 174 +++++++++++------- 8 files changed, 206 insertions(+), 159 deletions(-) diff --git a/lib/src/components/user_context_menu.dart b/lib/src/components/user_context_menu.dart index 22d7343..0f03493 100644 --- a/lib/src/components/user_context_menu.dart +++ b/lib/src/components/user_context_menu.dart @@ -56,7 +56,7 @@ class _UserContextMenuState extends State { onSelect: () { Navigator.push(context, MaterialPageRoute( builder: (context) { - return ChatItemDetailsView(widget.contact.userId); + return ChatItemDetailsView(widget.contact); }, )); }, diff --git a/lib/src/database/daos/contacts_dao.dart b/lib/src/database/daos/contacts_dao.dart index 837202e..97daea9 100644 --- a/lib/src/database/daos/contacts_dao.dart +++ b/lib/src/database/daos/contacts_dao.dart @@ -108,7 +108,9 @@ class ContactsDao extends DatabaseAccessor } Stream> watchNotAcceptedContacts() { - return (select(contacts)..where((t) => t.accepted.equals(false))).watch(); + return (select(contacts) + ..where((t) => t.accepted.equals(false) & t.archived.equals(false))) + .watch(); // return (select(contacts)).watch(); } diff --git a/lib/src/localization/app_de.arb b/lib/src/localization/app_de.arb index 17d0151..dbfb542 100644 --- a/lib/src/localization/app_de.arb +++ b/lib/src/localization/app_de.arb @@ -39,6 +39,14 @@ "searchUsernameNotFoundBody": "Es wurde kein Benutzer mit dem Benutzernamen \"{username}\" gefunden.", "searchUsernameNewFollowerTitle": "Folgeanfragen", "searchUsernameQrCodeBtn": "QR-Code scannen", + "searchUserNamePending": "Ausstehend", + "@searchUserNamePending": {}, + "searchUserNameBlockUserTooltip": "Benutzer ohne Benachrichtigung blockieren.", + "@searchUserNameBlockUserTooltip": {}, + "searchUserNameRejectUserTooltip": "Die Anfrage ablehnen und den Anfragenden informieren.", + "@searchUserNameRejectUserTooltip": {}, + "searchUserNameArchiveUserTooltip": "Benutzer archivieren. Du wirst informiert sobald er deine Anfrage akzeptiert.", + "@searchUserNameArchiveUserTooltip": {}, "chatListViewSearchUserNameBtn": "Füge deinen ersten twonly-Kontakt hinzu!", "chatListViewSendFirstTwonly": "Sende dein erstes twonly!", "chatListDetailInput": "Nachricht eingeben", diff --git a/lib/src/localization/app_en.arb b/lib/src/localization/app_en.arb index 7d6f0bb..8065231 100644 --- a/lib/src/localization/app_en.arb +++ b/lib/src/localization/app_en.arb @@ -68,6 +68,14 @@ "@searchUsernameInput": {}, "searchUsernameTitle": "Search username", "@searchUsernameTitle": {}, + "searchUserNamePending": "Pending", + "@searchUserNamePending": {}, + "searchUserNameBlockUserTooltip": "Block the user without informing.", + "@searchUserNameBlockUserTooltip": {}, + "searchUserNameRejectUserTooltip": "Reject the request and let the requester know.", + "@searchUserNameRejectUserTooltip": {}, + "searchUserNameArchiveUserTooltip": "Archive the user. He will appear again as soon as he accepts your request.", + "@searchUserNameArchiveUserTooltip": {}, "searchUsernameNotFound": "Username not found", "@searchUsernameNotFound": {}, "searchUsernameNotFoundBody": "There is no user with the username \"{username}\" registered", diff --git a/lib/src/views/chats/chat_item_details_view.dart b/lib/src/views/chats/chat_item_details_view.dart index e4cb866..5ff3c10 100644 --- a/lib/src/views/chats/chat_item_details_view.dart +++ b/lib/src/views/chats/chat_item_details_view.dart @@ -209,9 +209,9 @@ class ChatListEntry extends StatelessWidget { /// Displays detailed information about a SampleItem. class ChatItemDetailsView extends StatefulWidget { - const ChatItemDetailsView(this.userid, {super.key}); + const ChatItemDetailsView(this.contact, {super.key}); - final int userid; + final Contact contact; @override State createState() => _ChatItemDetailsViewState(); @@ -220,7 +220,7 @@ class ChatItemDetailsView extends StatefulWidget { class _ChatItemDetailsViewState extends State { TextEditingController newMessageController = TextEditingController(); HashSet alreadyReportedOpened = HashSet(); - Contact? user; + late Contact user; String currentInputText = ""; late StreamSubscription userSub; late StreamSubscription> messageSub; @@ -231,6 +231,7 @@ class _ChatItemDetailsViewState extends State { @override void initState() { super.initState(); + user = widget.contact; initStreams(); } @@ -244,7 +245,7 @@ class _ChatItemDetailsViewState extends State { Future initStreams() async { await twonlyDatabase.messagesDao.removeOldMessages(); Stream contact = - twonlyDatabase.contactsDao.watchContact(widget.userid); + twonlyDatabase.contactsDao.watchContact(widget.contact.userId); userSub = contact.listen((contact) { setState(() { user = contact; @@ -252,15 +253,14 @@ class _ChatItemDetailsViewState extends State { }); Stream> msgStream = - twonlyDatabase.messagesDao.watchAllMessagesFrom(widget.userid); + twonlyDatabase.messagesDao.watchAllMessagesFrom(widget.contact.userId); messageSub = msgStream.listen((msgs) { if (!context.mounted) return; if (Platform.isAndroid) { - flutterLocalNotificationsPlugin.cancel(widget.userid); + flutterLocalNotificationsPlugin.cancel(widget.contact.userId); } else { flutterLocalNotificationsPlugin.cancelAll(); } - var updated = false; List displayedMessages = []; // should be cleared Map> tmpReactionsToMyMessages = {}; @@ -271,7 +271,6 @@ class _ChatItemDetailsViewState extends State { if (msg.kind == MessageKind.textMessage && msg.messageOtherId != null && msg.openedAt == null) { - updated = true; openedMessageOtherIds.add(msg.messageOtherId!); } @@ -294,25 +293,27 @@ class _ChatItemDetailsViewState extends State { } } if (openedMessageOtherIds.isNotEmpty) { - notifyContactAboutOpeningMessage(widget.userid, openedMessageOtherIds); + notifyContactAboutOpeningMessage( + widget.contact.userId, openedMessageOtherIds); } - twonlyDatabase.messagesDao.openedAllNonMediaMessages(widget.userid); + twonlyDatabase.messagesDao + .openedAllNonMediaMessages(widget.contact.userId); // should be fixed with that - if (!updated) { - // The stream should be get an update, so only update the UI when all are opened - setState(() { - reactionsToMyMessages = tmpReactionsToMyMessages; - reactionsToOtherMessages = tmpTeactionsToOtherMessages; - messages = displayedMessages; - }); - } + // if (!updated) { + // // The stream should be get an update, so only update the UI when all are opened + setState(() { + reactionsToMyMessages = tmpReactionsToMyMessages; + reactionsToOtherMessages = tmpTeactionsToOtherMessages; + messages = displayedMessages; + }); + // } }); } Future _sendMessage() async { - if (newMessageController.text == "" || user == null) return; + if (newMessageController.text == "") return; await sendTextMessage( - user!.userId, + user.userId, TextMessageContent( text: newMessageController.text, ), @@ -330,71 +331,67 @@ class _ChatItemDetailsViewState extends State { title: GestureDetector( onTap: () { Navigator.push(context, MaterialPageRoute(builder: (context) { - return ContactView(widget.userid); + return ContactView(widget.contact.userId); })); }, - child: (user == null) - ? Container() - : Row( - children: [ - ContactAvatar( - contact: user!, - fontSize: 19, - ), - SizedBox(width: 10), - Expanded( - child: Container( - color: Colors.transparent, - child: Row( - children: [ - Text(getContactDisplayName(user!)), - SizedBox(width: 10), - VerifiedShield(user!), - ], - ), - ), - ), - ], + child: Row( + children: [ + ContactAvatar( + contact: user, + fontSize: 19, + ), + SizedBox(width: 10), + Expanded( + child: Container( + color: Colors.transparent, + child: Row( + children: [ + Text(getContactDisplayName(user)), + SizedBox(width: 10), + VerifiedShield(user), + ], + ), ), + ), + ], + ), ), ), body: SafeArea( child: Column( children: [ - if (user != null) - Expanded( - child: ListView.builder( - itemCount: messages.length, - reverse: true, - itemBuilder: (context, i) { - bool lastMessageFromSameUser = false; - if (i > 0) { - lastMessageFromSameUser = - (messages[i - 1].messageOtherId == null && - messages[i].messageOtherId == null) || - (messages[i - 1].messageOtherId != null && - messages[i].messageOtherId != null); - } - Message msg = messages[i]; - List reactions = []; - if (reactionsToMyMessages.containsKey(msg.messageId)) { - reactions = reactionsToMyMessages[msg.messageId]!; - } - if (msg.messageOtherId != null && - reactionsToOtherMessages - .containsKey(msg.messageOtherId!)) { - reactions = - reactionsToOtherMessages[msg.messageOtherId!]!; - } - return ChatListEntry( - msg, - user!, - lastMessageFromSameUser, - reactions, - ); - }, - ), + Expanded( + child: ListView.builder( + itemCount: messages.length, + reverse: true, + itemBuilder: (context, i) { + bool lastMessageFromSameUser = false; + if (i > 0) { + lastMessageFromSameUser = + (messages[i - 1].messageOtherId == null && + messages[i].messageOtherId == null) || + (messages[i - 1].messageOtherId != null && + messages[i].messageOtherId != null); + } + Message msg = messages[i]; + List reactions = []; + if (reactionsToMyMessages.containsKey(msg.messageId)) { + reactions = reactionsToMyMessages[msg.messageId]!; + } + if (msg.messageOtherId != null && + reactionsToOtherMessages + .containsKey(msg.messageOtherId!)) { + reactions = reactionsToOtherMessages[msg.messageOtherId!]!; + } + return ChatListEntry( + msg, + user, + lastMessageFromSameUser, + reactions, + ); + }, ), + ), Padding( padding: const EdgeInsets.only( bottom: 30, left: 20, right: 20, top: 10), diff --git a/lib/src/views/chats/chat_list_view.dart b/lib/src/views/chats/chat_list_view.dart index 1d9900f..779bac5 100644 --- a/lib/src/views/chats/chat_list_view.dart +++ b/lib/src/views/chats/chat_list_view.dart @@ -173,6 +173,14 @@ class _UserListItem extends State { lastUpdateTime(); } + @override + void dispose() { + updateTime?.cancel(); + messagesNotOpenedStream.cancel(); + lastMessageStream.cancel(); + super.dispose(); + } + void initStreams() { lastMessageStream = twonlyDatabase.messagesDao .watchLastMessage(widget.user.userId) @@ -235,14 +243,6 @@ class _UserListItem extends State { }); } - @override - void dispose() { - updateTime?.cancel(); - super.dispose(); - messagesNotOpenedStream.cancel(); - lastMessageStream.cancel(); - } - @override Widget build(BuildContext context) { int flameCounter = getFlameCounterFromContact(widget.user); @@ -306,7 +306,7 @@ class _UserListItem extends State { Navigator.push( context, MaterialPageRoute(builder: (context) { - return ChatItemDetailsView(widget.user.userId); + return ChatItemDetailsView(widget.user); }), ); }, diff --git a/lib/src/views/chats/media_viewer_view.dart b/lib/src/views/chats/media_viewer_view.dart index 3bf1213..b1052b0 100644 --- a/lib/src/views/chats/media_viewer_view.dart +++ b/lib/src/views/chats/media_viewer_view.dart @@ -476,7 +476,7 @@ class _MediaViewerViewState extends State { Navigator.push( context, MaterialPageRoute(builder: (context) { - return ChatItemDetailsView(widget.contact.userId); + return ChatItemDetailsView(widget.contact); }), ); }, diff --git a/lib/src/views/chats/search_username_view.dart b/lib/src/views/chats/search_username_view.dart index 1733d5e..10a54a4 100644 --- a/lib/src/views/chats/search_username_view.dart +++ b/lib/src/views/chats/search_username_view.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:drift/drift.dart' hide Column; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:twonly/src/components/alert_dialog.dart'; import 'package:twonly/src/database/daos/contacts_dao.dart'; import 'package:twonly/src/database/tables/messages_table.dart'; @@ -29,6 +30,30 @@ class _SearchUsernameView extends State { bool _isLoading = false; bool hasRequestedUsers = false; + List contacts = []; + late StreamSubscription> contactsStream; + + @override + void initState() { + super.initState(); + initStreams(); + } + + @override + void dispose() { + contactsStream.cancel(); + super.dispose(); + } + + void initStreams() { + contactsStream = + twonlyDatabase.contactsDao.watchNotAcceptedContacts().listen((update) { + setState(() { + contacts = update; + }); + }); + } + Future _addNewUser(BuildContext context) async { final user = await getUser(); if (user == null || user.username == searchUserName.text) { @@ -105,9 +130,6 @@ class _SearchUsernameView extends State { ); } - Stream> contacts = - twonlyDatabase.contactsDao.watchNotAcceptedContacts(); - return Scaffold( appBar: AppBar( title: Text(context.lang.searchUsernameTitle), @@ -148,22 +170,12 @@ class _SearchUsernameView extends State { label: Text(context.lang.searchUsernameQrCodeBtn), ), SizedBox(height: 30), - if (hasRequestedUsers) + if (contacts.isNotEmpty) HeadLineComponent( context.lang.searchUsernameNewFollowerTitle, ), - StreamBuilder( - stream: contacts, - builder: (context, snapshot) { - if (!snapshot.hasData || - snapshot.data == null || - snapshot.data!.isEmpty) { - hasRequestedUsers = false; - return Container(); - } - hasRequestedUsers = true; - return Expanded(child: ContactsListView(snapshot.data!)); - }, + Expanded( + child: ContactsListView(contacts), ) ], ), @@ -194,6 +206,78 @@ class ContactsListView extends StatefulWidget { } class _ContactsListViewState extends State { + List sendRequestActions(Contact contact) { + return [ + Tooltip( + message: context.lang.searchUserNameArchiveUserTooltip, + child: IconButton( + icon: FaIcon(FontAwesomeIcons.boxArchive, size: 15), + onPressed: () async { + final update = ContactsCompanion(archived: Value(true)); + await twonlyDatabase.contactsDao + .updateContact(contact.userId, update); + }, + ), + ), + Text(context.lang.searchUserNamePending), + ]; + } + + List requestedActions(Contact contact) { + return [ + Tooltip( + message: context.lang.searchUserNameBlockUserTooltip, + child: IconButton( + icon: Icon(Icons.person_off_rounded, + color: const Color.fromARGB(164, 244, 67, 54)), + onPressed: () async { + final update = ContactsCompanion(blocked: Value(true)); + await twonlyDatabase.contactsDao + .updateContact(contact.userId, update); + }, + ), + ), + Tooltip( + message: context.lang.searchUserNameRejectUserTooltip, + child: IconButton( + icon: Icon(Icons.close, color: Colors.red), + onPressed: () async { + await twonlyDatabase.contactsDao + .deleteContactByUserId(contact.userId); + encryptAndSendMessage( + null, + contact.userId, + MessageJson( + kind: MessageKind.rejectRequest, + timestamp: DateTime.now(), + content: MessageContent(), + ), + ); + }, + ), + ), + IconButton( + icon: Icon(Icons.check, color: Colors.green), + onPressed: () async { + final update = ContactsCompanion(accepted: Value(true)); + await twonlyDatabase.contactsDao + .updateContact(contact.userId, update); + await encryptAndSendMessage( + null, + contact.userId, + MessageJson( + kind: MessageKind.acceptRequest, + timestamp: DateTime.now(), + content: MessageContent(), + ), + pushKind: PushKind.acceptRequest, + ); + notifyContactsAboutProfileChange(); + }, + ), + ]; + } + @override Widget build(BuildContext context) { return ListView.builder( @@ -206,61 +290,9 @@ class _ContactsListViewState extends State { leading: ContactAvatar(contact: contact), trailing: Row( mainAxisSize: MainAxisSize.min, - children: [ - if (!contact.requested) Text('Pending'), - if (contact.requested) ...[ - Tooltip( - message: "Block the user without informing.", - child: IconButton( - icon: Icon(Icons.person_off_rounded, - color: const Color.fromARGB(164, 244, 67, 54)), - onPressed: () async { - final update = ContactsCompanion(blocked: Value(true)); - await twonlyDatabase.contactsDao - .updateContact(contact.userId, update); - }, - ), - ), - Tooltip( - message: "Reject the request and let the requester know.", - child: IconButton( - icon: Icon(Icons.close, color: Colors.red), - onPressed: () async { - await twonlyDatabase.contactsDao - .deleteContactByUserId(contact.userId); - encryptAndSendMessage( - null, - contact.userId, - MessageJson( - kind: MessageKind.rejectRequest, - timestamp: DateTime.now(), - content: MessageContent(), - ), - ); - }, - ), - ), - IconButton( - icon: Icon(Icons.check, color: Colors.green), - onPressed: () async { - final update = ContactsCompanion(accepted: Value(true)); - await twonlyDatabase.contactsDao - .updateContact(contact.userId, update); - encryptAndSendMessage( - null, - contact.userId, - MessageJson( - kind: MessageKind.acceptRequest, - timestamp: DateTime.now(), - content: MessageContent(), - ), - pushKind: PushKind.acceptRequest, - ); - notifyContactsAboutProfileChange(); - }, - ), - ], - ], + children: contact.requested + ? requestedActions(contact) + : sendRequestActions(contact), ), ); },