Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -11005,6 +11005,17 @@ private String getUserNameForTyping(TLRPC.User user) {
return "";
}

private void filterBlockedMessageReactions(long dialogId, TLRPC.TL_messageReactions reactions) {
if (!NekoConfig.ignoreBlocked.Bool() || reactions == null || dialogId >= 0) {
return;
}
TLRPC.Chat chat = getChat(-dialogId);
if (!ChatObject.isMegagroup(chat)) {
return;
}
MessageHelper.getInstance(currentAccount).filterBlockedMessageReactions(reactions);
}

private void updatePrintingStrings() {
LongSparseArray<LongSparseArray<CharSequence>> newStrings = new LongSparseArray<>();
LongSparseArray<LongSparseArray<Integer>> newTypes = new LongSparseArray<>();
Expand Down Expand Up @@ -18989,6 +19000,7 @@ public boolean processUpdateArray(ArrayList<TLRPC.Update> updates, ArrayList<TLR
} else if (baseUpdate instanceof TLRPC.TL_updateMessageReactions) {
TLRPC.TL_updateMessageReactions update = (TLRPC.TL_updateMessageReactions) baseUpdate;
long dialogId = MessageObject.getPeerId(update.peer);
filterBlockedMessageReactions(dialogId, update.reactions);

getMessagesStorage().updateMessageReactions(dialogId, update.msg_id, update.reactions);
if (NaConfig.INSTANCE.getSaveLocalLastSeen().Bool()) {
Expand Down Expand Up @@ -19948,6 +19960,7 @@ public boolean processUpdateArray(ArrayList<TLRPC.Update> updates, ArrayList<TLR
} else if (baseUpdate instanceof TLRPC.TL_updateMessageReactions) {
TLRPC.TL_updateMessageReactions update = (TLRPC.TL_updateMessageReactions) baseUpdate;
long dialogId = MessageObject.getPeerId(update.peer);
filterBlockedMessageReactions(dialogId, update.reactions);
long pendingPaid = StarsController.getInstance(currentAccount).getPendingPaidReactions(dialogId, update.msg_id);
if (pendingPaid != 0) {
final StarsController starsController = StarsController.getInstance(currentAccount);
Expand Down
9 changes: 9 additions & 0 deletions TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -31982,6 +31982,15 @@ public void dismiss() {
v.setPredictiveCount(finalCount);
reactedView.setSeenCallback(v::setSeenUsers);
}
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);
}
});
Comment on lines +31986 to +31993
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);
+        }
+    }
+});


container.addView(v);
cachedViews.put(position, v);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
import org.telegram.tgnet.Vector;
import org.telegram.ui.ActionBar.Theme;

import tw.nekomimi.nekogram.helpers.MessageHelper;

import java.util.ArrayList;
import java.util.List;

Expand Down Expand Up @@ -142,14 +144,14 @@ protected void onAttachedToWindow() {
for (Object obj : v.objects) {
if (obj instanceof Long) {
long l = (long) obj;
if (fromId != l) {
if (fromId != l && !shouldFilterBlockedPeer(l)) {
usersToRequest.add(l);
dates.add(0);
}
} else if (obj instanceof TLRPC.TL_readParticipantDate) {
long userId = ((TLRPC.TL_readParticipantDate) obj).user_id;
int date = ((TLRPC.TL_readParticipantDate) obj).date;
if (fromId != userId) {
if (fromId != userId && !shouldFilterBlockedPeer(userId)) {
usersToRequest.add(userId);
dates.add(date);
}
Expand Down Expand Up @@ -235,7 +237,7 @@ private void loadReactions() {
ConnectionsManager.getInstance(currentAccount).sendRequest(getList, (response, error) -> {
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.

int ic = list.users.size();
LastSeenHelper.saveLastSeenFromPeerReactions(list.reactions, UserConfig.getInstance(currentAccount).getClientUserId());
post(() -> {
Expand Down Expand Up @@ -277,6 +279,9 @@ private void loadReactions() {
iconView.animate().alpha(1f).start();
}
for (TLRPC.User u : list.users) {
if (shouldFilterBlockedPeer(u.id)) {
continue;
}
if (message.messageOwner.from_id != null && u.id != message.messageOwner.from_id.user_id) {
boolean hasSame = false;
for (int i = 0; i < users.size(); i++) {
Expand All @@ -291,6 +296,9 @@ private void loadReactions() {
}
}
for (TLRPC.Chat u : list.chats) {
if (shouldFilterBlockedPeer(-u.id)) {
continue;
}
if (message.messageOwner.from_id != null && u.id != message.messageOwner.from_id.user_id) {
boolean hasSame = false;
for (int i = 0; i < users.size(); i++) {
Expand All @@ -311,6 +319,30 @@ private void loadReactions() {
}, ConnectionsManager.RequestFlagInvokeAfter);
}

private int filterBlockedReactionPeers(List<TLRPC.MessagePeerReaction> reactions) {
if (reactions == null || reactions.isEmpty()) {
return 0;
}
int removed = 0;
for (int i = reactions.size() - 1; i >= 0; i--) {
TLRPC.MessagePeerReaction reaction = reactions.get(i);
if (reaction == null || reaction.peer_id == null) {
continue;
}
long peerId = MessageObject.getPeerId(reaction.peer_id);
if (shouldFilterBlockedPeer(peerId)) {
reactions.remove(i);
removed++;
}
}
return removed;
}

private boolean shouldFilterBlockedPeer(long peerId) {
return peerId != 0 && message != null && message.isSupergroup() &&
MessageHelper.getInstance(currentAccount).isBlockedOrFilteredPeer(peerId);
}

public List<UserSeen> getSeenUsers() {
return seenUsers;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
import org.telegram.ui.Cells.ReactedUserHolderView;
import org.telegram.ui.Components.Reactions.ReactionsLayoutInBubble;

import tw.nekomimi.nekogram.helpers.MessageHelper;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
Expand Down Expand Up @@ -64,6 +66,7 @@ public class ReactedUsersListView extends FrameLayout {
private OnHeightChangedListener onHeightChangedListener;
private OnProfileSelectedListener onProfileSelectedListener;
private OnCustomEmojiSelectedListener onCustomEmojiSelectedListener;
private OnCountChangedListener onCountChangedListener;
ArrayList<ReactionsLayoutInBubble.VisibleReaction> customReactionsEmoji = new ArrayList<>();
ArrayList<TLRPC.InputStickerSet> customEmojiStickerSets = new ArrayList<>();
MessageContainsEmojiButton messageContainsEmojiButton;
Expand Down Expand Up @@ -198,6 +201,9 @@ public int getAdditionalHeight() {
public ReactedUsersListView setSeenUsers(List<ReactedHeaderView.UserSeen> users) {
if (userReactions != null && !userReactions.isEmpty()) {
for (ReactedHeaderView.UserSeen p : users) {
if (shouldFilterBlockedPeer(p.dialogId)) {
continue;
}
TLObject user = p.user;
if (user != null && p.date > 0) {
for (int i = 0; i < userReactions.size(); ++i) {
Expand All @@ -213,6 +219,9 @@ public ReactedUsersListView setSeenUsers(List<ReactedHeaderView.UserSeen> users)
}
List<TLRPC.TL_messagePeerReaction> nr = new ArrayList<>(users.size());
for (ReactedHeaderView.UserSeen p : users) {
if (shouldFilterBlockedPeer(p.dialogId)) {
continue;
}
ArrayList<TLRPC.MessagePeerReaction> userReactions = peerReactionMap.get(p.dialogId);
if (userReactions != null) {
continue;
Expand Down Expand Up @@ -244,6 +253,11 @@ public ReactedUsersListView setSeenUsers(List<ReactedHeaderView.UserSeen> users)
return this;
}

private boolean shouldFilterBlockedPeer(long peerId) {
return peerId != 0 && message != null && message.isSupergroup() &&
MessageHelper.getInstance(currentAccount).isBlockedOrFilteredPeer(peerId);
}
Comment on lines +256 to +259
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) { ... }


@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
Expand Down Expand Up @@ -273,6 +287,10 @@ private void load() {
TLRPC.TL_messages_messageReactionsList res = (TLRPC.TL_messages_messageReactionsList) response;
MessagesController.getInstance(currentAccount).putUsers(res.users, false);
MessagesController.getInstance(currentAccount).putChats(res.chats, false);
int filteredCount = Math.max(0, res.count - filterBlockedReactionPeers(res.reactions));
if (onCountChangedListener != null) {
onCountChangedListener.onCountChanged(this, filteredCount);
}
Comment on lines +290 to +293
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);
                     }

LastSeenHelper.saveLastSeenFromPeerReactions(res.reactions, UserConfig.getInstance(currentAccount).getClientUserId());

HashSet<ReactionsLayoutInBubble.VisibleReaction> visibleCustomEmojiReactions = new HashSet<>();
Expand Down Expand Up @@ -340,6 +358,26 @@ public void onAnimationEnd(Animator animation) {
}, ConnectionsManager.RequestFlagInvokeAfter);
}

private int filterBlockedReactionPeers(List<TLRPC.MessagePeerReaction> reactions) {
if (reactions == null || reactions.isEmpty() || message == null || !message.isSupergroup()) {
return 0;
}
MessageHelper messageHelper = MessageHelper.getInstance(currentAccount);
int removed = 0;
for (int i = reactions.size() - 1; i >= 0; i--) {
TLRPC.MessagePeerReaction reaction = reactions.get(i);
if (reaction == null || reaction.peer_id == null) {
continue;
}
long peerId = MessageObject.getPeerId(reaction.peer_id);
if (peerId != 0 && messageHelper.isBlockedOrFilteredPeer(peerId)) {
reactions.remove(i);
removed++;
}
}
return removed;
}

private void updateCustomReactionsButton() {
customEmojiStickerSets.clear();
ArrayList<TLRPC.InputStickerSet> sets = new ArrayList<>();
Expand Down Expand Up @@ -393,10 +431,19 @@ public ReactedUsersListView setOnHeightChangedListener(OnHeightChangedListener o
return this;
}

public ReactedUsersListView setOnCountChangedListener(OnCountChangedListener onCountChangedListener) {
this.onCountChangedListener = onCountChangedListener;
return this;
}

public interface OnHeightChangedListener {
void onHeightChanged(ReactedUsersListView view, int newHeight);
}

public interface OnCountChangedListener {
void onCountChanged(ReactedUsersListView view, int count);
}

public interface OnProfileSelectedListener {
void onProfileSelected(ReactedUsersListView view, long userId, TLRPC.MessagePeerReaction messagePeerReaction);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,12 @@ public void setCounter(int count) {
}

public void setCounter(int currentAccount, TLRPC.ReactionCount counter) {
this.count = counter.count;
counterView.setText(String.format("%s", LocaleController.formatShortNumber(counter.count, null)));
setCounter(currentAccount, counter, counter.count);
}

public void setCounter(int currentAccount, TLRPC.ReactionCount counter, int count) {
this.count = count;
counterView.setText(String.format("%s", LocaleController.formatShortNumber(count, null)));
ReactionsLayoutInBubble.VisibleReaction counterReaction = ReactionsLayoutInBubble.VisibleReaction.fromTL(counter.reaction);
reaction = counterReaction;
if (reaction.emojicon != null) {
Expand Down
11 changes: 9 additions & 2 deletions TMessagesProj/src/main/java/org/telegram/ui/MessageSeenView.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@
import org.telegram.ui.Components.RecyclerListView;
import org.telegram.ui.Components.StatusBadgeComponent;

import tw.nekomimi.nekogram.helpers.MessageHelper;

import java.util.ArrayList;
import java.util.HashMap;

Expand Down Expand Up @@ -117,7 +119,7 @@ public MessageSeenView(@NonNull Context context, int currentAccount, MessageObje
if (object instanceof TLRPC.TL_readParticipantDate) {
int date = ((TLRPC.TL_readParticipantDate) object).date;
Long peerId = ((TLRPC.TL_readParticipantDate) object).user_id;
if (finalFromId == peerId) {
if (finalFromId == peerId || shouldFilterBlockedPeer(chat, peerId)) {
continue;
}
TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(peerId);
Expand All @@ -129,7 +131,7 @@ public MessageSeenView(@NonNull Context context, int currentAccount, MessageObje
}
} else if (object instanceof Long) {
Long peerId = (Long) object;
if (finalFromId == peerId) {
if (finalFromId == peerId || shouldFilterBlockedPeer(chat, peerId)) {
continue;
}
if (peerId > 0) {
Expand Down Expand Up @@ -214,6 +216,11 @@ public MessageSeenView(@NonNull Context context, int currentAccount, MessageObje
setEnabled(false);
}

private boolean shouldFilterBlockedPeer(TLRPC.Chat chat, long peerId) {
return peerId != 0 && ChatObject.isMegagroup(chat) &&
MessageHelper.getInstance(currentAccount).isBlockedOrFilteredPeer(peerId);
}

boolean ignoreLayout;

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1042,15 +1042,78 @@ public boolean isBlockedUser(long senderId) {
return getMessagesController().blockePeers.indexOfKey(senderId) >= 0 || AyuFilter.isCustomFilteredPeer(senderId);
}

public boolean isBlockedOrFilteredPeer(long peerId) {
return isBlockedUser(peerId) || AyuFilter.isBlockedChannel(peerId);
}

public boolean isBlockedOrFiltered(TLRPC.Message message) {
if (message == null) {
return false;
}
long fromId = MessageObject.getFromChatId(message);
boolean blocked = isBlockedUser(fromId) || AyuFilter.isBlockedChannel(fromId);
boolean blocked = isBlockedOrFilteredPeer(fromId);
return blocked || AyuFilter.isFiltered(new MessageObject(currentAccount, message, false, false), null);
}

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;
}
Comment on lines +1058 to +1091
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.


private static void decrementReactionCount(TLRPC.TL_messageReactions reactions, TLRPC.Reaction removedReaction) {
if (removedReaction == null || reactions.results == null) {
return;
}
for (int i = reactions.results.size() - 1; i >= 0; i--) {
TLRPC.ReactionCount count = reactions.results.get(i);
if (count == null || !sameReaction(count.reaction, removedReaction)) {
continue;
}
count.count--;
if (count.count <= 0) {
reactions.results.remove(i);
}
return;
}
}

private static boolean sameReaction(TLRPC.Reaction first, TLRPC.Reaction second) {
if (first == null || second == null) {
return false;
}
return first.equals(second) || first instanceof TLRPC.TL_reactionPaid && second instanceof TLRPC.TL_reactionPaid;
}

public static void copyVideoFrameToClipboard(File videoFile, long positionMs, View bulletinContainer, Theme.ResourcesProvider resourcesProvider, Runnable fallbackAction) {
Utilities.globalQueue.postRunnable(() -> {
Bitmap bitmap = null;
Expand Down
Loading