fix(core): Replaced all StreamController.add() with .safeAdd() to fix bad state errors.
Signed-off-by: xsahil03x <[email protected]>
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
🐞 Fixed
|
🐞 Fixed
|
||||||
|
|
||||||
- Do not move a channel to top if the new message is from a thread.
|
- Do not move a channel to top if the new message is from a thread.
|
||||||
|
- [[#848]](https://github.com/GetStream/stream-chat-flutter/issues/848) Fixed "Bad state: Cannot add new events after calling close" by replacing all `.add` methods with a new `.safeAdd`.
|
||||||
|
|
||||||
## 3.3.1
|
## 3.3.1
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:rxdart/rxdart.dart';
|
import 'package:rxdart/rxdart.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat/stream_chat.dart';
|
||||||
import 'package:stream_chat_flutter_core/src/channel_list_core.dart';
|
import 'package:stream_chat_flutter_core/src/channel_list_core.dart';
|
||||||
import 'package:stream_chat_flutter_core/src/stream_chat_core.dart';
|
import 'package:stream_chat_flutter_core/src/stream_chat_core.dart';
|
||||||
|
import 'package:stream_chat_flutter_core/src/stream_controller_extension.dart';
|
||||||
|
|
||||||
/// Widget dedicated to the management of a channel list with pagination
|
/// Widget dedicated to the management of a channel list with pagination
|
||||||
/// [ChannelsBloc] is used together with [ChannelListCore] to manage a list of
|
/// [ChannelsBloc] is used together with [ChannelListCore] to manage a list of
|
||||||
@@ -116,7 +116,7 @@ class ChannelsBlocState extends State<ChannelsBloc>
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (_channelsController.hasValue) {
|
if (_channelsController.hasValue) {
|
||||||
_queryChannelsLoadingController.add(true);
|
_queryChannelsLoadingController.safeAdd(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -135,14 +135,14 @@ class ChannelsBlocState extends State<ChannelsBloc>
|
|||||||
)) {
|
)) {
|
||||||
newChannels = channels;
|
newChannels = channels;
|
||||||
if (clear) {
|
if (clear) {
|
||||||
_channelsController.add(channels);
|
_channelsController.safeAdd(channels);
|
||||||
} else {
|
} else {
|
||||||
final temp = oldChannels + channels;
|
final temp = oldChannels + channels;
|
||||||
_channelsController.add(temp);
|
_channelsController.safeAdd(temp);
|
||||||
}
|
}
|
||||||
if (_channelsController.hasValue &&
|
if (_channelsController.hasValue &&
|
||||||
_queryChannelsLoadingController.value) {
|
_queryChannelsLoadingController.value) {
|
||||||
_queryChannelsLoadingController.sink.add(false);
|
_queryChannelsLoadingController.safeAdd(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (newChannels.isEmpty || newChannels.length < paginationParams.limit) {
|
if (newChannels.isEmpty || newChannels.length < paginationParams.limit) {
|
||||||
@@ -150,11 +150,11 @@ class ChannelsBlocState extends State<ChannelsBloc>
|
|||||||
}
|
}
|
||||||
} catch (e, stk) {
|
} catch (e, stk) {
|
||||||
// reset loading controller
|
// reset loading controller
|
||||||
_queryChannelsLoadingController.sink.add(false);
|
_queryChannelsLoadingController.safeAdd(false);
|
||||||
if (_channelsController.hasValue) {
|
if (_channelsController.hasValue) {
|
||||||
_queryChannelsLoadingController.addError(e, stk);
|
_queryChannelsLoadingController.safeAddError(e, stk);
|
||||||
} else {
|
} else {
|
||||||
_channelsController.addError(e, stk);
|
_channelsController.safeAddError(e, stk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -200,7 +200,7 @@ class ChannelsBlocState extends State<ChannelsBloc>
|
|||||||
if (widget.channelsComparator != null) {
|
if (widget.channelsComparator != null) {
|
||||||
newChannels.sort(widget.channelsComparator);
|
newChannels.sort(widget.channelsComparator);
|
||||||
}
|
}
|
||||||
_channelsController.add(newChannels);
|
_channelsController.safeAdd(newChannels);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,7 +212,7 @@ class ChannelsBlocState extends State<ChannelsBloc>
|
|||||||
if (channelIndex > -1) {
|
if (channelIndex > -1) {
|
||||||
final channel = newChannels.removeAt(channelIndex);
|
final channel = newChannels.removeAt(channelIndex);
|
||||||
_hiddenChannels.add(channel);
|
_hiddenChannels.add(channel);
|
||||||
_channelsController.add(newChannels);
|
_channelsController.safeAdd(newChannels);
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
..add(client
|
..add(client
|
||||||
@@ -222,7 +222,7 @@ class ChannelsBlocState extends State<ChannelsBloc>
|
|||||||
)
|
)
|
||||||
.listen((e) {
|
.listen((e) {
|
||||||
final channel = e.channel;
|
final channel = e.channel;
|
||||||
_channelsController.add(List.from(
|
_channelsController.safeAdd(List.from(
|
||||||
(channels ?? [])..removeWhere((c) => c.cid == channel?.cid),
|
(channels ?? [])..removeWhere((c) => c.cid == channel?.cid),
|
||||||
));
|
));
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:rxdart/rxdart.dart';
|
import 'package:rxdart/rxdart.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat/stream_chat.dart';
|
||||||
import 'package:stream_chat_flutter_core/src/stream_chat_core.dart';
|
import 'package:stream_chat_flutter_core/src/stream_chat_core.dart';
|
||||||
|
import 'package:stream_chat_flutter_core/src/stream_controller_extension.dart';
|
||||||
|
|
||||||
/// [MessageSearchBloc] is used to manage a list of messages with pagination.
|
/// [MessageSearchBloc] is used to manage a list of messages with pagination.
|
||||||
/// This class can be used to load messages, perform queries, etc.
|
/// This class can be used to load messages, perform queries, etc.
|
||||||
@@ -95,7 +96,7 @@ class MessageSearchBlocState extends State<MessageSearchBloc>
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (_messageResponses.hasValue) {
|
if (_messageResponses.hasValue) {
|
||||||
_queryMessagesLoadingController.add(true);
|
_queryMessagesLoadingController.safeAdd(true);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
final oldMessages = List<GetMessageResponse>.from(messageResponses ?? []);
|
final oldMessages = List<GetMessageResponse>.from(messageResponses ?? []);
|
||||||
@@ -120,24 +121,24 @@ class MessageSearchBlocState extends State<MessageSearchBloc>
|
|||||||
|
|
||||||
final newMessages = response.results;
|
final newMessages = response.results;
|
||||||
if (clear) {
|
if (clear) {
|
||||||
_messageResponses.add(newMessages);
|
_messageResponses.safeAdd(newMessages);
|
||||||
} else {
|
} else {
|
||||||
final temp = oldMessages + newMessages;
|
final temp = oldMessages + newMessages;
|
||||||
_messageResponses.add(temp);
|
_messageResponses.safeAdd(temp);
|
||||||
}
|
}
|
||||||
if (_messageResponses.hasValue && _queryMessagesLoadingController.value) {
|
if (_messageResponses.hasValue && _queryMessagesLoadingController.value) {
|
||||||
_queryMessagesLoadingController.add(false);
|
_queryMessagesLoadingController.safeAdd(false);
|
||||||
}
|
}
|
||||||
if (newMessages.isEmpty || newMessages.length < pagination.limit) {
|
if (newMessages.isEmpty || newMessages.length < pagination.limit) {
|
||||||
_paginationEnded = true;
|
_paginationEnded = true;
|
||||||
}
|
}
|
||||||
} catch (e, stk) {
|
} catch (e, stk) {
|
||||||
// reset loading controller
|
// reset loading controller
|
||||||
_queryMessagesLoadingController.add(false);
|
_queryMessagesLoadingController.safeAdd(false);
|
||||||
if (_messageResponses.hasValue) {
|
if (_messageResponses.hasValue) {
|
||||||
_queryMessagesLoadingController.addError(e, stk);
|
_queryMessagesLoadingController.safeAddError(e, stk);
|
||||||
} else {
|
} else {
|
||||||
_messageResponses.addError(e, stk);
|
_messageResponses.safeAddError(e, stk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:rxdart/rxdart.dart';
|
import 'package:rxdart/rxdart.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat/stream_chat.dart';
|
||||||
|
import 'package:stream_chat_flutter_core/src/stream_controller_extension.dart';
|
||||||
|
|
||||||
/// Specifies query direction for pagination
|
/// Specifies query direction for pagination
|
||||||
enum QueryDirection {
|
enum QueryDirection {
|
||||||
@@ -92,10 +92,10 @@ class StreamChannelState extends State<StreamChannel> {
|
|||||||
channel.state == null) {
|
channel.state == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_queryTopMessagesController.add(true);
|
_queryTopMessagesController.safeAdd(true);
|
||||||
|
|
||||||
if (channel.state!.messages.isEmpty) {
|
if (channel.state!.messages.isEmpty) {
|
||||||
return _queryTopMessagesController.add(false);
|
return _queryTopMessagesController.safeAdd(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
final oldestMessage = channel.state!.messages.first;
|
final oldestMessage = channel.state!.messages.first;
|
||||||
@@ -109,9 +109,9 @@ class StreamChannelState extends State<StreamChannel> {
|
|||||||
if (state.messages.isEmpty || state.messages.length < limit) {
|
if (state.messages.isEmpty || state.messages.length < limit) {
|
||||||
_topPaginationEnded = true;
|
_topPaginationEnded = true;
|
||||||
}
|
}
|
||||||
_queryTopMessagesController.add(false);
|
_queryTopMessagesController.safeAdd(false);
|
||||||
} catch (e, stk) {
|
} catch (e, stk) {
|
||||||
_queryTopMessagesController.addError(e, stk);
|
_queryTopMessagesController.safeAddError(e, stk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,10 +123,10 @@ class StreamChannelState extends State<StreamChannel> {
|
|||||||
_queryBottomMessagesController.value ||
|
_queryBottomMessagesController.value ||
|
||||||
channel.state == null ||
|
channel.state == null ||
|
||||||
channel.state!.isUpToDate) return;
|
channel.state!.isUpToDate) return;
|
||||||
_queryBottomMessagesController.add(true);
|
_queryBottomMessagesController.safeAdd(true);
|
||||||
|
|
||||||
if (channel.state!.messages.isEmpty) {
|
if (channel.state!.messages.isEmpty) {
|
||||||
return _queryBottomMessagesController.add(false);
|
return _queryBottomMessagesController.safeAdd(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
final recentMessage = channel.state!.messages.last;
|
final recentMessage = channel.state!.messages.last;
|
||||||
@@ -140,9 +140,9 @@ class StreamChannelState extends State<StreamChannel> {
|
|||||||
if (state.messages.isEmpty || state.messages.length < limit) {
|
if (state.messages.isEmpty || state.messages.length < limit) {
|
||||||
_bottomPaginationEnded = true;
|
_bottomPaginationEnded = true;
|
||||||
}
|
}
|
||||||
_queryBottomMessagesController.add(false);
|
_queryBottomMessagesController.safeAdd(false);
|
||||||
} catch (e, stk) {
|
} catch (e, stk) {
|
||||||
_queryBottomMessagesController.addError(e, stk);
|
_queryBottomMessagesController.safeAddError(e, stk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,7 +166,7 @@ class StreamChannelState extends State<StreamChannel> {
|
|||||||
if (_topPaginationEnded ||
|
if (_topPaginationEnded ||
|
||||||
_queryTopMessagesController.value ||
|
_queryTopMessagesController.value ||
|
||||||
channel.state == null) return;
|
channel.state == null) return;
|
||||||
_queryTopMessagesController.add(true);
|
_queryTopMessagesController.safeAdd(true);
|
||||||
|
|
||||||
Message? message;
|
Message? message;
|
||||||
if (channel.state!.threads.containsKey(parentId)) {
|
if (channel.state!.threads.containsKey(parentId)) {
|
||||||
@@ -188,9 +188,9 @@ class StreamChannelState extends State<StreamChannel> {
|
|||||||
if (response.messages.isEmpty || response.messages.length < limit) {
|
if (response.messages.isEmpty || response.messages.length < limit) {
|
||||||
_topPaginationEnded = true;
|
_topPaginationEnded = true;
|
||||||
}
|
}
|
||||||
_queryTopMessagesController.add(false);
|
_queryTopMessagesController.safeAdd(false);
|
||||||
} catch (e, stk) {
|
} catch (e, stk) {
|
||||||
_queryTopMessagesController.addError(e, stk);
|
_queryTopMessagesController.safeAddError(e, stk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
/// Extension on [StreamController] to safely add events and errors.
|
||||||
|
extension StreamControllerX<T> on StreamController<T> {
|
||||||
|
/// Safely adds the event to the controller,
|
||||||
|
/// Returns early if the controller is closed.
|
||||||
|
void safeAdd(T event) {
|
||||||
|
if (isClosed) return;
|
||||||
|
add(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Safely adds the error to the controller,
|
||||||
|
/// Returns early if the controller is closed.
|
||||||
|
void safeAddError(Object error, [StackTrace? stackTrace]) {
|
||||||
|
if (isClosed) return;
|
||||||
|
addError(error, stackTrace);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:rxdart/rxdart.dart';
|
import 'package:rxdart/rxdart.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat_flutter_core/src/stream_controller_extension.dart';
|
||||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
|
|
||||||
/// Widget dedicated to the management of a users list with pagination.
|
/// Widget dedicated to the management of a users list with pagination.
|
||||||
@@ -82,7 +81,7 @@ class UsersBlocState extends State<UsersBloc>
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (_usersController.hasValue) {
|
if (_usersController.hasValue) {
|
||||||
_queryUsersLoadingController.add(true);
|
_queryUsersLoadingController.safeAdd(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -97,24 +96,24 @@ class UsersBlocState extends State<UsersBloc>
|
|||||||
|
|
||||||
final newUsers = usersResponse.users;
|
final newUsers = usersResponse.users;
|
||||||
if (clear) {
|
if (clear) {
|
||||||
_usersController.add(usersResponse.users);
|
_usersController.safeAdd(usersResponse.users);
|
||||||
} else {
|
} else {
|
||||||
final temp = oldUsers + usersResponse.users;
|
final temp = oldUsers + usersResponse.users;
|
||||||
_usersController.add(temp);
|
_usersController.safeAdd(temp);
|
||||||
}
|
}
|
||||||
if (_usersController.hasValue && _queryUsersLoadingController.value) {
|
if (_usersController.hasValue && _queryUsersLoadingController.value) {
|
||||||
_queryUsersLoadingController.add(false);
|
_queryUsersLoadingController.safeAdd(false);
|
||||||
}
|
}
|
||||||
if (newUsers.isEmpty || newUsers.length < pagination.limit) {
|
if (newUsers.isEmpty || newUsers.length < pagination.limit) {
|
||||||
_paginationEnded = true;
|
_paginationEnded = true;
|
||||||
}
|
}
|
||||||
} catch (e, stk) {
|
} catch (e, stk) {
|
||||||
// reset loading controller
|
// reset loading controller
|
||||||
_queryUsersLoadingController.add(false);
|
_queryUsersLoadingController.safeAdd(false);
|
||||||
if (_usersController.hasValue) {
|
if (_usersController.hasValue) {
|
||||||
_queryUsersLoadingController.addError(e, stk);
|
_queryUsersLoadingController.safeAddError(e, stk);
|
||||||
} else {
|
} else {
|
||||||
_usersController.addError(e, stk);
|
_usersController.safeAddError(e, stk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user