Skip to content

Commit dd43ac6

Browse files
authored
feat(llc, persistence): add messageCount to channel model and event (#2403)
1 parent e93e223 commit dd43ac6

File tree

13 files changed

+299
-1
lines changed

13 files changed

+299
-1
lines changed

packages/stream_chat/CHANGELOG.md

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

3+
✅ Added
4+
5+
- Added support for `Channel.messageCount` field.
6+
37
🐞 Fixed
48

59
- Fixed thread messages increasing the unread count in the main channel.

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

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,24 @@ class Channel {
416416
return state!.channelStateStream.map((cs) => cs.channel?.memberCount);
417417
}
418418

419+
/// Channel message count.
420+
///
421+
/// Note: This field is only populated if the `count_messages` option is
422+
/// enabled for your app.
423+
int? get messageCount {
424+
_checkInitialized();
425+
return state!._channelState.channel?.messageCount;
426+
}
427+
428+
/// Channel message count as a stream.
429+
///
430+
/// Note: This field is only populated if the `count_messages` option is
431+
/// enabled for your app.
432+
Stream<int?> get messageCountStream {
433+
_checkInitialized();
434+
return state!.channelStateStream.map((cs) => cs.channel?.messageCount);
435+
}
436+
419437
/// Channel id.
420438
String? get id => state?._channelState.channel?.id ?? _id;
421439

@@ -2165,6 +2183,8 @@ class ChannelClientState {
21652183

21662184
_listenChannelUpdated();
21672185

2186+
_listenChannelMessageCount();
2187+
21682188
_listenMemberAdded();
21692189

21702190
_listenMemberRemoved();
@@ -2334,6 +2354,23 @@ class ChannelClientState {
23342354
}));
23352355
}
23362356

2357+
void _listenChannelMessageCount() {
2358+
_subscriptions.add(_channel.on().listen(
2359+
(Event e) {
2360+
final messageCount = e.channelMessageCount;
2361+
if (messageCount == null) return;
2362+
2363+
updateChannelState(
2364+
channelState.copyWith(
2365+
channel: channelState.channel?.copyWith(
2366+
messageCount: messageCount,
2367+
),
2368+
),
2369+
);
2370+
},
2371+
));
2372+
}
2373+
23372374
void _listenChannelTruncated() {
23382375
_subscriptions.add(_channel
23392376
.on(EventType.channelTruncated, EventType.notificationChannelTruncated)

packages/stream_chat/lib/src/core/models/channel_model.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class ChannelModel {
3131
bool? disabled,
3232
bool? hidden,
3333
DateTime? truncatedAt,
34+
this.messageCount,
3435
}) : assert(
3536
(cid != null && cid.contains(':')) || (id != null && type != null),
3637
'provide either a cid or an id and type',
@@ -151,6 +152,13 @@ class ChannelModel {
151152
return null;
152153
}
153154

155+
/// The total number of messages in the channel.
156+
///
157+
/// Note: This field is only populated if the `count_messages` option is
158+
/// enabled for your app.
159+
@JsonKey(includeToJson: false)
160+
final int? messageCount;
161+
154162
/// Known top level fields.
155163
/// Useful for [Serializer] methods.
156164
static const topLevelFields = [
@@ -169,6 +177,7 @@ class ChannelModel {
169177
'members',
170178
'team',
171179
'cooldown',
180+
'message_count',
172181
];
173182

174183
/// Serialize to json
@@ -197,6 +206,7 @@ class ChannelModel {
197206
bool? disabled,
198207
bool? hidden,
199208
DateTime? truncatedAt,
209+
int? messageCount,
200210
}) =>
201211
ChannelModel(
202212
id: id ?? this.id,
@@ -223,6 +233,7 @@ class ChannelModel {
223233
// ignore: cast_nullable_to_non_nullable
224234
: DateTime.parse(extraData?['truncated_at'] as String)) ??
225235
this.truncatedAt,
236+
messageCount: messageCount ?? this.messageCount,
226237
);
227238

228239
/// Returns a new [ChannelModel] that is a combination of this channelModel
@@ -249,6 +260,7 @@ class ChannelModel {
249260
disabled: other.disabled,
250261
hidden: other.hidden,
251262
truncatedAt: other.truncatedAt,
263+
messageCount: other.messageCount,
252264
);
253265
}
254266
}

