Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/stream_chat/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## Upcoming

✅ Added

- Added support for `Channel.messageCount` field.

## 9.17.0

🐞 Fixed
Expand Down
37 changes: 37 additions & 0 deletions packages/stream_chat/lib/src/client/channel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,24 @@ class Channel {
return state!.channelStateStream.map((cs) => cs.channel?.memberCount);
}

/// Channel message count.
///
/// Note: This field is only populated if the `count_messages` option is
/// enabled for your app.
int? get messageCount {
_checkInitialized();
return state!._channelState.channel?.messageCount;
}

/// Channel message count as a stream.
///
/// Note: This field is only populated if the `count_messages` option is
/// enabled for your app.
Stream<int?> get messageCountStream {
_checkInitialized();
return state!.channelStateStream.map((cs) => cs.channel?.messageCount);
}

/// Channel id.
String? get id => state?._channelState.channel?.id ?? _id;

Expand Down Expand Up @@ -2169,6 +2187,8 @@ class ChannelClientState {

_listenChannelUpdated();

_listenChannelMessageCount();

_listenMemberAdded();

_listenMemberRemoved();
Expand Down Expand Up @@ -2347,6 +2367,23 @@ class ChannelClientState {
}));
}

void _listenChannelMessageCount() {
_subscriptions.add(_channel.on().listen(
(Event e) {
final messageCount = e.channelMessageCount;
if (messageCount == null) return;

updateChannelState(
channelState.copyWith(
channel: channelState.channel?.copyWith(
messageCount: messageCount,
),
),
);
},
));
}

void _listenChannelTruncated() {
_subscriptions.add(_channel
.on(EventType.channelTruncated, EventType.notificationChannelTruncated)
Expand Down
12 changes: 12 additions & 0 deletions packages/stream_chat/lib/src/core/models/channel_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class ChannelModel {
bool? disabled,
bool? hidden,
DateTime? truncatedAt,
this.messageCount,
}) : assert(
(cid != null && cid.contains(':')) || (id != null && type != null),
'provide either a cid or an id and type',
Expand Down Expand Up @@ -151,6 +152,13 @@ class ChannelModel {
return null;
}

/// The total number of messages in the channel.
///
/// Note: This field is only populated if the `count_messages` option is
/// enabled for your app.
@JsonKey(includeToJson: false)
final int? messageCount;

/// Known top level fields.
/// Useful for [Serializer] methods.
static const topLevelFields = [
Expand All @@ -169,6 +177,7 @@ class ChannelModel {
'members',
'team',
'cooldown',
'message_count',
];

/// Serialize to json
Expand Down Expand Up @@ -197,6 +206,7 @@ class ChannelModel {
bool? disabled,
bool? hidden,
DateTime? truncatedAt,
int? messageCount,
}) =>
ChannelModel(
id: id ?? this.id,
Expand All @@ -223,6 +233,7 @@ class ChannelModel {
// ignore: cast_nullable_to_non_nullable
: DateTime.parse(extraData?['truncated_at'] as String)) ??
this.truncatedAt,
messageCount: messageCount ?? this.messageCount,
);

/// Returns a new [ChannelModel] that is a combination of this channelModel
Expand All @@ -249,6 +260,7 @@ class ChannelModel {
disabled: other.disabled,
hidden: other.hidden,
truncatedAt: other.truncatedAt,
messageCount: other.messageCount,
);
}
}
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions packages/stream_chat/lib/src/core/models/event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class Event {
this.reminder,
this.pushPreference,
this.channelPushPreference,
this.channelMessageCount,
this.extraData = const {},
this.isLocal = true,
}) : createdAt = createdAt?.toUtc() ?? DateTime.now().toUtc();
Expand Down Expand Up @@ -162,6 +163,9 @@ class Event {
/// Push notification preferences for the current user for this channel.
final ChannelPushPreference? channelPushPreference;

/// The total number of messages in the channel.
final int? channelMessageCount;

/// Map of custom channel extraData
final Map<String, Object?> extraData;

Expand Down Expand Up @@ -203,6 +207,7 @@ class Event {
'reminder',
'push_preference',
'channel_push_preference',
'channel_message_count',
];

/// Serialize to json
Expand Down Expand Up @@ -246,6 +251,7 @@ class Event {
MessageReminder? reminder,
PushPreference? pushPreference,
ChannelPushPreference? channelPushPreference,
int? channelMessageCount,
Map<String, Object?>? extraData,
}) =>
Event(
Expand Down Expand Up @@ -284,6 +290,7 @@ class Event {
pushPreference: pushPreference ?? this.pushPreference,
channelPushPreference:
channelPushPreference ?? this.channelPushPreference,
channelMessageCount: channelMessageCount ?? this.channelMessageCount,
isLocal: isLocal,
extraData: extraData ?? this.extraData,
);
Expand Down
3 changes: 3 additions & 0 deletions packages/stream_chat/lib/src/core/models/event.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

160 changes: 160 additions & 0 deletions packages/stream_chat/test/src/client/channel_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5637,5 +5637,165 @@ void main() {
},
);
});

group('Channel message count events', () {
const channelId = 'test-channel-id';
const channelType = 'test-channel-type';
late Channel channel;

setUp(() {
final channelState = _generateChannelState(channelId, channelType);
channel = Channel.fromState(client, channelState);
});

tearDown(() {
channel.dispose();
});

test(
'should update channel messageCount when event contains channelMessageCount',
() async {
// Verify initial state - no messageCount
expect(channel.messageCount, isNull);

// Create event with channelMessageCount
final messageCountEvent = Event(
cid: channel.cid,
type: EventType.messageNew,
channelMessageCount: 42,
);

// Dispatch event
client.addEvent(messageCountEvent);

// Wait for the event to be processed
await Future.delayed(Duration.zero);

// Verify channel messageCount was updated
expect(channel.messageCount, equals(42));
},
);

test(
'should update channel messageCount from message.new and message.deleted events',
() async {
// Test with message.new event - count increases
final messageNewEvent = Event(
cid: channel.cid,
type: EventType.messageNew,
message: Message(
id: 'new-message-1',
text: 'Hello world!',
user: User(id: 'user-1'),
),
channelMessageCount: 1,
);

client.addEvent(messageNewEvent);
await Future.delayed(Duration.zero);
expect(channel.messageCount, equals(1));

// Test with another message.new event - count increases
final messageNewEvent2 = Event(
cid: channel.cid,
type: EventType.messageNew,
message: Message(
id: 'new-message-2',
text: 'Second message',
user: User(id: 'user-2'),
),
channelMessageCount: 2,
);

client.addEvent(messageNewEvent2);
await Future.delayed(Duration.zero);
expect(channel.messageCount, equals(2));

// Test with message.deleted event - count decreases
final messageDeletedEvent = Event(
cid: channel.cid,
type: EventType.messageDeleted,
message: Message(
id: 'new-message-1',
text: 'Hello world!',
user: User(id: 'user-1'),
),
channelMessageCount: 1,
);

client.addEvent(messageDeletedEvent);
await Future.delayed(Duration.zero);
expect(channel.messageCount, equals(1));
},
);

test(
'should preserve other channel properties when updating messageCount',
() async {
// Set initial channel state with some properties
final initialChannel = channel.state?.channelState.channel?.copyWith(
extraData: {'name': 'Test Channel'},
memberCount: 5,
frozen: true,
);

if (initialChannel != null) {
channel.state?.updateChannelState(
channel.state!.channelState.copyWith(channel: initialChannel),
);
}

// Verify initial state
expect(channel.name, 'Test Channel');
expect(channel.memberCount, equals(5));
expect(channel.frozen, equals(true));
expect(channel.messageCount, isNull);

// Update messageCount via event
final messageCountEvent = Event(
cid: channel.cid,
type: EventType.messageNew,
channelMessageCount: 100,
);

client.addEvent(messageCountEvent);
await Future.delayed(Duration.zero);

// Verify messageCount was updated while preserving other properties
expect(channel.messageCount, equals(100));
expect(channel.name, 'Test Channel');
expect(channel.memberCount, equals(5));
expect(channel.frozen, equals(true));
},
);

test(
'should provide messageCountStream for reactive updates',
() async {
expectLater(
channel.messageCountStream.distinct(),
emitsInOrder([null, 1, 5, 10]),
);

// Update messageCount multiple times
final counts = [1, 5, 10];
for (final count in counts) {
final event = Event(
cid: channel.cid,
type: EventType.messageNew,
message: Message(
id: 'msg-$count',
text: 'Message $count',
user: User(id: 'user-1'),
),
channelMessageCount: count,
);

client.addEvent(event);
await Future.delayed(Duration.zero);
}
},
);
});
});
}
4 changes: 4 additions & 0 deletions packages/stream_chat_persistence/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## Upcoming

- Added support for `Channel.messageCount` field.

## 9.17.0

- Updated `stream_chat` dependency to [`9.17.0`](https://pub.dev/packages/stream_chat/changelog).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class DriftChatDatabase extends _$DriftChatDatabase {

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

@override
MigrationStrategy get migration => MigrationStrategy(
Expand Down
Loading