You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

233 lines
6.9 KiB
TypeScript

import { type PayloadAction, createSlice } from '@reduxjs/toolkit'
import { type UserType } from '../types'
import { createAppAsyncThunk } from './hooks'
import type { ResponseData } from '../network'
import {
isActionPending,
isActionRejected,
token,
type PendingAction,
type RejectedAction,
} from '../utils'
type UserState = {
user: UserType
loading: boolean
error?: string
}
const defaultUser = (): UserType => {
return {
id: -1,
nickname: '',
email: '',
token: token.load(),
token_expiry_date: '',
money: '',
histories_id: [],
}
}
const initialState = (): UserState => {
return {
user: defaultUser(),
loading: false,
error: undefined,
}
}
const userSlice = createSlice({
name: 'user',
initialState: initialState(),
reducers: {
setUser: (state, action: PayloadAction<UserType>) => {
state.user = action.payload
},
clearUser() {
token.save('')
return initialState()
},
clearError: state => {
state.error = ''
},
},
extraReducers: builder => {
builder
.addCase(fetchUser.fulfilled, (state, action) => {
state.user = action.payload.success?.[0] as UserType
state.loading = false
})
.addCase(fetchLogin.fulfilled, (state, action) => {
state.user = action.payload.success?.[0] as UserType
state.loading = false
state.error = undefined
token.save(state.user.token)
})
.addCase(fetchLogout.fulfilled, state => {
if (state.user?.token) {
state.user.token = ''
}
state.loading = false
state.error = undefined
token.save('')
})
.addCase(fetchRegister.fulfilled, (state, action) => {
state.user = action.payload.success?.[0] as UserType
state.loading = false
state.error = undefined
token.save(state.user.token)
})
.addCase(fetchUnregister.fulfilled, (state, action) => {
state.user = action.payload.success?.[0] as UserType
state.loading = false
state.error = undefined
token.save(state.user.token)
})
.addCase(fetchAddMoney.fulfilled, (state, action) => {
state.user = action.payload.success?.[0] as UserType
state.loading = false
state.error = undefined
token.save(state.user.token)
})
.addMatcher<PendingAction>(isActionPending('user'), state => {
state.loading = true
state.error = undefined
})
.addMatcher<RejectedAction>(isActionRejected('user'), (state, action) => {
state.loading = false
if (action.payload) {
state.error = (action.payload as ResponseData).error
} else {
state.error = action.error.message
}
})
},
selectors: {
accessToken: (state: UserState) => state.user?.token,
user: (state: UserState) => state.user,
loading: (state: UserState) => state.loading,
error: (state: UserState) => state.error,
},
})
const fetchUser = createAppAsyncThunk<ResponseData>(
`${userSlice.name}/fetchUser`,
async (_, { fulfillWithValue, rejectWithValue, extra: networkApi }) => {
try {
const data = await networkApi.request<ResponseData>('/user/', {
method: 'POST',
})
return fulfillWithValue(data)
} catch (error) {
return rejectWithValue(error)
}
}
)
type RegisterProps = {
email: string
nickname: string
password: string
}
const fetchRegister = createAppAsyncThunk<ResponseData, RegisterProps>(
`${userSlice.name}/fetchRegister`,
async (props, { fulfillWithValue, rejectWithValue, extra: networkApi }) => {
try {
const data = await networkApi.request<ResponseData>('/user/', {
method: 'POST',
body: JSON.stringify({ register: props }),
})
return fulfillWithValue(data)
} catch (error) {
return rejectWithValue(error)
}
}
)
const fetchUnregister = createAppAsyncThunk<ResponseData>(
`${userSlice.name}/fetchUnregister`,
async (_, { fulfillWithValue, rejectWithValue, extra: networkApi }) => {
try {
const data = await networkApi.request<ResponseData>('/user/', {
method: 'POST',
body: JSON.stringify({ unregister: {} }),
})
return fulfillWithValue(data)
} catch (error) {
return rejectWithValue(error)
}
}
)
type LoginProps = {
email: string
password: string
}
const fetchLogin = createAppAsyncThunk<ResponseData, LoginProps>(
`${userSlice.name}/fetchLogin`,
async (props, { fulfillWithValue, rejectWithValue, extra: networkApi }) => {
try {
const data = await networkApi.request<ResponseData>('/user/', {
method: 'POST',
body: JSON.stringify({ login: props }),
})
return fulfillWithValue(data)
} catch (error) {
return rejectWithValue(error)
}
}
)
const fetchLogout = createAppAsyncThunk<ResponseData>(
`${userSlice.name}/fetchLogout`,
async (_, { fulfillWithValue, rejectWithValue, extra: networkApi }) => {
try {
const data = await networkApi.request<ResponseData>('/user/', {
method: 'POST',
body: JSON.stringify({ logout: {} }),
})
return fulfillWithValue(data)
} catch (error) {
return rejectWithValue(error)
}
}
)
type AddMoneyProps = {
money: string
}
const fetchAddMoney = createAppAsyncThunk<ResponseData, AddMoneyProps>(
`${userSlice.name}/fetchAddMoney`,
async (props, { fulfillWithValue, rejectWithValue, extra: networkApi }) => {
try {
const data = await networkApi.request<ResponseData>('/user/', {
method: 'POST',
body: JSON.stringify({ add_money: props }),
})
return fulfillWithValue(data)
} catch (error) {
return rejectWithValue(error)
}
}
)
class User {
selector = { ...userSlice.selectors }
action = userSlice.actions
reducer = userSlice.reducer
name = userSlice.name
fetch = {
user: fetchUser,
login: fetchLogin,
logout: fetchLogout,
register: fetchRegister,
unregister: fetchUnregister,
addMoney: fetchAddMoney,
}
}
export const user = new User()