refactor: use Link HTTP header for pagination
This commit is contained in:
		
							parent
							
								
									99def58c8b
								
							
						
					
					
						commit
						3b8ca902f1
					
				
					 2 changed files with 74 additions and 32 deletions
				
			
		|  | @ -2,6 +2,31 @@ const errors = { | ||||||
|     AUTHENTICATION_FAILED: "AUTHENTICATION_FAILED", |     AUTHENTICATION_FAILED: "AUTHENTICATION_FAILED", | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * Parses a HTTP Link header | ||||||
|  |  * @param {string} header - the HTTP Link header string | ||||||
|  |  */ | ||||||
|  | function _parseLinkHeader(header) { | ||||||
|  |     // remove whitespace and split
 | ||||||
|  |     let links = header.replace(/\ /g, "").split(","); | ||||||
|  | 
 | ||||||
|  |     return links.map(l => { | ||||||
|  |         let parts = l.split(";"); | ||||||
|  | 
 | ||||||
|  |         // assuming 0th is URL, removing <>
 | ||||||
|  |         let url = new URL(parts[0].slice(1, -1)); | ||||||
|  | 
 | ||||||
|  |         // get rel inbetween double-quotes
 | ||||||
|  |         let rel = parts[1].match(/"(.*?)"/g)[0].slice(1, -1); | ||||||
|  | 
 | ||||||
|  |         return { | ||||||
|  |             url, rel | ||||||
|  |         } | ||||||
|  |     }) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | _parseLinkHeader(`<https://wetdry.world/api/v1/timelines/home?max_id=114857293229157171>; rel="next", <https://wetdry.world/api/v1/timelines/home?min_id=114857736990577458>; rel="prev"`) | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * GET /api/v1/instance |  * GET /api/v1/instance | ||||||
|  * @param {string} host - The domain of the target server. |  * @param {string} host - The domain of the target server. | ||||||
|  | @ -258,9 +283,43 @@ export async function getTimeline(host, token, timeline, max_id, local_only, rem | ||||||
|     const data = await fetch(url, { |     const data = await fetch(url, { | ||||||
|         method: 'GET', |         method: 'GET', | ||||||
|         headers: { "Authorization": token ? `Bearer ${token}` : null } |         headers: { "Authorization": token ? `Bearer ${token}` : null } | ||||||
|     }).then(res => res.json()); |     }) | ||||||
| 
 | 
 | ||||||
|     return data; |     let links = _parseLinkHeader(data.headers.get("Link")); | ||||||
|  | 
 | ||||||
|  |     return { | ||||||
|  |         data: await data.json(), | ||||||
|  |         prev: links.find(f=>f.rel=="prev"), | ||||||
|  |         next: links.find(f=>f.rel=="next") | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * GET /api/v1/favourites | ||||||
|  |  * @param {string} host - The domain of the target server. | ||||||
|  |  * @param {string} token - The application token. | ||||||
|  |  * @param {string} max_id - If provided, only shows posts after this ID. | ||||||
|  |  */ | ||||||
|  | export async function getFavourites(host, token, max_id) { | ||||||
|  |     let url = `https://${host}/api/v1/favourites`; | ||||||
|  | 
 | ||||||
|  |     let params = new URLSearchParams(); | ||||||
|  |     if (max_id) params.append("max_id", max_id); | ||||||
|  |     const params_string = params.toString(); | ||||||
|  |     if (params_string) url += '?' + params_string; | ||||||
|  |      | ||||||
|  |     const data = await fetch(url, { | ||||||
|  |         method: 'GET', | ||||||
|  |         headers: { "Authorization": token ? `Bearer ${token}` : null } | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     let links = _parseLinkHeader(data.headers.get("Link")); | ||||||
|  | 
 | ||||||
|  |     return { | ||||||
|  |         data: await data.json(), | ||||||
|  |         prev: links.find(f=>f.rel=="prev"), | ||||||
|  |         next: links.find(f=>f.rel=="next") | ||||||
|  |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  | @ -559,26 +618,4 @@ export async function getUserPinnedPosts(host, token, user_id) { | ||||||
|     }).then(res => res.json()); |     }).then(res => res.json()); | ||||||
| 
 | 
 | ||||||
|     return data; |     return data; | ||||||
| } | } | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * GET /api/v1/favourites |  | ||||||
|  * @param {string} host - The domain of the target server. |  | ||||||
|  * @param {string} token - The application token. |  | ||||||
|  * @param {string} max_id - If provided, only shows posts after this ID. |  | ||||||
|  */ |  | ||||||
| export async function getFavourites(host, token, timeline, max_id) { |  | ||||||
|     let url = `https://${host}/api/v1/favourites`; |  | ||||||
| 
 |  | ||||||
|     let params = new URLSearchParams(); |  | ||||||
|     if (max_id) params.append("max_id", max_id); |  | ||||||
|     const params_string = params.toString(); |  | ||||||
|     if (params_string) url += '?' + params_string; |  | ||||||
|      |  | ||||||
|     const data = await fetch(url, { |  | ||||||
|         method: 'GET', |  | ||||||
|         headers: { "Authorization": token ? `Bearer ${token}` : null } |  | ||||||
|     }).then(res => res.json()); |  | ||||||
| 
 |  | ||||||
|     return data; |  | ||||||
| } |  | ||||||
|  | @ -10,14 +10,14 @@ export const timeline = writable([]); | ||||||
| const lang = Lang(); | const lang = Lang(); | ||||||
| 
 | 
 | ||||||
| let loading = false; | let loading = false; | ||||||
|  | let last_post = false; | ||||||
| 
 | 
 | ||||||
| export async function getTimeline(timelineType = "home", clean, localOnly = false, remoteOnly = false) { | export async function getTimeline(timelineType = "home", clean, localOnly = false, remoteOnly = false) { | ||||||
|     if (loading) return; // no spamming!!
 |     if (loading) return; // no spamming!!
 | ||||||
|     loading = true; |     loading = true; | ||||||
| 
 | 
 | ||||||
|     let last_post = false; |     // if (!clean && get(timeline).length > 0)
 | ||||||
|     if (!clean && get(timeline).length > 0) |     //     last_post = get(timeline)[get(timeline).length - 1].id;
 | ||||||
|         last_post = get(timeline)[get(timeline).length - 1].id; |  | ||||||
| 
 | 
 | ||||||
|     let timeline_data; |     let timeline_data; | ||||||
|     switch(timelineType) { |     switch(timelineType) { | ||||||
|  | @ -25,7 +25,7 @@ export async function getTimeline(timelineType = "home", clean, localOnly = fals | ||||||
|             timeline_data = await api.getFavourites( |             timeline_data = await api.getFavourites( | ||||||
|                 get(server).host, |                 get(server).host, | ||||||
|                 get(app).token, |                 get(app).token, | ||||||
|                 last_post, |                 last_post | ||||||
|             ) |             ) | ||||||
|             break; |             break; | ||||||
|              |              | ||||||
|  | @ -47,10 +47,15 @@ export async function getTimeline(timelineType = "home", clean, localOnly = fals | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (clean) timeline.set([]); |     if (clean) { | ||||||
|  |         timeline.set([]); | ||||||
|  |         last_post = false; | ||||||
|  |     } else { | ||||||
|  |         last_post = timeline_data.next.url.searchParams.get("max_id") | ||||||
|  |     }  | ||||||
| 
 | 
 | ||||||
|     for (let i in timeline_data) { |     for (let i in timeline_data.data) { | ||||||
|         const post_data = timeline_data[i]; |         const post_data = timeline_data.data[i]; | ||||||
|         const post = await parsePost(post_data, 1); |         const post = await parsePost(post_data, 1); | ||||||
|         if (!post) { |         if (!post) { | ||||||
|             if (post === null || post === undefined) { |             if (post === null || post === undefined) { | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue