migrate sample app

This commit is contained in:
Salvatore Giordano
2021-04-20 13:14:07 +02:00
parent bc9e0d0285
commit 4c4473d4b5
8 changed files with 77 additions and 71 deletions
+25 -29
View File
@@ -27,7 +27,7 @@ class Channel {
} }
/// Create a channel client instance from a [ChannelState] object /// Create a channel client instance from a [ChannelState] object
Channel.fromState(this._client, ChannelState channelState) { Channel.fromState(this._client, ChannelState channelState) : _extraData = {} {
_cid = channelState.channel!.cid; _cid = channelState.channel!.cid;
_id = channelState.channel!.id; _id = channelState.channel!.id;
type = channelState.channel!.type; type = channelState.channel!.type;
@@ -45,9 +45,9 @@ class Channel {
String? _id; String? _id;
String? _cid; String? _cid;
Map<String, dynamic>? _extraData; Map<String, dynamic> _extraData;
set extraData(Map<String, dynamic>? extraData) { set extraData(Map<String, dynamic> extraData) {
if (_initializedCompleter.isCompleted) { if (_initializedCompleter.isCompleted) {
throw Exception( throw Exception(
'Once the channel is initialized you should use channel.update ' 'Once the channel is initialized you should use channel.update '
@@ -189,7 +189,7 @@ class Channel {
} }
/// Channel extra data /// Channel extra data
Map<String, dynamic>? get extraData => Map<String, dynamic> get extraData =>
state?._channelState.channel?.extraData ?? _extraData; state?._channelState.channel?.extraData ?? _extraData;
/// Channel extra data as a stream /// Channel extra data as a stream
@@ -1028,7 +1028,7 @@ class Channel {
}) })
..addAll(options); ..addAll(options);
if (_extraData != null) { if (_extraData.isNotEmpty) {
payload['data'] = _extraData; payload['data'] = _extraData;
} }
@@ -1684,8 +1684,7 @@ class ChannelClientState {
...messages, ...messages,
]; ];
newThreads[parentId]! newThreads[parentId]!.sort(_sortByCreatedAt);
.sort(_sortByCreatedAt as int Function(Message, Message)?);
} else { } else {
newThreads[parentId] = messages; newThreads[parentId] = messages;
} }
@@ -1712,7 +1711,7 @@ class ChannelClientState {
.any((newMessage) => newMessage.id == m.id) != .any((newMessage) => newMessage.id == m.id) !=
true) true)
.toList(), .toList(),
]..sort(_sortByCreatedAt as int Function(Message, Message)?); ]..sort(_sortByCreatedAt);
final newWatchers = <User>[ final newWatchers = <User>[
...updatedState.watchers, ...updatedState.watchers,
@@ -1751,17 +1750,8 @@ class ChannelClientState {
); );
} }
int? _sortByCreatedAt(a, b) { int _sortByCreatedAt(Message a, Message b) =>
if (a.createdAt == null) { a.createdAt.compareTo(b.createdAt);
return 1;
}
if (b.createdAt == null) {
return -1;
}
return a.createdAt.compareTo(b.createdAt);
}
/// The channel state related to this client /// The channel state related to this client
ChannelState get _channelState => _channelStateController.value!; ChannelState get _channelState => _channelStateController.value!;
@@ -1799,15 +1789,15 @@ class ChannelClientState {
} }
/// Channel related typing users last value /// Channel related typing users last value
List<User>? get typingEvents => _typingEventsController.value as List<User>?; List<User> get typingEvents => _typingEventsController.value!;
/// Channel related typing users stream /// Channel related typing users stream
Stream<List<User?>> get typingEventsStream => _typingEventsController.stream; Stream<List<User>> get typingEventsStream => _typingEventsController.stream;
final BehaviorSubject<List<User?>> _typingEventsController = final BehaviorSubject<List<User>> _typingEventsController =
BehaviorSubject.seeded([]); BehaviorSubject.seeded([]);
final Channel _channel; final Channel _channel;
final Map<User?, DateTime> _typings = {}; final Map<User, DateTime> _typings = {};
void _listenTypingEvents() { void _listenTypingEvents() {
if (_channelState.channel?.config.typingEvents == false) { if (_channelState.channel?.config.typingEvents == false) {
@@ -1818,9 +1808,12 @@ class ChannelClientState {
..add( ..add(
_channel.on(EventType.typingStart).listen( _channel.on(EventType.typingStart).listen(
(event) { (event) {
if (event.user!.id != _channel.client.state.user!.id) { if (event.user != null) {
_typings[event.user] = DateTime.now(); final user = event.user!;
_typingEventsController.add(_typings.keys.toList()); if (user.id != _channel.client.state.user?.id) {
_typings[user] = DateTime.now();
_typingEventsController.add(_typings.keys.toList());
}
} }
}, },
), ),
@@ -1828,9 +1821,12 @@ class ChannelClientState {
..add( ..add(
_channel.on(EventType.typingStop).listen( _channel.on(EventType.typingStop).listen(
(event) { (event) {
if (event.user!.id != _channel.client.state.user!.id) { if (event.user != null) {
_typings.remove(event.user); final user = event.user!;
_typingEventsController.add(_typings.keys.toList()); if (user.id != _channel.client.state.user?.id) {
_typings.remove(event.user);
_typingEventsController.add(_typings.keys.toList());
}
} }
}, },
), ),
+6 -5
View File
@@ -780,10 +780,11 @@ class StreamChatClient {
PaginationParams paginationParams = const PaginationParams(), PaginationParams paginationParams = const PaginationParams(),
}) async { }) async {
final offlineChannels = (await _chatPersistenceClient?.getChannelStates( final offlineChannels = (await _chatPersistenceClient?.getChannelStates(
filter: filter, filter: filter,
sort: sort, sort: sort,
paginationParams: paginationParams, paginationParams: paginationParams,
))!; )) ??
[];
final updatedData = _mapChannelStateToChannel(offlineChannels); final updatedData = _mapChannelStateToChannel(offlineChannels);
state.channels = updatedData.key; state.channels = updatedData.key;
return updatedData.value; return updatedData.value;
@@ -1193,7 +1194,7 @@ class StreamChatClient {
Channel channel( Channel channel(
String type, { String type, {
String? id, String? id,
Map<String, dynamic>? extraData, Map<String, dynamic> extraData = const {},
}) { }) {
if (id != null && state.channels?.containsKey('$type:$id') == true) { if (id != null && state.channels?.containsKey('$type:$id') == true) {
if (state.channels!['$type:$id'] != null) { if (state.channels!['$type:$id'] != null) {
@@ -516,7 +516,7 @@ void main() {
); );
await channelClient.watch(); await channelClient.watch();
final event = Event(type: EventType.any); final event = const Event(type: EventType.any);
when( when(
() => mockDio.post<String>( () => mockDio.post<String>(
@@ -564,7 +564,7 @@ void main() {
); );
await channelClient.watch(); await channelClient.watch();
final event = Event(type: EventType.typingStart); final event = const Event(type: EventType.typingStart);
when( when(
() => mockDio.post<String>( () => mockDio.post<String>(
@@ -610,7 +610,7 @@ void main() {
); );
await channelClient.watch(); await channelClient.watch();
final event = Event(type: EventType.typingStop); final event = const Event(type: EventType.typingStop);
when( when(
() => mockDio.post<String>( () => mockDio.post<String>(
@@ -901,8 +901,10 @@ void main() {
'presence': true, 'presence': true,
}; };
when(() => mockDio.post<String>('/channels/messaging/query', when(() => mockDio.post<String>(
data: options)).thenAnswer( '/channels/messaging/query',
data: options,
)).thenAnswer(
(_) async => Response( (_) async => Response(
data: r''' data: r'''
{ {
@@ -1198,8 +1200,10 @@ void main() {
final response = await channelClient.query(options: options); final response = await channelClient.query(options: options);
verify(() => mockDio.post<String>('/channels/messaging/query', verify(() => mockDio.post<String>(
data: options)).called(1); '/channels/messaging/query',
data: options,
)).called(1);
expect(channelClient.id, response.channel?.id); expect(channelClient.id, response.channel?.id);
expect(channelClient.cid, response.channel?.cid); expect(channelClient.cid, response.channel?.cid);
}); });
@@ -3,6 +3,7 @@ analyzer:
- lib/**/*.g.dart - lib/**/*.g.dart
- lib/**/*.freezed.dart - lib/**/*.freezed.dart
- example/* - example/*
- test/*
linter: linter:
rules: rules:
- always_use_package_imports - always_use_package_imports
@@ -38,8 +38,8 @@ class StreamExample extends StatelessWidget {
/// If you'd prefer using pre-made UI widgets for your app, please see our other /// If you'd prefer using pre-made UI widgets for your app, please see our other
/// package, `stream_chat_flutter`. /// package, `stream_chat_flutter`.
const StreamExample({ const StreamExample({
Key key, Key? key,
@required this.client, required this.client,
}) : super(key: key); }) : super(key: key);
/// Instance of Stream Client. /// Instance of Stream Client.
@@ -55,7 +55,7 @@ class StreamExample extends StatelessWidget {
home: HomeScreen(), home: HomeScreen(),
builder: (context, child) => StreamChatCore( builder: (context, child) => StreamChatCore(
client: client, client: client,
child: child, child: child!,
), ),
); );
} }
@@ -82,7 +82,7 @@ class HomeScreen extends StatelessWidget {
'type': 'messaging', 'type': 'messaging',
'members': { 'members': {
r'$in': [ r'$in': [
StreamChatCore.of(context).user.id, StreamChatCore.of(context).user!.id,
] ]
} }
}, },
@@ -100,10 +100,13 @@ class HomeScreen extends StatelessWidget {
), ),
); );
}, },
errorBuilder: (BuildContext context, dynamic error) { errorBuilder: (
BuildContext context,
dynamic error,
) {
return Center( return Center(
child: Text( child: Text(
'Oh no, something went wrong. Please check your config.'), 'Oh no, something went wrong. Please check your config. ${error}'),
); );
}, },
listBuilder: ( listBuilder: (
@@ -112,20 +115,20 @@ class HomeScreen extends StatelessWidget {
) => ) =>
LazyLoadScrollView( LazyLoadScrollView(
onEndOfPage: () async { onEndOfPage: () async {
channelListController.paginateData(); channelListController.paginateData!();
}, },
child: ListView.builder( child: ListView.builder(
itemCount: channels.length, itemCount: channels.length,
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
final _item = channels[index]; final _item = channels[index];
return ListTile( return ListTile(
title: Text(_item.name), title: Text(_item.name!),
subtitle: StreamBuilder<Message>( subtitle: StreamBuilder<Message?>(
stream: _item.state.lastMessageStream, stream: _item.state!.lastMessageStream,
initialData: _item.state.lastMessage, initialData: _item.state!.lastMessage,
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.hasData) { if (snapshot.hasData) {
return Text(snapshot.data.text); return Text(snapshot.data!.text!);
} }
return SizedBox(); return SizedBox();
@@ -169,8 +172,8 @@ class MessageScreen extends StatefulWidget {
} }
class _MessageScreenState extends State<MessageScreen> { class _MessageScreenState extends State<MessageScreen> {
TextEditingController _controller; late final TextEditingController _controller;
ScrollController _scrollController; late final ScrollController _scrollController;
final messageListController = MessageListController(); final messageListController = MessageListController();
@override @override
@@ -203,11 +206,11 @@ class _MessageScreenState extends State<MessageScreen> {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: StreamBuilder<List<User>>( title: StreamBuilder<List<User>>(
initialData: channel.state.typingEvents, initialData: channel.state?.typingEvents,
stream: channel.state.typingEventsStream, stream: channel.state?.typingEventsStream,
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.hasData && snapshot.data.isNotEmpty) { if (snapshot.hasData && snapshot.data!.isNotEmpty) {
return Text('${snapshot.data.first.name} is typing...'); return Text('${snapshot.data!.first.name} is typing...');
} }
return SizedBox(); return SizedBox();
}, },
@@ -219,7 +222,7 @@ class _MessageScreenState extends State<MessageScreen> {
Expanded( Expanded(
child: LazyLoadScrollView( child: LazyLoadScrollView(
onEndOfPage: () async { onEndOfPage: () async {
messageListController.paginateData(); messageListController.paginateData!();
}, },
child: MessageListCore( child: MessageListCore(
emptyBuilder: (BuildContext context) { emptyBuilder: (BuildContext context) {
@@ -247,12 +250,12 @@ class _MessageScreenState extends State<MessageScreen> {
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
final item = messages[index]; final item = messages[index];
final client = StreamChatCore.of(context).client; final client = StreamChatCore.of(context).client;
if (item.user.id == client.uid) { if (item.user!.id == client.uid) {
return Align( return Align(
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
child: Padding( child: Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Text(item.text), child: Text(item.text!),
), ),
); );
} else { } else {
@@ -260,7 +263,7 @@ class _MessageScreenState extends State<MessageScreen> {
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Padding( child: Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Text(item.text), child: Text(item.text!),
), ),
); );
} }
@@ -268,7 +271,7 @@ class _MessageScreenState extends State<MessageScreen> {
); );
}, },
errorWidgetBuilder: (BuildContext context, error) { errorWidgetBuilder: (BuildContext context, error) {
print(error?.toString()); print(error.toString());
return Center( return Center(
child: SizedBox( child: SizedBox(
height: 100.0, height: 100.0,
@@ -282,7 +285,7 @@ class _MessageScreenState extends State<MessageScreen> {
), ),
), ),
Padding( Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8),
child: Row( child: Row(
children: [ children: [
Expanded( Expanded(
@@ -308,7 +311,7 @@ class _MessageScreenState extends State<MessageScreen> {
} }
}, },
child: const Padding( child: const Padding(
padding: EdgeInsets.all(8.0), padding: EdgeInsets.all(8),
child: Center( child: Center(
child: Icon( child: Icon(
Icons.send, Icons.send,
@@ -332,12 +335,12 @@ class _MessageScreenState extends State<MessageScreen> {
/// below, we add two simple extensions to the [StreamChatClient] and [Channel]. /// below, we add two simple extensions to the [StreamChatClient] and [Channel].
extension on StreamChatClient { extension on StreamChatClient {
/// Fetches the current user id. /// Fetches the current user id.
String get uid => state.user.id; String get uid => state.user!.id;
} }
extension on Channel { extension on Channel {
/// Fetches the name of the channel by accessing [extraData] or [cid]. /// Fetches the name of the channel by accessing [extraData] or [cid].
String get name { String? get name {
final _channelName = extraData['name']; final _channelName = extraData['name'];
if (_channelName != null) { if (_channelName != null) {
return _channelName; return _channelName;
@@ -18,7 +18,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1 version: 1.0.0+1
environment: environment:
sdk: ">=2.7.0 <3.0.0" sdk: '>=2.12.0 <3.0.0'
dependencies: dependencies:
flutter: flutter:
@@ -133,6 +133,7 @@ class ChannelListCoreState extends State<ChannelListCore> {
stream: channelsBlocState.channelsStream, stream: channelsBlocState.channelsStream,
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.hasError) { if (snapshot.hasError) {
print('snapshot: ${snapshot.stackTrace}');
return widget.errorBuilder(context, snapshot.error!); return widget.errorBuilder(context, snapshot.error!);
} }
if (!snapshot.hasData) { if (!snapshot.hasData) {
@@ -779,8 +779,8 @@ void main() {
final mockClient = MockClient(); final mockClient = MockClient();
final channels = _generateChannels(mockClient); final channels = _generateChannels(mockClient);
int channelComparator(Channel a, Channel b) { int channelComparator(Channel a, Channel b) {
final aData = a.extraData!['extra_data_key'] as String; final aData = a.extraData['extra_data_key'] as String;
final bData = b.extraData!['extra_data_key'] as String; final bData = b.extraData['extra_data_key'] as String;
return bData.compareTo(aData); return bData.compareTo(aData);
} }