1
0
mirror of https://github.com/tooot-app/app synced 2025-02-07 05:45:23 +01:00

Rewrite timeline pagination

Using Link in returned headers instead. Also, `since_id` is not the correct way to refresh. See https://mastodonpy.readthedocs.io/en/stable/index.html?highlight=pagination#a-note-about-pagination
This commit is contained in:
Zhiyuan Zheng 2020-10-29 23:06:25 +01:00
parent 9c30edcc65
commit 2ab227a370
No known key found for this signature in database
GPG Key ID: 078A93AB607D85E0
4 changed files with 130 additions and 79 deletions

View File

@ -23,10 +23,10 @@ export async function client (url, query, { body, ...customConfig } = {}) {
let data
try {
const response = await fetch(`https://${url}${queryString}`, config)
const response = await fetch(`${url}${queryString}`, config)
data = await response.json()
if (response.ok) {
return data
return { headers: response.headers, body: data }
}
throw new Error(response.statusText)
} catch (err) {

View File

@ -43,26 +43,11 @@ export default function Timeline ({
{...(state.pointer && { initialScrollIndex: state.pointer })}
{...(!disableRefresh && {
onRefresh: () =>
dispatch(
fetch({
page,
query: [{ key: 'since_id', value: state.toots[0].id }]
})
),
dispatch(fetch({ page, paginationDirection: 'prev' })),
refreshing: state.status === 'loading',
onEndReached: () => {
if (!timelineReady) {
dispatch(
fetch({
page,
query: [
{
key: 'max_id',
value: state.toots[state.toots.length - 1].id
}
]
})
)
dispatch(fetch({ page, paginationDirection: 'next' }))
setTimelineReady(true)
}
},

View File

@ -5,9 +5,9 @@ import { client } from 'src/api/client'
export const fetch = createAsyncThunk(
'account/fetch',
async ({ id }, { getState }) => {
const instanceLocal = getState().instanceInfo.local + '/api/v1/'
return await client.get(`${instanceLocal}accounts/${id}`)
const instanceLocal = `https://${getState().instanceInfo.local}/api/v1/`
const res = await client.get(`${instanceLocal}accounts/${id}`)
return res.body
}
)

View File

@ -11,11 +11,35 @@ import { client } from 'src/api/client'
// Hashtag: hastag
// List: list
function getPagination (headers, direction) {
if (!headers) console.error('Missing pagination headers')
const paginationLinks = headers.get('Link')
if (paginationLinks) {
if (direction) {
return {
[direction]: paginationLinks.split(
new RegExp(`<([^>]+)>; rel="${direction}"`)
)[1]
}
} else {
return {
prev: paginationLinks.split(new RegExp(/<([^>]+)>; rel="prev"/))[1],
next: paginationLinks.split(new RegExp(/<([^>]+)>; rel="next"/))[1]
}
}
} else {
return
}
}
export const fetch = createAsyncThunk(
'timeline/fetch',
async ({ page, query = [], account, hashtag, list, toot }, { getState }) => {
const instanceLocal = getState().instanceInfo.local + '/api/v1/'
const instanceRemote = getState().instanceInfo.remote + '/api/v1/'
async (
{ page, paginationDirection, query = [], account, hashtag, list, toot },
{ getState }
) => {
const instanceLocal = `https://${getState().instanceInfo.local}/api/v1/`
const instanceRemote = `https://${getState().instanceInfo.remote}/api/v1/`
// If header if needed for remote server
const header = {
headers: {
@ -23,90 +47,123 @@ export const fetch = createAsyncThunk(
}
}
let res
// For same page, but only pagination
if (paginationDirection) {
res = await client.get(
getState().timelines[page].pagination[paginationDirection],
query,
header
)
return {
toots: res.body,
pagination: getPagination(res.headers, paginationDirection)
}
}
// For each page's first query
switch (page) {
case 'Following':
res = await client.get(`${instanceLocal}timelines/home`, query, header)
return {
toots: await client.get(
`${instanceLocal}timelines/home`,
query,
header
)
toots: res.body,
pagination: getPagination(res.headers)
}
case 'Local':
query.push({ key: 'local', value: 'true' })
res = await client.get(
`${instanceLocal}timelines/public`,
query,
header
)
return {
toots: await client.get(
`${instanceLocal}timelines/public`,
query,
header
)
toots: res.body,
pagination: getPagination(res.headers)
}
case 'LocalPublic':
res = await client.get(
`${instanceLocal}timelines/public`,
query,
header
)
return {
toots: await client.get(
`${instanceLocal}timelines/public`,
query,
header
)
toots: res.body,
pagination: getPagination(res.headers)
}
case 'RemotePublic':
res = await client.get(`${instanceRemote}timelines/public`, query)
return {
toots: await client.get(`${instanceRemote}timelines/public`, query)
toots: res.body,
pagination: getPagination(res.headers)
}
case 'Notifications':
res = await client.get(`${instanceLocal}notifications`, query, header)
return {
toots: await client.get(
`${instanceLocal}notifications`,
query,
header
)
toots: res.body,
pagination: getPagination(res.headers)
}
case 'Account_Default':
const toots = await client.get(
res = await client.get(
`${instanceLocal}accounts/${account}/statuses`,
[{ key: 'pinned', value: 'true' }],
header
)
toots.push(
...(await client.get(
`${instanceLocal}accounts/${account}/statuses`,
[{ key: 'exclude_replies', value: 'true' }],
header
))
const toots = res.body
res = await client.get(
`${instanceLocal}accounts/${account}/statuses`,
[{ key: 'exclude_replies', value: 'true' }],
header
)
toots.push(...res.body)
return { toots: toots }
case 'Account_All':
res = await client.get(
`${instanceLocal}accounts/${account}/statuses`,
query,
header
)
return {
toots: await client.get(
`${instanceLocal}accounts/${account}/statuses`,
query,
header
)
toots: res.body
}
case 'Account_Media':
res = await client.get(
`${instanceLocal}accounts/${account}/statuses`,
[{ key: 'only_media', value: 'true' }],
header
)
return {
toots: await client.get(
`${instanceLocal}accounts/${account}/statuses`,
[{ key: 'only_media', value: 'true' }],
header
)
toots: res.body
}
case 'Hashtag':
res = await client.get(
`${instanceLocal}timelines/tag/${hashtag}`,
query,
header
)
return {
toots: await client.get(
`${instanceLocal}timelines/tag/${hashtag}`,
query,
header
)
toots: res.body,
pagination: getPagination(res.headers)
}
case 'List':
res = await client.get(
`${instanceLocal}timelines/list/${list}`,
query,
header
)
return {
toots: await client.get(
`${instanceLocal}timelines/list/${list}`,
query,
header
)
toots: res.body,
pagination: getPagination(res.headers)
}
case 'Toot':
const current = await client.get(
`${instanceLocal}statuses/${toot}`,
@ -122,14 +179,16 @@ export const fetch = createAsyncThunk(
toots: [...context.ancestors, current, ...context.descendants],
pointer: context.ancestors.length
}
default:
console.error('Timeline type error')
console.error('First time fetching timeline error')
}
}
)
const timelineInitState = {
toots: [],
pagination: { prev: undefined, next: undefined },
pointer: undefined,
status: 'idle'
}
@ -160,15 +219,22 @@ export const timelineSlice = createSlice({
},
[fetch.fulfilled]: (state, action) => {
state[action.meta.arg.page].status = 'succeeded'
if (
action.meta.arg.query &&
action.meta.arg.query[0].key === 'since_id'
) {
if (action.meta.arg.paginationDirection === 'prev') {
state[action.meta.arg.page].toots.unshift(...action.payload.toots)
} else {
state[action.meta.arg.page].toots.push(...action.payload.toots)
}
state[action.meta.arg.page].pointer = action.payload.pointer
if (action.payload.pagination) {
state[action.meta.arg.page].pagination = {
...state[action.meta.arg.page].pagination,
...action.payload.pagination
}
}
if (action.payload.pointer) {
state[action.meta.arg.page].pointer = action.payload.pointer
}
},
[fetch.rejected]: (state, action) => {
state[action.meta.arg.page].status = 'failed'