Convert `actions/account_notes` into Typescript (#26601)
parent
7730083611
commit
bd06c13204
9 changed files with 149 additions and 162 deletions
@ -1,37 +0,0 @@ |
||||
import api from '../api'; |
||||
|
||||
export const ACCOUNT_NOTE_SUBMIT_REQUEST = 'ACCOUNT_NOTE_SUBMIT_REQUEST'; |
||||
export const ACCOUNT_NOTE_SUBMIT_SUCCESS = 'ACCOUNT_NOTE_SUBMIT_SUCCESS'; |
||||
export const ACCOUNT_NOTE_SUBMIT_FAIL = 'ACCOUNT_NOTE_SUBMIT_FAIL'; |
||||
|
||||
export function submitAccountNote(id, value) { |
||||
return (dispatch, getState) => { |
||||
dispatch(submitAccountNoteRequest()); |
||||
|
||||
api(getState).post(`/api/v1/accounts/${id}/note`, { |
||||
comment: value, |
||||
}).then(response => { |
||||
dispatch(submitAccountNoteSuccess(response.data)); |
||||
}).catch(error => dispatch(submitAccountNoteFail(error))); |
||||
}; |
||||
} |
||||
|
||||
export function submitAccountNoteRequest() { |
||||
return { |
||||
type: ACCOUNT_NOTE_SUBMIT_REQUEST, |
||||
}; |
||||
} |
||||
|
||||
export function submitAccountNoteSuccess(relationship) { |
||||
return { |
||||
type: ACCOUNT_NOTE_SUBMIT_SUCCESS, |
||||
relationship, |
||||
}; |
||||
} |
||||
|
||||
export function submitAccountNoteFail(error) { |
||||
return { |
||||
type: ACCOUNT_NOTE_SUBMIT_FAIL, |
||||
error, |
||||
}; |
||||
} |
@ -0,0 +1,18 @@ |
||||
import { createAppAsyncThunk } from 'mastodon/store/typed_functions'; |
||||
|
||||
import api from '../api'; |
||||
|
||||
export const submitAccountNote = createAppAsyncThunk( |
||||
'account_note/submit', |
||||
async (args: { id: string; value: string }, { getState }) => { |
||||
// TODO: replace `unknown` with `ApiRelationshipJSON` when it is merged
|
||||
const response = await api(getState).post<unknown>( |
||||
`/api/v1/accounts/${args.id}/note`, |
||||
{ |
||||
comment: args.value, |
||||
}, |
||||
); |
||||
|
||||
return { relationship: response.data }; |
||||
}, |
||||
); |
@ -1,76 +0,0 @@ |
||||
// @ts-check
|
||||
|
||||
import axios from 'axios'; |
||||
import LinkHeader from 'http-link-header'; |
||||
|
||||
import ready from './ready'; |
||||
|
||||
/** |
||||
* @param {import('axios').AxiosResponse} response |
||||
* @returns {LinkHeader} |
||||
*/ |
||||
export const getLinks = response => { |
||||
const value = response.headers.link; |
||||
|
||||
if (!value) { |
||||
return new LinkHeader(); |
||||
} |
||||
|
||||
return LinkHeader.parse(value); |
||||
}; |
||||
|
||||
/** @type {import('axios').RawAxiosRequestHeaders} */ |
||||
const csrfHeader = {}; |
||||
|
||||
/** |
||||
* @returns {void} |
||||
*/ |
||||
const setCSRFHeader = () => { |
||||
/** @type {HTMLMetaElement | null} */ |
||||
const csrfToken = document.querySelector('meta[name=csrf-token]'); |
||||
|
||||
if (csrfToken) { |
||||
csrfHeader['X-CSRF-Token'] = csrfToken.content; |
||||
} |
||||
}; |
||||
|
||||
ready(setCSRFHeader); |
||||
|
||||
/** |
||||
* @param {() => import('immutable').Map<string,any>} getState |
||||
* @returns {import('axios').RawAxiosRequestHeaders} |
||||
*/ |
||||
const authorizationHeaderFromState = getState => { |
||||
const accessToken = getState && getState().getIn(['meta', 'access_token'], ''); |
||||
|
||||
if (!accessToken) { |
||||
return {}; |
||||
} |
||||
|
||||
return { |
||||
'Authorization': `Bearer ${accessToken}`, |
||||
}; |
||||
}; |
||||
|
||||
/** |
||||
* @param {() => import('immutable').Map<string,any>} getState |
||||
* @returns {import('axios').AxiosInstance} |
||||
*/ |
||||
export default function api(getState) { |
||||
return axios.create({ |
||||
headers: { |
||||
...csrfHeader, |
||||
...authorizationHeaderFromState(getState), |
||||
}, |
||||
|
||||
transformResponse: [ |
||||
function (data) { |
||||
try { |
||||
return JSON.parse(data); |
||||
} catch { |
||||
return data; |
||||
} |
||||
}, |
||||
], |
||||
}); |
||||
} |
@ -0,0 +1,63 @@ |
||||
import type { AxiosResponse, RawAxiosRequestHeaders } from 'axios'; |
||||
import axios from 'axios'; |
||||
import LinkHeader from 'http-link-header'; |
||||
|
||||
import ready from './ready'; |
||||
import type { GetState } from './store'; |
||||
|
||||
export const getLinks = (response: AxiosResponse) => { |
||||
const value = response.headers.link as string | undefined; |
||||
|
||||
if (!value) { |
||||
return new LinkHeader(); |
||||
} |
||||
|
||||
return LinkHeader.parse(value); |
||||
}; |
||||
|
||||
const csrfHeader: RawAxiosRequestHeaders = {}; |
||||
|
||||
const setCSRFHeader = () => { |
||||
const csrfToken = document.querySelector<HTMLMetaElement>( |
||||
'meta[name=csrf-token]', |
||||
); |
||||
|
||||
if (csrfToken) { |
||||
csrfHeader['X-CSRF-Token'] = csrfToken.content; |
||||
} |
||||
}; |
||||
|
||||
void ready(setCSRFHeader); |
||||
|
||||
const authorizationHeaderFromState = (getState?: GetState) => { |
||||
const accessToken = |
||||
getState && (getState().meta.get('access_token', '') as string); |
||||
|
||||
if (!accessToken) { |
||||
return {}; |
||||
} |
||||
|
||||
return { |
||||
Authorization: `Bearer ${accessToken}`, |
||||
} as RawAxiosRequestHeaders; |
||||
}; |
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default function api(getState: GetState) { |
||||
return axios.create({ |
||||
headers: { |
||||
...csrfHeader, |
||||
...authorizationHeaderFromState(getState), |
||||
}, |
||||
|
||||
transformResponse: [ |
||||
function (data: unknown) { |
||||
try { |
||||
return JSON.parse(data as string) as unknown; |
||||
} catch { |
||||
return data; |
||||
} |
||||
}, |
||||
], |
||||
}); |
||||
} |
@ -1,45 +1,8 @@ |
||||
import type { TypedUseSelectorHook } from 'react-redux'; |
||||
import { useDispatch, useSelector } from 'react-redux'; |
||||
|
||||
import { configureStore } from '@reduxjs/toolkit'; |
||||
|
||||
import { rootReducer } from '../reducers'; |
||||
|
||||
import { errorsMiddleware } from './middlewares/errors'; |
||||
import { loadingBarMiddleware } from './middlewares/loading_bar'; |
||||
import { soundsMiddleware } from './middlewares/sounds'; |
||||
|
||||
export const store = configureStore({ |
||||
reducer: rootReducer, |
||||
middleware: (getDefaultMiddleware) => |
||||
getDefaultMiddleware({ |
||||
// In development, Redux Toolkit enables 2 default middlewares to detect
|
||||
// common issues with states. Unfortunately, our use of ImmutableJS for state
|
||||
// triggers both, so lets disable them until our state is fully refactored
|
||||
|
||||
// https://redux-toolkit.js.org/api/serializabilityMiddleware
|
||||
// This checks recursively that every values in the state are serializable in JSON
|
||||
// Which is not the case, as we use ImmutableJS structures, but also File objects
|
||||
serializableCheck: false, |
||||
|
||||
// https://redux-toolkit.js.org/api/immutabilityMiddleware
|
||||
// This checks recursively if every value in the state is immutable (ie, a JS primitive type)
|
||||
// But this is not the case, as our Root State is an ImmutableJS map, which is an object
|
||||
immutableCheck: false, |
||||
}) |
||||
.concat( |
||||
loadingBarMiddleware({ |
||||
promiseTypeSuffixes: ['REQUEST', 'SUCCESS', 'FAIL'], |
||||
}), |
||||
) |
||||
.concat(errorsMiddleware) |
||||
.concat(soundsMiddleware()), |
||||
}); |
||||
|
||||
// Infer the `RootState` and `AppDispatch` types from the store itself
|
||||
export type RootState = ReturnType<typeof rootReducer>; |
||||
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
|
||||
export type AppDispatch = typeof store.dispatch; |
||||
|
||||
export const useAppDispatch: () => AppDispatch = useDispatch; |
||||
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector; |
||||
export { store } from './store'; |
||||
export type { GetState, AppDispatch, RootState } from './store'; |
||||
|
||||
export { |
||||
createAppAsyncThunk, |
||||
useAppDispatch, |
||||
useAppSelector, |
||||
} from './typed_functions'; |
||||
|
@ -0,0 +1,40 @@ |
||||
import { configureStore } from '@reduxjs/toolkit'; |
||||
|
||||
import { rootReducer } from '../reducers'; |
||||
|
||||
import { errorsMiddleware } from './middlewares/errors'; |
||||
import { loadingBarMiddleware } from './middlewares/loading_bar'; |
||||
import { soundsMiddleware } from './middlewares/sounds'; |
||||
|
||||
export const store = configureStore({ |
||||
reducer: rootReducer, |
||||
middleware: (getDefaultMiddleware) => |
||||
getDefaultMiddleware({ |
||||
// In development, Redux Toolkit enables 2 default middlewares to detect
|
||||
// common issues with states. Unfortunately, our use of ImmutableJS for state
|
||||
// triggers both, so lets disable them until our state is fully refactored
|
||||
|
||||
// https://redux-toolkit.js.org/api/serializabilityMiddleware
|
||||
// This checks recursively that every values in the state are serializable in JSON
|
||||
// Which is not the case, as we use ImmutableJS structures, but also File objects
|
||||
serializableCheck: false, |
||||
|
||||
// https://redux-toolkit.js.org/api/immutabilityMiddleware
|
||||
// This checks recursively if every value in the state is immutable (ie, a JS primitive type)
|
||||
// But this is not the case, as our Root State is an ImmutableJS map, which is an object
|
||||
immutableCheck: false, |
||||
}) |
||||
.concat( |
||||
loadingBarMiddleware({ |
||||
promiseTypeSuffixes: ['REQUEST', 'SUCCESS', 'FAIL'], |
||||
}), |
||||
) |
||||
.concat(errorsMiddleware) |
||||
.concat(soundsMiddleware()), |
||||
}); |
||||
|
||||
// Infer the `RootState` and `AppDispatch` types from the store itself
|
||||
export type RootState = ReturnType<typeof rootReducer>; |
||||
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
|
||||
export type AppDispatch = typeof store.dispatch; |
||||
export type GetState = typeof store.getState; |
@ -0,0 +1,16 @@ |
||||
import type { TypedUseSelectorHook } from 'react-redux'; |
||||
import { useDispatch, useSelector } from 'react-redux'; |
||||
|
||||
import { createAsyncThunk } from '@reduxjs/toolkit'; |
||||
|
||||
import type { AppDispatch, RootState } from './store'; |
||||
|
||||
export const useAppDispatch: () => AppDispatch = useDispatch; |
||||
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector; |
||||
|
||||
export const createAppAsyncThunk = createAsyncThunk.withTypes<{ |
||||
state: RootState; |
||||
dispatch: AppDispatch; |
||||
rejectValue: string; |
||||
extra: { s: string; n: number }; |
||||
}>(); |
Loading…
Reference in new issue