Merge pull request #1145 from ThibG/glitch-soc/merge-upstream

Merge upstream changes
master
ThibG 2019-06-28 19:11:06 +02:00 committed by GitHub
commit f7c0e326ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 99 additions and 12 deletions

View File

@ -48,7 +48,7 @@ export function submitSearch() {
dispatch(importFetchedStatuses(response.data.statuses)); dispatch(importFetchedStatuses(response.data.statuses));
} }
dispatch(fetchSearchSuccess(response.data)); dispatch(fetchSearchSuccess(response.data, value));
dispatch(fetchRelationships(response.data.accounts.map(item => item.id))); dispatch(fetchRelationships(response.data.accounts.map(item => item.id)));
}).catch(error => { }).catch(error => {
dispatch(fetchSearchFail(error)); dispatch(fetchSearchFail(error));
@ -62,12 +62,11 @@ export function fetchSearchRequest() {
}; };
}; };
export function fetchSearchSuccess(results) { export function fetchSearchSuccess(results, searchTerm) {
return { return {
type: SEARCH_FETCH_SUCCESS, type: SEARCH_FETCH_SUCCESS,
results, results,
accounts: results.accounts, searchTerm,
statuses: results.statuses,
}; };
}; };

View File

@ -7,6 +7,7 @@ import StatusContainer from 'flavours/glitch/containers/status_container';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import Hashtag from 'flavours/glitch/components/hashtag'; import Hashtag from 'flavours/glitch/components/hashtag';
import Icon from 'flavours/glitch/components/icon'; import Icon from 'flavours/glitch/components/icon';
import { searchEnabled } from 'flavours/glitch/util/initial_state';
const messages = defineMessages({ const messages = defineMessages({
dismissSuggestion: { id: 'suggestions.dismiss', defaultMessage: 'Dismiss suggestion' }, dismissSuggestion: { id: 'suggestions.dismiss', defaultMessage: 'Dismiss suggestion' },
@ -20,6 +21,7 @@ class SearchResults extends ImmutablePureComponent {
suggestions: ImmutablePropTypes.list.isRequired, suggestions: ImmutablePropTypes.list.isRequired,
fetchSuggestions: PropTypes.func.isRequired, fetchSuggestions: PropTypes.func.isRequired,
dismissSuggestion: PropTypes.func.isRequired, dismissSuggestion: PropTypes.func.isRequired,
searchTerm: PropTypes.string,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
}; };
@ -27,8 +29,8 @@ class SearchResults extends ImmutablePureComponent {
this.props.fetchSuggestions(); this.props.fetchSuggestions();
} }
render() { render () {
const { intl, results, suggestions, dismissSuggestion } = this.props; const { intl, results, suggestions, dismissSuggestion, searchTerm } = this.props;
if (results.isEmpty() && !suggestions.isEmpty()) { if (results.isEmpty() && !suggestions.isEmpty()) {
return ( return (
@ -51,6 +53,16 @@ class SearchResults extends ImmutablePureComponent {
</div> </div>
</div> </div>
); );
} else if(results.get('statuses') && results.get('statuses').size === 0 && !searchEnabled && !(searchTerm.startsWith('@') || searchTerm.startsWith('#') || searchTerm.includes(' '))) {
statuses = (
<section>
<h5><Icon id='quote-right' fixedWidth /><FormattedMessage id='search_results.statuses' defaultMessage='Toots' /></h5>
<div className='search-results__info'>
<FormattedMessage id='search_results.statuses_fts_disabled' defaultMessage='Searching toots by their content is not enabled on this Mastodon server.' />
</div>
</section>
);
} }
let accounts, statuses, hashtags; let accounts, statuses, hashtags;

View File

@ -5,6 +5,7 @@ import { fetchSuggestions, dismissSuggestion } from '../../../actions/suggestion
const mapStateToProps = state => ({ const mapStateToProps = state => ({
results: state.getIn(['search', 'results']), results: state.getIn(['search', 'results']),
suggestions: state.getIn(['suggestions', 'items']), suggestions: state.getIn(['suggestions', 'items']),
searchTerm: state.getIn(['search', 'searchTerm']),
}); });
const mapDispatchToProps = dispatch => ({ const mapDispatchToProps = dispatch => ({

View File

@ -16,6 +16,7 @@ const initialState = ImmutableMap({
submitted: false, submitted: false,
hidden: false, hidden: false,
results: ImmutableMap(), results: ImmutableMap(),
searchTerm: '',
}); });
export default function search(state = initialState, action) { export default function search(state = initialState, action) {
@ -40,7 +41,7 @@ export default function search(state = initialState, action) {
accounts: ImmutableList(action.results.accounts.map(item => item.id)), accounts: ImmutableList(action.results.accounts.map(item => item.id)),
statuses: ImmutableList(action.results.statuses.map(item => item.id)), statuses: ImmutableList(action.results.statuses.map(item => item.id)),
hashtags: fromJS(action.results.hashtags), hashtags: fromJS(action.results.hashtags),
})).set('submitted', true); })).set('submitted', true).set('searchTerm', action.searchTerm);
default: default:
return state; return state;
} }

View File

@ -78,6 +78,11 @@
font-weight: 500; font-weight: 500;
} }
.search-results__info {
padding: 10px;
color: $secondary-text-color;
}
.trends { .trends {
&__header { &__header {
color: $dark-text-color; color: $dark-text-color;

View File

@ -48,7 +48,7 @@ export function submitSearch() {
dispatch(importFetchedStatuses(response.data.statuses)); dispatch(importFetchedStatuses(response.data.statuses));
} }
dispatch(fetchSearchSuccess(response.data)); dispatch(fetchSearchSuccess(response.data, value));
dispatch(fetchRelationships(response.data.accounts.map(item => item.id))); dispatch(fetchRelationships(response.data.accounts.map(item => item.id)));
}).catch(error => { }).catch(error => {
dispatch(fetchSearchFail(error)); dispatch(fetchSearchFail(error));
@ -62,10 +62,11 @@ export function fetchSearchRequest() {
}; };
}; };
export function fetchSearchSuccess(results) { export function fetchSearchSuccess(results, searchTerm) {
return { return {
type: SEARCH_FETCH_SUCCESS, type: SEARCH_FETCH_SUCCESS,
results, results,
searchTerm,
}; };
}; };

