Skip to content
Snippets Groups Projects
Commit 90ebff83 authored by Kamile Liucija Vainiute's avatar Kamile Liucija Vainiute
Browse files

auth for acad

parent c44249e0
No related branches found
No related tags found
No related merge requests found
Pipeline #713 passed
Showing
with 338 additions and 26 deletions
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
"axios": "^0.21.1", "axios": "^0.21.1",
"clsx": "^1.1.1", "clsx": "^1.1.1",
"external-svg-loader": "^1.3.3", "external-svg-loader": "^1.3.3",
"js-cookie": "^3.0.0",
"react": "^17.0.2", "react": "^17.0.2",
"react-animate-height": "^2.0.23", "react-animate-height": "^2.0.23",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
...@@ -38,6 +39,7 @@ ...@@ -38,6 +39,7 @@
"web-vitals": "^1.1.2" "web-vitals": "^1.1.2"
}, },
"devDependencies": { "devDependencies": {
"@types/js-cookie": "^2.2.7",
"@types/react-router-dom": "^5.1.8", "@types/react-router-dom": "^5.1.8",
"@types/react-transition-group": "^4.4.2", "@types/react-transition-group": "^4.4.2",
"autoprefixer": "^10.3.1", "autoprefixer": "^10.3.1",
...@@ -3632,6 +3634,12 @@ ...@@ -3632,6 +3634,12 @@
"pretty-format": "^26.0.0" "pretty-format": "^26.0.0"
} }
}, },
"node_modules/@types/js-cookie": {
"version": "2.2.7",
"resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-2.2.7.tgz",
"integrity": "sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA==",
"dev": true
},
"node_modules/@types/json-schema": { "node_modules/@types/json-schema": {
"version": "7.0.8", "version": "7.0.8",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.8.tgz", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.8.tgz",
...@@ -13609,6 +13617,14 @@ ...@@ -13609,6 +13617,14 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/js-cookie": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.0.tgz",
"integrity": "sha512-oUbbplKuH07/XX2YD2+Q+GMiPpnVXaRz8npE7suhBH9QEkJe2W7mQ6rwuMXHue3fpfcftQwzgyvGzIHyfCSngQ==",
"engines": {
"node": ">=12"
}
},
"node_modules/js-tokens": { "node_modules/js-tokens": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
...@@ -28510,6 +28526,12 @@ ...@@ -28510,6 +28526,12 @@
"pretty-format": "^26.0.0" "pretty-format": "^26.0.0"
} }
}, },
"@types/js-cookie": {
"version": "2.2.7",
"resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-2.2.7.tgz",
"integrity": "sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA==",
"dev": true
},
"@types/json-schema": { "@types/json-schema": {
"version": "7.0.8", "version": "7.0.8",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.8.tgz", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.8.tgz",
...@@ -36079,6 +36101,11 @@ ...@@ -36079,6 +36101,11 @@
} }
} }
}, },
"js-cookie": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.0.tgz",
"integrity": "sha512-oUbbplKuH07/XX2YD2+Q+GMiPpnVXaRz8npE7suhBH9QEkJe2W7mQ6rwuMXHue3fpfcftQwzgyvGzIHyfCSngQ=="
},
"js-tokens": { "js-tokens": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
"axios": "^0.21.1", "axios": "^0.21.1",
"clsx": "^1.1.1", "clsx": "^1.1.1",
"external-svg-loader": "^1.3.3", "external-svg-loader": "^1.3.3",
"js-cookie": "^3.0.0",
"react": "^17.0.2", "react": "^17.0.2",
"react-animate-height": "^2.0.23", "react-animate-height": "^2.0.23",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
...@@ -76,6 +77,7 @@ ...@@ -76,6 +77,7 @@
] ]
}, },
"devDependencies": { "devDependencies": {
"@types/js-cookie": "^2.2.7",
"@types/react-router-dom": "^5.1.8", "@types/react-router-dom": "^5.1.8",
"@types/react-transition-group": "^4.4.2", "@types/react-transition-group": "^4.4.2",
"autoprefixer": "^10.3.1", "autoprefixer": "^10.3.1",
......
...@@ -30,35 +30,35 @@ export const modules: ModuleProps[] = [ ...@@ -30,35 +30,35 @@ export const modules: ModuleProps[] = [
color: 'pink', color: 'pink',
title: 'Design as a framework', title: 'Design as a framework',
description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Quis ipsum suspendisse ultrices gravida. Risus commodo viverra maecenas accumsan lacus vel facilisis.', description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Quis ipsum suspendisse ultrices gravida. Risus commodo viverra maecenas accumsan lacus vel facilisis.',
playlistId: 'njLC4K8YECTEbxDcuRkrg2' playlistId: '0e24478d-e0af-4729-a46b-bdebe75150b1'
}, },
{ {
module: Module.SYNBIO, module: Module.SYNBIO,
color: 'violet', color: 'violet',
title: 'Synthetic biology by design', title: 'Synthetic biology by design',
description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Quis ipsum suspendisse ultrices gravida. Risus commodo viverra maecenas accumsan lacus vel facilisis.', description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Quis ipsum suspendisse ultrices gravida. Risus commodo viverra maecenas accumsan lacus vel facilisis.',
playlistId: 'njLC4K8YECTEbxDcuRkrg2' playlistId: '0e24478d-e0af-4729-a46b-bdebe75150b1'
}, },
{ {
module: Module.HP, module: Module.HP,
color: 'blue', color: 'blue',
title: 'Human practices & social impact', title: 'Human practices & social impact',
description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Quis ipsum suspendisse ultrices gravida. Risus commodo viverra maecenas accumsan lacus vel facilisis.', description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Quis ipsum suspendisse ultrices gravida. Risus commodo viverra maecenas accumsan lacus vel facilisis.',
playlistId: 'njLC4K8YECTEbxDcuRkrg2' playlistId: '0e24478d-e0af-4729-a46b-bdebe75150b1'
}, },
{ {
module: Module.BIOSAFETY, module: Module.BIOSAFETY,
color: 'babyBlue', color: 'babyBlue',
title: 'Biosafety & biosecurity', title: 'Biosafety & biosecurity',
description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Quis ipsum suspendisse ultrices gravida. Risus commodo viverra maecenas accumsan lacus vel facilisis.', description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Quis ipsum suspendisse ultrices gravida. Risus commodo viverra maecenas accumsan lacus vel facilisis.',
playlistId: 'njLC4K8YECTEbxDcuRkrg2' playlistId: '0e24478d-e0af-4729-a46b-bdebe75150b1'
}, },
{ {
module: Module.ENTREPRENEURSHIP, module: Module.ENTREPRENEURSHIP,
color: 'green', color: 'green',
title: 'Entrepreneurship & innovation', title: 'Entrepreneurship & innovation',
description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Quis ipsum suspendisse ultrices gravida. Risus commodo viverra maecenas accumsan lacus vel facilisis.', description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Quis ipsum suspendisse ultrices gravida. Risus commodo viverra maecenas accumsan lacus vel facilisis.',
playlistId: 'njLC4K8YECTEbxDcuRkrg2' playlistId: '0e24478d-e0af-4729-a46b-bdebe75150b1'
} }
] ]
......
import React, { useState } from 'react'
import Modal from '../Modal'
import Button from '../Button'
import { ButtonType, ButtonVariant } from '../Button/Button'
type LoginAction = typeof import('../../state/user/userActions').login
export interface LoginStateProps {
isLoading: boolean
error: string | null
}
interface LoginDispatchProps {
login: LoginAction
}
interface LoginComponentProps {
isOpen: boolean
setIsOpen: (isOpen: boolean) => void
disableClosing?: boolean
}
type LoginProps = LoginComponentProps & LoginDispatchProps & LoginStateProps
const Login = (props: LoginProps): JSX.Element => {
const { isOpen, setIsOpen, login, error, disableClosing } = props
const [username, setUsername] = useState<string>('')
const [token, setToken] = useState<string>('')
const handleSubmitClick = (): void => {
if (username.length > 0 && token.length > 0) login(username, token)
}
return (
<Modal
isOpen={isOpen}
setIsOpen={
disableClosing !== undefined && disableClosing ? () => {} : setIsOpen
}
>
<div className='bg-black-dark p-5 rounded-xl w-72 h-max flex flex-col items-center justify-around'>
<p className='font-medium text-2xl text-white text-center'>Sign in</p>
<div className='text-center py-4'>
<label
className='text-center text-white font-raleway text-xl'
htmlFor='user'
>
User
</label>
<input
className='shadow appearance-none border rounded-xl w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline'
id='user'
type='text'
onChange={(value) => setUsername(value.target.value)}
/>
</div>
<div className='text-center py-4'>
<label
className='text-center text-white font-raleway text-xl'
htmlFor='token'
>
Token
</label>
<input
className='shadow appearance-none border rounded-xl w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline'
id='token'
type='text'
onChange={(value) => setToken(value.target.value)}
/>
</div>
<Button
variant={ButtonVariant.COLOURFUL}
type={ButtonType.BUTTON}
value='Enter'
classes='text-white font-medium text-xl'
onClick={handleSubmitClick}
/>
<p className='font-light text-xs text-pink'>{error}</p>
</div>
</Modal>
)
}
export default Login
import { connect } from 'react-redux'
import { RootState } from '../../state/store'
import Login from './Login'
import {
login
} from '../../state/user/userActions'
const mapStateToProps = (
state: RootState
): {isLoading: boolean, error: string | null} => ({
isLoading: state.userReducer.isLoading,
error: state.userReducer.error
})
const mapDispatchToProps = {
login
}
export default connect(mapStateToProps, mapDispatchToProps)(Login)
import React, { Fragment } from 'react' import React, { Fragment } from 'react'
import { Dialog, Transition } from '@headlessui/react' import { Dialog, Transition } from '@headlessui/react'
import clsx from 'clsx'
interface ModalProps { interface ModalProps {
isOpen: boolean isOpen: boolean
setIsOpen: (isOpen: boolean) => void setIsOpen: (isOpen: boolean) => void
children: JSX.Element children: JSX.Element
classes?: string
} }
const Modal = (props: ModalProps): JSX.Element => { const Modal = (props: ModalProps): JSX.Element => {
const { isOpen, setIsOpen, children } = props const { isOpen, setIsOpen, children, classes } = props
return ( return (
<Transition.Root show={isOpen} as={Fragment}> <Transition.Root show={isOpen} as={Fragment}>
<Dialog <Dialog
as='div' as='div'
static static
className='fixed z-10 inset-0 overflow-y-auto bg-black bg-opacity-90' className='fixed z-10 inset-0 overflow-y-auto bg-black-dark bg-opacity-90'
open={isOpen} open={isOpen}
onClose={setIsOpen} onClose={setIsOpen}
> >
...@@ -48,7 +50,12 @@ const Modal = (props: ModalProps): JSX.Element => { ...@@ -48,7 +50,12 @@ const Modal = (props: ModalProps): JSX.Element => {
leaveFrom='opacity-100 translate-y-0 sm:scale-100' leaveFrom='opacity-100 translate-y-0 sm:scale-100'
leaveTo='opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95' leaveTo='opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95'
> >
<div className='inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:m-8 sm:align-middle sm:max-w-screen-xl sm:w-full'> <div
className={clsx(
'inline-block align-bottom rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:m-8 sm:align-middle',
classes
)}
>
{children} {children}
</div> </div>
</Transition.Child> </Transition.Child>
......
...@@ -26,7 +26,7 @@ const NavLink = (props: { link: string, name: string }): JSX.Element => { ...@@ -26,7 +26,7 @@ const NavLink = (props: { link: string, name: string }): JSX.Element => {
const Navbar = React.forwardRef((props, ref): JSX.Element => { const Navbar = React.forwardRef((props, ref): JSX.Element => {
return ( return (
<div <div
className='w-full flex justify-between py-5 px-16 items-center' className='w-full flex justify-between py-5 px-16 items-center z-20'
ref={ref as LegacyRef<HTMLDivElement>} ref={ref as LegacyRef<HTMLDivElement>}
> >
<Button <Button
......
import React, { useEffect } from 'react' import React, { useEffect, useState } from 'react'
import Calendar from './Calendar' import Calendar from './Calendar'
import Login from '../../components/Login'
const Engage = (props: { const Engage = (props: {
navbarRef: React.RefObject<HTMLDivElement> navbarRef: React.RefObject<HTMLDivElement>
footerRef: React.RefObject<HTMLDivElement> footerRef: React.RefObject<HTMLDivElement>
isAuthenticated: boolean
}): JSX.Element => { }): JSX.Element => {
const { navbarRef, footerRef } = props const { navbarRef, footerRef, isAuthenticated } = props
const [modalIsOpen, setModalIsOpen] = useState<boolean>(!isAuthenticated)
useEffect(() => { useEffect(() => {
if (navbarRef?.current !== null) { if (navbarRef?.current !== null) {
navbarRef.current.classList.remove('absolute') navbarRef.current.classList.remove('absolute')
...@@ -16,10 +20,20 @@ const Engage = (props: { ...@@ -16,10 +20,20 @@ const Engage = (props: {
}, [navbarRef, footerRef]) }, [navbarRef, footerRef])
return ( return (
<div className='w-full max-w-screen-xl m-auto px-16 2xl:px-0'> <div className='w-full max-w-screen-xl m-auto px-16 2xl:px-0'>
<h1 className='text-3xl font-bold my-3'> {!isAuthenticated ? (
Engage with our live activities! <Login
</h1> isOpen={modalIsOpen}
<Calendar /> setIsOpen={setModalIsOpen}
disableClosing={!isAuthenticated}
/>
) : (
<>
<h1 className='text-3xl font-bold my-3'>
Engage with our live activities!
</h1>
<Calendar />
</>
)}
</div> </div>
) )
} }
......
export { default } from './Engage' import { connect } from 'react-redux'
import { RootState } from '../../state/store'
import Engage from './Engage'
const mapStateToProps = (
state: RootState
): {isAuthenticated: boolean} => ({
isAuthenticated: state.userReducer.isAuthenticated
})
export default connect(mapStateToProps)(Engage)
...@@ -3,12 +3,22 @@ import Button from '../../components/Button' ...@@ -3,12 +3,22 @@ import Button from '../../components/Button'
import { ButtonType, ButtonVariant } from '../../components/Button/Button' import { ButtonType, ButtonVariant } from '../../components/Button/Button'
import Quotes from './Quotes' import Quotes from './Quotes'
import { about } from '../../assets/text/landing' import { about } from '../../assets/text/landing'
import Login from '../../components/Login'
const Landing = (props: { export interface LandingStateProps {
isAuthenticated: boolean
}
interface LandingComponentProps {
navbarRef: React.RefObject<HTMLDivElement> navbarRef: React.RefObject<HTMLDivElement>
footerRef: React.RefObject<HTMLDivElement> footerRef: React.RefObject<HTMLDivElement>
}): JSX.Element => { }
const { navbarRef, footerRef } = props
type LandingProps = LandingComponentProps & LandingStateProps
const Landing = (props: LandingProps): JSX.Element => {
const { navbarRef, footerRef, isAuthenticated } = props
const [modalIsOpen, setModalIsOpen] = useState<boolean>(false)
const infoDivRef = useRef<HTMLDivElement>(null) const infoDivRef = useRef<HTMLDivElement>(null)
const [infoDivImgHeight, setInfoDivImgHeight] = useState<string | undefined>() const [infoDivImgHeight, setInfoDivImgHeight] = useState<string | undefined>()
useEffect(() => { useEffect(() => {
...@@ -34,11 +44,14 @@ const Landing = (props: { ...@@ -34,11 +44,14 @@ const Landing = (props: {
</div> </div>
<Button <Button
variant={ButtonVariant.COLOURFUL_ROUND} variant={ButtonVariant.COLOURFUL_ROUND}
type={ButtonType.LINK} type={ButtonType.BUTTON}
value='Start now' value='Start now'
link='/about'
classes='text-2xl' classes='text-2xl'
onClick={() => setModalIsOpen(true)}
/> />
{!isAuthenticated && (
<Login isOpen={modalIsOpen} setIsOpen={setModalIsOpen} />
)}
</div> </div>
<div className='flex py-24 px-20 max-w-screen-xl mx-auto justify-between items-center'> <div className='flex py-24 px-20 max-w-screen-xl mx-auto justify-between items-center'>
<div className='w-1/2' ref={infoDivRef}> <div className='w-1/2' ref={infoDivRef}>
......
export { default } from './Landing' import { connect } from 'react-redux'
import { RootState } from '../../state/store'
import Landing from './Landing'
const mapStateToProps = (
state: RootState
): {isAuthenticated: boolean} => ({
isAuthenticated: state.userReducer.isAuthenticated
})
export default connect(mapStateToProps)(Landing)
...@@ -132,7 +132,11 @@ const Module = (props: ModuleProps): JSX.Element => { ...@@ -132,7 +132,11 @@ const Module = (props: ModuleProps): JSX.Element => {
</div> </div>
</div> </div>
</div> </div>
<Modal isOpen={showVideo} setIsOpen={setShowVideo}> <Modal
isOpen={showVideo}
setIsOpen={setShowVideo}
classes='sm:max-w-screen-xl sm:w-full'
>
<div className='relative' style={{ paddingTop: '56.25%' }}> <div className='relative' style={{ paddingTop: '56.25%' }}>
<iframe <iframe
sandbox='allow-same-origin allow-scripts allow-popups' sandbox='allow-same-origin allow-scripts allow-popups'
......
...@@ -12,6 +12,7 @@ import { ...@@ -12,6 +12,7 @@ import {
} from './moduleActions' } from './moduleActions'
import * as api from './moduleApi' import * as api from './moduleApi'
import { ModuleProps, modules } from '../../assets/text/modules' import { ModuleProps, modules } from '../../assets/text/modules'
import { getSpeakerById } from '../../assets/text/speakers'
function * fetchPlaylistSaga (action: ReturnType<typeof fetchPlaylist>): any { function * fetchPlaylistSaga (action: ReturnType<typeof fetchPlaylist>): any {
const { payload } = action const { payload } = action
...@@ -21,13 +22,17 @@ function * fetchPlaylistSaga (action: ReturnType<typeof fetchPlaylist>): any { ...@@ -21,13 +22,17 @@ function * fetchPlaylistSaga (action: ReturnType<typeof fetchPlaylist>): any {
const playlistItems: PlaylistItem[] = data const playlistItems: PlaylistItem[] = data
.filter((item: any) => item.video !== null) .filter((item: any) => item.video !== null)
.map((item: { video: { duration: number, uuid: any, name: any, account: { displayName: any } } }) => { .map((item: { video: { duration: number, description: any, uuid: any, name: any, account: { displayName: any } } }) => {
playlistLength = playlistLength + item.video.duration playlistLength = playlistLength + item.video.duration
const description = JSON.parse(item.video.description.replace(/\n/g, ''))
const speaker = getSpeakerById(description.speakerId)
return ({ return ({
id: item.video.uuid, id: item.video.uuid,
title: item.video.name, title: item.video.name,
length: item.video?.duration ?? 0, length: item.video?.duration ?? 0,
authorName: item.video.account.displayName authorName: speaker?.name,
description: speaker?.description
}) })
} }
) )
......
import { combineReducers } from 'redux' import { combineReducers } from 'redux'
import moduleReducer from './module/moduleReducer' import moduleReducer from './module/moduleReducer'
import userReducer from './user/userReducer'
export default combineReducers({ export default combineReducers({
moduleReducer moduleReducer,
userReducer
}) })
import { ForkEffect, spawn } from 'redux-saga/effects' import { ForkEffect, spawn } from 'redux-saga/effects'
import moduleSaga from './module/moduleSaga' import moduleSaga from './module/moduleSaga'
import userSaga from './user/userSaga'
export default function * sagas (): Generator<ForkEffect<void>, void> { export default function * sagas (): Generator<ForkEffect<void>, void> {
yield spawn(moduleSaga) yield spawn(moduleSaga)
yield spawn(userSaga)
} }
import { action } from 'typesafe-actions'
import { LOGIN_REQUEST, LOGIN_REQUEST_ERROR, LOGIN_REQUEST_SUCCESS } from './userModel'
export const login = (user: string, token: string): {
type: '@user/LOGIN_REQUEST'
payload: {
user: string
token: string
}
} => action(LOGIN_REQUEST, { user, token })
export const loginSuccess = (): {
type: '@user/LOGIN_REQUEST_SUCCESS'
} => action(LOGIN_REQUEST_SUCCESS)
export const loginError = (error: string): {
type: '@user/LOGIN_REQUEST_ERROR'
payload: {
error: string
}
} => action(LOGIN_REQUEST_ERROR, { error })
import axios, { AxiosResponse } from 'axios'
export const login = async (username: string, token: string): Promise<AxiosResponse> => {
return await axios.post('https://api.igem.io/api/auth/acad/login', { username, token })
}
export const LOGIN_REQUEST = '@user/LOGIN_REQUEST'
export const LOGIN_REQUEST_SUCCESS = '@user/LOGIN_REQUEST_SUCCESS'
export const LOGIN_REQUEST_ERROR = '@user/LOGIN_REQUEST_ERROR'
import { ActionType, Reducer } from 'typesafe-actions'
import Cookies from 'js-cookie'
import {
LOGIN_REQUEST,
LOGIN_REQUEST_ERROR,
LOGIN_REQUEST_SUCCESS
} from './userModel'
const initialState = {
isAuthenticated: Cookies.get('academy_auth') === 'true',
isLoading: false,
error: null
}
interface UserProps {
isAuthenticated: boolean
isLoading: boolean
error: string | null
}
type UserActions =
| ActionType<typeof import('./userActions')>
const userReducer: Reducer<UserProps, UserActions> = (
/* eslint-disable-next-line */
state = initialState,
action
) => {
switch (action.type) {
case LOGIN_REQUEST: {
return {
...state,
isLoading: true,
error: null,
isAuthenticated: false
}
}
case LOGIN_REQUEST_SUCCESS: {
return {
...state,
error: null,
isLoading: false,
isAuthenticated: true
}
}
case LOGIN_REQUEST_ERROR: {
return {
...state,
isAuthenticated: false,
error: action.payload.error,
isLoading: false
}
}
default:
return state
}
}
export default userReducer
import { call, ForkEffect, put, takeLatest } from 'redux-saga/effects'
import * as api from './userApi'
import Cookies from 'js-cookie'
import { LOGIN_REQUEST } from './userModel'
import { login, loginError, loginSuccess } from './userActions'
function * loginSaga (action: ReturnType<typeof login>): any {
const { payload } = action
try {
const data = yield call(api.login, payload.user, payload.token)
console.log(data)
yield put(loginSuccess())
Cookies.set('academy_auth', Date.now().toString(), { expires: 3 })
} catch (e) {
yield put(loginError(e.response.data.error.message))
console.error(e.response.data.error.message)
Cookies.remove('academy_auth', { expires: 3 })
}
}
function * userSaga (): Generator<ForkEffect<never>, void> {
yield takeLatest(LOGIN_REQUEST, loginSaga)
}
export default userSaga
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment