Merge branch 'develop' of github.com:GetStream/stream-chat-flutter into develop
This commit is contained in:
@@ -294,6 +294,18 @@ class Channel {
|
||||
return data;
|
||||
}
|
||||
|
||||
/// List of user permissions on this channel
|
||||
List<String> get ownCapabilities =>
|
||||
state?._channelState.channel?.ownCapabilities ?? [];
|
||||
|
||||
/// List of user permissions on this channel
|
||||
Stream<List<String>> get ownCapabilitiesStream {
|
||||
_checkInitialized();
|
||||
return state!.channelStateStream
|
||||
.map((cs) => cs.channel?.ownCapabilities ?? [])
|
||||
.distinct();
|
||||
}
|
||||
|
||||
/// Channel extra data as a stream.
|
||||
Stream<Map<String, Object?>> get extraDataStream {
|
||||
_checkInitialized();
|
||||
@@ -489,6 +501,7 @@ class Channel {
|
||||
Future<SendMessageResponse> sendMessage(
|
||||
Message message, {
|
||||
bool skipPush = false,
|
||||
bool skipEnrichUrl = false,
|
||||
}) async {
|
||||
_checkInitialized();
|
||||
// Cancelling previous completer in case it's called again in the process
|
||||
@@ -536,6 +549,7 @@ class Channel {
|
||||
id!,
|
||||
type,
|
||||
skipPush: skipPush,
|
||||
skipEnrichUrl: skipEnrichUrl,
|
||||
);
|
||||
state!.updateMessage(response.message);
|
||||
if (cooldown > 0) cooldownStartedAt = DateTime.now();
|
||||
@@ -552,7 +566,10 @@ class Channel {
|
||||
///
|
||||
/// Waits for a [_messageAttachmentsUploadCompleter] to complete
|
||||
/// before actually updating the message.
|
||||
Future<UpdateMessageResponse> updateMessage(Message message) async {
|
||||
Future<UpdateMessageResponse> updateMessage(
|
||||
Message message, {
|
||||
bool skipEnrichUrl = false,
|
||||
}) async {
|
||||
final originalMessage = message;
|
||||
|
||||
// Cancelling previous completer in case it's called again in the process
|
||||
@@ -590,7 +607,10 @@ class Channel {
|
||||
message = await attachmentsUploadCompleter.future;
|
||||
}
|
||||
|
||||
final response = await _client.updateMessage(message);
|
||||
final response = await _client.updateMessage(
|
||||
message,
|
||||
skipEnrichUrl: skipEnrichUrl,
|
||||
);
|
||||
|
||||
final m = response.message.copyWith(
|
||||
ownReactions: message.ownReactions,
|
||||
@@ -620,12 +640,14 @@ class Channel {
|
||||
Message message, {
|
||||
Map<String, Object?>? set,
|
||||
List<String>? unset,
|
||||
bool skipEnrichUrl = false,
|
||||
}) async {
|
||||
try {
|
||||
final response = await _client.partialUpdateMessage(
|
||||
message.id,
|
||||
set: set,
|
||||
unset: unset,
|
||||
skipEnrichUrl: skipEnrichUrl,
|
||||
);
|
||||
|
||||
final updatedMessage = response.message.copyWith(
|
||||
@@ -1247,7 +1269,9 @@ class Channel {
|
||||
if (preferOffline && cid != null) {
|
||||
final updatedState = await _client.chatPersistenceClient
|
||||
?.getChannelStateByCid(cid!, messagePagination: messagesPagination);
|
||||
if (updatedState != null && updatedState.messages.isNotEmpty) {
|
||||
if (updatedState != null &&
|
||||
updatedState.messages != null &&
|
||||
updatedState.messages!.isNotEmpty) {
|
||||
if (this.state == null) {
|
||||
_initState(updatedState);
|
||||
} else {
|
||||
@@ -1330,14 +1354,6 @@ class Channel {
|
||||
return _client.unmuteChannel(cid!);
|
||||
}
|
||||
|
||||
/// Bans the user with given [userID] from the channel.
|
||||
@Deprecated("Use 'banMember' instead. This method will be removed in v4.0.0")
|
||||
Future<EmptyResponse> banUser(
|
||||
String userID,
|
||||
Map<String, dynamic> options,
|
||||
) =>
|
||||
banMember(userID, options);
|
||||
|
||||
/// Bans the member with given [userID] from the channel.
|
||||
Future<EmptyResponse> banMember(
|
||||
String userID,
|
||||
@@ -1352,12 +1368,6 @@ class Channel {
|
||||
return _client.banUser(userID, opts);
|
||||
}
|
||||
|
||||
/// Remove the ban for the user with given [userID] in the channel.
|
||||
@Deprecated(
|
||||
"Use 'unbanMember' instead. This method will be removed in v4.0.0",
|
||||
)
|
||||
Future<EmptyResponse> unbanUser(String userID) => unbanMember(userID);
|
||||
|
||||
/// Remove the ban for the member with given [userID] in the channel.
|
||||
Future<EmptyResponse> unbanMember(String userID) async {
|
||||
_checkInitialized();
|
||||
@@ -1558,7 +1568,7 @@ class ChannelClientState {
|
||||
|
||||
void _checkExpiredAttachmentMessages(ChannelState channelState) async {
|
||||
final expiredAttachmentMessagesId = channelState.messages
|
||||
.where((m) =>
|
||||
?.where((m) =>
|
||||
!_updatedMessagesIds.contains(m.id) &&
|
||||
m.attachments.isNotEmpty &&
|
||||
m.attachments.any((e) {
|
||||
@@ -1585,7 +1595,8 @@ class ChannelClientState {
|
||||
.map((e) => e.id)
|
||||
.toList();
|
||||
|
||||
if (expiredAttachmentMessagesId.isNotEmpty) {
|
||||
if (expiredAttachmentMessagesId != null &&
|
||||
expiredAttachmentMessagesId.isNotEmpty) {
|
||||
await _channel._initializedCompleter.future;
|
||||
_updatedMessagesIds.addAll(expiredAttachmentMessagesId);
|
||||
_channel.getMessagesById(expiredAttachmentMessagesId);
|
||||
@@ -1595,9 +1606,10 @@ class ChannelClientState {
|
||||
void _listenMemberAdded() {
|
||||
_subscriptions.add(_channel.on(EventType.memberAdded).listen((Event e) {
|
||||
final member = e.member;
|
||||
final existingMembers = channelState.members ?? [];
|
||||
updateChannelState(channelState.copyWith(
|
||||
members: [
|
||||
...channelState.members,
|
||||
...existingMembers,
|
||||
member!,
|
||||
],
|
||||
));
|
||||
@@ -1607,11 +1619,13 @@ class ChannelClientState {
|
||||
void _listenMemberRemoved() {
|
||||
_subscriptions.add(_channel.on(EventType.memberRemoved).listen((Event e) {
|
||||
final user = e.user;
|
||||
final existingMembers = channelState.members ?? [];
|
||||
final existingRead = channelState.read ?? [];
|
||||
updateChannelState(channelState.copyWith(
|
||||
members: channelState.members
|
||||
members: existingMembers
|
||||
.where((m) => m.userId != user!.id)
|
||||
.toList(growable: false),
|
||||
read: channelState.read
|
||||
read: existingRead
|
||||
.where((r) => r.user.id != user!.id)
|
||||
.toList(growable: false),
|
||||
));
|
||||
@@ -1781,9 +1795,10 @@ class ChannelClientState {
|
||||
updateMessage(message);
|
||||
|
||||
if (message.pinned) {
|
||||
final _existingPinnedMessages = _channelState.pinnedMessages ?? [];
|
||||
_channelState = _channelState.copyWith(
|
||||
pinnedMessages: [
|
||||
..._channelState.pinnedMessages,
|
||||
..._existingPinnedMessages,
|
||||
message,
|
||||
],
|
||||
);
|
||||
@@ -1821,10 +1836,6 @@ class ChannelClientState {
|
||||
}));
|
||||
}
|
||||
|
||||
/// Add a [message] to this [channelState].
|
||||
@Deprecated('Use updateMessage instead')
|
||||
void addMessage(Message message) => updateMessage(message);
|
||||
|
||||
/// Updates the [message] in the state if it exists. Adds it otherwise.
|
||||
void updateMessage(Message message) {
|
||||
if (message.parentId == null || message.showInChannel == true) {
|
||||
@@ -1919,7 +1930,7 @@ class ChannelClientState {
|
||||
)
|
||||
.listen(
|
||||
(event) {
|
||||
final readList = List<Read>.from(_channelState.read);
|
||||
final readList = List<Read>.from(_channelState.read ?? []);
|
||||
final userReadIndex =
|
||||
read.indexWhere((r) => r.user.id == event.user!.id);
|
||||
|
||||
@@ -1940,31 +1951,34 @@ class ChannelClientState {
|
||||
}
|
||||
|
||||
/// Channel message list.
|
||||
List<Message> get messages => _channelState.messages;
|
||||
List<Message> get messages => _channelState.messages ?? <Message>[];
|
||||
|
||||
/// Channel message list as a stream.
|
||||
Stream<List<Message>> get messagesStream => channelStateStream
|
||||
.map((cs) => cs.messages)
|
||||
.map((cs) => cs.messages ?? <Message>[])
|
||||
.distinct(const ListEquality().equals);
|
||||
|
||||
/// Channel pinned message list.
|
||||
List<Message> get pinnedMessages => _channelState.pinnedMessages;
|
||||
List<Message> get pinnedMessages =>
|
||||
_channelState.pinnedMessages ?? <Message>[];
|
||||
|
||||
/// Channel pinned message list as a stream.
|
||||
Stream<List<Message>> get pinnedMessagesStream => channelStateStream
|
||||
.map((cs) => cs.pinnedMessages)
|
||||
.map((cs) => cs.pinnedMessages ?? <Message>[])
|
||||
.distinct(const ListEquality().equals);
|
||||
|
||||
/// Get channel last message.
|
||||
Message? get lastMessage =>
|
||||
_channelState.messages.isNotEmpty ? _channelState.messages.last : null;
|
||||
_channelState.messages != null && _channelState.messages!.isNotEmpty
|
||||
? _channelState.messages!.last
|
||||
: null;
|
||||
|
||||
/// Get channel last message.
|
||||
Stream<Message?> get lastMessageStream =>
|
||||
messagesStream.map((event) => event.isNotEmpty ? event.last : null);
|
||||
|
||||
/// Channel members list.
|
||||
List<Member> get members => _channelState.members
|
||||
List<Member> get members => (_channelState.members ?? <Member>[])
|
||||
.map((e) => e.copyWith(user: _channel.client.state.users[e.user!.id]))
|
||||
.toList();
|
||||
|
||||
@@ -1985,7 +1999,7 @@ class ChannelClientState {
|
||||
channelStateStream.map((cs) => cs.watcherCount);
|
||||
|
||||
/// Channel watchers list.
|
||||
List<User> get watchers => _channelState.watchers
|
||||
List<User> get watchers => (_channelState.watchers ?? <User>[])
|
||||
.map((e) => _channel.client.state.users[e.id] ?? e)
|
||||
.toList();
|
||||
|
||||
@@ -1997,11 +2011,20 @@ class ChannelClientState {
|
||||
(watchers, users) => watchers!.map((e) => users[e.id] ?? e).toList(),
|
||||
);
|
||||
|
||||
/// Channel member for the current user.
|
||||
Member? get currentUserMember => members.firstWhereOrNull(
|
||||
(m) => m.user?.id == _channel.client.state.currentUser?.id,
|
||||
);
|
||||
|
||||
/// User role for the current user.
|
||||
String? get currentUserRole => currentUserMember?.role;
|
||||
|
||||
/// Channel read list.
|
||||
List<Read> get read => _channelState.read;
|
||||
List<Read> get read => _channelState.read ?? <Read>[];
|
||||
|
||||
/// Channel read list as a stream.
|
||||
Stream<List<Read>> get readStream => channelStateStream.map((cs) => cs.read);
|
||||
Stream<List<Read>> get readStream =>
|
||||
channelStateStream.map((cs) => cs.read ?? <Read>[]);
|
||||
|
||||
bool _isCurrentUserRead(Read read) =>
|
||||
read.user.id == _channel._client.state.currentUser!.id;
|
||||
@@ -2022,7 +2045,7 @@ class ChannelClientState {
|
||||
|
||||
/// Setter for unread count.
|
||||
set unreadCount(int count) {
|
||||
final reads = [..._channelState.read];
|
||||
final reads = [...read];
|
||||
final currentUserReadIndex = reads.indexWhere(_isCurrentUserRead);
|
||||
|
||||
if (currentUserReadIndex < 0) return;
|
||||
@@ -2077,31 +2100,37 @@ class ChannelClientState {
|
||||
|
||||
/// Update channelState with updated information.
|
||||
void updateChannelState(ChannelState updatedState) {
|
||||
final _existingStateMessages = _channelState.messages ?? [];
|
||||
final _updatedStateMessages = updatedState.messages ?? [];
|
||||
final newMessages = <Message>[
|
||||
...updatedState.messages,
|
||||
..._channelState.messages
|
||||
..._updatedStateMessages,
|
||||
..._existingStateMessages
|
||||
.where((m) =>
|
||||
!updatedState.messages.any((newMessage) => newMessage.id == m.id))
|
||||
!_updatedStateMessages.any((newMessage) => newMessage.id == m.id))
|
||||
.toList(),
|
||||
]..sort(_sortByCreatedAt);
|
||||
|
||||
final _existingStateWatchers = _channelState.watchers ?? [];
|
||||
final _updatedStateWatchers = updatedState.watchers ?? [];
|
||||
final newWatchers = <User>[
|
||||
...updatedState.watchers,
|
||||
..._channelState.watchers
|
||||
..._updatedStateWatchers,
|
||||
..._existingStateWatchers
|
||||
.where((w) =>
|
||||
!updatedState.watchers.any((newWatcher) => newWatcher.id == w.id))
|
||||
!_updatedStateWatchers.any((newWatcher) => newWatcher.id == w.id))
|
||||
.toList(),
|
||||
];
|
||||
|
||||
final newMembers = <Member>[
|
||||
...updatedState.members,
|
||||
...updatedState.members ?? [],
|
||||
];
|
||||
|
||||
final _existingStateRead = _channelState.read ?? [];
|
||||
final _updatedStateRead = updatedState.read ?? [];
|
||||
final newReads = <Read>[
|
||||
...updatedState.read,
|
||||
..._channelState.read
|
||||
..._updatedStateRead,
|
||||
..._existingStateRead
|
||||
.where((r) =>
|
||||
!updatedState.read.any((newRead) => newRead.user.id == r.user.id))
|
||||
!_updatedStateRead.any((newRead) => newRead.user.id == r.user.id))
|
||||
.toList(),
|
||||
];
|
||||
|
||||
@@ -2253,9 +2282,9 @@ class ChannelClientState {
|
||||
_pinnedMessagesTimer = Timer.periodic(const Duration(seconds: 30), (_) {
|
||||
final now = DateTime.now();
|
||||
var expiredMessages = channelState.pinnedMessages
|
||||
.where((m) => m.pinExpires?.isBefore(now) == true)
|
||||
?.where((m) => m.pinExpires?.isBefore(now) == true)
|
||||
.toList();
|
||||
if (expiredMessages.isNotEmpty) {
|
||||
if (expiredMessages != null && expiredMessages.isNotEmpty) {
|
||||
expiredMessages = expiredMessages
|
||||
.map((m) => m.copyWith(
|
||||
pinExpires: null,
|
||||
|
||||
@@ -29,7 +29,6 @@ import 'package:stream_chat/src/core/platform_detector/platform_detector.dart';
|
||||
import 'package:stream_chat/src/core/util/utils.dart';
|
||||
import 'package:stream_chat/src/db/chat_persistence_client.dart';
|
||||
import 'package:stream_chat/src/event_type.dart';
|
||||
import 'package:stream_chat/src/location.dart';
|
||||
import 'package:stream_chat/src/ws/connection_status.dart';
|
||||
import 'package:stream_chat/src/ws/websocket.dart';
|
||||
import 'package:stream_chat/version.dart';
|
||||
@@ -66,10 +65,6 @@ class StreamChatClient {
|
||||
this.logLevel = Level.WARNING,
|
||||
this.logHandlerFunction = StreamChatClient.defaultLogHandler,
|
||||
RetryPolicy? retryPolicy,
|
||||
@Deprecated('''
|
||||
Location is now deprecated in favor of the new edge server. Will be removed in v4.0.0.
|
||||
Read more here: https://getstream.io/blog/chat-edge-infrastructure
|
||||
''') Location? location,
|
||||
String? baseURL,
|
||||
Duration connectTimeout = const Duration(seconds: 6),
|
||||
Duration receiveTimeout = const Duration(seconds: 6),
|
||||
@@ -426,6 +421,7 @@ class StreamChatClient {
|
||||
}
|
||||
|
||||
void _connectionStatusHandler(ConnectionStatus status) async {
|
||||
final previousState = wsConnectionStatus;
|
||||
final currentState = _wsConnectionStatus = status;
|
||||
|
||||
handleEvent(Event(
|
||||
@@ -433,7 +429,8 @@ class StreamChatClient {
|
||||
online: status == ConnectionStatus.connected,
|
||||
));
|
||||
|
||||
if (currentState == ConnectionStatus.connected) {
|
||||
if (currentState == ConnectionStatus.connected &&
|
||||
previousState != ConnectionStatus.connected) {
|
||||
// connection recovered
|
||||
final cids = state.channels.keys.toList(growable: false);
|
||||
if (cids.isNotEmpty) {
|
||||
@@ -621,7 +618,7 @@ class StreamChatClient {
|
||||
final channels = res.channels;
|
||||
|
||||
final users = channels
|
||||
.expand((it) => it.members)
|
||||
.expand((it) => it.members ?? <Member>[])
|
||||
.map((it) => it.user)
|
||||
.toList(growable: false);
|
||||
|
||||
@@ -1246,12 +1243,14 @@ class StreamChatClient {
|
||||
String channelId,
|
||||
String channelType, {
|
||||
bool skipPush = false,
|
||||
bool skipEnrichUrl = false,
|
||||
}) =>
|
||||
_chatApi.message.sendMessage(
|
||||
channelId,
|
||||
channelType,
|
||||
message,
|
||||
skipPush: skipPush,
|
||||
skipEnrichUrl: skipEnrichUrl,
|
||||
);
|
||||
|
||||
/// Lists all the message replies for the [parentId]
|
||||
@@ -1275,8 +1274,14 @@ class StreamChatClient {
|
||||
);
|
||||
|
||||
/// Update the given message
|
||||
Future<UpdateMessageResponse> updateMessage(Message message) =>
|
||||
_chatApi.message.updateMessage(message);
|
||||
Future<UpdateMessageResponse> updateMessage(
|
||||
Message message, {
|
||||
bool skipEnrichUrl = false,
|
||||
}) =>
|
||||
_chatApi.message.updateMessage(
|
||||
message,
|
||||
skipEnrichUrl: skipEnrichUrl,
|
||||
);
|
||||
|
||||
/// Partially update the given [messageId]
|
||||
/// Use [set] to define values to be set
|
||||
@@ -1285,11 +1290,13 @@ class StreamChatClient {
|
||||
String messageId, {
|
||||
Map<String, Object?>? set,
|
||||
List<String>? unset,
|
||||
bool skipEnrichUrl = false,
|
||||
}) =>
|
||||
_chatApi.message.partialUpdateMessage(
|
||||
messageId,
|
||||
set: set,
|
||||
unset: unset,
|
||||
skipEnrichUrl: skipEnrichUrl,
|
||||
);
|
||||
|
||||
/// Deletes the given message
|
||||
@@ -1555,18 +1562,6 @@ class ClientState {
|
||||
/// The current user as a stream
|
||||
Stream<OwnUser?> get currentUserStream => _currentUserController.stream;
|
||||
|
||||
// coverage:ignore-start
|
||||
|
||||
/// The current user
|
||||
@Deprecated('Use `.currentUser` instead, Will be removed in future releases')
|
||||
OwnUser? get user => _currentUserController.valueOrNull;
|
||||
|
||||
/// The current user as a stream
|
||||
@Deprecated(
|
||||
'Use `.currentUserStream` instead, Will be removed in future releases',
|
||||
)
|
||||
Stream<OwnUser?> get userStream => _currentUserController.stream;
|
||||
|
||||
// coverage:ignore-end
|
||||
|
||||
/// The current user
|
||||
|
||||
@@ -5,9 +5,9 @@ import 'package:rxdart/rxdart.dart';
|
||||
import 'package:stream_chat/src/client/retry_policy.dart';
|
||||
import 'package:stream_chat/stream_chat.dart';
|
||||
|
||||
/// The retry queue associated to a channel
|
||||
/// The retry queue associated to a channel.
|
||||
class RetryQueue {
|
||||
/// Instantiate a new RetryQueue object
|
||||
/// Instantiate a new RetryQueue object.
|
||||
RetryQueue({
|
||||
required this.channel,
|
||||
this.logger,
|
||||
@@ -17,13 +17,13 @@ class RetryQueue {
|
||||
_listenFailedEvents();
|
||||
}
|
||||
|
||||
/// The channel of this queue
|
||||
/// The channel of this queue.
|
||||
final Channel channel;
|
||||
|
||||
/// The client associated with this [channel]
|
||||
/// The client associated with this [channel].
|
||||
final StreamChatClient client;
|
||||
|
||||
/// The logger associated to this queue
|
||||
/// The logger associated to this queue.
|
||||
final Logger? logger;
|
||||
|
||||
late final RetryPolicy _retryPolicy;
|
||||
@@ -63,7 +63,7 @@ class RetryQueue {
|
||||
}).addTo(_compositeSubscription);
|
||||
}
|
||||
|
||||
/// Add a list of messages
|
||||
/// Add a list of messages.
|
||||
void add(List<Message> messages) {
|
||||
if (messages.isEmpty) return;
|
||||
if (!_messageQueue.containsAllMessage(messages)) {
|
||||
@@ -113,6 +113,7 @@ class RetryQueue {
|
||||
} catch (e) {
|
||||
if (e is! StreamChatNetworkError || !e.isRetriable) {
|
||||
_messageQueue.removeMessage(message);
|
||||
_sendFailedEvent(message);
|
||||
return true;
|
||||
}
|
||||
// retry logic
|
||||
@@ -174,10 +175,10 @@ class RetryQueue {
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether our [_messageQueue] has messages or not
|
||||
/// Whether our [_messageQueue] has messages or not.
|
||||
bool get hasMessages => _messageQueue.isNotEmpty;
|
||||
|
||||
/// Call this method to dispose this object
|
||||
/// Call this method to dispose this object.
|
||||
void dispose() {
|
||||
_messageQueue.clear();
|
||||
_compositeSubscription.dispose();
|
||||
|
||||
Reference in New Issue
Block a user