View File

@ -7,6 +7,7 @@ import StatusContainer from '../../../containers/status_container';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import Hashtag from '../../../components/hashtag'; import Hashtag from '../../../components/hashtag';
import Icon from 'mastodon/components/icon'; import Icon from 'mastodon/components/icon';
import { searchEnabled } from '../../../initial_state';
const messages = defineMessages({ const messages = defineMessages({
dismissSuggestion: { id: 'suggestions.dismiss', defaultMessage: 'Dismiss suggestion' }, dismissSuggestion: { id: 'suggestions.dismiss', defaultMessage: 'Dismiss suggestion' },
@ -20,6 +21,7 @@ class SearchResults extends ImmutablePureComponent {
suggestions: ImmutablePropTypes.list.isRequired, suggestions: ImmutablePropTypes.list.isRequired,
fetchSuggestions: PropTypes.func.isRequired, fetchSuggestions: PropTypes.func.isRequired,
dismissSuggestion: PropTypes.func.isRequired, dismissSuggestion: PropTypes.func.isRequired,
searchTerm: PropTypes.string,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
}; };
@ -28,7 +30,7 @@ class SearchResults extends ImmutablePureComponent {
} }
render () { render () {
const { intl, results, suggestions, dismissSuggestion } = this.props; const { intl, results, suggestions, dismissSuggestion, searchTerm } = this.props;
if (results.isEmpty() && !suggestions.isEmpty()) { if (results.isEmpty() && !suggestions.isEmpty()) {
return ( return (
@ -76,6 +78,16 @@ class SearchResults extends ImmutablePureComponent {
{results.get('statuses').map(statusId => <StatusContainer key={statusId} id={statusId} />)} {results.get('statuses').map(statusId => <StatusContainer key={statusId} id={statusId} />)}
</div> </div>
); );
} else if(results.get('statuses') && results.get('statuses').size === 0 && !searchEnabled && !(searchTerm.startsWith('@') || searchTerm.startsWith('#') || searchTerm.includes(' '))) {
statuses = (
<div className='search-results__section'>
<h5><Icon id='quote-right' fixedWidth /><FormattedMessage id='search_results.statuses' defaultMessage='Toots' /></h5>
<div className='search-results__info'>
<FormattedMessage id='search_results.statuses_fts_disabled' defaultMessage='Searching toots by their content is not enabled on this Mastodon server.' />
</div>
</div>
);
} }
if (results.get('hashtags') && results.get('hashtags').size > 0) { if (results.get('hashtags') && results.get('hashtags').size > 0) {

View File

@ -5,6 +5,7 @@ import { fetchSuggestions, dismissSuggestion } from '../../../actions/suggestion
const mapStateToProps = state => ({ const mapStateToProps = state => ({
results: state.getIn(['search', 'results']), results: state.getIn(['search', 'results']),
suggestions: state.getIn(['suggestions', 'items']), suggestions: state.getIn(['suggestions', 'items']),
searchTerm: state.getIn(['search', 'value']),
}); });
const mapDispatchToProps = dispatch => ({ const mapDispatchToProps = dispatch => ({

View File

@ -16,6 +16,7 @@ const initialState = ImmutableMap({
submitted: false, submitted: false,
hidden: false, hidden: false,
results: ImmutableMap(), results: ImmutableMap(),
searchTerm: '',
}); });
export default function search(state = initialState, action) { export default function search(state = initialState, action) {
@ -40,7 +41,7 @@ export default function search(state = initialState, action) {
accounts: ImmutableList(action.results.accounts.map(item => item.id)), accounts: ImmutableList(action.results.accounts.map(item => item.id)),
statuses: ImmutableList(action.results.statuses.map(item => item.id)), statuses: ImmutableList(action.results.statuses.map(item => item.id)),
hashtags: fromJS(action.results.hashtags), hashtags: fromJS(action.results.hashtags),
})).set('submitted', true); })).set('submitted', true).set('searchTerm', action.searchTerm);
default: default:
return state; return state;
} }

View File

@ -3996,6 +3996,11 @@ a.status-card.compact:hover {
} }
} }
.search-results__info {
padding: 10px;
color: $secondary-text-color;
}
.modal-root { .modal-root {
position: relative; position: relative;
transition: opacity 0.3s linear; transition: opacity 0.3s linear;

View File

@ -14,7 +14,7 @@ class ActivityPub::UpdatePollSerializer < ActivityPub::Serializer
end end
def actor def actor
ActivityPub::TagManager.instance.uri_for(object) ActivityPub::TagManager.instance.uri_for(object.account)
end end
def to def to

View File

@ -0,0 +1,27 @@
# frozen_string_literal: true
require 'rails_helper'
describe ActivityPub::UpdatePollSerializer do
let(:account) { Fabricate(:account) }
let(:poll) { Fabricate(:poll, account: account) }
let!(:status) { Fabricate(:status, account: account, poll: poll) }
before(:each) do
@serialization = ActiveModelSerializers::SerializableResource.new(status, serializer: ActivityPub::UpdatePollSerializer, adapter: ActivityPub::Adapter)
end
subject { JSON.parse(@serialization.to_json) }
it 'has a Update type' do
expect(subject['type']).to eql('Update')
end
it 'has an object with Question type' do
expect(subject['object']['type']).to eql('Question')
end
it 'has the correct actor URI set' do
expect(subject['actor']).to eql(ActivityPub::TagManager.instance.uri_for(account))
end
end

View File

@ -0,0 +1,22 @@
require 'rails_helper'
describe ActivityPub::DistributePollUpdateWorker do
subject { described_class.new }
let(:account) { Fabricate(:account) }
let(:follower) { Fabricate(:account, protocol: :activitypub, inbox_url: 'http://example.com') }
let(:poll) { Fabricate(:poll, account: account) }
let!(:status) { Fabricate(:status, account: account, poll: poll) }
describe '#perform' do
before do
allow(ActivityPub::DeliveryWorker).to receive(:push_bulk)
follower.follow!(account)
end
it 'delivers to followers' do
subject.perform(status.id)
expect(ActivityPub::DeliveryWorker).to have_received(:push_bulk).with(['http://example.com'])
end
end
end