-import api from '../api'
+import api, { getLinks } from '../api'
import Immutable from 'immutable';
export const ACCOUNT_SET_SELF = 'ACCOUNT_SET_SELF';
export const FOLLOWERS_FETCH_SUCCESS = 'FOLLOWERS_FETCH_SUCCESS';
export const FOLLOWERS_FETCH_FAIL = 'FOLLOWERS_FETCH_FAIL';
+export const FOLLOWERS_EXPAND_REQUEST = 'FOLLOWERS_EXPAND_REQUEST';
+export const FOLLOWERS_EXPAND_SUCCESS = 'FOLLOWERS_EXPAND_SUCCESS';
+export const FOLLOWERS_EXPAND_FAIL = 'FOLLOWERS_EXPAND_FAIL';
+
export const FOLLOWING_FETCH_REQUEST = 'FOLLOWING_FETCH_REQUEST';
export const FOLLOWING_FETCH_SUCCESS = 'FOLLOWING_FETCH_SUCCESS';
export const FOLLOWING_FETCH_FAIL = 'FOLLOWING_FETCH_FAIL';
+export const FOLLOWING_EXPAND_REQUEST = 'FOLLOWING_EXPAND_REQUEST';
+export const FOLLOWING_EXPAND_SUCCESS = 'FOLLOWING_EXPAND_SUCCESS';
+export const FOLLOWING_EXPAND_FAIL = 'FOLLOWING_EXPAND_FAIL';
+
export const RELATIONSHIPS_FETCH_REQUEST = 'RELATIONSHIPS_FETCH_REQUEST';
export const RELATIONSHIPS_FETCH_SUCCESS = 'RELATIONSHIPS_FETCH_SUCCESS';
export const RELATIONSHIPS_FETCH_FAIL = 'RELATIONSHIPS_FETCH_FAIL';
export function setAccountSelf(account) {
return {
type: ACCOUNT_SET_SELF,
- account: account
+ account
};
};
export function fetchAccountRequest(id) {
return {
type: ACCOUNT_FETCH_REQUEST,
- id: id
+ id
};
};
export function fetchAccountSuccess(account) {
return {
type: ACCOUNT_FETCH_SUCCESS,
- account: account
+ account
};
};
export function fetchAccountFail(id, error) {
return {
type: ACCOUNT_FETCH_FAIL,
- id: id,
- error: error
+ id,
+ error
};
};
export function followAccountRequest(id) {
return {
type: ACCOUNT_FOLLOW_REQUEST,
- id: id
+ id
};
};
export function followAccountSuccess(relationship) {
return {
type: ACCOUNT_FOLLOW_SUCCESS,
- relationship: relationship
+ relationship
};
};
export function followAccountFail(error) {
return {
type: ACCOUNT_FOLLOW_FAIL,
- error: error
+ error
};
};
export function unfollowAccountRequest(id) {
return {
type: ACCOUNT_UNFOLLOW_REQUEST,
- id: id
+ id
};
};
export function unfollowAccountSuccess(relationship) {
return {
type: ACCOUNT_UNFOLLOW_SUCCESS,
- relationship: relationship
+ relationship
};
};
export function unfollowAccountFail(error) {
return {
type: ACCOUNT_UNFOLLOW_FAIL,
- error: error
+ error
};
};
export function fetchAccountTimelineRequest(id) {
return {
type: ACCOUNT_TIMELINE_FETCH_REQUEST,
- id: id
+ id
};
};
export function fetchAccountTimelineSuccess(id, statuses, replace) {
return {
type: ACCOUNT_TIMELINE_FETCH_SUCCESS,
- id: id,
- statuses: statuses,
- replace: replace
+ id,
+ statuses,
+ replace
};
};
export function fetchAccountTimelineFail(id, error) {
return {
type: ACCOUNT_TIMELINE_FETCH_FAIL,
- id: id,
- error: error
+ id,
+ error
};
};
export function expandAccountTimelineRequest(id) {
return {
type: ACCOUNT_TIMELINE_EXPAND_REQUEST,
- id: id
+ id
};
};
export function expandAccountTimelineSuccess(id, statuses) {
return {
type: ACCOUNT_TIMELINE_EXPAND_SUCCESS,
- id: id,
- statuses: statuses
+ id,
+ statuses
};
};
export function expandAccountTimelineFail(id, error) {
return {
type: ACCOUNT_TIMELINE_EXPAND_FAIL,
- id: id,
- error: error
+ id,
+ error
};
};
export function blockAccountRequest(id) {
return {
type: ACCOUNT_BLOCK_REQUEST,
- id: id
+ id
};
};
export function blockAccountSuccess(relationship) {
return {
type: ACCOUNT_BLOCK_SUCCESS,
- relationship: relationship
+ relationship
};
};
export function blockAccountFail(error) {
return {
type: ACCOUNT_BLOCK_FAIL,
- error: error
+ error
};
};
export function unblockAccountRequest(id) {
return {
type: ACCOUNT_UNBLOCK_REQUEST,
- id: id
+ id
};
};
export function unblockAccountSuccess(relationship) {
return {
type: ACCOUNT_UNBLOCK_SUCCESS,
- relationship: relationship
+ relationship
};
};
export function unblockAccountFail(error) {
return {
type: ACCOUNT_UNBLOCK_FAIL,
- error: error
+ error
};
};
dispatch(fetchFollowersRequest(id));
api(getState).get(`/api/v1/accounts/${id}/followers`).then(response => {
- dispatch(fetchFollowersSuccess(id, response.data));
+ const prev = getLinks(response).refs.find(link => link.rel === 'prev').uri;
+
+ dispatch(fetchFollowersSuccess(id, response.data, prev));
dispatch(fetchRelationships(response.data.map(item => item.id)));
}).catch(error => {
dispatch(fetchFollowersFail(id, error));
export function fetchFollowersRequest(id) {
return {
type: FOLLOWERS_FETCH_REQUEST,
- id: id
+ id
};
};
-export function fetchFollowersSuccess(id, accounts) {
+export function fetchFollowersSuccess(id, accounts, prev) {
return {
type: FOLLOWERS_FETCH_SUCCESS,
- id: id,
- accounts: accounts
+ id,
+ accounts,
+ prev
};
};
export function fetchFollowersFail(id, error) {
return {
type: FOLLOWERS_FETCH_FAIL,
- id: id,
- error: error
+ id,
+ error
+ };
+};
+
+export function expandFollowers(id) {
+ return (dispatch, getState) => {
+ const url = getState().getIn(['user_lists', 'followers', id, 'prev']);
+
+ dispatch(expandFollowersRequest(id));
+
+ api(getState).get(url).then(response => {
+ const prev = getLinks(response).refs.find(link => link.rel === 'prev').uri;
+
+ dispatch(expandFollowersSuccess(id, response.data, prev));
+ dispatch(fetchRelationships(response.data.map(item => item.id)));
+ }).catch(error => {
+ dispatch(expandFollowersFail(id, error));
+ });
+ };
+};
+
+export function expandFollowersRequest(id) {
+ return {
+ type: FOLLOWERS_EXPAND_REQUEST,
+ id
+ };
+};
+
+export function expandFollowersSuccess(id, accounts, prev) {
+ return {
+ type: FOLLOWERS_EXPAND_SUCCESS,
+ id,
+ accounts,
+ prev
+ };
+};
+
+export function expandFollowersFail(id, error) {
+ return {
+ type: FOLLOWERS_EXPAND_FAIL,
+ id,
+ error
};
};
export function fetchFollowingRequest(id) {
return {
type: FOLLOWING_FETCH_REQUEST,
- id: id
+ id
};
};
export function fetchFollowingSuccess(id, accounts) {
return {
type: FOLLOWING_FETCH_SUCCESS,
- id: id,
- accounts: accounts
+ id,
+ accounts
};
};
export function fetchFollowingFail(id, error) {
return {
type: FOLLOWING_FETCH_FAIL,
- id: id,
- error: error
+ id,
+ error
+ };
+};
+
+export function expandFollowing(id) {
+ return (dispatch, getState) => {
+ const url = getState().getIn(['user_lists', 'following', id, 'prev']);
+
+ dispatch(expandFollowingRequest(id));
+
+ api(getState).get(url).then(response => {
+ const prev = getLinks(response).refs.find(link => link.rel === 'prev').uri;
+
+ dispatch(expandFollowingSuccess(id, response.data, prev));
+ dispatch(fetchRelationships(response.data.map(item => item.id)));
+ }).catch(error => {
+ dispatch(expandFollowingFail(id, error));
+ });
+ };
+};
+
+export function expandFollowingRequest(id) {
+ return {
+ type: FOLLOWING_EXPAND_REQUEST,
+ id
+ };
+};
+
+export function expandFollowingSuccess(id, accounts, prev) {
+ return {
+ type: FOLLOWING_EXPAND_SUCCESS,
+ id,
+ accounts,
+ prev
+ };
+};
+
+export function expandFollowingFail(id, error) {
+ return {
+ type: FOLLOWING_EXPAND_FAIL,
+ id,
+ error
};
};
export function fetchRelationshipsRequest(ids) {
return {
type: RELATIONSHIPS_FETCH_REQUEST,
- ids: ids
+ ids
};
};
export function fetchRelationshipsSuccess(relationships) {
return {
type: RELATIONSHIPS_FETCH_SUCCESS,
- relationships: relationships
+ relationships
};
};
export function fetchRelationshipsFail(error) {
return {
type: RELATIONSHIPS_FETCH_FAIL,
- error: error
+ error
};
};
-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 { fetchFollowers } from '../../actions/accounts';
-import { ScrollContainer } from 'react-router-scroll';
-import AccountContainer from './containers/account_container';
+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 {
+ fetchFollowers,
+ expandFollowers
+} from '../../actions/accounts';
+import { ScrollContainer } from 'react-router-scroll';
+import AccountContainer from './containers/account_container';
const mapStateToProps = (state, props) => ({
- accountIds: state.getIn(['user_lists', 'followers', Number(props.params.accountId)])
+ accountIds: state.getIn(['user_lists', 'followers', Number(props.params.accountId), 'items'])
});
const Followers = React.createClass({
}
},
+ handleScroll (e) {
+ const { scrollTop, scrollHeight, clientHeight } = e.target;
+
+ if (scrollTop === scrollHeight - clientHeight) {
+ this.props.dispatch(expandFollowers(Number(this.props.params.accountId)));
+ }
+ },
+
render () {
const { accountIds } = this.props;
return (
<ScrollContainer scrollKey='followers'>
- <div className='scrollable'>
- {accountIds.map(id => <AccountContainer key={id} id={id} withNote={false} />)}
+ <div className='scrollable' onScroll={this.handleScroll}>
+ <div>
+ {accountIds.map(id => <AccountContainer key={id} id={id} withNote={false} />)}
+ </div>
</div>
</ScrollContainer>
);
-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 { fetchFollowing } from '../../actions/accounts';
-import { ScrollContainer } from 'react-router-scroll';
-import AccountContainer from '../followers/containers/account_container';
+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 {
+ fetchFollowing,
+ expandFollowing
+} from '../../actions/accounts';
+import { ScrollContainer } from 'react-router-scroll';
+import AccountContainer from '../followers/containers/account_container';
const mapStateToProps = (state, props) => ({
- accountIds: state.getIn(['user_lists', 'following', Number(props.params.accountId)])
+ accountIds: state.getIn(['user_lists', 'following', Number(props.params.accountId), 'items'])
});
const Following = React.createClass({
}
},
+ handleScroll (e) {
+ const { scrollTop, scrollHeight, clientHeight } = e.target;
+
+ if (scrollTop === scrollHeight - clientHeight) {
+ this.props.dispatch(expandFollowing(Number(this.props.params.accountId)));
+ }
+ },
+
render () {
const { accountIds } = this.props;
return (
<ScrollContainer scrollKey='following'>
- <div className='scrollable'>
- {accountIds.map(id => <AccountContainer key={id} id={id} withNote={false} />)}
+ <div className='scrollable' onScroll={this.handleScroll}>
+ <div>
+ {accountIds.map(id => <AccountContainer key={id} id={id} withNote={false} />)}
+ </div>
</div>
</ScrollContainer>
);
import {
FOLLOWERS_FETCH_SUCCESS,
- FOLLOWING_FETCH_SUCCESS
+ FOLLOWERS_EXPAND_SUCCESS,
+ FOLLOWING_FETCH_SUCCESS,
+ FOLLOWING_EXPAND_SUCCESS
} from '../actions/accounts';
import { SUGGESTIONS_FETCH_SUCCESS } from '../actions/suggestions';
import {
favourited_by: Immutable.Map()
});
+const normalizeList = (state, type, id, accounts, prev) => {
+ return state.setIn([type, id], Immutable.Map({
+ prev,
+ items: Immutable.List(accounts.map(item => item.id))
+ }));
+};
+
+const appendToList = (state, type, id, accounts, prev) => {
+ return state.updateIn([type, id], map => {
+ return map.set('prev', prev).update('items', list => list.push(...accounts.map(item => item.id)));
+ });
+};
+
export default function userLists(state = initialState, action) {
switch(action.type) {
case FOLLOWERS_FETCH_SUCCESS:
- return state.setIn(['followers', action.id], Immutable.List(action.accounts.map(item => item.id)));
+ return normalizeList(state, 'followers', action.id, action.accounts, action.prev);
+ case FOLLOWERS_EXPAND_SUCCESS:
+ return appendToList(state, 'followers', action.id, action.accounts, action.prev);
case FOLLOWING_FETCH_SUCCESS:
- return state.setIn(['following', action.id], Immutable.List(action.accounts.map(item => item.id)));
+ return normalizeList(state, 'following', action.id, action.accounts, action.prev);
+ case FOLLOWING_EXPAND_SUCCESS:
+ return appendToList(state, 'following', action.id, action.accounts, action.prev);
case SUGGESTIONS_FETCH_SUCCESS:
return state.set('suggestions', Immutable.List(action.accounts.map(item => item.id)));
case REBLOGS_FETCH_SUCCESS: