From f75eb1a8b0386d2ca5c1c05ee3b10364b3e11211 Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 10 Oct 2024 13:04:38 +0200 Subject: [PATCH] =?UTF-8?q?Fix=20=E2=80=9CMention=E2=80=9D=20appearing=20f?= =?UTF-8?q?or=20otherwise=20filtered=20posts=20(#32356)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/notification_with_status.tsx | 9 +++- app/javascript/mastodon/selectors/filters.ts | 50 +++++++++++++++++++ app/javascript/mastodon/selectors/index.js | 15 +----- 3 files changed, 60 insertions(+), 14 deletions(-) create mode 100644 app/javascript/mastodon/selectors/filters.ts diff --git a/app/javascript/mastodon/features/notifications_v2/components/notification_with_status.tsx b/app/javascript/mastodon/features/notifications_v2/components/notification_with_status.tsx index a1c275a1f..3e6428287 100644 --- a/app/javascript/mastodon/features/notifications_v2/components/notification_with_status.tsx +++ b/app/javascript/mastodon/features/notifications_v2/components/notification_with_status.tsx @@ -13,6 +13,7 @@ import { import type { IconProp } from 'mastodon/components/icon'; import { Icon } from 'mastodon/components/icon'; import Status from 'mastodon/containers/status_container'; +import { getStatusHidden } from 'mastodon/selectors/filters'; import { useAppSelector, useAppDispatch } from 'mastodon/store'; import { DisplayedName } from './displayed_name'; @@ -48,6 +49,12 @@ export const NotificationWithStatus: React.FC<{ (state) => state.statuses.getIn([statusId, 'visibility']) === 'direct', ); + const isFiltered = useAppSelector( + (state) => + statusId && + getStatusHidden(state, { id: statusId, contextType: 'notifications' }), + ); + const handlers = useMemo( () => ({ open: () => { @@ -73,7 +80,7 @@ export const NotificationWithStatus: React.FC<{ [dispatch, statusId], ); - if (!statusId) return null; + if (!statusId || isFiltered) return null; return ( diff --git a/app/javascript/mastodon/selectors/filters.ts b/app/javascript/mastodon/selectors/filters.ts new file mode 100644 index 000000000..f84d01216 --- /dev/null +++ b/app/javascript/mastodon/selectors/filters.ts @@ -0,0 +1,50 @@ +import { createSelector } from '@reduxjs/toolkit'; + +import type { RootState } from 'mastodon/store'; +import { toServerSideType } from 'mastodon/utils/filters'; + +// TODO: move to `app/javascript/mastodon/models` and use more globally +type Filter = Immutable.Map; + +// TODO: move to `app/javascript/mastodon/models` and use more globally +type FilterResult = Immutable.Map; + +export const getFilters = createSelector( + [ + (state: RootState) => state.filters as Immutable.Map, + (_, { contextType }: { contextType: string }) => contextType, + ], + (filters, contextType) => { + if (!contextType) { + return null; + } + + const now = new Date(); + const serverSideType = toServerSideType(contextType); + + return filters.filter((filter) => { + const context = filter.get('context') as Immutable.List; + const expiration = filter.get('expires_at') as Date | null; + return ( + context.includes(serverSideType) && + (expiration === null || expiration > now) + ); + }); + }, +); + +export const getStatusHidden = ( + state: RootState, + { id, contextType }: { id: string; contextType: string }, +) => { + const filters = getFilters(state, { contextType }); + if (filters === null) return false; + + const filtered = state.statuses.getIn([id, 'filtered']) as + | Immutable.List + | undefined; + return filtered?.some( + (result) => + filters.getIn([result.get('filter'), 'filter_action']) === 'hide', + ); +}; diff --git a/app/javascript/mastodon/selectors/index.js b/app/javascript/mastodon/selectors/index.js index 10e1b167c..345ceac49 100644 --- a/app/javascript/mastodon/selectors/index.js +++ b/app/javascript/mastodon/selectors/index.js @@ -1,23 +1,12 @@ import { createSelector } from '@reduxjs/toolkit'; import { List as ImmutableList, Map as ImmutableMap } from 'immutable'; -import { toServerSideType } from 'mastodon/utils/filters'; - import { me } from '../initial_state'; +import { getFilters } from './filters'; + export { makeGetAccount } from "./accounts"; -const getFilters = createSelector([state => state.get('filters'), (_, { contextType }) => contextType], (filters, contextType) => { - if (!contextType) { - return null; - } - - const now = new Date(); - const serverSideType = toServerSideType(contextType); - - return filters.filter(filter => filter.get('context').includes(serverSideType) && (filter.get('expires_at') === null || filter.get('expires_at') > now)); -}); - export const makeGetStatus = () => { return createSelector( [