From 9b6173c9e56508a9abef92cd0b1e1dc92cf27926 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 13 Nov 2024 12:18:55 -0600 Subject: [PATCH] MastodonClient: return a MastodonResponse object with `.pagination()` convenience method --- src/api/MastodonClient.ts | 19 ++++++++++--------- src/api/MastodonResponse.ts | 16 ++++++++++++++++ src/queries/chats.ts | 5 ++--- src/queries/search.ts | 3 +-- src/queries/suggestions.ts | 5 ++--- src/utils/pagination.ts | 16 ---------------- 6 files changed, 31 insertions(+), 33 deletions(-) create mode 100644 src/api/MastodonResponse.ts delete mode 100644 src/utils/pagination.ts diff --git a/src/api/MastodonClient.ts b/src/api/MastodonClient.ts index a0d084349..a41036b27 100644 --- a/src/api/MastodonClient.ts +++ b/src/api/MastodonClient.ts @@ -1,4 +1,5 @@ import { HTTPError } from './HTTPError.ts'; +import { MastodonResponse } from './MastodonResponse.ts'; interface Opts { searchParams?: URLSearchParams | Record; @@ -19,35 +20,35 @@ export class MastodonClient { this.accessToken = accessToken; } - async get(path: string, opts: Opts = {}): Promise { + async get(path: string, opts: Opts = {}): Promise { return this.request('GET', path, undefined, opts); } - async post(path: string, data?: unknown, opts: Opts = {}): Promise { + async post(path: string, data?: unknown, opts: Opts = {}): Promise { return this.request('POST', path, data, opts); } - async put(path: string, data?: unknown, opts: Opts = {}): Promise { + async put(path: string, data?: unknown, opts: Opts = {}): Promise { return this.request('PUT', path, data, opts); } - async delete(path: string, opts: Opts = {}): Promise { + async delete(path: string, opts: Opts = {}): Promise { return this.request('DELETE', path, undefined, opts); } - async patch(path: string, data: unknown, opts: Opts = {}): Promise { + async patch(path: string, data: unknown, opts: Opts = {}): Promise { return this.request('PATCH', path, data, opts); } - async head(path: string, opts: Opts = {}): Promise { + async head(path: string, opts: Opts = {}): Promise { return this.request('HEAD', path, undefined, opts); } - async options(path: string, opts: Opts = {}): Promise { + async options(path: string, opts: Opts = {}): Promise { return this.request('OPTIONS', path, undefined, opts); } - async request(method: string, path: string, data: unknown, opts: Opts = {}): Promise { + async request(method: string, path: string, data: unknown, opts: Opts = {}): Promise { const url = new URL(path, this.baseUrl); if (opts.searchParams) { @@ -89,7 +90,7 @@ export class MastodonClient { throw new HTTPError(response, request); } - return response; + return new MastodonResponse(response.body, response); } } \ No newline at end of file diff --git a/src/api/MastodonResponse.ts b/src/api/MastodonResponse.ts new file mode 100644 index 000000000..fb54008b0 --- /dev/null +++ b/src/api/MastodonResponse.ts @@ -0,0 +1,16 @@ +import LinkHeader from 'http-link-header'; + +export class MastodonResponse extends Response { + + /** Parses the `Link` header and returns URLs for the `prev` and `next` pages of this response, if any. */ + pagination(): { prev?: string; next?: string } { + const header = this.headers.get('link'); + const links = header ? new LinkHeader(header) : undefined; + + return { + next: links?.refs.find((link) => link.rel === 'next')?.uri, + prev: links?.refs.find((link) => link.rel === 'prev')?.uri, + }; + } + +} diff --git a/src/queries/chats.ts b/src/queries/chats.ts index a80c6f757..f353da79e 100644 --- a/src/queries/chats.ts +++ b/src/queries/chats.ts @@ -13,7 +13,6 @@ import { normalizeChatMessage } from 'soapbox/normalizers/index.ts'; import toast from 'soapbox/toast.tsx'; import { ChatMessage } from 'soapbox/types/entities.ts'; import { reOrderChatListItems, updateChatMessage } from 'soapbox/utils/chats.ts'; -import { getPagination } from 'soapbox/utils/pagination.ts'; import { flattenPages, PaginatedResult, updatePageItem } from 'soapbox/utils/queries.ts'; import { queryClient } from './client.ts'; @@ -91,7 +90,7 @@ const useChatMessages = (chat: IChat) => { const response = await api.get(uri); const data = await response.json(); - const { next } = getPagination(response); + const { next } = response.pagination(); const hasMore = !!next; const result = data.map(normalizeChatMessage); @@ -144,7 +143,7 @@ const useChats = (search?: string) => { }); const data: IChat[] = await response.json(); - const { next } = getPagination(response); + const { next } = response.pagination(); const hasMore = !!next; setUnreadChatsCount(Number(response.headers.get('x-unread-messages-count')) || sumBy(data, (chat) => chat.unread)); diff --git a/src/queries/search.ts b/src/queries/search.ts index c23d84baa..38df39f0c 100644 --- a/src/queries/search.ts +++ b/src/queries/search.ts @@ -2,7 +2,6 @@ import { keepPreviousData, useInfiniteQuery } from '@tanstack/react-query'; import { useApi } from 'soapbox/hooks/useApi.ts'; import { Account } from 'soapbox/types/entities.ts'; -import { getPagination } from 'soapbox/utils/pagination.ts'; import { flattenPages, PaginatedResult } from 'soapbox/utils/queries.ts'; export default function useAccountSearch(q: string) { @@ -21,7 +20,7 @@ export default function useAccountSearch(q: string) { }); const data = await response.json(); - const { next } = getPagination(response); + const { next } = response.pagination(); const hasMore = !!next; return { diff --git a/src/queries/suggestions.ts b/src/queries/suggestions.ts index 71076a86a..9268fe120 100644 --- a/src/queries/suggestions.ts +++ b/src/queries/suggestions.ts @@ -4,7 +4,6 @@ import { fetchRelationships } from 'soapbox/actions/accounts.ts'; import { importFetchedAccounts } from 'soapbox/actions/importer/index.ts'; import { useApi } from 'soapbox/hooks/useApi.ts'; import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts'; -import { getPagination } from 'soapbox/utils/pagination.ts'; import { PaginatedResult, removePageItem } from '../utils/queries.ts'; @@ -34,7 +33,7 @@ const useSuggestions = () => { const getV2Suggestions = async (pageParam: PageParam): Promise> => { const endpoint = pageParam?.link || '/api/v2/suggestions'; const response = await api.get(endpoint); - const { next } = getPagination(response); + const { next } = response.pagination(); const hasMore = !!next; const data: Suggestion[] = await response.json(); @@ -93,7 +92,7 @@ function useOnboardingSuggestions() { const getV2Suggestions = async (pageParam: any): Promise<{ data: Suggestion[]; link: string | undefined; hasMore: boolean }> => { const link = pageParam?.link || '/api/v2/suggestions'; const response = await api.get(link); - const { next } = getPagination(response); + const { next } = response.pagination(); const hasMore = !!next; const data: Suggestion[] = await response.json(); diff --git a/src/utils/pagination.ts b/src/utils/pagination.ts deleted file mode 100644 index bf23f958f..000000000 --- a/src/utils/pagination.ts +++ /dev/null @@ -1,16 +0,0 @@ -import LinkHeader from 'http-link-header'; - -interface Pagination { - next?: string; - prev?: string; -} - -export function getPagination(response: Response): Pagination { - const header = response.headers.get('link'); - const links = header ? new LinkHeader(header) : undefined; - - return { - next: links?.refs.find((link) => link.rel === 'next')?.uri, - prev: links?.refs.find((link) => link.rel === 'prev')?.uri, - }; -}