From 6daa4672d2aeaa56c96c41ce63c954dc643c35d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Thu, 23 May 2024 18:16:10 +0200 Subject: [PATCH] Fix fetch (missing request headers etc.) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- src/actions/accounts.ts | 4 +-- src/actions/auth.ts | 4 +-- src/actions/export-data.ts | 4 +-- src/actions/timelines.ts | 2 ++ src/api/index.ts | 27 ++++++++++++------- src/entity-store/hooks/types.ts | 5 ++-- src/entity-store/hooks/useCreateEntity.ts | 3 ++- src/entity-store/hooks/useEntity.ts | 5 ++-- src/entity-store/hooks/useEntityLookup.ts | 11 ++++---- src/features/account/components/header.tsx | 4 ++- .../auth-login/components/login-page.tsx | 4 ++- .../components/chat-search/chat-search.tsx | 4 ++- src/features/chats/components/chat.tsx | 3 ++- .../group/group-membership-requests.tsx | 5 ++-- .../steps/avatar-selection-step.tsx | 4 ++- src/features/onboarding/steps/bio-step.tsx | 4 ++- .../steps/cover-photo-selection-step.tsx | 4 ++- .../onboarding/steps/display-name-step.tsx | 4 ++- .../manage-group-modal/create-group-modal.tsx | 4 ++- src/features/ui/components/navbar.tsx | 4 ++- src/reducers/auth.ts | 3 ++- src/reducers/me.ts | 3 ++- src/toast.tsx | 4 ++- 23 files changed, 79 insertions(+), 40 deletions(-) diff --git a/src/actions/accounts.ts b/src/actions/accounts.ts index 75b6c83db..04c88e8cb 100644 --- a/src/actions/accounts.ts +++ b/src/actions/accounts.ts @@ -4,7 +4,7 @@ import { selectAccount } from 'soapbox/selectors'; import { isLoggedIn } from 'soapbox/utils/auth'; import { getFeatures, parseVersion, PLEROMA } from 'soapbox/utils/features'; -import api, { getLinks } from '../api'; +import api, { getLinks, type PlfeResponse } from '../api'; import { importFetchedAccount, @@ -117,7 +117,7 @@ const BIRTHDAY_REMINDERS_FETCH_REQUEST = 'BIRTHDAY_REMINDERS_FETCH_REQUEST'; const BIRTHDAY_REMINDERS_FETCH_SUCCESS = 'BIRTHDAY_REMINDERS_FETCH_SUCCESS'; const BIRTHDAY_REMINDERS_FETCH_FAIL = 'BIRTHDAY_REMINDERS_FETCH_FAIL'; -const maybeRedirectLogin = (error: { response: Response }, history?: History) => { +const maybeRedirectLogin = (error: { response: PlfeResponse }, history?: History) => { // The client is unauthorized - redirect to login. if (history && error?.response?.status === 401) { history.push('/login'); diff --git a/src/actions/auth.ts b/src/actions/auth.ts index dfbcc8eea..4b5a7bd47 100644 --- a/src/actions/auth.ts +++ b/src/actions/auth.ts @@ -26,7 +26,7 @@ import { normalizeUsername } from 'soapbox/utils/input'; import { getScopes } from 'soapbox/utils/scopes'; import { isStandalone } from 'soapbox/utils/state'; -import api, { getFetch } from '../api'; +import api, { type PlfeResponse, getFetch } from '../api'; import { importFetchedAccount } from './importer'; @@ -187,7 +187,7 @@ const loadCredentials = (token: string, accountUrl: string) => const logIn = (username: string, password: string) => (dispatch: AppDispatch) => dispatch(getAuthApp()).then(() => dispatch(createUserToken(normalizeUsername(username), password)), - ).catch((error: { response: Response }) => { + ).catch((error: { response: PlfeResponse }) => { if ((error.response?.json as any)?.error === 'mfa_required') { // If MFA is required, throw the error and handle it in the component. throw error; diff --git a/src/actions/export-data.ts b/src/actions/export-data.ts index 8e6e7ac14..1db097bd4 100644 --- a/src/actions/export-data.ts +++ b/src/actions/export-data.ts @@ -1,6 +1,6 @@ import { defineMessages } from 'react-intl'; -import api, { getLinks } from 'soapbox/api'; +import api, { type PlfeResponse, getLinks } from 'soapbox/api'; import { normalizeAccount } from 'soapbox/normalizers'; import toast from 'soapbox/toast'; @@ -48,7 +48,7 @@ const fileExport = (content: string, fileName: string) => { document.body.removeChild(fileToDownload); }; -const listAccounts = (getState: () => RootState) => async(apiResponse: Response & { json: any }) => { +const listAccounts = (getState: () => RootState) => async(apiResponse: PlfeResponse) => { const followings = apiResponse.json; let accounts = []; let next = getLinks(apiResponse).refs.find(link => link.rel === 'next'); diff --git a/src/actions/timelines.ts b/src/actions/timelines.ts index de7bff566..91eef46ce 100644 --- a/src/actions/timelines.ts +++ b/src/actions/timelines.ts @@ -192,6 +192,8 @@ const expandTimeline = (timelineId: string, path: string, params: Record status.accounts))); + console.log(response); + dispatch(expandTimelineSuccess( timelineId, statuses, diff --git a/src/api/index.ts b/src/api/index.ts index 1d59e8d0f..9dedcdd04 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -11,6 +11,8 @@ import { RootState } from 'soapbox/store'; import { getAccessToken, getAppToken, isURL, parseBaseURL } from 'soapbox/utils/auth'; import { buildFullPath } from 'soapbox/utils/url'; +type PlfeResponse = Response & { data: string; json: T }; + /** Parse Link headers, mostly for pagination. @param {object} response - Fetch API response object @@ -57,7 +59,7 @@ const getFetch = (accessToken?: string | null, baseURL: string = '') => // Fetch API doesn't report upload progress, use XHR if (init?.onUploadProgress) { - return new Promise((resolve, reject) => { + return new Promise>((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.addEventListener('progress', init.onUploadProgress!); @@ -72,7 +74,7 @@ const getFetch = (accessToken?: string | null, baseURL: string = '') => } if (xhr.status >= 400) reject({ response: { status: xhr.status, data, json } }); - resolve({ status: xhr.status, data, json } as any); + resolve({ status: xhr.status, data, json } as any as PlfeResponse); }); xhr.open(init?.method || 'GET', fullPath, true); @@ -85,8 +87,8 @@ const getFetch = (accessToken?: string | null, baseURL: string = '') => return fetch(fullPath, { ...init, headers, - }).then(async (response) => { - const data = await response.text(); + }).then(async (res) => { + const data = await res.text(); let json: T = undefined!; try { @@ -95,11 +97,14 @@ const getFetch = (accessToken?: string | null, baseURL: string = '') => // } - if (!response.ok) { - const { headers, ok, redirected, status, statusText, type, url } = response; - throw { response: { headers, ok, redirected, status, statusText, type, url, data, json } }; + const { headers, ok, redirected, status, statusText, type, url } = res; + + const response = { headers, ok, redirected, status, statusText, type, url, data, json }; + + if (!ok) { + throw { response }; } - return { ...response, data, json }; + return response as any as PlfeResponse; }); }; @@ -120,7 +125,10 @@ const staticFetch = (input: URL | RequestInfo, init?: RequestInit | undefined) = } catch (e) { // } - return { ...response, data, json }; + + const { headers, ok, redirected, status, statusText, type, url } = response; + + return { headers, ok, redirected, status, statusText, type, url, data, json } as any as PlfeResponse; }); }; @@ -141,6 +149,7 @@ const api = (getState: () => RootState, authType: string = 'user') => { }; export { + type PlfeResponse, getLinks, getNextLink, getPrevLink, diff --git a/src/entity-store/hooks/types.ts b/src/entity-store/hooks/types.ts index b3ec5937a..bf9026b3f 100644 --- a/src/entity-store/hooks/types.ts +++ b/src/entity-store/hooks/types.ts @@ -1,4 +1,5 @@ import type { Entity } from '../types'; +import type { PlfeResponse } from 'soapbox/api'; import type z from 'zod'; type EntitySchema = z.ZodType; @@ -32,9 +33,9 @@ interface EntityCallbacks { /** * Passed into hooks to make requests. - * Must return an Axios response. + * Must return a response. */ -type EntityFn = (value: T) => Promise +type EntityFn = (value: T) => Promise export type { EntitySchema, diff --git a/src/entity-store/hooks/useCreateEntity.ts b/src/entity-store/hooks/useCreateEntity.ts index 7903e2a90..94fc1bab2 100644 --- a/src/entity-store/hooks/useCreateEntity.ts +++ b/src/entity-store/hooks/useCreateEntity.ts @@ -9,6 +9,7 @@ import { parseEntitiesPath } from './utils'; import type { EntityCallbacks, EntityFn, EntitySchema, ExpandedEntitiesPath } from './types'; import type { Entity } from '../types'; +import type { PlfeResponse } from 'soapbox/api'; interface UseCreateEntityOpts { schema?: EntitySchema; @@ -26,7 +27,7 @@ const useCreateEntity = ( const createEntity = async ( data: Data, - callbacks: EntityCallbacks = {}, + callbacks: EntityCallbacks = {}, ): Promise => { const result = await setPromise(entityFn(data)); const schema = opts.schema || z.custom(); diff --git a/src/entity-store/hooks/useEntity.ts b/src/entity-store/hooks/useEntity.ts index cfc934510..70001005f 100644 --- a/src/entity-store/hooks/useEntity.ts +++ b/src/entity-store/hooks/useEntity.ts @@ -10,6 +10,7 @@ import { selectEntity } from '../selectors'; import type { EntitySchema, EntityPath, EntityFn } from './types'; import type { Entity } from '../types'; +import type { PlfeResponse } from 'soapbox/api'; /** Additional options for the hook. */ interface UseEntityOpts { @@ -66,8 +67,8 @@ const useEntity = ( isLoading, isLoaded, error, - isUnauthorized: (error as { response?: Response })?.response?.status === 401, - isForbidden: (error as { response?: Response })?.response?.status === 403, + isUnauthorized: (error as { response?: PlfeResponse })?.response?.status === 401, + isForbidden: (error as { response?: PlfeResponse })?.response?.status === 403, }; }; diff --git a/src/entity-store/hooks/useEntityLookup.ts b/src/entity-store/hooks/useEntityLookup.ts index c440bdcf3..25b3baa18 100644 --- a/src/entity-store/hooks/useEntityLookup.ts +++ b/src/entity-store/hooks/useEntityLookup.ts @@ -7,10 +7,11 @@ import { useLoading } from 'soapbox/hooks/useLoading'; import { importEntities } from '../actions'; import { findEntity } from '../selectors'; -import { Entity } from '../types'; -import { EntityFn } from './types'; -import { type UseEntityOpts } from './useEntity'; +import type { EntityFn } from './types'; +import type { UseEntityOpts } from './useEntity'; +import type { Entity } from '../types'; +import type { PlfeResponse } from 'soapbox/api'; /** Entities will be filtered through this function until it returns true. */ type LookupFn = (entity: TEntity) => boolean @@ -56,8 +57,8 @@ const useEntityLookup = ( fetchEntity, isFetching, isLoading, - isUnauthorized: (error as { response?: Response })?.response?.status === 401, - isForbidden: (error as { response?: Response })?.response?.status === 403, + isUnauthorized: (error as { response?: PlfeResponse })?.response?.status === 401, + isForbidden: (error as { response?: PlfeResponse })?.response?.status === 403, }; }; diff --git a/src/features/account/components/header.tsx b/src/features/account/components/header.tsx index 87f4f7153..b43f0c83d 100644 --- a/src/features/account/components/header.tsx +++ b/src/features/account/components/header.tsx @@ -31,6 +31,8 @@ import { isDefaultHeader } from 'soapbox/utils/accounts'; import copy from 'soapbox/utils/copy'; import { MASTODON, parseVersion } from 'soapbox/utils/features'; +import type { PlfeResponse } from 'soapbox/api'; + const messages = defineMessages({ edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' }, linkVerifiedOn: { id: 'account.link_verified_on', defaultMessage: 'Ownership of this link was checked on {date}' }, @@ -93,7 +95,7 @@ const Header: React.FC = ({ account }) => { const createAndNavigateToChat = useMutation({ mutationFn: (accountId: string) => getOrCreateChatByAccountId(accountId), - onError: (error: { response: Response }) => { + onError: (error: { response: PlfeResponse }) => { const data = error.response?.json as any; toast.error(data?.error); }, diff --git a/src/features/auth-login/components/login-page.tsx b/src/features/auth-login/components/login-page.tsx index e2dc25edb..b9704000b 100644 --- a/src/features/auth-login/components/login-page.tsx +++ b/src/features/auth-login/components/login-page.tsx @@ -15,6 +15,8 @@ import ConsumersList from './consumers-list'; import LoginForm from './login-form'; import OtpAuthForm from './otp-auth-form'; +import type { PlfeResponse } from 'soapbox/api'; + const LoginPage = () => { const dispatch = useAppDispatch(); @@ -49,7 +51,7 @@ const LoginPage = () => { } else { setShouldRedirect(true); } - }).catch((error: { response: Response }) => { + }).catch((error: { response: PlfeResponse }) => { const data: any = error.response?.json; if (data?.error === 'mfa_required') { setMfaAuthNeeded(true); diff --git a/src/features/chats/components/chat-search/chat-search.tsx b/src/features/chats/components/chat-search/chat-search.tsx index 8b7f04df1..7eb74fb47 100644 --- a/src/features/chats/components/chat-search/chat-search.tsx +++ b/src/features/chats/components/chat-search/chat-search.tsx @@ -15,6 +15,8 @@ import Blankslate from './blankslate'; import EmptyResultsBlankslate from './empty-results-blankslate'; import Results from './results'; +import type { PlfeResponse } from 'soapbox/api'; + const messages = defineMessages({ placeholder: { id: 'chat_search.placeholder', defaultMessage: 'Type a name' }, }); @@ -44,7 +46,7 @@ const ChatSearch = (props: IChatSearch) => { const handleClickOnSearchResult = useMutation({ mutationFn: (accountId: string) => getOrCreateChatByAccountId(accountId), - onError: (error: { response: Response }) => { + onError: (error: { response: PlfeResponse }) => { const data = error.response?.json as any; toast.error(data?.error); }, diff --git a/src/features/chats/components/chat.tsx b/src/features/chats/components/chat.tsx index 68a14a410..130ba154a 100644 --- a/src/features/chats/components/chat.tsx +++ b/src/features/chats/components/chat.tsx @@ -12,6 +12,7 @@ import toast from 'soapbox/toast'; import ChatComposer from './chat-composer'; import ChatMessageList from './chat-message-list'; +import type { PlfeResponse } from 'soapbox/api'; import type { Attachment } from 'soapbox/types/entities'; const fileKeyGen = (): number => Math.floor((Math.random() * 0x10000)); @@ -69,7 +70,7 @@ const Chat: React.FC = ({ chat, inputRef, className }) => { onSuccess: () => { setErrorMessage(undefined); }, - onError: (error: { response: Response & { json: any } }, _variables, context) => { + onError: (error: { response: PlfeResponse }, _variables, context) => { const message = error.response?.json?.error; setErrorMessage(message || intl.formatMessage(messages.failedToSend)); setContent(context.prevContent as string); diff --git a/src/features/group/group-membership-requests.tsx b/src/features/group/group-membership-requests.tsx index 52ee117cb..f7aa6029c 100644 --- a/src/features/group/group-membership-requests.tsx +++ b/src/features/group/group-membership-requests.tsx @@ -11,6 +11,7 @@ import toast from 'soapbox/toast'; import ColumnForbidden from '../ui/components/column-forbidden'; +import type { PlfeResponse } from 'soapbox/api'; import type { Account as AccountEntity } from 'soapbox/schemas'; type RouteParams = { groupId: string }; @@ -80,7 +81,7 @@ const GroupMembershipRequests: React.FC = ({ params }) const handleAuthorize = async (account: AccountEntity) => authorize(account.id) .then(() => Promise.resolve()) - .catch((error: { response: Response }) => { + .catch((error: { response: PlfeResponse }) => { refetch(); let message = intl.formatMessage(messages.authorizeFail, { name: account.username }); @@ -95,7 +96,7 @@ const GroupMembershipRequests: React.FC = ({ params }) const handleReject = async (account: AccountEntity) => reject(account.id) .then(() => Promise.resolve()) - .catch((error: { response: Response }) => { + .catch((error: { response: PlfeResponse }) => { refetch(); let message = intl.formatMessage(messages.rejectFail, { name: account.username }); diff --git a/src/features/onboarding/steps/avatar-selection-step.tsx b/src/features/onboarding/steps/avatar-selection-step.tsx index c20b3cadd..be5d62601 100644 --- a/src/features/onboarding/steps/avatar-selection-step.tsx +++ b/src/features/onboarding/steps/avatar-selection-step.tsx @@ -10,6 +10,8 @@ import toast from 'soapbox/toast'; import { isDefaultAvatar } from 'soapbox/utils/accounts'; import resizeImage from 'soapbox/utils/resize-image'; +import type { PlfeResponse } from 'soapbox/api'; + const messages = defineMessages({ error: { id: 'onboarding.error', defaultMessage: 'An unexpected error occurred. Please try again or skip this step.' }, }); @@ -48,7 +50,7 @@ const AvatarSelectionStep = ({ onNext }: { onNext: () => void }) => { setDisabled(false); setSubmitting(false); onNext(); - }).catch((error: { response: Response }) => { + }).catch((error: { response: PlfeResponse }) => { setSubmitting(false); setDisabled(false); setSelectedFile(null); diff --git a/src/features/onboarding/steps/bio-step.tsx b/src/features/onboarding/steps/bio-step.tsx index 4245c0e9c..f5a450dfc 100644 --- a/src/features/onboarding/steps/bio-step.tsx +++ b/src/features/onboarding/steps/bio-step.tsx @@ -7,6 +7,8 @@ import { Button, FormGroup, Stack, Textarea } from 'soapbox/components/ui'; import { useAppDispatch, useOwnAccount } from 'soapbox/hooks'; import toast from 'soapbox/toast'; +import type { PlfeResponse } from 'soapbox/api'; + const messages = defineMessages({ bioPlaceholder: { id: 'onboarding.bio.placeholder', defaultMessage: 'Tell the world a little about yourself…' }, error: { id: 'onboarding.error', defaultMessage: 'An unexpected error occurred. Please try again or skip this step.' }, @@ -30,7 +32,7 @@ const BioStep = ({ onNext }: { onNext: () => void }) => { .then(() => { setSubmitting(false); onNext(); - }).catch((error: { response: Response }) => { + }).catch((error: { response: PlfeResponse }) => { setSubmitting(false); if (error.response?.status === 422) { diff --git a/src/features/onboarding/steps/cover-photo-selection-step.tsx b/src/features/onboarding/steps/cover-photo-selection-step.tsx index e94f6e1fc..a77e5a6c6 100644 --- a/src/features/onboarding/steps/cover-photo-selection-step.tsx +++ b/src/features/onboarding/steps/cover-photo-selection-step.tsx @@ -11,6 +11,8 @@ import toast from 'soapbox/toast'; import { isDefaultHeader } from 'soapbox/utils/accounts'; import resizeImage from 'soapbox/utils/resize-image'; +import type { PlfeResponse } from 'soapbox/api'; + const messages = defineMessages({ header: { id: 'account.header.alt', defaultMessage: 'Profile header' }, error: { id: 'onboarding.error', defaultMessage: 'An unexpected error occurred. Please try again or skip this step.' }, @@ -51,7 +53,7 @@ const CoverPhotoSelectionStep = ({ onNext }: { onNext: () => void }) => { setDisabled(false); setSubmitting(false); onNext(); - }).catch((error: { response: Response }) => { + }).catch((error: { response: PlfeResponse }) => { setSubmitting(false); setDisabled(false); setSelectedFile(null); diff --git a/src/features/onboarding/steps/display-name-step.tsx b/src/features/onboarding/steps/display-name-step.tsx index 3d94645d2..05fcb7bfb 100644 --- a/src/features/onboarding/steps/display-name-step.tsx +++ b/src/features/onboarding/steps/display-name-step.tsx @@ -7,6 +7,8 @@ import { Button, FormGroup, Input, Stack } from 'soapbox/components/ui'; import { useAppDispatch, useOwnAccount } from 'soapbox/hooks'; import toast from 'soapbox/toast'; +import type { PlfeResponse } from 'soapbox/api'; + const messages = defineMessages({ usernamePlaceholder: { id: 'onboarding.display_name.placeholder', defaultMessage: 'Eg. John Smith' }, error: { id: 'onboarding.error', defaultMessage: 'An unexpected error occurred. Please try again or skip this step.' }, @@ -41,7 +43,7 @@ const DisplayNameStep = ({ onNext }: { onNext: () => void }) => { .then(() => { setSubmitting(false); onNext(); - }).catch((error: { response: Response }) => { + }).catch((error: { response: PlfeResponse }) => { setSubmitting(false); if (error.response?.status === 422) { diff --git a/src/features/ui/components/modals/manage-group-modal/create-group-modal.tsx b/src/features/ui/components/modals/manage-group-modal/create-group-modal.tsx index a34847a7b..92b7e173b 100644 --- a/src/features/ui/components/modals/manage-group-modal/create-group-modal.tsx +++ b/src/features/ui/components/modals/manage-group-modal/create-group-modal.tsx @@ -11,6 +11,8 @@ import ConfirmationStep from './steps/confirmation-step'; import DetailsStep from './steps/details-step'; import PrivacyStep from './steps/privacy-step'; +import type { PlfeResponse } from 'soapbox/api'; + const messages = defineMessages({ next: { id: 'manage_group.next', defaultMessage: 'Next' }, create: { id: 'manage_group.create', defaultMessage: 'Create Group' }, @@ -64,7 +66,7 @@ const CreateGroupModal: React.FC = ({ onClose }) => { setCurrentStep(Steps.THREE); setGroup(group); }, - onError(error: { response?: Response }) { + onError(error: { response?: PlfeResponse }) { const msg = z.object({ error: z.string() }).safeParse(error?.response?.json); if (msg.success) { toast.error(msg.data.error); diff --git a/src/features/ui/components/navbar.tsx b/src/features/ui/components/navbar.tsx index 624245952..4b9fc8593 100644 --- a/src/features/ui/components/navbar.tsx +++ b/src/features/ui/components/navbar.tsx @@ -13,6 +13,8 @@ import { useAppDispatch, useFeatures, useOwnAccount, useRegistrationStatus } fro import ProfileDropdown from './profile-dropdown'; +import type { PlfeResponse } from 'soapbox/api'; + const messages = defineMessages({ login: { id: 'navbar.login.action', defaultMessage: 'Log in' }, username: { id: 'navbar.login.username.placeholder', defaultMessage: 'Email or username' }, @@ -50,7 +52,7 @@ const Navbar = () => { .then(() => dispatch(fetchInstance())) ); }) - .catch((error: { response: Response }) => { + .catch((error: { response: PlfeResponse }) => { setLoading(false); const data: any = error.response?.json; diff --git a/src/reducers/auth.ts b/src/reducers/auth.ts index ea7413253..c8ac07923 100644 --- a/src/reducers/auth.ts +++ b/src/reducers/auth.ts @@ -18,6 +18,7 @@ import { import { ME_FETCH_SKIP } from '../actions/me'; import type { AnyAction } from 'redux'; +import type { PlfeResponse } from 'soapbox/api'; import type { APIEntity, Account as AccountEntity } from 'soapbox/types/entities'; const AuthAppRecord = ImmutableRecord({ @@ -282,7 +283,7 @@ const persistAuthAccount = (account: APIEntity) => { } }; -const deleteForbiddenToken = (state: State, error: { response: Response }, token: string) => { +const deleteForbiddenToken = (state: State, error: { response: PlfeResponse }, token: string) => { if ([401, 403].includes(error.response?.status!)) { return deleteToken(state, token); } else { diff --git a/src/reducers/me.ts b/src/reducers/me.ts index e0fee28a6..a9f4be296 100644 --- a/src/reducers/me.ts +++ b/src/reducers/me.ts @@ -11,11 +11,12 @@ import { } from '../actions/me'; import type { AnyAction } from 'redux'; +import type { PlfeResponse } from 'soapbox/api'; import type { Me } from 'soapbox/types/soapbox'; const initialState: Me = null; -const handleForbidden = (state: Me, error: { response: Response }) => { +const handleForbidden = (state: Me, error: { response: PlfeResponse }) => { if (([401, 403] as any[]).includes(error.response?.status)) { return false; } else { diff --git a/src/toast.tsx b/src/toast.tsx index d6956cc12..0da442f80 100644 --- a/src/toast.tsx +++ b/src/toast.tsx @@ -5,6 +5,8 @@ import { defineMessages, MessageDescriptor } from 'react-intl'; import { Toast } from './components/ui'; import { httpErrorMessages } from './utils/errors'; +import type { PlfeResponse } from './api'; + type ToastText = string | MessageDescriptor type ToastType = 'success' | 'error' | 'info' @@ -39,7 +41,7 @@ const messages = defineMessages({ unexpectedMessage: { id: 'alert.unexpected.message', defaultMessage: 'Something went wrong.' }, }); -const showAlertForError = (networkError: { response: Response & { json: any } }) => { +const showAlertForError = (networkError: { response: PlfeResponse }) => { if (networkError?.response) { const { json, status, statusText } = networkError.response;