Use Link header for home timeline pagination
This commit is contained in:
parent
9c3af7a0c9
commit
2bc6ff3fa3
5 changed files with 74 additions and 15 deletions
|
@ -4,7 +4,7 @@ import { getSettings } from 'soapbox/actions/settings';
|
|||
import { normalizeStatus } from 'soapbox/normalizers';
|
||||
import { shouldFilter } from 'soapbox/utils/timelines';
|
||||
|
||||
import api, { getLinks } from '../api';
|
||||
import api, { getNextLink, getPrevLink } from '../api';
|
||||
|
||||
import { importFetchedStatus, importFetchedStatuses } from './importer';
|
||||
|
||||
|
@ -139,7 +139,7 @@ const parseTags = (tags: Record<string, any[]> = {}, mode: 'any' | 'all' | 'none
|
|||
};
|
||||
|
||||
const replaceHomeTimeline = (
|
||||
accountId: string | null,
|
||||
accountId: string | undefined,
|
||||
{ maxId }: Record<string, any> = {},
|
||||
done?: () => void,
|
||||
) => (dispatch: AppDispatch, _getState: () => RootState) => {
|
||||
|
@ -162,7 +162,12 @@ const expandTimeline = (timelineId: string, path: string, params: Record<string,
|
|||
return dispatch(noOpAsync());
|
||||
}
|
||||
|
||||
if (!params.max_id && !params.pinned && (timeline.items || ImmutableOrderedSet()).size > 0) {
|
||||
if (
|
||||
!params.max_id &&
|
||||
!params.pinned &&
|
||||
(timeline.items || ImmutableOrderedSet()).size > 0 &&
|
||||
!path.includes('max_id=')
|
||||
) {
|
||||
params.since_id = timeline.getIn(['items', 0]);
|
||||
}
|
||||
|
||||
|
@ -171,9 +176,16 @@ const expandTimeline = (timelineId: string, path: string, params: Record<string,
|
|||
dispatch(expandTimelineRequest(timelineId, isLoadingMore));
|
||||
|
||||
return api(getState).get(path, { params }).then(response => {
|
||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||
dispatch(importFetchedStatuses(response.data));
|
||||
dispatch(expandTimelineSuccess(timelineId, response.data, next ? next.uri : null, response.status === 206, isLoadingRecent, isLoadingMore));
|
||||
dispatch(expandTimelineSuccess(
|
||||
timelineId,
|
||||
response.data,
|
||||
getNextLink(response),
|
||||
getPrevLink(response),
|
||||
response.status === 206,
|
||||
isLoadingRecent,
|
||||
isLoadingMore,
|
||||
));
|
||||
done();
|
||||
}).catch(error => {
|
||||
dispatch(expandTimelineFail(timelineId, error, isLoadingMore));
|
||||
|
@ -181,9 +193,26 @@ const expandTimeline = (timelineId: string, path: string, params: Record<string,
|
|||
});
|
||||
};
|
||||
|
||||
const expandHomeTimeline = ({ accountId, maxId }: Record<string, any> = {}, done = noOp) => {
|
||||
const endpoint = accountId ? `/api/v1/accounts/${accountId}/statuses` : '/api/v1/timelines/home';
|
||||
const params: any = { max_id: maxId };
|
||||
interface ExpandHomeTimelineOpts {
|
||||
accountId?: string
|
||||
maxId?: string
|
||||
url?: string
|
||||
}
|
||||
|
||||
interface HomeTimelineParams {
|
||||
max_id?: string
|
||||
exclude_replies?: boolean
|
||||
with_muted?: boolean
|
||||
}
|
||||
|
||||
const expandHomeTimeline = ({ url, accountId, maxId }: ExpandHomeTimelineOpts = {}, done = noOp) => {
|
||||
const endpoint = url || (accountId ? `/api/v1/accounts/${accountId}/statuses` : '/api/v1/timelines/home');
|
||||
const params: HomeTimelineParams = {};
|
||||
|
||||
if (!url && maxId) {
|
||||
params.max_id = maxId;
|
||||
}
|
||||
|
||||
if (accountId) {
|
||||
params.exclude_replies = true;
|
||||
params.with_muted = true;
|
||||
|
@ -237,11 +266,20 @@ const expandTimelineRequest = (timeline: string, isLoadingMore: boolean) => ({
|
|||
skipLoading: !isLoadingMore,
|
||||
});
|
||||
|
||||
const expandTimelineSuccess = (timeline: string, statuses: APIEntity[], next: string | null, partial: boolean, isLoadingRecent: boolean, isLoadingMore: boolean) => ({
|
||||
const expandTimelineSuccess = (
|
||||
timeline: string,
|
||||
statuses: APIEntity[],
|
||||
next: string | undefined,
|
||||
prev: string | undefined,
|
||||
partial: boolean,
|
||||
isLoadingRecent: boolean,
|
||||
isLoadingMore: boolean,
|
||||
) => ({
|
||||
type: TIMELINE_EXPAND_SUCCESS,
|
||||
timeline,
|
||||
statuses,
|
||||
next,
|
||||
prev,
|
||||
partial,
|
||||
isLoadingRecent,
|
||||
skipLoading: !isLoadingMore,
|
||||
|
|
|
@ -30,7 +30,7 @@ const CarouselItem = React.forwardRef((
|
|||
setLoading(true);
|
||||
|
||||
if (isSelected) {
|
||||
dispatch(replaceHomeTimeline(null, { maxId: null }, () => setLoading(false)));
|
||||
dispatch(replaceHomeTimeline(undefined, { maxId: null }, () => setLoading(false)));
|
||||
|
||||
if (onPinned) {
|
||||
onPinned(null);
|
||||
|
|
|
@ -27,9 +27,10 @@ const HomeTimeline: React.FC = () => {
|
|||
const isPartial = useAppSelector(state => state.timelines.get('home')?.isPartial === true);
|
||||
const currentAccountId = useAppSelector(state => state.timelines.get('home')?.feedAccountId as string | undefined);
|
||||
const currentAccountRelationship = useAppSelector(state => currentAccountId ? state.relationships.get(currentAccountId) : null);
|
||||
const next = useAppSelector(state => state.timelines.get('home')?.next);
|
||||
|
||||
const handleLoadMore = (maxId: string) => {
|
||||
dispatch(expandHomeTimeline({ maxId, accountId: currentAccountId }));
|
||||
dispatch(expandHomeTimeline({ url: next, maxId, accountId: currentAccountId }));
|
||||
};
|
||||
|
||||
// Mastodon generates the feed in Redis, and can return a partial timeline
|
||||
|
@ -52,7 +53,7 @@ const HomeTimeline: React.FC = () => {
|
|||
};
|
||||
|
||||
const handleRefresh = () => {
|
||||
return dispatch(expandHomeTimeline({ maxId: null, accountId: currentAccountId }));
|
||||
return dispatch(expandHomeTimeline({ accountId: currentAccountId }));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -35,7 +35,7 @@ const TestTimeline: React.FC = () => {
|
|||
|
||||
React.useEffect(() => {
|
||||
dispatch(importFetchedStatuses(MOCK_STATUSES));
|
||||
dispatch(expandTimelineSuccess(timelineId, MOCK_STATUSES, null, false, false, false));
|
||||
dispatch(expandTimelineSuccess(timelineId, MOCK_STATUSES, undefined, undefined, false, false, false));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
|
|
|
@ -46,6 +46,8 @@ const TimelineRecord = ImmutableRecord({
|
|||
top: true,
|
||||
isLoading: false,
|
||||
hasMore: true,
|
||||
next: undefined as string | undefined,
|
||||
prev: undefined as string | undefined,
|
||||
items: ImmutableOrderedSet<string>(),
|
||||
queuedItems: ImmutableOrderedSet<string>(), //max= MAX_QUEUED_ITEMS
|
||||
feedAccountId: null,
|
||||
|
@ -87,13 +89,23 @@ const setFailed = (state: State, timelineId: string, failed: boolean) => {
|
|||
return state.update(timelineId, TimelineRecord(), timeline => timeline.set('loadingFailed', failed));
|
||||
};
|
||||
|
||||
const expandNormalizedTimeline = (state: State, timelineId: string, statuses: ImmutableList<ImmutableMap<string, any>>, next: string | null, isPartial: boolean, isLoadingRecent: boolean) => {
|
||||
const expandNormalizedTimeline = (
|
||||
state: State,
|
||||
timelineId: string,
|
||||
statuses: ImmutableList<ImmutableMap<string, any>>,
|
||||
next: string | undefined,
|
||||
prev: string | undefined,
|
||||
isPartial: boolean,
|
||||
isLoadingRecent: boolean,
|
||||
) => {
|
||||
const newIds = getStatusIds(statuses);
|
||||
|
||||
return state.update(timelineId, TimelineRecord(), timeline => timeline.withMutations(timeline => {
|
||||
timeline.set('isLoading', false);
|
||||
timeline.set('loadingFailed', false);
|
||||
timeline.set('isPartial', isPartial);
|
||||
timeline.set('next', next);
|
||||
timeline.set('prev', prev);
|
||||
|
||||
if (!next && !isLoadingRecent) timeline.set('hasMore', false);
|
||||
|
||||
|
@ -322,7 +334,15 @@ export default function timelines(state: State = initialState, action: AnyAction
|
|||
case TIMELINE_EXPAND_FAIL:
|
||||
return handleExpandFail(state, action.timeline);
|
||||
case TIMELINE_EXPAND_SUCCESS:
|
||||
return expandNormalizedTimeline(state, action.timeline, fromJS(action.statuses) as ImmutableList<ImmutableMap<string, any>>, action.next, action.partial, action.isLoadingRecent);
|
||||
return expandNormalizedTimeline(
|
||||
state,
|
||||
action.timeline,
|
||||
fromJS(action.statuses) as ImmutableList<ImmutableMap<string, any>>,
|
||||
action.next,
|
||||
action.prev,
|
||||
action.partial,
|
||||
action.isLoadingRecent,
|
||||
);
|
||||
case TIMELINE_UPDATE:
|
||||
return updateTimeline(state, action.timeline, action.statusId);
|
||||
case TIMELINE_UPDATE_QUEUE:
|
||||
|
|
Loading…
Reference in a new issue