Skip to content

Ignore reactions and the read status of the blocked users#359

Closed
nekohy wants to merge 3 commits into
risin42:devfrom
nekohy:ignore-reaction
Closed

Ignore reactions and the read status of the blocked users#359
nekohy wants to merge 3 commits into
risin42:devfrom
nekohy:ignore-reaction

Conversation

@nekohy
Copy link
Copy Markdown
Contributor

@nekohy nekohy commented May 12, 2026

忽略掉被屏蔽用户的已读状态和点击reaction

Summary by CodeRabbit

  • Bug Fixes

    • Blocked and filtered accounts are now properly excluded from message reaction displays and read/seen participant lists.
  • New Features

    • Reaction lists and counters now omit filtered peers, so tab counters and reaction headers reflect visible users only.
    • Reaction user lists report adjusted counts when blocked/filtered peers are removed.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 12, 2026

Walkthrough

The PR introduces peer-level blocked/filtered checks and reaction-filtering utilities in MessageHelper, then applies this filtering in MessagesController, ReactedHeaderView, ReactedUsersListView, MessageSeenView, and related UI tab counter wiring to exclude blocked/filtered supergroup peers from reaction lists and participant views.

Changes

Blocked/Filtered Peer Filtering in Reactions and Participants

Layer / File(s) Summary
Core blocking/filtering predicates and reaction filtering
tw/nekomimi/nekogram/helpers/MessageHelper.java
Introduces isBlockedOrFilteredPeer(long) peer predicate and rewrites isBlockedOrFiltered(TLRPC.Message) to use it, then adds filterBlockedMessageReactions(TLRPC.TL_messageReactions) to remove blocked/filtered peers from reaction lists and decrement reaction counts via internal helpers.
Message reaction filtering in MessagesController
org/telegram/messenger/MessagesController.java
Adds private filterBlockedMessageReactions(dialogId, reactions) helper that conditionally delegates to MessageHelper when blocking is enabled and the peer is a megagroup. Inserts calls in two TL_updateMessageReactions handling paths before storing/processing reactions.
Reaction header view filtering
org/telegram/ui/Components/ReactedHeaderView.java
Filters blocked/filtered peers from read participants in onAttachedToWindow, adjusts displayed reaction counts after filtering, and skips filtered peers in users and chats reaction loops. Implements filterBlockedReactionPeers and shouldFilterBlockedPeer helpers.
Reaction users list view filtering
org/telegram/ui/Components/ReactedUsersListView.java
Filters blocked/filtered peers in setSeenUsers for both existing reactions and new seen entries. Filters reactions in async response handler before persisting and computes filteredCount reported via a new OnCountChangedListener and setter.
Message seen/played participant view filtering
org/telegram/ui/MessageSeenView.java
Filters blocked/filtered megagroup peers from participant lists by extending skip conditions for TL_readParticipantDate and Long peer participants. Implements shouldFilterBlockedPeer helper delegating to MessageHelper.
Reaction tab counter wiring
org/telegram/ui/ChatActivity.java, org/telegram/ui/Components/ReactionTabHolderView.java
Captures tab index for the count-change lambda and updates ReactionTabHolderView counters using the filtered counts; adds new setCounter(currentAccount, counter, count) overload and delegates existing overload to it.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main changes: filtering blocked users from reactions and read status visibility across multiple UI components and the message helper.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ast-grep (0.42.2)
TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6df3a5db-7bd9-4f29-ae3c-a04c462f8175

📥 Commits

Reviewing files that changed from the base of the PR and between e3e4419 and 56afda9.

📒 Files selected for processing (5)
  • TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java
  • TMessagesProj/src/main/java/org/telegram/ui/Components/ReactedHeaderView.java
  • TMessagesProj/src/main/java/org/telegram/ui/Components/ReactedUsersListView.java
  • TMessagesProj/src/main/java/org/telegram/ui/MessageSeenView.java
  • TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/MessageHelper.java

