diff --git a/package.json b/package.json index 6e8d35c9..bdbb8cc0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "impro", - "version": "0.20.120", + "version": "0.20.121", "type": "module", "scripts": { "start": "rm -rf \"${BUILD_DIR:-build}\" && NODE_ENV=development eleventy --serve", diff --git a/src/js/dataLayer/derived.js b/src/js/dataLayer/derived.js index ae094fc4..c4f8a86b 100644 --- a/src/js/dataLayer/derived.js +++ b/src/js/dataLayer/derived.js @@ -3,6 +3,7 @@ import { filterAlgorithmicFeed, filterAuthorFeed, filterBookmarksFeed, + filterSearchResults, } from "/js/feedFilters.js"; import { createBlockedPost, @@ -405,7 +406,7 @@ export class Derived extends ReactiveStore { }); } return { - feed: hydratedFeedItems, + feed: filterSearchResults(hydratedFeedItems, this.isAuthenticated), cursor: data.cursor, }; }); @@ -1071,9 +1072,11 @@ export class Derived extends ReactiveStore { for (const result of data.posts) { const post = this.$hydratedPosts.get(result.uri); if (!post) continue; - hydratedSearchResults.push(this.attachParentAuthor(post)); + hydratedSearchResults.push({ post: this.attachParentAuthor(post) }); } - return hydratedSearchResults; + return filterSearchResults(hydratedSearchResults, this.isAuthenticated).map( + (item) => item.post, + ); } hydrateNotification(rawNotification) { diff --git a/src/js/feedFilters.js b/src/js/feedFilters.js index c7a180a1..2af2eda0 100644 --- a/src/js/feedFilters.js +++ b/src/js/feedFilters.js @@ -371,6 +371,19 @@ export function filterAlgorithmicFeed( }; } +export function filterSearchResults(feedItems, isAuthenticated) { + const filter = FeedFilter.compose( + new FilterBlockedQuotes(), + new FilterBlockedPosts(), + new FilterMutedQuotes(), + new FilterMutedPosts(), + new FilterHiddenPosts(), + new FilterContentLabeledPosts(), + new FilterUnauthorizedPosts(isAuthenticated), + ); + return filter.filterFeedItems(feedItems); +} + export function filterBookmarksFeed(feed) { return { feed: new FilterEmptyPosts().filterFeedItems(feed.feed), diff --git a/tests/unit/specs/dataLayer/derived.test.js b/tests/unit/specs/dataLayer/derived.test.js index 93d1dae4..762129e5 100644 --- a/tests/unit/specs/dataLayer/derived.test.js +++ b/tests/unit/specs/dataLayer/derived.test.js @@ -390,6 +390,42 @@ describe("$hydratedHashtagFeeds", () => { assert.deepEqual(result.feed[0].post.likeCount, 6); assert.deepEqual(result.feed[0].post.viewer.like, "fake like"); }); + + it("should filter out muted, hidden, and hide-labeled posts", () => { + const dataStore = new DataStore(createSessionState(null)); + const { derived } = makeDerived(dataStore, { + preferences: fakePreferences({ + isPostHidden: (uri) => uri === "hidden", + getContentLabel: (post) => + post.uri === "labeled" ? { visibility: "hide" } : null, + }), + }); + const mutedAuthor = createProfile({ + did: "did:plc:muted", + handle: "muted.test", + viewer: { muted: true }, + }); + dataStore.setProfiles([mutedAuthor]); + dataStore.$posts.set("muted", { uri: "muted", author: mutedAuthor }); + dataStore.$posts.set("hidden", { uri: "hidden" }); + dataStore.$posts.set("labeled", { uri: "labeled" }); + dataStore.$posts.set("ok", { uri: "ok" }); + dataStore.$hashtagFeeds.set(hashtagKey, { + posts: [ + { uri: "muted" }, + { uri: "hidden" }, + { uri: "labeled" }, + { uri: "ok" }, + ], + cursor: "c", + }); + + const result = derived.$hydratedHashtagFeeds.get(hashtagKey); + assert.deepEqual( + result.feed.map((item) => item.post.uri), + ["ok"], + ); + }); }); describe("$hydratedProfiles", () => { @@ -2695,6 +2731,41 @@ describe("post search results", () => { assert.deepEqual(derived.$postSearchCursorTop.get(), "tc"); assert.deepEqual(derived.$postSearchCursorLatest.get(), "lc"); }); + + it("should filter out muted, hidden, and hide-labeled posts", () => { + const dataStore = new DataStore(createSessionState(null)); + const { derived } = makeDerived(dataStore, { + preferences: fakePreferences({ + isPostHidden: (uri) => uri === "hidden", + getMediaLabel: (post) => + post.uri === "labeled" ? { visibility: "hide" } : null, + }), + }); + const mutedAuthor = createProfile({ + did: "did:plc:muted", + handle: "muted.test", + viewer: { muted: true }, + }); + dataStore.setProfiles([mutedAuthor]); + dataStore.$posts.set("muted", { uri: "muted", author: mutedAuthor }); + dataStore.$posts.set("hidden", { uri: "hidden" }); + dataStore.$posts.set("labeled", { uri: "labeled" }); + dataStore.$posts.set("ok", { uri: "ok" }); + dataStore.$postSearchResultsTop.set({ + posts: [ + { uri: "muted" }, + { uri: "hidden" }, + { uri: "labeled" }, + { uri: "ok" }, + ], + cursor: "tc", + }); + + assert.deepEqual( + derived.$postSearchResultsTop.get().map((post) => post.uri), + ["ok"], + ); + }); }); describe("$hydratedPostQuotes", () => {