222 lines
6.0 KiB
TypeScript
222 lines
6.0 KiB
TypeScript
// type PayloadAction<T> = {
|
|
// type: string;
|
|
// payload: T;
|
|
// }
|
|
import axios from "axios";
|
|
import { createAsyncThunk, createSlice, PayloadAction } from "@reduxjs/toolkit";
|
|
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
import { STORAGE_KEYS, MESSAGES, BASE_URL } from "../constants/config";
|
|
import { AUTH_STATUSES, StatusType } from "../constants/types";
|
|
|
|
interface AuthState {
|
|
phone: string | null;
|
|
otpId: number | null;
|
|
isLoggedIn: boolean;
|
|
status: StatusType;
|
|
sendOTPError: string | null;
|
|
verifyOTPError: string | null;
|
|
generalError: string | null;
|
|
}
|
|
|
|
interface SendOTPResponse {
|
|
success: boolean;
|
|
message: string;
|
|
data: {
|
|
otpId: number;
|
|
phone: string;
|
|
};
|
|
}
|
|
|
|
interface VerifyOTPResponse {
|
|
success: boolean;
|
|
message: string;
|
|
data: {
|
|
success: boolean;
|
|
message: string;
|
|
token: string;
|
|
token_expires_in: string;
|
|
};
|
|
}
|
|
|
|
interface SendOTPParams {
|
|
phone: string | null;
|
|
}
|
|
|
|
interface VerifyOTPParams {
|
|
phone: string | null;
|
|
otp: string | null;
|
|
otpId: string | null;
|
|
}
|
|
|
|
const initialState: AuthState = {
|
|
phone: null,
|
|
otpId: null,
|
|
isLoggedIn: false,
|
|
status: AUTH_STATUSES.IDLE,
|
|
sendOTPError: null,
|
|
verifyOTPError: null,
|
|
generalError: null,
|
|
};
|
|
|
|
//async thunk to send otp
|
|
export const sendOTP = createAsyncThunk<SendOTPResponse, SendOTPParams>(
|
|
"auth/sendOTP",
|
|
async (params, { rejectWithValue }) => {
|
|
try {
|
|
const response = await axios.post<SendOTPResponse>(
|
|
`${BASE_URL}/api/v1/send-otp`,
|
|
params
|
|
);
|
|
return response.data;
|
|
// if (!response.data.status) throw new Error(response.data.message);
|
|
} catch (error: any) {
|
|
let message = "Something went wrong. Please try again.";
|
|
|
|
if (axios.isAxiosError(error)) {
|
|
// try to read backend message safely
|
|
message = error.response?.data?.message || error.message || message;
|
|
} else if (error instanceof Error) {
|
|
message = error.message;
|
|
}
|
|
console.log(message, "message");
|
|
return rejectWithValue(message);
|
|
}
|
|
}
|
|
);
|
|
|
|
export const verifyOTP = createAsyncThunk<
|
|
VerifyOTPResponse,
|
|
VerifyOTPParams,
|
|
{ rejectValue: string }
|
|
>("auth/verifyOTP", async (params, { rejectWithValue }) => {
|
|
try {
|
|
console.log("params", params);
|
|
const response = await axios.post<VerifyOTPResponse>(
|
|
`${BASE_URL}/api/v1/verify-otp`,
|
|
params
|
|
);
|
|
if (!response.data.success) return rejectWithValue(response.data.message);
|
|
|
|
// Store tokens
|
|
await AsyncStorage.setItem(
|
|
STORAGE_KEYS.AUTH_TOKEN,
|
|
response.data.data.token
|
|
);
|
|
|
|
return response.data;
|
|
} catch (error: any) {
|
|
let message = "Something went wrong. Please try again.";
|
|
|
|
if (axios.isAxiosError(error)) {
|
|
message = error.response?.data?.message || error.message || message;
|
|
} else if (error instanceof Error) {
|
|
message = error.message;
|
|
}
|
|
console.log(message, "message");
|
|
return rejectWithValue(message);
|
|
}
|
|
});
|
|
|
|
export const logout = createAsyncThunk(
|
|
"auth/logout",
|
|
async (_, { dispatch }) => {
|
|
await AsyncStorage.removeItem(STORAGE_KEYS.AUTH_TOKEN);
|
|
dispatch(authSlice.actions.logout());
|
|
}
|
|
);
|
|
|
|
const authSlice = createSlice({
|
|
name: "auth",
|
|
initialState,
|
|
reducers: {
|
|
loginRequest: (state) => {
|
|
state.status = AUTH_STATUSES.LOADING;
|
|
},
|
|
loginSuccess: (state, action) => {
|
|
state.status = AUTH_STATUSES.SUCCESS;
|
|
state.isLoggedIn = true; // Set logged-in state to true
|
|
},
|
|
setIsLoggedIn: (state, action) => {
|
|
state.isLoggedIn = action.payload;
|
|
},
|
|
loginFailure: (state, action: PayloadAction<string>) => {
|
|
state.status = AUTH_STATUSES.FAILED;
|
|
state.generalError = action.payload;
|
|
},
|
|
logout: (state) => {
|
|
state.isLoggedIn = false;
|
|
},
|
|
clearSendOTPError: (state) => {
|
|
state.sendOTPError = null;
|
|
},
|
|
clearVerifyOTPError: (state) => {
|
|
state.verifyOTPError = null;
|
|
},
|
|
clearAllErrors: (state) => {
|
|
state.sendOTPError = null;
|
|
state.verifyOTPError = null;
|
|
state.generalError = null;
|
|
},
|
|
},
|
|
extraReducers: (builder) => {
|
|
builder
|
|
.addCase(sendOTP.pending, (state) => {
|
|
state.status = AUTH_STATUSES.LOADING;
|
|
state.sendOTPError = null;
|
|
})
|
|
.addCase(sendOTP.fulfilled, (state, action) => {
|
|
state.status = AUTH_STATUSES.SUCCESS;
|
|
state.otpId = action.payload.data.otpId;
|
|
state.phone = action.payload.data.phone;
|
|
state.sendOTPError = null;
|
|
})
|
|
.addCase(sendOTP.rejected, (state, action) => {
|
|
state.status = AUTH_STATUSES.FAILED;
|
|
state.sendOTPError = (action.payload as string) || "Failed to send OTP";
|
|
})
|
|
.addCase(verifyOTP.pending, (state) => {
|
|
state.status = AUTH_STATUSES.LOADING;
|
|
state.verifyOTPError = null;
|
|
})
|
|
.addCase(verifyOTP.fulfilled, (state, action) => {
|
|
state.status = AUTH_STATUSES.SUCCESS;
|
|
state.isLoggedIn = true;
|
|
state.verifyOTPError = null;
|
|
const token = action.payload.data.token;
|
|
AsyncStorage.setItem(STORAGE_KEYS.AUTH_TOKEN, token).catch((error) => {
|
|
console.log("Error storing token", error);
|
|
});
|
|
})
|
|
.addCase(verifyOTP.rejected, (state, action) => {
|
|
state.status = AUTH_STATUSES.FAILED;
|
|
state.verifyOTPError = action.payload || "Failed to verify OTP";
|
|
})
|
|
.addCase(logout.pending, (state) => {
|
|
state.status = AUTH_STATUSES.LOADING;
|
|
})
|
|
.addCase(logout.fulfilled, (state) => {
|
|
state.otpId = null;
|
|
state.isLoggedIn = false;
|
|
state.status = AUTH_STATUSES.IDLE;
|
|
state.verifyOTPError = null;
|
|
state.generalError = null;
|
|
state.sendOTPError = null;
|
|
state.phone = null;
|
|
})
|
|
.addCase(logout.rejected, (state, action) => {
|
|
state.status = AUTH_STATUSES.FAILED;
|
|
state.generalError = action.error.message || "Failed to log out";
|
|
});
|
|
},
|
|
});
|
|
|
|
export const {
|
|
logout: localLogout,
|
|
clearSendOTPError,
|
|
clearVerifyOTPError,
|
|
clearAllErrors,
|
|
setIsLoggedIn,
|
|
} = authSlice.actions;
|
|
|
|
export default authSlice.reducer;
|