diff --git a/lib/src/components/user_context_menu.dart b/lib/src/components/user_context_menu.dart index 0f03493..672ffc5 100644 --- a/lib/src/components/user_context_menu.dart +++ b/lib/src/components/user_context_menu.dart @@ -27,17 +27,30 @@ class _UserContextMenuState extends State { return PieMenu( onPressed: () => (), actions: [ - PieAction( - tooltip: Text(context.lang.contextMenuArchiveUser), - onSelect: () async { - final update = ContactsCompanion(archived: Value(true)); - if (context.mounted) { - await twonlyDatabase.contactsDao - .updateContact(widget.contact.userId, update); - } - }, - child: FaIcon(FontAwesomeIcons.boxArchive), - ), + if (!widget.contact.archived) + PieAction( + tooltip: Text(context.lang.contextMenuArchiveUser), + onSelect: () async { + final update = ContactsCompanion(archived: Value(true)); + if (context.mounted) { + await twonlyDatabase.contactsDao + .updateContact(widget.contact.userId, update); + } + }, + child: FaIcon(FontAwesomeIcons.boxArchive), + ), + if (widget.contact.archived) + PieAction( + tooltip: Text(context.lang.contextMenuUndoArchiveUser), + onSelect: () async { + final update = ContactsCompanion(archived: Value(false)); + if (context.mounted) { + await twonlyDatabase.contactsDao + .updateContact(widget.contact.userId, update); + } + }, + child: FaIcon(FontAwesomeIcons.boxOpen), + ), PieAction( tooltip: Text(context.lang.contextMenuVerifyUser), onSelect: () { diff --git a/lib/src/localization/app_de.arb b/lib/src/localization/app_de.arb index dbfb542..ac8284d 100644 --- a/lib/src/localization/app_de.arb +++ b/lib/src/localization/app_de.arb @@ -54,6 +54,14 @@ "@contextMenuVerifyUser": {}, "contextMenuArchiveUser": "Archivieren", "@contextMenuArchiveUser": {}, + "contextMenuUndoArchiveUser": "Archivierung aufheben", + "@contextMenuUndoArchiveUser": {}, + "startNewChatTitle": "Kontakt wählen", + "@startNewChatTitle": {}, + "startNewChatNewContact": "Neuer Kontakt", + "@startNewChatNewContact": {}, + "startNewChatYourContacts": "Deine Kontakte", + "@startNewChatYourContacts": {}, "contextMenuOpenChat": "Chat", "contextMenuSendImage": "Bild senden", "mediaViewerAuthReason": "Bitte authentifiziere dich, um diesen twonly zu sehen!", diff --git a/lib/src/localization/app_en.arb b/lib/src/localization/app_en.arb index 8065231..e637e10 100644 --- a/lib/src/localization/app_en.arb +++ b/lib/src/localization/app_en.arb @@ -60,6 +60,12 @@ "@shareImagedEditorSavedImage": {}, "shareImageSearchAllContacts": "Search all contacts", "@shareImageSearchAllContacts": {}, + "startNewChatTitle": "Select Contact", + "@startNewChatTitle": {}, + "startNewChatNewContact": "New Contact", + "@startNewChatNewContact": {}, + "startNewChatYourContacts": "Your Contacts", + "@startNewChatYourContacts": {}, "shareImageAllUsers": "All contacts", "@shareImageAllUsers": {}, "shareImageAllTwonlyWarning": "twonlies can only be send to verified contacts!", @@ -98,6 +104,8 @@ "@contextMenuVerifyUser": {}, "contextMenuArchiveUser": "Archive", "@contextMenuArchiveUser": {}, + "contextMenuUndoArchiveUser": "Undo archiving", + "@contextMenuUndoArchiveUser": {}, "contextMenuOpenChat": "Open chat", "@contextMenuOpenChat": {}, "contextMenuSendImage": "Send image", diff --git a/lib/src/utils/misc.dart b/lib/src/utils/misc.dart index 03ce93a..1475188 100644 --- a/lib/src/utils/misc.dart +++ b/lib/src/utils/misc.dart @@ -9,6 +9,7 @@ import 'package:local_auth/local_auth.dart'; import 'package:logging/logging.dart'; import 'package:path_provider/path_provider.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:pie_menu/pie_menu.dart'; import 'package:provider/provider.dart'; import 'package:twonly/src/database/twonly_database.dart'; import 'package:twonly/src/proto/api/error.pb.dart'; @@ -177,3 +178,27 @@ Future isAllowedToDownload() async { } return true; } + +PieTheme getPieCanvasTheme(BuildContext context) { + return PieTheme( + brightness: Theme.of(context).brightness, + rightClickShowsMenu: true, + radius: 70, + buttonTheme: PieButtonTheme( + backgroundColor: Theme.of(context).colorScheme.tertiary, + iconColor: Theme.of(context).colorScheme.surfaceBright, + ), + buttonThemeHovered: PieButtonTheme( + backgroundColor: Theme.of(context).colorScheme.primary, + iconColor: Theme.of(context).colorScheme.surfaceBright, + ), + tooltipPadding: EdgeInsets.all(20), + overlayColor: const Color.fromARGB(41, 0, 0, 0), + + // spacing: 0, + tooltipTextStyle: TextStyle( + fontSize: 32, + fontWeight: FontWeight.w600, + ), + ); +} diff --git a/lib/src/views/chats/chat_list_view.dart b/lib/src/views/chats/chat_list_view.dart index 779bac5..27a69e4 100644 --- a/lib/src/views/chats/chat_list_view.dart +++ b/lib/src/views/chats/chat_list_view.dart @@ -16,6 +16,7 @@ import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/views/camera_to_share/share_image_view.dart'; import 'package:twonly/src/views/chats/chat_item_details_view.dart'; import 'package:twonly/src/views/chats/media_viewer_view.dart'; +import 'package:twonly/src/views/chats/start_new_chat.dart'; import 'package:twonly/src/views/home_view.dart'; import 'package:twonly/src/views/settings/settings_main_view.dart'; import 'package:twonly/src/views/chats/search_username_view.dart'; @@ -32,18 +33,7 @@ class _ChatListViewState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: GestureDetector( - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => SettingsMainView(), - ), - ); - }, - child: Text("twonly"), - ), - // title: + title: Text("twonly"), actions: [ StreamBuilder( stream: twonlyDatabase.contactsDao.watchContactsRequested(), @@ -133,6 +123,21 @@ class _ChatListViewState extends State { ); }, ), + floatingActionButton: Padding( + padding: const EdgeInsets.only(bottom: 30.0), + child: FloatingActionButton( + foregroundColor: Colors.white, + onPressed: () { + Navigator.push( + context, + MaterialPageRoute(builder: (context) { + return StartNewChat(); + }), + ); + }, + child: FaIcon(FontAwesomeIcons.penToSquare), + ), + ), ); } } diff --git a/lib/src/views/chats/search_username_view.dart b/lib/src/views/chats/search_username_view.dart index 10a54a4..87789fa 100644 --- a/lib/src/views/chats/search_username_view.dart +++ b/lib/src/views/chats/search_username_view.dart @@ -184,12 +184,13 @@ class _SearchUsernameView extends State { floatingActionButton: Padding( padding: const EdgeInsets.only(bottom: 30.0), child: FloatingActionButton( + foregroundColor: Colors.white, onPressed: () { if (!_isLoading) _addNewUser(context); }, child: (_isLoading) ? const Center(child: CircularProgressIndicator()) - : Icon(Icons.arrow_right_rounded), + : FaIcon(FontAwesomeIcons.magnifyingGlassPlus), ), ), ); diff --git a/lib/src/views/chats/start_new_chat.dart b/lib/src/views/chats/start_new_chat.dart new file mode 100644 index 0000000..33a5674 --- /dev/null +++ b/lib/src/views/chats/start_new_chat.dart @@ -0,0 +1,183 @@ +import 'dart:async'; +import 'package:flutter/material.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:pie_menu/pie_menu.dart'; +import 'package:twonly/globals.dart'; +import 'package:twonly/src/components/flame.dart'; +import 'package:twonly/src/components/headline.dart'; +import 'package:twonly/src/components/initialsavatar.dart'; +import 'package:twonly/src/components/user_context_menu.dart'; +import 'package:twonly/src/database/daos/contacts_dao.dart'; +import 'package:twonly/src/database/twonly_database.dart'; +import 'package:twonly/src/utils/misc.dart'; +import 'package:twonly/src/views/chats/chat_item_details_view.dart'; +import 'package:twonly/src/views/chats/search_username_view.dart'; + +class StartNewChat extends StatefulWidget { + const StartNewChat({super.key}); + @override + State createState() => _StartNewChat(); +} + +class _StartNewChat extends State { + List contacts = []; + List allContacts = []; + final TextEditingController searchUserName = TextEditingController(); + late StreamSubscription> contactSub; + int maxTotalMediaCounter = 1000; + + @override + void initState() { + super.initState(); + + Stream> stream = + twonlyDatabase.contactsDao.watchContactsForShareView(); + + contactSub = stream.listen((update) { + setState(() { + allContacts = update; + }); + filterUsers(); + }); + } + + @override + void dispose() { + super.dispose(); + contactSub.cancel(); + } + + Future filterUsers() async { + if (searchUserName.value.text.isEmpty) { + setState(() { + contacts = allContacts; + }); + return; + } + List usersFiltered = allContacts + .where((user) => getContactDisplayName(user) + .toLowerCase() + .contains(searchUserName.value.text.toLowerCase())) + .toList(); + setState(() { + contacts = usersFiltered; + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(context.lang.startNewChatTitle), + ), + body: SafeArea( + child: PieCanvas( + theme: getPieCanvasTheme(context), + child: Padding( + padding: EdgeInsets.only(bottom: 40, left: 10, top: 20, right: 10), + child: Column( + children: [ + Padding( + padding: EdgeInsets.symmetric(horizontal: 10), + child: TextField( + onChanged: (_) { + filterUsers(); + }, + decoration: getInputDecoration( + context, + context.lang.shareImageSearchAllContacts, + ), + ), + ), + const SizedBox(height: 10), + Expanded( + child: UserList( + contacts, + maxTotalMediaCounter, + ), + ) + ], + ), + ), + ), + ), + ); + } +} + +class UserList extends StatelessWidget { + const UserList( + this.users, + this.maxTotalMediaCounter, { + super.key, + }); + final List users; + final int maxTotalMediaCounter; + + @override + Widget build(BuildContext context) { + // Step 1: Sort the users alphabetically + users + .sort((a, b) => b.lastMessageExchange.compareTo(a.lastMessageExchange)); + + return ListView.builder( + restorationId: 'new_message_users_list', + itemCount: users.length + 2, + itemBuilder: (BuildContext context, int i) { + if (i == 0) { + return ListTile( + title: Text(context.lang.startNewChatNewContact), + leading: CircleAvatar( + child: FaIcon( + FontAwesomeIcons.userPlus, + size: 15, + ), + ), + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => SearchUsernameView(), + ), + ); + }, + ); + } + if (i == 1) { + return HeadLineComponent(context.lang.startNewChatYourContacts); + } + Contact user = users[i - 2]; + int flameCounter = getFlameCounterFromContact(user); + return UserContextMenu( + contact: user, + child: ListTile( + title: Row( + mainAxisAlignment: MainAxisAlignment.start, // Center horizontally + crossAxisAlignment: + CrossAxisAlignment.center, // Center vertically + children: [ + Text(getContactDisplayName(user)), + if (flameCounter >= 1) + FlameCounterWidget( + user, + flameCounter, + maxTotalMediaCounter, + prefix: true, + ), + ], + ), + leading: ContactAvatar(contact: user), + onTap: () { + Navigator.pushReplacement( + context, + MaterialPageRoute(builder: (context) { + return ChatItemDetailsView(user); + }), + ); + }, + ), + ); + }, + ); + } +} diff --git a/lib/src/views/home_view.dart b/lib/src/views/home_view.dart index 1715778..c259cb3 100644 --- a/lib/src/views/home_view.dart +++ b/lib/src/views/home_view.dart @@ -2,6 +2,7 @@ import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:pie_menu/pie_menu.dart'; import 'package:twonly/src/services/notification_service.dart'; +import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/views/camera_to_share/share_image_view.dart'; import 'camera_to_share/camera_preview_view.dart'; import 'chats/chat_list_view.dart'; @@ -65,27 +66,7 @@ class HomeViewState extends State { @override Widget build(BuildContext context) { return PieCanvas( - theme: PieTheme( - brightness: Theme.of(context).brightness, - rightClickShowsMenu: true, - radius: 70, - buttonTheme: PieButtonTheme( - backgroundColor: Theme.of(context).colorScheme.tertiary, - iconColor: Theme.of(context).colorScheme.surfaceBright, - ), - buttonThemeHovered: PieButtonTheme( - backgroundColor: Theme.of(context).colorScheme.primary, - iconColor: Theme.of(context).colorScheme.surfaceBright, - ), - tooltipPadding: EdgeInsets.all(20), - overlayColor: const Color.fromARGB(41, 0, 0, 0), - - // spacing: 0, - tooltipTextStyle: TextStyle( - fontSize: 32, - fontWeight: FontWeight.w600, - ), - ), + theme: getPieCanvasTheme(context), child: Scaffold( body: PageView( controller: homeViewPageController,