forked from fedi/mastodon
Infinite scroll for account timelines
This commit is contained in:
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';
|
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_SUCCESS = 'ACCOUNT_TIMELINE_FETCH_SUCCESS';
|
||||||
export const ACCOUNT_TIMELINE_FETCH_FAIL = 'ACCOUNT_TIMELINE_FETCH_FAIL';
|
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) {
|
export function setAccountSelf(account) {
|
||||||
return {
|
return {
|
||||||
type: ACCOUNT_SET_SELF,
|
type: ACCOUNT_SET_SELF,
|
||||||
|
@ -27,10 +32,12 @@ export function setAccountSelf(account) {
|
||||||
|
|
||||||
export function fetchAccount(id) {
|
export function fetchAccount(id) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
|
const boundApi = api(getState);
|
||||||
|
|
||||||
dispatch(fetchAccountRequest(id));
|
dispatch(fetchAccountRequest(id));
|
||||||
|
|
||||||
api(getState).get(`/api/accounts/${id}`).then(response => {
|
axios.all([boundApi.get(`/api/accounts/${id}`), boundApi.get(`/api/accounts/relationships?id=${id}`)]).then(values => {
|
||||||
dispatch(fetchAccountSuccess(response.data));
|
dispatch(fetchAccountSuccess(values[0].data, values[1].data[0]));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
dispatch(fetchAccountFail(id, 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) {
|
export function fetchAccountRequest(id) {
|
||||||
return {
|
return {
|
||||||
type: ACCOUNT_FETCH_REQUEST,
|
type: ACCOUNT_FETCH_REQUEST,
|
||||||
|
@ -56,10 +77,11 @@ export function fetchAccountRequest(id) {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export function fetchAccountSuccess(account) {
|
export function fetchAccountSuccess(account, relationship) {
|
||||||
return {
|
return {
|
||||||
type: ACCOUNT_FETCH_SUCCESS,
|
type: ACCOUNT_FETCH_SUCCESS,
|
||||||
account: account
|
account: account,
|
||||||
|
relationship: relationship
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -159,3 +181,26 @@ export function fetchAccountTimelineFail(id, error) {
|
||||||
error: 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 { connect } from 'react-redux';
|
||||||
import PureRenderMixin from 'react-addons-pure-render-mixin';
|
import PureRenderMixin from 'react-addons-pure-render-mixin';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import { fetchAccount, followAccount, unfollowAccount, fetchAccountTimeline } from '../../actions/accounts';
|
import {
|
||||||
import { replyCompose } from '../../actions/compose';
|
fetchAccount,
|
||||||
import { favourite, reblog } from '../../actions/interactions';
|
followAccount,
|
||||||
import Header from './components/header';
|
unfollowAccount,
|
||||||
import { selectStatus } from '../../reducers/timelines';
|
fetchAccountTimeline,
|
||||||
import StatusList from '../../components/status_list';
|
expandAccountTimeline
|
||||||
import Immutable from 'immutable';
|
} 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) {
|
function selectAccount(state, id) {
|
||||||
return state.getIn(['timelines', 'accounts', id], null);
|
return state.getIn(['timelines', 'accounts', id], null);
|
||||||
|
@ -65,6 +71,10 @@ const Account = React.createClass({
|
||||||
this.props.dispatch(favourite(status));
|
this.props.dispatch(favourite(status));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
handleScrollToBottom () {
|
||||||
|
this.props.dispatch(expandAccountTimeline(this.props.account.get('id')));
|
||||||
|
},
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { account, statuses } = this.props;
|
const { account, statuses } = this.props;
|
||||||
|
|
||||||
|
@ -75,7 +85,7 @@ const Account = React.createClass({
|
||||||
return (
|
return (
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', 'flex': '0 0 auto', height: '100%' }}>
|
<div style={{ display: 'flex', flexDirection: 'column', 'flex': '0 0 auto', height: '100%' }}>
|
||||||
<Header account={account} onFollow={this.handleFollow} onUnfollow={this.handleUnfollow} />
|
<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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,8 @@ import {
|
||||||
ACCOUNT_FETCH_FAIL,
|
ACCOUNT_FETCH_FAIL,
|
||||||
ACCOUNT_FOLLOW_FAIL,
|
ACCOUNT_FOLLOW_FAIL,
|
||||||
ACCOUNT_UNFOLLOW_FAIL,
|
ACCOUNT_UNFOLLOW_FAIL,
|
||||||
ACCOUNT_TIMELINE_FETCH_FAIL
|
ACCOUNT_TIMELINE_FETCH_FAIL,
|
||||||
|
ACCOUNT_TIMELINE_EXPAND_FAIL
|
||||||
} from '../actions/accounts';
|
} from '../actions/accounts';
|
||||||
import { STATUS_FETCH_FAIL } from '../actions/statuses';
|
import { STATUS_FETCH_FAIL } from '../actions/statuses';
|
||||||
import Immutable from 'immutable';
|
import Immutable from 'immutable';
|
||||||
|
@ -48,6 +49,7 @@ export default function notifications(state = initialState, action) {
|
||||||
case ACCOUNT_FOLLOW_FAIL:
|
case ACCOUNT_FOLLOW_FAIL:
|
||||||
case ACCOUNT_UNFOLLOW_FAIL:
|
case ACCOUNT_UNFOLLOW_FAIL:
|
||||||
case ACCOUNT_TIMELINE_FETCH_FAIL:
|
case ACCOUNT_TIMELINE_FETCH_FAIL:
|
||||||
|
case ACCOUNT_TIMELINE_EXPAND_FAIL:
|
||||||
case STATUS_FETCH_FAIL:
|
case STATUS_FETCH_FAIL:
|
||||||
return notificationFromError(state, action.error);
|
return notificationFromError(state, action.error);
|
||||||
case NOTIFICATION_DISMISS:
|
case NOTIFICATION_DISMISS:
|
||||||
|
|
|
@ -13,7 +13,8 @@ import {
|
||||||
ACCOUNT_FETCH_SUCCESS,
|
ACCOUNT_FETCH_SUCCESS,
|
||||||
ACCOUNT_FOLLOW_SUCCESS,
|
ACCOUNT_FOLLOW_SUCCESS,
|
||||||
ACCOUNT_UNFOLLOW_SUCCESS,
|
ACCOUNT_UNFOLLOW_SUCCESS,
|
||||||
ACCOUNT_TIMELINE_FETCH_SUCCESS
|
ACCOUNT_TIMELINE_FETCH_SUCCESS,
|
||||||
|
ACCOUNT_TIMELINE_EXPAND_SUCCESS
|
||||||
} from '../actions/accounts';
|
} from '../actions/accounts';
|
||||||
import { STATUS_FETCH_SUCCESS } from '../actions/statuses';
|
import { STATUS_FETCH_SUCCESS } from '../actions/statuses';
|
||||||
import { FOLLOW_SUBMIT_SUCCESS } from '../actions/follow';
|
import { FOLLOW_SUBMIT_SUCCESS } from '../actions/follow';
|
||||||
|
@ -110,6 +111,17 @@ function normalizeAccountTimeline(state, accountId, statuses) {
|
||||||
return state;
|
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) {
|
function updateTimeline(state, timeline, status) {
|
||||||
state = normalizeStatus(state, status);
|
state = normalizeStatus(state, status);
|
||||||
state = state.update(timeline, list => list.unshift(status.get('id')));
|
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));
|
return normalizeContext(state, Immutable.fromJS(action.status), Immutable.fromJS(action.context.ancestors), Immutable.fromJS(action.context.descendants));
|
||||||
case ACCOUNT_TIMELINE_FETCH_SUCCESS:
|
case ACCOUNT_TIMELINE_FETCH_SUCCESS:
|
||||||
return normalizeAccountTimeline(state, action.id, Immutable.fromJS(action.statuses));
|
return normalizeAccountTimeline(state, action.id, Immutable.fromJS(action.statuses));
|
||||||
|
case ACCOUNT_TIMELINE_EXPAND_SUCCESS:
|
||||||
|
return appendNormalizedAccountTimeline(state, action.id, Immutable.fromJS(action.statuses));
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue