Merge branch 'pagination-trends' into 'main'

Pagination trends

Closes ditto#236

See merge request soapbox-pub/soapbox!3163
environments/review-main-yi2y9f/deployments/4924
Alex Gleason 2024-10-19 16:38:29 +00:00
commit 85fd43a095
4 zmienionych plików z 69 dodań i 11 usunięć

Wyświetl plik

@ -1,6 +1,7 @@
import { APIEntity } from 'soapbox/types/entities';
import { getFeatures } from 'soapbox/utils/features'; import { getFeatures } from 'soapbox/utils/features';
import api from '../api'; import api, { getLinks } from '../api';
import { importFetchedStatuses } from './importer'; import { importFetchedStatuses } from './importer';
@ -9,6 +10,8 @@ import type { AppDispatch, RootState } from 'soapbox/store';
const TRENDING_STATUSES_FETCH_REQUEST = 'TRENDING_STATUSES_FETCH_REQUEST'; const TRENDING_STATUSES_FETCH_REQUEST = 'TRENDING_STATUSES_FETCH_REQUEST';
const TRENDING_STATUSES_FETCH_SUCCESS = 'TRENDING_STATUSES_FETCH_SUCCESS'; const TRENDING_STATUSES_FETCH_SUCCESS = 'TRENDING_STATUSES_FETCH_SUCCESS';
const TRENDING_STATUSES_FETCH_FAIL = 'TRENDING_STATUSES_FETCH_FAIL'; const TRENDING_STATUSES_FETCH_FAIL = 'TRENDING_STATUSES_FETCH_FAIL';
const TRENDING_STATUSES_EXPAND_FAIL = 'TRENDING_STATUSES_EXPAND_FAIL';
const TRENDING_STATUSES_EXPAND_SUCCESS = 'TRENDING_STATUSES_EXPAND_SUCCESS';
const fetchTrendingStatuses = () => const fetchTrendingStatuses = () =>
(dispatch: AppDispatch, getState: () => RootState) => { (dispatch: AppDispatch, getState: () => RootState) => {
@ -20,18 +23,62 @@ const fetchTrendingStatuses = () =>
if (!features.trendingStatuses) return; if (!features.trendingStatuses) return;
dispatch({ type: TRENDING_STATUSES_FETCH_REQUEST }); dispatch({ type: TRENDING_STATUSES_FETCH_REQUEST });
return api(getState).get('/api/v1/trends/statuses').then(({ data: statuses }) => { return api(getState).get('/api/v1/trends/statuses').then((response) => {
const next = getLinks(response).refs.find(link => link.rel === 'next');
const statuses = response.data;
dispatch(importFetchedStatuses(statuses)); dispatch(importFetchedStatuses(statuses));
dispatch({ type: TRENDING_STATUSES_FETCH_SUCCESS, statuses }); dispatch(fetchTrendingStatusesSuccess(statuses, next ? next.uri : null));
return statuses; return statuses;
}).catch(error => { }).catch(error => {
dispatch({ type: TRENDING_STATUSES_FETCH_FAIL, error }); dispatch(fetchTrendingStatusesFail(error));
}); });
}; };
const fetchTrendingStatusesSuccess = (statuses: APIEntity[], next: string | null) => ({
type: TRENDING_STATUSES_FETCH_SUCCESS,
statuses,
next,
});
const fetchTrendingStatusesFail = (error: unknown) => ({
type: TRENDING_STATUSES_FETCH_FAIL,
error,
});
const expandTrendingStatuses = (path: string) =>
(dispatch: AppDispatch, getState: () => RootState) => {
api(getState).get(path).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next');
const statuses = response.data;
dispatch(importFetchedStatuses(statuses));
dispatch(expandTrendingStatusesSuccess(statuses, next ? next.uri : null));
}).catch(error => {
dispatch(expandTrendingStatusesFail(error));
});
};
const expandTrendingStatusesSuccess = (statuses: APIEntity[], next: string | null) => ({
type: TRENDING_STATUSES_EXPAND_SUCCESS,
statuses,
next,
});
const expandTrendingStatusesFail = (error: unknown) => ({
type: TRENDING_STATUSES_EXPAND_FAIL,
error,
});
export { export {
TRENDING_STATUSES_FETCH_REQUEST, TRENDING_STATUSES_FETCH_REQUEST,
TRENDING_STATUSES_FETCH_SUCCESS, TRENDING_STATUSES_FETCH_SUCCESS,
TRENDING_STATUSES_FETCH_FAIL, TRENDING_STATUSES_FETCH_FAIL,
TRENDING_STATUSES_EXPAND_SUCCESS,
TRENDING_STATUSES_EXPAND_FAIL,
fetchTrendingStatuses, fetchTrendingStatuses,
expandTrendingStatuses,
}; };

Wyświetl plik

