Skip to content

Commit

Permalink
Improve compose form performance, upgrade JS dependencies. LightingBox
Browse files Browse the repository at this point in the history
now allows to cycle through multiple images
  • Loading branch information
Gargron committed Feb 26, 2017
1 parent 3e9d794 commit 2c50687
Show file tree
Hide file tree
Showing 14 changed files with 1,245 additions and 707 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import Toggle from 'react-toggle';
import Collapsable from '../../../components/collapsable';
import UnlistedToggleContainer from '../containers/unlisted_toggle_container';
import SpoilerToggleContainer from '../containers/spoiler_toggle_container';
import PrivateToggleContainer from '../containers/private_toggle_container';
import SensitiveToggleContainer from '../containers/sensitive_toggle_container';

const messages = defineMessages({
placeholder: { id: 'compose_form.placeholder', defaultMessage: 'What is on your mind?' },
Expand All @@ -26,17 +29,15 @@ const ComposeForm = React.createClass({
text: React.PropTypes.string.isRequired,
suggestion_token: React.PropTypes.string,
suggestions: ImmutablePropTypes.list,
sensitive: React.PropTypes.bool,
spoiler: React.PropTypes.bool,
spoiler_text: React.PropTypes.string,
unlisted: React.PropTypes.bool,
private: React.PropTypes.bool,
unlisted: React.PropTypes.bool,
spoiler_text: React.PropTypes.string,
fileDropDate: React.PropTypes.instanceOf(Date),
focusDate: React.PropTypes.instanceOf(Date),
preselectDate: React.PropTypes.instanceOf(Date),
is_submitting: React.PropTypes.bool,
is_uploading: React.PropTypes.bool,
media_count: React.PropTypes.number,
me: React.PropTypes.number,
needsPrivacyWarning: React.PropTypes.bool,
mentionedDomains: React.PropTypes.array.isRequired,
Expand All @@ -45,10 +46,7 @@ const ComposeForm = React.createClass({
onClearSuggestions: React.PropTypes.func.isRequired,
onFetchSuggestions: React.PropTypes.func.isRequired,
onSuggestionSelected: React.PropTypes.func.isRequired,
onChangeSensitivity: React.PropTypes.func.isRequired,
onChangeSpoilerness: React.PropTypes.func.isRequired,
onChangeSpoilerText: React.PropTypes.func.isRequired,
onChangeVisibility: React.PropTypes.func.isRequired
},

mixins: [PureRenderMixin],
Expand Down Expand Up @@ -80,23 +78,10 @@ const ComposeForm = React.createClass({
this.props.onSuggestionSelected(tokenStart, token, value);
},

handleChangeSensitivity (e) {
this.props.onChangeSensitivity(e.target.checked);
},

handleChangeSpoilerness (e) {
this.props.onChangeSpoilerness(e.target.checked);
this.props.onChangeSpoilerText('');
},

handleChangeSpoilerText (e) {
this.props.onChangeSpoilerText(e.target.value);
},

handleChangeVisibility (e) {
this.props.onChangeVisibility(e.target.checked);
},

componentDidUpdate (prevProps) {
if (this.props.focusDate !== prevProps.focusDate) {
// If replying to zero or one users, places the cursor at the end of the textbox.
Expand Down Expand Up @@ -172,24 +157,10 @@ const ComposeForm = React.createClass({
<UploadButtonContainer style={{ paddingTop: '4px' }} />
</div>

<label className='compose-form__label with-border' style={{ marginTop: '10px' }}>
<Toggle checked={this.props.spoiler} onChange={this.handleChangeSpoilerness} />
<span className='compose-form__label__text'><FormattedMessage id='compose_form.spoiler' defaultMessage='Hide text behind warning' /></span>
</label>

<label className='compose-form__label with-border'>
<Toggle checked={this.props.private} onChange={this.handleChangeVisibility} />
<span className='compose-form__label__text'><FormattedMessage id='compose_form.private' defaultMessage='Mark as private' /></span>
</label>

<SpoilerToggleContainer />
<PrivateToggleContainer />
<UnlistedToggleContainer />

<Collapsable isVisible={this.props.media_count > 0} fullHeight={39.5}>
<label className='compose-form__label'>
<Toggle checked={this.props.sensitive} onChange={this.handleChangeSensitivity} />
<span className='compose-form__label__text'><FormattedMessage id='compose_form.sensitive' defaultMessage='Mark media as sensitive' /></span>
</label>
</Collapsable>
<SensitiveToggleContainer />
</div>
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import PureRenderMixin from 'react-addons-pure-render-mixin';
import { FormattedMessage } from 'react-intl';
import Toggle from 'react-toggle';

const PrivateToggle = React.createClass({

propTypes: {
isPrivate: React.PropTypes.bool,
onChange: React.PropTypes.func.isRequired
},

mixins: [PureRenderMixin],

render () {
const { isPrivate, onChange } = this.props;

return (
<label className='compose-form__label with-border'>
<Toggle checked={isPrivate} onChange={onChange} />
<span className='compose-form__label__text'><FormattedMessage id='compose_form.private' defaultMessage='Mark as private' /></span>
</label>
);
}

});

export default PrivateToggle;
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import PureRenderMixin from 'react-addons-pure-render-mixin';
import { FormattedMessage } from 'react-intl';
import Toggle from 'react-toggle';
import Collapsable from '../../../components/collapsable';

const SensitiveToggle = React.createClass({

propTypes: {
hasMedia: React.PropTypes.bool,
isSensitive: React.PropTypes.bool,
onChange: React.PropTypes.func.isRequired
},

mixins: [PureRenderMixin],

render () {
const { hasMedia, isSensitive, onChange } = this.props;

return (
<Collapsable isVisible={hasMedia} fullHeight={39.5}>
<label className='compose-form__label'>
<Toggle checked={isSensitive} onChange={onChange} />
<span className='compose-form__label__text'><FormattedMessage id='compose_form.sensitive' defaultMessage='Mark media as sensitive' /></span>
</label>
</Collapsable>
);
}

});

export default SensitiveToggle;
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import PureRenderMixin from 'react-addons-pure-render-mixin';
import { FormattedMessage } from 'react-intl';
import Toggle from 'react-toggle';

const SpoilerToggle = React.createClass({

propTypes: {
isSpoiler: React.PropTypes.bool,
onChange: React.PropTypes.func.isRequired
},

mixins: [PureRenderMixin],

render () {
const { isSpoiler, onChange } = this.props;

return (
<label className='compose-form__label with-border' style={{ marginTop: '10px' }}>
<Toggle checked={isSpoiler} onChange={onChange} />
<span className='compose-form__label__text'><FormattedMessage id='compose_form.spoiler' defaultMessage='Hide text behind warning' /></span>
</label>
);
}

});

export default SpoilerToggle;
Original file line number Diff line number Diff line change
@@ -1,26 +1,29 @@
import { connect } from 'react-redux';
import ComposeForm from '../components/compose_form';
import { createSelector } from 'reselect';
import {
changeCompose,
submitCompose,
clearComposeSuggestions,
fetchComposeSuggestions,
selectComposeSuggestion,
changeComposeSensitivity,
changeComposeSpoilerness,
changeComposeSpoilerText,
changeComposeVisibility,
changeComposeListability
} from '../../../actions/compose';

const getMentionedUsernames = createSelector(state => state.getIn(['compose', 'text']), text => text.match(/(?:^|[^\/\w])@([a-z0-9_]+@[a-z0-9\.\-]+)/ig));

const getMentionedDomains = createSelector(getMentionedUsernames, mentionedUsernamesWithDomains => {
return mentionedUsernamesWithDomains !== null ? [...new Set(mentionedUsernamesWithDomains.map(item => item.split('@')[2]))] : [];
});

const mapStateToProps = (state, props) => {
const mentionedUsernamesWithDomains = state.getIn(['compose', 'text']).match(/(?:^|[^\/\w])@([a-z0-9_]+@[a-z0-9\.\-]+)/ig);
const mentionedUsernames = getMentionedUsernames(state);
const mentionedUsernamesWithDomains = getMentionedDomains(state);

return {
text: state.getIn(['compose', 'text']),
suggestion_token: state.getIn(['compose', 'suggestion_token']),
suggestions: state.getIn(['compose', 'suggestions']),
sensitive: state.getIn(['compose', 'sensitive']),
spoiler: state.getIn(['compose', 'spoiler']),
spoiler_text: state.getIn(['compose', 'spoiler_text']),
unlisted: state.getIn(['compose', 'unlisted'], ),
Expand All @@ -30,10 +33,9 @@ const mapStateToProps = (state, props) => {
preselectDate: state.getIn(['compose', 'preselectDate']),
is_submitting: state.getIn(['compose', 'is_submitting']),
is_uploading: state.getIn(['compose', 'is_uploading']),
media_count: state.getIn(['compose', 'media_attachments']).size,
me: state.getIn(['compose', 'me']),
needsPrivacyWarning: state.getIn(['compose', 'private']) && mentionedUsernamesWithDomains !== null,
mentionedDomains: mentionedUsernamesWithDomains !== null ? [...new Set(mentionedUsernamesWithDomains.map(item => item.split('@')[2]))] : []
needsPrivacyWarning: state.getIn(['compose', 'private']) && mentionedUsernames !== null,
mentionedDomains: mentionedUsernamesWithDomains
};
};

Expand All @@ -59,22 +61,10 @@ const mapDispatchToProps = (dispatch) => ({
dispatch(selectComposeSuggestion(position, token, accountId));
},

onChangeSensitivity (checked) {
dispatch(changeComposeSensitivity(checked));
},

onChangeSpoilerness (checked) {
dispatch(changeComposeSpoilerness(checked));
},

onChangeSpoilerText (checked) {
dispatch(changeComposeSpoilerText(checked));
},

onChangeVisibility (checked) {
dispatch(changeComposeVisibility(checked));
},

});

export default connect(mapStateToProps, mapDispatchToProps)(ComposeForm);
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { connect } from 'react-redux';
import PrivateToggle from '../components/private_toggle';
import { changeComposeVisibility } from '../../../actions/compose';

const mapStateToProps = state => ({
isPrivate: state.getIn(['compose', 'private'])
});

const mapDispatchToProps = dispatch => ({

onChange (e) {
dispatch(changeComposeVisibility(e.target.checked));
}

});

export default connect(mapStateToProps, mapDispatchToProps)(PrivateToggle);
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { connect } from 'react-redux';
import SensitiveToggle from '../components/sensitive_toggle';
import { changeComposeSensitivity } from '../../../actions/compose';

const mapStateToProps = state => ({
hasMedia: state.getIn(['compose', 'media_attachments']).size > 0,
isSensitive: state.getIn(['compose', 'sensitive'])
});

const mapDispatchToProps = dispatch => ({

onChange (e) {
dispatch(changeComposeSensitivity(e.target.checked));
}

});

export default connect(mapStateToProps, mapDispatchToProps)(SensitiveToggle);
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { connect } from 'react-redux';
import SpoilerToggle from '../components/spoiler_toggle';
import { changeComposeSpoilerness } from '../../../actions/compose';

const mapStateToProps = state => ({
isSpoiler: state.getIn(['compose', 'spoiler'])
});

const mapDispatchToProps = dispatch => ({

onChange (e) {
dispatch(changeComposeSpoilerness(e.target.checked));
}

});

export default connect(mapStateToProps, mapDispatchToProps)(SpoilerToggle);
Original file line number Diff line number Diff line change
Expand Up @@ -131,19 +131,14 @@ const Modal = React.createClass({
return null;
}

const url = media.get(index).get('url');
const hasLeft = index > 0;
const hasRight = index + 1 < media.size;
const url = media.get(index).get('url');

let leftNav, rightNav;

leftNav = rightNav = '';

if (hasLeft) {
leftNav = <div style={leftNavStyle} className='modal-container--nav' onClick={this.handlePrevClick}><i className='fa fa-fw fa-chevron-left' /></div>;
}

if (hasRight) {
if (media.size > 1) {
leftNav = <div style={leftNavStyle} className='modal-container--nav' onClick={this.handlePrevClick}><i className='fa fa-fw fa-chevron-left' /></div>;
rightNav = <div style={rightNavStyle} className='modal-container--nav' onClick={this.handleNextClick}><i className='fa fa-fw fa-chevron-right' /></div>;
}

Expand Down
5 changes: 4 additions & 1 deletion app/assets/javascripts/components/reducers/compose.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,10 @@ export default function compose(state = initialState, action) {
case COMPOSE_SENSITIVITY_CHANGE:
return state.set('sensitive', action.checked);
case COMPOSE_SPOILERNESS_CHANGE:
return (action.checked ? state : state.set('spoiler_text', '')).set('spoiler', action.checked);
return state.withMutations(map => {
map.set('spoiler_text', '');
map.set('spoiler', action.checked);
});
case COMPOSE_SPOILER_TEXT_CHANGE:
return state.set('spoiler_text', action.text);
case COMPOSE_VISIBILITY_CHANGE:
Expand Down
4 changes: 2 additions & 2 deletions app/assets/javascripts/components/reducers/modal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ export default function modal(state = initialState, action) {
case MODAL_CLOSE:
return state.set('open', false);
case MODAL_INDEX_DECREASE:
return state.update('index', index => Math.max(index - 1, 0));
return state.update('index', index => (index - 1) % state.get('media').size);
case MODAL_INDEX_INCREASE:
return state.update('index', index => Math.min(index + 1, state.get('media').size - 1));
return state.update('index', index => (index + 1) % state.get('media').size);
default:
return state;
}
Expand Down
2 changes: 1 addition & 1 deletion app/assets/javascripts/components/selectors/index.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createSelector } from 'reselect'
import { createSelector } from 'reselect';
import Immutable from 'immutable';

const getStatuses = state => state.get('statuses');
Expand Down
Loading

0 comments on commit 2c50687

Please sign in to comment.