diff --git a/app/javascript/flavours/glitch/actions/filters.js b/app/javascript/flavours/glitch/actions/filters.js
index 050b303222..9aa31028ab 100644
--- a/app/javascript/flavours/glitch/actions/filters.js
+++ b/app/javascript/flavours/glitch/actions/filters.js
@@ -1,9 +1,24 @@
import api from 'flavours/glitch/util/api';
+import { openModal } from './modal';
export const FILTERS_FETCH_REQUEST = 'FILTERS_FETCH_REQUEST';
export const FILTERS_FETCH_SUCCESS = 'FILTERS_FETCH_SUCCESS';
export const FILTERS_FETCH_FAIL = 'FILTERS_FETCH_FAIL';
+export const FILTERS_STATUS_CREATE_REQUEST = 'FILTERS_STATUS_CREATE_REQUEST';
+export const FILTERS_STATUS_CREATE_SUCCESS = 'FILTERS_STATUS_CREATE_SUCCESS';
+export const FILTERS_STATUS_CREATE_FAIL = 'FILTERS_STATUS_CREATE_FAIL';
+
+export const FILTERS_CREATE_REQUEST = 'FILTERS_CREATE_REQUEST';
+export const FILTERS_CREATE_SUCCESS = 'FILTERS_CREATE_SUCCESS';
+export const FILTERS_CREATE_FAIL = 'FILTERS_CREATE_FAIL';
+
+export const initAddFilter = (status, { contextType }) => dispatch =>
+ dispatch(openModal('FILTER', {
+ statusId: status?.get('id'),
+ contextType: contextType,
+ }));
+
export const fetchFilters = () => (dispatch, getState) => {
dispatch({
type: FILTERS_FETCH_REQUEST,
@@ -11,7 +26,7 @@ export const fetchFilters = () => (dispatch, getState) => {
});
api(getState)
- .get('/api/v1/filters')
+ .get('/api/v2/filters')
.then(({ data }) => dispatch({
type: FILTERS_FETCH_SUCCESS,
filters: data,
@@ -24,3 +39,55 @@ export const fetchFilters = () => (dispatch, getState) => {
skipAlert: true,
}));
};
+
+export const createFilterStatus = (params, onSuccess, onFail) => (dispatch, getState) => {
+ dispatch(createFilterStatusRequest());
+
+ api(getState).post(`/api/v1/filters/${params.filter_id}/statuses`, params).then(response => {
+ dispatch(createFilterStatusSuccess(response.data));
+ if (onSuccess) onSuccess();
+ }).catch(error => {
+ dispatch(createFilterStatusFail(error));
+ if (onFail) onFail();
+ });
+};
+
+export const createFilterStatusRequest = () => ({
+ type: FILTERS_STATUS_CREATE_REQUEST,
+});
+
+export const createFilterStatusSuccess = filter_status => ({
+ type: FILTERS_STATUS_CREATE_SUCCESS,
+ filter_status,
+});
+
+export const createFilterStatusFail = error => ({
+ type: FILTERS_STATUS_CREATE_FAIL,
+ error,
+});
+
+export const createFilter = (params, onSuccess, onFail) => (dispatch, getState) => {
+ dispatch(createFilterRequest());
+
+ api(getState).post('/api/v2/filters', params).then(response => {
+ dispatch(createFilterSuccess(response.data));
+ if (onSuccess) onSuccess(response.data);
+ }).catch(error => {
+ dispatch(createFilterFail(error));
+ if (onFail) onFail();
+ });
+};
+
+export const createFilterRequest = () => ({
+ type: FILTERS_CREATE_REQUEST,
+});
+
+export const createFilterSuccess = filter => ({
+ type: FILTERS_CREATE_SUCCESS,
+ filter,
+});
+
+export const createFilterFail = error => ({
+ type: FILTERS_CREATE_FAIL,
+ error,
+});
diff --git a/app/javascript/flavours/glitch/actions/importer/index.js b/app/javascript/flavours/glitch/actions/importer/index.js
index ec41fea6e2..94d133b5f3 100644
--- a/app/javascript/flavours/glitch/actions/importer/index.js
+++ b/app/javascript/flavours/glitch/actions/importer/index.js
@@ -5,6 +5,7 @@ export const ACCOUNTS_IMPORT = 'ACCOUNTS_IMPORT';
export const STATUS_IMPORT = 'STATUS_IMPORT';
export const STATUSES_IMPORT = 'STATUSES_IMPORT';
export const POLLS_IMPORT = 'POLLS_IMPORT';
+export const FILTERS_IMPORT = 'FILTERS_IMPORT';
function pushUnique(array, object) {
if (array.every(element => element.id !== object.id)) {
@@ -28,6 +29,10 @@ export function importStatuses(statuses) {
return { type: STATUSES_IMPORT, statuses };
}
+export function importFilters(filters) {
+ return { type: FILTERS_IMPORT, filters };
+}
+
export function importPolls(polls) {
return { type: POLLS_IMPORT, polls };
}
@@ -61,11 +66,16 @@ export function importFetchedStatuses(statuses) {
const accounts = [];
const normalStatuses = [];
const polls = [];
+ const filters = [];
function processStatus(status) {
pushUnique(normalStatuses, normalizeStatus(status, getState().getIn(['statuses', status.id]), getState().get('local_settings')));
pushUnique(accounts, status.account);
+ if (status.filtered) {
+ status.filtered.forEach(result => pushUnique(filters, result.filter));
+ }
+
if (status.reblog && status.reblog.id) {
processStatus(status.reblog);
}
@@ -80,6 +90,7 @@ export function importFetchedStatuses(statuses) {
dispatch(importPolls(polls));
dispatch(importFetchedAccounts(accounts));
dispatch(importStatuses(normalStatuses));
+ dispatch(importFilters(filters));
};
}
diff --git a/app/javascript/flavours/glitch/actions/importer/normalizer.js b/app/javascript/flavours/glitch/actions/importer/normalizer.js
index c6acdbdbb3..9950a720bd 100644
--- a/app/javascript/flavours/glitch/actions/importer/normalizer.js
+++ b/app/javascript/flavours/glitch/actions/importer/normalizer.js
@@ -42,6 +42,14 @@ export function normalizeAccount(account) {
return account;
}
+export function normalizeFilterResult(result) {
+ const normalResult = { ...result };
+
+ normalResult.filter = normalResult.filter.id;
+
+ return normalResult;
+}
+
export function normalizeStatus(status, normalOldStatus, settings) {
const normalStatus = { ...status };
normalStatus.account = status.account.id;
@@ -54,6 +62,10 @@ export function normalizeStatus(status, normalOldStatus, settings) {
normalStatus.poll = status.poll.id;
}
+ if (status.filtered) {
+ normalStatus.filtered = status.filtered.map(normalizeFilterResult);
+ }
+
// Only calculate these values when status first encountered and
// when the underlying values change. Otherwise keep the ones
// already in the reducer
diff --git a/app/javascript/flavours/glitch/actions/notifications.js b/app/javascript/flavours/glitch/actions/notifications.js
index 63c1fe038a..ae3ee1a4c2 100644
--- a/app/javascript/flavours/glitch/actions/notifications.js
+++ b/app/javascript/flavours/glitch/actions/notifications.js
@@ -12,10 +12,8 @@ import { saveSettings } from './settings';
import { defineMessages } from 'react-intl';
import { List as ImmutableList } from 'immutable';
import { unescapeHTML } from 'flavours/glitch/util/html';
-import { getFiltersRegex } from 'flavours/glitch/selectors';
import { usePendingItems as preferPendingItems } from 'flavours/glitch/util/initial_state';
import compareId from 'flavours/glitch/util/compare_id';
-import { searchTextFromRawStatus } from 'flavours/glitch/actions/importer/normalizer';
import { requestNotificationPermission } from 'flavours/glitch/util/notifications';
export const NOTIFICATIONS_UPDATE = 'NOTIFICATIONS_UPDATE';
@@ -74,20 +72,17 @@ export function updateNotifications(notification, intlMessages, intlLocale) {
const showInColumn = activeFilter === 'all' ? getState().getIn(['settings', 'notifications', 'shows', notification.type], true) : activeFilter === notification.type;
const showAlert = getState().getIn(['settings', 'notifications', 'alerts', notification.type], true);
const playSound = getState().getIn(['settings', 'notifications', 'sounds', notification.type], true);
- const filters = getFiltersRegex(getState(), { contextType: 'notifications' });
let filtered = false;
- if (['mention', 'status'].includes(notification.type)) {
- const dropRegex = filters[0];
- const regex = filters[1];
- const searchIndex = searchTextFromRawStatus(notification.status);
+ if (['mention', 'status'].includes(notification.type) && notification.status.filtered) {
+ const filters = notification.status.filtered.filter(result => result.filter.context.includes('notifications'));
- if (dropRegex && dropRegex.test(searchIndex)) {
+ if (filters.some(result => result.filter.filter_action === 'hide')) {
return;
}
- filtered = regex && regex.test(searchIndex);
+ filtered = filters.length > 0;
}
if (['follow_request'].includes(notification.type)) {
diff --git a/app/javascript/flavours/glitch/actions/statuses.js b/app/javascript/flavours/glitch/actions/statuses.js
index 1f223f22ef..58c1d44a69 100644
--- a/app/javascript/flavours/glitch/actions/statuses.js
+++ b/app/javascript/flavours/glitch/actions/statuses.js
@@ -42,9 +42,9 @@ export function fetchStatusRequest(id, skipLoading) {
};
};
-export function fetchStatus(id) {
+export function fetchStatus(id, forceFetch = false) {
return (dispatch, getState) => {
- const skipLoading = getState().getIn(['statuses', id], null) !== null;
+ const skipLoading = !forceFetch && getState().getIn(['statuses', id], null) !== null;
dispatch(fetchContext(id));
diff --git a/app/javascript/flavours/glitch/actions/streaming.js b/app/javascript/flavours/glitch/actions/streaming.js
index 90d6a02313..375728cb5a 100644
--- a/app/javascript/flavours/glitch/actions/streaming.js
+++ b/app/javascript/flavours/glitch/actions/streaming.js
@@ -21,7 +21,6 @@ import {
updateReaction as updateAnnouncementsReaction,
deleteAnnouncement,
} from './announcements';
-import { fetchFilters } from './filters';
import { getLocale } from 'mastodon/locales';
const { messages } = getLocale();
@@ -97,9 +96,6 @@ export const connectTimelineStream = (timelineId, channelName, params = {}, opti
case 'conversation':
dispatch(updateConversations(JSON.parse(data.payload)));
break;
- case 'filters_changed':
- dispatch(fetchFilters());
- break;
case 'announcement':
dispatch(updateAnnouncements(JSON.parse(data.payload)));
break;
diff --git a/app/javascript/flavours/glitch/actions/timelines.js b/app/javascript/flavours/glitch/actions/timelines.js
index 0b36d8ac3a..0d6f844b3f 100644
--- a/app/javascript/flavours/glitch/actions/timelines.js
+++ b/app/javascript/flavours/glitch/actions/timelines.js
@@ -4,8 +4,7 @@ import api, { getLinks } from 'flavours/glitch/util/api';
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
import compareId from 'flavours/glitch/util/compare_id';
import { me, usePendingItems as preferPendingItems } from 'flavours/glitch/util/initial_state';
-import { getFiltersRegex } from 'flavours/glitch/selectors';
-import { searchTextFromRawStatus } from 'flavours/glitch/actions/importer/normalizer';
+import { toServerSideType } from 'flavours/glitch/util/filters';
export const TIMELINE_UPDATE = 'TIMELINE_UPDATE';
export const TIMELINE_DELETE = 'TIMELINE_DELETE';
@@ -40,14 +39,13 @@ export function updateTimeline(timeline, status, accept) {
return;
}
- const filters = getFiltersRegex(getState(), { contextType: timeline });
- const dropRegex = filters[0];
- const regex = filters[1];
- const text = searchTextFromRawStatus(status);
- let filtered = false;
+ let filtered = false;
- if (status.account.id !== me) {
- filtered = (dropRegex && dropRegex.test(text)) || (regex && regex.test(text));
+ if (status.filtered) {
+ const contextType = toServerSideType(timeline);
+ const filters = status.filtered.filter(result => result.filter.context.includes(contextType));
+
+ filtered = filters.length > 0;
}
dispatch(importFetchedStatus(status));
diff --git a/app/javascript/flavours/glitch/components/status.js b/app/javascript/flavours/glitch/components/status.js
index b2d9acebcd..e238456c5b 100644
--- a/app/javascript/flavours/glitch/components/status.js
+++ b/app/javascript/flavours/glitch/components/status.js
@@ -79,6 +79,7 @@ class Status extends ImmutablePureComponent {
onOpenMedia: PropTypes.func,
onOpenVideo: PropTypes.func,
onBlock: PropTypes.func,
+ onAddFilter: PropTypes.func,
onEmbed: PropTypes.func,
onHeightChange: PropTypes.func,
onToggleHidden: PropTypes.func,
@@ -455,8 +456,8 @@ class Status extends ImmutablePureComponent {
}
handleUnfilterClick = e => {
- const { onUnfilter, status } = this.props;
- onUnfilter(status.get('reblog') ? status.get('reblog') : status, () => this.setState({ forceFilter: false }));
+ this.setState({ forceFilter: false });
+ e.preventDefault();
}
handleFilterClick = () => {
@@ -557,8 +558,8 @@ class Status extends ImmutablePureComponent {
);
}
- const filtered = (status.get('filtered') || status.getIn(['reblog', 'filtered'])) && settings.get('filtering_behavior') !== 'content_warning';
- if (forceFilter === undefined ? filtered : forceFilter) {
+ const matchedFilters = status.get('matched_filters');
+ if (this.state.forceFilter === undefined ? matchedFilters : this.state.forceFilter) {
const minHandlers = this.props.muted ? {} : {
moveUp: this.handleHotkeyMoveUp,
moveDown: this.handleHotkeyMoveDown,
@@ -567,13 +568,11 @@ class Status extends ImmutablePureComponent {
return (
+
+
+
+