diff --git a/app/assets/javascripts/components/actions/interactions.jsx b/app/assets/javascripts/components/actions/interactions.jsx
index b3569d0b318..5b13fd02e80 100644
--- a/app/assets/javascripts/components/actions/interactions.jsx
+++ b/app/assets/javascripts/components/actions/interactions.jsx
@@ -20,6 +20,10 @@ export const REBLOGS_FETCH_REQUEST = 'REBLOGS_FETCH_REQUEST';
export const REBLOGS_FETCH_SUCCESS = 'REBLOGS_FETCH_SUCCESS';
export const REBLOGS_FETCH_FAIL = 'REBLOGS_FETCH_FAIL';
+export const FAVOURITES_FETCH_REQUEST = 'FAVOURITES_FETCH_REQUEST';
+export const FAVOURITES_FETCH_SUCCESS = 'FAVOURITES_FETCH_SUCCESS';
+export const FAVOURITES_FETCH_FAIL = 'FAVOURITES_FETCH_FAIL';
+
export function reblog(status) {
return function (dispatch, getState) {
dispatch(reblogRequest(status));
diff --git a/app/assets/javascripts/components/components/relative_timestamp.jsx b/app/assets/javascripts/components/components/relative_timestamp.jsx
index 10b9e027432..1de44bb950e 100644
--- a/app/assets/javascripts/components/components/relative_timestamp.jsx
+++ b/app/assets/javascripts/components/components/relative_timestamp.jsx
@@ -21,35 +21,28 @@ moment.updateLocale('en', {
const RelativeTimestamp = React.createClass({
- getInitialState () {
- return {
- text: ''
- };
- },
-
propTypes: {
- timestamp: React.PropTypes.string.isRequired
+ timestamp: React.PropTypes.string.isRequired,
+ now: React.PropTypes.any
},
mixins: [PureRenderMixin],
- componentWillMount () {
- this._updateMomentText();
- this.interval = setInterval(this._updateMomentText, 60000);
- },
-
- componentWillUnmount () {
- clearInterval(this.interval);
- },
-
- _updateMomentText () {
- this.setState({ text: moment(this.props.timestamp).fromNow() });
- },
-
render () {
+ const timestamp = moment(this.props.timestamp);
+ const now = this.props.now;
+
+ let string = '';
+
+ if (timestamp.isAfter(now)) {
+ string = 'Just now';
+ } else {
+ string = timestamp.from(now);
+ }
+
return (
- {this.state.text}
+ {string}
);
}
diff --git a/app/assets/javascripts/components/components/status.jsx b/app/assets/javascripts/components/components/status.jsx
index 8424023a614..53b2017708b 100644
--- a/app/assets/javascripts/components/components/status.jsx
+++ b/app/assets/javascripts/components/components/status.jsx
@@ -22,7 +22,8 @@ const Status = React.createClass({
onReblog: React.PropTypes.func,
onDelete: React.PropTypes.func,
onOpenMedia: React.PropTypes.func,
- me: React.PropTypes.number
+ me: React.PropTypes.number,
+ now: React.PropTypes.any
},
mixins: [PureRenderMixin],
@@ -43,7 +44,7 @@ const Status = React.createClass({
render () {
let media = '';
- let { status, ...other } = this.props;
+ const { status, now, ...other } = this.props;
if (status === null) {
return
;
@@ -80,7 +81,7 @@ const Status = React.createClass({
diff --git a/app/assets/javascripts/components/components/status_list.jsx b/app/assets/javascripts/components/components/status_list.jsx
index b4463b69c28..49d4bef6469 100644
--- a/app/assets/javascripts/components/components/status_list.jsx
+++ b/app/assets/javascripts/components/components/status_list.jsx
@@ -3,6 +3,7 @@ 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';
+import moment from 'moment';
const StatusList = React.createClass({
@@ -18,8 +19,22 @@ const StatusList = React.createClass({
};
},
+ getInitialState () {
+ return {
+ now: moment()
+ };
+ },
+
mixins: [PureRenderMixin],
+ componentDidMount () {
+ this._interval = setInterval(() => this.setState({ now: moment() }), 60000);
+ },
+
+ componentWillUnmount () {
+ clearInterval(this._interval);
+ },
+
handleScroll (e) {
const { scrollTop, scrollHeight, clientHeight } = e.target;
@@ -35,7 +50,7 @@ const StatusList = React.createClass({
{statusIds.map((statusId) => {
- return ;
+ return ;
})}
diff --git a/app/assets/javascripts/components/containers/status_container.jsx b/app/assets/javascripts/components/containers/status_container.jsx
index 5e0b489b3e1..2bcb7026c0a 100644
--- a/app/assets/javascripts/components/containers/status_container.jsx
+++ b/app/assets/javascripts/components/containers/status_container.jsx
@@ -13,13 +13,47 @@ import {
} from '../actions/interactions';
import { deleteStatus } from '../actions/statuses';
import { openMedia } from '../actions/modal';
+import { createSelector } from 'reselect'
-const makeMapStateToProps = () => {
- const getStatus = makeGetStatus();
+const mapStateToProps = (state, props) => ({
+ statusBase: state.getIn(['statuses', props.id]),
+ me: state.getIn(['meta', 'me'])
+});
- const mapStateToProps = (state, props) => ({
- status: getStatus(state, props.id),
- me: state.getIn(['meta', 'me'])
+const makeMapStateToPropsInner = () => {
+ const getStatus = (() => {
+ return createSelector(
+ [
+ (_, base) => base,
+ (state, base) => (base ? state.getIn(['accounts', base.get('account')]) : null),
+ (state, base) => (base ? state.getIn(['statuses', base.get('reblog')], null) : null)
+ ],
+
+ (base, account, reblog) => (base ? base.set('account', account).set('reblog', reblog) : null)
+ );
+ })();
+
+ const mapStateToProps = (state, { statusBase }) => ({
+ status: getStatus(state, statusBase)
+ });
+
+ return mapStateToProps;
+};
+
+const makeMapStateToPropsLast = () => {
+ const getStatus = (() => {
+ return createSelector(
+ [
+ (_, status) => status,
+ (state, status) => (status ? state.getIn(['accounts', status.getIn(['reblog', 'account'])], null) : null)
+ ],
+
+ (status, reblogAccount) => (status && status.get('reblog') ? status.setIn(['reblog', 'account'], reblogAccount) : status)
+ );
+ })();
+
+ const mapStateToProps = (state, { status }) => ({
+ status: getStatus(state, status)
});
return mapStateToProps;
@@ -61,4 +95,8 @@ const mapDispatchToProps = (dispatch) => ({
});
-export default connect(makeMapStateToProps, mapDispatchToProps)(Status);
+export default connect(mapStateToProps, mapDispatchToProps)(
+ connect(makeMapStateToPropsInner)(
+ connect(makeMapStateToPropsLast)(Status)
+ )
+);
diff --git a/app/assets/javascripts/components/reducers/user_lists.jsx b/app/assets/javascripts/components/reducers/user_lists.jsx
index 686179af205..4c201f927b6 100644
--- a/app/assets/javascripts/components/reducers/user_lists.jsx
+++ b/app/assets/javascripts/components/reducers/user_lists.jsx
@@ -3,13 +3,18 @@ import {
FOLLOWING_FETCH_SUCCESS
} from '../actions/accounts';
import { SUGGESTIONS_FETCH_SUCCESS } from '../actions/suggestions';
-import { REBLOGS_FETCH_SUCCESS } from '../actions/interactions';
+import {
+ REBLOGS_FETCH_SUCCESS,
+ FAVOURITES_FETCH_SUCCESS
+} from '../actions/interactions';
import Immutable from 'immutable';
const initialState = Immutable.Map({
followers: Immutable.Map(),
following: Immutable.Map(),
- suggestions: Immutable.List()
+ suggestions: Immutable.List(),
+ reblogged_by: Immutable.Map(),
+ favourited_by: Immutable.Map()
});
export default function userLists(state = initialState, action) {
@@ -22,6 +27,8 @@ export default function userLists(state = initialState, action) {
return state.set('suggestions', Immutable.List(action.accounts.map(item => item.id)));
case REBLOGS_FETCH_SUCCESS:
return state.setIn(['reblogged_by', action.id], Immutable.List(action.accounts.map(item => item.id)));
+ case FAVOURITES_FETCH_SUCCESS:
+ return state.setIn(['favourited_by', action.id], Immutable.List(action.accounts.map(item => item.id)));
default:
return state;
}