Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to show local-only toots in public timeline #1372

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion app/controllers/api/v1/timelines/public_controller.rb
Expand Up @@ -29,6 +29,8 @@ def public_statuses
params_slice(:max_id, :since_id, :min_id)
)

statuses = statuses.not_local_only unless truthy_param?(:allow_local_only)

if truthy_param?(:only_media)
# `SELECT DISTINCT id, updated_at` is too slow, so pluck ids at first, and then select id, updated_at with ids.
status_ids = statuses.joins(:media_attachments).distinct(:id).pluck(:id)
Expand All @@ -47,7 +49,7 @@ def insert_pagination_headers
end

def pagination_params(core_params)
params.slice(:local, :remote, :limit, :only_media).permit(:local, :remote, :limit, :only_media).merge(core_params)
params.slice(:local, :remote, :limit, :only_media, :allow_local_only).permit(:local, :remote, :limit, :only_media, :allow_local_only).merge(core_params)
end

def next_path
Expand Down
4 changes: 3 additions & 1 deletion app/javascript/flavours/glitch/actions/compose.js
Expand Up @@ -193,7 +193,9 @@ export function submitCompose(routerHistory) {

if (response.data.in_reply_to_id === null && response.data.visibility === 'public') {
insertIfOnline('community');
insertIfOnline('public');
if (!response.data.local_only) {
insertIfOnline('public');
}
} else if (response.data.visibility === 'direct') {
insertIfOnline('direct');
}
Expand Down
2 changes: 1 addition & 1 deletion app/javascript/flavours/glitch/actions/streaming.js
Expand Up @@ -73,7 +73,7 @@ const refreshHomeTimelineAndNotification = (dispatch, done) => {

export const connectUserStream = () => connectTimelineStream('home', 'user', refreshHomeTimelineAndNotification);
export const connectCommunityStream = ({ onlyMedia } = {}) => connectTimelineStream(`community${onlyMedia ? ':media' : ''}`, `public:local${onlyMedia ? ':media' : ''}`);
export const connectPublicStream = ({ onlyMedia, onlyRemote } = {}) => connectTimelineStream(`public${onlyRemote ? ':remote' : ''}${onlyMedia ? ':media' : ''}`, `public${onlyRemote ? ':remote' : ''}${onlyMedia ? ':media' : ''}`);
export const connectPublicStream = ({ onlyMedia, onlyRemote, allowLocalOnly } = {}) => connectTimelineStream(`public${onlyRemote ? ':remote' : (allowLocalOnly ? ':allow_local_only' : '')}${onlyMedia ? ':media' : ''}`, `public${onlyRemote ? ':remote' : (allowLocalOnly ? ':allow_local_only' : '')}${onlyMedia ? ':media' : ''}`);
export const connectHashtagStream = (id, tag, local, accept) => connectTimelineStream(`hashtag:${id}${local ? ':local' : ''}`, `hashtag${local ? ':local' : ''}&tag=${tag}`, null, accept);
export const connectDirectStream = () => connectTimelineStream('direct', 'direct');
export const connectListStream = id => connectTimelineStream(`list:${id}`, `list&list=${id}`);
2 changes: 1 addition & 1 deletion app/javascript/flavours/glitch/actions/timelines.js
Expand Up @@ -130,7 +130,7 @@ export function expandTimeline(timelineId, path, params = {}, done = noOp) {
};

export const expandHomeTimeline = ({ maxId } = {}, done = noOp) => expandTimeline('home', '/api/v1/timelines/home', { max_id: maxId }, done);
export const expandPublicTimeline = ({ maxId, onlyMedia, onlyRemote } = {}, done = noOp) => expandTimeline(`public${onlyRemote ? ':remote' : ''}${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { remote: !!onlyRemote, max_id: maxId, only_media: !!onlyMedia }, done);
export const expandPublicTimeline = ({ maxId, onlyMedia, onlyRemote, allowLocalOnly } = {}, done = noOp) => expandTimeline(`public${onlyRemote ? ':remote' : (allowLocalOnly ? ':allow_local_only' : '')}${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { remote: !!onlyRemote, allow_local_only: !!allowLocalOnly, max_id: maxId, only_media: !!onlyMedia }, done);
export const expandCommunityTimeline = ({ maxId, onlyMedia } = {}, done = noOp) => expandTimeline(`community${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { local: true, max_id: maxId, only_media: !!onlyMedia }, done);
export const expandDirectTimeline = ({ maxId } = {}, done = noOp) => expandTimeline('direct', '/api/v1/timelines/direct', { max_id: maxId }, done);
export const expandAccountTimeline = (accountId, { maxId, withReplies } = {}) => expandTimeline(`account:${accountId}${withReplies ? ':with_replies' : ''}`, `/api/v1/accounts/${accountId}/statuses`, { exclude_replies: !withReplies, max_id: maxId });
Expand Down
Expand Up @@ -22,6 +22,7 @@ class ColumnSettings extends React.PureComponent {
<div className='column-settings__row'>
<SettingToggle settings={settings} settingPath={['other', 'onlyMedia']} onChange={onChange} label={<FormattedMessage id='community.column_settings.media_only' defaultMessage='Media only' />} />
<SettingToggle settings={settings} settingPath={['other', 'onlyRemote']} onChange={onChange} label={<FormattedMessage id='community.column_settings.remote_only' defaultMessage='Remote only' />} />
{!settings.getIn(['other', 'onlyRemote']) && <SettingToggle settings={settings} settingPath={['other', 'allowLocalOnly']} onChange={onChange} label={<FormattedMessage id='community.column_settings.allow_local_only' defaultMessage='Show local-only toots' />} />}
</div>
</div>
);
Expand Down
29 changes: 16 additions & 13 deletions app/javascript/flavours/glitch/features/public_timeline/index.js
Expand Up @@ -20,12 +20,14 @@ const mapStateToProps = (state, { columnId }) => {
const index = columns.findIndex(c => c.get('uuid') === uuid);
const onlyMedia = (columnId && index >= 0) ? columns.get(index).getIn(['params', 'other', 'onlyMedia']) : state.getIn(['settings', 'public', 'other', 'onlyMedia']);
const onlyRemote = (columnId && index >= 0) ? columns.get(index).getIn(['params', 'other', 'onlyRemote']) : state.getIn(['settings', 'public', 'other', 'onlyRemote']);
const allowLocalOnly = (columnId && index >= 0) ? columns.get(index).getIn(['params', 'other', 'allowLocalOnly']) : state.getIn(['settings', 'public', 'other', 'allowLocalOnly']);
const timelineState = state.getIn(['timelines', `public${onlyMedia ? ':media' : ''}`]);

return {
hasUnread: !!timelineState && timelineState.get('unread') > 0,
onlyMedia,
onlyRemote,
allowLocalOnly,
};
};

Expand All @@ -49,15 +51,16 @@ class PublicTimeline extends React.PureComponent {
hasUnread: PropTypes.bool,
onlyMedia: PropTypes.bool,
onlyRemote: PropTypes.bool,
allowLocalOnly: PropTypes.bool,
};

handlePin = () => {
const { columnId, dispatch, onlyMedia, onlyRemote } = this.props;
const { columnId, dispatch, onlyMedia, onlyRemote, allowLocalOnly } = this.props;

if (columnId) {
dispatch(removeColumn(columnId));
} else {
dispatch(addColumn(onlyRemote ? 'REMOTE' : 'PUBLIC', { other: { onlyMedia, onlyRemote } }));
dispatch(addColumn(onlyRemote ? 'REMOTE' : 'PUBLIC', { other: { onlyMedia, onlyRemote, allowLocalOnly } }));
}
}

Expand All @@ -71,19 +74,19 @@ class PublicTimeline extends React.PureComponent {
}

componentDidMount () {
const { dispatch, onlyMedia, onlyRemote } = this.props;
const { dispatch, onlyMedia, onlyRemote, allowLocalOnly } = this.props;

dispatch(expandPublicTimeline({ onlyMedia, onlyRemote }));
this.disconnect = dispatch(connectPublicStream({ onlyMedia, onlyRemote }));
dispatch(expandPublicTimeline({ onlyMedia, onlyRemote, allowLocalOnly }));
this.disconnect = dispatch(connectPublicStream({ onlyMedia, onlyRemote, allowLocalOnly }));
}

componentDidUpdate (prevProps) {
if (prevProps.onlyMedia !== this.props.onlyMedia || prevProps.onlyRemote !== this.props.onlyRemote) {
const { dispatch, onlyMedia, onlyRemote } = this.props;
if (prevProps.onlyMedia !== this.props.onlyMedia || prevProps.onlyRemote !== this.props.onlyRemote || prevProps.allowLocalOnly !== this.props.allowLocalOnly) {
const { dispatch, onlyMedia, onlyRemote, allowLocalOnly } = this.props;

this.disconnect();
dispatch(expandPublicTimeline({ onlyMedia, onlyRemote }));
this.disconnect = dispatch(connectPublicStream({ onlyMedia, onlyRemote }));
dispatch(expandPublicTimeline({ onlyMedia, onlyRemote, allowLocalOnly }));
this.disconnect = dispatch(connectPublicStream({ onlyMedia, onlyRemote, allowLocalOnly }));
}
}

Expand All @@ -99,13 +102,13 @@ class PublicTimeline extends React.PureComponent {
}

handleLoadMore = maxId => {
const { dispatch, onlyMedia, onlyRemote } = this.props;
const { dispatch, onlyMedia, onlyRemote, allowLocalOnly } = this.props;

dispatch(expandPublicTimeline({ maxId, onlyMedia, onlyRemote }));
dispatch(expandPublicTimeline({ maxId, onlyMedia, onlyRemote, allowLocalOnly }));
}

render () {
const { intl, columnId, hasUnread, multiColumn, onlyMedia, onlyRemote } = this.props;
const { intl, columnId, hasUnread, multiColumn, onlyMedia, onlyRemote, allowLocalOnly } = this.props;
const pinned = !!columnId;

return (
Expand All @@ -124,7 +127,7 @@ class PublicTimeline extends React.PureComponent {
</ColumnHeader>

<StatusListContainer
timelineId={`public${onlyRemote ? ':remote' : ''}${onlyMedia ? ':media' : ''}`}
timelineId={`public${onlyRemote ? ':remote' : (allowLocalOnly ? ':allow_local_only' : '')}${onlyMedia ? ':media' : ''}`}
onLoadMore={this.handleLoadMore}
trackScroll={!pinned}
scrollKey={`public_timeline-${columnId}`}
Expand Down
4 changes: 3 additions & 1 deletion app/javascript/mastodon/actions/compose.js
Expand Up @@ -173,7 +173,9 @@ export function submitCompose(routerHistory) {

if (response.data.in_reply_to_id === null && response.data.visibility === 'public') {
insertIfOnline('community');
insertIfOnline('public');
if (!response.data.local_only) {
insertIfOnline('public');
}
}
}).catch(function (error) {
dispatch(submitComposeFail(error));
Expand Down
21 changes: 15 additions & 6 deletions streaming/index.js
Expand Up @@ -307,6 +307,7 @@ const startWorker = (workerId) => {

const PUBLIC_ENDPOINTS = [
'/api/v1/streaming/public',
'/api/v1/streaming/public/allow_local_only',
'/api/v1/streaming/public/local',
'/api/v1/streaming/public/remote',
'/api/v1/streaming/hashtag',
Expand Down Expand Up @@ -362,7 +363,7 @@ const startWorker = (workerId) => {
});
};

const streamFrom = (ids, req, output, attachCloseHandler, needsFiltering = false, notificationOnly = false) => {
const streamFrom = (ids, req, output, attachCloseHandler, needsFiltering = false, notificationOnly = false, allowLocalOnly = false) => {
const accountId = req.accountId || req.remoteAddress;
const streamType = notificationOnly ? ' (notification)' : '';

Expand Down Expand Up @@ -393,7 +394,7 @@ const startWorker = (workerId) => {
}

// Only send local-only statuses to logged-in users
if (event === 'update' && payload.local_only && !req.accountId) {
if (event === 'update' && payload.local_only && !req.accountId || !allowLocalOnly) {
log.silly(req.requestId, `Message ${payload.id} filtered because it was local-only`);
return;
}
Expand Down Expand Up @@ -562,14 +563,16 @@ const startWorker = (workerId) => {
const onlyMedia = req.query.only_media === '1' || req.query.only_media === 'true';
const channel = onlyMedia ? 'timeline:public:media' : 'timeline:public';

streamFrom(channel, req, streamToHttp(req, res), streamHttpEnd(req), true);
const allowLocalOnly = req.query.allow_local_only === '1' || req.query.allow_local_only === 'true';

streamFrom(channel, req, streamToHttp(req, res), streamHttpEnd(req), true, false, allowLocalOnly);
});

app.get('/api/v1/streaming/public/local', (req, res) => {
const onlyMedia = req.query.only_media === '1' || req.query.only_media === 'true';
const channel = onlyMedia ? 'timeline:public:local:media' : 'timeline:public:local';

streamFrom(channel, req, streamToHttp(req, res), streamHttpEnd(req), true);
streamFrom(channel, req, streamToHttp(req, res), streamHttpEnd(req), true, false, true);
});

app.get('/api/v1/streaming/public/remote', (req, res) => {
Expand Down Expand Up @@ -645,17 +648,23 @@ const startWorker = (workerId) => {
case 'public':
streamFrom('timeline:public', req, streamToWs(req, ws), streamWsEnd(req, ws), true);
break;
case 'public:allow_local_only':
streamFrom('timeline:public', req, streamToWs(req, ws), streamWsEnd(req, ws), true, false, true);
break;
case 'public:local':
streamFrom('timeline:public:local', req, streamToWs(req, ws), streamWsEnd(req, ws), true);
streamFrom('timeline:public:local', req, streamToWs(req, ws), streamWsEnd(req, ws), true, false, true);
break;
case 'public:remote':
streamFrom('timeline:public:remote', req, streamToWs(req, ws), streamWsEnd(req, ws), true);
break;
case 'public:media':
streamFrom('timeline:public:media', req, streamToWs(req, ws), streamWsEnd(req, ws), true);
break;
case 'public:allow_local_only:media':
streamFrom('timeline:public:media', req, streamToWs(req, ws), streamWsEnd(req, ws), true, false, true);
break;
case 'public:local:media':
streamFrom('timeline:public:local:media', req, streamToWs(req, ws), streamWsEnd(req, ws), true);
streamFrom('timeline:public:local:media', req, streamToWs(req, ws), streamWsEnd(req, ws), true, false, true);
break;
case 'public:remote:media':
streamFrom('timeline:public:remote:media', req, streamToWs(req, ws), streamWsEnd(req, ws), true);
Expand Down