diff --git a/example/lib/main.dart b/example/lib/main.dart index 803df8e6..ba6226c5 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -373,6 +373,10 @@ class _ChannelListPageState extends State { limit: 20, ), channelWidget: ChannelPage(), + onChannelLongPress: (channel) { + if (!channel.isDistinct) + _showChannelDetailsModal(channel); + }, ), ), ), @@ -383,6 +387,29 @@ 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 new file mode 100644 index 00000000..e8891a89 --- /dev/null +++ b/lib/src/channel_actions_modal.dart @@ -0,0 +1,162 @@ +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) { + 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, + ), + 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, + ), + 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( + color: Color(0xff7a7a7a), + ), + title: 'Cancel', + onTap: () { + Navigator.pop(context); + }, + ), + ], + ); + }, + ), + ); + } +} diff --git a/lib/src/option_list_tile.dart b/lib/src/option_list_tile.dart new file mode 100644 index 00000000..c1f48b71 --- /dev/null +++ b/lib/src/option_list_tile.dart @@ -0,0 +1,68 @@ +import 'package:flutter/material.dart'; +import 'package:stream_chat_flutter/src/stream_svg_icon.dart'; + +class OptionListTile extends StatelessWidget { + final String title; + final StreamSvgIcon leading; + final Widget trailing; + final VoidCallback onTap; + final Color titleColor; + + OptionListTile({ + this.title, + this.leading, + this.trailing, + this.onTap, + this.titleColor, + }); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + Container( + color: Color(0xffe6e6e6), + height: 2.0, + ), + Material( + color: Colors.white, + child: Container( + height: 56.0, + child: InkWell( + onTap: onTap, + child: Row( + children: [ + if (leading != null) + Expanded( + child: Center(child: leading), + ), + if (leading == null) + SizedBox( + width: 16.0, + ), + Expanded( + flex: 4, + child: Text( + title, + style: TextStyle( + fontWeight: FontWeight.w600, color: titleColor), + )), + Expanded( + flex: 2, + child: Padding( + padding: const EdgeInsets.only(right: 16.0), + child: Align( + alignment: Alignment.centerRight, + child: trailing ?? Container(), + ), + ), + ), + ], + ), + ), + ), + ), + ], + ); + } +} diff --git a/lib/stream_chat_flutter.dart b/lib/stream_chat_flutter.dart index 1cef26a6..e4d4752a 100644 --- a/lib/stream_chat_flutter.dart +++ b/lib/stream_chat_flutter.dart @@ -43,3 +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';