Merge remote-tracking branch 'origin/develop' into typescript
This commit is contained in:
commit
94a79ba760
|
@ -219,7 +219,7 @@ export function submitCompose(routerHistory, force = false) {
|
||||||
|
|
||||||
const status = state.getIn(['compose', 'text'], '');
|
const status = state.getIn(['compose', 'text'], '');
|
||||||
const media = state.getIn(['compose', 'media_attachments']);
|
const media = state.getIn(['compose', 'media_attachments']);
|
||||||
let to = state.getIn(['compose', 'to'], null);
|
let to = state.getIn(['compose', 'to']);
|
||||||
|
|
||||||
if (!validateSchedule(state)) {
|
if (!validateSchedule(state)) {
|
||||||
dispatch(snackbar.error(messages.scheduleError));
|
dispatch(snackbar.error(messages.scheduleError));
|
||||||
|
|
|
@ -161,7 +161,7 @@ class ModalRoot extends React.PureComponent {
|
||||||
if (this.unlistenHistory) {
|
if (this.unlistenHistory) {
|
||||||
this.unlistenHistory();
|
this.unlistenHistory();
|
||||||
}
|
}
|
||||||
if (!['FAVOURITES', 'MENTIONS', 'REACTIONS', 'REBLOGS'].includes(type)) {
|
if (!['FAVOURITES', 'MENTIONS', 'REACTIONS', 'REBLOGS', 'MEDIA'].includes(type)) {
|
||||||
const { state } = this.history.location;
|
const { state } = this.history.location;
|
||||||
if (state && state.soapboxModalKey === this._modalHistoryKey) {
|
if (state && state.soapboxModalKey === this._modalHistoryKey) {
|
||||||
this.history.goBack();
|
this.history.goBack();
|
||||||
|
|
|
@ -11,6 +11,7 @@ class ReplyMentions extends ImmutablePureComponent {
|
||||||
onOpenMentionsModal: PropTypes.func.isRequired,
|
onOpenMentionsModal: PropTypes.func.isRequired,
|
||||||
explicitAddressing: PropTypes.bool,
|
explicitAddressing: PropTypes.bool,
|
||||||
to: ImmutablePropTypes.orderedSet,
|
to: ImmutablePropTypes.orderedSet,
|
||||||
|
parentTo: ImmutablePropTypes.orderedSet,
|
||||||
isReply: PropTypes.bool,
|
isReply: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -21,12 +22,23 @@ class ReplyMentions extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { explicitAddressing, to, isReply } = this.props;
|
const { explicitAddressing, to, parentTo, isReply } = this.props;
|
||||||
|
|
||||||
if (!explicitAddressing || !isReply || !to || to.size === 0) {
|
if (!explicitAddressing || !isReply || !to || (parentTo.size === 0)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (to.size === 0) {
|
||||||
|
return (
|
||||||
|
<a href='#' className='reply-mentions' onClick={this.handleClick}>
|
||||||
|
<FormattedMessage
|
||||||
|
id='reply_mentions.reply_empty'
|
||||||
|
defaultMessage='Replying to post'
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<a href='#' className='reply-mentions' onClick={this.handleClick}>
|
<a href='#' className='reply-mentions' onClick={this.handleClick}>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { openModal } from 'soapbox/actions/modals';
|
import { openModal } from 'soapbox/actions/modals';
|
||||||
|
import { statusToMentionsAccountIdsArray } from 'soapbox/reducers/compose';
|
||||||
import { makeGetStatus } from 'soapbox/selectors';
|
import { makeGetStatus } from 'soapbox/selectors';
|
||||||
import { getFeatures } from 'soapbox/utils/features';
|
import { getFeatures } from 'soapbox/utils/features';
|
||||||
|
|
||||||
|
@ -28,8 +29,14 @@ const makeMapStateToProps = () => {
|
||||||
}
|
}
|
||||||
const to = state.getIn(['compose', 'to']);
|
const to = state.getIn(['compose', 'to']);
|
||||||
|
|
||||||
|
const me = state.get('me');
|
||||||
|
const account = state.getIn(['accounts', me]);
|
||||||
|
|
||||||
|
const parentTo = statusToMentionsAccountIdsArray(state, status, account);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
to,
|
to,
|
||||||
|
parentTo,
|
||||||
isReply: true,
|
isReply: true,
|
||||||
explicitAddressing: true,
|
explicitAddressing: true,
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
import { Map as ImmutableMap, fromJS } from 'immutable';
|
||||||
|
|
||||||
|
import { normalizeInstance } from '../instance';
|
||||||
|
|
||||||
|
describe('normalizeInstance()', () => {
|
||||||
|
it('normalizes an empty Map', () => {
|
||||||
|
const expected = ImmutableMap({
|
||||||
|
description_limit: 1500,
|
||||||
|
configuration: ImmutableMap({
|
||||||
|
statuses: ImmutableMap({
|
||||||
|
max_characters: 500,
|
||||||
|
max_media_attachments: 4,
|
||||||
|
}),
|
||||||
|
polls: ImmutableMap({
|
||||||
|
max_options: 4,
|
||||||
|
max_characters_per_option: 25,
|
||||||
|
min_expiration: 300,
|
||||||
|
max_expiration: 2629746,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
version: '0.0.0',
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = normalizeInstance(ImmutableMap());
|
||||||
|
expect(result).toEqual(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('normalizes Pleroma instance with Mastodon configuration format', () => {
|
||||||
|
const instance = fromJS(require('soapbox/__fixtures__/pleroma-instance.json'));
|
||||||
|
|
||||||
|
const expected = {
|
||||||
|
configuration: {
|
||||||
|
statuses: {
|
||||||
|
max_characters: 5000,
|
||||||
|
max_media_attachments: Infinity,
|
||||||
|
},
|
||||||
|
polls: {
|
||||||
|
max_options: 20,
|
||||||
|
max_characters_per_option: 200,
|
||||||
|
min_expiration: 0,
|
||||||
|
max_expiration: 31536000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = normalizeInstance(instance);
|
||||||
|
expect(result.toJS()).toMatchObject(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('normalizes Mastodon instance with retained configuration', () => {
|
||||||
|
const instance = fromJS(require('soapbox/__fixtures__/mastodon-instance.json'));
|
||||||
|
|
||||||
|
const expected = {
|
||||||
|
configuration: {
|
||||||
|
statuses: {
|
||||||
|
max_characters: 500,
|
||||||
|
max_media_attachments: 4,
|
||||||
|
characters_reserved_per_url: 23,
|
||||||
|
},
|
||||||
|
media_attachments: {
|
||||||
|
image_size_limit: 10485760,
|
||||||
|
image_matrix_limit: 16777216,
|
||||||
|
video_size_limit: 41943040,
|
||||||
|
video_frame_rate_limit: 60,
|
||||||
|
video_matrix_limit: 2304000,
|
||||||
|
},
|
||||||
|
polls: {
|
||||||
|
max_options: 4,
|
||||||
|
max_characters_per_option: 50,
|
||||||
|
min_expiration: 300,
|
||||||
|
max_expiration: 2629746,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = normalizeInstance(instance);
|
||||||
|
expect(result.toJS()).toMatchObject(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('normalizes Mastodon 3.0.0 instance with default configuration', () => {
|
||||||
|
const instance = fromJS(require('soapbox/__fixtures__/mastodon-3.0.0-instance.json'));
|
||||||
|
|
||||||
|
const expected = {
|
||||||
|
configuration: {
|
||||||
|
statuses: {
|
||||||
|
max_characters: 500,
|
||||||
|
max_media_attachments: 4,
|
||||||
|
},
|
||||||
|
polls: {
|
||||||
|
max_options: 4,
|
||||||
|
max_characters_per_option: 25,
|
||||||
|
min_expiration: 300,
|
||||||
|
max_expiration: 2629746,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = normalizeInstance(instance);
|
||||||
|
expect(result.toJS()).toMatchObject(expected);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,65 @@
|
||||||
|
import { Map as ImmutableMap } from 'immutable';
|
||||||
|
|
||||||
|
import { parseVersion, PLEROMA } from 'soapbox/utils/features';
|
||||||
|
import { isNumber } from 'soapbox/utils/numbers';
|
||||||
|
|
||||||
|
// Use Mastodon defaults
|
||||||
|
const baseInstance = ImmutableMap({
|
||||||
|
description_limit: 1500,
|
||||||
|
configuration: ImmutableMap({
|
||||||
|
statuses: ImmutableMap({
|
||||||
|
max_characters: 500,
|
||||||
|
max_media_attachments: 4,
|
||||||
|
}),
|
||||||
|
polls: ImmutableMap({
|
||||||
|
max_options: 4,
|
||||||
|
max_characters_per_option: 25,
|
||||||
|
min_expiration: 300,
|
||||||
|
max_expiration: 2629746,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
version: '0.0.0',
|
||||||
|
});
|
||||||
|
|
||||||
|
// Build Mastodon configuration from Pleroma instance
|
||||||
|
const pleromaToMastodonConfig = instance => {
|
||||||
|
return ImmutableMap({
|
||||||
|
statuses: ImmutableMap({
|
||||||
|
max_characters: instance.get('max_toot_chars'),
|
||||||
|
}),
|
||||||
|
polls: ImmutableMap({
|
||||||
|
max_options: instance.getIn(['poll_limits', 'max_options']),
|
||||||
|
max_characters_per_option: instance.getIn(['poll_limits', 'max_option_chars']),
|
||||||
|
min_expiration: instance.getIn(['poll_limits', 'min_expiration']),
|
||||||
|
max_expiration: instance.getIn(['poll_limits', 'max_expiration']),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Use new value only if old value is undefined
|
||||||
|
const mergeDefined = (oldVal, newVal) => oldVal === undefined ? newVal : oldVal;
|
||||||
|
|
||||||
|
// Get the software's default attachment limit
|
||||||
|
const getAttachmentLimit = software => software === PLEROMA ? Infinity : 4;
|
||||||
|
|
||||||
|
// Normalize instance (Pleroma, Mastodon, etc.) to Mastodon's format
|
||||||
|
export const normalizeInstance = instance => {
|
||||||
|
const { software } = parseVersion(instance.get('version'));
|
||||||
|
const mastodonConfig = pleromaToMastodonConfig(instance);
|
||||||
|
|
||||||
|
return instance.withMutations(instance => {
|
||||||
|
// Merge configuration
|
||||||
|
instance.update('configuration', ImmutableMap(), configuration => (
|
||||||
|
configuration.mergeDeepWith(mergeDefined, mastodonConfig)
|
||||||
|
));
|
||||||
|
|
||||||
|
// If max attachments isn't set, check the backend software
|
||||||
|
instance.updateIn(['configuration', 'statuses', 'max_media_attachments'], value => {
|
||||||
|
return isNumber(value) ? value : getAttachmentLimit(software);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Merge defaults & cleanup
|
||||||
|
instance.mergeDeepWith(mergeDefined, baseInstance);
|
||||||
|
instance.deleteAll(['max_toot_chars', 'poll_limits']);
|
||||||
|
});
|
||||||
|
};
|
|
@ -428,7 +428,7 @@ export default function compose(state = initialState, action) {
|
||||||
case REDRAFT:
|
case REDRAFT:
|
||||||
return state.withMutations(map => {
|
return state.withMutations(map => {
|
||||||
map.set('text', action.raw_text || unescapeHTML(expandMentions(action.status)));
|
map.set('text', action.raw_text || unescapeHTML(expandMentions(action.status)));
|
||||||
map.set('to', action.explicitAddressing ? getExplicitMentions(action.status.get('account', 'id'), action.status) : null);
|
map.set('to', action.explicitAddressing ? getExplicitMentions(action.status.get('account', 'id'), action.status) : undefined);
|
||||||
map.set('in_reply_to', action.status.get('in_reply_to_id'));
|
map.set('in_reply_to', action.status.get('in_reply_to_id'));
|
||||||
map.set('privacy', action.status.get('visibility'));
|
map.set('privacy', action.status.get('visibility'));
|
||||||
// TODO: Actually fix this rather than just removing it
|
// TODO: Actually fix this rather than just removing it
|
||||||
|
|
|
@ -2,10 +2,9 @@ import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable';
|
||||||
|
|
||||||
import { ADMIN_CONFIG_UPDATE_REQUEST, ADMIN_CONFIG_UPDATE_SUCCESS } from 'soapbox/actions/admin';
|
import { ADMIN_CONFIG_UPDATE_REQUEST, ADMIN_CONFIG_UPDATE_SUCCESS } from 'soapbox/actions/admin';
|
||||||
import { PLEROMA_PRELOAD_IMPORT } from 'soapbox/actions/preload';
|
import { PLEROMA_PRELOAD_IMPORT } from 'soapbox/actions/preload';
|
||||||
|
import { normalizeInstance } from 'soapbox/normalizers/instance';
|
||||||
import KVStore from 'soapbox/storage/kv_store';
|
import KVStore from 'soapbox/storage/kv_store';
|
||||||
import { ConfigDB } from 'soapbox/utils/config_db';
|
import { ConfigDB } from 'soapbox/utils/config_db';
|
||||||
import { parseVersion, PLEROMA } from 'soapbox/utils/features';
|
|
||||||
import { isNumber } from 'soapbox/utils/numbers';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
INSTANCE_REMEMBER_SUCCESS,
|
INSTANCE_REMEMBER_SUCCESS,
|
||||||
|
@ -14,6 +13,8 @@ import {
|
||||||
NODEINFO_FETCH_SUCCESS,
|
NODEINFO_FETCH_SUCCESS,
|
||||||
} from '../actions/instance';
|
} from '../actions/instance';
|
||||||
|
|
||||||
|
const initialState = normalizeInstance(ImmutableMap());
|
||||||
|
|
||||||
const nodeinfoToInstance = nodeinfo => {
|
const nodeinfoToInstance = nodeinfo => {
|
||||||
// Match Pleroma's develop branch
|
// Match Pleroma's develop branch
|
||||||
return ImmutableMap({
|
return ImmutableMap({
|
||||||
|
@ -30,67 +31,6 @@ const nodeinfoToInstance = nodeinfo => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Use Mastodon defaults
|
|
||||||
const initialState = ImmutableMap({
|
|
||||||
description_limit: 1500,
|
|
||||||
configuration: ImmutableMap({
|
|
||||||
statuses: ImmutableMap({
|
|
||||||
max_characters: 500,
|
|
||||||
max_media_attachments: 4,
|
|
||||||
}),
|
|
||||||
polls: ImmutableMap({
|
|
||||||
max_options: 4,
|
|
||||||
max_characters_per_option: 25,
|
|
||||||
min_expiration: 300,
|
|
||||||
max_expiration: 2629746,
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
version: '0.0.0',
|
|
||||||
});
|
|
||||||
|
|
||||||
// Build Mastodon configuration from Pleroma instance
|
|
||||||
const pleromaToMastodonConfig = instance => {
|
|
||||||
return ImmutableMap({
|
|
||||||
statuses: ImmutableMap({
|
|
||||||
max_characters: instance.get('max_toot_chars'),
|
|
||||||
}),
|
|
||||||
polls: ImmutableMap({
|
|
||||||
max_options: instance.getIn(['poll_limits', 'max_options']),
|
|
||||||
max_characters_per_option: instance.getIn(['poll_limits', 'max_option_chars']),
|
|
||||||
min_expiration: instance.getIn(['poll_limits', 'min_expiration']),
|
|
||||||
max_expiration: instance.getIn(['poll_limits', 'max_expiration']),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Use new value only if old value is undefined
|
|
||||||
const mergeDefined = (oldVal, newVal) => oldVal === undefined ? newVal : oldVal;
|
|
||||||
|
|
||||||
// Get the software's default attachment limit
|
|
||||||
const getAttachmentLimit = software => software === PLEROMA ? Infinity : 4;
|
|
||||||
|
|
||||||
// Normalize instance (Pleroma, Mastodon, etc.) to Mastodon's format
|
|
||||||
const normalizeInstance = instance => {
|
|
||||||
const { software } = parseVersion(instance.get('version'));
|
|
||||||
const mastodonConfig = pleromaToMastodonConfig(instance);
|
|
||||||
|
|
||||||
return instance.withMutations(instance => {
|
|
||||||
// Merge configuration
|
|
||||||
instance.update('configuration', ImmutableMap(), configuration => (
|
|
||||||
configuration.mergeDeepWith(mergeDefined, mastodonConfig)
|
|
||||||
));
|
|
||||||
|
|
||||||
// If max attachments isn't set, check the backend software
|
|
||||||
instance.updateIn(['configuration', 'statuses', 'max_media_attachments'], value => {
|
|
||||||
return isNumber(value) ? value : getAttachmentLimit(software);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Merge defaults & cleanup
|
|
||||||
instance.mergeDeepWith(mergeDefined, initialState);
|
|
||||||
instance.deleteAll(['max_toot_chars', 'poll_limits']);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const importInstance = (state, instance) => {
|
const importInstance = (state, instance) => {
|
||||||
return normalizeInstance(instance);
|
return normalizeInstance(instance);
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue