diff --git a/bitchat/Services/GeohashParticipantTracker.swift b/bitchat/Services/GeohashParticipantTracker.swift index be1ea35e..ac8d6226 100644 --- a/bitchat/Services/GeohashParticipantTracker.swift +++ b/bitchat/Services/GeohashParticipantTracker.swift @@ -80,6 +80,8 @@ final class GeohashParticipantTracker: ObservableObject { /// Record activity from a participant in a specific geohash func recordParticipant(pubkeyHex: String, geohash: String) { let key = pubkeyHex.lowercased() + guard context?.isBlocked(key) != true else { return } + var map = participants[geohash] ?? [:] map[key] = Date() participants[geohash] = map @@ -107,7 +109,9 @@ final class GeohashParticipantTracker: ObservableObject { func participantCount(for geohash: String) -> Int { let cutoff = Date().addingTimeInterval(activityCutoff) let map = participants[geohash] ?? [:] - return map.values.filter { $0 >= cutoff }.count + return map.filter { key, lastSeen in + lastSeen >= cutoff && context?.isBlocked(key) != true + }.count } /// Get the visible people list for the active geohash (read-only query) diff --git a/bitchatTests/GeohashParticipantTrackerTests.swift b/bitchatTests/GeohashParticipantTrackerTests.swift index d05b8171..fe9190cc 100644 --- a/bitchatTests/GeohashParticipantTrackerTests.swift +++ b/bitchatTests/GeohashParticipantTrackerTests.swift @@ -131,6 +131,57 @@ struct GeohashParticipantTrackerTests { #expect(people.first?.id == "pubkey1") } + @Test func recordParticipant_ignoresBlockedParticipant() { + let tracker = GeohashParticipantTracker() + let context = MockParticipantContext() + context.blockedPubkeys = ["blocked"] + tracker.configure(context: context) + tracker.setActiveGeohash("abc123") + + tracker.recordParticipant(pubkeyHex: "BLOCKED") + + #expect(tracker.participantCount(for: "abc123") == 0) + #expect(tracker.getVisiblePeople().isEmpty) + } + + @Test func participantCount_excludesParticipantBlockedAfterRecording() { + let tracker = GeohashParticipantTracker() + let context = MockParticipantContext() + tracker.configure(context: context) + tracker.setActiveGeohash("abc123") + tracker.recordParticipant(pubkeyHex: "later-blocked") + + context.blockedPubkeys.insert("later-blocked") + tracker.refresh() + + #expect(tracker.participantCount(for: "abc123") == 0) + #expect(tracker.visiblePeople.isEmpty) + } + + @Test func participantCount_matchesVisiblePeopleWithMixedBlockStatus() { + let tracker = GeohashParticipantTracker() + let context = MockParticipantContext() + tracker.configure(context: context) + tracker.setActiveGeohash("abc123") + tracker.recordParticipant(pubkeyHex: "visible") + tracker.recordParticipant(pubkeyHex: "later-blocked") + + context.blockedPubkeys.insert("later-blocked") + tracker.refresh() + + #expect(tracker.participantCount(for: "abc123") == 1) + #expect(tracker.visiblePeople.count == 1) + #expect(tracker.visiblePeople.first?.id == "visible") + } + + @Test func participantCount_withoutContext_preservesRecordedParticipants() { + let tracker = GeohashParticipantTracker() + + tracker.recordParticipant(pubkeyHex: "participant", geohash: "abc123") + + #expect(tracker.participantCount(for: "abc123") == 1) + } + @Test func getVisiblePeople_usesDisplayNameFromContext() async { let tracker = GeohashParticipantTracker() let context = MockParticipantContext()