Merge branch 'master' into glitch-soc/tentative-merge

Conflicts:
	README.md
	app/controllers/statuses_controller.rb
	app/lib/feed_manager.rb
	config/navigation.rb
	spec/lib/feed_manager_spec.rb

Conflicts were resolved by taking both versions for each change.
This means the two filter systems (glitch-soc's keyword mutes and tootsuite's
custom filters) are in place, which will be changed in a follow-up commit.
This commit is contained in:
Thibaut Girka
2018-07-09 07:05:29 +02:00
3127 changed files with 7554 additions and 3945 deletions

View File

@@ -1,15 +1,9 @@
import React from 'react';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import SettingText from '../../../components/setting_text';
import { injectIntl, FormattedMessage } from 'react-intl';
import SettingToggle from '../../notifications/components/setting_toggle';
const messages = defineMessages({
filter_regex: { id: 'home.column_settings.filter_regex', defaultMessage: 'Filter out by regular expressions' },
settings: { id: 'home.settings', defaultMessage: 'Column settings' },
});
@injectIntl
export default class ColumnSettings extends React.PureComponent {
@@ -21,19 +15,13 @@ export default class ColumnSettings extends React.PureComponent {
};
render () {
const { settings, onChange, intl } = this.props;
const { settings, onChange } = this.props;
return (
<div>
<div className='column-settings__row'>
<SettingToggle settings={settings} settingPath={['other', 'onlyMedia']} onChange={onChange} label={<FormattedMessage id='community.column_settings.media_only' defaultMessage='Media Only' />} />
</div>
<span className='column-settings__section'><FormattedMessage id='home.column_settings.advanced' defaultMessage='Advanced' /></span>
<div className='column-settings__row'>
<SettingText settings={settings} settingKey={['regex', 'body']} onChange={onChange} label={intl.formatMessage(messages.filter_regex)} />
</div>
</div>
);
}

View File

@@ -14,7 +14,7 @@ export default class AutosuggestAccount extends ImmutablePureComponent {
const { account } = this.props;
return (
<div className='autosuggest-account'>
<div className='autosuggest-account' title={account.get('acct')}>
<div className='autosuggest-account-icon'><Avatar account={account} size={18} /></div>
<DisplayName account={account} />
</div>

View File

@@ -7,7 +7,7 @@ const makeMapStateToProps = () => {
const getStatus = makeGetStatus();
const mapStateToProps = state => ({
status: getStatus(state, state.getIn(['compose', 'in_reply_to'])),
status: getStatus(state, { id: state.getIn(['compose', 'in_reply_to']) }),
});
return mapStateToProps;

View File

@@ -7,7 +7,6 @@ import ColumnHeader from '../../components/column_header';
import { expandDirectTimeline } from '../../actions/timelines';
import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import ColumnSettingsContainer from './containers/column_settings_container';
import { connectDirectStream } from '../../actions/streaming';
const messages = defineMessages({
@@ -86,9 +85,7 @@ export default class DirectTimeline extends React.PureComponent {
onClick={this.handleHeaderClick}
pinned={pinned}
multiColumn={multiColumn}
>
<ColumnSettingsContainer />
</ColumnHeader>
/>
<StatusListContainer
trackScroll={!pinned}

View File

@@ -1,14 +1,8 @@
import React from 'react';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { injectIntl, FormattedMessage } from 'react-intl';
import SettingToggle from '../../notifications/components/setting_toggle';
import SettingText from '../../../components/setting_text';
const messages = defineMessages({
filter_regex: { id: 'home.column_settings.filter_regex', defaultMessage: 'Filter out by regular expressions' },
settings: { id: 'home.settings', defaultMessage: 'Column settings' },
});
@injectIntl
export default class ColumnSettings extends React.PureComponent {
@@ -20,7 +14,7 @@ export default class ColumnSettings extends React.PureComponent {
};
render () {
const { settings, onChange, intl } = this.props;
const { settings, onChange } = this.props;
return (
<div>
@@ -33,12 +27,6 @@ export default class ColumnSettings extends React.PureComponent {
<div className='column-settings__row'>
<SettingToggle prefix='home_timeline' settings={settings} settingPath={['shows', 'reply']} onChange={onChange} label={<FormattedMessage id='home.column_settings.show_replies' defaultMessage='Show replies' />} />
</div>
<span className='column-settings__section'><FormattedMessage id='home.column_settings.advanced' defaultMessage='Advanced' /></span>
<div className='column-settings__row'>
<SettingText prefix='home_timeline' settings={settings} settingKey={['regex', 'body']} onChange={onChange} label={intl.formatMessage(messages.filter_regex)} />
</div>
</div>
);
}

View File

@@ -91,6 +91,7 @@ export default class Notification extends ImmutablePureComponent {
hidden={this.props.hidden}
onMoveDown={this.handleMoveDown}
onMoveUp={this.handleMoveUp}
contextType='notifications'
/>
);
}

View File

@@ -58,7 +58,7 @@ const makeMapStateToProps = () => {
const getStatus = makeGetStatus();
const mapStateToProps = (state, props) => {
const status = getStatus(state, props.params.statusId);
const status = getStatus(state, { id: props.params.statusId });
let ancestorsIds = Immutable.List();
let descendantsIds = Immutable.List();
@@ -276,7 +276,7 @@ export default class Status extends ImmutablePureComponent {
handleHotkeyMention = e => {
e.preventDefault();
this.handleMentionClick(this.props.status);
this.handleMentionClick(this.props.status.get('account'));
}
handleHotkeyOpenProfile = () => {
@@ -336,6 +336,7 @@ export default class Status extends ImmutablePureComponent {
id={id}
onMoveUp={this.handleMoveUp}
onMoveDown={this.handleMoveDown}
contextType='thread'
/>
));
}

View File

@@ -16,6 +16,8 @@ const messages = defineMessages({
next: { id: 'lightbox.next', defaultMessage: 'Next' },
});
const previewState = 'previewMediaModal';
@injectIntl
export default class MediaModal extends ImmutablePureComponent {
@@ -26,6 +28,10 @@ export default class MediaModal extends ImmutablePureComponent {
intl: PropTypes.object.isRequired,
};
static contextTypes = {
router: PropTypes.object,
};
state = {
index: null,
navigationHidden: false,
@@ -61,10 +67,24 @@ export default class MediaModal extends ImmutablePureComponent {
componentDidMount () {
window.addEventListener('keyup', this.handleKeyUp, false);
if (this.context.router) {
const history = this.context.router.history;
history.push(history.location.pathname, previewState);
this.unlistenHistory = history.listen(() => {
this.props.onClose();
});
}
}
componentWillUnmount () {
window.removeEventListener('keyup', this.handleKeyUp);
if (this.context.router) {
this.unlistenHistory();
if (this.context.router.history.location.state === previewState) {
this.context.router.history.goBack();
}
}
}
getIndex () {

View File

@@ -8,10 +8,10 @@ import { isUserTouching } from '../../../is_mobile';
export const links = [
<NavLink className='tabs-bar__link primary' to='/timelines/home' data-preview-title-id='column.home' data-preview-icon='home' ><i className='fa fa-fw fa-home' /><FormattedMessage id='tabs_bar.home' defaultMessage='Home' /></NavLink>,
<NavLink className='tabs-bar__link primary' to='/notifications' data-preview-title-id='column.notifications' data-preview-icon='bell' ><i className='fa fa-fw fa-bell' /><FormattedMessage id='tabs_bar.notifications' defaultMessage='Notifications' /></NavLink>,
<NavLink className='tabs-bar__link primary' to='/search' data-preview-title-id='tabs_bar.search' data-preview-icon='bell' ><i className='fa fa-fw fa-search' /><FormattedMessage id='tabs_bar.search' defaultMessage='Search' /></NavLink>,
<NavLink className='tabs-bar__link secondary' to='/timelines/public/local' data-preview-title-id='column.community' data-preview-icon='users' ><i className='fa fa-fw fa-users' /><FormattedMessage id='tabs_bar.local_timeline' defaultMessage='Local' /></NavLink>,
<NavLink className='tabs-bar__link secondary' exact to='/timelines/public' data-preview-title-id='column.public' data-preview-icon='globe' ><i className='fa fa-fw fa-globe' /><FormattedMessage id='tabs_bar.federated_timeline' defaultMessage='Federated' /></NavLink>,
<NavLink className='tabs-bar__link primary' to='/search' data-preview-title-id='tabs_bar.search' data-preview-icon='bell' ><i className='fa fa-fw fa-search' /><FormattedMessage id='tabs_bar.search' defaultMessage='Search' /></NavLink>,
<NavLink className='tabs-bar__link primary' style={{ flexGrow: '0', flexBasis: '30px' }} to='/getting-started' data-preview-title-id='getting_started.heading' data-preview-icon='bars' ><i className='fa fa-fw fa-bars' /></NavLink>,
];

View File

@@ -11,15 +11,6 @@ const makeGetStatusIds = () => createSelector([
(state, { type }) => state.getIn(['timelines', type, 'items'], ImmutableList()),
(state) => state.get('statuses'),
], (columnSettings, statusIds, statuses) => {
const rawRegex = columnSettings.getIn(['regex', 'body'], '').trim();
let regex = null;
try {
regex = rawRegex && new RegExp(rawRegex, 'i');
} catch (e) {
// Bad regex, don't affect filters
}
return statusIds.filter(id => {
if (id === null) return true;
@@ -34,11 +25,6 @@ const makeGetStatusIds = () => createSelector([
showStatus = showStatus && (statusForId.get('in_reply_to_id') === null || statusForId.get('in_reply_to_account_id') === me);
}
if (showStatus && regex && statusForId.get('account') !== me) {
const searchIndex = statusForId.get('reblog') ? statuses.getIn([statusForId.get('reblog'), 'search_index']) : statusForId.get('search_index');
showStatus = !regex.test(searchIndex);
}
return showStatus;
});
});

View File

@@ -12,6 +12,7 @@ import { debounce } from 'lodash';
import { uploadCompose, resetCompose } from '../../actions/compose';
import { expandHomeTimeline } from '../../actions/timelines';
import { expandNotifications } from '../../actions/notifications';
import { fetchFilters } from '../../actions/filters';
import { clearHeight } from '../../actions/height_cache';
import { WrappedSwitch, WrappedRoute } from './util/react_router_helpers';
import UploadArea from './components/upload_area';
@@ -297,6 +298,7 @@ export default class UI extends React.PureComponent {
this.props.dispatch(expandHomeTimeline());
this.props.dispatch(expandNotifications());
setTimeout(() => this.props.dispatch(fetchFilters()), 500);
}
componentDidMount () {