@ -3,7 +3,7 @@ import React, { useEffect, useRef } from 'react';
import { FormattedMessage, defineMessages, useIntl } from 'react-intl'; import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
import { expandSearch, setFilter, setSearchAccount } from 'soapbox/actions/search'; import { expandSearch, setFilter, setSearchAccount } from 'soapbox/actions/search';
import { fetchTrendingStatuses } from 'soapbox/actions/trending-statuses'; import { expandTrendingStatuses, fetchTrendingStatuses } from 'soapbox/actions/trending-statuses';
import { useAccount } from 'soapbox/api/hooks'; import { useAccount } from 'soapbox/api/hooks';
import Hashtag from 'soapbox/components/hashtag'; import Hashtag from 'soapbox/components/hashtag';
import IconButton from 'soapbox/components/icon-button'; import IconButton from 'soapbox/components/icon-button';
@ -36,13 +36,20 @@ const SearchResults = () => {
const results = useAppSelector((state) => state.search.results); const results = useAppSelector((state) => state.search.results);
const suggestions = useAppSelector((state) => state.suggestions.items); const suggestions = useAppSelector((state) => state.suggestions.items);
const trendingStatuses = useAppSelector((state) => state.trending_statuses.items); const trendingStatuses = useAppSelector((state) => state.trending_statuses.items);
const nextTrendingStatuses = useAppSelector((state) => state.trending_statuses.next);
const trends = useAppSelector((state) => state.trends.items); const trends = useAppSelector((state) => state.trends.items);
const submitted = useAppSelector((state) => state.search.submitted); const submitted = useAppSelector((state) => state.search.submitted);
const selectedFilter = useAppSelector((state) => state.search.filter); const selectedFilter = useAppSelector((state) => state.search.filter);
const filterByAccount = useAppSelector((state) => state.search.accountId || undefined); const filterByAccount = useAppSelector((state) => state.search.accountId || undefined);
const { account } = useAccount(filterByAccount); const { account } = useAccount(filterByAccount);
const handleLoadMore = () => dispatch(expandSearch(selectedFilter)); const handleLoadMore = () => {
if (results.accounts.size || results.statuses.size || results.hashtags.size) {
dispatch(expandSearch(selectedFilter));
} else if (nextTrendingStatuses) {
dispatch(expandTrendingStatuses(nextTrendingStatuses));
}
};
const handleUnsetAccount = () => dispatch(setSearchAccount(null)); const handleUnsetAccount = () => dispatch(setSearchAccount(null));
@ -224,7 +231,7 @@ const SearchResults = () => {
scrollKey={`${selectedFilter}:${value}`} scrollKey={`${selectedFilter}:${value}`}
isLoading={submitted && !loaded} isLoading={submitted && !loaded}
showLoading={submitted && !loaded && searchResults?.isEmpty()} showLoading={submitted && !loaded && searchResults?.isEmpty()}
hasMore={hasMore} hasMore={(!!nextTrendingStatuses) || hasMore}
onLoadMore={handleLoadMore} onLoadMore={handleLoadMore}
placeholderComponent={placeholderComponent} placeholderComponent={placeholderComponent}
placeholderCount={20} placeholderCount={20}

Wyświetl plik

@ -47,7 +47,7 @@ const ZapsModal: React.FC<IZapsModal> = ({ onClose, statusId }) => {
const handleLoadMore = () => { const handleLoadMore = () => {
if (next) { if (next) {
dispatch(expandZaps(statusId, next!)); dispatch(expandZaps(statusId, next));
} }
}; };

Wyświetl plik

@ -3,6 +3,7 @@ import { OrderedSet as ImmutableOrderedSet, Record as ImmutableRecord } from 'im
import { import {
TRENDING_STATUSES_FETCH_REQUEST, TRENDING_STATUSES_FETCH_REQUEST,
TRENDING_STATUSES_FETCH_SUCCESS, TRENDING_STATUSES_FETCH_SUCCESS,
TRENDING_STATUSES_EXPAND_SUCCESS,
} from 'soapbox/actions/trending-statuses'; } from 'soapbox/actions/trending-statuses';
import type { AnyAction } from 'redux'; import type { AnyAction } from 'redux';
@ -11,6 +12,7 @@ import type { APIEntity } from 'soapbox/types/entities';
const ReducerRecord = ImmutableRecord({ const ReducerRecord = ImmutableRecord({
items: ImmutableOrderedSet<string>(), items: ImmutableOrderedSet<string>(),
isLoading: false, isLoading: false,
next: null as string | null,
}); });
type State = ReturnType<typeof ReducerRecord>; type State = ReturnType<typeof ReducerRecord>;
@ -18,10 +20,11 @@ type APIEntities = Array<APIEntity>;
const toIds = (items: APIEntities) => ImmutableOrderedSet(items.map(item => item.id)); const toIds = (items: APIEntities) => ImmutableOrderedSet(items.map(item => item.id));
const importStatuses = (state: State, statuses: APIEntities) => { const importStatuses = (state: State, statuses: APIEntities, next: string|null) => {
return state.withMutations(state => { return state.withMutations(state => {
state.set('items', toIds(statuses)); state.update('items', list => list.concat(toIds(statuses)));
state.set('isLoading', false); state.set('isLoading', false);
state.set('next', next ? next : null);
}); });
}; };
@ -29,8 +32,9 @@ export default function trending_statuses(state: State = ReducerRecord(), action
switch (action.type) { switch (action.type) {
case TRENDING_STATUSES_FETCH_REQUEST: case TRENDING_STATUSES_FETCH_REQUEST:
return state.set('isLoading', true); return state.set('isLoading', true);
case TRENDING_STATUSES_EXPAND_SUCCESS:
case TRENDING_STATUSES_FETCH_SUCCESS: case TRENDING_STATUSES_FETCH_SUCCESS:
return importStatuses(state, action.statuses); return importStatuses(state, action.statuses, action.next);
default: default:
return state; return state;
} }