diff --git a/app/javascript/mastodon/actions/notification_groups.ts b/app/javascript/mastodon/actions/notification_groups.ts index fc5807c8c0..ab62f8715c 100644 --- a/app/javascript/mastodon/actions/notification_groups.ts +++ b/app/javascript/mastodon/actions/notification_groups.ts @@ -15,6 +15,7 @@ import type { NotificationGap } from 'mastodon/reducers/notification_groups'; import { selectSettingsNotificationsExcludedTypes, selectSettingsNotificationsQuickFilterActive, + selectSettingsNotificationsShows, } from 'mastodon/selectors/settings'; import type { AppDispatch } from 'mastodon/store'; import { @@ -104,7 +105,31 @@ export const fetchNotificationsGap = createDataLoadingThunk( export const processNewNotificationForGroups = createAppAsyncThunk( 'notificationGroups/processNew', - (notification: ApiNotificationJSON, { dispatch }) => { + (notification: ApiNotificationJSON, { dispatch, getState }) => { + const state = getState(); + const activeFilter = selectSettingsNotificationsQuickFilterActive(state); + const notificationShows = selectSettingsNotificationsShows(state); + + const showInColumn = + activeFilter === 'all' + ? notificationShows[notification.type] + : activeFilter === notification.type; + + if (!showInColumn) return; + + if ( + (notification.type === 'mention' || notification.type === 'update') && + notification.status.filtered + ) { + const filters = notification.status.filtered.filter((result) => + result.filter.context.includes('notifications'), + ); + + if (filters.some((result) => result.filter.filter_action === 'hide')) { + return; + } + } + dispatchAssociatedRecords(dispatch, [notification]); return notification; diff --git a/app/javascript/mastodon/actions/streaming.js b/app/javascript/mastodon/actions/streaming.js index f50f41b0d9..44025bd596 100644 --- a/app/javascript/mastodon/actions/streaming.js +++ b/app/javascript/mastodon/actions/streaming.js @@ -104,7 +104,7 @@ export const connectTimelineStream = (timelineId, channelName, params = {}, opti const notificationJSON = JSON.parse(data.payload); dispatch(updateNotifications(notificationJSON, messages, locale)); // TODO: remove this once the groups feature replaces the previous one - if(getState().notificationGroups.groups.length > 0) { + if(getState().settings.getIn(['notifications', 'groupingBeta'], false)) { dispatch(processNewNotificationForGroups(notificationJSON)); } break; diff --git a/app/javascript/mastodon/api_types/statuses.ts b/app/javascript/mastodon/api_types/statuses.ts index a934faeb7a..2c59645ea7 100644 --- a/app/javascript/mastodon/api_types/statuses.ts +++ b/app/javascript/mastodon/api_types/statuses.ts @@ -58,6 +58,29 @@ export interface ApiPreviewCardJSON { authors: ApiPreviewCardAuthorJSON[]; } +export type FilterContext = + | 'home' + | 'notifications' + | 'public' + | 'thread' + | 'account'; + +export interface ApiFilterJSON { + id: string; + title: string; + context: FilterContext; + expires_at: string; + filter_action: 'warn' | 'hide'; + keywords?: unknown[]; // TODO: FilterKeywordSerializer + statuses?: unknown[]; // TODO: FilterStatusSerializer +} + +export interface ApiFilterResultJSON { + filter: ApiFilterJSON; + keyword_matches: string[]; + status_matches: string[]; +} + export interface ApiStatusJSON { id: string; created_at: string; @@ -80,8 +103,7 @@ export interface ApiStatusJSON { bookmarked?: boolean; pinned?: boolean; - // filtered: FilterResult[] - filtered: unknown; // TODO + filtered?: ApiFilterResultJSON[]; content?: string; text?: string; diff --git a/app/javascript/mastodon/reducers/notification_groups.ts b/app/javascript/mastodon/reducers/notification_groups.ts index 871f50364b..0d3f34d2fb 100644 --- a/app/javascript/mastodon/reducers/notification_groups.ts +++ b/app/javascript/mastodon/reducers/notification_groups.ts @@ -387,12 +387,14 @@ export const notificationGroupsReducer = createReducer( }) .addCase(processNewNotificationForGroups.fulfilled, (state, action) => { const notification = action.payload; - processNewNotification( - usePendingItems ? state.pendingGroups : state.groups, - notification, - ); - updateLastReadId(state); - trimNotifications(state); + if (notification) { + processNewNotification( + usePendingItems ? state.pendingGroups : state.groups, + notification, + ); + updateLastReadId(state); + trimNotifications(state); + } }) .addCase(disconnectTimeline, (state, action) => { if (action.payload.timeline === 'home') {