Merge branch 'master' into glitch-soc/merge-upstream
This commit is contained in:
@@ -11,7 +11,7 @@ import { showAlertForError } from './alerts';
|
||||
import { showAlert } from './alerts';
|
||||
import { defineMessages } from 'react-intl';
|
||||
|
||||
let cancelFetchComposeSuggestionsAccounts;
|
||||
let cancelFetchComposeSuggestionsAccounts, cancelFetchComposeSuggestionsTags;
|
||||
|
||||
export const COMPOSE_CHANGE = 'COMPOSE_CHANGE';
|
||||
export const COMPOSE_SUBMIT_REQUEST = 'COMPOSE_SUBMIT_REQUEST';
|
||||
@@ -325,10 +325,12 @@ const fetchComposeSuggestionsAccounts = throttle((dispatch, getState, token) =>
|
||||
if (cancelFetchComposeSuggestionsAccounts) {
|
||||
cancelFetchComposeSuggestionsAccounts();
|
||||
}
|
||||
|
||||
api(getState).get('/api/v1/accounts/search', {
|
||||
cancelToken: new CancelToken(cancel => {
|
||||
cancelFetchComposeSuggestionsAccounts = cancel;
|
||||
}),
|
||||
|
||||
params: {
|
||||
q: token.slice(1),
|
||||
resolve: false,
|
||||
@@ -349,9 +351,30 @@ const fetchComposeSuggestionsEmojis = (dispatch, getState, token) => {
|
||||
dispatch(readyComposeSuggestionsEmojis(token, results));
|
||||
};
|
||||
|
||||
const fetchComposeSuggestionsTags = (dispatch, getState, token) => {
|
||||
dispatch(updateSuggestionTags(token));
|
||||
};
|
||||
const fetchComposeSuggestionsTags = throttle((dispatch, getState, token) => {
|
||||
if (cancelFetchComposeSuggestionsTags) {
|
||||
cancelFetchComposeSuggestionsTags();
|
||||
}
|
||||
|
||||
api(getState).get('/api/v2/search', {
|
||||
cancelToken: new CancelToken(cancel => {
|
||||
cancelFetchComposeSuggestionsTags = cancel;
|
||||
}),
|
||||
|
||||
params: {
|
||||
type: 'hashtags',
|
||||
q: token.slice(1),
|
||||
resolve: false,
|
||||
limit: 4,
|
||||
},
|
||||
}).then(({ data }) => {
|
||||
dispatch(readyComposeSuggestionsTags(token, data.hashtags));
|
||||
}).catch(error => {
|
||||
if (!isCancel(error)) {
|
||||
dispatch(showAlertForError(error));
|
||||
}
|
||||
});
|
||||
}, 200, { leading: true, trailing: true });
|
||||
|
||||
export function fetchComposeSuggestions(token) {
|
||||
return (dispatch, getState) => {
|
||||
@@ -385,6 +408,12 @@ export function readyComposeSuggestionsAccounts(token, accounts) {
|
||||
};
|
||||
};
|
||||
|
||||
export const readyComposeSuggestionsTags = (token, tags) => ({
|
||||
type: COMPOSE_SUGGESTIONS_READY,
|
||||
token,
|
||||
tags,
|
||||
});
|
||||
|
||||
export function selectComposeSuggestion(position, token, suggestion, path) {
|
||||
return (dispatch, getState) => {
|
||||
let completion, startPosition;
|
||||
@@ -394,8 +423,8 @@ export function selectComposeSuggestion(position, token, suggestion, path) {
|
||||
startPosition = position - 1;
|
||||
|
||||
dispatch(useEmoji(suggestion));
|
||||
} else if (suggestion[0] === '#') {
|
||||
completion = suggestion;
|
||||
} else if (typeof suggestion === 'object' && suggestion.name) {
|
||||
completion = `#${suggestion.name}`;
|
||||
startPosition = position - 1;
|
||||
} else {
|
||||
completion = getState().getIn(['accounts', suggestion, 'acct']);
|
||||
|
@@ -23,6 +23,7 @@ export function blockDomain(domain) {
|
||||
api(getState).post('/api/v1/domain_blocks', { domain }).then(() => {
|
||||
const at_domain = '@' + domain;
|
||||
const accounts = getState().get('accounts').filter(item => item.get('acct').endsWith(at_domain)).valueSeq().map(item => item.get('id'));
|
||||
|
||||
dispatch(blockDomainSuccess(domain, accounts));
|
||||
}).catch(err => {
|
||||
dispatch(blockDomainFail(domain, err));
|
||||
|
@@ -10,6 +10,10 @@ export const SEARCH_FETCH_REQUEST = 'SEARCH_FETCH_REQUEST';
|
||||
export const SEARCH_FETCH_SUCCESS = 'SEARCH_FETCH_SUCCESS';
|
||||
export const SEARCH_FETCH_FAIL = 'SEARCH_FETCH_FAIL';
|
||||
|
||||
export const SEARCH_EXPAND_REQUEST = 'SEARCH_EXPAND_REQUEST';
|
||||
export const SEARCH_EXPAND_SUCCESS = 'SEARCH_EXPAND_SUCCESS';
|
||||
export const SEARCH_EXPAND_FAIL = 'SEARCH_EXPAND_FAIL';
|
||||
|
||||
export function changeSearch(value) {
|
||||
return {
|
||||
type: SEARCH_CHANGE,
|
||||
@@ -77,8 +81,50 @@ export function fetchSearchFail(error) {
|
||||
};
|
||||
};
|
||||
|
||||
export function showSearch() {
|
||||
return {
|
||||
type: SEARCH_SHOW,
|
||||
};
|
||||
export const expandSearch = type => (dispatch, getState) => {
|
||||
const value = getState().getIn(['search', 'value']);
|
||||
const offset = getState().getIn(['search', 'results', type]).size;
|
||||
|
||||
dispatch(expandSearchRequest());
|
||||
|
||||
api(getState).get('/api/v2/search', {
|
||||
params: {
|
||||
q: value,
|
||||
type,
|
||||
offset,
|
||||
},
|
||||
}).then(({ data }) => {
|
||||
if (data.accounts) {
|
||||
dispatch(importFetchedAccounts(data.accounts));
|
||||
}
|
||||
|
||||
if (data.statuses) {
|
||||
dispatch(importFetchedStatuses(data.statuses));
|
||||
}
|
||||
|
||||
dispatch(expandSearchSuccess(data, value, type));
|
||||
dispatch(fetchRelationships(data.accounts.map(item => item.id)));
|
||||
}).catch(error => {
|
||||
dispatch(expandSearchFail(error));
|
||||
});
|
||||
};
|
||||
|
||||
export const expandSearchRequest = () => ({
|
||||
type: SEARCH_EXPAND_REQUEST,
|
||||
});
|
||||
|
||||
export const expandSearchSuccess = (results, searchTerm, searchType) => ({
|
||||
type: SEARCH_EXPAND_SUCCESS,
|
||||
results,
|
||||
searchTerm,
|
||||
searchType,
|
||||
});
|
||||
|
||||
export const expandSearchFail = error => ({
|
||||
type: SEARCH_EXPAND_FAIL,
|
||||
error,
|
||||
});
|
||||
|
||||
export const showSearch = () => ({
|
||||
type: SEARCH_SHOW,
|
||||
});
|
||||
|
28
app/javascript/mastodon/components/autosuggest_hashtag.js
Normal file
28
app/javascript/mastodon/components/autosuggest_hashtag.js
Normal file
@@ -0,0 +1,28 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { shortNumberFormat } from 'mastodon/utils/numbers';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
export default class AutosuggestHashtag extends React.PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
tag: PropTypes.shape({
|
||||
name: PropTypes.string.isRequired,
|
||||
url: PropTypes.string,
|
||||
history: PropTypes.array.isRequired,
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
render () {
|
||||
const { tag } = this.props;
|
||||
const weeklyUses = shortNumberFormat(tag.history.reduce((total, day) => total + (day.uses * 1), 0));
|
||||
|
||||
return (
|
||||
<div className='autosuggest-hashtag'>
|
||||
<div className='autosuggest-hashtag__name'>#<strong>{tag.name}</strong></div>
|
||||
<div className='autosuggest-hashtag__uses'><FormattedMessage id='autosuggest_hashtag.per_week' defaultMessage='{count} per week' values={{ count: weeklyUses }} /></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import AutosuggestAccountContainer from '../features/compose/containers/autosuggest_account_container';
|
||||
import AutosuggestEmoji from './autosuggest_emoji';
|
||||
import AutosuggestHashtag from './autosuggest_hashtag';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import PropTypes from 'prop-types';
|
||||
import { isRtl } from '../rtl';
|
||||
@@ -167,12 +168,12 @@ export default class AutosuggestInput extends ImmutablePureComponent {
|
||||
const { selectedSuggestion } = this.state;
|
||||
let inner, key;
|
||||
|
||||
if (typeof suggestion === 'object') {
|
||||
if (typeof suggestion === 'object' && suggestion.shortcode) {
|
||||
inner = <AutosuggestEmoji emoji={suggestion} />;
|
||||
key = suggestion.id;
|
||||
} else if (suggestion[0] === '#') {
|
||||
inner = suggestion;
|
||||
key = suggestion;
|
||||
} else if (typeof suggestion === 'object' && suggestion.name) {
|
||||
inner = <AutosuggestHashtag tag={suggestion} />;
|
||||
key = suggestion.name;
|
||||
} else {
|
||||
inner = <AutosuggestAccountContainer id={suggestion} />;
|
||||
key = suggestion;
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import AutosuggestAccountContainer from '../features/compose/containers/autosuggest_account_container';
|
||||
import AutosuggestEmoji from './autosuggest_emoji';
|
||||
import AutosuggestHashtag from './autosuggest_hashtag';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import PropTypes from 'prop-types';
|
||||
import { isRtl } from '../rtl';
|
||||
@@ -173,12 +174,12 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
||||
const { selectedSuggestion } = this.state;
|
||||
let inner, key;
|
||||
|
||||
if (typeof suggestion === 'object') {
|
||||
if (typeof suggestion === 'object' && suggestion.shortcode) {
|
||||
inner = <AutosuggestEmoji emoji={suggestion} />;
|
||||
key = suggestion.id;
|
||||
} else if (suggestion[0] === '#') {
|
||||
inner = suggestion;
|
||||
key = suggestion;
|
||||
} else if (typeof suggestion === 'object' && suggestion.name) {
|
||||
inner = <AutosuggestHashtag tag={suggestion} />;
|
||||
key = suggestion.name;
|
||||
} else {
|
||||
inner = <AutosuggestAccountContainer id={suggestion} />;
|
||||
key = suggestion;
|
||||
|
@@ -55,6 +55,7 @@ export default class StatusContent extends React.PureComponent {
|
||||
link.addEventListener('click', this.onHashtagClick.bind(this, link.text), false);
|
||||
} else {
|
||||
link.setAttribute('title', link.href);
|
||||
link.classList.add('unhandled-link');
|
||||
}
|
||||
|
||||
link.setAttribute('target', '_blank');
|
||||
@@ -233,46 +234,23 @@ export default class StatusContent extends React.PureComponent {
|
||||
</div>
|
||||
);
|
||||
} else if (this.props.onClick) {
|
||||
const output = [
|
||||
<div
|
||||
ref={this.setRef}
|
||||
tabIndex='0'
|
||||
key='content'
|
||||
className={classNames}
|
||||
style={directionStyle}
|
||||
dangerouslySetInnerHTML={content}
|
||||
lang={status.get('language')}
|
||||
onMouseDown={this.handleMouseDown}
|
||||
onMouseUp={this.handleMouseUp}
|
||||
/>,
|
||||
];
|
||||
return (
|
||||
<div className={classNames} ref={this.setRef} tabIndex='0' style={directionStyle} onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp}>
|
||||
<div className='status__content__text status__content__text--visible' style={directionStyle} dangerouslySetInnerHTML={content} lang={status.get('language')} />
|
||||
|
||||
if (this.state.collapsed) {
|
||||
output.push(readMoreButton);
|
||||
}
|
||||
{!!this.state.collapsed && readMoreButton}
|
||||
|
||||
if (status.get('poll')) {
|
||||
output.push(<PollContainer pollId={status.get('poll')} />);
|
||||
}
|
||||
|
||||
return output;
|
||||
{!!status.get('poll') && <PollContainer pollId={status.get('poll')} />}
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
const output = [
|
||||
<div
|
||||
tabIndex='0'
|
||||
ref={this.setRef}
|
||||
className='status__content'
|
||||
style={directionStyle}
|
||||
dangerouslySetInnerHTML={content}
|
||||
lang={status.get('language')}
|
||||
/>,
|
||||
];
|
||||
return (
|
||||
<div className={classNames} ref={this.setRef} tabIndex='0' style={directionStyle}>
|
||||
<div className='status__content__text status__content__text--visible' style={directionStyle} dangerouslySetInnerHTML={content} lang={status.get('language')} />
|
||||
|
||||
if (status.get('poll')) {
|
||||
output.push(<PollContainer pollId={status.get('poll')} />);
|
||||
}
|
||||
|
||||
return output;
|
||||
{!!status.get('poll') && <PollContainer pollId={status.get('poll')} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -8,6 +8,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import Hashtag from '../../../components/hashtag';
|
||||
import Icon from 'mastodon/components/icon';
|
||||
import { searchEnabled } from '../../../initial_state';
|
||||
import LoadMore from 'mastodon/components/load_more';
|
||||
|
||||
const messages = defineMessages({
|
||||
dismissSuggestion: { id: 'suggestions.dismiss', defaultMessage: 'Dismiss suggestion' },
|
||||
@@ -20,15 +21,24 @@ class SearchResults extends ImmutablePureComponent {
|
||||
results: ImmutablePropTypes.map.isRequired,
|
||||
suggestions: ImmutablePropTypes.list.isRequired,
|
||||
fetchSuggestions: PropTypes.func.isRequired,
|
||||
expandSearch: PropTypes.func.isRequired,
|
||||
dismissSuggestion: PropTypes.func.isRequired,
|
||||
searchTerm: PropTypes.string,
|
||||
intl: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
this.props.fetchSuggestions();
|
||||
if (this.props.searchTerm === '') {
|
||||
this.props.fetchSuggestions();
|
||||
}
|
||||
}
|
||||
|
||||
handleLoadMoreAccounts = () => this.props.expandSearch('accounts');
|
||||
|
||||
handleLoadMoreStatuses = () => this.props.expandSearch('statuses');
|
||||
|
||||
handleLoadMoreHashtags = () => this.props.expandSearch('hashtags');
|
||||
|
||||
render () {
|
||||
const { intl, results, suggestions, dismissSuggestion, searchTerm } = this.props;
|
||||
|
||||
@@ -65,6 +75,8 @@ class SearchResults extends ImmutablePureComponent {
|
||||
<h5><Icon id='users' fixedWidth /><FormattedMessage id='search_results.accounts' defaultMessage='People' /></h5>
|
||||
|
||||
{results.get('accounts').map(accountId => <AccountContainer key={accountId} id={accountId} />)}
|
||||
|
||||
{results.get('accounts').size >= 5 && <LoadMore visible onClick={this.handleLoadMoreAccounts} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -76,6 +88,8 @@ class SearchResults extends ImmutablePureComponent {
|
||||
<h5><Icon id='quote-right' fixedWidth /><FormattedMessage id='search_results.statuses' defaultMessage='Toots' /></h5>
|
||||
|
||||
{results.get('statuses').map(statusId => <StatusContainer key={statusId} id={statusId} />)}
|
||||
|
||||
{results.get('statuses').size >= 5 && <LoadMore visible onClick={this.handleLoadMoreStatuses} />}
|
||||
</div>
|
||||
);
|
||||
} else if(results.get('statuses') && results.get('statuses').size === 0 && !searchEnabled && !(searchTerm.startsWith('@') || searchTerm.startsWith('#') || searchTerm.includes(' '))) {
|
||||
@@ -97,6 +111,8 @@ class SearchResults extends ImmutablePureComponent {
|
||||
<h5><Icon id='hashtag' fixedWidth /><FormattedMessage id='search_results.hashtags' defaultMessage='Hashtags' /></h5>
|
||||
|
||||
{results.get('hashtags').map(hashtag => <Hashtag key={hashtag.get('name')} hashtag={hashtag} />)}
|
||||
|
||||
{results.get('hashtags').size >= 5 && <LoadMore visible onClick={this.handleLoadMoreHashtags} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import { connect } from 'react-redux';
|
||||
import SearchResults from '../components/search_results';
|
||||
import { fetchSuggestions, dismissSuggestion } from '../../../actions/suggestions';
|
||||
import { fetchSuggestions, dismissSuggestion } from 'mastodon/actions/suggestions';
|
||||
import { expandSearch } from 'mastodon/actions/search';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
results: state.getIn(['search', 'results']),
|
||||
@@ -10,6 +11,7 @@ const mapStateToProps = state => ({
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
fetchSuggestions: () => dispatch(fetchSuggestions()),
|
||||
expandSearch: type => dispatch(expandSearch(type)),
|
||||
dismissSuggestion: account => dispatch(dismissSuggestion(account.get('id'))),
|
||||
});
|
||||
|
||||
|
@@ -17,7 +17,6 @@ import {
|
||||
COMPOSE_SUGGESTIONS_CLEAR,
|
||||
COMPOSE_SUGGESTIONS_READY,
|
||||
COMPOSE_SUGGESTION_SELECT,
|
||||
COMPOSE_SUGGESTION_TAGS_UPDATE,
|
||||
COMPOSE_TAG_HISTORY_UPDATE,
|
||||
COMPOSE_SENSITIVITY_CHANGE,
|
||||
COMPOSE_SPOILERNESS_CHANGE,
|
||||
@@ -144,15 +143,20 @@ const insertSuggestion = (state, position, token, completion, path) => {
|
||||
});
|
||||
};
|
||||
|
||||
const updateSuggestionTags = (state, token) => {
|
||||
const prefix = token.slice(1);
|
||||
const sortHashtagsByUse = (state, tags) => {
|
||||
const personalHistory = state.get('tagHistory');
|
||||
|
||||
return state.merge({
|
||||
suggestions: state.get('tagHistory')
|
||||
.filter(tag => tag.toLowerCase().startsWith(prefix.toLowerCase()))
|
||||
.slice(0, 4)
|
||||
.map(tag => '#' + tag),
|
||||
suggestion_token: token,
|
||||
return tags.sort((a, b) => {
|
||||
const usedA = personalHistory.includes(a.name);
|
||||
const usedB = personalHistory.includes(b.name);
|
||||
|
||||
if (usedA === usedB) {
|
||||
return 0;
|
||||
} else if (usedA && !usedB) {
|
||||
return 1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -201,6 +205,16 @@ const expiresInFromExpiresAt = expires_at => {
|
||||
return [300, 1800, 3600, 21600, 86400, 259200, 604800].find(expires_in => expires_in >= delta) || 24 * 3600;
|
||||
};
|
||||
|
||||
const normalizeSuggestions = (state, { accounts, emojis, tags }) => {
|
||||
if (accounts) {
|
||||
return accounts.map(item => item.id);
|
||||
} else if (emojis) {
|
||||
return emojis;
|
||||
} else {
|
||||
return sortHashtagsByUse(state, tags);
|
||||
}
|
||||
};
|
||||
|
||||
export default function compose(state = initialState, action) {
|
||||
switch(action.type) {
|
||||
case STORE_HYDRATE:
|
||||
@@ -311,11 +325,9 @@ export default function compose(state = initialState, action) {
|
||||
case COMPOSE_SUGGESTIONS_CLEAR:
|
||||
return state.update('suggestions', ImmutableList(), list => list.clear()).set('suggestion_token', null);
|
||||
case COMPOSE_SUGGESTIONS_READY:
|
||||
return state.set('suggestions', ImmutableList(action.accounts ? action.accounts.map(item => item.id) : action.emojis)).set('suggestion_token', action.token);
|
||||
return state.set('suggestions', ImmutableList(normalizeSuggestions(state, action))).set('suggestion_token', action.token);
|
||||
case COMPOSE_SUGGESTION_SELECT:
|
||||
return insertSuggestion(state, action.position, action.token, action.completion, action.path);
|
||||
case COMPOSE_SUGGESTION_TAGS_UPDATE:
|
||||
return updateSuggestionTags(state, action.token);
|
||||
case COMPOSE_TAG_HISTORY_UPDATE:
|
||||
return state.set('tagHistory', fromJS(action.tags));
|
||||
case TIMELINE_DELETE:
|
||||
|
@@ -8,6 +8,8 @@ import {
|
||||
CONVERSATIONS_UPDATE,
|
||||
CONVERSATIONS_READ,
|
||||
} from '../actions/conversations';
|
||||
import { ACCOUNT_BLOCK_SUCCESS, ACCOUNT_MUTE_SUCCESS } from 'mastodon/actions/accounts';
|
||||
import { DOMAIN_BLOCK_SUCCESS } from 'mastodon/actions/domain_blocks';
|
||||
import compareId from '../compare_id';
|
||||
|
||||
const initialState = ImmutableMap({
|
||||
@@ -74,6 +76,10 @@ const expandNormalizedConversations = (state, conversations, next, isLoadingRece
|
||||
});
|
||||
};
|
||||
|
||||
const filterConversations = (state, accountIds) => {
|
||||
return state.update('items', list => list.filterNot(item => item.get('accounts').some(accountId => accountIds.includes(accountId))));
|
||||
};
|
||||
|
||||
export default function conversations(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case CONVERSATIONS_FETCH_REQUEST:
|
||||
@@ -96,6 +102,11 @@ export default function conversations(state = initialState, action) {
|
||||
|
||||
return item;
|
||||
}));
|
||||
case ACCOUNT_BLOCK_SUCCESS:
|
||||
case ACCOUNT_MUTE_SUCCESS:
|
||||
return filterConversations(state, [action.relationship.id]);
|
||||
case DOMAIN_BLOCK_SUCCESS:
|
||||
return filterConversations(state, action.accounts);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
@@ -12,6 +12,7 @@ import {
|
||||
ACCOUNT_BLOCK_SUCCESS,
|
||||
ACCOUNT_MUTE_SUCCESS,
|
||||
} from '../actions/accounts';
|
||||
import { DOMAIN_BLOCK_SUCCESS } from 'mastodon/actions/domain_blocks';
|
||||
import { TIMELINE_DELETE, TIMELINE_DISCONNECT } from '../actions/timelines';
|
||||
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
|
||||
import compareId from '../compare_id';
|
||||
@@ -83,8 +84,8 @@ const expandNormalizedNotifications = (state, notifications, next, usePendingIte
|
||||
});
|
||||
};
|
||||
|
||||
const filterNotifications = (state, relationship) => {
|
||||
const helper = list => list.filterNot(item => item !== null && item.get('account') === relationship.id);
|
||||
const filterNotifications = (state, accountIds) => {
|
||||
const helper = list => list.filterNot(item => item !== null && accountIds.includes(item.get('account')));
|
||||
return state.update('items', helper).update('pendingItems', helper);
|
||||
};
|
||||
|
||||
@@ -118,9 +119,11 @@ export default function notifications(state = initialState, action) {
|
||||
case NOTIFICATIONS_EXPAND_SUCCESS:
|
||||
return expandNormalizedNotifications(state, action.notifications, action.next, action.usePendingItems);
|
||||
case ACCOUNT_BLOCK_SUCCESS:
|
||||
return filterNotifications(state, action.relationship);
|
||||
return filterNotifications(state, [action.relationship.id]);
|
||||
case ACCOUNT_MUTE_SUCCESS:
|
||||
return action.relationship.muting_notifications ? filterNotifications(state, action.relationship) : state;
|
||||
return action.relationship.muting_notifications ? filterNotifications(state, [action.relationship.id]) : state;
|
||||
case DOMAIN_BLOCK_SUCCESS:
|
||||
return filterNotifications(state, action.accounts);
|
||||
case NOTIFICATIONS_CLEAR:
|
||||
return state.set('items', ImmutableList()).set('pendingItems', ImmutableList()).set('hasMore', false);
|
||||
case TIMELINE_DELETE:
|
||||
|
@@ -3,6 +3,7 @@ import {
|
||||
SEARCH_CLEAR,
|
||||
SEARCH_FETCH_SUCCESS,
|
||||
SEARCH_SHOW,
|
||||
SEARCH_EXPAND_SUCCESS,
|
||||
} from '../actions/search';
|
||||
import {
|
||||
COMPOSE_MENTION,
|
||||
@@ -42,6 +43,8 @@ export default function search(state = initialState, action) {
|
||||
statuses: ImmutableList(action.results.statuses.map(item => item.id)),
|
||||
hashtags: fromJS(action.results.hashtags),
|
||||
})).set('submitted', true).set('searchTerm', action.searchTerm);
|
||||
case SEARCH_EXPAND_SUCCESS:
|
||||
return state.updateIn(['results', action.searchType], list => list.concat(action.results[action.searchType].map(item => item.id)));
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
@@ -4,6 +4,8 @@ import {
|
||||
SUGGESTIONS_FETCH_FAIL,
|
||||
SUGGESTIONS_DISMISS,
|
||||
} from '../actions/suggestions';
|
||||
import { ACCOUNT_BLOCK_SUCCESS, ACCOUNT_MUTE_SUCCESS } from 'mastodon/actions/accounts';
|
||||
import { DOMAIN_BLOCK_SUCCESS } from 'mastodon/actions/domain_blocks';
|
||||
import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable';
|
||||
|
||||
const initialState = ImmutableMap({
|
||||
@@ -24,6 +26,11 @@ export default function suggestionsReducer(state = initialState, action) {
|
||||
return state.set('isLoading', false);
|
||||
case SUGGESTIONS_DISMISS:
|
||||
return state.update('items', list => list.filterNot(id => id === action.id));
|
||||
case ACCOUNT_BLOCK_SUCCESS:
|
||||
case ACCOUNT_MUTE_SUCCESS:
|
||||
return state.update('items', list => list.filterNot(id => id === action.relationship.id));
|
||||
case DOMAIN_BLOCK_SUCCESS:
|
||||
return state.update('items', list => list.filterNot(id => action.accounts.includes(id)));
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
@@ -445,7 +445,8 @@
|
||||
}
|
||||
|
||||
.autosuggest-account,
|
||||
.autosuggest-emoji {
|
||||
.autosuggest-emoji,
|
||||
.autosuggest-hashtag {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
@@ -454,6 +455,14 @@
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.autosuggest-hashtag {
|
||||
justify-content: space-between;
|
||||
|
||||
strong {
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.autosuggest-account-icon,
|
||||
.autosuggest-emoji img {
|
||||
display: block;
|
||||
@@ -753,6 +762,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
a.unhandled-link {
|
||||
color: lighten($ui-highlight-color, 8%);
|
||||
}
|
||||
|
||||
.status__content__spoiler-link {
|
||||
background: $action-button-color;
|
||||
|
||||
@@ -1936,6 +1949,9 @@ a.account__display-name {
|
||||
background: lighten($ui-base-color, 8%);
|
||||
flex: 0 0 auto;
|
||||
overflow-y: auto;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.tabs-bar__link {
|
||||
@@ -4006,8 +4022,9 @@ a.status-card.compact:hover {
|
||||
}
|
||||
|
||||
.search-results__info {
|
||||
padding: 10px;
|
||||
color: $secondary-text-color;
|
||||
padding: 20px;
|
||||
color: $darker-text-color;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.modal-root {
|
||||
|
Reference in New Issue
Block a user