diff --git a/package.json b/package.json index 40495ce9..c1a0fb29 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "electron-store": "^8.1.0", "emoji-mart": "^5.5.2", "megalodon": "9.2.2", + "parse-link-header": "^2.0.0", "react-blurhash": "^0.3.0", "react-hotkeys-hook": "^4.4.1", "react-icons": "^5.0.0", diff --git a/renderer/components/timelines/Timeline.tsx b/renderer/components/timelines/Timeline.tsx index 5f15170e..da964e74 100644 --- a/renderer/components/timelines/Timeline.tsx +++ b/renderer/components/timelines/Timeline.tsx @@ -9,6 +9,7 @@ import { useRouter } from 'next/router' import Compose from '../compose/Compose' import { useHotkeys } from 'react-hotkeys-hook' import { Input, Spinner } from '@material-tailwind/react' +import parse from 'parse-link-header' const TIMELINE_STATUSES_COUNT = 30 const TIMELINE_MAX_STATUSES = 2147483647 @@ -27,6 +28,7 @@ export default function Timeline(props: Props) { const [composeHeight, setComposeHeight] = useState(120) const [list, setList] = useState(null) const [filters, setFilters] = useState>([]) + const [nextMaxId, setNextMaxId] = useState(null) const router = useRouter() const { formatMessage } = useIntl() @@ -147,11 +149,18 @@ export default function Timeline(props: Props) { } case 'favourites': { const res = await client.getFavourites(options) - // TODO: handle next_id in link header to get more posts + const link = parse(res.headers.link) + if (link !== null && link.next) { + setNextMaxId(link.next.max_id) + } return res.data } case 'bookmarks': { const res = await client.getBookmarks(options) + const link = parse(res.headers.link) + if (link !== null && link.next) { + setNextMaxId(link.next.max_id) + } return res.data } default: { @@ -190,11 +199,23 @@ export default function Timeline(props: Props) { const loadMore = useCallback(async () => { console.debug('appending') - const maxId = statuses[statuses.length - 1].id + let maxId = null + switch (props.timeline) { + case 'favourites': + case 'bookmarks': + if (!nextMaxId) { + return + } + maxId = nextMaxId + break + default: + maxId = statuses[statuses.length - 1].id + break + } const append = await loadTimeline(props.timeline, props.client, maxId) setStatuses(last => [...last, ...append]) - }, [props.client, statuses, setStatuses]) + }, [props.client, statuses, setStatuses, nextMaxId]) const prependUnreads = useCallback(() => { console.debug('prepending') diff --git a/yarn.lock b/yarn.lock index ddbfae0d..b3c71878 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2706,6 +2706,7 @@ __metadata: megalodon: 9.2.2 next: ^12.3.4 nextron: ^8.12.0 + parse-link-header: ^2.0.0 postcss: ^8.4.31 react: ^18.2.0 react-blurhash: ^0.3.0 @@ -7156,6 +7157,15 @@ __metadata: languageName: node linkType: hard +"parse-link-header@npm:^2.0.0": + version: 2.0.0 + resolution: "parse-link-header@npm:2.0.0" + dependencies: + xtend: ~4.0.1 + checksum: 0e96c6af9910e8f92084b49b8dc6a10dd58db470847d1499f562576180c1ac5e49d18007697f0d538e5f3efdc8ce1d8777641f3ae225302b74af0dd0578b628e + languageName: node + linkType: hard + "parse-srcset@npm:^1.0.2": version: 1.0.2 resolution: "parse-srcset@npm:1.0.2" @@ -9216,6 +9226,13 @@ __metadata: languageName: node linkType: hard +"xtend@npm:~4.0.1": + version: 4.0.2 + resolution: "xtend@npm:4.0.2" + checksum: ac5dfa738b21f6e7f0dd6e65e1b3155036d68104e67e5d5d1bde74892e327d7e5636a076f625599dc394330a731861e87343ff184b0047fef1360a7ec0a5a36a + languageName: node + linkType: hard + "y18n@npm:^5.0.5": version: 5.0.8 resolution: "y18n@npm:5.0.8"