You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
127 lines
3.5 KiB
127 lines
3.5 KiB
import { createAsyncThunk, createSlice, isAnyOf } from '@reduxjs/toolkit'
|
|
import type { PayloadAction } from '@reduxjs/toolkit'
|
|
import { createStandaloneToast } from '@chakra-ui/react'
|
|
import Router from 'next/router'
|
|
import { RootState } from './store'
|
|
import {
|
|
AuthState,
|
|
GoogleLoginRequestPayload,
|
|
LoginRequestPayload,
|
|
RegisterRequestPayload,
|
|
} from '../services/types'
|
|
import { removeUserAndToken, saveUserAndToken } from '../shared/utils'
|
|
import { LOCAL_STORAGE_KEY } from '../shared/constants'
|
|
import { googleLogout } from '@react-oauth/google'
|
|
import { authService } from '../services/authService'
|
|
const toast = createStandaloneToast()
|
|
|
|
const initialState: AuthState = {
|
|
loading: false,
|
|
accessToken:
|
|
typeof window !== 'undefined'
|
|
? localStorage.getItem(LOCAL_STORAGE_KEY.TOKEN)
|
|
: null,
|
|
user:
|
|
typeof window !== 'undefined'
|
|
? JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEY.USER))
|
|
: null,
|
|
}
|
|
|
|
export const login = createAsyncThunk(
|
|
'auth/login',
|
|
async (payload: LoginRequestPayload, { rejectWithValue }) => {
|
|
try {
|
|
const res = await authService.login(payload)
|
|
const { accessToken, user } = res
|
|
saveUserAndToken(user, accessToken)
|
|
Router.push('/')
|
|
return res
|
|
} catch (e) {
|
|
toast({
|
|
title: e.response.data.error || 'Login failed',
|
|
status: 'error',
|
|
})
|
|
return rejectWithValue(e.response.data)
|
|
}
|
|
}
|
|
)
|
|
|
|
export const loginWithGoogle = createAsyncThunk(
|
|
'auth/google-login',
|
|
async (payload: GoogleLoginRequestPayload, thunkAPI) => {
|
|
try {
|
|
const res = await authService.loginWithGoogle(payload)
|
|
const { accessToken, user } = res
|
|
saveUserAndToken(user, accessToken)
|
|
Router.push('/')
|
|
return res
|
|
} catch (e) {
|
|
toast({
|
|
title: e.response.data.error || 'Login failed',
|
|
status: 'error',
|
|
})
|
|
return thunkAPI.rejectWithValue(e.response.data)
|
|
}
|
|
}
|
|
)
|
|
|
|
export const register = createAsyncThunk(
|
|
'auth/register',
|
|
async (payload: RegisterRequestPayload, thunkAPI) => {
|
|
try {
|
|
const res = await authService.register(payload)
|
|
const { accessToken, user } = res
|
|
saveUserAndToken(user, accessToken)
|
|
Router.push('/')
|
|
return res
|
|
} catch (e) {
|
|
toast({
|
|
title: e.response.data.error || 'Signup failed',
|
|
status: 'error',
|
|
})
|
|
return thunkAPI.rejectWithValue(e.response.data)
|
|
}
|
|
}
|
|
)
|
|
|
|
export const authSlice = createSlice({
|
|
name: 'auth',
|
|
initialState,
|
|
reducers: {
|
|
logout: (state) => {
|
|
removeUserAndToken()
|
|
googleLogout()
|
|
state.accessToken = null
|
|
state.user = null
|
|
Router.push('/login')
|
|
},
|
|
},
|
|
extraReducers: (builder) => {
|
|
builder
|
|
.addMatcher(
|
|
isAnyOf(login.fulfilled, register.fulfilled, loginWithGoogle.fulfilled),
|
|
(state, action: PayloadAction<any>) => {
|
|
state.loading = false
|
|
state.user = action.payload.user
|
|
state.accessToken = action.payload.accessToken
|
|
}
|
|
)
|
|
.addMatcher(isAnyOf(login.rejected, register.rejected), (state) => {
|
|
state.loading = false
|
|
state.user = null
|
|
state.accessToken = null
|
|
})
|
|
.addMatcher(isAnyOf(login.pending, register.pending), (state) => {
|
|
state.loading = true
|
|
})
|
|
},
|
|
})
|
|
|
|
export const { logout } = authSlice.actions
|
|
|
|
export const selectAuthLoading = (state: RootState) => state.auth.loading
|
|
export const selectAuthUser = (state: RootState) => state.auth.user
|
|
export const selectAuthAccessToken = (state: RootState) =>
|
|
state.auth.accessToken
|
|
|
|
export default authSlice.reducer
|