From ca6bd62084fd5861089e9ff5db90abad099ba0d4 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Thu, 31 Dec 2020 16:32:45 +0530 Subject: [PATCH] fix: Added confirmation dialogs and fixed alignment --- example/lib/main.dart | 26 --- lib/src/channel_actions_modal.dart | 197 ---------------- lib/src/channel_bottom_sheet.dart | 360 +++++++++++++++-------------- lib/src/channel_list_view.dart | 22 +- lib/stream_chat_flutter.dart | 2 +- 5 files changed, 210 insertions(+), 397 deletions(-) delete mode 100644 lib/src/channel_actions_modal.dart diff --git a/example/lib/main.dart b/example/lib/main.dart index c283793a..803df8e6 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -373,9 +373,6 @@ class _ChannelListPageState extends State { limit: 20, ), channelWidget: ChannelPage(), - onChannelLongPress: (channel) { - _showChannelDetailsModal(channel); - }, ), ), ), @@ -386,29 +383,6 @@ class _ChannelListPageState extends State { ), ); } - - void _showChannelDetailsModal(Channel channel) { - showModalBottomSheet( - context: context, - builder: (context) { - return StreamChannel( - child: ChannelActionsModal( - onViewInfoTap: () { - // TODO: Go to group info screen when PR is merged - }, - ), - channel: channel); - }, - isScrollControlled: true, - clipBehavior: Clip.antiAlias, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(16.0), - topRight: Radius.circular(16.0), - ), - ), - ); - } } class ChannelPageArgs { diff --git a/lib/src/channel_actions_modal.dart b/lib/src/channel_actions_modal.dart deleted file mode 100644 index 424f5e38..00000000 --- a/lib/src/channel_actions_modal.dart +++ /dev/null @@ -1,197 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../stream_chat_flutter.dart'; -import 'channel_info.dart'; -import 'option_list_tile.dart'; - -class ChannelActionsModal extends StatefulWidget { - VoidCallback onViewInfoTap; - - ChannelActionsModal({this.onViewInfoTap}); - - @override - _ChannelActionsModalState createState() => _ChannelActionsModalState(); -} - -class _ChannelActionsModalState extends State { - @override - Widget build(BuildContext context) { - var channel = StreamChannel.of(context).channel; - - return Material( - clipBehavior: Clip.antiAlias, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(16.0), - topRight: Radius.circular(16.0), - ), - ), - child: FutureBuilder( - future: StreamChannel.of(context).channel.queryMembers( - filter: {}, - ), - builder: (context, snapshot) { - if (!snapshot.hasData) { - return Container( - height: 100.0, - alignment: Alignment.center, - child: Padding( - padding: const EdgeInsets.all(16.0), - child: CircularProgressIndicator(), - ), - ); - } - - var userAsMember = snapshot.data.members - .firstWhere((e) => e.user.id == StreamChat.of(context).user.id); - var isOwner = userAsMember.role == 'owner'; - - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - SizedBox( - height: 24.0, - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: ChannelName( - textStyle: TextStyle( - fontSize: 16.0, - color: Color(0xff000000), - fontWeight: FontWeight.bold, - ), - ), - ), - SizedBox( - height: 5.0, - ), - ChannelInfo( - showTypingIndicator: false, - channel: StreamChannel.of(context).channel, - textStyle: - StreamChatTheme.of(context).channelPreviewTheme.subtitle, - ), - SizedBox( - height: 17.0, - ), - if (channel.isDistinct && channel.memberCount == 2) - Column( - children: [ - UserAvatar( - user: snapshot.data.members - .firstWhere((e) => e.user.id != userAsMember.user.id) - .user, - constraints: BoxConstraints( - maxHeight: 64.0, - maxWidth: 64.0, - ), - borderRadius: BorderRadius.circular(32.0), - ), - SizedBox( - height: 6.0, - ), - Text( - snapshot.data.members - .firstWhere((e) => e.user.id != userAsMember.user.id) - .user - .name, - style: TextStyle( - fontSize: 12.0, - fontWeight: FontWeight.w600, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ], - ), - if (!(channel.isDistinct && channel.memberCount == 2)) - Container( - height: 94.0, - child: ListView.builder( - scrollDirection: Axis.horizontal, - itemCount: snapshot.data.members.length, - itemBuilder: (context, index) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: Column( - children: [ - UserAvatar( - user: snapshot.data.members[index].user, - constraints: BoxConstraints( - maxHeight: 64.0, - maxWidth: 64.0, - ), - borderRadius: BorderRadius.circular(32.0), - ), - SizedBox( - height: 6.0, - ), - Text( - snapshot.data.members[index].user.name, - style: TextStyle( - fontSize: 12.0, - fontWeight: FontWeight.w600, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ], - ), - ); - }, - ), - ), - SizedBox( - height: 24, - ), - OptionListTile( - leading: StreamSvgIcon.user( - color: Color(0xff7a7a7a), - ), - title: 'View Info', - onTap: widget.onViewInfoTap, - ), - if (!channel.isDistinct) - OptionListTile( - leading: StreamSvgIcon.userRemove( - color: Color(0xff7a7a7a), - ), - title: 'Leave Group', - onTap: () async { - var channel = StreamChannel.of(context).channel; - - await channel - .removeMembers([StreamChat.of(context).user.id]); - Navigator.pop(context); - }, - ), - if (isOwner) - OptionListTile( - leading: StreamSvgIcon.delete( - color: Colors.red, - ), - title: 'Delete Conversation', - titleColor: Colors.red, - onTap: () async { - var channel = StreamChannel.of(context).channel; - - await channel.delete(); - Navigator.pop(context); - }, - ), - OptionListTile( - leading: StreamSvgIcon.close_small( - color: Color(0xff7a7a7a), - ), - title: 'Cancel', - onTap: () { - Navigator.pop(context); - }, - ), - ], - ); - }, - ), - ); - } -} diff --git a/lib/src/channel_bottom_sheet.dart b/lib/src/channel_bottom_sheet.dart index 2d16bf6d..695f34c6 100644 --- a/lib/src/channel_bottom_sheet.dart +++ b/lib/src/channel_bottom_sheet.dart @@ -1,193 +1,209 @@ import 'package:flutter/material.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_flutter/src/stream_svg_icon.dart'; -import 'package:stream_chat_flutter/src/utils.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; +import '../stream_chat_flutter.dart'; import 'channel_info.dart'; -import 'channel_name.dart'; -import 'stream_chat.dart'; -import 'stream_chat_theme.dart'; -import 'user_avatar.dart'; +import 'option_list_tile.dart'; -class ChannelBottomSheet extends StatelessWidget { - const ChannelBottomSheet({ - Key key, - }) : super(key: key); +class ChannelBottomSheet extends StatefulWidget { + VoidCallback onViewInfoTap; + + ChannelBottomSheet({this.onViewInfoTap}); + @override + _ChannelBottomSheetState createState() => _ChannelBottomSheetState(); +} + +class _ChannelBottomSheetState extends State { @override Widget build(BuildContext context) { - final channel = StreamChannel.of(context).channel; - return SafeArea( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Padding( - padding: const EdgeInsets.symmetric( - vertical: 2.0, - ), - child: Center( - child: StreamChannel( - showLoading: false, - channel: channel, - child: ChannelName( - textStyle: - StreamChatTheme.of(context).channelPreviewTheme.title, - ), - ), - ), - ), - Padding( - padding: const EdgeInsets.symmetric( - vertical: 2.0, - ), - child: Center( - child: ChannelInfo( - channel: channel, - textStyle: - StreamChatTheme.of(context).channelPreviewTheme.subtitle, - ), - ), - ), - Padding( - padding: const EdgeInsets.symmetric( - vertical: 15.0, - ), - child: Center( - child: StreamBuilder>( - stream: channel.state.membersStream.map((event) => event - .where((m) => m.userId != StreamChat.of(context).user.id) - .toList()), - initialData: channel.state.members - .where((m) => m.userId != StreamChat.of(context).user.id) - .toList(), - builder: _buildMembers, - ), - ), - ), - Divider(), - if (channel.isGroup && !channel.isDistinct) - ListTile( - leading: StreamSvgIcon.userRemove( - size: 24, - color: Color(0xff7A7A7A), - ), - title: Text( - 'Leave Group', - style: TextStyle(fontWeight: FontWeight.bold), - ), - onTap: () async { - final confirm = await showConfirmationDialog( - context, - title: 'Leave Group', - okText: 'LEAVE', - question: 'Are you sure you want to leave this group?', - cancelText: 'CANCEL', - icon: StreamSvgIcon.userRemove( - color: Colors.red, - ), - ); - if (confirm == true) { - await channel - .removeMembers([StreamChat.of(context).user.id]); - Navigator.pop(context); - } - }, - ), - if ([ - 'admin', - 'owner', - ].contains(channel.state.members - .firstWhere((m) => m.userId == channel.client.state.user.id, - orElse: () => null) - ?.role)) - ListTile( - leading: StreamSvgIcon.delete( - color: Color(0xFFFF3742), - size: 24, - ), - title: Text( - 'Delete chat', - style: TextStyle( - color: Color(0xFFFF3742), - ), - ), - onTap: () async { - final res = await showConfirmationDialog( - context, - title: 'Delete Conversation', - okText: 'DELETE', - question: - 'Are you sure you want to delete this conversation?', - cancelText: 'CANCEL', - icon: StreamSvgIcon.delete( - color: Colors.red, - ), - ); - var channel = StreamChannel.of(context).channel; - if (res == true) { - await channel.delete().then((value) { - Navigator.pop(context); - }); - } - }, - ), - ], + var channel = StreamChannel.of(context).channel; + + var members = channel.state.members; + + var userAsMember = + members.firstWhere((e) => e.user.id == StreamChat.of(context).user.id); + var isOwner = userAsMember.role == 'owner'; + + return Material( + clipBehavior: Clip.antiAlias, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(16.0), + topRight: Radius.circular(16.0), ), ), - ); - } - - Widget _buildMembers( - BuildContext context, - AsyncSnapshot> snapshot, - ) { - if (snapshot.data.isEmpty) { - return SizedBox(); - } - - return Container( - height: 83, - child: ListView( - padding: EdgeInsets.only( - left: (MediaQuery.of(context).size.width / 2) - 48, - ), - scrollDirection: Axis.horizontal, - children: snapshot.data.map((m) { - return Padding( - padding: const EdgeInsets.symmetric( - horizontal: 8.0, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + height: 24.0, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: ChannelName( + textStyle: TextStyle( + fontSize: 16.0, + color: Color(0xff000000), + fontWeight: FontWeight.bold, + ), ), - child: Column( - children: [ + ), + SizedBox( + height: 5.0, + ), + ChannelInfo( + showTypingIndicator: false, + channel: StreamChannel.of(context).channel, + textStyle: StreamChatTheme.of(context).channelPreviewTheme.subtitle, + ), + SizedBox( + height: 17.0, + ), + if (channel.isDistinct && channel.memberCount == 2) + Column( + children: [ UserAvatar( - showOnlineStatus: true, - user: m.user, - borderRadius: BorderRadius.circular(32), - constraints: BoxConstraints.tight( - Size.square(64), + user: members + .firstWhere((e) => e.user.id != userAsMember.user.id) + .user, + constraints: BoxConstraints( + maxHeight: 64.0, + maxWidth: 64.0, ), + borderRadius: BorderRadius.circular(32.0), ), - Padding( - padding: const EdgeInsets.only(top: 5.0), - child: Text( - m.user.name?.split(' ')?.elementAt(0), - style: StreamChatTheme.of(context) - .channelPreviewTheme - .title - .copyWith( - fontSize: 12, - ), + SizedBox( + height: 6.0, + ), + Text( + members + .firstWhere((e) => e.user.id != userAsMember.user.id) + .user + .name, + style: TextStyle( + fontSize: 12.0, + fontWeight: FontWeight.w600, ), + maxLines: 1, + overflow: TextOverflow.ellipsis, ), ], ), - ); - }).toList(), + if (!(channel.isDistinct && channel.memberCount == 2)) + Container( + height: 94.0, + child: ListView.builder( + scrollDirection: Axis.horizontal, + itemCount: members.length, + itemBuilder: (context, index) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: Column( + children: [ + UserAvatar( + user: members[index].user, + constraints: BoxConstraints( + maxHeight: 64.0, + maxWidth: 64.0, + ), + borderRadius: BorderRadius.circular(32.0), + ), + SizedBox( + height: 6.0, + ), + Text( + members[index].user.name, + style: TextStyle( + fontSize: 12.0, + fontWeight: FontWeight.w600, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ], + ), + ); + }, + ), + ), + SizedBox( + height: 17.0, + ), + OptionListTile( + leading: StreamSvgIcon.user( + color: Color(0xff7a7a7a), + ), + title: 'View Info', + onTap: widget.onViewInfoTap, + ), + if (!channel.isDistinct) + OptionListTile( + leading: StreamSvgIcon.userRemove( + color: Color(0xff7a7a7a), + ), + title: 'Leave Group', + onTap: () async { + _showLeaveDialog(); + }, + ), + if (isOwner) + OptionListTile( + leading: StreamSvgIcon.delete( + color: Colors.red, + ), + title: 'Delete Conversation', + titleColor: Colors.red, + onTap: () async { + _showDeleteDialog(); + }, + ), + OptionListTile( + leading: StreamSvgIcon.close_small( + color: Color(0xff7a7a7a), + ), + title: 'Cancel', + onTap: () { + Navigator.pop(context); + }, + ), + ], ), ); } + + void _showDeleteDialog() async { + final res = await showConfirmationDialog( + context, + title: 'Delete Conversation', + okText: 'DELETE', + question: 'Are you sure you want to delete this conversation?', + cancelText: 'CANCEL', + icon: StreamSvgIcon.delete( + color: Colors.red, + ), + ); + var channel = StreamChannel.of(context).channel; + if (res == true) { + await channel.delete(); + Navigator.pop(context); + } + } + + void _showLeaveDialog() async { + final res = await showConfirmationDialog( + context, + title: 'Leave conversation', + okText: 'LEAVE', + question: 'Are you sure you want to leave this conversation?', + cancelText: 'CANCEL', + icon: StreamSvgIcon.userRemove( + color: Colors.red, + ), + ); + var channel = StreamChannel.of(context).channel; + if (res == true) { + await channel.removeMembers([StreamChat.of(context).user.id]); + Navigator.pop(context); + } + } } diff --git a/lib/src/channel_list_view.dart b/lib/src/channel_list_view.dart index 5efa1e29..b42377cf 100644 --- a/lib/src/channel_list_view.dart +++ b/lib/src/channel_list_view.dart @@ -560,7 +560,27 @@ class _ChannelListViewState extends State context: context, builder: (context) { return StreamChannel( - child: ChannelBottomSheet(), + child: ChannelBottomSheet( + onViewInfoTap: () { + if (channel.memberCount == 2 && + channel.isDistinct) { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => StreamChannel( + channel: channel, + child: ChatInfoScreen( + user: + channel.state.members.first.user, + ), + ), + ), + ); + } + + // TODO: Add group screen + }, + ), channel: channel, ); }, diff --git a/lib/stream_chat_flutter.dart b/lib/stream_chat_flutter.dart index e4d4752a..d2dfc692 100644 --- a/lib/stream_chat_flutter.dart +++ b/lib/stream_chat_flutter.dart @@ -43,4 +43,4 @@ export 'src/message_search_bloc.dart'; export 'src/message_search_item.dart'; export 'src/message_search_list_view.dart'; export 'src/unread_indicator.dart'; -export 'src/channel_actions_modal.dart'; +export 'src/chat_info_screen.dart';