Merge branch 'develop' into develop
This commit is contained in:
@@ -2,20 +2,26 @@
|
||||
|
||||
✅ Added
|
||||
|
||||
- You can now pass `score` to `client.sendReaction` and `channel.sendReaction` functions
|
||||
- You can now pass `score` to `client.sendReaction` and `channel.sendReaction` functions.
|
||||
- Added new `client.partialUpdateUsers` function in order to partially update users.
|
||||
|
||||
🐞 Fixed
|
||||
|
||||
- [[#890]](https://github.com/GetStream/stream-chat-flutter/pull/890). Fixed Reactions not updating on thread messages. Thanks [bstolinski](https://github.com/bstolinski).
|
||||
- [[#890]](https://github.com/GetStream/stream-chat-flutter/pull/890) Fixed Reactions not updating on thread messages.
|
||||
Thanks [bstolinski](https://github.com/bstolinski).
|
||||
- [[#897]](https://github.com/GetStream/stream-chat-flutter/issues/897) Fixed error type mis-match in `AuthInterceptor`.
|
||||
|
||||
## 3.4.0
|
||||
|
||||
🐞 Fixed
|
||||
|
||||
- [[#857]](https://github.com/GetStream/stream-chat-flutter/issues/857) Channel now listens for member ban/unban and
|
||||
updates the channel state with the latest data.
|
||||
- [[#748]](https://github.com/GetStream/stream-chat-flutter/issues/748) `Message.user` is now also included while saving users in persistence.
|
||||
- [[#748]](https://github.com/GetStream/stream-chat-flutter/issues/748) `Message.user` is now also included while saving
|
||||
users in persistence.
|
||||
- [[#871]](https://github.com/GetStream/stream-chat-flutter/issues/871) Fixed thread message deletion.
|
||||
- [[#846]](https://github.com/GetStream/stream-chat-flutter/issues/846) Fixed `message.ownReactions` getting truncated when receiving a reaction event.
|
||||
- [[#846]](https://github.com/GetStream/stream-chat-flutter/issues/846) Fixed `message.ownReactions` getting truncated
|
||||
when receiving a reaction event.
|
||||
- Add check for invalid image URLs
|
||||
- Fix `channelState.pinnedMessagesStream` getting reset to `0` after a channel update.
|
||||
- Fixed `unreadCount` after removing user from a channel.
|
||||
|
||||
@@ -1073,6 +1073,28 @@ class StreamChatClient {
|
||||
Future<UpdateUsersResponse> updateUsers(List<User> users) =>
|
||||
_chatApi.user.updateUsers(users);
|
||||
|
||||
/// Partially update the given user with [id].
|
||||
/// Use [set] to define values to be set.
|
||||
/// Use [unset] to define values to be unset.
|
||||
Future<UpdateUsersResponse> partialUpdateUser(
|
||||
String id, {
|
||||
Map<String, Object?>? set,
|
||||
List<String>? unset,
|
||||
}) {
|
||||
final user = PartialUpdateUserRequest(
|
||||
id: id,
|
||||
set: set,
|
||||
unset: unset,
|
||||
);
|
||||
return partialUpdateUsers([user]);
|
||||
}
|
||||
|
||||
/// Batch partial updates the [users].
|
||||
Future<UpdateUsersResponse> partialUpdateUsers(
|
||||
List<PartialUpdateUserRequest> users,
|
||||
) =>
|
||||
_chatApi.user.partialUpdateUsers(users);
|
||||
|
||||
/// Bans a user from all channels
|
||||
Future<EmptyResponse> banUser(
|
||||
String targetUserId, [
|
||||
|
||||
@@ -156,3 +156,29 @@ class PaginationParams extends Equatable {
|
||||
lessThanOrEqual,
|
||||
];
|
||||
}
|
||||
|
||||
/// Request model for the [client.partialUpdateUser] api call.
|
||||
@JsonSerializable(createFactory: false)
|
||||
class PartialUpdateUserRequest extends Equatable {
|
||||
/// Creates a new PartialUpdateUserRequest instance.
|
||||
const PartialUpdateUserRequest({
|
||||
required this.id,
|
||||
this.set,
|
||||
this.unset,
|
||||
});
|
||||
|
||||
/// User ID.
|
||||
final String id;
|
||||
|
||||
/// Fields to set.
|
||||
final Map<String, Object?>? set;
|
||||
|
||||
/// Fields to unset.
|
||||
final List<String>? unset;
|
||||
|
||||
/// Serialize model to json
|
||||
Map<String, dynamic> toJson() => _$PartialUpdateUserRequestToJson(this);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [id, set, unset];
|
||||
}
|
||||
|
||||
@@ -54,3 +54,14 @@ Map<String, dynamic> _$PaginationParamsToJson(PaginationParams instance) {
|
||||
writeNotNull('id_lte', instance.lessThanOrEqual);
|
||||
return val;
|
||||
}
|
||||
|
||||
Map<String, dynamic> _$PartialUpdateUserRequestToJson(
|
||||
PartialUpdateUserRequest instance) =>
|
||||
<String, dynamic>{
|
||||
'stringify': instance.stringify,
|
||||
'hash_code': instance.hashCode,
|
||||
'id': instance.id,
|
||||
'set': instance.set,
|
||||
'unset': instance.unset,
|
||||
'props': instance.props,
|
||||
};
|
||||
|
||||
@@ -46,4 +46,17 @@ class UserApi {
|
||||
);
|
||||
return UpdateUsersResponse.fromJson(response.data);
|
||||
}
|
||||
|
||||
/// Batch partial update of [users].
|
||||
Future<UpdateUsersResponse> partialUpdateUsers(
|
||||
List<PartialUpdateUserRequest> users,
|
||||
) async {
|
||||
final response = await _client.patch(
|
||||
'/users',
|
||||
data: {
|
||||
'users': users,
|
||||
},
|
||||
);
|
||||
return UpdateUsersResponse.fromJson(response.data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,10 +49,13 @@ class AuthInterceptor extends Interceptor {
|
||||
DioError err,
|
||||
ErrorInterceptorHandler handler,
|
||||
) async {
|
||||
ErrorResponse? error;
|
||||
final data = err.response?.data;
|
||||
if (data != null) error = ErrorResponse.fromJson(data);
|
||||
if (error?.code == ChatErrorCode.tokenExpired.code) {
|
||||
if (data == null || data is! Map<String, dynamic>) {
|
||||
return handler.next(err);
|
||||
}
|
||||
|
||||
final error = ErrorResponse.fromJson(data);
|
||||
if (error.code == ChatErrorCode.tokenExpired.code) {
|
||||
if (_tokenManager.isStatic) return handler.next(err);
|
||||
_client.lock();
|
||||
await _tokenManager.loadToken(refresh: true);
|
||||
|
||||
@@ -21,7 +21,7 @@ ChannelState _$ChannelStateFromJson(Map<String, dynamic> json) => ChannelState(
|
||||
pinnedMessages: (json['pinned_messages'] as List<dynamic>?)
|
||||
?.map((e) => Message.fromJson(e as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
const [],
|
||||
_emptyPinnedMessages,
|
||||
watcherCount: json['watcher_count'] as int?,
|
||||
watchers: (json['watchers'] as List<dynamic>?)
|
||||
?.map((e) => User.fromJson(e as Map<String, dynamic>))
|
||||
|
||||
@@ -1772,6 +1772,46 @@ void main() {
|
||||
verifyNoMoreInteractions(api.user);
|
||||
});
|
||||
|
||||
test('`.partialUpdateUser`', () async {
|
||||
const userId = 'test-user-id';
|
||||
|
||||
final set = {'color': 'yellow'};
|
||||
final unset = <String>[];
|
||||
|
||||
final partialUpdateRequest = PartialUpdateUserRequest(
|
||||
id: userId,
|
||||
set: set,
|
||||
unset: unset,
|
||||
);
|
||||
|
||||
final updatedUser = User(
|
||||
id: userId,
|
||||
extraData: {'color': set['color']},
|
||||
);
|
||||
|
||||
when(() => api.user.partialUpdateUsers([partialUpdateRequest]))
|
||||
.thenAnswer(
|
||||
(_) async => UpdateUsersResponse()
|
||||
..users = {
|
||||
updatedUser.id: updatedUser,
|
||||
},
|
||||
);
|
||||
|
||||
final res = await client.partialUpdateUser(
|
||||
userId,
|
||||
set: set,
|
||||
unset: unset,
|
||||
);
|
||||
|
||||
expect(res, isNotNull);
|
||||
expect(res.users, {updatedUser.id: updatedUser});
|
||||
|
||||
verify(
|
||||
() => api.user.partialUpdateUsers([partialUpdateRequest]),
|
||||
).called(1);
|
||||
verifyNoMoreInteractions(api.user);
|
||||
});
|
||||
|
||||
test('`.banUser`', () async {
|
||||
const userId = 'test-user-id';
|
||||
|
||||
|
||||
@@ -82,4 +82,37 @@ void main() {
|
||||
verify(() => client.post(path, data: any(named: 'data'))).called(1);
|
||||
verifyNoMoreInteractions(client);
|
||||
});
|
||||
|
||||
test('partialUpdateUsers', () async {
|
||||
const user = PartialUpdateUserRequest(
|
||||
id: 'test-user-id',
|
||||
set: {'color': 'yellow'},
|
||||
);
|
||||
|
||||
const path = '/users';
|
||||
|
||||
final updatedUser = {user.id: User(id: user.id, extraData: user.set!)};
|
||||
|
||||
when(() => client.patch(path, data: {
|
||||
'users': [user],
|
||||
})).thenAnswer(
|
||||
(_) async => successResponse(
|
||||
path,
|
||||
data: {
|
||||
'users':
|
||||
updatedUser.map((key, value) => MapEntry(key, value.toJson()))
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
final res = await userApi.partialUpdateUsers([user]);
|
||||
|
||||
expect(res, isNotNull);
|
||||
expect(res.users.length, updatedUser.length);
|
||||
|
||||
verify(() => client.patch(path, data: {
|
||||
'users': [user]
|
||||
})).called(1);
|
||||
verifyNoMoreInteractions(client);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
- [[#888]](https://github.com/GetStream/stream-chat-flutter/issues/888) Fix `unban` command not working in `MessageInput`.
|
||||
- [[#805]](https://github.com/GetStream/stream-chat-flutter/issues/805) Updated chewie dependency version to 1.3.0
|
||||
- Fix `showScrollToBottom` in `MessageListView` not respecting false value.
|
||||
|
||||
## 3.4.0
|
||||
- Updated `stream_chat_flutter_core` dependency to [`3.4.0`](https://pub.dev/packages/stream_chat_flutter_core/changelog).
|
||||
|
||||
@@ -712,20 +712,21 @@ class _MessageListViewState extends State<MessageListView> {
|
||||
);
|
||||
},
|
||||
),
|
||||
BetterStreamBuilder<bool>(
|
||||
stream: streamChannel!.channel.state!.isUpToDateStream,
|
||||
initialData: streamChannel!.channel.state!.isUpToDate,
|
||||
builder: (context, snapshot) => ValueListenableBuilder<bool>(
|
||||
valueListenable: _showScrollToBottom,
|
||||
child: _buildScrollToBottom(),
|
||||
builder: (context, value, child) {
|
||||
if (!snapshot || value) {
|
||||
return child!;
|
||||
}
|
||||
return const Offstage();
|
||||
},
|
||||
if (widget.showScrollToBottom)
|
||||
BetterStreamBuilder<bool>(
|
||||
stream: streamChannel!.channel.state!.isUpToDateStream,
|
||||
initialData: streamChannel!.channel.state!.isUpToDate,
|
||||
builder: (context, snapshot) => ValueListenableBuilder<bool>(
|
||||
valueListenable: _showScrollToBottom,
|
||||
child: _buildScrollToBottom(),
|
||||
builder: (context, value, child) {
|
||||
if (!snapshot || value) {
|
||||
return child!;
|
||||
}
|
||||
return const Offstage();
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
if (widget.showFloatingDateDivider)
|
||||
_buildFloatingDateDivider(itemCount),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user