packages/stream_chat/lib/src/core/models/channel_model.g.dart

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/stream_chat/lib/src/core/models/event.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class Event {
4343
this.reminder,
4444
this.pushPreference,
4545
this.channelPushPreference,
46+
this.channelMessageCount,
4647
this.extraData = const {},
4748
this.isLocal = true,
4849
}) : createdAt = createdAt?.toUtc() ?? DateTime.now().toUtc();
@@ -162,6 +163,9 @@ class Event {
162163
/// Push notification preferences for the current user for this channel.
163164
final ChannelPushPreference? channelPushPreference;
164165

166+
/// The total number of messages in the channel.
167+
final int? channelMessageCount;
168+
165169
/// Map of custom channel extraData
166170
final Map<String, Object?> extraData;
167171

@@ -203,6 +207,7 @@ class Event {
203207
'reminder',
204208
'push_preference',
205209
'channel_push_preference',
210+
'channel_message_count',
206211
];
207212

208213
/// Serialize to json
@@ -246,6 +251,7 @@ class Event {
246251
MessageReminder? reminder,
247252
PushPreference? pushPreference,
248253
ChannelPushPreference? channelPushPreference,
254+
int? channelMessageCount,
249255
Map<String, Object?>? extraData,
250256
}) =>
251257
Event(
@@ -284,6 +290,7 @@ class Event {
284290
pushPreference: pushPreference ?? this.pushPreference,
285291
channelPushPreference:
286292
channelPushPreference ?? this.channelPushPreference,
293+
channelMessageCount: channelMessageCount ?? this.channelMessageCount,
287294
isLocal: isLocal,
288295
extraData: extraData ?? this.extraData,
289296
);

packages/stream_chat/lib/src/core/models/event.g.dart

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/stream_chat/test/src/client/channel_test.dart

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5637,5 +5637,165 @@ void main() {
56375637
},
56385638
);
56395639
});
5640+
5641+
group('Channel message count events', () {
5642+
const channelId = 'test-channel-id';
5643+
const channelType = 'test-channel-type';
5644+
late Channel channel;
5645+
5646+
setUp(() {
5647+
final channelState = _generateChannelState(channelId, channelType);
5648+
channel = Channel.fromState(client, channelState);
5649+
});
5650+
5651+
tearDown(() {
5652+
channel.dispose();
5653+
});
5654+
5655+
test(
5656+
'should update channel messageCount when event contains channelMessageCount',
5657+
() async {
5658+
// Verify initial state - no messageCount
5659+
expect(channel.messageCount, isNull);
5660+
5661+
// Create event with channelMessageCount
5662+
final messageCountEvent = Event(
5663+
cid: channel.cid,
5664+
type: EventType.messageNew,
5665+
channelMessageCount: 42,
5666+
);
5667+
5668+
// Dispatch event
5669+
client.addEvent(messageCountEvent);
5670+
5671+
// Wait for the event to be processed
5672+
await Future.delayed(Duration.zero);
5673+
5674+
// Verify channel messageCount was updated
5675+
expect(channel.messageCount, equals(42));
5676+
},
5677+
);
5678+
5679+
test(
5680+
'should update channel messageCount from message.new and message.deleted events',
5681+
() async {
5682+
// Test with message.new event - count increases
5683+
final messageNewEvent = Event(
5684+
cid: channel.cid,
5685+
type: EventType.messageNew,
5686+
message: Message(
5687+
id: 'new-message-1',
5688+
text: 'Hello world!',
5689+
user: User(id: 'user-1'),
5690+
),
5691+
channelMessageCount: 1,
5692+
);
5693+
5694+
client.addEvent(messageNewEvent);
5695+
await Future.delayed(Duration.zero);
5696+
expect(channel.messageCount, equals(1));
5697+
5698+
// Test with another message.new event - count increases
5699+
final messageNewEvent2 = Event(
5700+
cid: channel.cid,
5701+
type: EventType.messageNew,
5702+
message: Message(
5703+
id: 'new-message-2',
5704+
text: 'Second message',
5705+
user: User(id: 'user-2'),
5706+
),
5707+
channelMessageCount: 2,
5708+
);
5709+
5710+
client.addEvent(messageNewEvent2);
5711+
await Future.delayed(Duration.zero);
5712+
expect(channel.messageCount, equals(2));
5713+
5714+
// Test with message.deleted event - count decreases
5715+
final messageDeletedEvent = Event(
5716+
cid: channel.cid,
5717+
type: EventType.messageDeleted,
5718+
message: Message(
5719+
id: 'new-message-1',
5720+
text: 'Hello world!',
5721+
user: User(id: 'user-1'),
5722+
),
5723+
channelMessageCount: 1,
5724+
);
5725+
5726+
client.addEvent(messageDeletedEvent);
5727+
await Future.delayed(Duration.zero);
5728+
expect(channel.messageCount, equals(1));
5729+
},
5730+
);
5731+
5732+
test(
5733+
'should preserve other channel properties when updating messageCount',
5734+
() async {
5735+
// Set initial channel state with some properties
5736+
final initialChannel = channel.state?.channelState.channel?.copyWith(
5737+
extraData: {'name': 'Test Channel'},
5738+
memberCount: 5,
5739+
frozen: true,
5740+
);
5741+
5742+
if (initialChannel != null) {
5743+
channel.state?.updateChannelState(
5744+
channel.state!.channelState.copyWith(channel: initialChannel),
5745+
);
5746+
}
5747+
5748+
// Verify initial state
5749+
expect(channel.name, 'Test Channel');
5750+
expect(channel.memberCount, equals(5));
5751+
expect(channel.frozen, equals(true));
5752+
expect(channel.messageCount, isNull);
5753+
5754+
// Update messageCount via event
5755+
final messageCountEvent = Event(
5756+
cid: channel.cid,
5757+
type: EventType.messageNew,
5758+
channelMessageCount: 100,
5759+
);
5760+
5761+
client.addEvent(messageCountEvent);
5762+
await Future.delayed(Duration.zero);
5763+
5764+
// Verify messageCount was updated while preserving other properties
5765+
expect(channel.messageCount, equals(100));
5766+
expect(channel.name, 'Test Channel');
5767+
expect(channel.memberCount, equals(5));
5768+
expect(channel.frozen, equals(true));
5769+
},
5770+
);
5771+
5772+
test(
5773+
'should provide messageCountStream for reactive updates',
5774+
() async {
5775+
expectLater(
5776+
channel.messageCountStream.distinct(),
5777+
emitsInOrder([null, 1, 5, 10]),
5778+
);
5779+
5780+
// Update messageCount multiple times
5781+
final counts = [1, 5, 10];
5782+
for (final count in counts) {
5783+
final event = Event(
5784+
cid: channel.cid,
5785+
type: EventType.messageNew,
5786+
message: Message(
5787+
id: 'msg-$count',
5788+
text: 'Message $count',
5789+
user: User(id: 'user-1'),
5790+
),
5791+
channelMessageCount: count,
5792+
);
5793+
5794+
client.addEvent(event);
5795+
await Future.delayed(Duration.zero);
5796+
}
5797+
},
5798+
);
5799+
});
56405800
});
56415801
}

packages/stream_chat_persistence/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## Upcoming
2+
3+
- Added support for `Channel.messageCount` field.
4+
15
## 9.17.0
26

37
- Updated `stream_chat` dependency to [`9.17.0`](https://pub.dev/packages/stream_chat/changelog).

packages/stream_chat_persistence/lib/src/db/drift_chat_database.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ class DriftChatDatabase extends _$DriftChatDatabase {
5555

5656
// you should bump this number whenever you change or add a table definition.
5757
@override
58-
int get schemaVersion => 23;
58+
int get schemaVersion => 24;
5959

6060
@override
6161
MigrationStrategy get migration => MigrationStrategy(

0 commit comments

Comments
 (0)