Skip to content

Commit 7fe34aa

Browse files
authored
feat(llc, ui, persistence)!: add support for reaction emoji_code (#2326)
1 parent 00c0b15 commit 7fe34aa

28 files changed

+819
-551
lines changed

migrations/v10-migration.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88

99
This guide includes breaking changes grouped by release phase:
1010

11+
### 🚧 v10.0.0-beta.4
12+
13+
- [SendReaction](#-sendreaction)
14+
1115
### 🚧 v10.0.0-beta.3
1216

1317
- [AttachmentPickerType](#-attachmentpickertype)
@@ -24,6 +28,65 @@ This guide includes breaking changes grouped by release phase:
2428

2529
---
2630

31+
## 🧪 Migration for v10.0.0-beta.4
32+
33+
### 🛠 SendReaction
34+
35+
#### Key Changes:
36+
37+
- `sendReaction` method now accepts a full `Reaction` object instead of individual parameters.
38+
39+
#### Migration Steps:
40+
41+
**Before:**
42+
```dart
43+
// Using individual parameters
44+
channel.sendReaction(
45+
message,
46+
'like',
47+
score: 1,
48+
extraData: {'custom_field': 'value'},
49+
);
50+
51+
client.sendReaction(
52+
messageId,
53+
'love',
54+
enforceUnique: true,
55+
extraData: {'custom_field': 'value'},
56+
);
57+
```
58+
59+
**After:**
60+
```dart
61+
// Using Reaction object
62+
channel.sendReaction(
63+
message,
64+
Reaction(
65+
type: 'like',
66+
score: 1,
67+
emojiCode: '👍',
68+
extraData: {'custom_field': 'value'},
69+
),
70+
);
71+
72+
client.sendReaction(
73+
messageId,
74+
Reaction(
75+
type: 'love',
76+
emojiCode: '❤️',
77+
extraData: {'custom_field': 'value'},
78+
),
79+
enforceUnique: true,
80+
);
81+
```
82+
83+
> ⚠️ **Important:**
84+
> - The `sendReaction` method now requires a `Reaction` object
85+
> - Optional parameters like `enforceUnique` and `skipPush` remain as method parameters
86+
> - You can now specify custom emoji codes for reactions using the `emojiCode` field
87+
88+
---
89+
2790
## 🧪 Migration for v10.0.0-beta.3
2891

2992
### 🛠 AttachmentPickerType

packages/stream_chat/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
## Upcoming Beta
22

3+
🛑️ Breaking
4+
5+
- **Changed `sendReaction` method signature**: The `sendReaction` method on both `Client` and
6+
`Channel` now accepts a full `Reaction` object instead of individual parameters (`type`, `score`,
7+
`extraData`). This change provides more flexibility and better type safety.
8+
39
✅ Added
410

511
- Added comprehensive location sharing support with static and live location features:

packages/stream_chat/lib/src/client/channel.dart

Lines changed: 42 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1407,28 +1407,17 @@ class Channel {
14071407
/// Set [enforceUnique] to true to remove the existing user reaction.
14081408
Future<SendReactionResponse> sendReaction(
14091409
Message message,
1410-
String type, {
1411-
int score = 1,
1412-
Map<String, Object?> extraData = const {},
1410+
Reaction reaction, {
1411+
bool skipPush = false,
14131412
bool enforceUnique = false,
14141413
}) async {
14151414
_checkInitialized();
1416-
final currentUser = _client.state.currentUser;
1417-
if (currentUser == null) {
1418-
throw StateError(
1419-
'Cannot send reaction: current user is not available. '
1420-
'Ensure the client is connected and a user is set.',
1421-
);
1422-
}
14231415

14241416
final messageId = message.id;
1425-
final reaction = Reaction(
1426-
type: type,
1417+
// ignore: parameter_assignments
1418+
reaction = reaction.copyWith(
14271419
messageId: messageId,
1428-
user: currentUser,
1429-
score: score,
1430-
createdAt: DateTime.timestamp(),
1431-
extraData: extraData,
1420+
user: _client.state.currentUser,
14321421
);
14331422

14341423
final updatedMessage = message.addMyReaction(
@@ -1441,9 +1430,8 @@ class Channel {
14411430
try {
14421431
final reactionResp = await _client.sendReaction(
14431432
messageId,
1444-
reaction.type,
1445-
score: reaction.score,
1446-
extraData: reaction.extraData,
1433+
reaction,
1434+
skipPush: skipPush,
14471435
enforceUnique: enforceUnique,
14481436
);
14491437
return reactionResp;
@@ -1459,6 +1447,8 @@ class Channel {
14591447
Message message,
14601448
Reaction reaction,
14611449
) async {
1450+
_checkInitialized();
1451+
14621452
final updatedMessage = message.deleteMyReaction(
14631453
reactionType: reaction.type,
14641454
);
@@ -2843,24 +2833,25 @@ class ChannelClientState {
28432833
void _listenReactionDeleted() {
28442834
_subscriptions.add(
28452835
_channel.on(EventType.reactionDeleted).listen((event) {
2846-
final eventMessage = event.message;
2847-
if (eventMessage == null) return;
2848-
2849-
final reaction = event.reaction;
2850-
if (reaction == null) return;
2836+
final (eventReaction, eventMessage) = (event.reaction, event.message);
2837+
if (eventReaction == null || eventMessage == null) return;
28512838

28522839
final messageId = eventMessage.id;
28532840
final parentId = eventMessage.parentId;
28542841

28552842
for (final message in [...messages, ...?threads[parentId]]) {
28562843
if (message.id == messageId) {
2857-
final updatedOwnReactions = message.ownReactions?.where((it) {
2858-
return it.userId != reaction.userId || it.type != reaction.type;
2859-
});
2844+
final currentUserId = _channel.client.state.currentUser?.id;
2845+
2846+
final currentMessage = switch (currentUserId) {
2847+
final userId? when userId == eventReaction.userId =>
2848+
message.deleteMyReaction(reactionType: eventReaction.type),
2849+
_ => message,
2850+
};
28602851

28612852
return updateMessage(
28622853
eventMessage.copyWith(
2863-
ownReactions: updatedOwnReactions?.toList(),
2854+
ownReactions: currentMessage.ownReactions,
28642855
),
28652856
);
28662857
}
@@ -2871,17 +2862,25 @@ class ChannelClientState {
28712862

28722863
void _listenReactionNew() {
28732864
_subscriptions.add(_channel.on(EventType.reactionNew).listen((event) {
2874-
final eventMessage = event.message;
2875-
if (eventMessage == null) return;
2865+
final (eventReaction, eventMessage) = (event.reaction, event.message);
2866+
if (eventReaction == null || eventMessage == null) return;
28762867

28772868
final messageId = eventMessage.id;
28782869
final parentId = eventMessage.parentId;
28792870

28802871
for (final message in [...messages, ...?threads[parentId]]) {
28812872
if (message.id == messageId) {
2873+
final currentUserId = _channel.client.state.currentUser?.id;
2874+
2875+
final currentMessage = switch (currentUserId) {
2876+
final userId? when userId == eventReaction.userId =>
2877+
message.addMyReaction(eventReaction),
2878+
_ => message,
2879+
};
2880+
28822881
return updateMessage(
28832882
eventMessage.copyWith(
2884-
ownReactions: message.ownReactions,
2883+
ownReactions: currentMessage.ownReactions,
28852884
),
28862885
);
28872886
}
@@ -2892,17 +2891,26 @@ class ChannelClientState {
28922891
void _listenReactionUpdated() {
28932892
_subscriptions.add(
28942893
_channel.on(EventType.reactionUpdated).listen((event) {
2895-
final eventMessage = event.message;
2896-
if (eventMessage == null) return;
2894+
final (eventReaction, eventMessage) = (event.reaction, event.message);
2895+
if (eventReaction == null || eventMessage == null) return;
28972896

28982897
final messageId = eventMessage.id;
28992898
final parentId = eventMessage.parentId;
29002899

29012900
for (final message in [...messages, ...?threads[parentId]]) {
29022901
if (message.id == messageId) {
2902+
final currentUserId = _channel.client.state.currentUser?.id;
2903+
2904+
final currentMessage = switch (currentUserId) {
2905+
final userId? when userId == eventReaction.userId =>
2906+
// reaction.updated is only called if enforce_unique is true
2907+
message.addMyReaction(eventReaction, enforceUnique: true),
2908+
_ => message,
2909+
};
2910+
29032911
return updateMessage(
29042912
eventMessage.copyWith(
2905-
ownReactions: message.ownReactions,
2913+
ownReactions: currentMessage.ownReactions,
29062914
),
29072915
);
29082916
}

packages/stream_chat/lib/src/client/client.dart

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import 'package:stream_chat/src/core/models/own_user.dart';
3434
import 'package:stream_chat/src/core/models/poll.dart';
3535
import 'package:stream_chat/src/core/models/poll_option.dart';
3636
import 'package:stream_chat/src/core/models/poll_vote.dart';
37+
import 'package:stream_chat/src/core/models/reaction.dart';
3738
import 'package:stream_chat/src/core/models/thread.dart';
3839
import 'package:stream_chat/src/core/models/user.dart';
3940
import 'package:stream_chat/src/core/util/event_controller.dart';
@@ -1587,23 +1588,16 @@ class StreamChatClient {
15871588
/// Set [enforceUnique] to true to remove the existing user reaction
15881589
Future<SendReactionResponse> sendReaction(
15891590
String messageId,
1590-
String reactionType, {
1591-
int score = 1,
1592-
Map<String, Object?> extraData = const {},
1591+
Reaction reaction, {
1592+
bool skipPush = false,
15931593
bool enforceUnique = false,
1594-
}) {
1595-
final _extraData = {
1596-
'score': score,
1597-
...extraData,
1598-
};
1599-
1600-
return _chatApi.message.sendReaction(
1601-
messageId,
1602-
reactionType,
1603-
extraData: _extraData,
1604-
enforceUnique: enforceUnique,
1605-
);
1606-
}
1594+
}) =>
1595+
_chatApi.message.sendReaction(
1596+
messageId,
1597+
reaction,
1598+
skipPush: skipPush,
1599+
enforceUnique: enforceUnique,
1600+
);
16071601

16081602
/// Delete a [reactionType] from this [messageId]
16091603
Future<EmptyResponse> deleteReaction(

packages/stream_chat/lib/src/core/api/message_api.dart

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import 'package:stream_chat/src/core/models/draft.dart';
88
import 'package:stream_chat/src/core/models/draft_message.dart';
99
import 'package:stream_chat/src/core/models/filter.dart';
1010
import 'package:stream_chat/src/core/models/message.dart';
11+
import 'package:stream_chat/src/core/models/reaction.dart';
1112

1213
/// Defines the api dedicated to messages operations
1314
class MessageApi {
@@ -208,19 +209,17 @@ class MessageApi {
208209
/// Set [enforceUnique] to true to remove the existing user reaction
209210
Future<SendReactionResponse> sendReaction(
210211
String messageId,
211-
String reactionType, {
212-
Map<String, Object?> extraData = const {},
212+
Reaction reaction, {
213+
bool skipPush = false,
213214
bool enforceUnique = false,
214215
}) async {
215-
final reaction = Map<String, Object?>.from(extraData)
216-
..addAll({'type': reactionType});
217-
218216
final response = await _client.post(
219217
'/messages/$messageId/reaction',
220-
data: {
221-
'reaction': reaction,
218+
data: json.encode({
219+
'reaction': reaction.toJson(),
220+
'skip_push': skipPush,
222221
'enforce_unique': enforceUnique,
223-
},
222+
}),
224223
);
225224
return SendReactionResponse.fromJson(response.data);
226225
}

0 commit comments

Comments
 (0)