diff --git a/app/javascript/flavours/glitch/actions/interactions.js b/app/javascript/flavours/glitch/actions/interactions.js index 6b8864a03..78f7741ab 100644 --- a/app/javascript/flavours/glitch/actions/interactions.js +++ b/app/javascript/flavours/glitch/actions/interactions.js @@ -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, }; } diff --git a/app/javascript/flavours/glitch/features/favourites/index.jsx b/app/javascript/flavours/glitch/features/favourites/index.jsx index 2b36945ee..49fd62b96 100644 --- a/app/javascript/flavours/glitch/features/favourites/index.jsx +++ b/app/javascript/flavours/glitch/features/favourites/index.jsx @@ -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 { /> diff --git a/app/javascript/flavours/glitch/reducers/user_lists.js b/app/javascript/flavours/glitch/reducers/user_lists.js index dd240e99d..a10fed433 100644 --- a/app/javascript/flavours/glitch/reducers/user_lists.js +++ b/app/javascript/flavours/glitch/reducers/user_lists.js @@ -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: