import { ThunkAction } from 'redux-thunk'; import { BaseState, Favorite, FavoriteCategory, FavoritesActionTypes, FavoritesAlert, FavoritesError, FavoritesFilters, FavoritesKeys, FavoritesSourcePaths, GetFavoritesLoadingAction, GetFavoritesSuccessAction, GetFavoritesErrorAction, SetFavoritesAlertAction, SetFavoritesFiltersAction, SetFavoriteErrorAction, SetFavoriteSuccessAction, SetFavoritesFromFavoritesAction, UnsetFavoriteErrorAction, UnsetFavoriteSuccessAction, } from '../redux-types/favorites.redux-types'; import ApolloClient, { InMemoryCache, MutationOptions, QueryOptions, } from 'apollo-boost'; import { SET_FAVORITE, UNSET_FAVORITE } from '../apollo/mutations'; import { GET_FAVORITES } from '../apollo/queries'; const setFavoritesAlert = (alert: FavoritesAlert): SetFavoritesAlertAction => ({ type: FavoritesActionTypes.SET_FAVORITES_ALERT, alert, }); const setFavoritesFilters = ( filters: FavoritesFilters, ): SetFavoritesFiltersAction => ({ type: FavoritesActionTypes.SET_FAVORITES_FILTERS, filters, }); const getFavoritesError = (error: FavoritesError): GetFavoritesErrorAction => ({ type: FavoritesActionTypes.GET_FAVORITES_ERROR, error, }); const getFavoritesLoading = (): GetFavoritesLoadingAction => ({ type: FavoritesActionTypes.GET_FAVORITES_LOADING, loading: true, }); const getFavoritesSuccess = ( data: Favorite[], keys: FavoritesKeys, ): GetFavoritesSuccessAction => ({ type: FavoritesActionTypes.GET_FAVORITES_SUCCESS, data, keys, }); const setFavoriteError = (error: FavoritesError): SetFavoriteErrorAction => ({ type: FavoritesActionTypes.SET_FAVORITE_ERROR, error, }); const setFavoriteSuccess = ( data: Favorite[], keys: FavoritesKeys, ): SetFavoriteSuccessAction => ({ type: FavoritesActionTypes.SET_FAVORITE_SUCCESS, data, keys, }); const setFavoriteFromFavorite = ( keys: FavoritesKeys, ): SetFavoritesFromFavoritesAction => ({ type: FavoritesActionTypes.SET_FAVORITE_FROM_FAVORITE, keys, }); const unsetFavoriteError = ( error: FavoritesError, ): UnsetFavoriteErrorAction => ({ type: FavoritesActionTypes.UNSET_FAVORITE_ERROR, error, }); const unsetFavoriteSuccess = ( data: Favorite[], keys: FavoritesKeys, ): UnsetFavoriteSuccessAction => ({ type: FavoritesActionTypes.UNSET_FAVORITE_SUCCESS, data, keys, }); export function getFavorites( client: ApolloClient, ): ThunkAction< Promise, AppState, {}, | GetFavoritesLoadingAction | GetFavoritesSuccessAction | GetFavoritesErrorAction > { return async (dispatch): Promise => { dispatch(getFavoritesLoading()); try { const options: QueryOptions = { query: GET_FAVORITES, }; const { data: { favorites: data }, } = await client.query(options); const keys = data.reduce( (obj: object, item: Favorite) => Object.assign(obj, { [item.id]: true }), {}, ); dispatch(getFavoritesSuccess(data, keys)); } catch (error) { dispatch(getFavoritesError(error)); } }; } export function pruneFavorites(): ThunkAction< Promise, AppState, {}, SetFavoriteSuccessAction > { return async (dispatch, getState): Promise => { const { favorites } = await getState(); const data = [...favorites.data].filter( favorite => favorites.keys[favorite.id], ); dispatch(setFavoriteSuccess([...data], { ...favorites.keys })); }; } export function setFavorite( id: string, category: FavoriteCategory, client: ApolloClient, sourcePath: FavoritesSourcePaths, ): ThunkAction< Promise, AppState, {}, | SetFavoriteErrorAction | SetFavoriteSuccessAction | SetFavoritesAlertAction | SetFavoritesFromFavoritesAction > { return async (dispatch, getState): Promise => { try { const { favorites } = await getState(); const { data, keys } = favorites; const options: MutationOptions = { mutation: SET_FAVORITE, variables: { setFavoriteInput: { id, category }, }, }; const { data: response } = await client.mutate(options); if (!response || !response.setFavorite) { const error = 'Error setting favorite: invalid Apollo response'; dispatch(setFavoriteError(error)); throw new Error(error); } if (sourcePath !== FavoritesSourcePaths.FAVORITES_PAGE) { dispatch( setFavoriteSuccess([{ ...response.setFavorite }, ...data], { ...keys, [id]: true, }), ); } else { dispatch(setFavoriteFromFavorite({ ...keys, [id]: true })); } dispatch( setFavoritesAlert({ didSet: true, isVisible: true, }), ); setTimeout(() => { dispatch( setFavoritesAlert({ didSet: true, isVisible: false, }), ); }, 4000); } catch (error) { dispatch(setFavoriteError(error)); throw new Error(error); } }; } export function setFilters( filters: FavoritesFilters, ): ThunkAction, AppState, {}, SetFavoritesFiltersAction> { return async dispatch => { dispatch(setFavoritesFilters(filters)); }; } export function unsetFavorite( id: string, client: ApolloClient, sourcePath: FavoritesSourcePaths, ): ThunkAction< Promise, AppState, {}, | UnsetFavoriteErrorAction | UnsetFavoriteSuccessAction | SetFavoritesAlertAction > { return async (dispatch, getState) => { try { const { favorites } = await getState(); const { data: prevData, keys: prevKeys } = favorites; const options: MutationOptions = { mutation: UNSET_FAVORITE, variables: { unsetFavoriteInput: id, }, }; const { data: response } = await client.mutate(options); if ((response && !response.unsetFavorite) || !response) { const error = 'Error unsetting favorite: invalid Apollo response'; dispatch(unsetFavoriteError(error)); throw new Error(error); } const prevFavoriteIndex = prevData.findIndex( (favorite: Favorite) => favorite.id === id, ); const data = [...prevData]; const keys = { ...prevKeys }; if (sourcePath !== FavoritesSourcePaths.FAVORITES_PAGE) { data.splice(prevFavoriteIndex, 1); } delete keys[id]; dispatch(unsetFavoriteSuccess(data, keys)); dispatch( setFavoritesAlert({ didSet: false, isVisible: true, }), ); setTimeout(() => { dispatch( setFavoritesAlert({ didSet: false, isVisible: false, }), ); }, 4000); } catch (error) { dispatch(unsetFavoriteError(error)); throw new Error(error); } }; }