diff --git a/damus/Features/Profile/Views/ProfileView.swift b/damus/Features/Profile/Views/ProfileView.swift index 8bf8e693..29cae6c4 100644 --- a/damus/Features/Profile/Views/ProfileView.swift +++ b/damus/Features/Profile/Views/ProfileView.swift @@ -118,6 +118,7 @@ struct ProfileView: View { damus_state.contacts.follow_state(profile.pubkey) == .unfollows && bannerBlurViewOpacity() > 1.0 } + /// Builds the profile timeline filter, while ensuring the user's own profile bypasses NSFW and hashtag-spam content filters. func content_filter(_ fstate: FilterState) -> ((NostrEvent) -> Bool) { var filters = ContentFilters.defaults(damus_state: damus_state) filters.append(fstate.filter) diff --git a/damus/Features/Timeline/Models/ContentFilters.swift b/damus/Features/Timeline/Models/ContentFilters.swift index c456cfec..18e06b70 100644 --- a/damus/Features/Timeline/Models/ContentFilters.swift +++ b/damus/Features/Timeline/Models/ContentFilters.swift @@ -107,15 +107,24 @@ extension ContentFilters { return ContentFilters(filters: ContentFilters.defaults(damus_state: damus_state)) } + /// Returns the app's standard content filters. + /// + /// The logged-in user's own events bypass NSFW and hashtag-spam filters so they remain visible on their own profile. @MainActor static func defaults(damus_state: DamusState) -> [(NostrEvent) -> Bool] { var filters = Array<(NostrEvent) -> Bool>() if damus_state.settings.hide_nsfw_tagged_content { - filters.append(nsfw_tag_filter) + filters.append { ev in + guard ev.pubkey != damus_state.pubkey else { return true } + return nsfw_tag_filter(ev: ev) + } } if damus_state.settings.hide_hashtag_spam { let max_hashtags = damus_state.settings.max_hashtags - filters.append({ ev in hashtag_spam_filter(ev: ev, max_hashtags: max_hashtags) }) + filters.append { ev in + guard ev.pubkey != damus_state.pubkey else { return true } + return hashtag_spam_filter(ev: ev, max_hashtags: max_hashtags) + } } filters.append(get_repost_of_muted_user_filter(damus_state: damus_state)) filters.append(timestamp_filter) diff --git a/damusTests/ProfileViewTests.swift b/damusTests/ProfileViewTests.swift index 332e4dbf..f3ee1062 100644 --- a/damusTests/ProfileViewTests.swift +++ b/damusTests/ProfileViewTests.swift @@ -12,6 +12,15 @@ final class ProfileViewTests: XCTestCase { let enUsLocale = Locale(identifier: "en-US") + @MainActor + private func makeProfileEvent(content: String, tags: [[String]], keypair: Keypair = test_keypair) -> NostrEvent { + guard let event = NostrEvent(content: content, keypair: keypair, tags: tags) else { + XCTFail("Expected test event creation to succeed") + fatalError("Failed to create test event") + } + return event + } + override func setUpWithError() throws { // Put setup code here. This method is called before the invocation of each test method in the class. } @@ -20,6 +29,28 @@ final class ProfileViewTests: XCTestCase { // Put teardown code here. This method is called after the invocation of each test method in the class. } + @MainActor + func testOwnProfileBypassesNsfwAndHashtagFilters() throws { + let damusState = generate_test_damus_state(mock_profile_info: nil) + damusState.settings.hide_nsfw_tagged_content = true + damusState.settings.hide_hashtag_spam = true + damusState.settings.max_hashtags = 1 + + let ownNsfwEvent = makeProfileEvent(content: "hello #nsfw", tags: [["t", "nsfw"]]) + let ownHashtagSpamEvent = makeProfileEvent(content: "#one #two", tags: [["t", "one"], ["t", "two"]]) + let otherKeypair = generate_new_keypair().to_keypair() + let otherNsfwEvent = makeProfileEvent(content: "hello #nsfw", tags: [["t", "nsfw"]], keypair: otherKeypair) + let otherHashtagSpamEvent = makeProfileEvent(content: "#one #two", tags: [["t", "one"], ["t", "two"]], keypair: otherKeypair) + + let filters = ContentFilters.defaults(damus_state: damusState) + let combinedFilter = ContentFilters(filters: filters) + + XCTAssertTrue(combinedFilter.filter(ev: ownNsfwEvent)) + XCTAssertTrue(combinedFilter.filter(ev: ownHashtagSpamEvent)) + XCTAssertFalse(combinedFilter.filter(ev: otherNsfwEvent)) + XCTAssertFalse(combinedFilter.filter(ev: otherHashtagSpamEvent)) + } + func testFollowedByString() throws { let pk1 = test_pubkey let pk2 = test_pubkey_2