From 2c5068727997d4b223e74e765df75d9773b954f7 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sun, 26 Feb 2017 01:23:44 +0100 Subject: Improve compose form performance, upgrade JS dependencies. LightingBox now allows to cycle through multiple images --- .../features/compose/components/compose_form.jsx | 45 ++++------------------ .../features/compose/components/private_toggle.jsx | 27 +++++++++++++ .../compose/components/sensitive_toggle.jsx | 31 +++++++++++++++ .../features/compose/components/spoiler_toggle.jsx | 27 +++++++++++++ .../compose/containers/compose_form_container.jsx | 32 ++++++--------- .../containers/private_toggle_container.jsx | 17 ++++++++ .../containers/sensitive_toggle_container.jsx | 18 +++++++++ .../containers/spoiler_toggle_container.jsx | 17 ++++++++ .../features/ui/containers/modal_container.jsx | 11 ++---- .../javascripts/components/reducers/compose.jsx | 5 ++- .../javascripts/components/reducers/modal.jsx | 4 +- .../javascripts/components/selectors/index.jsx | 2 +- 12 files changed, 166 insertions(+), 70 deletions(-) create mode 100644 app/assets/javascripts/components/features/compose/components/private_toggle.jsx create mode 100644 app/assets/javascripts/components/features/compose/components/sensitive_toggle.jsx create mode 100644 app/assets/javascripts/components/features/compose/components/spoiler_toggle.jsx create mode 100644 app/assets/javascripts/components/features/compose/containers/private_toggle_container.jsx create mode 100644 app/assets/javascripts/components/features/compose/containers/sensitive_toggle_container.jsx create mode 100644 app/assets/javascripts/components/features/compose/containers/spoiler_toggle_container.jsx (limited to 'app') diff --git a/app/assets/javascripts/components/features/compose/components/compose_form.jsx b/app/assets/javascripts/components/features/compose/components/compose_form.jsx index 45812def6..31ae8e034 100644 --- a/app/assets/javascripts/components/features/compose/components/compose_form.jsx +++ b/app/assets/javascripts/components/features/compose/components/compose_form.jsx @@ -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?' }, @@ -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, @@ -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], @@ -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. @@ -172,24 +157,10 @@ const ComposeForm = React.createClass({ - - - - + + - - 0} fullHeight={39.5}> - - + ); } diff --git a/app/assets/javascripts/components/features/compose/components/private_toggle.jsx b/app/assets/javascripts/components/features/compose/components/private_toggle.jsx new file mode 100644 index 000000000..902ee70ca --- /dev/null +++ b/app/assets/javascripts/components/features/compose/components/private_toggle.jsx @@ -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 ( + + ); + } + +}); + +export default PrivateToggle; diff --git a/app/assets/javascripts/components/features/compose/components/sensitive_toggle.jsx b/app/assets/javascripts/components/features/compose/components/sensitive_toggle.jsx new file mode 100644 index 000000000..97cc9487e --- /dev/null +++ b/app/assets/javascripts/components/features/compose/components/sensitive_toggle.jsx @@ -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 ( + + + + ); + } + +}); + +export default SensitiveToggle; diff --git a/app/assets/javascripts/components/features/compose/components/spoiler_toggle.jsx b/app/assets/javascripts/components/features/compose/components/spoiler_toggle.jsx new file mode 100644 index 000000000..1c59e4393 --- /dev/null +++ b/app/assets/javascripts/components/features/compose/components/spoiler_toggle.jsx @@ -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 ( + + ); + } + +}); + +export default SpoilerToggle; diff --git a/app/assets/javascripts/components/features/compose/containers/compose_form_container.jsx b/app/assets/javascripts/components/features/compose/containers/compose_form_container.jsx index bff273c15..53129af6e 100644 --- a/app/assets/javascripts/components/features/compose/containers/compose_form_container.jsx +++ b/app/assets/javascripts/components/features/compose/containers/compose_form_container.jsx @@ -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'], ), @@ -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 }; }; @@ -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); diff --git a/app/assets/javascripts/components/features/compose/containers/private_toggle_container.jsx b/app/assets/javascripts/components/features/compose/containers/private_toggle_container.jsx new file mode 100644 index 000000000..ee3596902 --- /dev/null +++ b/app/assets/javascripts/components/features/compose/containers/private_toggle_container.jsx @@ -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); diff --git a/app/assets/javascripts/components/features/compose/containers/sensitive_toggle_container.jsx b/app/assets/javascripts/components/features/compose/containers/sensitive_toggle_container.jsx new file mode 100644 index 000000000..97b3361ba --- /dev/null +++ b/app/assets/javascripts/components/features/compose/containers/sensitive_toggle_container.jsx @@ -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); diff --git a/app/assets/javascripts/components/features/compose/containers/spoiler_toggle_container.jsx b/app/assets/javascripts/components/features/compose/containers/spoiler_toggle_container.jsx new file mode 100644 index 000000000..0bd4df759 --- /dev/null +++ b/app/assets/javascripts/components/features/compose/containers/spoiler_toggle_container.jsx @@ -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); diff --git a/app/assets/javascripts/components/features/ui/containers/modal_container.jsx b/app/assets/javascripts/components/features/ui/containers/modal_container.jsx index 4c47fb8c5..d8301b20f 100644 --- a/app/assets/javascripts/components/features/ui/containers/modal_container.jsx +++ b/app/assets/javascripts/components/features/ui/containers/modal_container.jsx @@ -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 =
; - } - - if (hasRight) { + if (media.size > 1) { + leftNav =
; rightNav =
; } diff --git a/app/assets/javascripts/components/reducers/compose.jsx b/app/assets/javascripts/components/reducers/compose.jsx index e401a2d98..dead5fd77 100644 --- a/app/assets/javascripts/components/reducers/compose.jsx +++ b/app/assets/javascripts/components/reducers/compose.jsx @@ -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: diff --git a/app/assets/javascripts/components/reducers/modal.jsx b/app/assets/javascripts/components/reducers/modal.jsx index 07da65771..37ffbc62b 100644 --- a/app/assets/javascripts/components/reducers/modal.jsx +++ b/app/assets/javascripts/components/reducers/modal.jsx @@ -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; } diff --git a/app/assets/javascripts/components/selectors/index.jsx b/app/assets/javascripts/components/selectors/index.jsx index faa7f92d0..0e88654a1 100644 --- a/app/assets/javascripts/components/selectors/index.jsx +++ b/app/assets/javascripts/components/selectors/index.jsx @@ -1,4 +1,4 @@ -import { createSelector } from 'reselect' +import { createSelector } from 'reselect'; import Immutable from 'immutable'; const getStatuses = state => state.get('statuses'); -- cgit