forked from treehouse/mastodon
Infinite scroll for account timelines
parent
4a670780f0
commit
2a84271e85
|
@ -1,4 +1,5 @@
|
|||
import api from '../api'
|
||||
import api from '../api'
|
||||
import axios from 'axios';
|
||||
|
||||
export const ACCOUNT_SET_SELF = 'ACCOUNT_SET_SELF';
|
||||
|
||||
|
@ -18,6 +19,10 @@ export const ACCOUNT_TIMELINE_FETCH_REQUEST = 'ACCOUNT_TIMELINE_FETCH_REQUEST';
|
|||
export const ACCOUNT_TIMELINE_FETCH_SUCCESS = 'ACCOUNT_TIMELINE_FETCH_SUCCESS';
|
||||
export const ACCOUNT_TIMELINE_FETCH_FAIL = 'ACCOUNT_TIMELINE_FETCH_FAIL';
|
||||
|
||||
export const ACCOUNT_TIMELINE_EXPAND_REQUEST = 'ACCOUNT_TIMELINE_EXPAND_REQUEST';
|
||||
export const ACCOUNT_TIMELINE_EXPAND_SUCCESS = 'ACCOUNT_TIMELINE_EXPAND_SUCCESS';
|
||||
export const ACCOUNT_TIMELINE_EXPAND_FAIL = 'ACCOUNT_TIMELINE_EXPAND_FAIL';
|
||||
|
||||
export function setAccountSelf(account) {
|
||||
return {
|
||||
type: ACCOUNT_SET_SELF,
|
||||
|
@ -27,10 +32,12 @@ export function setAccountSelf(account) {
|
|||
|
||||
export function fetchAccount(id) {
|
||||
return (dispatch, getState) => {
|
||||
const boundApi = api(getState);
|
||||
|
||||
dispatch(fetchAccountRequest(id));
|
||||
|
||||
api(getState).get(`/api/accounts/${id}`).then(response => {
|
||||
dispatch(fetchAccountSuccess(response.data));
|
||||
axios.all([boundApi.get(`/api/accounts/${id}`), boundApi.get(`/api/accounts/relationships?id=${id}`)]).then(values => {
|
||||
dispatch(fetchAccountSuccess(values[0].data, values[1].data[0]));
|
||||
}).catch(error => {
|
||||
dispatch(fetchAccountFail(id, error));
|
||||
});
|
||||
|
@ -49,6 +56,20 @@ export function fetchAccountTimeline(id) {
|
|||
};
|
||||
};
|
||||
|
||||
export function expandAccountTimeline(id) {
|
||||
return (dispatch, getState) => {
|
||||
const lastId = getState().getIn(['timelines', 'accounts_timelines', id]).last();
|
||||
|
||||
dispatch(expandAccountTimelineRequest(id));
|
||||
|
||||
api(getState).get(`/api/accounts/${id}/statuses?max_id=${lastId}`).then(response => {
|
||||
dispatch(expandAccountTimelineSuccess(id, response.data));
|
||||
}).catch(error => {
|
||||
dispatch(expandAccountTimelineFail(id, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchAccountRequest(id) {
|
||||
return {
|
||||
type: ACCOUNT_FETCH_REQUEST,
|
||||
|
@ -56,10 +77,11 @@ export function fetchAccountRequest(id) {
|
|||
};
|
||||
};
|
||||
|
||||
export function fetchAccountSuccess(account) {
|
||||
export function fetchAccountSuccess(account, relationship) {
|
||||
return {
|
||||
type: ACCOUNT_FETCH_SUCCESS,
|
||||
account: account
|
||||
account: account,
|
||||
relationship: relationship
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -159,3 +181,26 @@ export function fetchAccountTimelineFail(id, error) {
|
|||
error: error
|
||||
};
|
||||
};
|
||||
|
||||
export function expandAccountTimelineRequest(id) {
|
||||
return {
|
||||
type: ACCOUNT_TIMELINE_EXPAND_REQUEST,
|
||||
id: id
|
||||
};
|
||||
};
|
||||
|
||||
export function expandAccountTimelineSuccess(id, statuses) {
|
||||
return {
|
||||
type: ACCOUNT_TIMELINE_EXPAND_SUCCESS,
|
||||
id: id,
|
||||
statuses: statuses
|
||||
};
|
||||
};
|
||||
|
||||
export function expandAccountTimelineFail(id, error) {
|
||||
return {
|
||||
type: ACCOUNT_TIMELINE_EXPAND_FAIL,
|
||||
id: id,
|
||||
error: error
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,13 +1,19 @@
|
|||
import { connect } from 'react-redux';
|
||||
import PureRenderMixin from 'react-addons-pure-render-mixin';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { fetchAccount, followAccount, unfollowAccount, fetchAccountTimeline } from '../../actions/accounts';
|
||||
import { replyCompose } from '../../actions/compose';
|
||||
import { favourite, reblog } from '../../actions/interactions';
|
||||
import Header from './components/header';
|
||||
import { selectStatus } from '../../reducers/timelines';
|
||||
import StatusList from '../../components/status_list';
|
||||
import Immutable from 'immutable';
|
||||
import { connect } from 'react-redux';
|
||||
import PureRenderMixin from 'react-addons-pure-render-mixin';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import {
|
||||
fetchAccount,
|
||||
followAccount,
|
||||
unfollowAccount,
|
||||
fetchAccountTimeline,
|
||||
expandAccountTimeline
|
||||
} from '../../actions/accounts';
|
||||
import { replyCompose } from '../../actions/compose';
|
||||
import { favourite, reblog } from '../../actions/interactions';
|
||||
import Header from './components/header';
|
||||
import { selectStatus } from '../../reducers/timelines';
|
||||
import StatusList from '../../components/status_list';
|
||||
import Immutable from 'immutable';
|
||||
|
||||
function selectAccount(state, id) {
|
||||
return state.getIn(['timelines', 'accounts', id], null);
|
||||
|
@ -65,6 +71,10 @@ const Account = React.createClass({
|
|||
this.props.dispatch(favourite(status));
|
||||
},
|
||||
|
||||
handleScrollToBottom () {
|
||||
this.props.dispatch(expandAccountTimeline(this.props.account.get('id')));
|
||||
},
|
||||
|
||||
render () {
|
||||
const { account, statuses } = this.props;
|
||||
|
||||
|
@ -75,7 +85,7 @@ const Account = React.createClass({
|
|||
return (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', 'flex': '0 0 auto', height: '100%' }}>
|
||||
<Header account={account} onFollow={this.handleFollow} onUnfollow={this.handleUnfollow} />
|
||||
<StatusList statuses={statuses} onReply={this.handleReply} onReblog={this.handleReblog} onFavourite={this.handleFavourite} />
|
||||
<StatusList statuses={statuses} onScrollToBottom={this.handleScrollToBottom} onReply={this.handleReply} onReblog={this.handleReblog} onFavourite={this.handleFavourite} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -10,7 +10,8 @@ import {
|
|||
ACCOUNT_FETCH_FAIL,
|
||||
ACCOUNT_FOLLOW_FAIL,
|
||||
ACCOUNT_UNFOLLOW_FAIL,
|
||||
ACCOUNT_TIMELINE_FETCH_FAIL
|
||||
ACCOUNT_TIMELINE_FETCH_FAIL,
|
||||
ACCOUNT_TIMELINE_EXPAND_FAIL
|
||||
} from '../actions/accounts';
|
||||
import { STATUS_FETCH_FAIL } from '../actions/statuses';
|
||||
import Immutable from 'immutable';
|
||||
|
@ -48,6 +49,7 @@ export default function notifications(state = initialState, action) {
|
|||
case ACCOUNT_FOLLOW_FAIL:
|
||||
case ACCOUNT_UNFOLLOW_FAIL:
|
||||
case ACCOUNT_TIMELINE_FETCH_FAIL:
|
||||
case ACCOUNT_TIMELINE_EXPAND_FAIL:
|
||||
case STATUS_FETCH_FAIL:
|
||||
return notificationFromError(state, action.error);
|
||||
case NOTIFICATION_DISMISS:
|
||||
|
|
|
@ -13,7 +13,8 @@ import {
|
|||
ACCOUNT_FETCH_SUCCESS,
|
||||
ACCOUNT_FOLLOW_SUCCESS,
|
||||
ACCOUNT_UNFOLLOW_SUCCESS,
|
||||
ACCOUNT_TIMELINE_FETCH_SUCCESS
|
||||
ACCOUNT_TIMELINE_FETCH_SUCCESS,
|
||||
ACCOUNT_TIMELINE_EXPAND_SUCCESS
|
||||
} from '../actions/accounts';
|
||||
import { STATUS_FETCH_SUCCESS } from '../actions/statuses';
|
||||
import { FOLLOW_SUBMIT_SUCCESS } from '../actions/follow';
|
||||
|
@ -110,6 +111,17 @@ function normalizeAccountTimeline(state, accountId, statuses) {
|
|||
return state;
|
||||
};
|
||||
|
||||
function appendNormalizedAccountTimeline(state, accountId, statuses) {
|
||||
let moreIds = Immutable.List();
|
||||
|
||||
statuses.forEach((status, i) => {
|
||||
state = normalizeStatus(state, status);
|
||||
moreIds = moreIds.set(i, status.get('id'));
|
||||
});
|
||||
|
||||
return state.updateIn(['accounts_timelines', accountId], Immutable.List(), list => list.push(...moreIds));
|
||||
};
|
||||
|
||||
function updateTimeline(state, timeline, status) {
|
||||
state = normalizeStatus(state, status);
|
||||
state = state.update(timeline, list => list.unshift(status.get('id')));
|
||||
|
@ -176,6 +188,8 @@ export default function timelines(state = initialState, action) {
|
|||
return normalizeContext(state, Immutable.fromJS(action.status), Immutable.fromJS(action.context.ancestors), Immutable.fromJS(action.context.descendants));
|
||||
case ACCOUNT_TIMELINE_FETCH_SUCCESS:
|
||||
return normalizeAccountTimeline(state, action.id, Immutable.fromJS(action.statuses));
|
||||
case ACCOUNT_TIMELINE_EXPAND_SUCCESS:
|
||||
return appendNormalizedAccountTimeline(state, action.id, Immutable.fromJS(action.statuses));
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue