diff --git a/src/actions/authentication.ts b/src/actions/authentication.ts index 970702d..33e9b4b 100644 --- a/src/actions/authentication.ts +++ b/src/actions/authentication.ts @@ -1,4 +1,13 @@ import { Action } from 'redux' +import { normalize } from 'normalizr' + +import { apiFetch } from 'src/api' +import { setEntities } from 'src/actions/entities' +import { startRequest, finishRequest } from 'src/actions/requests' +import { userSchema } from 'src/store/schemas' + +import { REQUEST_KEYS } from 'src/constants' +import { AppThunkAction, Entity } from 'src/types' export interface SetAuthenticatedAction extends Action { type: 'AUTHENTICATION_SET_AUTHENTICATED' @@ -21,3 +30,24 @@ export const setUser = (userId: string): SetUserAction => ({ type: 'AUTHENTICATION_SET_USER', payload: userId, }) + +export const fetchSelf = (): AppThunkAction => async dispatch => { + dispatch(startRequest(REQUEST_KEYS.FETCH_GROUP_AVAILABILITY)) + + try { + const self = await apiFetch({ + path: '/api/self', + }) + + const result = normalize(self, userSchema) + + dispatch(setEntities(result.entities)) + dispatch(setUser(self.id)) + dispatch(setAuthenticated(true)) + + dispatch(finishRequest(REQUEST_KEYS.FETCH_GROUP_AVAILABILITY, true)) + } catch (err) { + dispatch(finishRequest(REQUEST_KEYS.FETCH_GROUP_AVAILABILITY, false)) + throw err + } +} diff --git a/src/actions/directory.ts b/src/actions/directory.ts index 4b5603a..a8450bf 100644 --- a/src/actions/directory.ts +++ b/src/actions/directory.ts @@ -1,14 +1,14 @@ import { Action } from 'redux' import { normalize } from 'normalizr' -import { getGroup, getGroups } from '../api/groups' -import { setEntity, setEntities } from '../actions/entities' -import { startRequest, finishRequest } from '../actions/requests' -import { groupSchema } from '../store/schemas' -import { AppThunkAction } from '../types' +import { apiFetch } from 'src/api' +import { setEntity, setEntities } from 'src/actions/entities' +import { startRequest, finishRequest } from 'src/actions/requests' +import { groupSchema } from 'src/store/schemas' -const FETCH_GROUP_ID = 'FETCH_GROUP' -const FETCH_GROUPS_ID = 'FETCH_GROUPS' +import { REQUEST_KEYS } from 'src/constants' +import { objectToQuerystring } from 'src/utils' +import { AppThunkAction, Entity } from 'src/types' export interface SetGroupsAction extends Action { type: 'DIRECTORY_SET_GROUPS' @@ -44,38 +44,47 @@ export const setContinuation = (continuation: string): SetContinuationAction => export const fetchGroup = (id: string): AppThunkAction => { return async dispatch => { - dispatch(startRequest(FETCH_GROUP_ID)) + dispatch(startRequest(REQUEST_KEYS.FETCH_GROUP)) try { - const group = await getGroup(id) + const group = await apiFetch({ + path: `/api/group/${id}` + }) + dispatch(setEntity('group', group)) - dispatch(finishRequest(FETCH_GROUP_ID, true)) + dispatch(finishRequest(REQUEST_KEYS.FETCH_GROUP, true)) } catch (err) { - dispatch(finishRequest(FETCH_GROUP_ID, false)) + dispatch(finishRequest(REQUEST_KEYS.FETCH_GROUP, false)) throw err } } } -export const fetchGroups = (sort?: string, continuation?: string): AppThunkAction => { - return async dispatch => { - dispatch(startRequest(FETCH_GROUPS_ID)) +interface GroupsResponse { + groups: Entity[] + continuation?: string +} - try { - const response = await getGroups(sort, continuation) - const groups = normalize(response.groups, [groupSchema]) +export const fetchGroups = (sort?: string, continuation?: string): AppThunkAction => async dispatch => { + dispatch(startRequest(REQUEST_KEYS.FETCH_GROUPS)) - dispatch(setEntities(groups.entities)) - dispatch(setGroups(groups.result)) + try { + const response = await apiFetch({ + path: `/api/groups?${objectToQuerystring({ sort, continuation })}` + }) - if (response.continuation) { - dispatch(setContinuation(response.continuation)) - } + const groups = normalize(response.groups, [groupSchema]) - dispatch(finishRequest(FETCH_GROUPS_ID, true)) - } catch (err) { - dispatch(finishRequest(FETCH_GROUPS_ID, false)) - throw err + dispatch(setEntities(groups.entities)) + dispatch(setGroups(groups.result)) + + if (response.continuation) { + dispatch(setContinuation(response.continuation)) } + + dispatch(finishRequest(REQUEST_KEYS.FETCH_GROUPS, true)) + } catch (err) { + dispatch(finishRequest(REQUEST_KEYS.FETCH_GROUPS, false)) + throw err } } diff --git a/src/actions/registration.ts b/src/actions/registration.ts index 330472c..b2d473e 100644 --- a/src/actions/registration.ts +++ b/src/actions/registration.ts @@ -1,22 +1,16 @@ -import { Action, AnyAction } from 'redux' -import { ThunkAction } from 'redux-thunk' +import { Action } from 'redux' +import { apiFetch } from 'src/api' import { setFieldNotification } from 'src/actions/forms' import { startRequest, finishRequest } from 'src/actions/requests' -import { createGroup as fetchCreateGroup } from 'src/api/groups' -import { fetchGroupAvailability, register as fetchRegister } from 'src/api/registration' import { LOCAL_STORAGE_ACCESS_TOKEN_KEY, - LOCAL_STORAGE_ACCESS_TOKEN_AT_KEY, LOCAL_STORAGE_REFRESH_TOKEN_KEY, + REQUEST_KEYS, } from 'src/constants' -import { AppState, AppThunkAction } from 'src/types' -const FETCH_GROUP_AVAILABILITY_ID = 'FETCH_GROUP_AVAILABILITY' -const FETCH_USER_AVAILABILITY_ID = 'FETCH_USER_AVAILABILITY' -const CREATE_GROUP_ID = 'CREATE_GROUP' -const REGISTER_ID = 'REGISTER' +import { AppThunkAction } from 'src/types' export interface SetStepAction extends Action { type: 'REGISTRATION_SET_STEP' @@ -25,84 +19,141 @@ export interface SetStepAction extends Action { export type RegistrationActions = SetStepAction +interface AvailabilityResponse { + id: string + available: boolean +} + export const setStep = (step: number): SetStepAction => ({ type: 'REGISTRATION_SET_STEP', payload: step, }) -export const checkGroupAvailability = (name: string): AppThunkAction => { - return async dispatch => { - dispatch(startRequest(FETCH_GROUP_AVAILABILITY_ID)) - - try { - const response = await fetchGroupAvailability(name) +export const checkGroupAvailability = (name: string): AppThunkAction => async dispatch => { + dispatch(startRequest(REQUEST_KEYS.FETCH_GROUP_AVAILABILITY)) + + try { + const { id, available } = await apiFetch({ + path: '/api/group/available', + method: 'post', + body: { + name, + }, + }) + + if (available) { + dispatch(setFieldNotification('group-name', 'success', `${id} is available`)) + } else { + dispatch(setFieldNotification('group-name', 'error', `${id} isn't available`)) + } - if (response.available) { - dispatch(setFieldNotification('group-name', 'success', `${response.id} is available`)) - } else { - dispatch(setFieldNotification('group-name', 'error', `${response.id} isn't available`)) - } + dispatch(finishRequest(REQUEST_KEYS.FETCH_GROUP_AVAILABILITY, true)) + } catch (err) { + dispatch(finishRequest(REQUEST_KEYS.FETCH_GROUP_AVAILABILITY, false)) + throw err + } +} - dispatch(finishRequest(FETCH_GROUP_AVAILABILITY_ID, true)) - } catch (err) { - dispatch(finishRequest(FETCH_GROUP_AVAILABILITY_ID, false)) - throw err +export const checkUserAvailability = (name: string): AppThunkAction => async dispatch => { + dispatch(startRequest(REQUEST_KEYS.FETCH_USER_AVAILABILITY)) + + try { + const { id, available } = await apiFetch({ + path: '/api/user/available', + method: 'post', + body: { + name, + }, + }) + + if (available) { + dispatch(setFieldNotification('user-id', 'success', `${id} is available`)) + } else { + dispatch(setFieldNotification('user-id', 'error', `${id} isn't available`)) } + + dispatch(finishRequest(REQUEST_KEYS.FETCH_USER_AVAILABILITY, true)) + } catch (err) { + dispatch(finishRequest(REQUEST_KEYS.FETCH_USER_AVAILABILITY, false)) + throw err } } -export const checkUserAvailability = (name: string): AppThunkAction => { - return async dispatch => { - dispatch(startRequest(FETCH_USER_AVAILABILITY_ID)) +interface CreateGroupOptions { + name: string + registration: string + about?: string +} - try { - const { id, available } = await fetchGroupAvailability(name) +interface CreateGroupResponse { + id: string +} - if (available) { - dispatch(setFieldNotification('user-id', 'success', `${id} is available`)) - } else { - dispatch(setFieldNotification('user-id', 'error', `${id} isn't available`)) - } +export const createGroup = (options: CreateGroupOptions): AppThunkAction => async dispatch => { + const { name, registration, about } = options - dispatch(finishRequest(FETCH_USER_AVAILABILITY_ID, true)) - } catch (err) { - dispatch(finishRequest(FETCH_USER_AVAILABILITY_ID, false)) - throw err - } - } -} + dispatch(startRequest(REQUEST_KEYS.CREATE_GROUP)) -export const createGroup = (name: string, registration: string, about?: string): ThunkAction, AppState, void, AnyAction> => { - return async dispatch => { - dispatch(startRequest(CREATE_GROUP_ID)) + try { + const { id } = await apiFetch({ + path: '/api/group', + method: 'post', + body: { + name, + registration, + about, + }, + }) - try { - const { id } = await fetchCreateGroup(name, registration, about) - dispatch(finishRequest(CREATE_GROUP_ID, true)) + dispatch(finishRequest(REQUEST_KEYS.CREATE_GROUP, true)) - return id - } catch (err) { - dispatch(finishRequest(CREATE_GROUP_ID, false)) - throw err - } + return id + } catch (err) { + dispatch(finishRequest(REQUEST_KEYS.CREATE_GROUP, false)) + throw err } } -export const register = (id: string, email: string, password: string, name?: string, group?: string): ThunkAction, AppState, void, AnyAction> => { - return async dispatch => { - dispatch(startRequest(REGISTER_ID)) - - try { - const response = await fetchRegister(id, email, password, name, group) - dispatch(finishRequest(REGISTER_ID, true)) +interface RegisterOptions { + id: string + email: string + password: string + name?: string + group?: string +} - localStorage.setItem(LOCAL_STORAGE_ACCESS_TOKEN_KEY, response.access) - localStorage.setItem(LOCAL_STORAGE_REFRESH_TOKEN_KEY, response.refresh) +interface RegisterResponse { + id: string + access: string + refresh: string +} - return response.id - } catch (err) { - dispatch(finishRequest(REGISTER_ID, false)) - throw err - } +export const register = (options: RegisterOptions): AppThunkAction => async dispatch => { + const { id, email, password, name, group } = options + + dispatch(startRequest(REQUEST_KEYS.REGISTER)) + + try { + const response = await apiFetch({ + path: '/api/register', + method: 'post', + body: { + id, + email, + password, + name, + group, + }, + }) + + dispatch(finishRequest(REQUEST_KEYS.REGISTER, true)) + + localStorage.setItem(LOCAL_STORAGE_ACCESS_TOKEN_KEY, response.access) + localStorage.setItem(LOCAL_STORAGE_REFRESH_TOKEN_KEY, response.refresh) + + return response.id + } catch (err) { + dispatch(finishRequest(REQUEST_KEYS.REGISTER, false)) + throw err } } diff --git a/src/api/errors.ts b/src/api/errors.ts index 513d7d6..4a2ba59 100644 --- a/src/api/errors.ts +++ b/src/api/errors.ts @@ -1,5 +1,6 @@ import { History } from 'history' +import { setFieldNotification } from 'src/actions/forms' import { showNotification } from 'src/actions/notifications' import { AppThunkDispatch, FormNotification } from 'src/types' @@ -10,6 +11,11 @@ export function handleApiError(err: HttpError, dispatch: AppThunkDispatch, histo if (err instanceof BadRequestError) { dispatch(showNotification('error', `Error: ${err.message}`)) + + for (const error of err.errors) { + const { field, type, message } = error + if (field) dispatch(setFieldNotification(field, type, message)) + } } if (err instanceof UnauthorizedError) { diff --git a/src/api/fetch.ts b/src/api/fetch.ts index bc046a8..3461665 100644 --- a/src/api/fetch.ts +++ b/src/api/fetch.ts @@ -68,7 +68,7 @@ const getResponseData = async (response: Response) => { } } -const apiFetch: APIFetch = async (options: FetchOptions) => { +export const apiFetch: APIFetch = async (options: FetchOptions) => { const { path, method = 'get', body } = options const contentType = 'application/json' const config = await getConfig() @@ -137,7 +137,3 @@ const apiFetch: APIFetch = async (options: FetchOptions) => { return await checkResponse(response, doRefresh) } - -export { - apiFetch as fetch, -} diff --git a/src/api/groups.ts b/src/api/groups.ts deleted file mode 100644 index b5169d7..0000000 --- a/src/api/groups.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { fetch } from './fetch' -import { Entity } from '../types' - -interface GroupsResponse { - groups: Entity[] - continuation?: string -} - -interface NewGroupResponse { - id: string -} - -export async function getGroup(id: string) { - return await fetch({ - path: `/api/group/${id}` - }) -} - -export async function getGroups(sort: string = 'members', continuation?: string) { - const params = { - sort, - continuation, - } - - const querystring = Object.entries(params).filter(([name, value]) => value !== undefined).map(([name, value]) => `${name}=${value}`).join('&') - - return await fetch({ - path: `/api/groups?${querystring}` - }) -} - -export async function createGroup(name: string, registration: string, about?: string) { - return await fetch({ - path: '/api/group', - method: 'post', - body: { - name, - registration, - about, - }, - }) -} diff --git a/src/api/index.ts b/src/api/index.ts new file mode 100644 index 0000000..3c82583 --- /dev/null +++ b/src/api/index.ts @@ -0,0 +1 @@ +export { apiFetch } from './fetch' diff --git a/src/api/registration.ts b/src/api/registration.ts deleted file mode 100644 index 5eaffdc..0000000 --- a/src/api/registration.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { fetch } from './fetch' - -interface AvailabilityResponse { - id: string - available: boolean -} - -interface RegisterResponse { - id: string - access: string - refresh: string -} - -export async function fetchGroupAvailability(name: string) { - return await fetch({ - path: '/api/group/available', - method: 'post', - body: { - name, - }, - }) -} - -export async function fetchUserAvailability(name: string) { - return await fetch({ - path: '/api/user/available', - method: 'post', - body: { - name, - }, - }) -} - -export async function register(id: string, email: string, password: string, name?: string, group?: string) { - return await fetch({ - path: '/api/register', - method: 'post', - body: { - id, - email, - password, - name, - group, - }, - }) -} diff --git a/src/api/users.ts b/src/api/users.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/components/app/app.scss b/src/components/app/app.scss index d666bd0..7e638a8 100644 --- a/src/components/app/app.scss +++ b/src/components/app/app.scss @@ -5,7 +5,7 @@ // Colors $orange: hsl(14, 100%, 53%); $yellow: hsl(48, 100%, 67%); -$green: hsl(141, 71%, 48%); +$green: hsl(141, 65%, 31%); $turquoise: hsl(171, 100%, 41%); $cyan: hsl(204, 86%, 53%); $blue: hsl(217, 72%, 30%); diff --git a/src/components/app/app.tsx b/src/components/app/app.tsx index e93eb56..92ca44b 100644 --- a/src/components/app/app.tsx +++ b/src/components/app/app.tsx @@ -1,6 +1,8 @@ -import React, { FC } from 'react' +import React, { FC, useEffect } from 'react' import { BrowserRouter as Router, Route, Link } from 'react-router-dom' +import { LOCAL_STORAGE_ACCESS_TOKEN_KEY } from 'src/constants' + import Footer from '../footer' import NavigationMenu from '../navigation-menu' import NotificationContainer from '../notification-container' @@ -9,11 +11,12 @@ import UserInfo from '../user-info' import About from '../pages/about' import Developers from '../pages/developers' +import Directory from '../pages/directory' import Home from '../pages/home' import Login from '../pages/login' import Register from '../pages/register' import RegisterGroup from '../pages/register-group' -import Directory from '../pages/directory' +import Self from '../pages/self' import Test from '../pages/test' import './app.scss' @@ -21,12 +24,19 @@ import './app.scss' interface Props { collapsed: boolean fetching: boolean + fetchSelf: () => void } -const App: FC = ({ collapsed, fetching }) => { +const App: FC = ({ collapsed, fetching, fetchSelf }) => { const mainMenuWidth = 250 const mainColumnLeftMargin = collapsed ? 0 : mainMenuWidth + useEffect(() => { + if (localStorage.getItem(LOCAL_STORAGE_ACCESS_TOKEN_KEY)) { + fetchSelf() + } + }, []) + return (
@@ -49,6 +59,7 @@ const App: FC = ({ collapsed, fetching }) => { + diff --git a/src/components/app/index.ts b/src/components/app/index.ts index 525874c..fd025e5 100644 --- a/src/components/app/index.ts +++ b/src/components/app/index.ts @@ -1,8 +1,10 @@ import { connect } from 'react-redux' +import { fetchSelf } from 'src/actions/authentication' import { getFetching } from 'src/selectors' import { getCollapsed } from 'src/selectors/menu' -import { AppState } from 'src/types' + +import { AppState, AppThunkDispatch } from 'src/types' import App from './app' @@ -11,6 +13,13 @@ const mapStateToProps = (state: AppState) => ({ fetching: getFetching(state), }) +const mapDispatchToProps = (dispatch: AppThunkDispatch) => ({ + fetchSelf: () => { + dispatch(fetchSelf()) + } +}) + export default connect( - mapStateToProps + mapStateToProps, + mapDispatchToProps )(App) diff --git a/src/components/create-group-form/index.ts b/src/components/create-group-form/index.ts index 61c3a04..3dfc395 100644 --- a/src/components/create-group-form/index.ts +++ b/src/components/create-group-form/index.ts @@ -2,7 +2,7 @@ import { FocusEventHandler } from 'react' import { connect } from 'react-redux' import { checkGroupAvailability } from 'src/actions/registration' -import { AppState, AppThunkDispatch } from 'src/types' +import { AppThunkDispatch } from 'src/types' import CreateGroupForm from './create-group-form' diff --git a/src/components/forms/password-field/password-field.tsx b/src/components/forms/password-field/password-field.tsx index 365c676..4610df2 100644 --- a/src/components/forms/password-field/password-field.tsx +++ b/src/components/forms/password-field/password-field.tsx @@ -56,7 +56,7 @@ const PasswordField: FC = ({ inputClassDictionary['is-success'] = true controlClassDictionary['has-icons-right'] = true icon = faCheckCircle - passwordMessage = Strength: 🔥 + passwordMessage = Strength: LIT break } } diff --git a/src/components/forms/select-field/select-field.tsx b/src/components/forms/select-field/select-field.tsx index 21ae29c..a5c81e9 100644 --- a/src/components/forms/select-field/select-field.tsx +++ b/src/components/forms/select-field/select-field.tsx @@ -1,7 +1,9 @@ import React, { FC } from 'react' +import classNames from 'classnames' import noop from 'lodash/noop' -import { FormNotification } from 'src/types' +import { notificationTypeToClassName } from 'src/utils' +import { FormNotification, ClassDictionary } from 'src/types' interface SelectOptions { [value: string]: string @@ -20,18 +22,33 @@ const SelectField: FC = ({ label, options = {}, value = '', + notification, setValue = noop, }) => { const opts = Object.entries(options) + const controlClassDictionary: ClassDictionary = { select: true, 'is-small': true } + const helpClassDictionary: ClassDictionary = { help: true } + + console.log('notification', notification) + + if (notification) { + const ncn = notificationTypeToClassName(notification.type) + + controlClassDictionary[ncn] = true + helpClassDictionary[ncn] = true + } return (
-
+
+ {notification && +

{notification.message}

+ }
) } diff --git a/src/components/forms/text-field/text-field.tsx b/src/components/forms/text-field/text-field.tsx index ed0b99f..c35e708 100644 --- a/src/components/forms/text-field/text-field.tsx +++ b/src/components/forms/text-field/text-field.tsx @@ -29,14 +29,13 @@ const TextField: FC = ({ setValue = noop, onBlur = noop, }) => { - const controlClassNames = classNames({ control: true, 'has-icons-left': !!icon }) - const helpClassDictionary: ClassDictionary = {} + const controlClassDictionary = { control: true, 'has-icons-left': !!icon } + const helpClassDictionary: ClassDictionary = { help: true } const inputClassDictionary: ClassDictionary = { input: true } if (notification) { const ncn = notificationTypeToClassName(notification.type) - helpClassDictionary['help'] = true helpClassDictionary[ncn] = true inputClassDictionary[ncn] = true } @@ -44,7 +43,7 @@ const TextField: FC = ({ return (
-
+
setValue(e.target.value)} onBlur={onBlur} /> {icon && diff --git a/src/components/pages/register/index.ts b/src/components/pages/register/index.ts index e69d4bb..cbf80aa 100644 --- a/src/components/pages/register/index.ts +++ b/src/components/pages/register/index.ts @@ -1,5 +1,6 @@ import { connect } from 'react-redux' +import { handleApiError } from 'src/api/errors' import { getForm } from 'src/selectors/forms' import { getStep } from 'src/selectors/registration' import { initForm, initField } from 'src/actions/forms' @@ -37,20 +38,24 @@ const mapDispatchToProps = (dispatch: AppThunkDispatch, ownProps: Props) => ({ return } - const groupId = await dispatch(createGroup( - valueFromForm(form, 'group-name', ''), - valueFromForm(form, 'group-registration', '') - )) - - const userId = await dispatch(register( - valueFromForm(form, 'user-id', ''), - valueFromForm(form, 'user-email', ''), - valueFromForm(form, 'password', ''), - valueFromForm(form, 'user-name', ''), - groupId - )) - - ownProps.history.push('/self') + try { + const group = await dispatch(createGroup({ + name: valueFromForm(form, 'group-name', ''), + registration: valueFromForm(form, 'group-registration', ''), + })) + + await dispatch(register({ + id: valueFromForm(form, 'user-id', ''), + email: valueFromForm(form, 'user-email', ''), + password: valueFromForm(form, 'password', ''), + name: valueFromForm(form, 'user-name', ''), + group, + })) + + ownProps.history.push('/self') + } catch (err) { + handleApiError(err, dispatch, ownProps.history) + } } }) diff --git a/src/components/pages/self/index.ts b/src/components/pages/self/index.ts new file mode 100644 index 0000000..5d46a00 --- /dev/null +++ b/src/components/pages/self/index.ts @@ -0,0 +1,23 @@ + +import { connect } from 'react-redux' +import { getAuthenticatedUserId } from 'src/selectors/authentication' +import { getEntity } from 'src/selectors/entities' +import { AppState, AppThunkDispatch } from 'src/types' + +import Self from './self' + +const mapStateToProps = (state: AppState) => { + const userId = getAuthenticatedUserId(state) + const user = userId ? getEntity(state, 'user', userId) : undefined + + return { + user, + } +} + +const mapDispatchToProps = (dispatch: AppThunkDispatch) => ({}) + +export default connect( + mapStateToProps, + mapDispatchToProps +)(Self) diff --git a/src/components/pages/self/self.tsx b/src/components/pages/self/self.tsx new file mode 100644 index 0000000..e5551de --- /dev/null +++ b/src/components/pages/self/self.tsx @@ -0,0 +1,30 @@ +import React, { FC, useEffect } from 'react' +import { setTitle } from 'src/utils' + +import { Entity } from 'src/types' + +import PageHeader from 'src/components/page-header' + +interface Props { + user?: Entity +} + +const Self: FC = ({ user }) => { + useEffect(() => { + if (user) setTitle(user.name as string) + }, [user]) + + return ( +
+ + +
+

+ Hello. +

+
+
+ ) +} + +export default Self diff --git a/src/constants/index.ts b/src/constants/index.ts index cc8b9bf..5ecf24d 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -6,5 +6,10 @@ export const LOCAL_STORAGE_ACCESS_TOKEN_AT_KEY = 'FLEXOR_ACCESS_TOKEN_AT' export const LOCAL_STORAGE_REFRESH_TOKEN_KEY = 'FLEXOR_REFRESH_TOKEN' export const REQUEST_KEYS = { - + FETCH_GROUP: 'FETCH_GROUP', + FETCH_GROUPS: 'FETCH_GROUPS', + FETCH_GROUP_AVAILABILITY: 'FETCH_GROUP_AVAILABILITY', + FETCH_USER_AVAILABILITY: 'FETCH_USER_AVAILABILITY', + CREATE_GROUP: 'CREATE_GROUP', + REGISTER: 'REGISTER', } diff --git a/src/selectors/authentication.ts b/src/selectors/authentication.ts index 552d21d..117ffab 100644 --- a/src/selectors/authentication.ts +++ b/src/selectors/authentication.ts @@ -1,3 +1,4 @@ import { AppState } from '../types' export const getAuthenticated = (state: AppState) => state.authentication.authenticated +export const getAuthenticatedUserId = (state: AppState) => state.authentication.userId diff --git a/src/types/index.ts b/src/types/index.ts index beb1991..8348379 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -44,4 +44,4 @@ export { } from './store' export type AppThunkDispatch = ThunkDispatch -export type AppThunkAction = ThunkAction, AppState, void, AnyAction> +export type AppThunkAction = ThunkAction, AppState, void, AnyAction> diff --git a/src/utils/index.ts b/src/utils/index.ts index 9d1aabd..2ac9eeb 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -8,6 +8,8 @@ export function notificationTypeToClassName(type: NotificationType): string { } } +export const objectToQuerystring = (obj: object) => Object.entries(obj).filter(([name, value]) => value !== undefined).map(([name, value]) => `${name}=${value}`).join('&') + export function setTitle(title: string, decorate: boolean = true) { if (decorate) { document.title = `${title} | Flexor`