diff --git a/app/_layout.tsx b/app/_layout.tsx
index f96ea3c..1dfe892 100644
--- a/app/_layout.tsx
+++ b/app/_layout.tsx
@@ -11,6 +11,7 @@ import * as SplashScreen from "expo-splash-screen";
import AsyncStorage from "@react-native-async-storage/async-storage";
import { STORAGE_KEYS } from "@/constants/config";
import { setIsLoggedIn } from "@/store/authSlice";
+import { getUserDetails } from "@/store/userSlice";
SplashScreen.preventAutoHideAsync();
@@ -35,11 +36,29 @@ function SplashAndAuthRouter() {
useEffect(() => {
const loadAuth = async () => {
- const token = await AsyncStorage.getItem(STORAGE_KEYS.AUTH_TOKEN);
- dispatch(setIsLoggedIn(!!token));
- setLoading(false);
- SplashScreen.hideAsync();
+ try {
+ const token = await AsyncStorage.getItem(STORAGE_KEYS.AUTH_TOKEN);
+
+ if (!token) {
+ dispatch(setIsLoggedIn(false));
+ return;
+ }
+
+ // Token exists, now verify it by calling getUserDetails
+ // await dispatch(getUserDetails()).unwrap();
+
+ // If it reaches here, the token is valid
+ dispatch(setIsLoggedIn(true));
+ } catch (error) {
+ // getUserDetails failed => token is invalid
+ dispatch(setIsLoggedIn(false));
+ await AsyncStorage.removeItem(STORAGE_KEYS.AUTH_TOKEN); // optionally clean up
+ } finally {
+ setLoading(false);
+ SplashScreen.hideAsync();
+ }
};
+
loadAuth();
}, []);
diff --git a/app/user/edit_name.tsx b/app/user/edit_name.tsx
new file mode 100644
index 0000000..0944b1e
--- /dev/null
+++ b/app/user/edit_name.tsx
@@ -0,0 +1,183 @@
+import React from "react";
+import {
+ View,
+ Text,
+ TextInput,
+ StyleSheet,
+ TouchableOpacity,
+ KeyboardAvoidingView,
+ Platform,
+ Keyboard,
+ TouchableWithoutFeedback,
+} from "react-native";
+import { useDispatch, useSelector } from "react-redux";
+import { RootState } from "@/store";
+import { Formik } from "formik";
+import * as Yup from "yup";
+import api from "@/services/axiosClient";
+import { BASE_URL } from "@/constants/config";
+import { setUsername } from "@/store/userSlice";
+import { useSafeAreaInsets } from "react-native-safe-area-context";
+import { router } from "expo-router";
+import { useSnackbar } from "@/contexts/Snackbar";
+
+export default function EditName() {
+ const { data } = useSelector((state: RootState) => state.user);
+ const originalName = data?.name || "";
+ const dispatch = useDispatch();
+ const insets = useSafeAreaInsets();
+ const nameSchema = Yup.object().shape({
+ name: Yup.string().required("Name is required"),
+ });
+ const { showSnackbar } = useSnackbar();
+ const handleSave = async (values: { name: string }) => {
+ try {
+ await api.put(`${BASE_URL}/api/v1/update-user-information`, {
+ name: values.name,
+ mobile: data?.mobile,
+ });
+
+ dispatch(setUsername(values.name));
+ showSnackbar("Name updated successfully", "success");
+ router.back();
+ } catch (error) {
+ console.error("Error updating name:", error);
+ }
+ };
+
+ return (
+
+
+
+
+ {({
+ handleChange,
+ handleBlur,
+ handleSubmit,
+ values,
+ touched,
+ errors,
+ }) => {
+ const hasChanged = values.name !== originalName;
+ const hasError = !!errors.name;
+
+ return (
+
+
+ Enter Name
+
+ {touched.name && errors.name && (
+ {errors.name}
+ )}
+
+
+ void}
+ style={[
+ styles.button,
+ hasChanged && !hasError
+ ? styles.buttonEnabled
+ : styles.buttonDisabled,
+ { marginBottom: insets.bottom },
+ ]}
+ disabled={!hasChanged || hasError}
+ >
+ Save
+
+
+ );
+ }}
+
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: "#F3F5F8",
+ paddingHorizontal: 16,
+ },
+ inner: {
+ flex: 1,
+ justifyContent: "space-between",
+ paddingVertical: 24,
+ },
+ formContainer: {
+ flex: 1,
+ justifyContent: "space-between",
+ },
+ inputContainer: {
+ marginBottom: 24,
+ },
+ label: {
+ fontSize: 14,
+ marginBottom: 8,
+ color: "#252A34",
+ fontFamily: "Inter",
+ lineHeight: 20,
+ },
+ input: {
+ backgroundColor: "#ffffff",
+ borderRadius: 4,
+ borderWidth: 1,
+ height: 40,
+ paddingHorizontal: 8,
+ fontSize: 14,
+ fontFamily: "Inter",
+ fontWeight: "500",
+ lineHeight: 20,
+ },
+ error: {
+ marginTop: 8,
+ color: "#D51D10",
+ fontSize: 12,
+ fontFamily: "Inter",
+ fontWeight: "bold",
+ lineHeight: 20,
+ },
+ button: {
+ height: 48,
+ borderRadius: 4,
+ alignItems: "center",
+ justifyContent: "center",
+ width: "100%",
+ paddingHorizontal: 16,
+ },
+ buttonEnabled: {
+ backgroundColor: "#008761",
+ },
+ buttonDisabled: {
+ backgroundColor: "#B0B7C5", // from Figma: rgba(176, 183, 197)
+ },
+ buttonText: {
+ color: "#FCFCFC",
+ fontSize: 14,
+ fontFamily: "Inter",
+ fontWeight: "bold",
+ lineHeight: 20,
+ },
+});
diff --git a/app/user/profile.tsx b/app/user/profile.tsx
index 3d0cc37..f35d52d 100644
--- a/app/user/profile.tsx
+++ b/app/user/profile.tsx
@@ -9,6 +9,10 @@ import {
} from "react-native";
import { MaterialIcons } from "@expo/vector-icons";
import LanguageModal from "@/components/Profile/LangaugeModal";
+import { useSelector } from "react-redux";
+import { RootState } from "@/store";
+import EditIcon from "../../assets/icons/edit.svg";
+import { router } from "expo-router";
export default function ProfileScreen() {
const [isLangaugeModalVisible, setLanguageModalVisible] =
@@ -17,6 +21,12 @@ export default function ProfileScreen() {
const toggleLanguageModal = () => {
setLanguageModalVisible(!isLangaugeModalVisible);
};
+
+ const { data } = useSelector((state: RootState) => state.user);
+
+ const userName = data?.name || "User";
+ const mobileNumber = data?.mobile || "Not provided";
+
return (
<>
@@ -26,16 +36,20 @@ export default function ProfileScreen() {
style={styles.avatar}
/>
-
+
Name
- Amar Kesari
+ {userName}
-
+ {
+ router.push("/user/edit_name");
+ }}
+ >
@@ -43,7 +57,7 @@ export default function ProfileScreen() {
Mobile Number
- 9876543210
+ {mobileNumber}
@@ -113,7 +127,7 @@ const styles = StyleSheet.create({
editAvatar: {
position: "absolute",
bottom: 0,
- right: 105,
+ right: "35%",
width: 40,
height: 40,
backgroundColor: "#008866",
diff --git a/assets/icons/edit.svg b/assets/icons/edit.svg
new file mode 100644
index 0000000..aa9db27
--- /dev/null
+++ b/assets/icons/edit.svg
@@ -0,0 +1,8 @@
+
diff --git a/store/authSlice.ts b/store/authSlice.ts
index 9fa31fa..df875e4 100644
--- a/store/authSlice.ts
+++ b/store/authSlice.ts
@@ -199,6 +199,7 @@ export const {
clearSendOTPError,
clearVerifyOTPError,
clearAllErrors,
+ setIsLoggedIn,
} = authSlice.actions;
export default authSlice.reducer;
diff --git a/store/rootReducer.ts b/store/rootReducer.ts
index 3eb108c..e30f68a 100644
--- a/store/rootReducer.ts
+++ b/store/rootReducer.ts
@@ -1,10 +1,12 @@
import { combineReducers } from "@reduxjs/toolkit";
import authreducer from "./authSlice";
import telemetryReducer from "./telemetrySlice";
+import userReducer from "./userSlice";
const rootReducer = combineReducers({
auth: authreducer,
telemetry: telemetryReducer,
+ user: userReducer,
});
export default rootReducer;
diff --git a/store/userSlice.ts b/store/userSlice.ts
index da7c1bb..49dae8a 100644
--- a/store/userSlice.ts
+++ b/store/userSlice.ts
@@ -36,8 +36,10 @@ export const getUserDetails = createAsyncThunk(
"user/getUserDetails",
async (_, { rejectWithValue }) => {
try {
+ console.log("Fetching user details from API...");
const response = await api.get(`${BASE_URL}/api/v1/get-user-details`);
if (response.data.success) {
+ console.log("User details fetched successfully:", response.data.data);
return response.data.data;
} else {
return rejectWithValue("Failed to fetch user data");
@@ -61,6 +63,13 @@ const userSlice = createSlice({
state.error = null;
state.loading = false;
},
+ setUsername: (state, action: PayloadAction) => {
+ if (state.data) {
+ state.data.name = action.payload;
+ } else {
+ console.warn("Cannot set username, user data is not loaded");
+ }
+ },
},
extraReducers: (builder) => {
builder
@@ -82,5 +91,5 @@ const userSlice = createSlice({
},
});
-export const { clearUser } = userSlice.actions;
+export const { clearUser, setUsername } = userSlice.actions;
export default userSlice.reducer;