M app/javascript/flavours/glitch/actions/interactions.js => app/javascript/flavours/glitch/actions/interactions.js +55 -3
@@ 1,5 1,6 @@
-import api from '../api';
+import api, { getLinks } from '../api';
+import { fetchRelationships } from './accounts';
import { importFetchedAccounts, importFetchedStatus } from './importer';
export const REBLOG_REQUEST = 'REBLOG_REQUEST';
@@ 26,6 27,10 @@ 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 const FAVOURITES_EXPAND_REQUEST = 'FAVOURITES_EXPAND_REQUEST';
+export const FAVOURITES_EXPAND_SUCCESS = 'FAVOURITES_EXPAND_SUCCESS';
+export const FAVOURITES_EXPAND_FAIL = 'FAVOURITES_EXPAND_FAIL';
+
export const PIN_REQUEST = 'PIN_REQUEST';
export const PIN_SUCCESS = 'PIN_SUCCESS';
export const PIN_FAIL = 'PIN_FAIL';
@@ 294,8 299,10 @@ export function fetchFavourites(id) {
dispatch(fetchFavouritesRequest(id));
api(getState).get(`/api/v1/statuses/${id}/favourited_by`).then(response => {
+ const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedAccounts(response.data));
- dispatch(fetchFavouritesSuccess(id, response.data));
+ dispatch(fetchFavouritesSuccess(id, response.data, next ? next.uri : null));
+ dispatch(fetchRelationships(response.data.map(item => item.id)));
}).catch(error => {
dispatch(fetchFavouritesFail(id, error));
});
@@ 309,17 316,62 @@ export function fetchFavouritesRequest(id) {
};
}
-export function fetchFavouritesSuccess(id, accounts) {
+export function fetchFavouritesSuccess(id, accounts, next) {
return {
type: FAVOURITES_FETCH_SUCCESS,
id,
accounts,
+ next,
};
}
export function fetchFavouritesFail(id, error) {
return {
type: FAVOURITES_FETCH_FAIL,
+ id,
+ error,
+ };
+}
+
+export function expandFavourites(id) {
+ return (dispatch, getState) => {
+ const url = getState().getIn(['user_lists', 'favourited_by', id, 'next']);
+ if (url === null) {
+ return;
+ }
+
+ dispatch(expandFavouritesRequest(id));
+
+ api(getState).get(url).then(response => {
+ const next = getLinks(response).refs.find(link => link.rel === 'next');
+
+ dispatch(importFetchedAccounts(response.data));
+ dispatch(expandFavouritesSuccess(id, response.data, next ? next.uri : null));
+ dispatch(fetchRelationships(response.data.map(item => item.id)));
+ }).catch(error => dispatch(expandFavouritesFail(id, error)));
+ };
+}
+
+export function expandFavouritesRequest(id) {
+ return {
+ type: FAVOURITES_EXPAND_REQUEST,
+ id,
+ };
+}
+
+export function expandFavouritesSuccess(id, accounts, next) {
+ return {
+ type: FAVOURITES_EXPAND_SUCCESS,
+ id,
+ accounts,
+ next,
+ };
+}
+
+export function expandFavouritesFail(id, error) {
+ return {
+ type: FAVOURITES_EXPAND_FAIL,
+ id,
error,
};
}
M app/javascript/flavours/glitch/features/favourites/index.jsx => app/javascript/flavours/glitch/features/favourites/index.jsx +16 -9
@@ 8,7 8,9 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux';
-import { fetchFavourites } from 'flavours/glitch/actions/interactions';
+import { debounce } from 'lodash';
+
+import { fetchFavourites, expandFavourites } from 'flavours/glitch/actions/interactions';
import ColumnHeader from 'flavours/glitch/components/column_header';
import { Icon } from 'flavours/glitch/components/icon';
import { LoadingIndicator } from 'flavours/glitch/components/loading_indicator';
@@ 23,7 25,9 @@ const messages = defineMessages({
});
const mapStateToProps = (state, props) => ({
- accountIds: state.getIn(['user_lists', 'favourited_by', props.params.statusId]),
+ accountIds: state.getIn(['user_lists', 'favourited_by', props.params.statusId, 'items']),
+ hasMore: !!state.getIn(['user_lists', 'favourited_by', props.params.statusId, 'next']),
+ isLoading: state.getIn(['user_lists', 'favourited_by', props.params.statusId, 'isLoading'], true),
});
class Favourites extends ImmutablePureComponent {
@@ 32,6 36,8 @@ class Favourites extends ImmutablePureComponent {
params: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired,
accountIds: ImmutablePropTypes.list,
+ hasMore: PropTypes.bool,
+ isLoading: PropTypes.bool,
multiColumn: PropTypes.bool,
intl: PropTypes.object.isRequired,
};
@@ 42,12 48,6 @@ class Favourites extends ImmutablePureComponent {
}
}
- UNSAFE_componentWillReceiveProps (nextProps) {
- if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) {
- this.props.dispatch(fetchFavourites(nextProps.params.statusId));
- }
- }
-
handleHeaderClick = () => {
this.column.scrollTop();
};
@@ 60,8 60,12 @@ class Favourites extends ImmutablePureComponent {
this.props.dispatch(fetchFavourites(this.props.params.statusId));
};
+ handleLoadMore = debounce(() => {
+ this.props.dispatch(expandFavourites(this.props.params.statusId));
+ }, 300, { leading: true });
+
render () {
- const { intl, accountIds, multiColumn } = this.props;
+ const { intl, accountIds, hasMore, isLoading, multiColumn } = this.props;
if (!accountIds) {
return (
@@ 87,6 91,9 @@ class Favourites extends ImmutablePureComponent {
/>
<ScrollableList
scrollKey='favourites'
+ onLoadMore={this.handleLoadMore}
+ hasMore={hasMore}
+ isLoading={isLoading}
emptyMessage={emptyMessage}
bindToDocument={!multiColumn}
>
M app/javascript/flavours/glitch/reducers/user_lists.js => app/javascript/flavours/glitch/reducers/user_lists.js +14 -1
@@ 45,7 45,12 @@ import {
} from 'flavours/glitch/actions/featured_tags';
import {
REBLOGS_FETCH_SUCCESS,
+ FAVOURITES_FETCH_REQUEST,
FAVOURITES_FETCH_SUCCESS,
+ FAVOURITES_FETCH_FAIL,
+ FAVOURITES_EXPAND_REQUEST,
+ FAVOURITES_EXPAND_SUCCESS,
+ FAVOURITES_EXPAND_FAIL,
} from 'flavours/glitch/actions/interactions';
import {
MUTES_FETCH_REQUEST,
@@ 135,7 140,15 @@ export default function userLists(state = initialState, action) {
case REBLOGS_FETCH_SUCCESS:
return state.setIn(['reblogged_by', action.id], ImmutableList(action.accounts.map(item => item.id)));
case FAVOURITES_FETCH_SUCCESS:
- return state.setIn(['favourited_by', action.id], ImmutableList(action.accounts.map(item => item.id)));
+ return normalizeList(state, ['favourited_by', action.id], action.accounts, action.next);
+ case FAVOURITES_EXPAND_SUCCESS:
+ return appendToList(state, ['favourited_by', action.id], action.accounts, action.next);
+ case FAVOURITES_FETCH_REQUEST:
+ case FAVOURITES_EXPAND_REQUEST:
+ return state.setIn(['favourited_by', action.id, 'isLoading'], true);
+ case FAVOURITES_FETCH_FAIL:
+ case FAVOURITES_EXPAND_FAIL:
+ return state.setIn(['favourited_by', action.id, 'isLoading'], false);
case NOTIFICATIONS_UPDATE:
return action.notification.type === 'follow_request' ? normalizeFollowRequest(state, action.notification) : state;
case FOLLOW_REQUESTS_FETCH_SUCCESS: