Skip to content

Commit 239ad76

Browse files
authored
fix(llc): unread count not updating for newly joined channels (#2217)
1 parent 5833912 commit 239ad76

File tree

3 files changed

+321
-131
lines changed

3 files changed

+321
-131
lines changed

packages/stream_chat/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
- `channel.deleteDraft`: Deletes a draft message for a specific channel.
1111
- `client.queryDrafts`: Queries draft messages created by the current user.
1212

13+
🐞 Fixed
14+
15+
- Fixed `channelState.unreadCount` not updating if the user is not part of the read list.
16+
1317
🔄 Changed
1418

1519
- Improved read event handling in the `Channel` class to properly update unread state information.

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

Lines changed: 69 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1611,9 +1611,6 @@ class Channel {
16111611
/// read from a particular message onwards.
16121612
Future<EmptyResponse> markRead({String? messageId}) async {
16131613
_checkInitialized();
1614-
client.state.totalUnreadCount =
1615-
max(0, (client.state.totalUnreadCount) - (state!.unreadCount));
1616-
state!.unreadCount = 0;
16171614
return _client.markChannelRead(id!, type, messageId: messageId);
16181615
}
16191616

@@ -1623,29 +1620,7 @@ class Channel {
16231620
/// to be marked as unread.
16241621
Future<EmptyResponse> markUnread(String messageId) async {
16251622
_checkInitialized();
1626-
1627-
final response = await _client.markChannelUnread(id!, type, messageId);
1628-
1629-
final lastReadDate = state!.currentUserRead?.lastRead;
1630-
final currentUnread = state!.currentUserRead?.unreadMessages ?? 0;
1631-
1632-
final messagesFromMarked = state!.messages
1633-
.where((message) => message.user?.id != client.state.currentUser?.id)
1634-
.skipWhile((message) => message.id != messageId)
1635-
.toList();
1636-
final channelUnreadCount = max(currentUnread, messagesFromMarked.length);
1637-
final additionalTotalUnreadCount = currentUnread > 0
1638-
? messagesFromMarked
1639-
.takeWhile((message) =>
1640-
lastReadDate == null ||
1641-
message.createdAt.isBefore(lastReadDate))
1642-
.length
1643-
: messagesFromMarked.length;
1644-
1645-
client.state.totalUnreadCount += additionalTotalUnreadCount;
1646-
state!.unreadCount = channelUnreadCount;
1647-
1648-
return response;
1623+
return _client.markChannelUnread(id!, type, messageId);
16491624
}
16501625

16511626
/// Mark the thread with [threadId] in the channel as read.
@@ -2750,16 +2725,21 @@ class ChannelClientState {
27502725
EventType.notificationMessageNew,
27512726
)
27522727
.listen((event) {
2753-
final message = event.message!;
2754-
final showInChannel =
2755-
message.parentId != null && message.showInChannel != true;
2756-
if (isUpToDate || showInChannel) {
2728+
final message = event.message;
2729+
if (message == null) return;
2730+
2731+
final isThreadMessage = message.parentId != null;
2732+
final isShownInChannel = message.showInChannel == true;
2733+
final isThreadOnlyMessage = isThreadMessage && !isShownInChannel;
2734+
2735+
// Only add the message if the channel is upToDate or if the message is
2736+
// a thread-only message.
2737+
if (isUpToDate || isThreadOnlyMessage) {
27572738
updateMessage(message);
27582739
}
27592740

2760-
if (_countMessageAsUnread(message)) {
2761-
unreadCount += 1;
2762-
}
2741+
// Otherwise, check if we can count the message as unread.
2742+
if (_countMessageAsUnread(message)) unreadCount += 1;
27632743
}));
27642744
}
27652745

@@ -2783,6 +2763,24 @@ class ChannelClientState {
27832763
return true;
27842764
}
27852765

2766+
/// Updates the [read] in the state if it exists. Adds it otherwise.
2767+
void updateRead([Iterable<Read>? read]) {
2768+
final existingReads = <Read>[...?channelState.read];
2769+
final updatedReads = <Read>[
2770+
...existingReads.merge(
2771+
read,
2772+
key: (read) => read.user.id,
2773+
update: (original, updated) => updated,
2774+
),
2775+
];
2776+
2777+
updateChannelState(
2778+
channelState.copyWith(
2779+
read: updatedReads,
2780+
),
2781+
);
2782+
}
2783+
27862784
/// Updates the [message] in the state if it exists. Adds it otherwise.
27872785
void updateMessage(Message message) {
27882786
// Determine if the message should be displayed in the channel view.
@@ -2947,35 +2945,23 @@ class ChannelClientState {
29472945
_subscriptions
29482946
..add(
29492947
_channel
2950-
.on(EventType.messageRead, EventType.notificationMarkRead)
2948+
.on(
2949+
EventType.messageRead,
2950+
EventType.notificationMarkRead,
2951+
)
29512952
.listen(
29522953
(event) {
29532954
final user = event.user;
29542955
if (user == null) return;
29552956

2956-
final existingRead = [...?channelState.read];
2957-
// Return if the user does not have a existing read.
2958-
if (!existingRead.any((r) => r.user.id == user.id)) return;
2959-
2960-
Read? maybeUpdateRead(Read? existingRead) {
2961-
if (existingRead == null) return null;
2962-
if (existingRead.user.id == user.id) {
2963-
return Read(
2964-
user: user,
2965-
lastRead: event.createdAt,
2966-
unreadMessages: event.unreadMessages,
2967-
lastReadMessageId: event.lastReadMessageId,
2968-
);
2969-
}
2970-
2971-
return existingRead;
2972-
}
2973-
2974-
updateChannelState(
2975-
channelState.copyWith(
2976-
read: [...existingRead.map(maybeUpdateRead).nonNulls],
2977-
),
2957+
final updatedRead = Read(
2958+
user: user,
2959+
lastRead: event.createdAt,
2960+
unreadMessages: event.unreadMessages,
2961+
lastReadMessageId: event.lastReadMessageId,
29782962
);
2963+
2964+
return updateRead([updatedRead]);
29792965
},
29802966
),
29812967
)
@@ -2985,29 +2971,14 @@ class ChannelClientState {
29852971
final user = event.user;
29862972
if (user == null) return;
29872973

2988-
final existingRead = [...?channelState.read];
2989-
// Return if the user does not have a existing read.
2990-
if (!existingRead.any((r) => r.user.id == user.id)) return;
2991-
2992-
Read? maybeUpdateRead(Read? existingRead) {
2993-
if (existingRead == null) return null;
2994-
if (existingRead.user.id == user.id) {
2995-
return Read(
2996-
user: user,
2997-
lastRead: event.lastReadAt!,
2998-
unreadMessages: event.unreadMessages,
2999-
lastReadMessageId: event.lastReadMessageId,
3000-
);
3001-
}
3002-
3003-
return existingRead;
3004-
}
3005-
3006-
updateChannelState(
3007-
channelState.copyWith(
3008-
read: [...existingRead.map(maybeUpdateRead).nonNulls],
3009-
),
2974+
final updatedRead = Read(
2975+
user: user,
2976+
lastRead: event.lastReadAt!,
2977+
unreadMessages: event.unreadMessages,
2978+
lastReadMessageId: event.lastReadMessageId,
30102979
);
2980+
2981+
return updateRead([updatedRead]);
30112982
},
30122983
),
30132984
);
@@ -3116,26 +3087,32 @@ class ChannelClientState {
31163087

31173088
/// Setter for unread count.
31183089
set unreadCount(int count) {
3119-
final reads = [...read];
3120-
final currentUserReadIndex = reads.indexWhere(_isCurrentUserRead);
3090+
final currentUser = _channel.client.state.currentUser;
3091+
if (currentUser == null) return;
31213092

3122-
if (currentUserReadIndex < 0) return;
3093+
var existingUserRead = currentUserRead;
3094+
if (existingUserRead == null) {
3095+
final lastMessageAt = _channelState.channel?.lastMessageAt;
3096+
existingUserRead = Read(
3097+
user: currentUser,
3098+
lastRead: lastMessageAt ?? DateTime.now(),
3099+
);
3100+
}
31233101

3124-
reads[currentUserReadIndex] =
3125-
reads[currentUserReadIndex].copyWith(unreadMessages: count);
3126-
_channelState = _channelState.copyWith(read: reads);
3102+
return updateRead([existingUserRead.copyWith(unreadMessages: count)]);
31273103
}
31283104

31293105
bool _countMessageAsUnread(Message message) {
3130-
// Don't count if the message is silent or shadowed.
3131-
if (message.silent) return false;
3132-
if (message.shadowed) return false;
3106+
// Don't count if the channel doesn't allow read events.
3107+
if (!_channel.canReceiveReadEvents) return false;
31333108

31343109
// Don't count if the channel is muted.
31353110
if (_channel.isMuted) return false;
31363111

3137-
// Don't count if the channel doesn't allow read events.
3138-
if (!_channel.canReceiveReadEvents) return false;
3112+
// Don't count if the message is silent or shadowed or ephemeral.
3113+
if (message.silent) return false;
3114+
if (message.shadowed) return false;
3115+
if (message.isEphemeral) return false;
31393116

31403117
// Don't count thread replies which are not shown in the channel as unread.
31413118
if (message.parentId != null && message.showInChannel == false) {
@@ -3153,6 +3130,9 @@ class ChannelClientState {
31533130
// Don't count user's own messages as unread.
31543131
if (messageUser.id == currentUser.id) return false;
31553132

3133+
// Don't count restricted messages as unread.
3134+
if (message.isNotVisibleTo(currentUser.id)) return false;
3135+
31563136
// Don't count messages from muted users as unread.
31573137
final isMuted = currentUser.mutes.any((it) => it.user.id == messageUser.id);
31583138
if (isMuted) return false;

0 commit comments

Comments
 (0)