[Glitch] Convert `actions/account_notes` into Typescript
Port bd06c13204 to glitch-soc
Signed-off-by: Claire <claire.github-309c@sitedethib.com>
			
			
This commit is contained in:
		
							parent
							
								
									c82d4cfb71
								
							
						
					
					
						commit
						6fb5fafd28
					
				| 
						 | 
					@ -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 'flavours/glitch/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,75 +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;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -11,7 +11,7 @@ const mapStateToProps = (state, { account }) => ({
 | 
				
			||||||
const mapDispatchToProps = (dispatch, { account }) => ({
 | 
					const mapDispatchToProps = (dispatch, { account }) => ({
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  onSave (value) {
 | 
					  onSave (value) {
 | 
				
			||||||
    dispatch(submitAccountNote(account.get('id'), value));
 | 
					    dispatch(submitAccountNote({ id: account.get('id'), value}));
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
import { Map as ImmutableMap, fromJS } from 'immutable';
 | 
					import { Map as ImmutableMap, fromJS } from 'immutable';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  ACCOUNT_NOTE_SUBMIT_SUCCESS,
 | 
					  submitAccountNote,
 | 
				
			||||||
} from '../actions/account_notes';
 | 
					} from '../actions/account_notes';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  ACCOUNT_FOLLOW_SUCCESS,
 | 
					  ACCOUNT_FOLLOW_SUCCESS,
 | 
				
			||||||
| 
						 | 
					@ -73,10 +73,10 @@ export default function relationships(state = initialState, action) {
 | 
				
			||||||
  case ACCOUNT_UNMUTE_SUCCESS:
 | 
					  case ACCOUNT_UNMUTE_SUCCESS:
 | 
				
			||||||
  case ACCOUNT_PIN_SUCCESS:
 | 
					  case ACCOUNT_PIN_SUCCESS:
 | 
				
			||||||
  case ACCOUNT_UNPIN_SUCCESS:
 | 
					  case ACCOUNT_UNPIN_SUCCESS:
 | 
				
			||||||
  case ACCOUNT_NOTE_SUBMIT_SUCCESS:
 | 
					 | 
				
			||||||
    return normalizeRelationship(state, action.relationship);
 | 
					 | 
				
			||||||
  case RELATIONSHIPS_FETCH_SUCCESS:
 | 
					  case RELATIONSHIPS_FETCH_SUCCESS:
 | 
				
			||||||
    return normalizeRelationships(state, action.relationships);
 | 
					    return normalizeRelationships(state, action.relationships);
 | 
				
			||||||
 | 
					  case submitAccountNote.fulfilled:
 | 
				
			||||||
 | 
					    return normalizeRelationship(state, action.payload.relationship);
 | 
				
			||||||
  case DOMAIN_BLOCK_SUCCESS:
 | 
					  case DOMAIN_BLOCK_SUCCESS:
 | 
				
			||||||
    return setDomainBlocking(state, action.accounts, true);
 | 
					    return setDomainBlocking(state, action.accounts, true);
 | 
				
			||||||
  case DOMAIN_UNBLOCK_SUCCESS:
 | 
					  case DOMAIN_UNBLOCK_SUCCESS:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,45 +1,8 @@
 | 
				
			||||||
import type { TypedUseSelectorHook } from 'react-redux';
 | 
					export { store } from './store';
 | 
				
			||||||
import { useDispatch, useSelector } from 'react-redux';
 | 
					export type { GetState, AppDispatch, RootState } from './store';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { configureStore } from '@reduxjs/toolkit';
 | 
					export {
 | 
				
			||||||
 | 
					  createAppAsyncThunk,
 | 
				
			||||||
import { rootReducer } from '../reducers';
 | 
					  useAppDispatch,
 | 
				
			||||||
 | 
					  useAppSelector,
 | 
				
			||||||
import { errorsMiddleware } from './middlewares/errors';
 | 
					} from './typed_functions';
 | 
				
			||||||
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;
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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