if (response instanceof TLRPC.TL_messages_messageReactionsList) {
TLRPC.TL_messages_messageReactionsList list = (TLRPC.TL_messages_messageReactionsList) response;
int c = list.count;
int c = Math.max(0, list.count - filterBlockedReactionPeers(list.reactions));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Reaction count is inaccurate — subtracts blocked peers from a 3-item sample, not the total.

getList.limit = 3 at line 234, so list.reactions is capped at 3 items, while list.count is the server's full reaction total (which can be much larger). filterBlockedReactionPeers(list.reactions) therefore returns at most the number of blocked reactors visible in the top-3 sample, not the actual number of blocked reactors on the message. Whenever blocked users have reacted but don't fall within the first 3 returned, the displayed count c still includes them — which defeats this PR's intent for the header count display.

Concrete example: message has 100 reactions including 10 from blocked users, sample contains 0 of them → c = 100, but the intended value is 90.

Possible directions:

  • Derive c from message.messageOwner.reactions.results (aggregated counts) and reconcile against a fully-fetched list of blocked reactors, or
  • Request a larger window (or paginate) before computing c, accepting the bandwidth cost, or
  • Explicitly document that this count is best-effort and the avatar preview is the only filtered view.

Comment on lines +255 to +258
private boolean shouldFilterBlockedPeer(long peerId) {
return peerId != 0 && message != null && message.isSupergroup() &&
MessageHelper.getInstance(currentAccount).isBlockedOrFilteredPeer(peerId);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect MessageHelper for existing reaction/blocked-peer filtering APIs
fd -t f 'MessageHelper.java' | xargs -I {} rg -nP -C2 '\b(isBlockedOrFilteredPeer|filterBlockedMessageReactions|shouldFilterBlockedPeer|filterBlockedReactionPeers)\b' {}

# Locate all current duplicates of these helpers across the codebase
rg -nP -C1 '\b(shouldFilterBlockedPeer|filterBlockedReactionPeers)\s*\(' --type=java

Repository: risin42/NagramX

Length of output: 8095


Consider centralizing shouldFilterBlockedPeer and filterBlockedReactionPeers in MessageHelper.

These helpers are duplicated near-verbatim across ReactedUsersListView.java (lines 255–258 and 357–375) and ReactedHeaderView.java (lines 322–344 and 341–343). Since MessageHelper already hosts related centralized utilities (isBlockedOrFilteredPeer, filterBlockedMessageReactions), consolidating these wrappers there would eliminate duplication and prevent drift in the supergroup gate and iteration semantics.

Suggested approach:

// In MessageHelper
public boolean shouldFilterBlockedPeer(MessageObject message, long peerId) { ... }
public int filterBlockedReactionPeers(MessageObject message, List<TLRPC.MessagePeerReaction> reactions) { ... }

Comment on lines +1058 to +1091
public boolean filterBlockedMessageReactions(TLRPC.TL_messageReactions reactions) {
if (reactions == null || !NekoConfig.ignoreBlocked.Bool()) {
return false;
}
boolean changed = false;
if (reactions.recent_reactions != null) {
for (int i = reactions.recent_reactions.size() - 1; i >= 0; i--) {
TLRPC.MessagePeerReaction reaction = reactions.recent_reactions.get(i);
if (reaction == null || reaction.peer_id == null) {
continue;
}
long peerId = MessageObject.getPeerId(reaction.peer_id);
if (peerId != 0 && isBlockedOrFilteredPeer(peerId)) {
reactions.recent_reactions.remove(i);
decrementReactionCount(reactions, reaction.reaction);
changed = true;
}
}
}
if (reactions.top_reactors != null) {
for (int i = reactions.top_reactors.size() - 1; i >= 0; i--) {
TLRPC.MessageReactor reactor = reactions.top_reactors.get(i);
if (reactor == null || reactor.peer_id == null) {
continue;
}
long peerId = MessageObject.getPeerId(reactor.peer_id);
if (peerId != 0 && isBlockedOrFilteredPeer(peerId)) {
reactions.top_reactors.remove(i);
changed = true;
}
}
}
return changed;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for TLRPC.MessageReactor and TLRPC.TL_messageReactions definitions
ast-grep --pattern 'class $_ {
  $$$
  top_reactors
  $$$
}'

# Search for usage patterns where top_reactors and recent_reactions are accessed together
rg -nP -A5 -B5 'top_reactors.*recent_reactions|recent_reactions.*top_reactors'

# Look for comments or documentation about the relationship
rg -nP 'top_reactors' --type=java -C3

Repository: risin42/NagramX

Length of output: 14907


🏁 Script executed:

#!/bin/bash
# Find MessageReactor class definition
rg -n "class MessageReactor|class TL_messageReactor" --type=java -A 10

# Find decrementReactionCount method
rg -n "decrementReactionCount" --type=java -A 15

# Check if MessageReactor has a reaction field
rg -n "MessageReactor" --type=java | grep -E "reaction|count" | head -20

Repository: risin42/NagramX

Length of output: 8335


🏁 Script executed:

#!/bin/bash
# Find MessagePeerReaction class definition to compare
rg -n "class MessagePeerReaction" --type=java -A 10

# Check how MessageReactor relates to reactions
rg -n "top_reactors" --type=java -B5 -A5 | grep -E "reaction|count" | head -30

Repository: risin42/NagramX

Length of output: 5648


Inconsistent reaction count handling when filtering top_reactors.

When removing from recent_reactions (line 1072), the code decrements counts via decrementReactionCount. However, when removing from top_reactors (lines 1077-1089), counts are not decremented despite MessageReactor containing a count field.

The root cause is that MessagePeerReaction includes a reaction field (allowing decrementReactionCount to work), while MessageReactor does not. This means if a blocked peer appears only in top_reactors and not in recent_reactions, their reaction count remains in results even though they are removed from the display, causing count mismatches.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e00a47cb-23ce-43aa-950e-a36796ea5823

📥 Commits

Reviewing files that changed from the base of the PR and between 56afda9 and c9ce418.

📒 Files selected for processing (3)
  • TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java
  • TMessagesProj/src/main/java/org/telegram/ui/Components/ReactedUsersListView.java
  • TMessagesProj/src/main/java/org/telegram/ui/Components/ReactionTabHolderView.java

Comment on lines +31986 to +31993
final ReactionTabHolderView tabHolder = (ReactionTabHolderView) tabsView.getChildAt(position);
v.setOnCountChangedListener((view, filteredCount) -> {
if (tabIndex < 0) {
tabHolder.setCounter(filteredCount);
} else {
tabHolder.setCounter(currentAccount, counters.get(tabIndex), filteredCount);
}
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚖️ Poor tradeoff

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check listener cleanup and view recycling patterns in ReactedUsersListView

# Search for setOnCountChangedListener to understand the listener lifecycle
rg -nP --type=java -C5 'setOnCountChangedListener' -g '!**/test/**'

# Check if there's listener cleanup (removeListener, clear, null assignment)
rg -nP --type=java -C3 '\b(remove.*[Ll]istener|clear.*[Ll]istener|OnCountChangedListener.*=\s*null)' -g '!**/test/**' -g '**/ReactedUsersListView.java'

# Check how cachedViews are managed (cleanup, recycling)
rg -nP --type=java -C5 'cachedViews\.(clear|remove)' -g '**/ChatActivity.java'

Repository: risin42/NagramX

Length of output: 2984


🏁 Script executed:

#!/bin/bash

# Get the full method context around lines 31985-31993
echo "=== Full method context (lines 31970-32010) ==="
sed -n '31970,32010p' TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java

# Check what tabsView is and how it's used
echo -e "\n=== Search for tabsView declaration and usage ==="
rg -n 'tabsView\s*=' TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java | head -20

# Check ReactedUsersListView to see how/when the listener callback is invoked
echo -e "\n=== ReactedUsersListView callback invocation ==="
rg -n -A3 'onCountChangedListener' TMessagesProj/src/main/java/org/telegram/ui/Components/ReactedUsersListView.java | grep -A3 'onCountChangedListener\.'

Repository: risin42/NagramX

Length of output: 3310


🏁 Script executed:

#!/bin/bash

# Check how ReactedUsersListView triggers the count changed callback
echo "=== When does onCountChangedListener fire in ReactedUsersListView? ==="
rg -n -B10 'onCountChangedListener.onCountChanged' TMessagesProj/src/main/java/org/telegram/ui/Components/ReactedUsersListView.java | head -40

# Check if there's any cleanup of listeners when views are recycled
echo -e "\n=== View recycling and cleanup in ChatActivity adapter ==="
sed -n '31940,32010p' TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java

# Look for cachedViews.get to see if views are being reused
echo -e "\n=== How cachedViews is used - view reuse pattern ==="
rg -n -B3 -A3 'cachedViews\.get' TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java | head -50

Repository: risin42/NagramX

Length of output: 6612


🏁 Script executed:

#!/bin/bash

# Check if tabsView children are ever modified after initial setup
echo "=== Checking if tabsView is modified after creation ==="
rg -n 'tabsView\.(add|remove|insert|clear|setText)' TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java

# Check the OnPageChangeListener to see if tabs might change during paging
echo -e "\n=== OnPageChangeListener implementation context ==="
sed -n '32005,32050p' TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java

# Check if there are any conditions where position could be invalid or tabs reorganized
echo -e "\n=== Context after line 32010 - view management ==="
sed -n '32010,32080p' TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java

Repository: risin42/NagramX

Length of output: 8775


Capture of view reference in asynchronously-firing listener creates stale reference risk.

The tabHolder is captured from tabsView.getChildAt(position) before the listener is set, but the listener fires asynchronously (via network response in ReactedUsersListView lines 284-292). Combined with view caching in the ViewPager adapter:

  • ReactedUsersListView stores the listener as a field, keeping it alive indefinitely
  • When the network response arrives and the callback fires, it uses the captured tabHolder reference from creation time
  • If the view hierarchy has changed or the view instance has been recycled, counter updates apply to the wrong tab
  • This pattern likely causes the "reaction num display err" mentioned in the commit message

Safer approach: Look up the view inside the lambda when the callback actually fires, rather than capturing the reference beforehand.

Suggested fix
-final int tabIndex = index;
-final ReactionTabHolderView tabHolder = (ReactionTabHolderView) tabsView.getChildAt(position);
-v.setOnCountChangedListener((view, filteredCount) -> {
-    if (tabIndex < 0) {
-        tabHolder.setCounter(filteredCount);
-    } else {
-        tabHolder.setCounter(currentAccount, counters.get(tabIndex), filteredCount);
-    }
-});
+final int tabIndex = index;
+final int tabPosition = position;
+v.setOnCountChangedListener((view, filteredCount) -> {
+    ReactionTabHolderView tabHolder = (ReactionTabHolderView) tabsView.getChildAt(tabPosition);
+    if (tabHolder != null) {
+        if (tabIndex < 0) {
+            tabHolder.setCounter(filteredCount);
+        } else {
+            tabHolder.setCounter(currentAccount, counters.get(tabIndex), filteredCount);
+        }
+    }
+});

Comment on lines +290 to +293
int filteredCount = Math.max(0, res.count - filterBlockedReactionPeers(res.reactions));
if (onCountChangedListener != null) {
onCountChangedListener.onCountChanged(this, filteredCount);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use cumulative blocked-removal when reporting filtered total.

filteredCount subtracts only the blocked peers removed from the current page, so paginated loads can report inflated/unstable tab counts.

Suggested fix
@@
-    private boolean showReactionPreview;
+    private boolean showReactionPreview;
+    private int filteredRemovedTotal;
@@
     private void load() {
         isLoading = true;
+        if (offset == null) {
+            filteredRemovedTotal = 0;
+        }
@@
-                    int filteredCount = Math.max(0, res.count - filterBlockedReactionPeers(res.reactions));
+                    filteredRemovedTotal += filterBlockedReactionPeers(res.reactions);
+                    int filteredCount = Math.max(0, res.count - filteredRemovedTotal);
                     if (onCountChangedListener != null) {
                         onCountChangedListener.onCountChanged(this, filteredCount);
                     }

@risin42 risin42 added the WIP Work in progress label May 16, 2026
@risin42
Copy link
Copy Markdown
Owner

risin42 commented May 19, 2026

Fixed in 1a6ee22

@risin42 risin42 closed this May 19, 2026
@risin42 risin42 removed the WIP Work in progress label May 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants