@@ -16,6 +16,10 @@ export const UNFAVOURITE_REQUEST = 'UNFAVOURITE_REQUEST'; | |||
export const UNFAVOURITE_SUCCESS = 'UNFAVOURITE_SUCCESS'; | |||
export const UNFAVOURITE_FAIL = 'UNFAVOURITE_FAIL'; | |||
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 function reblog(status) { | |||
return function (dispatch, getState) { | |||
dispatch(reblogRequest(status)); | |||
@@ -157,3 +161,37 @@ export function unfavouriteFail(status, error) { | |||
error: error | |||
}; | |||
}; | |||
export function fetchReblogs(id) { | |||
return (dispatch, getState) => { | |||
dispatch(fetchReblogsRequest(id)); | |||
api(getState).get(`/api/v1/statuses/${id}/reblogged_by`).then(response => { | |||
dispatch(fetchReblogsSuccess(id, response.data)); | |||
}).catch(error => { | |||
dispatch(fetchReblogsFail(id, error)); | |||
}); | |||
}; | |||
}; | |||
export function fetchReblogsRequest(id) { | |||
return { | |||
type: REBLOGS_FETCH_REQUEST, | |||
id | |||
}; | |||
}; | |||
export function fetchReblogsSuccess(id, accounts) { | |||
return { | |||
type: REBLOGS_FETCH_SUCCESS, | |||
id, | |||
accounts | |||
}; | |||
}; | |||
export function fetchReblogsFail(id, error) { | |||
return { | |||
type: REBLOGS_FETCH_FAIL, | |||
error | |||
}; | |||
}; |
@@ -28,6 +28,7 @@ import MentionsTimeline from '../features/mentions_timeline'; | |||
import Compose from '../features/compose'; | |||
import Followers from '../features/followers'; | |||
import Following from '../features/following'; | |||
import Reblogs from '../features/reblogs'; | |||
const store = configureStore(); | |||
@@ -83,6 +84,7 @@ const Mastodon = React.createClass({ | |||
<Route path='/statuses/mentions' component={MentionsTimeline} /> | |||
<Route path='/statuses/all' component={PublicTimeline} /> | |||
<Route path='/statuses/:statusId' component={Status} /> | |||
<Route path='/statuses/:statusId/reblogs' component={Reblogs} /> | |||
<Route path='/accounts/:accountId' component={Account}> | |||
<IndexRoute component={AccountTimeline} /> | |||
<Route path='/accounts/:accountId/followers' component={Followers} /> | |||
@@ -0,0 +1,61 @@ | |||
import { connect } from 'react-redux'; | |||
import PureRenderMixin from 'react-addons-pure-render-mixin'; | |||
import ImmutablePropTypes from 'react-immutable-proptypes'; | |||
import LoadingIndicator from '../../components/loading_indicator'; | |||
import { fetchReblogs } from '../../actions/interactions'; | |||
import { ScrollContainer } from 'react-router-scroll'; | |||
import AccountContainer from '../followers/containers/account_container'; | |||
import Column from '../ui/components/column'; | |||
import ColumnBackButton from '../../components/column_back_button'; | |||
const mapStateToProps = (state, props) => ({ | |||
accountIds: state.getIn(['user_lists', 'reblogged_by', Number(props.params.statusId)]) | |||
}); | |||
const Reblogs = React.createClass({ | |||
propTypes: { | |||
params: React.PropTypes.object.isRequired, | |||
dispatch: React.PropTypes.func.isRequired, | |||
accountIds: ImmutablePropTypes.list | |||
}, | |||
mixins: [PureRenderMixin], | |||
componentWillMount () { | |||
this.props.dispatch(fetchReblogs(Number(this.props.params.statusId))); | |||
}, | |||
componentWillReceiveProps(nextProps) { | |||
if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) { | |||
this.props.dispatch(fetchReblogs(Number(nextProps.params.statusId))); | |||
} | |||
}, | |||
render () { | |||
const { accountIds } = this.props; | |||
if (!accountIds) { | |||
return ( | |||
<Column> | |||
<LoadingIndicator /> | |||
</Column> | |||
); | |||
} | |||
return ( | |||
<Column> | |||
<ColumnBackButton /> | |||
<ScrollContainer scrollKey='reblogs'> | |||
<div style={{ overflowY: 'scroll', flex: '1 1 auto', overflowX: 'hidden' }} className='scrollable'> | |||
{accountIds.map(id => <AccountContainer key={id} id={id} withNote={false} />)} | |||
</div> | |||
</ScrollContainer> | |||
</Column> | |||
); | |||
} | |||
}); | |||
export default connect(mapStateToProps)(Reblogs); |
@@ -6,6 +6,7 @@ import StatusContent from '../../../components/status_content'; | |||
import MediaGallery from '../../../components/media_gallery'; | |||
import VideoPlayer from '../../../components/video_player'; | |||
import moment from 'moment'; | |||
import { Link } from 'react-router'; | |||
const DetailedStatus = React.createClass({ | |||
@@ -53,7 +54,7 @@ const DetailedStatus = React.createClass({ | |||
{media} | |||
<div style={{ marginTop: '15px', color: '#616b86', fontSize: '14px', lineHeight: '18px' }}> | |||
<a className='detailed-status__datetime' style={{ color: 'inherit' }} href={status.get('url')} target='_blank' rel='noopener'>{moment(status.get('created_at')).format('HH:mm, DD MMM Y')}</a> · <i className='fa fa-retweet' /><span style={{ fontWeight: '500', fontSize: '12px', marginLeft: '6px', display: 'inline-block' }}>{status.get('reblogs_count')}</span> · <i className='fa fa-star' /><span style={{ fontWeight: '500', fontSize: '12px', marginLeft: '6px', display: 'inline-block' }}>{status.get('favourites_count')}</span> | |||
<a className='detailed-status__datetime' style={{ color: 'inherit' }} href={status.get('url')} target='_blank' rel='noopener'>{moment(status.get('created_at')).format('HH:mm, DD MMM Y')}</a> · <Link to={`/statuses/${status.get('id')}/reblogs`} style={{ color: 'inherit', textDecoration: 'none' }}><i className='fa fa-retweet' /><span style={{ fontWeight: '500', fontSize: '12px', marginLeft: '6px', display: 'inline-block' }}>{status.get('reblogs_count')}</span></Link> · <i className='fa fa-star' /><span style={{ fontWeight: '500', fontSize: '12px', marginLeft: '6px', display: 'inline-block' }}>{status.get('favourites_count')}</span> | |||
</div> | |||
</div> | |||
); | |||
@@ -12,7 +12,8 @@ import { | |||
REBLOG_SUCCESS, | |||
UNREBLOG_SUCCESS, | |||
FAVOURITE_SUCCESS, | |||
UNFAVOURITE_SUCCESS | |||
UNFAVOURITE_SUCCESS, | |||
REBLOGS_FETCH_SUCCESS | |||
} from '../actions/interactions'; | |||
import { | |||
TIMELINE_REFRESH_SUCCESS, | |||
@@ -64,6 +65,7 @@ export default function accounts(state = initialState, action) { | |||
case SUGGESTIONS_FETCH_SUCCESS: | |||
case FOLLOWERS_FETCH_SUCCESS: | |||
case FOLLOWING_FETCH_SUCCESS: | |||
case REBLOGS_FETCH_SUCCESS: | |||
return normalizeAccounts(state, action.accounts); | |||
case TIMELINE_REFRESH_SUCCESS: | |||
case TIMELINE_EXPAND_SUCCESS: | |||
@@ -3,6 +3,7 @@ import { | |||
FOLLOWING_FETCH_SUCCESS | |||
} from '../actions/accounts'; | |||
import { SUGGESTIONS_FETCH_SUCCESS } from '../actions/suggestions'; | |||
import { REBLOGS_FETCH_SUCCESS } from '../actions/interactions'; | |||
import Immutable from 'immutable'; | |||
const initialState = Immutable.Map({ | |||
@@ -19,6 +20,8 @@ export default function userLists(state = initialState, action) { | |||
return state.setIn(['following', action.id], Immutable.List(action.accounts.map(item => item.id))); | |||
case SUGGESTIONS_FETCH_SUCCESS: | |||
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))); | |||
default: | |||
return state; | |||
} | |||