diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 4d3cfeaf6a..c082c59999 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -2686,5 +2686,19 @@ "select" : "Select", "copyMessageText" : "Copy message text", "pinMessage" : "Pin message", - "add": "Add" + "add": "Add", + "addMembers": "Add members", + "chatInfo": "Chat info", + "mute": "Mute", + "membersInfo": "Members ({count})", + "@membersInfo": { + "placeholders": { + "count": {} + } + }, + "members": "Members", + "media": "Media", + "files": "Files", + "links": "Links", + "downloads": "Downloads" } \ No newline at end of file diff --git a/lib/pages/chat_details/chat_details.dart b/lib/pages/chat_details/chat_details.dart index 9e3f562215..1a1c44e00a 100644 --- a/lib/pages/chat_details/chat_details.dart +++ b/lib/pages/chat_details/chat_details.dart @@ -1,7 +1,13 @@ import 'package:collection/collection.dart'; import 'package:file_picker/file_picker.dart'; +import 'package:fluffychat/di/global/get_it_initializer.dart'; +import 'package:fluffychat/pages/chat_details/chat_details_actions_enum.dart'; +import 'package:fluffychat/pages/chat_details/chat_details_page_view/chat_details_members_page.dart'; +import 'package:fluffychat/pages/chat_details/chat_details_page_view/chat_details_page_enum.dart'; import 'package:fluffychat/pages/invitation_selection/invitation_selection.dart'; import 'package:fluffychat/pages/invitation_selection/invitation_selection_web.dart'; +import 'package:fluffychat/presentation/extensions/room_summary_extension.dart'; +import 'package:fluffychat/presentation/model/chat_details/chat_details_page_model.dart'; import 'package:fluffychat/utils/extension/build_context_extension.dart'; import 'package:fluffychat/utils/responsive/responsive_utils.dart'; import 'package:flutter/material.dart'; @@ -33,20 +39,38 @@ class ChatDetails extends StatefulWidget { } class ChatDetailsController extends State { - static const invitationSelectionMobileAndTabletKey = - Key('InvitationSelectionMobileAndTabletKey'); + final invitationSelectionMobileAndTabletKey = + const Key('InvitationSelectionMobileAndTabletKey'); - static const invitationSelectionWebAndDesktopKey = - Key('InvitationSelectionWebAndDesktopKey'); + final invitationSelectionWebAndDesktopKey = + const Key('InvitationSelectionWebAndDesktopKey'); + + final actionsMobileAndTabletKey = const Key('ActionsMobileAndTabletKey'); + + final actionsWebAndDesktopKey = const Key('ActionsWebAndDesktopKey'); + + final responsive = getIt.get(); + + PageController pageController = PageController(); + + final ValueNotifier currentPage = ValueNotifier(0); List? members; + bool displaySettings = false; + String? get roomId => widget.roomId; + + bool get isMobileAndTablet => + responsive.isMobile(context) || responsive.isTablet(context); + + Room? get room => Matrix.of(context).client.getRoomById(roomId!); + + int get actualMembersCount => room!.summary.actualMembersCount; + void toggleDisplaySettings() => setState(() => displaySettings = !displaySettings); - String? get roomId => widget.roomId; - void setDisplaynameAction() async { final room = Matrix.of(context).client.getRoomById(roomId!)!; final input = await showTextInputDialog( @@ -366,13 +390,15 @@ class ChatDetailsController extends State { showDialog( context: context, barrierDismissible: false, + useSafeArea: false, + useRootNavigator: PlatformInfos.isMobile ? false : true, builder: (context) { return SlotLayout( config: { const WidthPlatformBreakpoint( begin: ResponsiveUtils.minDesktopWidth, ): SlotLayout.from( - key: invitationSelectionMobileAndTabletKey, + key: invitationSelectionWebAndDesktopKey, builder: (_) => InvitationSelectionWebView( roomId: roomId!, ), @@ -380,7 +406,7 @@ class ChatDetailsController extends State { const WidthPlatformBreakpoint( end: ResponsiveUtils.minDesktopWidth, ): SlotLayout.from( - key: invitationSelectionWebAndDesktopKey, + key: invitationSelectionMobileAndTabletKey, builder: (_) => InvitationSelection( roomId: roomId!, ), @@ -391,6 +417,55 @@ class ChatDetailsController extends State { ); } + List chatDetailsActionsButton() => [ + if (responsive.isDesktop(context)) ChatDetailsActions.addMembers, + ChatDetailsActions.mute, + ChatDetailsActions.search, + ChatDetailsActions.more, + ]; + + List chatDetailsPages() => [ + ChatDetailsPageModel( + page: ChatDetailsPage.members, + child: ChatDetailsMembersPage( + members: members ?? [], + actualMembersCount: actualMembersCount, + canRequestMoreMembers: members!.length < actualMembersCount, + requestMoreMembersAction: requestMoreMembersAction, + openDialogInvite: openDialogInvite, + isMobileAndTablet: isMobileAndTablet, + ), + ), + const ChatDetailsPageModel( + page: ChatDetailsPage.media, + child: SizedBox.shrink(), + ), + const ChatDetailsPageModel( + page: ChatDetailsPage.files, + child: SizedBox.shrink(), + ), + const ChatDetailsPageModel( + page: ChatDetailsPage.links, + child: SizedBox.shrink(), + ), + const ChatDetailsPageModel( + page: ChatDetailsPage.downloads, + child: SizedBox.shrink(), + ), + ]; + + void onTapActionsButton(ChatDetailsActions action) { + switch (action) { + case ChatDetailsActions.addMembers: + openDialogInvite(); + break; + case ChatDetailsActions.mute: + case ChatDetailsActions.search: + case ChatDetailsActions.more: + break; + } + } + @override Widget build(BuildContext context) { members ??= diff --git a/lib/pages/chat_details/chat_details_actions_button.dart b/lib/pages/chat_details/chat_details_actions_button.dart index 102441f136..58e8e15ec7 100644 --- a/lib/pages/chat_details/chat_details_actions_button.dart +++ b/lib/pages/chat_details/chat_details_actions_button.dart @@ -41,22 +41,23 @@ class ChatDetailsActionsButton extends StatelessWidget { Widget build(BuildContext context) { return Material( color: Colors.transparent, - child: Container( - width: width, - padding: padding ?? const EdgeInsetsDirectional.all(8), - decoration: ShapeDecoration( - color: buttonColor, - shape: RoundedRectangleBorder( - side: borderSide ?? - BorderSide( - width: 0.50, - color: LinagoraRefColors.material().neutral[90]!, - ), - borderRadius: BorderRadius.circular(borderRadius ?? 16), + child: InkWell( + onTap: onTap, + borderRadius: BorderRadius.circular(borderRadius ?? 16), + child: Container( + width: width, + padding: padding ?? const EdgeInsetsDirectional.all(8), + decoration: ShapeDecoration( + color: buttonColor, + shape: RoundedRectangleBorder( + side: borderSide ?? + BorderSide( + width: 0.50, + color: LinagoraRefColors.material().neutral[90]!, + ), + borderRadius: BorderRadius.circular(borderRadius ?? 16), + ), ), - ), - child: InkWell( - onTap: onTap, child: Column( mainAxisSize: MainAxisSize.max, children: [ diff --git a/lib/pages/chat_details/chat_details_header_view.dart b/lib/pages/chat_details/chat_details_header_view.dart new file mode 100644 index 0000000000..14f73e3224 --- /dev/null +++ b/lib/pages/chat_details/chat_details_header_view.dart @@ -0,0 +1,63 @@ +import 'package:fluffychat/pages/chat_details/chat_details_actions_enum.dart'; +import 'package:flutter/material.dart'; + +import 'chat_details_actions_button.dart'; + +typedef OnTapIconButtonCallbackAction = Function(ChatDetailsActions); + +class ActionsHeaderBuilder extends StatelessWidget { + final List actions; + + final double? width; + + final double? iconSize; + + final double? borderRadius; + + final BorderSide? borderSide; + + final EdgeInsetsDirectional? padding; + + final Color? buttonColor; + + final Color? iconColor; + + final OnTapIconButtonCallbackAction? onTap; + + const ActionsHeaderBuilder({ + super.key, + required this.actions, + this.width, + this.iconSize, + this.borderRadius, + this.borderSide, + this.padding, + this.buttonColor, + this.iconColor, + this.onTap, + }); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsetsDirectional.all(16.0), + child: Wrap( + spacing: 16, + children: actions.map((action) { + return ChatDetailsActionsButton( + title: action.getTitle(context), + onTap: () => onTap!(action), + iconData: action.getIconData(), + iconSize: iconSize, + width: width, + borderRadius: borderRadius, + borderSide: borderSide, + padding: padding, + buttonColor: buttonColor, + iconColor: iconColor, + ); + }).toList(), + ), + ); + } +} diff --git a/lib/pages/chat_details/chat_details_page_view/chat_details_members_page.dart b/lib/pages/chat_details/chat_details_page_view/chat_details_members_page.dart new file mode 100644 index 0000000000..7316f2becc --- /dev/null +++ b/lib/pages/chat_details/chat_details_page_view/chat_details_members_page.dart @@ -0,0 +1,117 @@ +import 'package:fluffychat/pages/chat_details/participant_list_item.dart'; +import 'package:flutter/material.dart'; +import 'package:linagora_design_flutter/linagora_design_flutter.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:matrix/matrix.dart'; + +class ChatDetailsMembersPage extends StatelessWidget { + final List members; + final int actualMembersCount; + final VoidCallback openDialogInvite; + final VoidCallback requestMoreMembersAction; + final bool canRequestMoreMembers; + final bool isMobileAndTablet; + + const ChatDetailsMembersPage({ + super.key, + required this.members, + required this.actualMembersCount, + required this.openDialogInvite, + required this.requestMoreMembersAction, + required this.canRequestMoreMembers, + required this.isMobileAndTablet, + }); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + _listMemberInfoMobileAndTablet(context), + Expanded( + child: ListView.builder( + shrinkWrap: true, + itemCount: members.length + (canRequestMoreMembers ? 1 : 0), + itemBuilder: (BuildContext context, int index) { + if (index < members.length) { + return ParticipantListItem(members[index]); + } + + return ListTile( + title: Text( + L10n.of(context)!.loadCountMoreParticipants( + (actualMembersCount - members.length).toString(), + ), + ), + leading: CircleAvatar( + backgroundColor: Theme.of(context).scaffoldBackgroundColor, + child: const Icon( + Icons.refresh, + color: Colors.grey, + ), + ), + onTap: requestMoreMembersAction, + ); + }, + ), + ), + ], + ); + } + + Widget _listMemberInfoMobileAndTablet(BuildContext context) { + if (!isMobileAndTablet) return const SizedBox.shrink(); + return Column( + children: [ + Container( + padding: const EdgeInsetsDirectional.all(16), + color: LinagoraSysColors.material().surface, + child: Align( + alignment: Alignment.centerLeft, + child: Text( + L10n.of(context)!.membersInfo( + actualMembersCount.toString(), + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.labelLarge?.copyWith( + color: LinagoraRefColors.material().neutral[40], + ), + ), + ), + ), + InkWell( + onTap: openDialogInvite, + child: Container( + padding: const EdgeInsetsDirectional.only( + start: 16.0, + top: 8.0, + bottom: 8.0, + ), + margin: const EdgeInsetsDirectional.symmetric( + horizontal: 16.0, + vertical: 10.0, + ), + child: Row( + children: [ + Icon( + Icons.person_add, + size: 18, + color: LinagoraSysColors.material().primary, + ), + const SizedBox(width: 8), + Text( + L10n.of(context)!.addMembers, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.labelLarge?.copyWith( + color: LinagoraSysColors.material().primary, + ), + ), + ], + ), + ), + ), + ], + ); + } +} diff --git a/lib/pages/chat_details/chat_details_view.dart b/lib/pages/chat_details/chat_details_view.dart index 657aaaaa4e..8ab28b14c0 100644 --- a/lib/pages/chat_details/chat_details_view.dart +++ b/lib/pages/chat_details/chat_details_view.dart @@ -1,21 +1,16 @@ -import 'package:fluffychat/pages/chat_details/chat_detail_view_style.dart'; +import 'package:fluffychat/pages/chat_details/chat_details_header_view.dart'; +import 'package:fluffychat/pages/chat_details/chat_details_page_view/chat_details_page_view.dart'; +import 'package:fluffychat/pages/chat_details/chat_details_view_style.dart'; import 'package:fluffychat/presentation/extensions/room_summary_extension.dart'; -import 'package:fluffychat/utils/extension/build_context_extension.dart'; -import 'package:fluffychat/widgets/avatar/avatar_style.dart'; +import 'package:fluffychat/widgets/avatar/avatar.dart'; +import 'package:fluffychat/widgets/twake_components/twake_icon_button.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:go_router/go_router.dart'; +import 'package:linagora_design_flutter/linagora_design_flutter.dart'; import 'package:matrix/matrix.dart'; -import 'package:matrix_link_text/link_text.dart'; -import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pages/chat_details/chat_details.dart'; -import 'package:fluffychat/pages/chat_details/participant_list_item.dart'; -import 'package:fluffychat/utils/fluffy_share.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; -import 'package:fluffychat/widgets/chat_settings_popup_menu.dart'; -import 'package:fluffychat/widgets/content_banner.dart'; -import 'package:fluffychat/widgets/layouts/max_width_body.dart'; -import 'package:fluffychat/widgets/matrix.dart'; -import '../../utils/url_launcher.dart'; class ChatDetailsView extends StatelessWidget { final ChatDetailsController controller; @@ -24,8 +19,7 @@ class ChatDetailsView extends StatelessWidget { @override Widget build(BuildContext context) { - final room = Matrix.of(context).client.getRoomById(controller.roomId!); - if (room == null) { + if (controller.room == null) { return Scaffold( appBar: AppBar( title: Text(L10n.of(context)!.oopsSomethingWentWrong), @@ -37,411 +31,186 @@ class ChatDetailsView extends StatelessWidget { } controller.members!.removeWhere((u) => u.membership == Membership.leave); - final actualMembersCount = room.summary.actualMembersCount; - final canRequestMoreMembers = - controller.members!.length < actualMembersCount; - final iconColor = Theme.of(context).textTheme.bodyLarge!.color; return StreamBuilder( - stream: room.onUpdate.stream, + stream: controller.room?.onUpdate.stream, builder: (context, snapshot) { return Scaffold( - body: NestedScrollView( - headerSliverBuilder: - (BuildContext context, bool innerBoxIsScrolled) => [ - SliverAppBar( - toolbarHeight: ChatDetailViewStyle.toolbarHeight, - automaticallyImplyLeading: false, - elevation: Theme.of(context).appBarTheme.elevation, - expandedHeight: 300.0, - floating: true, - pinned: true, - title: Row( - children: [ - IconButton( - icon: const Icon(Icons.close_outlined), - onPressed: controller.onPressedClose, - ), - Expanded( - child: Text( - room.getLocalizedDisplayname( - MatrixLocals(L10n.of(context)!), - ), - ), + backgroundColor: controller.isMobileAndTablet + ? Theme.of(context).colorScheme.background + : Theme.of(context).colorScheme.surface, + appBar: AppBar( + automaticallyImplyLeading: false, + toolbarHeight: ChatDetailViewStyle.toolbarHeight, + bottom: PreferredSize( + preferredSize: const Size.fromHeight(1), + child: Container( + decoration: BoxDecoration( + border: Border( + bottom: BorderSide(color: Colors.black.withOpacity(0.15)), + ), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.08), + offset: const Offset(0, 1), + blurRadius: 80, ), - Row( - children: [ - if (room.canonicalAlias.isNotEmpty) - IconButton( - tooltip: L10n.of(context)!.share, - icon: Icon(Icons.adaptive.share_outlined), - onPressed: () => FluffyShare.share( - AppConfig.inviteLinkPrefix + room.canonicalAlias, - context, - ), - ), - ChatSettingsPopupMenu(room, false) - ], + BoxShadow( + color: Colors.black.withOpacity(0.15), + offset: const Offset(0, 1), + blurRadius: 3, + spreadRadius: 0.5, ), ], ), - backgroundColor: Theme.of(context).appBarTheme.backgroundColor, - flexibleSpace: FlexibleSpaceBar( - background: ContentBanner( - mxContent: room.avatar, - onEdit: room.canSendEvent('m.room.avatar') - ? controller.setAvatarAction - : null, - defaultIcon: Icons.group_outlined, - ), - ), ), - ], - body: MaxWidthBody( - child: ListView.builder( - itemCount: controller.members!.length + - 1 + - (canRequestMoreMembers ? 1 : 0), - itemBuilder: (BuildContext context, int i) => i == 0 - ? Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - ListTile( - onTap: room.canSendEvent(EventTypes.RoomTopic) - ? controller.setTopicAction - : null, - trailing: room.canSendEvent(EventTypes.RoomTopic) - ? Icon( - Icons.edit_outlined, - color: Theme.of(context) - .colorScheme - .onBackground, - ) - : null, - title: Text( - L10n.of(context)!.groupDescription, - style: TextStyle( - color: Theme.of(context).colorScheme.secondary, - fontWeight: FontWeight.bold, - ), - ), - ), - if (room.topic.isNotEmpty) - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16.0, - ), - child: LinkText( - text: room.topic.isEmpty - ? L10n.of(context)!.addGroupDescription - : room.topic, - linkStyle: - const TextStyle(color: Colors.blueAccent), - textStyle: TextStyle( - fontSize: 14, - color: Theme.of(context) - .textTheme - .bodyMedium! - .color, - decorationColor: Theme.of(context) - .textTheme - .bodyMedium! - .color, - ), - onLinkTap: (url) => - UrlLauncher(context, url.toString()) - .launchUrl(), - ), - ), - const SizedBox(height: 8), - const Divider(height: 1), - ListTile( - title: Text( - L10n.of(context)!.settings, - style: TextStyle( - color: Theme.of(context).colorScheme.secondary, - fontWeight: FontWeight.bold, - ), - ), - trailing: Icon( - controller.displaySettings - ? Icons.keyboard_arrow_down_outlined - : Icons.keyboard_arrow_right_outlined, - ), - onTap: controller.toggleDisplaySettings, - ), - if (controller.displaySettings) ...[ - if (room.canSendEvent('m.room.name')) - ListTile( - leading: CircleAvatar( - backgroundColor: - Theme.of(context).scaffoldBackgroundColor, - foregroundColor: iconColor, - child: const Icon( - Icons.people_outline_outlined, - ), - ), - title: Text( - L10n.of(context)!.changeTheNameOfTheGroup, - ), - subtitle: Text( - room.getLocalizedDisplayname( - MatrixLocals(L10n.of(context)!), - ), - ), - onTap: controller.setDisplaynameAction, - ), - if (room.joinRules == JoinRules.public) - ListTile( - leading: CircleAvatar( - backgroundColor: - Theme.of(context).scaffoldBackgroundColor, - foregroundColor: iconColor, - child: const Icon(Icons.link_outlined), - ), - onTap: controller.editAliases, - title: Text(L10n.of(context)!.editRoomAliases), - subtitle: Text( - (room.canonicalAlias.isNotEmpty) - ? room.canonicalAlias - : L10n.of(context)!.none, - ), - ), - ListTile( - leading: CircleAvatar( - backgroundColor: - Theme.of(context).scaffoldBackgroundColor, - foregroundColor: iconColor, - child: const Icon( - Icons.insert_emoticon_outlined, - ), - ), - title: Text(L10n.of(context)!.emoteSettings), - subtitle: Text(L10n.of(context)!.setCustomEmotes), - onTap: controller.goToEmoteSettings, - ), - PopupMenuButton( - onSelected: controller.setJoinRulesAction, - itemBuilder: (BuildContext context) => - >[ - if (room.canChangeJoinRules) - PopupMenuItem( - value: JoinRules.public, - child: Text( - JoinRules.public.getLocalizedString( - MatrixLocals(L10n.of(context)!), - ), - ), - ), - if (room.canChangeJoinRules) - PopupMenuItem( - value: JoinRules.invite, - child: Text( - JoinRules.invite.getLocalizedString( - MatrixLocals(L10n.of(context)!), - ), - ), - ), - ], - child: ListTile( - leading: CircleAvatar( - backgroundColor: - Theme.of(context).scaffoldBackgroundColor, - foregroundColor: iconColor, - child: const Icon(Icons.shield_outlined), - ), - title: Text( - L10n.of(context)!.whoIsAllowedToJoinThisGroup, - ), - subtitle: Text( - room.joinRules?.getLocalizedString( - MatrixLocals(L10n.of(context)!), - ) ?? - L10n.of(context)!.none, - ), - ), - ), - PopupMenuButton( - onSelected: controller.setHistoryVisibilityAction, - itemBuilder: (BuildContext context) => - >[ - if (room.canChangeHistoryVisibility) - PopupMenuItem( - value: HistoryVisibility.invited, - child: Text( - HistoryVisibility.invited - .getLocalizedString( - MatrixLocals(L10n.of(context)!), - ), - ), - ), - if (room.canChangeHistoryVisibility) - PopupMenuItem( - value: HistoryVisibility.joined, - child: Text( - HistoryVisibility.joined - .getLocalizedString( - MatrixLocals(L10n.of(context)!), - ), - ), - ), - if (room.canChangeHistoryVisibility) - PopupMenuItem( - value: HistoryVisibility.shared, - child: Text( - HistoryVisibility.shared - .getLocalizedString( - MatrixLocals(L10n.of(context)!), - ), - ), - ), - if (room.canChangeHistoryVisibility) - PopupMenuItem( - value: HistoryVisibility.worldReadable, - child: Text( - HistoryVisibility.worldReadable - .getLocalizedString( - MatrixLocals(L10n.of(context)!), - ), - ), - ), - ], - child: ListTile( - leading: CircleAvatar( - backgroundColor: - Theme.of(context).scaffoldBackgroundColor, - foregroundColor: iconColor, - child: const Icon(Icons.visibility_outlined), - ), - title: Text( - L10n.of(context)!.visibilityOfTheChatHistory, - ), - subtitle: Text( - room.historyVisibility?.getLocalizedString( - MatrixLocals(L10n.of(context)!), - ) ?? - L10n.of(context)!.none, - ), - ), - ), - if (room.joinRules == JoinRules.public) - PopupMenuButton( - onSelected: controller.setGuestAccessAction, - itemBuilder: (BuildContext context) => - >[ - if (room.canChangeGuestAccess) - PopupMenuItem( - value: GuestAccess.canJoin, - child: Text( - GuestAccess.canJoin.getLocalizedString( - MatrixLocals( - L10n.of(context)!, - ), - ), - ), - ), - if (room.canChangeGuestAccess) - PopupMenuItem( - value: GuestAccess.forbidden, - child: Text( - GuestAccess.forbidden - .getLocalizedString( - MatrixLocals( - L10n.of(context)!, - ), - ), - ), - ), - ], - child: ListTile( - leading: CircleAvatar( - backgroundColor: Theme.of(context) - .scaffoldBackgroundColor, - foregroundColor: iconColor, - child: const Icon( - Icons.person_add_alt_1_outlined, - ), - ), - title: Text( - L10n.of(context)!.areGuestsAllowedToJoin, - ), - subtitle: Text( - room.guestAccess.getLocalizedString( - MatrixLocals(L10n.of(context)!), - ), - ), - ), - ), - ListTile( - title: - Text(L10n.of(context)!.editChatPermissions), - subtitle: Text( - L10n.of(context)!.whoCanPerformWhichAction, - ), - leading: CircleAvatar( - backgroundColor: - Theme.of(context).scaffoldBackgroundColor, - foregroundColor: iconColor, - child: const Icon( - Icons.edit_attributes_outlined, - ), - ), - onTap: () => context.goChild('permissions'), - ), - ], - const Divider(height: 1), - ListTile( - title: Text( - actualMembersCount > 1 - ? L10n.of(context)!.countMembers( - actualMembersCount.toString(), - ) - : L10n.of(context)!.emptyChat, - style: TextStyle( - color: Theme.of(context).colorScheme.secondary, - fontWeight: FontWeight.bold, - ), - ), - ), - room.canInvite - ? ListTile( - title: Text(L10n.of(context)!.inviteContact), - leading: CircleAvatar( - backgroundColor: - Theme.of(context).primaryColor, - foregroundColor: Colors.white, - radius: AvatarStyle.defaultSize / 2, - child: const Icon(Icons.add_outlined), - ), - onTap: () => controller.openDialogInvite(), - ) - : Container(), - ], - ) - : i < controller.members!.length + 1 - ? ParticipantListItem(controller.members![i - 1]) - : ListTile( - title: Text( - L10n.of(context)!.loadCountMoreParticipants( - (actualMembersCount - - controller.members!.length) - .toString(), - ), - ), - leading: CircleAvatar( - backgroundColor: - Theme.of(context).scaffoldBackgroundColor, - child: const Icon( - Icons.refresh, - color: Colors.grey, - ), - ), - onTap: controller.requestMoreMembersAction, + ), + backgroundColor: Theme.of(context).colorScheme.background, + title: Align( + alignment: Alignment.centerLeft, + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + TwakeIconButton( + icon: Icons.arrow_back, + onTap: () => context.pop(), + tooltip: L10n.of(context)!.back, + paddingAll: 8.0, + margin: const EdgeInsets.symmetric(horizontal: 8.0), + ), + Expanded( + child: Text( + L10n.of(context)!.chatInfo, + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.titleLarge?.copyWith( + color: Theme.of(context).colorScheme.onSurface, ), + ), + ), + const SizedBox(width: 56) + ], ), ), ), + body: SizedBox( + width: double.infinity, + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + _groupAvatarBuilder(context: context, room: controller.room!), + _groupNameAndInfoBuilder( + context: context, + room: controller.room!, + ), + ActionsHeaderBuilder( + actions: controller.chatDetailsActionsButton(), + width: ChatDetailViewStyle.actionsHeaderWidth(context), + buttonColor: !controller.isMobileAndTablet + ? LinagoraRefColors.material().primary[100] + : null, + onTap: (actions) => controller.onTapActionsButton( + actions, + ), + ), + Expanded( + child: ClipRRect( + borderRadius: BorderRadius.all( + Radius.circular( + ChatDetailViewStyle.chatDetailsPageViewWebBorderRadius, + ), + ), + child: Container( + width: ChatDetailViewStyle.chatDetailsPageViewWebWidth, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.background, + ), + child: ValueListenableBuilder( + valueListenable: controller.currentPage, + builder: (context, currentPage, _) { + return ChatDetailsPageViewBuilder( + pages: controller.chatDetailsPages(), + currentIndexPageSelected: currentPage, + pageController: controller.pageController, + onTapGoToPage: (page) { + controller.currentPage.value = page; + controller.pageController.jumpToPage(page); + }, + ); + }, + ), + ), + ), + ), + ], + ), + ), ); }, ); } + + Padding _groupAvatarBuilder({ + required BuildContext context, + required Room room, + }) { + return Padding( + padding: ChatDetailViewStyle.groupAvatarPadding, + child: InkWell( + borderRadius: BorderRadius.circular( + ChatDetailViewStyle.groupAvatarBorderRadius, + ), + onTap: room.canSendEvent('m.room.avatar') + ? controller.setAvatarAction + : null, + child: Hero( + tag: 'content_banner', + child: Avatar( + mxContent: room.avatar, + name: room.getLocalizedDisplayname( + MatrixLocals(L10n.of(context)!), + ), + size: ChatDetailViewStyle.groupAvatarSize, + ), + ), + ), + ); + } + + Padding _groupNameAndInfoBuilder({ + required BuildContext context, + required Room room, + }) { + final actualMembersCount = room.summary.actualMembersCount; + return Padding( + padding: ChatDetailViewStyle.groupNameAndInfoPadding, + child: Column( + children: [ + Text( + room.getLocalizedDisplayname( + MatrixLocals(L10n.of(context)!), + ), + style: Theme.of(context).textTheme.titleLarge?.copyWith( + color: Theme.of(context).colorScheme.onSurface, + ), + maxLines: 2, + textAlign: TextAlign.center, + overflow: TextOverflow.ellipsis, + ), + Text( + actualMembersCount > 1 + ? L10n.of(context)!.countMembers( + actualMembersCount.toString(), + ) + : L10n.of(context)!.emptyChat, + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: LinagoraRefColors.material().tertiary[20], + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ], + ), + ); + } } diff --git a/lib/pages/chat_details/chat_details_view_style.dart b/lib/pages/chat_details/chat_details_view_style.dart new file mode 100644 index 0000000000..71c84fc89b --- /dev/null +++ b/lib/pages/chat_details/chat_details_view_style.dart @@ -0,0 +1,26 @@ +import 'package:fluffychat/di/global/get_it_initializer.dart'; +import 'package:fluffychat/utils/responsive/responsive_utils.dart'; +import 'package:flutter/cupertino.dart'; + +class ChatDetailViewStyle { + static ResponsiveUtils responsive = getIt.get(); + + static double toolbarHeight = 56; + + static double actionsHeaderWidth(BuildContext context) => + responsive.isMobile(context) || responsive.isTablet(context) ? 98 : 148; + + static double chatDetailsPageViewWebBorderRadius = 16.0; + + static double chatDetailsPageViewWebWidth = 640.0; + + static EdgeInsetsDirectional groupAvatarPadding = + const EdgeInsetsDirectional.symmetric(vertical: 16.0); + + static EdgeInsetsDirectional groupNameAndInfoPadding = + const EdgeInsetsDirectional.symmetric(horizontal: 16.0); + + static double groupAvatarSize = 96.0; + + static double groupAvatarBorderRadius = 48; +} diff --git a/lib/pages/new_private_chat/widget/expansion_contact_list_tile.dart b/lib/pages/new_private_chat/widget/expansion_contact_list_tile.dart index 85e21c9747..8c7371653a 100644 --- a/lib/pages/new_private_chat/widget/expansion_contact_list_tile.dart +++ b/lib/pages/new_private_chat/widget/expansion_contact_list_tile.dart @@ -43,6 +43,7 @@ class ExpansionContactListTile extends StatelessWidget { ), Expanded( child: Column( + mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ IntrinsicWidth( diff --git a/lib/utils/dialog/warning_dialog.dart b/lib/utils/dialog/warning_dialog.dart index c53ca36e43..8c3802c9c1 100644 --- a/lib/utils/dialog/warning_dialog.dart +++ b/lib/utils/dialog/warning_dialog.dart @@ -41,15 +41,15 @@ class WarningDialog { DialogAction( text: cancelText ?? L10n.of(context)!.cancel, onPressed: () { - onCancel?.call(); Navigator.pop(context); + onCancel?.call(); }, ), DialogAction( text: acceptText ?? L10n.of(context)!.ok, onPressed: () { - onAccept?.call(); Navigator.pop(context); + onAccept?.call(); }, ), ],