diff --git a/AnimatedTabBar.js b/AnimatedTabBar.js
new file mode 100644
index 0000000..4c9fe51
--- /dev/null
+++ b/AnimatedTabBar.js
@@ -0,0 +1,97 @@
+import React, { useRef } from 'react';
+import { View, TouchableOpacity, Animated, Text } from 'react-native';
+import Ionicons from '@expo/vector-icons/Ionicons';
+import { colors } from './constants/colors';
+import { styles } from './AnimatedTabBar.styles';
+
+const TAB_ICONS = {
+ MainPage: 'home',
+ AccessStack: 'list',
+ MyPageStack: 'person-sharp',
+ AlertStack: 'notifications',
+};
+
+const TAB_LABELS = {
+ MainPage: '홈',
+ AccessStack: '출입 권한',
+ MyPageStack: '마이페이지',
+ AlertStack: '알림',
+};
+
+export default function AnimatedTabBar({ state, descriptors, navigation }) {
+ const scales = useRef(state.routes.map(() => new Animated.Value(1))).current;
+ const tilts = useRef(state.routes.map(() => new Animated.Value(0))).current;
+
+ const onPress = (route, index, isFocused) => {
+ const event = navigation.emit({
+ type: 'tabPress',
+ target: route.key,
+ canPreventDefault: true,
+ });
+ // 포커스된 탭이 아니고, 이벤트가 취소되지 않았으면 해당 route로 이동
+ if (!isFocused && !event.defaultPrevented) {
+ navigation.navigate(route.name);
+ }
+ Animated.parallel([
+ // 크기 변화
+ Animated.sequence([
+ Animated.timing(scales[index], { toValue: 1.2, duration: 120, useNativeDriver: true }),
+ Animated.spring(scales[index], { toValue: 1, friction: 3, useNativeDriver: true }),
+ ]),
+ // 기울기 변화
+ Animated.sequence([
+ Animated.timing(tilts[index], { toValue: 1, duration: 150, useNativeDriver: true }), // 느리게(150ms)
+ Animated.timing(tilts[index], { toValue: -1, duration: 150, useNativeDriver: true }), // 느리게(150ms)
+ Animated.timing(tilts[index], { toValue: 0, duration: 100, useNativeDriver: true }), // 중앙으로 복귀
+ ]),
+ ]).start();
+ };
+
+ return (
+
+ {state.routes.map((route, index) => {
+ // 현재 탭이 포커스된 상태인지 확인
+ const isFocused = state.index === index;
+ // 현재 탭의 아이콘과 레이블 설정
+ const iconName = TAB_ICONS[route.name] || 'ellipse-outline';
+ const label = TAB_LABELS[route.name] || route.name;
+
+ return (
+ onPress(route, index, isFocused)}
+ style={styles.tab}
+ activeOpacity={0.8}
+ >
+
+
+
+ {label}
+
+ );
+ })}
+
+ );
+}
diff --git a/AnimatedTabBar.styles.js b/AnimatedTabBar.styles.js
new file mode 100644
index 0000000..74a2a70
--- /dev/null
+++ b/AnimatedTabBar.styles.js
@@ -0,0 +1,50 @@
+import { StyleSheet } from 'react-native';
+import { colors } from './constants/colors';
+
+export const styles = StyleSheet.create({
+ tabBar: {
+ flexDirection: 'row',
+ height: 80, // 하단 탭 높이
+ backgroundColor: colors.white, // 하단 탭 배경색
+ // 테두리 반경
+ borderTopLeftRadius: 24,
+ borderTopRightRadius: 24,
+ // 그림자 효과
+ shadowColor: colors.black,
+ elevation: 8,
+ },
+ tab: {
+ flex: 1,
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ iconWrapper: {
+ backgroundColor: colors.white,
+ padding: 3,
+ borderRadius: 16,
+ marginBottom: 2,
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ iconWrapperActive: {
+ backgroundColor: colors.white,
+ padding: 3,
+ borderRadius: 8,
+ },
+ icon: {
+ color: colors.lightGray,
+ },
+ iconActive: {
+ color: colors.primary,
+ },
+ label: {
+ fontSize: 13,
+ color: colors.lightGray,
+ fontWeight: 'bold',
+ marginTop: 2,
+ },
+ labelActive: {
+ color: colors.darkGray,
+ fontWeight: 'bold',
+ },
+});
diff --git a/App.js b/App.js
index adc90d6..1bf4a17 100644
--- a/App.js
+++ b/App.js
@@ -1,7 +1,13 @@
import AppNavigator from './AppNavigator';
+import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context';
const App = () => {
- return ;
+ return (
+
+
+
+
+ );
};
export default App;
diff --git a/AppNavigator.js b/AppNavigator.js
index 20c285b..694c329 100644
--- a/AppNavigator.js
+++ b/AppNavigator.js
@@ -1,12 +1,16 @@
import { NavigationContainer, DefaultTheme } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
-import { useState, useEffect } from 'react';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+import { useState, useEffect, useRef } from 'react';
import Ionicons from '@expo/vector-icons/Ionicons';
import { StatusBar } from 'react-native';
import HomeButtonController from './components/buttons/HomeButtonController';
import LoadingOverlay from './components/loadings/LoadingOverlay';
import { getMyInfo } from './apis/MyPageApi';
import { useAuthStore } from './stores/authStore';
+import { useModalStore } from './stores/modalStore';
+import PasswordConfirmModal from './modals/PasswordConfirmModal';
+import AnimatedTabBar from './AnimatedTabBar';
// 로그인 전 페이지
import WelcomePage from './pages/WelcomePage';
@@ -22,9 +26,88 @@ import AccessListPage from './pages/AccessListPage';
import MyAccessListPage from './pages/MyAccessListPage';
import AccessRequestPage from './pages/AccessRequestPage';
import AccessRequestRolePage from './pages/AccessRequestRolePage';
+import AlertList from './pages/AlertList';
import { colors } from './constants/colors';
const Stack = createStackNavigator();
+const Tab = createBottomTabNavigator();
+
+// Tab 네비게이터 옵션
+const tabScreenOptions = ({ route }) => ({
+ // tabBarIcon: ({ color, size }) => {
+ // let iconName;
+ // if (route.name === 'MainPage') iconName = 'home';
+ // else if (route.name === 'MyPageStack') iconName = 'person-sharp';
+ // else if (route.name === 'AccessStack') iconName = 'list';
+ // else if (route.name === 'AlertStack') iconName = 'notifications';
+ // else iconName = 'ellipse-outline';
+ // return ;
+ // },
+ // tabBarActiveTintColor: colors.secondary,
+ // tabBarInactiveTintColor: 'gray',
+ headerShown: false,
+});
+
+// Stack 네비게이터 옵션
+const screenOptions = {
+ headerStyle: { backgroundColor: colors.secondary, height: 100 },
+ headerTintColor: colors.white,
+ headerTitleStyle: { fontWeight: '600', fontSize: 26 },
+ headerTitleAlign: 'center',
+ gestureEnabled: true,
+ headerBackImage: () => ,
+ headerBackTitle: '',
+};
+
+// 마이페이지 스택 네비게이터
+function MyPageStack() {
+ return (
+
+
+
+
+ );
+}
+
+// 출입 권한 스택 네비게이터
+function AccessStack() {
+ return (
+
+
+
+
+
+
+ );
+}
+
+function AlertStack() {
+ return (
+
+
+
+ );
+}
export default function AppNavigator() {
const {
@@ -36,6 +119,16 @@ export default function AppNavigator() {
setAccessToken,
clearAccessToken,
} = useAuthStore();
+
+ const showPasswordModal = useModalStore((state) => state.showPasswordModal);
+ // 네비게이션 객체에 직접 접근하기 위한 ref
+ const navigationRef = useRef();
+
+ const handleTabPress = (e, tabName) => {
+ e.preventDefault();
+ const currentRoute = navigationRef.current?.getCurrentRoute();
+ showPasswordModal(tabName, currentRoute?.name || 'MainPage');
+ };
const [navState, setNavState] = useState(null);
// 앱 시작 시 토큰 유효성 확인
@@ -74,77 +167,48 @@ export default function AppNavigator() {
};
return (
-
-
+
+
- ,
- headerBackTitle: '',
- }}
- >
- {isLoggedIn ? (
- <>
-
-
-
-
-
-
-
- >
- ) : (
- <>
-
-
-
-
- >
- )}
-
+
+ {isLoggedIn ? (
+ }
+ >
+
+
+
+ handleTabPress(e, 'MyPageStack'), // 비밀번호 인증 이후 이동
+ }}
+ />
+
+ ) : (
+
+
+
+
+
+
+ )}
);
}
diff --git a/components/headers/WaveHeader.js b/components/headers/WaveHeader.js
index a5f5f98..31abc14 100644
--- a/components/headers/WaveHeader.js
+++ b/components/headers/WaveHeader.js
@@ -4,11 +4,16 @@ import { Ionicons } from '@expo/vector-icons';
import { useNavigation } from '@react-navigation/native';
import { colors } from '../../constants/colors';
-const WaveHeader = () => {
+const WaveHeader = ({ onBackPress }) => {
const navigation = useNavigation();
const handleBackButton = () => {
- navigation.goBack();
+ if (onBackPress) {
+ // onBackPress가 전달된 경우
+ onBackPress();
+ } else {
+ navigation.goBack();
+ }
};
return (
diff --git a/modals/PasswordConfirmModal.js b/modals/PasswordConfirmModal.js
index aaf3d9f..eeedcad 100644
--- a/modals/PasswordConfirmModal.js
+++ b/modals/PasswordConfirmModal.js
@@ -1,16 +1,25 @@
import { View, Text, Modal } from 'react-native';
-import React, { useState } from 'react';
+import React, { useState, useEffect } from 'react';
import { styles } from './styles/PasswordConfirmModal.styles';
import NormalInput from '../components/textinputs/NormalInput';
import NormalButton from '../components/buttons/NormalButton';
import WaveHeader from '../components/headers/WaveHeader';
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
import { verifyPassword } from '../apis/PasswordApi';
+import { useModalStore } from '../stores/modalStore';
-const PasswordConfirmModal = ({ visible = true, onCloseHandler }) => {
+const PasswordConfirmModal = ({ navigationRef }) => {
+ const { isPasswordModalVisible, pendingTab, prevTab, hidePasswordModal } = useModalStore();
const [password, setPassword] = useState('');
const [errorText, setErrorText] = useState(''); // NormalText ErrorText
+ useEffect(() => {
+ if (isPasswordModalVisible) {
+ setPassword('');
+ setErrorText('');
+ }
+ }, [isPasswordModalVisible]);
+
// 비밀번호 규칙 검사 핸들러 (8자 이상, 영문/숫자/특수문자 포함)
const isValidPassword = (pw) =>
/^(?=.*[A-Za-z])(?=.*\d)(?=.*[!@#$%^&*()_+{}\[\]:;<>,.?~\\/-]).{8,}$/.test(pw);
@@ -31,17 +40,20 @@ const PasswordConfirmModal = ({ visible = true, onCloseHandler }) => {
setErrorText('8자 이상, 영문/숫자/특수문자를 포함해야 합니다.');
return;
}
-
try {
await verifyPassword(password);
-
- // 모달창 닫기
- onCloseHandler();
+ navigationRef.current?.navigate(pendingTab);
+ hidePasswordModal();
} catch (error) {
console.log(error);
setErrorText('비밀번호가 일치하지 않습니다. 다시 입력해주세요.');
}
};
+
+ const onClosePasswordModal = () => {
+ hidePasswordModal();
+ navigationRef.current?.navigate(prevTab);
+ };
// 모달 오류시 임시 코드
// const handleConfirm = async () => {
// if (!isValidPassword(password)) {
@@ -51,21 +63,26 @@ const PasswordConfirmModal = ({ visible = true, onCloseHandler }) => {
// // 임시: 비밀번호가 'Lgcns01!'이면 성공, 아니면 실패
// if (password === 'Lgcns01!') {
- // onCloseHandler(); // 성공 시 모달 닫기
+ // navigationRef.current?.navigate(pendingTab);
+ // hidePasswordModal();
// } else {
// setErrorText('비밀번호가 일치하지 않습니다. 다시 입력해주세요.');
// }
// };
return (
-
+
-
+
비밀번호 확인
개인정보 보호를 위해 비밀번호를 확인합니다.
diff --git a/package-lock.json b/package-lock.json
index d1d6f90..1c0f5b7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,20 +10,23 @@
"dependencies": {
"@react-native-async-storage/async-storage": "1.23.1",
"@react-native-masked-view/masked-view": "0.3.2",
+ "@react-navigation/bottom-tabs": "^7.3.13",
"@react-navigation/native": "^7.1.6",
"@react-navigation/stack": "^7.2.10",
"axios": "^1.9.0",
"expo": "~52.0.46",
+ "expo-linear-gradient": "~14.0.2",
"expo-status-bar": "~2.0.1",
"react": "18.3.1",
"react-native": "0.76.9",
"react-native-awesome-alerts": "^2.0.0",
"react-native-gesture-handler": "~2.20.2",
"react-native-keyboard-aware-scroll-view": "^0.9.5",
+ "react-native-linear-gradient": "^2.8.3",
"react-native-modal": "^14.0.0-rc.1",
"react-native-qrcode-svg": "^6.3.15",
"react-native-reanimated": "~3.16.1",
- "react-native-safe-area-context": "^4.12.0",
+ "react-native-safe-area-context": "^4.14.1",
"react-native-screens": "~4.4.0",
"react-native-vector-icons": "^10.2.0",
"zustand": "^5.0.4"
@@ -3892,53 +3895,58 @@
}
}
},
+ "node_modules/@react-navigation/bottom-tabs": {
+ "version": "7.3.13",
+ "resolved": "https://registry.npmjs.org/@react-navigation/bottom-tabs/-/bottom-tabs-7.3.13.tgz",
+ "integrity": "sha512-J3MWXBJc3y6hefZNRqdj/JD4nzIDLzZL5GIYj89pR6oRf2Iibz9t1qV7yzxEc1KOaNDkXVZ/5U16PArEJFfykQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@react-navigation/elements": "^2.4.2",
+ "color": "^4.2.3"
+ },
+ "peerDependencies": {
+ "@react-navigation/native": "^7.1.9",
+ "react": ">= 18.2.0",
+ "react-native": "*",
+ "react-native-safe-area-context": ">= 4.0.0",
+ "react-native-screens": ">= 4.0.0"
+ }
+ },
"node_modules/@react-navigation/core": {
- "version": "7.8.5",
- "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-7.8.5.tgz",
- "integrity": "sha512-xDUXs6NI6ASiZgf53I7NPG0iJVGClPL5O3r8ddOCkS6fhVmPRun64m2zxUWnPcxtheFNTFfQ1IXH+gcenTcv/w==",
+ "version": "7.9.2",
+ "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-7.9.2.tgz",
+ "integrity": "sha512-lqCyKMWWaSwGK4VV3wRXXEKvl5IKrVH207Kp77TLCnITnd4KQIdgjzzJ/Pr62ugki3VTAErq1vg0yRlcXciCbg==",
"license": "MIT",
"dependencies": {
- "@react-navigation/routers": "^7.3.5",
+ "@react-navigation/routers": "^7.3.7",
"escape-string-regexp": "^4.0.0",
- "nanoid": "3.3.8",
+ "nanoid": "^3.3.11",
"query-string": "^7.1.3",
- "react-is": "^18.2.0",
- "use-latest-callback": "^0.2.1",
- "use-sync-external-store": "^1.2.2"
+ "react-is": "^19.1.0",
+ "use-latest-callback": "^0.2.3",
+ "use-sync-external-store": "^1.5.0"
},
"peerDependencies": {
"react": ">= 18.2.0"
}
},
- "node_modules/@react-navigation/core/node_modules/nanoid": {
- "version": "3.3.8",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
- "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "MIT",
- "bin": {
- "nanoid": "bin/nanoid.cjs"
- },
- "engines": {
- "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
- }
+ "node_modules/@react-navigation/core/node_modules/react-is": {
+ "version": "19.1.0",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.1.0.tgz",
+ "integrity": "sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg==",
+ "license": "MIT"
},
"node_modules/@react-navigation/elements": {
- "version": "2.3.8",
- "resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-2.3.8.tgz",
- "integrity": "sha512-2ZVBtPfrkmOxzvIyDu3fPZ6aS4HcXL+TvzPDGa1znY2OP1Llo6wH14AmJHQFDquiInp2656hRMM1BkfJ3yPwew==",
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-2.4.2.tgz",
+ "integrity": "sha512-cudKLsRtOB+i8iDzfBKypdqiHsDy1ruqCfYAtwKEclDmLsxu3/90YXoBtoPyFNyIpsn3GtsJzZsrYWQh78xSWg==",
"license": "MIT",
"dependencies": {
"color": "^4.2.3"
},
"peerDependencies": {
"@react-native-masked-view/masked-view": ">= 0.2.0",
- "@react-navigation/native": "^7.1.6",
+ "@react-navigation/native": "^7.1.9",
"react": ">= 18.2.0",
"react-native": "*",
"react-native-safe-area-context": ">= 4.0.0"
@@ -3950,65 +3958,29 @@
}
},
"node_modules/@react-navigation/native": {
- "version": "7.1.6",
- "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-7.1.6.tgz",
- "integrity": "sha512-XcfygfHDfAgf2iC4rNBc67Yy0M1aYRGNeNKqja5AJPFZoBQhAEAxKCwHsH4g3qU0zIbzLCthoSl5107dBjoeZw==",
+ "version": "7.1.9",
+ "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-7.1.9.tgz",
+ "integrity": "sha512-/A0oBwZIeD23o4jsnB0fEyKmKS+l4LAbJP/ioVvsGEubGp+sc5ouQNranOh7JwR0R1eX0MjcsLKprEwB+nztdw==",
"license": "MIT",
"dependencies": {
- "@react-navigation/core": "^7.8.5",
+ "@react-navigation/core": "^7.9.2",
"escape-string-regexp": "^4.0.0",
"fast-deep-equal": "^3.1.3",
- "nanoid": "3.3.8",
- "use-latest-callback": "^0.2.1"
+ "nanoid": "^3.3.11",
+ "use-latest-callback": "^0.2.3"
},
"peerDependencies": {
"react": ">= 18.2.0",
"react-native": "*"
}
},
- "node_modules/@react-navigation/native/node_modules/nanoid": {
- "version": "3.3.8",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
- "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "MIT",
- "bin": {
- "nanoid": "bin/nanoid.cjs"
- },
- "engines": {
- "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
- }
- },
"node_modules/@react-navigation/routers": {
- "version": "7.3.5",
- "resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-7.3.5.tgz",
- "integrity": "sha512-SBh/3G7pURIQfIwG4OnAfLvq0E4+l1Ii6577z22cIhWIrTOHFXg0rMxC7ft/amzxYn+iG2nYa4dONRd+xIs+yg==",
+ "version": "7.3.7",
+ "resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-7.3.7.tgz",
+ "integrity": "sha512-5ffgrefOs2zWqcCVX+OKn+RDx0puopQtxqetegFrTfWQ6pGXdY/5v4kBpPwaOFrNEeE/LPbHt9IJaJuvyhB7RA==",
"license": "MIT",
"dependencies": {
- "nanoid": "3.3.8"
- }
- },
- "node_modules/@react-navigation/routers/node_modules/nanoid": {
- "version": "3.3.8",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
- "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "MIT",
- "bin": {
- "nanoid": "bin/nanoid.cjs"
- },
- "engines": {
- "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ "nanoid": "^3.3.11"
}
},
"node_modules/@react-navigation/stack": {
@@ -6455,6 +6427,17 @@
"react": "*"
}
},
+ "node_modules/expo-linear-gradient": {
+ "version": "14.0.2",
+ "resolved": "https://registry.npmjs.org/expo-linear-gradient/-/expo-linear-gradient-14.0.2.tgz",
+ "integrity": "sha512-nvac1sPUfFFJ4mY25UkvubpUV/olrBH+uQw5k+beqSvQaVQiUfFtYzfRr+6HhYBNb4AEsOtpsCRkpDww3M2iGQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "expo": "*",
+ "react": "*",
+ "react-native": "*"
+ }
+ },
"node_modules/expo-modules-autolinking": {
"version": "2.0.8",
"resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-2.0.8.tgz",
@@ -10864,6 +10847,16 @@
"react-native": ">=0.48.4"
}
},
+ "node_modules/react-native-linear-gradient": {
+ "version": "2.8.3",
+ "resolved": "https://registry.npmjs.org/react-native-linear-gradient/-/react-native-linear-gradient-2.8.3.tgz",
+ "integrity": "sha512-KflAXZcEg54PXkLyflaSZQ3PJp4uC4whM7nT/Uot9m0e/qxFV3p6uor1983D1YOBJbJN7rrWdqIjq0T42jOJyA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "*",
+ "react-native": "*"
+ }
+ },
"node_modules/react-native-modal": {
"version": "14.0.0-rc.1",
"resolved": "https://registry.npmjs.org/react-native-modal/-/react-native-modal-14.0.0-rc.1.tgz",
@@ -10918,9 +10911,9 @@
}
},
"node_modules/react-native-safe-area-context": {
- "version": "4.12.0",
- "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.12.0.tgz",
- "integrity": "sha512-ukk5PxcF4p3yu6qMZcmeiZgowhb5AsKRnil54YFUUAXVIS7PJcMHGGC+q44fCiBg44/1AJk5njGMez1m9H0BVQ==",
+ "version": "4.14.1",
+ "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.14.1.tgz",
+ "integrity": "sha512-+tUhT5WBl8nh5+P+chYhAjR470iCByf9z5EYdCEbPaAK3Yfzw+o8VRPnUgmPAKlSccOgQBxx3NOl/Wzckn9ujg==",
"license": "MIT",
"peerDependencies": {
"react": "*",
diff --git a/package.json b/package.json
index 8102387..1a5d1a6 100644
--- a/package.json
+++ b/package.json
@@ -11,20 +11,23 @@
"dependencies": {
"@react-native-async-storage/async-storage": "1.23.1",
"@react-native-masked-view/masked-view": "0.3.2",
+ "@react-navigation/bottom-tabs": "^7.3.13",
"@react-navigation/native": "^7.1.6",
"@react-navigation/stack": "^7.2.10",
"axios": "^1.9.0",
"expo": "~52.0.46",
+ "expo-linear-gradient": "~14.0.2",
"expo-status-bar": "~2.0.1",
"react": "18.3.1",
"react-native": "0.76.9",
"react-native-awesome-alerts": "^2.0.0",
"react-native-gesture-handler": "~2.20.2",
"react-native-keyboard-aware-scroll-view": "^0.9.5",
+ "react-native-linear-gradient": "^2.8.3",
"react-native-modal": "^14.0.0-rc.1",
"react-native-qrcode-svg": "^6.3.15",
"react-native-reanimated": "~3.16.1",
- "react-native-safe-area-context": "^4.12.0",
+ "react-native-safe-area-context": "^4.14.1",
"react-native-screens": "~4.4.0",
"react-native-vector-icons": "^10.2.0",
"zustand": "^5.0.4"
diff --git a/pages/AlertList.js b/pages/AlertList.js
new file mode 100644
index 0000000..2ec3903
--- /dev/null
+++ b/pages/AlertList.js
@@ -0,0 +1,16 @@
+import React from 'react';
+import { View } from 'react-native';
+import MenuList from '../components/lists/MenuList';
+
+const alertlist = [
+ { label: '알림 제목 1', message: '알림 내용 1' },
+ { label: '알림 재목 2', message: '알림 내용 2' },
+];
+
+export default function AlertList() {
+ return (
+
+
+
+ );
+}
diff --git a/pages/MainPage.js b/pages/MainPage.js
index 75e4f3a..5e2d77d 100644
--- a/pages/MainPage.js
+++ b/pages/MainPage.js
@@ -131,10 +131,6 @@ const MainPage = () => {
userVC={userVC}
initialIndex={initialIndex >= 0 ? initialIndex : 0} // 처음 보여줄 카드 인덱스
/>
-
-
-
-
);
};
diff --git a/pages/MyPage.js b/pages/MyPage.js
index 0978053..1709457 100644
--- a/pages/MyPage.js
+++ b/pages/MyPage.js
@@ -1,7 +1,7 @@
-import React, { useState } from 'react';
+import React, { useState, useEffect } from 'react';
import { View, Text } from 'react-native';
import { styles } from './styles/MyPage.styles';
-import PasswordConfirmModal from '../modals/PasswordConfirmModal';
+// import PasswordConfirmModal from '../modals/PasswordConfirmModal';
import WaveHeader from '../components/headers/WaveHeader';
import NormalInput from '../components/textinputs/NormalInput';
import GrayButton from '../components/buttons/GrayButton';
@@ -12,8 +12,6 @@ import { useAuthStore } from '../stores/authStore';
export default function MyPage() {
const { accessToken, clearAccessToken } = useAuthStore();
- const [isVerified, setIsVerified] = useState(false); // 비밀번호 인증 여부
- const [isModalVisible, setIsModalVisible] = useState(true);
// Alert 관리 상태변수
const [showUpdatePasswordConfirmAlert, setShowUpdatePasswordConfirmAlert] = useState(false);
@@ -31,27 +29,27 @@ export default function MyPage() {
const navigation = useNavigation();
- // 인증 완료 시, 모달 창 닫음
- const handleCloseModal = async () => {
- setIsModalVisible(false);
- setIsVerified(true);
-
+ useEffect(() => {
// 회원 정보 불러오기
- try {
- if (!accessToken) {
- throw new Error('토큰이 존재하지 않습니다.');
+ const fetchUserInfo = async () => {
+ try {
+ if (!accessToken) {
+ throw new Error('토큰이 존재하지 않습니다.');
+ }
+ const data = await getMyInfo();
+ setUserInfo({
+ name: data.name,
+ birth: data.birthDate,
+ contact: data.contact,
+ email: data.email,
+ });
+ } catch (error) {
+ console.log('내 정보 조회 실패:', error.response.data);
}
- const data = await getMyInfo();
- setUserInfo({
- name: data.name,
- birth: data.birthDate,
- contact: data.contact,
- email: data.email,
- });
- } catch (error) {
- console.log('내 정보 조회 실패:', error.response.data);
- }
- };
+ };
+
+ fetchUserInfo();
+ }, [accessToken]);
// 로그아웃 버튼 클릭 핸들러
const handleLogout = () => {
@@ -115,71 +113,64 @@ export default function MyPage() {
return (
- {/* 비밀번호 인증 완료 시, 본문 보이도록 설정 */}
- {isVerified && (
- <>
-
- 마이 페이지
-
-
-
-
-
-
- |
-
- |
-
-
-
- setShowUpdatePasswordConfirmAlert(false)}
- />
-
- setShowDeleteUserConfirmAlert(false)}
- />
-
- >
- )}
-
-
+
+ 마이 페이지
+
+
+
+
+
+
+ |
+
+ |
+
+
+
+ setShowUpdatePasswordConfirmAlert(false)}
+ />
+
+ setShowDeleteUserConfirmAlert(false)}
+ />
+
);
}
diff --git a/stores/modalStore.js b/stores/modalStore.js
new file mode 100644
index 0000000..11b539a
--- /dev/null
+++ b/stores/modalStore.js
@@ -0,0 +1,21 @@
+import { create } from 'zustand';
+
+export const useModalStore = create((set) => ({
+ isPasswordModalVisible: false,
+ pendingTab: null, // 이동 시도한 탭 이름
+ prevTab: '', // 이전 탭 이름
+ showPasswordModal: (tabName, prevTab) =>
+ set({
+ isPasswordModalVisible: true,
+ pendingTab: tabName,
+ prevTab,
+ }),
+ hidePasswordModal: () =>
+ set({
+ isPasswordModalVisible: false,
+ pendingTab: null,
+ }),
+ // 일단 이건 킾
+ //isVerified: false,
+ //setVerified: (verified) => set({ isVerified: verified }),
+}));