all statuses/accounts to assemble, but at least lists don't have to be re-rendered all the time now. Also add "mention" dropdown optionmaster
@@ -6,6 +6,7 @@ export const COMPOSE_SUBMIT_SUCCESS = 'COMPOSE_SUBMIT_SUCCESS'; | |||
export const COMPOSE_SUBMIT_FAIL = 'COMPOSE_SUBMIT_FAIL'; | |||
export const COMPOSE_REPLY = 'COMPOSE_REPLY'; | |||
export const COMPOSE_REPLY_CANCEL = 'COMPOSE_REPLY_CANCEL'; | |||
export const COMPOSE_MENTION = 'COMPOSE_MENTION'; | |||
export const COMPOSE_UPLOAD_REQUEST = 'COMPOSE_UPLOAD_REQUEST'; | |||
export const COMPOSE_UPLOAD_SUCCESS = 'COMPOSE_UPLOAD_SUCCESS'; | |||
export const COMPOSE_UPLOAD_FAIL = 'COMPOSE_UPLOAD_FAIL'; | |||
@@ -32,6 +33,13 @@ export function cancelReplyCompose() { | |||
}; | |||
}; | |||
export function mentionCompose(account) { | |||
return { | |||
type: COMPOSE_MENTION, | |||
account: account | |||
}; | |||
}; | |||
export function submitCompose() { | |||
return function (dispatch, getState) { | |||
dispatch(submitComposeRequest()); | |||
@@ -9,7 +9,8 @@ const StatusActionBar = React.createClass({ | |||
onReply: React.PropTypes.func, | |||
onFavourite: React.PropTypes.func, | |||
onReblog: React.PropTypes.func, | |||
onDelete: React.PropTypes.func | |||
onDelete: React.PropTypes.func, | |||
onMention: React.PropTypes.func | |||
}, | |||
mixins: [PureRenderMixin], | |||
@@ -30,12 +31,18 @@ const StatusActionBar = React.createClass({ | |||
this.props.onDelete(this.props.status); | |||
}, | |||
handleMentionClick () { | |||
this.props.onMention(this.props.status.get('account')); | |||
}, | |||
render () { | |||
const { status, me } = this.props; | |||
let menu = []; | |||
if (status.getIn(['account', 'id']) === me) { | |||
menu.push({ text: 'Delete', action: this.handleDeleteClick }); | |||
} else { | |||
menu.push({ text: 'Mention', action: this.handleMentionClick }); | |||
} | |||
return ( | |||
@@ -2,18 +2,14 @@ import Status from './status'; | |||
import ImmutablePropTypes from 'react-immutable-proptypes'; | |||
import PureRenderMixin from 'react-addons-pure-render-mixin'; | |||
import { ScrollContainer } from 'react-router-scroll'; | |||
import StatusContainer from '../containers/status_container'; | |||
const StatusList = React.createClass({ | |||
propTypes: { | |||
statuses: ImmutablePropTypes.list.isRequired, | |||
onReply: React.PropTypes.func, | |||
onReblog: React.PropTypes.func, | |||
onFavourite: React.PropTypes.func, | |||
onDelete: React.PropTypes.func, | |||
statusIds: ImmutablePropTypes.list.isRequired, | |||
onScrollToBottom: React.PropTypes.func, | |||
trackScroll: React.PropTypes.bool, | |||
me: React.PropTypes.number | |||
trackScroll: React.PropTypes.bool | |||
}, | |||
getDefaultProps () { | |||
@@ -33,13 +29,13 @@ const StatusList = React.createClass({ | |||
}, | |||
render () { | |||
const { statuses, onScrollToBottom, trackScroll, ...other } = this.props; | |||
const { statusIds, onScrollToBottom, trackScroll } = this.props; | |||
const scrollableArea = ( | |||
<div style={{ overflowY: 'scroll', flex: '1 1 auto', overflowX: 'hidden' }} className='scrollable' onScroll={this.handleScroll}> | |||
<div> | |||
{statuses.map((status) => { | |||
return <Status key={status.get('id')} {...other} status={status} />; | |||
{statusIds.map((statusId) => { | |||
return <StatusContainer key={statusId} id={statusId} />; | |||
})} | |||
</div> | |||
</div> | |||
@@ -0,0 +1,59 @@ | |||
import { connect } from 'react-redux'; | |||
import Status from '../components/status'; | |||
import { makeGetStatus } from '../selectors'; | |||
import { | |||
replyCompose, | |||
mentionCompose | |||
} from '../actions/compose'; | |||
import { | |||
reblog, | |||
favourite, | |||
unreblog, | |||
unfavourite | |||
} from '../actions/interactions'; | |||
import { deleteStatus } from '../actions/statuses'; | |||
const makeMapStateToProps = () => { | |||
const getStatus = makeGetStatus(); | |||
const mapStateToProps = (state, props) => ({ | |||
status: getStatus(state, props.id), | |||
me: state.getIn(['timelines', 'me']) | |||
}); | |||
return mapStateToProps; | |||
}; | |||
const mapDispatchToProps = (dispatch) => ({ | |||
onReply (status) { | |||
dispatch(replyCompose(status)); | |||
}, | |||
onReblog (status) { | |||
if (status.get('reblogged')) { | |||
dispatch(unreblog(status)); | |||
} else { | |||
dispatch(reblog(status)); | |||
} | |||
}, | |||
onFavourite (status) { | |||
if (status.get('favourited')) { | |||
dispatch(unfavourite(status)); | |||
} else { | |||
dispatch(favourite(status)); | |||
} | |||
}, | |||
onDelete (status) { | |||
dispatch(deleteStatus(status.get('id'))); | |||
}, | |||
onMention (account) { | |||
dispatch(mentionCompose(account)); | |||
} | |||
}); | |||
export default connect(makeMapStateToProps, mapDispatchToProps)(Status); |
@@ -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> | |||
); | |||
}, | |||
@@ -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> | |||
@@ -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} /> | |||
} | |||
}); | |||
@@ -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 ( | |||
@@ -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); |
@@ -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); |
@@ -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); |
@@ -2,6 +2,7 @@ import { | |||
COMPOSE_CHANGE, | |||
COMPOSE_REPLY, | |||
COMPOSE_REPLY_CANCEL, | |||
COMPOSE_MENTION, | |||
COMPOSE_SUBMIT_REQUEST, | |||
COMPOSE_SUBMIT_SUCCESS, | |||
COMPOSE_SUBMIT_FAIL, | |||
@@ -32,7 +33,7 @@ function statusToTextMentions(state, status) { | |||
if (status.getIn(['account', 'id']) !== me) { | |||
set = set.add(`@${status.getIn(['account', 'acct'])} `); | |||
} | |||
return set.union(status.get('mentions').filterNot(mention => mention.get('id') === me).map(mention => `@${mention.get('acct')} `)).join(''); | |||
}; | |||
@@ -92,6 +93,8 @@ export default function compose(state = initialState, action) { | |||
return removeMedia(state, action.media_id); | |||
case COMPOSE_UPLOAD_PROGRESS: | |||
return state.set('progress', Math.round((action.loaded / action.total) * 100)); | |||
case COMPOSE_MENTION: | |||
return state.update('text', text => `${text}@${action.account.get('acct')} `); | |||
case TIMELINE_DELETE: | |||
if (action.id === state.get('in_reply_to')) { | |||
return state.set('in_reply_to', null); | |||
@@ -17,15 +17,15 @@ export const getAccount = createSelector([getAccountBase, getAccountRelationship | |||
const getStatusBase = (state, id) => state.getIn(['timelines', 'statuses', id], null); | |||
export const getStatus = createSelector([getStatusBase, getStatuses, getAccounts], (base, statuses, accounts) => { | |||
if (base === null) { | |||
return null; | |||
} | |||
return assembleStatus(base.get('id'), statuses, accounts); | |||
}); | |||
export const makeGetStatus = () => { | |||
return createSelector([getStatusBase, getStatuses, getAccounts], (base, statuses, accounts) => { | |||
if (base === null) { | |||
return null; | |||
} | |||
const getAccountTimelineIds = (state, id) => state.getIn(['timelines', 'accounts_timelines', id], Immutable.List()); | |||
return assembleStatus(base.get('id'), statuses, accounts); | |||
}); | |||
}; | |||
const assembleStatus = (id, statuses, accounts) => { | |||
let status = statuses.get(id, null); | |||
@@ -48,26 +48,6 @@ const assembleStatus = (id, statuses, accounts) => { | |||
return status.set('reblog', reblog).set('account', accounts.get(status.get('account'))); | |||
}; | |||
const assembleStatusList = (ids, statuses, accounts) => { | |||
return ids.map(statusId => assembleStatus(statusId, statuses, accounts)).filterNot(status => status === null); | |||
}; | |||
export const getAccountTimeline = createSelector([getAccountTimelineIds, getStatuses, getAccounts], assembleStatusList); | |||
const getTimelineIds = (state, timelineType) => state.getIn(['timelines', timelineType]); | |||
export const makeGetTimeline = () => { | |||
return createSelector([getTimelineIds, getStatuses, getAccounts], assembleStatusList); | |||
}; | |||
const getStatusAncestorsIds = (state, id) => state.getIn(['timelines', 'ancestors', id], Immutable.OrderedSet()); | |||
export const getStatusAncestors = createSelector([getStatusAncestorsIds, getStatuses, getAccounts], assembleStatusList); | |||
const getStatusDescendantsIds = (state, id) => state.getIn(['timelines', 'descendants', id], Immutable.OrderedSet()); | |||
export const getStatusDescendants = createSelector([getStatusDescendantsIds, getStatuses, getAccounts], assembleStatusList); | |||
const getNotificationsBase = state => state.get('notifications'); | |||
export const getNotifications = createSelector([getNotificationsBase], (base) => { | |||