Merge branch 'GetStream:develop' into develop
This commit is contained in:
@@ -1,18 +1,27 @@
|
|||||||
## Upcoming
|
## Upcoming
|
||||||
|
|
||||||
|
✅ Added
|
||||||
|
|
||||||
|
- You can now pass `score` to `client.sendReaction` and `channel.sendReaction` functions.
|
||||||
|
- Added new `client.partialUpdateUsers` function in order to partially update users.
|
||||||
|
|
||||||
🐞 Fixed
|
🐞 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
|
## 3.4.0
|
||||||
|
|
||||||
🐞 Fixed
|
🐞 Fixed
|
||||||
|
|
||||||
- [[#857]](https://github.com/GetStream/stream-chat-flutter/issues/857) Channel now listens for member ban/unban and
|
- [[#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.
|
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.
|
- [[#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
|
- Add check for invalid image URLs
|
||||||
- Fix `channelState.pinnedMessagesStream` getting reset to `0` after a channel update.
|
- Fix `channelState.pinnedMessagesStream` getting reset to `0` after a channel update.
|
||||||
- Fixed `unreadCount` after removing user from a channel.
|
- Fixed `unreadCount` after removing user from a channel.
|
||||||
|
|||||||
@@ -811,6 +811,7 @@ class Channel {
|
|||||||
Future<SendReactionResponse> sendReaction(
|
Future<SendReactionResponse> sendReaction(
|
||||||
Message message,
|
Message message,
|
||||||
String type, {
|
String type, {
|
||||||
|
int score = 1,
|
||||||
Map<String, Object?> extraData = const {},
|
Map<String, Object?> extraData = const {},
|
||||||
bool enforceUnique = false,
|
bool enforceUnique = false,
|
||||||
}) async {
|
}) async {
|
||||||
@@ -829,7 +830,7 @@ class Channel {
|
|||||||
createdAt: now,
|
createdAt: now,
|
||||||
type: type,
|
type: type,
|
||||||
user: user,
|
user: user,
|
||||||
score: 1,
|
score: score,
|
||||||
extraData: extraData,
|
extraData: extraData,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -866,6 +867,7 @@ class Channel {
|
|||||||
final reactionResp = await _client.sendReaction(
|
final reactionResp = await _client.sendReaction(
|
||||||
messageId,
|
messageId,
|
||||||
type,
|
type,
|
||||||
|
score: score,
|
||||||
extraData: extraData,
|
extraData: extraData,
|
||||||
enforceUnique: enforceUnique,
|
enforceUnique: enforceUnique,
|
||||||
);
|
);
|
||||||
@@ -1322,7 +1324,8 @@ class Channel {
|
|||||||
|
|
||||||
/// Remove the ban for the user with given [userID] in the channel.
|
/// Remove the ban for the user with given [userID] in the channel.
|
||||||
@Deprecated(
|
@Deprecated(
|
||||||
"Use 'unbanMember' instead. This method will be removed in v4.0.0")
|
"Use 'unbanMember' instead. This method will be removed in v4.0.0",
|
||||||
|
)
|
||||||
Future<EmptyResponse> unbanUser(String userID) => unbanMember(userID);
|
Future<EmptyResponse> unbanUser(String userID) => unbanMember(userID);
|
||||||
|
|
||||||
/// Remove the ban for the member with given [userID] in the channel.
|
/// Remove the ban for the member with given [userID] in the channel.
|
||||||
|
|||||||
@@ -1073,6 +1073,28 @@ class StreamChatClient {
|
|||||||
Future<UpdateUsersResponse> updateUsers(List<User> users) =>
|
Future<UpdateUsersResponse> updateUsers(List<User> users) =>
|
||||||
_chatApi.user.updateUsers(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
|
/// Bans a user from all channels
|
||||||
Future<EmptyResponse> banUser(
|
Future<EmptyResponse> banUser(
|
||||||
String targetUserId, [
|
String targetUserId, [
|
||||||
@@ -1157,15 +1179,22 @@ class StreamChatClient {
|
|||||||
Future<SendReactionResponse> sendReaction(
|
Future<SendReactionResponse> sendReaction(
|
||||||
String messageId,
|
String messageId,
|
||||||
String reactionType, {
|
String reactionType, {
|
||||||
|
int score = 1,
|
||||||
Map<String, Object?> extraData = const {},
|
Map<String, Object?> extraData = const {},
|
||||||
bool enforceUnique = false,
|
bool enforceUnique = false,
|
||||||
}) =>
|
}) {
|
||||||
_chatApi.message.sendReaction(
|
final _extraData = {
|
||||||
messageId,
|
'score': score,
|
||||||
reactionType,
|
...extraData,
|
||||||
extraData: extraData,
|
};
|
||||||
enforceUnique: enforceUnique,
|
|
||||||
);
|
return _chatApi.message.sendReaction(
|
||||||
|
messageId,
|
||||||
|
reactionType,
|
||||||
|
extraData: _extraData,
|
||||||
|
enforceUnique: enforceUnique,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Delete a [reactionType] from this [messageId]
|
/// Delete a [reactionType] from this [messageId]
|
||||||
Future<EmptyResponse> deleteReaction(
|
Future<EmptyResponse> deleteReaction(
|
||||||
|
|||||||
@@ -156,3 +156,29 @@ class PaginationParams extends Equatable {
|
|||||||
lessThanOrEqual,
|
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);
|
writeNotNull('id_lte', instance.lessThanOrEqual);
|
||||||
return val;
|
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);
|
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,
|
DioError err,
|
||||||
ErrorInterceptorHandler handler,
|
ErrorInterceptorHandler handler,
|
||||||
) async {
|
) async {
|
||||||
ErrorResponse? error;
|
|
||||||
final data = err.response?.data;
|
final data = err.response?.data;
|
||||||
if (data != null) error = ErrorResponse.fromJson(data);
|
if (data == null || data is! Map<String, dynamic>) {
|
||||||
if (error?.code == ChatErrorCode.tokenExpired.code) {
|
return handler.next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
final error = ErrorResponse.fromJson(data);
|
||||||
|
if (error.code == ChatErrorCode.tokenExpired.code) {
|
||||||
if (_tokenManager.isStatic) return handler.next(err);
|
if (_tokenManager.isStatic) return handler.next(err);
|
||||||
_client.lock();
|
_client.lock();
|
||||||
await _tokenManager.loadToken(refresh: true);
|
await _tokenManager.loadToken(refresh: true);
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ ChannelState _$ChannelStateFromJson(Map<String, dynamic> json) => ChannelState(
|
|||||||
pinnedMessages: (json['pinned_messages'] as List<dynamic>?)
|
pinnedMessages: (json['pinned_messages'] as List<dynamic>?)
|
||||||
?.map((e) => Message.fromJson(e as Map<String, dynamic>))
|
?.map((e) => Message.fromJson(e as Map<String, dynamic>))
|
||||||
.toList() ??
|
.toList() ??
|
||||||
const [],
|
_emptyPinnedMessages,
|
||||||
watcherCount: json['watcher_count'] as int?,
|
watcherCount: json['watcher_count'] as int?,
|
||||||
watchers: (json['watchers'] as List<dynamic>?)
|
watchers: (json['watchers'] as List<dynamic>?)
|
||||||
?.map((e) => User.fromJson(e as Map<String, dynamic>))
|
?.map((e) => User.fromJson(e as Map<String, dynamic>))
|
||||||
|
|||||||
@@ -1116,6 +1116,135 @@ void main() {
|
|||||||
verify(() => client.sendReaction(message.id, type)).called(1);
|
verify(() => client.sendReaction(message.id, type)).called(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should work fine with score passed explicitly', () async {
|
||||||
|
const type = 'test-reaction-type';
|
||||||
|
final message = Message(id: 'test-message-id');
|
||||||
|
|
||||||
|
const score = 5;
|
||||||
|
final reaction = Reaction(
|
||||||
|
type: type,
|
||||||
|
messageId: message.id,
|
||||||
|
score: score,
|
||||||
|
);
|
||||||
|
|
||||||
|
when(() => client.sendReaction(
|
||||||
|
message.id,
|
||||||
|
type,
|
||||||
|
score: score,
|
||||||
|
)).thenAnswer(
|
||||||
|
(_) async => SendReactionResponse()
|
||||||
|
..message = message
|
||||||
|
..reaction = reaction,
|
||||||
|
);
|
||||||
|
|
||||||
|
expectLater(
|
||||||
|
// skipping first seed message list -> [] messages
|
||||||
|
channel.state?.messagesStream.skip(1),
|
||||||
|
emitsInOrder([
|
||||||
|
[
|
||||||
|
isSameMessageAs(
|
||||||
|
message.copyWith(
|
||||||
|
status: MessageSendingStatus.sent,
|
||||||
|
reactionCounts: {type: 1},
|
||||||
|
reactionScores: {type: score},
|
||||||
|
latestReactions: [reaction],
|
||||||
|
ownReactions: [reaction],
|
||||||
|
),
|
||||||
|
matchReactions: true,
|
||||||
|
matchSendingStatus: true,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
final res = await channel.sendReaction(
|
||||||
|
message,
|
||||||
|
type,
|
||||||
|
score: score,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
expect(res.reaction.type, type);
|
||||||
|
expect(res.reaction.messageId, message.id);
|
||||||
|
expect(res.reaction.score, score);
|
||||||
|
|
||||||
|
verify(() => client.sendReaction(
|
||||||
|
message.id,
|
||||||
|
type,
|
||||||
|
score: score,
|
||||||
|
)).called(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should work fine with score passed explicitly and in extraData',
|
||||||
|
() async {
|
||||||
|
const type = 'test-reaction-type';
|
||||||
|
final message = Message(id: 'test-message-id');
|
||||||
|
|
||||||
|
const score = 5;
|
||||||
|
const extraDataScore = 3;
|
||||||
|
const extraData = {
|
||||||
|
'score': extraDataScore,
|
||||||
|
};
|
||||||
|
final reaction = Reaction(
|
||||||
|
type: type,
|
||||||
|
messageId: message.id,
|
||||||
|
score: extraDataScore,
|
||||||
|
);
|
||||||
|
|
||||||
|
when(() => client.sendReaction(
|
||||||
|
message.id,
|
||||||
|
type,
|
||||||
|
score: score,
|
||||||
|
extraData: extraData,
|
||||||
|
)).thenAnswer(
|
||||||
|
(_) async => SendReactionResponse()
|
||||||
|
..message = message
|
||||||
|
..reaction = reaction,
|
||||||
|
);
|
||||||
|
|
||||||
|
expectLater(
|
||||||
|
// skipping first seed message list -> [] messages
|
||||||
|
channel.state?.messagesStream.skip(1),
|
||||||
|
emitsInOrder([
|
||||||
|
[
|
||||||
|
isSameMessageAs(
|
||||||
|
message.copyWith(
|
||||||
|
status: MessageSendingStatus.sent,
|
||||||
|
reactionCounts: {type: 1},
|
||||||
|
reactionScores: {type: extraDataScore},
|
||||||
|
latestReactions: [reaction],
|
||||||
|
ownReactions: [reaction],
|
||||||
|
),
|
||||||
|
matchReactions: true,
|
||||||
|
matchSendingStatus: true,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
final res = await channel.sendReaction(
|
||||||
|
message,
|
||||||
|
type,
|
||||||
|
score: score,
|
||||||
|
extraData: extraData,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
expect(res.reaction.type, type);
|
||||||
|
expect(res.reaction.messageId, message.id);
|
||||||
|
expect(
|
||||||
|
res.reaction.score,
|
||||||
|
extraDataScore,
|
||||||
|
);
|
||||||
|
|
||||||
|
verify(() => client.sendReaction(
|
||||||
|
message.id,
|
||||||
|
type,
|
||||||
|
score: score,
|
||||||
|
extraData: extraData,
|
||||||
|
)).called(1);
|
||||||
|
});
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'should restore previous message if `client.sendReaction` throws',
|
'should restore previous message if `client.sendReaction` throws',
|
||||||
() async {
|
() async {
|
||||||
|
|||||||
@@ -1772,6 +1772,46 @@ void main() {
|
|||||||
verifyNoMoreInteractions(api.user);
|
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 {
|
test('`.banUser`', () async {
|
||||||
const userId = 'test-user-id';
|
const userId = 'test-user-id';
|
||||||
|
|
||||||
@@ -1956,23 +1996,109 @@ void main() {
|
|||||||
verifyNoMoreInteractions(api.channel);
|
verifyNoMoreInteractions(api.channel);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('`.sendReaction`', () async {
|
group('`.sendReaction`', () {
|
||||||
const messageId = 'test-message-id';
|
test('`.sendReaction with default params`', () async {
|
||||||
const reactionType = 'like';
|
const messageId = 'test-message-id';
|
||||||
|
const reactionType = 'like';
|
||||||
|
const extraData = {'score': 1};
|
||||||
|
|
||||||
when(() => api.message.sendReaction(messageId, reactionType))
|
when(() => api.message.sendReaction(
|
||||||
.thenAnswer((_) async => SendReactionResponse()
|
messageId,
|
||||||
..message = Message(id: messageId)
|
reactionType,
|
||||||
..reaction = Reaction(type: reactionType, messageId: messageId));
|
extraData: extraData,
|
||||||
|
)).thenAnswer((_) async => SendReactionResponse()
|
||||||
|
..message = Message(id: messageId)
|
||||||
|
..reaction = Reaction(type: reactionType, messageId: messageId));
|
||||||
|
|
||||||
final res = await client.sendReaction(messageId, reactionType);
|
final res = await client.sendReaction(messageId, reactionType);
|
||||||
expect(res, isNotNull);
|
expect(res, isNotNull);
|
||||||
expect(res.message.id, messageId);
|
expect(res.message.id, messageId);
|
||||||
expect(res.reaction.type, reactionType);
|
expect(res.reaction.type, reactionType);
|
||||||
expect(res.reaction.messageId, messageId);
|
expect(res.reaction.messageId, messageId);
|
||||||
|
|
||||||
verify(() => api.message.sendReaction(messageId, reactionType)).called(1);
|
verify(() => api.message.sendReaction(
|
||||||
verifyNoMoreInteractions(api.message);
|
messageId,
|
||||||
|
reactionType,
|
||||||
|
extraData: extraData,
|
||||||
|
)).called(1);
|
||||||
|
verifyNoMoreInteractions(api.message);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('`.sendReaction with score`', () async {
|
||||||
|
const messageId = 'test-message-id';
|
||||||
|
const reactionType = 'like';
|
||||||
|
const score = 3;
|
||||||
|
const extraData = {'score': score};
|
||||||
|
|
||||||
|
when(() => api.message.sendReaction(
|
||||||
|
messageId,
|
||||||
|
reactionType,
|
||||||
|
extraData: extraData,
|
||||||
|
)).thenAnswer((_) async => SendReactionResponse()
|
||||||
|
..message = Message(id: messageId)
|
||||||
|
..reaction = Reaction(
|
||||||
|
type: reactionType,
|
||||||
|
messageId: messageId,
|
||||||
|
score: score,
|
||||||
|
));
|
||||||
|
|
||||||
|
final res = await client.sendReaction(
|
||||||
|
messageId,
|
||||||
|
reactionType,
|
||||||
|
score: score,
|
||||||
|
);
|
||||||
|
expect(res, isNotNull);
|
||||||
|
expect(res.message.id, messageId);
|
||||||
|
expect(res.reaction.type, reactionType);
|
||||||
|
expect(res.reaction.messageId, messageId);
|
||||||
|
expect(res.reaction.score, score);
|
||||||
|
|
||||||
|
verify(() => api.message.sendReaction(
|
||||||
|
messageId,
|
||||||
|
reactionType,
|
||||||
|
extraData: extraData,
|
||||||
|
)).called(1);
|
||||||
|
verifyNoMoreInteractions(api.message);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('`.sendReaction with score passed in extradata also`', () async {
|
||||||
|
const messageId = 'test-message-id';
|
||||||
|
const reactionType = 'like';
|
||||||
|
const score = 3;
|
||||||
|
const extraDataScore = 5;
|
||||||
|
const extraData = {'score': extraDataScore};
|
||||||
|
|
||||||
|
when(() => api.message.sendReaction(
|
||||||
|
messageId,
|
||||||
|
reactionType,
|
||||||
|
extraData: extraData,
|
||||||
|
)).thenAnswer((_) async => SendReactionResponse()
|
||||||
|
..message = Message(id: messageId)
|
||||||
|
..reaction = Reaction(
|
||||||
|
type: reactionType,
|
||||||
|
messageId: messageId,
|
||||||
|
score: extraDataScore,
|
||||||
|
));
|
||||||
|
|
||||||
|
final res = await client.sendReaction(
|
||||||
|
messageId,
|
||||||
|
reactionType,
|
||||||
|
score: score,
|
||||||
|
extraData: extraData,
|
||||||
|
);
|
||||||
|
expect(res, isNotNull);
|
||||||
|
expect(res.message.id, messageId);
|
||||||
|
expect(res.reaction.type, reactionType);
|
||||||
|
expect(res.reaction.messageId, messageId);
|
||||||
|
expect(res.reaction.score, extraDataScore);
|
||||||
|
|
||||||
|
verify(() => api.message.sendReaction(
|
||||||
|
messageId,
|
||||||
|
reactionType,
|
||||||
|
extraData: extraData,
|
||||||
|
)).called(1);
|
||||||
|
verifyNoMoreInteractions(api.message);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('`.deleteReaction`', () async {
|
test('`.deleteReaction`', () async {
|
||||||
|
|||||||
@@ -175,14 +175,14 @@ void main() {
|
|||||||
|
|
||||||
test('markAllRead', () async {
|
test('markAllRead', () async {
|
||||||
const path = '/channels/read';
|
const path = '/channels/read';
|
||||||
when(() => client.post(path)).thenAnswer(
|
when(() => client.post(path, data: {})).thenAnswer(
|
||||||
(_) async => successResponse(path, data: <String, dynamic>{}));
|
(_) async => successResponse(path, data: <String, dynamic>{}));
|
||||||
|
|
||||||
final res = await channelApi.markAllRead();
|
final res = await channelApi.markAllRead();
|
||||||
|
|
||||||
expect(res, isNotNull);
|
expect(res, isNotNull);
|
||||||
|
|
||||||
verify(() => client.post(path)).called(1);
|
verify(() => client.post(path, data: {})).called(1);
|
||||||
verifyNoMoreInteractions(client);
|
verifyNoMoreInteractions(client);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -82,4 +82,37 @@ void main() {
|
|||||||
verify(() => client.post(path, data: any(named: 'data'))).called(1);
|
verify(() => client.post(path, data: any(named: 'data'))).called(1);
|
||||||
verifyNoMoreInteractions(client);
|
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);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,11 @@
|
|||||||
|
## Upcoming
|
||||||
|
|
||||||
|
🐞 Fixed
|
||||||
|
|
||||||
|
- [[#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
|
## 3.4.0
|
||||||
- Updated `stream_chat_flutter_core` dependency to [`3.4.0`](https://pub.dev/packages/stream_chat_flutter_core/changelog).
|
- Updated `stream_chat_flutter_core` dependency to [`3.4.0`](https://pub.dev/packages/stream_chat_flutter_core/changelog).
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
archiveVersion = 1;
|
archiveVersion = 1;
|
||||||
classes = {
|
classes = {
|
||||||
};
|
};
|
||||||
objectVersion = 46;
|
objectVersion = 50;
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
@@ -156,7 +156,7 @@
|
|||||||
97C146E61CF9000F007C117D /* Project object */ = {
|
97C146E61CF9000F007C117D /* Project object */ = {
|
||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
LastUpgradeCheck = 1020;
|
LastUpgradeCheck = 1300;
|
||||||
ORGANIZATIONNAME = "";
|
ORGANIZATIONNAME = "";
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
97C146ED1CF9000F007C117D = {
|
97C146ED1CF9000F007C117D = {
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "1020"
|
LastUpgradeVersion = "1300"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
|
|||||||
@@ -1816,6 +1816,8 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
|
|
||||||
_mentionedUsers.clear();
|
_mentionedUsers.clear();
|
||||||
|
|
||||||
|
message = _replaceUserNameWithId(message);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Future sendingFuture;
|
Future sendingFuture;
|
||||||
if (widget.editMessage == null ||
|
if (widget.editMessage == null ||
|
||||||
@@ -2081,3 +2083,21 @@ class _CountdownButton extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Message _replaceUserNameWithId(Message message) {
|
||||||
|
final mentionedUsers = message.mentionedUsers;
|
||||||
|
if (mentionedUsers.isEmpty) return message;
|
||||||
|
|
||||||
|
var messageTextToSend = message.text;
|
||||||
|
if (messageTextToSend == null) return message;
|
||||||
|
|
||||||
|
for (final user in mentionedUsers.toSet()) {
|
||||||
|
final userName = user.name;
|
||||||
|
messageTextToSend = messageTextToSend!.replaceAll(
|
||||||
|
'@$userName',
|
||||||
|
'@${user.id}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return message.copyWith(text: messageTextToSend);
|
||||||
|
}
|
||||||
|
|||||||
@@ -712,20 +712,21 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
BetterStreamBuilder<bool>(
|
if (widget.showScrollToBottom)
|
||||||
stream: streamChannel!.channel.state!.isUpToDateStream,
|
BetterStreamBuilder<bool>(
|
||||||
initialData: streamChannel!.channel.state!.isUpToDate,
|
stream: streamChannel!.channel.state!.isUpToDateStream,
|
||||||
builder: (context, snapshot) => ValueListenableBuilder<bool>(
|
initialData: streamChannel!.channel.state!.isUpToDate,
|
||||||
valueListenable: _showScrollToBottom,
|
builder: (context, snapshot) => ValueListenableBuilder<bool>(
|
||||||
child: _buildScrollToBottom(),
|
valueListenable: _showScrollToBottom,
|
||||||
builder: (context, value, child) {
|
child: _buildScrollToBottom(),
|
||||||
if (!snapshot || value) {
|
builder: (context, value, child) {
|
||||||
return child!;
|
if (!snapshot || value) {
|
||||||
}
|
return child!;
|
||||||
return const Offstage();
|
}
|
||||||
},
|
return const Offstage();
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
if (widget.showFloatingDateDivider)
|
if (widget.showFloatingDateDivider)
|
||||||
_buildFloatingDateDivider(itemCount),
|
_buildFloatingDateDivider(itemCount),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -84,9 +84,10 @@ class MessageText extends StatelessWidget {
|
|||||||
String _replaceMentions(String text) {
|
String _replaceMentions(String text) {
|
||||||
var messageTextToRender = text;
|
var messageTextToRender = text;
|
||||||
for (final user in message.mentionedUsers.toSet()) {
|
for (final user in message.mentionedUsers.toSet()) {
|
||||||
|
final userId = user.id;
|
||||||
final userName = user.name;
|
final userName = user.name;
|
||||||
messageTextToRender = messageTextToRender.replaceAll(
|
messageTextToRender = messageTextToRender.replaceAll(
|
||||||
'@$userName',
|
'@$userId',
|
||||||
'[@$userName](@${userName.replaceAll(' ', '')})',
|
'[@$userName](@${userName.replaceAll(' ', '')})',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ environment:
|
|||||||
dependencies:
|
dependencies:
|
||||||
cached_network_image: ^3.0.0
|
cached_network_image: ^3.0.0
|
||||||
characters: ^1.1.0
|
characters: ^1.1.0
|
||||||
chewie: ^1.2.0
|
chewie: ^1.3.0
|
||||||
collection: ^1.15.0
|
collection: ^1.15.0
|
||||||
diacritic: ^0.1.3
|
diacritic: ^0.1.3
|
||||||
dio: ^4.0.0
|
dio: ^4.0.0
|
||||||
|
|||||||
Reference in New Issue
Block a user