[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 }) => ({
 | 
			
		||||
 | 
			
		||||
  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 {
 | 
			
		||||
  ACCOUNT_NOTE_SUBMIT_SUCCESS,
 | 
			
		||||
  submitAccountNote,
 | 
			
		||||
} from '../actions/account_notes';
 | 
			
		||||
import {
 | 
			
		||||
  ACCOUNT_FOLLOW_SUCCESS,
 | 
			
		||||
| 
						 | 
				
			
			@ -73,10 +73,10 @@ export default function relationships(state = initialState, action) {
 | 
			
		|||
  case ACCOUNT_UNMUTE_SUCCESS:
 | 
			
		||||
  case ACCOUNT_PIN_SUCCESS:
 | 
			
		||||
  case ACCOUNT_UNPIN_SUCCESS:
 | 
			
		||||
  case ACCOUNT_NOTE_SUBMIT_SUCCESS:
 | 
			
		||||
    return normalizeRelationship(state, action.relationship);
 | 
			
		||||
  case RELATIONSHIPS_FETCH_SUCCESS:
 | 
			
		||||
    return normalizeRelationships(state, action.relationships);
 | 
			
		||||
  case submitAccountNote.fulfilled:
 | 
			
		||||
    return normalizeRelationship(state, action.payload.relationship);
 | 
			
		||||
  case DOMAIN_BLOCK_SUCCESS:
 | 
			
		||||
    return setDomainBlocking(state, action.accounts, true);
 | 
			
		||||
  case DOMAIN_UNBLOCK_SUCCESS:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,45 +1,8 @@
 | 
			
		|||
import type { TypedUseSelectorHook } from 'react-redux';
 | 
			
		||||
import { useDispatch, useSelector } from 'react-redux';
 | 
			
		||||
export { store } from './store';
 | 
			
		||||
export type { GetState, AppDispatch, RootState } from './store';
 | 
			
		||||
 | 
			
		||||
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 {
 | 
			
		||||
  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