import { Action } from 'redux' import { apiFetch } from '../api' import { setEntities } from '../actions/entities' import { startRequest, finishRequest } from '../actions/requests' import { normalize } from '../utils/normalization' import { LOCAL_STORAGE_ACCESS_TOKEN_KEY, LOCAL_STORAGE_REFRESH_TOKEN_KEY, LOCAL_STORAGE_ACCESS_TOKEN_EXPIRES_AT_KEY, } from '../constants' import { AppThunkAction, Entity, RequestKey, EntityType, Settings } from '../types' export interface SetCheckedAction extends Action { type: 'AUTHENTICATION_SET_CHECKED' } export interface SetAuthenticatedAction extends Action { type: 'AUTHENTICATION_SET_AUTHENTICATED' payload: boolean } export interface SetUserAction extends Action { type: 'AUTHENTICATION_SET_USER' payload: string } export interface UnauthenticateAction extends Action { type: 'AUTHENTICATION_UNAUTHENTICATE' } export type AuthenticationActions = SetCheckedAction | SetAuthenticatedAction | SetUserAction | UnauthenticateAction export const setChecked = (): SetCheckedAction => ({ type: 'AUTHENTICATION_SET_CHECKED', }) export const setAuthenticated = (authenticated: boolean): SetAuthenticatedAction => ({ type: 'AUTHENTICATION_SET_AUTHENTICATED', payload: authenticated, }) export const setUser = (userId: string): SetUserAction => ({ type: 'AUTHENTICATION_SET_USER', payload: userId, }) export const unauthenticate = (): UnauthenticateAction => ({ type: 'AUTHENTICATION_UNAUTHENTICATE', }) export const fetchSelf = (): AppThunkAction => async dispatch => { dispatch(startRequest(RequestKey.FetchSelf)) try { const self = await apiFetch({ path: '/v1/self', }) const result = normalize([self], EntityType.User) dispatch(setEntities(result.entities)) dispatch(setUser(self.id)) dispatch(setAuthenticated(true)) dispatch(finishRequest(RequestKey.FetchSelf, true)) } catch (err) { dispatch(setAuthenticated(false)) dispatch(finishRequest(RequestKey.FetchSelf, false)) throw err } } interface AuthenticateResponse { id: string access: string refresh: string expires: number } export const authenticate = (name: string, password: string): AppThunkAction => async dispatch => { dispatch(startRequest(RequestKey.Authenticate)) try { const response = await apiFetch({ path: '/v1/authenticate', method: 'post', body: { id: name, password, }, }) localStorage.setItem(LOCAL_STORAGE_ACCESS_TOKEN_KEY, response.access) localStorage.setItem(LOCAL_STORAGE_REFRESH_TOKEN_KEY, response.refresh) if (response.expires) localStorage.setItem(LOCAL_STORAGE_ACCESS_TOKEN_EXPIRES_AT_KEY, response.expires.toString()) dispatch(finishRequest(RequestKey.Authenticate, true)) await dispatch(fetchSelf()) return response.id } catch (err) { dispatch(finishRequest(RequestKey.Authenticate, false)) throw err } } interface UpdateSelfOptions { name: string about: string requiresApproval: boolean privacy: string imageUrl: string coverImageUrl: string theme: string settings: Settings } export const updateSelf = (options: UpdateSelfOptions): AppThunkAction => async dispatch => { const { name, about, requiresApproval, privacy, imageUrl, coverImageUrl, theme, settings } = options dispatch(startRequest(RequestKey.UpdateSelf)) try { const self = await apiFetch({ path: '/v1/self', method: 'put', body: { name, about, requiresApproval, privacy, imageUrl, coverImageUrl, theme, settings, }, }) const result = normalize([self], EntityType.User) dispatch(setEntities(result.entities)) dispatch(setUser(self.id)) dispatch(setAuthenticated(true)) dispatch(finishRequest(RequestKey.UpdateSelf, true)) } catch (err) { dispatch(finishRequest(RequestKey.UpdateSelf, false)) throw err } }