Merge tag 'v2.8.0' into instance_only_statuses

This commit is contained in:
Renato "Lond" Cerqueira
2019-04-13 23:47:24 +02:00
689 changed files with 25483 additions and 9047 deletions

View File

@ -30,6 +30,12 @@ import {
COMPOSE_UPLOAD_CHANGE_SUCCESS,
COMPOSE_UPLOAD_CHANGE_FAIL,
COMPOSE_RESET,
COMPOSE_POLL_ADD,
COMPOSE_POLL_REMOVE,
COMPOSE_POLL_OPTION_ADD,
COMPOSE_POLL_OPTION_CHANGE,
COMPOSE_POLL_OPTION_REMOVE,
COMPOSE_POLL_SETTINGS_CHANGE,
} from '../actions/compose';
import { TIMELINE_DELETE } from '../actions/timelines';
import { STORE_HYDRATE } from '../actions/store';
@ -57,6 +63,7 @@ const initialState = ImmutableMap({
is_uploading: false,
progress: 0,
media_attachments: ImmutableList(),
poll: null,
suggestion_token: null,
suggestions: ImmutableList(),
default_privacy: 'public',
@ -67,6 +74,12 @@ const initialState = ImmutableMap({
tagHistory: ImmutableList(),
});
const initialPoll = ImmutableMap({
options: ImmutableList(['', '']),
expires_in: 24 * 3600,
multiple: false,
});
function statusToTextMentions(state, status) {
let set = ImmutableOrderedSet([]);
@ -89,6 +102,7 @@ function clearAll(state) {
map.set('federation', state.get('default_federation'));
map.set('sensitive', false);
map.update('media_attachments', list => list.clear());
map.set('poll', null);
map.set('idempotencyKey', uuid());
});
};
@ -256,6 +270,7 @@ export default function compose(state = initialState, action) {
map.set('spoiler', false);
map.set('spoiler_text', '');
map.set('privacy', state.get('default_privacy'));
map.set('poll', null);
map.set('federation', state.get('default_federation'));
map.set('idempotencyKey', uuid());
});
@ -340,7 +355,27 @@ export default function compose(state = initialState, action) {
map.set('spoiler', false);
map.set('spoiler_text', '');
}
if (action.status.get('poll')) {
map.set('poll', ImmutableMap({
options: action.status.getIn(['poll', 'options']).map(x => x.get('title')),
multiple: action.status.getIn(['poll', 'multiple']),
expires_in: 24 * 3600,
}));
}
});
case COMPOSE_POLL_ADD:
return state.set('poll', initialPoll);
case COMPOSE_POLL_REMOVE:
return state.set('poll', null);
case COMPOSE_POLL_OPTION_ADD:
return state.updateIn(['poll', 'options'], options => options.push(action.title));
case COMPOSE_POLL_OPTION_CHANGE:
return state.setIn(['poll', 'options', action.index], action.title);
case COMPOSE_POLL_OPTION_REMOVE:
return state.updateIn(['poll', 'options'], options => options.delete(action.index));
case COMPOSE_POLL_SETTINGS_CHANGE:
return state.update('poll', poll => poll.set('expires_in', action.expiresIn).set('multiple', action.isMultiple));
default:
return state;
}

View File

