diff options
author | Eugen Rochko <eugen@zeonfederated.com> | 2016-10-24 17:11:02 +0200 |
---|---|---|
committer | Eugen Rochko <eugen@zeonfederated.com> | 2016-10-24 18:08:23 +0200 |
commit | f8f40f15dafca65dc07d5c5c19fb9a9dc3473dd6 (patch) | |
tree | b9817a27143158a5c26f9b447a8a58dbb4b272d3 /app/assets/javascripts/components/features | |
parent | 61db14bcbe424731c01cf782e8e147a9551c6125 (diff) |
Move status components inside individual containers. We still need to select
all statuses/accounts to assemble, but at least lists don't have to be re-rendered all the time now. Also add "mention" dropdown option
Diffstat (limited to 'app/assets/javascripts/components/features')
7 files changed, 86 insertions, 114 deletions
diff --git a/app/assets/javascripts/components/features/account/components/action_bar.jsx b/app/assets/javascripts/components/features/account/components/action_bar.jsx index 0f26b1e5a..195b143af 100644 --- a/app/assets/javascripts/components/features/account/components/action_bar.jsx +++ b/app/assets/javascripts/components/features/account/components/action_bar.jsx @@ -8,7 +8,8 @@ const ActionBar = React.createClass({ account: ImmutablePropTypes.map.isRequired, me: React.PropTypes.number.isRequired, onFollow: React.PropTypes.func.isRequired, - onBlock: React.PropTypes.func.isRequired + onBlock: React.PropTypes.func.isRequired, + onMention: React.PropTypes.func.isRequired }, mixins: [PureRenderMixin], @@ -18,6 +19,8 @@ const ActionBar = React.createClass({ let menu = []; + menu.push({ text: 'Mention', action: this.props.onMention }); + if (account.get('id') === me) { menu.push({ text: 'Edit profile', href: '/settings/profile' }); } else if (account.getIn(['relationship', 'blocking'])) { @@ -32,26 +35,26 @@ const ActionBar = React.createClass({ return ( <div style={{ borderTop: '1px solid #363c4b', borderBottom: '1px solid #363c4b', lineHeight: '36px', overflow: 'hidden', flex: '0 0 auto', display: 'flex' }}> + <div style={{ padding: '10px', flex: '1 1 auto' }}> + <DropdownMenu items={menu} icon='bars' size={24} /> + </div> + <div style={{ flex: '1 1 auto', display: 'flex', lineHeight: '18px' }}> - <div style={{ overflow: 'hidden', width: '80px', borderRight: '1px solid #363c4b', padding: '10px', paddingRight: '5px' }}> + <div style={{ overflow: 'hidden', width: '80px', borderLeft: '1px solid #363c4b', padding: '10px', paddingRight: '5px' }}> <span style={{ display: 'block', textTransform: 'uppercase', fontSize: '11px', color: '#616b86' }}>Posts</span> <span style={{ display: 'block', fontSize: '15px', fontWeight: '500', color: '#fff' }}>{account.get('statuses_count')}</span> </div> - <div style={{ overflow: 'hidden', width: '80px', borderRight: '1px solid #363c4b', padding: '10px 5px' }}> + <div style={{ overflow: 'hidden', width: '80px', borderLeft: '1px solid #363c4b', padding: '10px 5px' }}> <span style={{ display: 'block', textTransform: 'uppercase', fontSize: '11px', color: '#616b86' }}>Follows</span> <span style={{ display: 'block', fontSize: '15px', fontWeight: '500', color: '#fff' }}>{account.get('following_count')}</span> </div> - <div style={{ overflow: 'hidden', width: '80px', padding: '10px 5px', borderRight: '1px solid #363c4b' }}> + <div style={{ overflow: 'hidden', width: '80px', padding: '10px 5px', borderLeft: '1px solid #363c4b' }}> <span style={{ display: 'block', textTransform: 'uppercase', fontSize: '11px', color: '#616b86' }}>Followers</span> <span style={{ display: 'block', fontSize: '15px', fontWeight: '500', color: '#fff' }}>{account.get('followers_count')}</span> </div> </div> - - <div style={{ padding: '10px', flex: '1 1 auto' }}> - <DropdownMenu items={menu} icon='bars' size={24} /> - </div> </div> ); }, diff --git a/app/assets/javascripts/components/features/account/index.jsx b/app/assets/javascripts/components/features/account/index.jsx index 83770eb74..76d69f751 100644 --- a/app/assets/javascripts/components/features/account/index.jsx +++ b/app/assets/javascripts/components/features/account/index.jsx @@ -10,6 +10,7 @@ import { fetchAccountTimeline, expandAccountTimeline } from '../../actions/accounts'; +import { mentionCompose } from '../../actions/compose'; import Header from './components/header'; import { getAccountTimeline, @@ -62,6 +63,10 @@ const Account = React.createClass({ } }, + handleMention () { + this.props.dispatch(mentionCompose(this.props.account)); + }, + render () { const { account, me } = this.props; @@ -78,7 +83,7 @@ const Account = React.createClass({ <ColumnBackButton /> <Header account={account} me={me} /> - <ActionBar account={account} me={me} onFollow={this.handleFollow} onBlock={this.handleBlock} /> + <ActionBar account={account} me={me} onFollow={this.handleFollow} onBlock={this.handleBlock} onMention={this.handleMention} /> {this.props.children} </Column> diff --git a/app/assets/javascripts/components/features/account_timeline/index.jsx b/app/assets/javascripts/components/features/account_timeline/index.jsx index 0b3d641d5..f79570361 100644 --- a/app/assets/javascripts/components/features/account_timeline/index.jsx +++ b/app/assets/javascripts/components/features/account_timeline/index.jsx @@ -1,23 +1,15 @@ import { connect } from 'react-redux'; import PureRenderMixin from 'react-addons-pure-render-mixin'; import ImmutablePropTypes from 'react-immutable-proptypes'; -import { getAccountTimeline } from '../../selectors'; import { fetchAccountTimeline, expandAccountTimeline } from '../../actions/accounts'; -import { deleteStatus } from '../../actions/statuses'; -import { replyCompose } from '../../actions/compose'; -import { - favourite, - reblog, - unreblog, - unfavourite -} from '../../actions/interactions'; import StatusList from '../../components/status_list'; +import LoadingIndicator from '../../components/loading_indicator'; const mapStateToProps = (state, props) => ({ - statuses: getAccountTimeline(state, Number(props.params.accountId)), + statusIds: state.getIn(['timelines', 'accounts_timelines', Number(props.params.accountId)]), me: state.getIn(['timelines', 'me']) }); @@ -26,7 +18,7 @@ const AccountTimeline = React.createClass({ propTypes: { params: React.PropTypes.object.isRequired, dispatch: React.PropTypes.func.isRequired, - statuses: ImmutablePropTypes.list + statusIds: ImmutablePropTypes.list }, mixins: [PureRenderMixin], @@ -41,38 +33,18 @@ const AccountTimeline = React.createClass({ } }, - handleReply (status) { - this.props.dispatch(replyCompose(status)); - }, - - handleReblog (status) { - if (status.get('reblogged')) { - this.props.dispatch(unreblog(status)); - } else { - this.props.dispatch(reblog(status)); - } - }, - - handleFavourite (status) { - if (status.get('favourited')) { - this.props.dispatch(unfavourite(status)); - } else { - this.props.dispatch(favourite(status)); - } - }, - - handleDelete (status) { - this.props.dispatch(deleteStatus(status.get('id'))); - }, - handleScrollToBottom () { this.props.dispatch(expandAccountTimeline(Number(this.props.params.accountId))); }, render () { - const { statuses, me } = this.props; + const { statusIds, me } = this.props; + + if (!statusIds) { + return <LoadingIndicator />; + } - return <StatusList statuses={statuses} me={me} onScrollToBottom={this.handleScrollToBottom} onReply={this.handleReply} onReblog={this.handleReblog} onFavourite={this.handleFavourite} onDelete={this.handleDelete} /> + return <StatusList statusIds={statusIds} me={me} onScrollToBottom={this.handleScrollToBottom} /> } }); diff --git a/app/assets/javascripts/components/features/status/components/action_bar.jsx b/app/assets/javascripts/components/features/status/components/action_bar.jsx index 6d6aa87fc..d0cae4557 100644 --- a/app/assets/javascripts/components/features/status/components/action_bar.jsx +++ b/app/assets/javascripts/components/features/status/components/action_bar.jsx @@ -11,6 +11,7 @@ const ActionBar = React.createClass({ onReblog: React.PropTypes.func.isRequired, onFavourite: React.PropTypes.func.isRequired, onDelete: React.PropTypes.func.isRequired, + onMention: React.PropTypes.func.isRequired, me: React.PropTypes.number.isRequired }, @@ -23,6 +24,8 @@ const ActionBar = React.createClass({ if (me === status.getIn(['account', 'id'])) { menu.push({ text: 'Delete', action: () => this.props.onDelete(status) }); + } else { + menu.push({ text: 'Mention', action: () => this.props.onMention(status.get('account')) }); } return ( diff --git a/app/assets/javascripts/components/features/status/index.jsx b/app/assets/javascripts/components/features/status/index.jsx index c51fb5d31..f4ca8ff92 100644 --- a/app/assets/javascripts/components/features/status/index.jsx +++ b/app/assets/javascripts/components/features/status/index.jsx @@ -9,22 +9,32 @@ import DetailedStatus from './components/detailed_status'; import ActionBar from './components/action_bar'; import Column from '../ui/components/column'; import { favourite, reblog } from '../../actions/interactions'; -import { replyCompose } from '../../actions/compose'; +import { + replyCompose, + mentionCompose +} from '../../actions/compose'; import { deleteStatus } from '../../actions/statuses'; import { - getStatus, + makeGetStatus, getStatusAncestors, getStatusDescendants } from '../../selectors'; import { ScrollContainer } from 'react-router-scroll'; import ColumnBackButton from '../../components/column_back_button'; +import StatusContainer from '../../containers/status_container'; -const mapStateToProps = (state, props) => ({ - status: getStatus(state, Number(props.params.statusId)), - ancestors: getStatusAncestors(state, Number(props.params.statusId)), - descendants: getStatusDescendants(state, Number(props.params.statusId)), - me: state.getIn(['timelines', 'me']) -}); +const makeMapStateToProps = () => { + const getStatus = makeGetStatus(); + + const mapStateToProps = (state, props) => ({ + status: getStatus(state, Number(props.params.statusId)), + ancestorsIds: state.getIn(['timelines', 'ancestors', Number(props.params.statusId)]), + descendantsIds: state.getIn(['timelines', 'descendants', Number(props.params.statusId)]), + me: state.getIn(['timelines', 'me']) + }); + + return mapStateToProps; +}; const Status = React.createClass({ @@ -32,8 +42,8 @@ const Status = React.createClass({ params: React.PropTypes.object.isRequired, dispatch: React.PropTypes.func.isRequired, status: ImmutablePropTypes.map, - ancestors: ImmutablePropTypes.orderedSet.isRequired, - descendants: ImmutablePropTypes.orderedSet.isRequired + ancestorsIds: ImmutablePropTypes.orderedSet, + descendantsIds: ImmutablePropTypes.orderedSet }, mixins: [PureRenderMixin], @@ -64,12 +74,17 @@ const Status = React.createClass({ this.props.dispatch(deleteStatus(status.get('id'))); }, + handleMentionClick (account) { + this.props.dispatch(mentionCompose(account)); + }, + renderChildren (list) { - return list.map(s => <EmbeddedStatus status={s} me={this.props.me} key={s.get('id')} onReply={this.handleReplyClick} onFavourite={this.handleFavouriteClick} onReblog={this.handleReblogClick} onDelete={this.handleDeleteClick} />); + return list.map(id => <StatusContainer key={id} id={id} />); }, render () { - const { status, ancestors, descendants, me } = this.props; + let ancestors, descendants; + const { status, ancestorsIds, descendantsIds, me } = this.props; if (status === null) { return ( @@ -81,18 +96,26 @@ const Status = React.createClass({ const account = status.get('account'); + if (ancestorsIds) { + ancestors = <div>{this.renderChildren(ancestorsIds)}</div>; + } + + if (descendantsIds) { + descendants = <div>{this.renderChildren(descendantsIds)}</div>; + } + return ( <Column> <ColumnBackButton /> <ScrollContainer scrollKey='thread'> <div style={{ overflowY: 'scroll', flex: '1 1 auto' }} className='scrollable'> - <div>{this.renderChildren(ancestors)}</div> + {ancestors} <DetailedStatus status={status} me={me} /> - <ActionBar status={status} me={me} onReply={this.handleReplyClick} onFavourite={this.handleFavouriteClick} onReblog={this.handleReblogClick} onDelete={this.handleDeleteClick} /> + <ActionBar status={status} me={me} onReply={this.handleReplyClick} onFavourite={this.handleFavouriteClick} onReblog={this.handleReblogClick} onDelete={this.handleDeleteClick} onMention={this.handleMentionClick} /> - <div>{this.renderChildren(descendants)}</div> + {descendants} </div> </ScrollContainer> </Column> @@ -101,4 +124,4 @@ const Status = React.createClass({ }); -export default connect(mapStateToProps)(Status); +export default connect(makeMapStateToProps)(Status); diff --git a/app/assets/javascripts/components/features/ui/containers/compose_form_container.jsx b/app/assets/javascripts/components/features/ui/containers/compose_form_container.jsx index 747eb9691..163d6fa20 100644 --- a/app/assets/javascripts/components/features/ui/containers/compose_form_container.jsx +++ b/app/assets/javascripts/components/features/ui/containers/compose_form_container.jsx @@ -1,15 +1,21 @@ import { connect } from 'react-redux'; import ComposeForm from '../components/compose_form'; import { changeCompose, submitCompose, cancelReplyCompose } from '../../../actions/compose'; -import { getStatus } from '../../../selectors'; +import { makeGetStatus } from '../../../selectors'; -const mapStateToProps = function (state, props) { - return { - text: state.getIn(['compose', 'text']), - is_submitting: state.getIn(['compose', 'is_submitting']), - is_uploading: state.getIn(['compose', 'is_uploading']), - in_reply_to: getStatus(state, state.getIn(['compose', 'in_reply_to'])) +const makeMapStateToProps = () => { + const getStatus = makeGetStatus(); + + const mapStateToProps = function (state, props) { + return { + text: state.getIn(['compose', 'text']), + is_submitting: state.getIn(['compose', 'is_submitting']), + is_uploading: state.getIn(['compose', 'is_uploading']), + in_reply_to: getStatus(state, state.getIn(['compose', 'in_reply_to'])) + }; }; + + return mapStateToProps; }; const mapDispatchToProps = function (dispatch) { @@ -28,4 +34,4 @@ const mapDispatchToProps = function (dispatch) { } }; -export default connect(mapStateToProps, mapDispatchToProps)(ComposeForm); +export default connect(makeMapStateToProps, mapDispatchToProps)(ComposeForm); diff --git a/app/assets/javascripts/components/features/ui/containers/status_list_container.jsx b/app/assets/javascripts/components/features/ui/containers/status_list_container.jsx index 045cc59d1..213435a06 100644 --- a/app/assets/javascripts/components/features/ui/containers/status_list_container.jsx +++ b/app/assets/javascripts/components/features/ui/containers/status_list_container.jsx @@ -1,57 +1,17 @@ import { connect } from 'react-redux'; import StatusList from '../../../components/status_list'; -import { replyCompose } from '../../../actions/compose'; -import { - reblog, - favourite, - unreblog, - unfavourite -} from '../../../actions/interactions'; import { expandTimeline } from '../../../actions/timelines'; -import { makeGetTimeline } from '../../../selectors'; -import { deleteStatus } from '../../../actions/statuses'; -const makeMapStateToProps = () => { - const getTimeline = makeGetTimeline(); - - const mapStateToProps = (state, props) => ({ - statuses: getTimeline(state, props.type), - me: state.getIn(['timelines', 'me']) - }); - - return mapStateToProps; -}; +const mapStateToProps = (state, props) => ({ + statusIds: state.getIn(['timelines', props.type]) +}); const mapDispatchToProps = function (dispatch, props) { return { - onReply (status) { - dispatch(replyCompose(status)); - }, - - onFavourite (status) { - if (status.get('favourited')) { - dispatch(unfavourite(status)); - } else { - dispatch(favourite(status)); - } - }, - - onReblog (status) { - if (status.get('reblogged')) { - dispatch(unreblog(status)); - } else { - dispatch(reblog(status)); - } - }, - onScrollToBottom () { dispatch(expandTimeline(props.type)); - }, - - onDelete (status) { - dispatch(deleteStatus(status.get('id'))); } }; }; -export default connect(makeMapStateToProps, mapDispatchToProps)(StatusList); +export default connect(mapStateToProps, mapDispatchToProps)(StatusList); |