diff --git a/lib/pages/chat_details/chat_details_view.dart b/lib/pages/chat_details/chat_details_view.dart index 4e19e43ca..3c17112a5 100644 --- a/lib/pages/chat_details/chat_details_view.dart +++ b/lib/pages/chat_details/chat_details_view.dart @@ -85,33 +85,16 @@ class ChatDetailsView extends StatelessWidget { padding: const EdgeInsets.all(32.0), child: Stack( children: [ - Material( - elevation: Theme.of(context) - .appBarTheme - .scrolledUnderElevation ?? - 4, - shadowColor: Theme.of(context) - .appBarTheme - .shadowColor, - shape: RoundedRectangleBorder( - side: BorderSide( - color: Theme.of(context).dividerColor, - ), - borderRadius: BorderRadius.circular( - Avatar.defaultSize * 2.5, - ), - ), - child: Hero( - tag: controller - .widget.embeddedCloseButton != - null - ? 'embedded_content_banner' - : 'content_banner', - child: Avatar( - mxContent: room.avatar, - name: displayname, - size: Avatar.defaultSize * 2.5, - ), + Hero( + tag: + controller.widget.embeddedCloseButton != + null + ? 'embedded_content_banner' + : 'content_banner', + child: Avatar( + mxContent: room.avatar, + name: displayname, + size: Avatar.defaultSize * 2.5, ), ), if (!room.isDirectChat && @@ -170,7 +153,7 @@ class ChatDetailsView extends StatelessWidget { : displayname, maxLines: 1, overflow: TextOverflow.ellipsis, - // style: const TextStyle(fontSize: 18), + style: const TextStyle(fontSize: 18), ), ), TextButton.icon( @@ -202,10 +185,7 @@ class ChatDetailsView extends StatelessWidget { ), ], ), - Divider( - height: 1, - color: Theme.of(context).dividerColor, - ), + Divider(color: Theme.of(context).dividerColor), if (!room.canChangeStateEvent(EventTypes.RoomTopic)) ListTile( title: Text( @@ -261,10 +241,7 @@ class ChatDetailsView extends StatelessWidget { ), ), const SizedBox(height: 16), - Divider( - height: 1, - color: Theme.of(context).dividerColor, - ), + Divider(color: Theme.of(context).dividerColor), ListTile( leading: CircleAvatar( backgroundColor: @@ -316,10 +293,7 @@ class ChatDetailsView extends StatelessWidget { onTap: () => context .push('/rooms/${room.id}/details/permissions'), ), - Divider( - height: 1, - color: Theme.of(context).dividerColor, - ), + Divider(color: Theme.of(context).dividerColor), ListTile( title: Text( L10n.of(context)!.countParticipants( diff --git a/lib/pages/new_group/new_group_view.dart b/lib/pages/new_group/new_group_view.dart index a83497d7b..df2a4ac49 100644 --- a/lib/pages/new_group/new_group_view.dart +++ b/lib/pages/new_group/new_group_view.dart @@ -102,10 +102,6 @@ class NewGroupView extends StatelessWidget { child: SizedBox( width: double.infinity, child: ElevatedButton( - style: ElevatedButton.styleFrom( - foregroundColor: Theme.of(context).colorScheme.onPrimary, - backgroundColor: Theme.of(context).colorScheme.primary, - ), onPressed: controller.loading ? null : controller.submitAction, child: controller.loading diff --git a/lib/pages/new_space/new_space_view.dart b/lib/pages/new_space/new_space_view.dart index e90c7a84e..2f46e5e73 100644 --- a/lib/pages/new_space/new_space_view.dart +++ b/lib/pages/new_space/new_space_view.dart @@ -74,10 +74,6 @@ class NewSpaceView extends StatelessWidget { child: SizedBox( width: double.infinity, child: ElevatedButton( - style: ElevatedButton.styleFrom( - foregroundColor: Theme.of(context).colorScheme.onPrimary, - backgroundColor: Theme.of(context).colorScheme.primary, - ), onPressed: controller.loading ? null : controller.submitAction, child: controller.loading diff --git a/lib/pages/settings/settings_view.dart b/lib/pages/settings/settings_view.dart index b1d60f142..b2293f4b2 100644 --- a/lib/pages/settings/settings_view.dart +++ b/lib/pages/settings/settings_view.dart @@ -28,13 +28,6 @@ class SettingsView extends StatelessWidget { ), ), title: Text(L10n.of(context)!.settings), - actions: [ - TextButton.icon( - onPressed: controller.logoutAction, - label: Text(L10n.of(context)!.logout), - icon: const Icon(Icons.logout_outlined), - ), - ], ), body: ListTileTheme( iconColor: Theme.of(context).colorScheme.onSurface, @@ -55,32 +48,17 @@ class SettingsView extends StatelessWidget { padding: const EdgeInsets.all(32.0), child: Stack( children: [ - Material( - elevation: Theme.of(context) - .appBarTheme - .scrolledUnderElevation ?? - 4, - shadowColor: - Theme.of(context).appBarTheme.shadowColor, - shape: RoundedRectangleBorder( - side: BorderSide( - color: Theme.of(context).dividerColor, - ), - borderRadius: BorderRadius.circular( - Avatar.defaultSize * 2.5, - ), - ), - child: Avatar( - mxContent: profile?.avatarUrl, - name: displayname, - size: Avatar.defaultSize * 2.5, - ), + Avatar( + mxContent: profile?.avatarUrl, + name: displayname, + size: Avatar.defaultSize * 2.5, ), if (profile != null) Positioned( bottom: 0, right: 0, child: FloatingActionButton.small( + elevation: 2, onPressed: controller.setAvatarAction, heroTag: null, child: const Icon(Icons.camera_alt_outlined), @@ -108,7 +86,9 @@ class SettingsView extends StatelessWidget { displayname, maxLines: 1, overflow: TextOverflow.ellipsis, - // style: const TextStyle(fontSize: 18), + style: const TextStyle( + fontSize: 18, + ), ), ), TextButton.icon( @@ -135,10 +115,7 @@ class SettingsView extends StatelessWidget { ); }, ), - Divider( - height: 1, - color: Theme.of(context).dividerColor, - ), + Divider(color: Theme.of(context).dividerColor), if (showChatBackupBanner == null) ListTile( leading: const Icon(Icons.backup_outlined), @@ -154,60 +131,54 @@ class SettingsView extends StatelessWidget { onChanged: controller.firstRunBootstrapAction, ), Divider( - height: 1, color: Theme.of(context).dividerColor, ), ListTile( leading: const Icon(Icons.format_paint_outlined), title: Text(L10n.of(context)!.changeTheme), onTap: () => context.go('/rooms/settings/style'), - trailing: const Icon(Icons.chevron_right_outlined), ), ListTile( leading: const Icon(Icons.notifications_outlined), title: Text(L10n.of(context)!.notifications), onTap: () => context.go('/rooms/settings/notifications'), - trailing: const Icon(Icons.chevron_right_outlined), ), ListTile( leading: const Icon(Icons.devices_outlined), title: Text(L10n.of(context)!.devices), onTap: () => context.go('/rooms/settings/devices'), - trailing: const Icon(Icons.chevron_right_outlined), ), ListTile( leading: const Icon(Icons.forum_outlined), title: Text(L10n.of(context)!.chat), onTap: () => context.go('/rooms/settings/chat'), - trailing: const Icon(Icons.chevron_right_outlined), ), ListTile( leading: const Icon(Icons.shield_outlined), title: Text(L10n.of(context)!.security), onTap: () => context.go('/rooms/settings/security'), - trailing: const Icon(Icons.chevron_right_outlined), - ), - Divider( - height: 1, - color: Theme.of(context).dividerColor, ), + Divider(color: Theme.of(context).dividerColor), ListTile( leading: const Icon(Icons.help_outline_outlined), title: Text(L10n.of(context)!.help), onTap: () => launchUrlString(AppConfig.supportUrl), - trailing: const Icon(Icons.open_in_new_outlined), ), ListTile( leading: const Icon(Icons.shield_sharp), title: Text(L10n.of(context)!.privacy), onTap: () => launchUrlString(AppConfig.privacyUrl), - trailing: const Icon(Icons.open_in_new_outlined), ), ListTile( leading: const Icon(Icons.info_outline_rounded), title: Text(L10n.of(context)!.about), onTap: () => PlatformInfos.showDialog(context), - trailing: const Icon(Icons.chevron_right_outlined), + ), + Divider(color: Theme.of(context).dividerColor), + ListTile( + leading: const Icon(Icons.logout_outlined), + title: Text(L10n.of(context)!.logout), + onTap: controller.logoutAction, ), ], ), diff --git a/lib/pages/settings_3pid/settings_3pid_view.dart b/lib/pages/settings_3pid/settings_3pid_view.dart index 49f8b4489..4c5377c64 100644 --- a/lib/pages/settings_3pid/settings_3pid_view.dart +++ b/lib/pages/settings_3pid/settings_3pid_view.dart @@ -69,7 +69,7 @@ class Settings3PidView extends StatelessWidget { .withTheseAddressesRecoveryDescription, ), ), - const Divider(height: 1), + const Divider(), Expanded( child: ListView.builder( itemCount: identifier.length, diff --git a/lib/pages/settings_chat/settings_chat_view.dart b/lib/pages/settings_chat/settings_chat_view.dart index c3abe990e..01cc7af29 100644 --- a/lib/pages/settings_chat/settings_chat_view.dart +++ b/lib/pages/settings_chat/settings_chat_view.dart @@ -71,10 +71,7 @@ class SettingsChatView extends StatelessWidget { storeKey: SettingKeys.swipeRightToLeftToReply, defaultValue: AppConfig.swipeRightToLeftToReply, ), - Divider( - height: 1, - color: Theme.of(context).dividerColor, - ), + Divider(color: Theme.of(context).dividerColor), ListTile( title: Text( L10n.of(context)!.customEmojisAndStickers, @@ -93,10 +90,7 @@ class SettingsChatView extends StatelessWidget { child: Icon(Icons.chevron_right_outlined), ), ), - Divider( - height: 1, - color: Theme.of(context).dividerColor, - ), + Divider(color: Theme.of(context).dividerColor), ListTile( title: Text( L10n.of(context)!.calls, diff --git a/lib/pages/settings_emotes/settings_emotes_view.dart b/lib/pages/settings_emotes/settings_emotes_view.dart index 7e72cd464..be1e36f92 100644 --- a/lib/pages/settings_emotes/settings_emotes_view.dart +++ b/lib/pages/settings_emotes/settings_emotes_view.dart @@ -117,7 +117,7 @@ class EmotesSettingsView extends StatelessWidget { onChanged: controller.setIsGloballyActive, ), if (!controller.readonly || controller.room != null) - const Divider(thickness: 1), + const Divider(), imageKeys.isEmpty ? Center( child: Padding( diff --git a/lib/pages/settings_security/settings_security_view.dart b/lib/pages/settings_security/settings_security_view.dart index 49b39d750..6ff7e1696 100644 --- a/lib/pages/settings_security/settings_security_view.dart +++ b/lib/pages/settings_security/settings_security_view.dart @@ -86,7 +86,6 @@ class SettingsSecurityView extends StatelessWidget { ), }, Divider( - height: 1, color: Theme.of(context).dividerColor, ), ListTile( diff --git a/lib/pages/settings_style/settings_style_view.dart b/lib/pages/settings_style/settings_style_view.dart index 86f48fe87..1a07dd2bc 100644 --- a/lib/pages/settings_style/settings_style_view.dart +++ b/lib/pages/settings_style/settings_style_view.dart @@ -136,7 +136,6 @@ class SettingsStyleView extends StatelessWidget { ), const SizedBox(height: 8), Divider( - height: 1, color: Theme.of(context).dividerColor, ), ListTile( @@ -167,7 +166,6 @@ class SettingsStyleView extends StatelessWidget { onChanged: controller.switchTheme, ), Divider( - height: 1, color: Theme.of(context).dividerColor, ), ListTile( @@ -192,7 +190,6 @@ class SettingsStyleView extends StatelessWidget { defaultValue: AppConfig.separateChatTypes, ), Divider( - height: 1, color: Theme.of(context).dividerColor, ), ListTile( diff --git a/lib/pages/user_bottom_sheet/user_bottom_sheet.dart b/lib/pages/user_bottom_sheet/user_bottom_sheet.dart index 527915072..1ae173341 100644 --- a/lib/pages/user_bottom_sheet/user_bottom_sheet.dart +++ b/lib/pages/user_bottom_sheet/user_bottom_sheet.dart @@ -226,6 +226,45 @@ class UserBottomSheetController extends State { } } + bool isSending = false; + + Object? sendError; + + final TextEditingController sendController = TextEditingController(); + + void sendAction([_]) async { + final userId = widget.user?.id ?? widget.profile?.userId; + final client = Matrix.of(context).client; + if (userId == null) throw ('user or profile must not be null!'); + + final input = sendController.text.trim(); + if (input.isEmpty) return; + + setState(() { + isSending = true; + sendError = null; + }); + try { + final roomId = await client.startDirectChat(userId); + if (!mounted) return; + final room = client.getRoomById(roomId); + if (room == null) { + throw ('DM Room found or created but room not found in client'); + } + await room.sendTextEvent(input); + setState(() { + isSending = false; + sendController.clear(); + }); + } catch (e, s) { + Logs().d('Unable to send message', e, s); + setState(() { + isSending = false; + sendError = e; + }); + } + } + void knockAccept() async { final user = widget.user!; final result = await showFutureLoadingDialog( diff --git a/lib/pages/user_bottom_sheet/user_bottom_sheet_view.dart b/lib/pages/user_bottom_sheet/user_bottom_sheet_view.dart index 9e72a4a3f..c6d3b5979 100644 --- a/lib/pages/user_bottom_sheet/user_bottom_sheet_view.dart +++ b/lib/pages/user_bottom_sheet/user_bottom_sheet_view.dart @@ -7,6 +7,7 @@ import 'package:matrix/matrix.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/utils/date_time_extension.dart'; import 'package:fluffychat/utils/fluffy_share.dart'; +import 'package:fluffychat/utils/localized_exception_extension.dart'; import 'package:fluffychat/utils/url_launcher.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/presence_builder.dart'; @@ -29,6 +30,7 @@ class UserBottomSheetView extends StatelessWidget { final client = Matrix.of(controller.widget.outerContext).client; final profileSearchError = controller.widget.profileSearchError; + final dmRoomId = client.getDirectChatFromUserId(userId); return SafeArea( child: Scaffold( appBar: AppBar( @@ -90,19 +92,19 @@ class UserBottomSheetView extends StatelessWidget { ), ], ), - actions: [ - if (userId != client.userID && - !client.ignoredUsers.contains(userId)) - Padding( - padding: const EdgeInsets.only(right: 8.0), - child: IconButton( - icon: const Icon(Icons.block_outlined), - tooltip: L10n.of(context)!.block, - onPressed: () => controller - .participantAction(UserBottomSheetAction.ignore), - ), - ), - ], + actions: dmRoomId == null + ? null + : [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: FloatingActionButton.small( + elevation: 0, + onPressed: () => controller + .participantAction(UserBottomSheetAction.message), + child: const Icon(Icons.chat_outlined), + ), + ), + ], ), body: StreamBuilder( stream: user?.room.client.onSync.stream.where( @@ -169,25 +171,10 @@ class UserBottomSheetView extends StatelessWidget { children: [ Padding( padding: const EdgeInsets.all(16.0), - child: Material( - elevation: Theme.of(context) - .appBarTheme - .scrolledUnderElevation ?? - 4, - shadowColor: Theme.of(context).appBarTheme.shadowColor, - shape: RoundedRectangleBorder( - side: BorderSide( - color: Theme.of(context).dividerColor, - ), - borderRadius: BorderRadius.circular( - Avatar.defaultSize * 2.5, - ), - ), - child: Avatar( - mxContent: avatarUrl, - name: displayname, - size: Avatar.defaultSize * 2.5, - ), + child: Avatar( + mxContent: avatarUrl, + name: displayname, + size: Avatar.defaultSize * 2.5, ), ), Expanded( @@ -212,7 +199,7 @@ class UserBottomSheetView extends StatelessWidget { displayname, maxLines: 1, overflow: TextOverflow.ellipsis, - // style: const TextStyle(fontSize: 18), + style: const TextStyle(fontSize: 18), ), ), TextButton.icon( @@ -247,16 +234,35 @@ class UserBottomSheetView extends StatelessWidget { horizontal: 16.0, vertical: 8.0, ), - child: ElevatedButton.icon( - onPressed: () => controller - .participantAction(UserBottomSheetAction.message), - icon: const Icon(Icons.forum_outlined), - label: Text( - controller.widget.user == null - ? L10n.of(context)!.startConversation - : L10n.of(context)!.sendAMessage, - ), - ), + child: dmRoomId == null + ? ElevatedButton.icon( + onPressed: () => controller.participantAction( + UserBottomSheetAction.message, + ), + icon: const Icon(Icons.chat_outlined), + label: Text(L10n.of(context)!.startConversation), + ) + : TextField( + controller: controller.sendController, + readOnly: controller.isSending, + onSubmitted: controller.sendAction, + minLines: 1, + maxLines: 1, + textInputAction: TextInputAction.send, + decoration: InputDecoration( + errorText: controller.sendError + ?.toLocalizedString(context), + hintText: L10n.of(context)!.sendMessages, + suffixIcon: controller.isSending + ? const CircularProgressIndicator.adaptive( + strokeWidth: 2, + ) + : IconButton( + icon: const Icon(Icons.send_outlined), + onPressed: controller.sendAction, + ), + ), + ), ), PresenceBuilder( userId: userId, @@ -334,8 +340,8 @@ class UserBottomSheetView extends StatelessWidget { ), ), ), - Divider(color: Theme.of(context).dividerColor), ], + Divider(color: Theme.of(context).dividerColor), if (user != null && user.canKick) ListTile( textColor: Theme.of(context).colorScheme.error, @@ -370,7 +376,7 @@ class UserBottomSheetView extends StatelessWidget { textColor: Theme.of(context).colorScheme.onErrorContainer, iconColor: Theme.of(context).colorScheme.onErrorContainer, title: Text(L10n.of(context)!.reportUser), - leading: const Icon(Icons.report_outlined), + leading: const Icon(Icons.gavel_outlined), onTap: () => controller .participantAction(UserBottomSheetAction.report), ), @@ -385,6 +391,16 @@ class UserBottomSheetView extends StatelessWidget { style: const TextStyle(color: Colors.orange), ), ), + if (userId != client.userID && + !client.ignoredUsers.contains(userId)) + ListTile( + textColor: Theme.of(context).colorScheme.onErrorContainer, + iconColor: Theme.of(context).colorScheme.onErrorContainer, + leading: const Icon(Icons.block_outlined), + title: Text(L10n.of(context)!.block), + onTap: () => controller + .participantAction(UserBottomSheetAction.ignore), + ), ], ); },