From 293ad5c9bf8ef9b92581c19075566d170f90c4a8 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Mon, 21 Sep 2020 17:17:13 +0200 Subject: [PATCH 1/3] fix last active on new message --- lib/src/channel_header.dart | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/src/channel_header.dart b/lib/src/channel_header.dart index 5a7a146c..fbac80f1 100644 --- a/lib/src/channel_header.dart +++ b/lib/src/channel_header.dart @@ -120,15 +120,17 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget { stream: channel.lastMessageAtStream, initialData: channel.lastMessageAt, builder: (context, snapshot) { - return (snapshot.data != null) - ? Text( - 'Active ${Jiffy(snapshot.data.toLocal()).fromNow()}', - style: StreamChatTheme.of(context) - .channelTheme - .channelHeaderTheme - .lastMessageAt, - ) - : SizedBox(); + if (snapshot.data == null) { + return SizedBox(); + } + final jiffyDate = Jiffy(snapshot.data?.toLocal()); + return Text( + 'Active ${jiffyDate.isBefore(Jiffy()) ? jiffyDate.fromNow() : 'now'}', + style: StreamChatTheme.of(context) + .channelTheme + .channelHeaderTheme + .lastMessageAt, + ); }, ); } From e72da27051ea55f75398e6425b3d126a9ed5ac65 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Mon, 21 Sep 2020 17:17:30 +0200 Subject: [PATCH 2/3] ellipse name if too long --- lib/src/channel_name.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/src/channel_name.dart b/lib/src/channel_name.dart index ef7599b7..96a6a14a 100644 --- a/lib/src/channel_name.dart +++ b/lib/src/channel_name.dart @@ -42,6 +42,7 @@ class ChannelName extends StatelessWidget { return Text( title, style: textStyle, + overflow: TextOverflow.ellipsis, ); }, ); From 2bc72bd96e854d0a435cef653c737494d31ebe73 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Wed, 23 Sep 2020 16:42:50 +0200 Subject: [PATCH 3/3] add CreateChannelPage to example --- example/lib/main.dart | 194 +++++++++++++++++++++++++++++++++++++ example/pubspec.yaml | 2 +- lib/src/channels_bloc.dart | 15 ++- pubspec.yaml | 2 +- 4 files changed, 206 insertions(+), 7 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index f81c61be..ca204fe8 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -108,6 +108,14 @@ class ChannelListPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( + floatingActionButton: FloatingActionButton( + child: Icon(Icons.add), + onPressed: () { + Navigator.of(context).push(MaterialPageRoute(builder: (context) { + return CreateChannelPage(); + })); + }, + ), body: ChannelsBloc( child: ChannelListView( filter: { @@ -198,3 +206,189 @@ class ThreadPage extends StatelessWidget { ); } } + +class CreateChannelPage extends StatefulWidget { + @override + _CreateChannelPageState createState() => _CreateChannelPageState(); +} + +class _CreateChannelPageState extends State { + final ScrollController _scrollController = ScrollController(); + Client client; + List users = []; + List selectedUsers = []; + int offset = 0; + bool loading = false; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + elevation: 0, + backgroundColor: Colors.transparent, + title: Text( + 'Create a channel', + style: Theme.of(context).textTheme.headline6, + ), + ), + floatingActionButton: + selectedUsers.isNotEmpty ? _buildFAB(context) : SizedBox(), + body: _buildListView(), + ); + } + + ListView _buildListView() { + return ListView.builder( + controller: _scrollController, + itemBuilder: _itemBuilder, + itemCount: users.length, + ); + } + + Widget _itemBuilder(context, i) { + final user = users[i]; + return ListTile( + onLongPress: () { + _selectUser(user); + }, + selected: selectedUsers.contains(user), + onTap: () { + if (selectedUsers.isNotEmpty) { + return _selectUser(user); + } + _createChannel(context, [user]); + }, + leading: UserAvatar( + user: user, + ), + title: Text(user.name), + ); + } + + Widget _buildFAB(BuildContext context) { + return FloatingActionButton( + child: Icon(Icons.done), + onPressed: () async { + String name; + if (selectedUsers.length > 1) { + name = await _showEnterNameDialog(context); + if (name?.isNotEmpty != true) { + return; + } + } + + _createChannel(context, selectedUsers, name); + }, + ); + } + + Future _showEnterNameDialog(BuildContext context) { + final controller = TextEditingController(); + return showDialog( + context: context, + builder: (context) => SimpleDialog( + contentPadding: const EdgeInsets.all(16), + title: Text('Enter a name for the channel'), + children: [ + TextField( + controller: controller, + decoration: InputDecoration( + border: OutlineInputBorder(), + ), + ), + ButtonBar( + children: [ + FlatButton( + onPressed: () => Navigator.pop(context), + child: Text('Cancel'), + ), + FlatButton( + onPressed: () => Navigator.pop(context, controller.text), + child: Text('Ok'), + ), + ], + ), + ], + ), + ); + } + + Future _createChannel( + BuildContext context, + List users, [ + String name, + ]) async { + final channel = client.channel('messaging', extraData: { + 'members': [ + client.state.user.id, + ...users.map((e) => e.id), + ], + if (name != null) 'name': name, + }); + await channel.watch(); + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (context) { + return StreamChannel( + child: ChannelPage(), + channel: channel, + ); + }, + ), + ); + } + + void _selectUser(User user) { + if (!selectedUsers.contains(user)) { + setState(() { + selectedUsers.add(user); + }); + } else { + setState(() { + selectedUsers.remove(user); + }); + } + } + + @override + void initState() { + super.initState(); + + client = StreamChat.of(context).client; + + _scrollController.addListener(() async { + if (!loading && + _scrollController.offset >= + _scrollController.position.maxScrollExtent - 100) { + offset += 25; + await _queryUsers(); + } + }); + + _queryUsers(); + } + + Future _queryUsers() { + loading = true; + return client.queryUsers( + pagination: PaginationParams( + limit: 25, + offset: offset, + ), + sort: [ + SortOption( + 'name', + direction: SortOption.ASC, + ), + ], + ).then((value) { + setState(() { + users = [ + ...users, + ...value.users, + ]; + }); + }).whenComplete(() => loading = false); + } +} diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 23beab12..9e62c5e5 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -1,7 +1,7 @@ name: example description: A new Flutter project. -version: 1.0.3+4 +version: 1.0.6+7 environment: sdk: ">=2.2.2 <3.0.0" diff --git a/lib/src/channels_bloc.dart b/lib/src/channels_bloc.dart index cc2d4f93..df6f641b 100644 --- a/lib/src/channels_bloc.dart +++ b/lib/src/channels_bloc.dart @@ -124,18 +124,23 @@ class ChannelsBlocState extends State _subscriptions.add(client.on(EventType.messageNew).listen((e) { final newChannels = List.from(channels ?? []); final index = newChannels.indexWhere((c) => c.cid == e.cid); - if (index > 0) { - final channel = newChannels.removeAt(index); - newChannels.insert(0, channel); - _channelsController.add(newChannels); + if (index > -1) { + if (index > 0) { + final channel = newChannels.removeAt(index); + newChannels.insert(0, channel); + } } else { final hiddenIndex = _hiddenChannels.indexWhere((c) => c.cid == e.cid); if (hiddenIndex > -1) { newChannels.insert(0, _hiddenChannels[hiddenIndex]); _hiddenChannels.removeAt(hiddenIndex); - _channelsController.add(newChannels); + } else { + if (client.state.channels[e.cid] != null) { + newChannels.insert(0, client.state.channels[e.cid]); + } } } + _channelsController.add(newChannels); })); } diff --git a/pubspec.yaml b/pubspec.yaml index 34a48b8f..66a60736 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -23,7 +23,7 @@ dependencies: file_picker: ^1.12.0 image_picker: ^0.6.7+2 flutter_keyboard_visibility: ^3.2.1 - stream_chat: ^0.2.5 + stream_chat: ^0.2.5+1 mime: ^0.9.6+3 visibility_detector: ^0.1.5 http_parser: ^3.1.4