Optionally use Link header for pagination in various timelines

This commit is contained in:
oakes 2023-06-15 14:57:58 -04:00
parent a985348bf1
commit e1cacb6ee4
9 changed files with 36 additions and 28 deletions

View File

@ -221,29 +221,29 @@ const expandHomeTimeline = ({ url, accountId, maxId }: ExpandHomeTimelineOpts =
return expandTimeline('home', endpoint, params, done); return expandTimeline('home', endpoint, params, done);
}; };
const expandPublicTimeline = ({ maxId, onlyMedia }: Record<string, any> = {}, done = noOp) => const expandPublicTimeline = ({ url, maxId, onlyMedia }: Record<string, any> = {}, done = noOp) =>
expandTimeline(`public${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { max_id: maxId, only_media: !!onlyMedia }, done); expandTimeline(`public${onlyMedia ? ':media' : ''}`, url || '/api/v1/timelines/public', url ? {} : { max_id: maxId, only_media: !!onlyMedia }, done);
const expandRemoteTimeline = (instance: string, { maxId, onlyMedia }: Record<string, any> = {}, done = noOp) => const expandRemoteTimeline = (instance: string, { url, maxId, onlyMedia }: Record<string, any> = {}, done = noOp) =>
expandTimeline(`remote${onlyMedia ? ':media' : ''}:${instance}`, '/api/v1/timelines/public', { local: false, instance: instance, max_id: maxId, only_media: !!onlyMedia }, done); expandTimeline(`remote${onlyMedia ? ':media' : ''}:${instance}`, url || '/api/v1/timelines/public', url ? {} : { local: false, instance: instance, max_id: maxId, only_media: !!onlyMedia }, done);
const expandCommunityTimeline = ({ maxId, onlyMedia }: Record<string, any> = {}, done = noOp) => const expandCommunityTimeline = ({ url, maxId, onlyMedia }: Record<string, any> = {}, done = noOp) =>
expandTimeline(`community${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { local: true, max_id: maxId, only_media: !!onlyMedia }, done); expandTimeline(`community${onlyMedia ? ':media' : ''}`, url || '/api/v1/timelines/public', url ? {} : { local: true, max_id: maxId, only_media: !!onlyMedia }, done);
const expandDirectTimeline = ({ maxId }: Record<string, any> = {}, done = noOp) => const expandDirectTimeline = ({ url, maxId }: Record<string, any> = {}, done = noOp) =>
expandTimeline('direct', '/api/v1/timelines/direct', { max_id: maxId }, done); expandTimeline('direct', url || '/api/v1/timelines/direct', url ? {} : { max_id: maxId }, done);
const expandAccountTimeline = (accountId: string, { maxId, withReplies }: Record<string, any> = {}) => const expandAccountTimeline = (accountId: string, { url, maxId, withReplies }: Record<string, any> = {}) =>
expandTimeline(`account:${accountId}${withReplies ? ':with_replies' : ''}`, `/api/v1/accounts/${accountId}/statuses`, { exclude_replies: !withReplies, max_id: maxId, with_muted: true }); expandTimeline(`account:${accountId}${withReplies ? ':with_replies' : ''}`, url || `/api/v1/accounts/${accountId}/statuses`, url ? {} : { exclude_replies: !withReplies, max_id: maxId, with_muted: true });
const expandAccountFeaturedTimeline = (accountId: string) => const expandAccountFeaturedTimeline = (accountId: string) =>
expandTimeline(`account:${accountId}:pinned`, `/api/v1/accounts/${accountId}/statuses`, { pinned: true, with_muted: true }); expandTimeline(`account:${accountId}:pinned`, `/api/v1/accounts/${accountId}/statuses`, { pinned: true, with_muted: true });
const expandAccountMediaTimeline = (accountId: string | number, { maxId }: Record<string, any> = {}) => const expandAccountMediaTimeline = (accountId: string | number, { url, maxId }: Record<string, any> = {}) =>
expandTimeline(`account:${accountId}:media`, `/api/v1/accounts/${accountId}/statuses`, { max_id: maxId, only_media: true, limit: 40, with_muted: true }); expandTimeline(`account:${accountId}:media`, url || `/api/v1/accounts/${accountId}/statuses`, url ? {} : { max_id: maxId, only_media: true, limit: 40, with_muted: true });
const expandListTimeline = (id: string, { maxId }: Record<string, any> = {}, done = noOp) => const expandListTimeline = (id: string, { url, maxId }: Record<string, any> = {}, done = noOp) =>
expandTimeline(`list:${id}`, `/api/v1/timelines/list/${id}`, { max_id: maxId }, done); expandTimeline(`list:${id}`, url || `/api/v1/timelines/list/${id}`, url ? {} : { max_id: maxId }, done);
const expandGroupTimeline = (id: string, { maxId }: Record<string, any> = {}, done = noOp) => const expandGroupTimeline = (id: string, { maxId }: Record<string, any> = {}, done = noOp) =>
expandTimeline(`group:${id}`, `/api/v1/timelines/group/${id}`, { max_id: maxId }, done); expandTimeline(`group:${id}`, `/api/v1/timelines/group/${id}`, { max_id: maxId }, done);
@ -254,8 +254,8 @@ const expandGroupTimelineFromTag = (id: string, tagName: string, { maxId }: Reco
const expandGroupMediaTimeline = (id: string | number, { maxId }: Record<string, any> = {}) => const expandGroupMediaTimeline = (id: string | number, { maxId }: Record<string, any> = {}) =>
expandTimeline(`group:${id}:media`, `/api/v1/timelines/group/${id}`, { max_id: maxId, only_media: true, limit: 40, with_muted: true }); expandTimeline(`group:${id}:media`, `/api/v1/timelines/group/${id}`, { max_id: maxId, only_media: true, limit: 40, with_muted: true });
const expandHashtagTimeline = (hashtag: string, { maxId, tags }: Record<string, any> = {}, done = noOp) => { const expandHashtagTimeline = (hashtag: string, { url, maxId, tags }: Record<string, any> = {}, done = noOp) => {
return expandTimeline(`hashtag:${hashtag}`, `/api/v1/timelines/tag/${hashtag}`, { return expandTimeline(`hashtag:${hashtag}`, url || `/api/v1/timelines/tag/${hashtag}`, url ? {} : {
max_id: maxId, max_id: maxId,
any: parseTags(tags, 'any'), any: parseTags(tags, 'any'),
all: parseTags(tags, 'all'), all: parseTags(tags, 'all'),

View File

@ -64,6 +64,7 @@ const AccountGallery = () => {
const attachments: ImmutableList<Attachment> = useAppSelector((state) => getAccountGallery(state, accountId as string)); const attachments: ImmutableList<Attachment> = useAppSelector((state) => getAccountGallery(state, accountId as string));
const isLoading = useAppSelector((state) => state.timelines.get(`account:${accountId}:media`)?.isLoading); const isLoading = useAppSelector((state) => state.timelines.get(`account:${accountId}:media`)?.isLoading);
const hasMore = useAppSelector((state) => state.timelines.get(`account:${accountId}:media`)?.hasMore); const hasMore = useAppSelector((state) => state.timelines.get(`account:${accountId}:media`)?.hasMore);
const next = useAppSelector(state => state.timelines.get(`account:${accountId}:media`)?.next);
const node = useRef<HTMLDivElement>(null); const node = useRef<HTMLDivElement>(null);
@ -75,7 +76,7 @@ const AccountGallery = () => {
const handleLoadMore = (maxId: string | null) => { const handleLoadMore = (maxId: string | null) => {
if (accountId && accountId !== -1) { if (accountId && accountId !== -1) {
dispatch(expandAccountMediaTimeline(accountId, { maxId })); dispatch(expandAccountMediaTimeline(accountId, { url: next, maxId }));
} }
}; };

View File

@ -40,6 +40,7 @@ const AccountTimeline: React.FC<IAccountTimeline> = ({ params, withReplies = fal
const patronEnabled = soapboxConfig.getIn(['extensions', 'patron', 'enabled']) === true; const patronEnabled = soapboxConfig.getIn(['extensions', 'patron', 'enabled']) === true;
const isLoading = useAppSelector(state => state.getIn(['timelines', `account:${path}`, 'isLoading']) === true); const isLoading = useAppSelector(state => state.getIn(['timelines', `account:${path}`, 'isLoading']) === true);
const hasMore = useAppSelector(state => state.getIn(['timelines', `account:${path}`, 'hasMore']) === true); const hasMore = useAppSelector(state => state.getIn(['timelines', `account:${path}`, 'hasMore']) === true);
const next = useAppSelector(state => state.timelines.get(`account:${path}`)?.next);
const accountUsername = account?.username || params.username; const accountUsername = account?.username || params.username;
@ -69,7 +70,7 @@ const AccountTimeline: React.FC<IAccountTimeline> = ({ params, withReplies = fal
const handleLoadMore = (maxId: string) => { const handleLoadMore = (maxId: string) => {
if (account) { if (account) {
dispatch(expandAccountTimeline(account.id, { maxId, withReplies })); dispatch(expandAccountTimeline(account.id, { url: next, maxId, withReplies }));
} }
}; };

View File

@ -5,7 +5,7 @@ import { connectCommunityStream } from 'soapbox/actions/streaming';
import { expandCommunityTimeline } from 'soapbox/actions/timelines'; import { expandCommunityTimeline } from 'soapbox/actions/timelines';
import PullToRefresh from 'soapbox/components/pull-to-refresh'; import PullToRefresh from 'soapbox/components/pull-to-refresh';
import { Column } from 'soapbox/components/ui'; import { Column } from 'soapbox/components/ui';
import { useAppDispatch, useSettings } from 'soapbox/hooks'; import { useAppSelector, useAppDispatch, useSettings } from 'soapbox/hooks';
import Timeline from '../ui/components/timeline'; import Timeline from '../ui/components/timeline';
@ -19,11 +19,12 @@ const CommunityTimeline = () => {
const settings = useSettings(); const settings = useSettings();
const onlyMedia = settings.getIn(['community', 'other', 'onlyMedia']); const onlyMedia = settings.getIn(['community', 'other', 'onlyMedia']);
const next = useAppSelector(state => state.timelines.get('community')?.next);
const timelineId = 'community'; const timelineId = 'community';
const handleLoadMore = (maxId: string) => { const handleLoadMore = (maxId: string) => {
dispatch(expandCommunityTimeline({ maxId, onlyMedia })); dispatch(expandCommunityTimeline({ url: next, maxId, onlyMedia }));
}; };
const handleRefresh = () => { const handleRefresh = () => {

View File

@ -6,7 +6,7 @@ import { connectDirectStream } from 'soapbox/actions/streaming';
import { expandDirectTimeline } from 'soapbox/actions/timelines'; import { expandDirectTimeline } from 'soapbox/actions/timelines';
import AccountSearch from 'soapbox/components/account-search'; import AccountSearch from 'soapbox/components/account-search';
import { Column } from 'soapbox/components/ui'; import { Column } from 'soapbox/components/ui';
import { useAppDispatch } from 'soapbox/hooks'; import { useAppSelector, useAppDispatch } from 'soapbox/hooks';
import Timeline from '../ui/components/timeline'; import Timeline from '../ui/components/timeline';
@ -18,6 +18,7 @@ const messages = defineMessages({
const DirectTimeline = () => { const DirectTimeline = () => {
const intl = useIntl(); const intl = useIntl();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const next = useAppSelector(state => state.timelines.get('direct')?.next);
useEffect(() => { useEffect(() => {
dispatch(expandDirectTimeline()); dispatch(expandDirectTimeline());
@ -33,7 +34,7 @@ const DirectTimeline = () => {
}; };
const handleLoadMore = (maxId: string) => { const handleLoadMore = (maxId: string) => {
dispatch(expandDirectTimeline({ maxId })); dispatch(expandDirectTimeline({ url: next, maxId }));
}; };
return ( return (

View File

@ -39,6 +39,7 @@ export const HashtagTimeline: React.FC<IHashtagTimeline> = ({ params }) => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const disconnects = useRef<(() => void)[]>([]); const disconnects = useRef<(() => void)[]>([]);
const tag = useAppSelector((state) => state.tags.get(id)); const tag = useAppSelector((state) => state.tags.get(id));
const next = useAppSelector(state => state.timelines.get(`hashtag:${id}`)?.next);
// Mastodon supports displaying results from multiple hashtags. // Mastodon supports displaying results from multiple hashtags.
// https://github.com/mastodon/mastodon/issues/6359 // https://github.com/mastodon/mastodon/issues/6359
@ -89,7 +90,7 @@ export const HashtagTimeline: React.FC<IHashtagTimeline> = ({ params }) => {
}; };
const handleLoadMore = (maxId: string) => { const handleLoadMore = (maxId: string) => {
dispatch(expandHashtagTimeline(id, { maxId, tags })); dispatch(expandHashtagTimeline(id, { url: next, maxId, tags }));
}; };
const handleFollow = () => { const handleFollow = () => {

View File

@ -17,6 +17,7 @@ const ListTimeline: React.FC = () => {
const { id } = useParams<{ id: string }>(); const { id } = useParams<{ id: string }>();
const list = useAppSelector((state) => state.lists.get(id)); const list = useAppSelector((state) => state.lists.get(id));
const next = useAppSelector(state => state.timelines.get(`list:${id}`)?.next);
useEffect(() => { useEffect(() => {
dispatch(fetchList(id)); dispatch(fetchList(id));
@ -30,7 +31,7 @@ const ListTimeline: React.FC = () => {
}, [id]); }, [id]);
const handleLoadMore = (maxId: string) => { const handleLoadMore = (maxId: string) => {
dispatch(expandListTimeline(id, { maxId })); dispatch(expandListTimeline(id, { url: next, maxId }));
}; };
const handleEditClick = () => { const handleEditClick = () => {

View File

@ -7,7 +7,7 @@ import { connectPublicStream } from 'soapbox/actions/streaming';
import { expandPublicTimeline } from 'soapbox/actions/timelines'; import { expandPublicTimeline } from 'soapbox/actions/timelines';
import PullToRefresh from 'soapbox/components/pull-to-refresh'; import PullToRefresh from 'soapbox/components/pull-to-refresh';
import { Accordion, Column } from 'soapbox/components/ui'; import { Accordion, Column } from 'soapbox/components/ui';
import { useAppDispatch, useInstance, useSettings } from 'soapbox/hooks'; import { useAppSelector, useAppDispatch, useInstance, useSettings } from 'soapbox/hooks';
import PinnedHostsPicker from '../remote-timeline/components/pinned-hosts-picker'; import PinnedHostsPicker from '../remote-timeline/components/pinned-hosts-picker';
import Timeline from '../ui/components/timeline'; import Timeline from '../ui/components/timeline';
@ -24,6 +24,7 @@ const CommunityTimeline = () => {
const instance = useInstance(); const instance = useInstance();
const settings = useSettings(); const settings = useSettings();
const onlyMedia = settings.getIn(['public', 'other', 'onlyMedia']); const onlyMedia = settings.getIn(['public', 'other', 'onlyMedia']);
const next = useAppSelector(state => state.timelines.get('public')?.next);
const timelineId = 'public'; const timelineId = 'public';
@ -39,7 +40,7 @@ const CommunityTimeline = () => {
}; };
const handleLoadMore = (maxId: string) => { const handleLoadMore = (maxId: string) => {
dispatch(expandPublicTimeline({ maxId, onlyMedia })); dispatch(expandPublicTimeline({ url: next, maxId, onlyMedia }));
}; };
const handleRefresh = () => { const handleRefresh = () => {

View File

@ -6,7 +6,7 @@ import { connectRemoteStream } from 'soapbox/actions/streaming';
import { expandRemoteTimeline } from 'soapbox/actions/timelines'; import { expandRemoteTimeline } from 'soapbox/actions/timelines';
import IconButton from 'soapbox/components/icon-button'; import IconButton from 'soapbox/components/icon-button';
import { Column, HStack, Text } from 'soapbox/components/ui'; import { Column, HStack, Text } from 'soapbox/components/ui';
import { useAppDispatch, useSettings } from 'soapbox/hooks'; import { useAppSelector, useAppDispatch, useSettings } from 'soapbox/hooks';
import Timeline from '../ui/components/timeline'; import Timeline from '../ui/components/timeline';
@ -30,6 +30,7 @@ const RemoteTimeline: React.FC<IRemoteTimeline> = ({ params }) => {
const timelineId = 'remote'; const timelineId = 'remote';
const onlyMedia = !!settings.getIn(['remote', 'other', 'onlyMedia']); const onlyMedia = !!settings.getIn(['remote', 'other', 'onlyMedia']);
const next = useAppSelector(state => state.timelines.get('remote')?.next);
const pinned: boolean = (settings.getIn(['remote_timeline', 'pinnedHosts']) as any).includes(instance); const pinned: boolean = (settings.getIn(['remote_timeline', 'pinnedHosts']) as any).includes(instance);
@ -44,7 +45,7 @@ const RemoteTimeline: React.FC<IRemoteTimeline> = ({ params }) => {
}; };
const handleLoadMore = (maxId: string) => { const handleLoadMore = (maxId: string) => {
dispatch(expandRemoteTimeline(instance, { maxId, onlyMedia })); dispatch(expandRemoteTimeline(instance, { url: next, maxId, onlyMedia }));
}; };
useEffect(() => { useEffect(() => {