@ -35,7 +35,7 @@ const updateConversation = (state, item) => state.update('items', list => {
}
});
const expandNormalizedConversations = (state, conversations, next) => {
const expandNormalizedConversations = (state, conversations, next, isLoadingRecent) => {
let items = ImmutableList(conversations.map(conversationToMap));
return state.withMutations(mutable => {
@ -66,7 +66,7 @@ const expandNormalizedConversations = (state, conversations, next) => {
});
}
if (!next) {
if (!next && !isLoadingRecent) {
mutable.set('hasMore', false);
}
@ -81,7 +81,7 @@ export default function conversations(state = initialState, action) {
case CONVERSATIONS_FETCH_FAIL:
return state.set('isLoading', false);
case CONVERSATIONS_FETCH_SUCCESS:
return expandNormalizedConversations(state, action.conversations, action.next);
return expandNormalizedConversations(state, action.conversations, action.next, action.isLoadingRecent);
case CONVERSATIONS_UPDATE:
return updateConversation(state, action.conversation);
case CONVERSATIONS_MOUNT:

View File

@ -0,0 +1,25 @@
import { Map as ImmutableMap, fromJS } from 'immutable';
import {
IDENTITY_PROOFS_ACCOUNT_FETCH_REQUEST,
IDENTITY_PROOFS_ACCOUNT_FETCH_SUCCESS,
IDENTITY_PROOFS_ACCOUNT_FETCH_FAIL,
} from '../actions/identity_proofs';
const initialState = ImmutableMap();
export default function identityProofsReducer(state = initialState, action) {
switch(action.type) {
case IDENTITY_PROOFS_ACCOUNT_FETCH_REQUEST:
return state.set('isLoading', true);
case IDENTITY_PROOFS_ACCOUNT_FETCH_FAIL:
return state.set('isLoading', false);
case IDENTITY_PROOFS_ACCOUNT_FETCH_SUCCESS:
return state.update(identity_proofs => identity_proofs.withMutations(map => {
map.set('isLoading', false);
map.set('loaded', true);
map.set(action.accountId, fromJS(action.identity_proofs));
}));
default:
return state;
}
};

View File

@ -29,6 +29,8 @@ import listAdder from './list_adder';
import filters from './filters';
import conversations from './conversations';
import suggestions from './suggestions';
import polls from './polls';
import identity_proofs from './identity_proofs';
const reducers = {
dropdown_menu,
@ -55,12 +57,14 @@ const reducers = {
notifications,
height_cache,
custom_emojis,
identity_proofs,
lists,
listEditor,
listAdder,
filters,
conversations,
suggestions,
polls,
};
export default combineReducers(reducers);

View File

@ -22,6 +22,7 @@ import {
const initialState = ImmutableMap({
listId: null,
isSubmitting: false,
isChanged: false,
title: '',
accounts: ImmutableMap({
@ -47,10 +48,16 @@ export default function listEditorReducer(state = initialState, action) {
map.set('isSubmitting', false);
});
case LIST_EDITOR_TITLE_CHANGE:
return state.set('title', action.value);
return state.withMutations(map => {
map.set('title', action.value);
map.set('isChanged', true);
});
case LIST_CREATE_REQUEST:
case LIST_UPDATE_REQUEST:
return state.set('isSubmitting', true);
return state.withMutations(map => {
map.set('isSubmitting', true);
map.set('isChanged', false);
});
case LIST_CREATE_FAIL:
case LIST_UPDATE_FAIL:
return state.set('isSubmitting', false);

View File

@ -108,6 +108,7 @@ export default function notifications(state = initialState, action) {
case NOTIFICATIONS_EXPAND_SUCCESS:
return expandNormalizedNotifications(state, action.notifications, action.next);
case ACCOUNT_BLOCK_SUCCESS:
return filterNotifications(state, action.relationship);
case ACCOUNT_MUTE_SUCCESS:
return action.relationship.muting_notifications ? filterNotifications(state, action.relationship) : state;
case NOTIFICATIONS_CLEAR:

View File

@ -0,0 +1,15 @@
import { POLLS_IMPORT } from 'mastodon/actions/importer';
import { Map as ImmutableMap, fromJS } from 'immutable';
const importPolls = (state, polls) => state.withMutations(map => polls.forEach(poll => map.set(poll.id, fromJS(poll))));
const initialState = ImmutableMap();
export default function polls(state = initialState, action) {
switch(action.type) {
case POLLS_IMPORT:
return importPolls(state, action.polls);
default:
return state;
}
}

View File

@ -9,6 +9,7 @@ const initialState = Immutable.Map({
favourite: false,
reblog: false,
mention: false,
poll: false,
}),
isSubscribed: false,
browserSupport: false,

View File

@ -31,6 +31,7 @@ const initialState = ImmutableMap({
favourite: true,
reblog: true,
mention: true,
poll: true,
}),
quickFilter: ImmutableMap({
@ -44,6 +45,7 @@ const initialState = ImmutableMap({
favourite: true,
reblog: true,
mention: true,
poll: true,
}),
sounds: ImmutableMap({
@ -51,6 +53,7 @@ const initialState = ImmutableMap({
favourite: true,
reblog: true,
mention: true,
poll: true,
}),
}),

View File

@ -6,6 +6,7 @@ import {
TIMELINE_EXPAND_REQUEST,
TIMELINE_EXPAND_FAIL,
TIMELINE_SCROLL_TOP,
TIMELINE_CONNECT,
TIMELINE_DISCONNECT,
} from '../actions/timelines';
import {
@ -20,6 +21,7 @@ const initialState = ImmutableMap();
const initialTimeline = ImmutableMap({
unread: 0,
online: false,
top: true,
isLoading: false,
hasMore: true,
@ -29,6 +31,8 @@ const initialTimeline = ImmutableMap({
const expandNormalizedTimeline = (state, timeline, statuses, next, isPartial, isLoadingRecent) => {
return state.update(timeline, initialTimeline, map => map.withMutations(mMap => {
mMap.set('isLoading', false);
mMap.set('isPartial', isPartial);
if (!next && !isLoadingRecent) mMap.set('hasMore', false);
if (!statuses.isEmpty()) {
@ -74,14 +78,15 @@ const updateTimeline = (state, timeline, status) => {
}));
};
const deleteStatus = (state, id, accountId, references) => {
const deleteStatus = (state, id, accountId, references, exclude_account = null) => {
state.keySeq().forEach(timeline => {
state = state.updateIn([timeline, 'items'], list => list.filterNot(item => item === id));
if (exclude_account === null || (timeline !== `account:${exclude_account}` && !timeline.startsWith(`account:${exclude_account}:`)))
state = state.updateIn([timeline, 'items'], list => list.filterNot(item => item === id));
});
// Remove reblogs of deleted status
references.forEach(ref => {
state = deleteStatus(state, ref[0], ref[1], []);
state = deleteStatus(state, ref[0], ref[1], [], exclude_account);
});
return state;
@ -100,7 +105,7 @@ const filterTimelines = (state, relationship, statuses) => {
}
references = statuses.filter(item => item.get('reblog') === status.get('id')).map(item => [item.get('id'), item.get('account')]);
state = deleteStatus(state, status.get('id'), status.get('account'), references);
state = deleteStatus(state, status.get('id'), status.get('account'), references, relationship.id);
});
return state;
@ -140,14 +145,13 @@ export default function timelines(state = initialState, action) {
return filterTimeline('home', state, action.relationship, action.statuses);
case TIMELINE_SCROLL_TOP:
return updateTop(state, action.timeline, action.top);
case TIMELINE_CONNECT:
return state.update(action.timeline, initialTimeline, map => map.set('online', true));
case TIMELINE_DISCONNECT:
return state.update(
action.timeline,
initialTimeline,
map => map.update(
'items',
items => items.first() ? items.unshift(null) : items
)
map => map.set('online', false).update('items', items => items.first() ? items.unshift(null) : items)
);
default:
return state;