+
Posts
{account.get('statuses_count')}
-
+
Follows
{account.get('following_count')}
-
+
Followers
{account.get('followers_count')}
-
-
-
-
);
},
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({
-
+
{this.props.children}
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;
- return
+ if (!statusIds) {
+ return
;
+ }
+
+ return
}
});
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 =>
);
+ return list.map(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 =
{this.renderChildren(ancestorsIds)}
;
+ }
+
+ if (descendantsIds) {
+ descendants =
{this.renderChildren(descendantsIds)}
;
+ }
+
return (
-
{this.renderChildren(ancestors)}
+ {ancestors}
-
+
-
{this.renderChildren(descendants)}
+ {descendants}
@@ -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);
diff --git a/app/assets/javascripts/components/reducers/compose.jsx b/app/assets/javascripts/components/reducers/compose.jsx
index 9c9ce566e..f2fd3ad80 100644
--- a/app/assets/javascripts/components/reducers/compose.jsx
+++ b/app/assets/javascripts/components/reducers/compose.jsx
@@ -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);
diff --git a/app/assets/javascripts/components/selectors/index.jsx b/app/assets/javascripts/components/selectors/index.jsx
index 91f900f90..b571e43d5 100644
--- a/app/assets/javascripts/components/selectors/index.jsx
+++ b/app/assets/javascripts/components/selectors/index.jsx
@@ -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;
- }
+export const makeGetStatus = () => {
+ return createSelector([getStatusBase, getStatuses, getAccounts], (base, statuses, accounts) => {
+ if (base === null) {
+ return null;
+ }
- return assembleStatus(base.get('id'), statuses, accounts);
-});
-
-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) => {