From f2138b4fdd2d7b40aa03731c9530cc25b9fb1b81 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=EA=B3=A0=EB=B3=91=EC=B0=AC?=
<70642609+byungchanKo99@users.noreply.github.com>
Date: Sun, 17 Nov 2024 05:18:16 +0900
Subject: [PATCH 1/5] =?UTF-8?q?[SZ-533]refector:=20=ED=8D=BC=EB=84=90=20?=
=?UTF-8?q?=EC=8A=A4=ED=85=9D1=20=ED=99=94=EB=A9=B4=20=EB=B6=84=EB=A6=AC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
app/{ => funnelScreen}/FunnelScreen.jsx | 84 ++++---------------
app/funnelScreen/FunnelStepOne.tsx | 105 ++++++++++++++++++++++++
2 files changed, 120 insertions(+), 69 deletions(-)
rename app/{ => funnelScreen}/FunnelScreen.jsx (69%)
create mode 100644 app/funnelScreen/FunnelStepOne.tsx
diff --git a/app/FunnelScreen.jsx b/app/funnelScreen/FunnelScreen.jsx
similarity index 69%
rename from app/FunnelScreen.jsx
rename to app/funnelScreen/FunnelScreen.jsx
index f484ccd..16d0566 100644
--- a/app/FunnelScreen.jsx
+++ b/app/funnelScreen/FunnelScreen.jsx
@@ -1,23 +1,21 @@
/* eslint-disable react/no-unstable-nested-components */
import React, { useContext } from 'react';
import { StyleSheet, Platform, Linking } from 'react-native';
-import { useFunnel } from '../hooks/funnel/useFunnel';
+import { useFunnel } from '../../hooks/funnel/useFunnel';
import {
Layout,
Text,
- Select,
- SelectItem,
Button,
TopNavigation,
TopNavigationAction,
Icon,
} from '@ui-kitten/components';
-import * as Animatable from 'react-native-animatable';
import messaging from '@react-native-firebase/messaging';
import { useRouter } from 'expo-router';
import { FunnelContext } from '@/contexts/FunnelContext';
-import useModal from '../hooks/common/useModal';
-import ConfirmModal from '../components/common/molecules/ConfirmModal';
+import useModal from '../../hooks/common/useModal';
+import ConfirmModal from '../../components/common/molecules/ConfirmModal';
+import FunnelStepOne from './FunnelStepOne';
const FunnelScreen = () => {
const { Funnel, setStep, goBack, currentStep } = useFunnel('Step1');
@@ -126,69 +124,17 @@ const FunnelScreen = () => {
{/* Step 1 */}
-
-
- 직업과 나이를 선택해주세요
-
-
- {/* 직업 드롭다운 */}
-
-
- {/* 직업이 선택되면 나이 드롭다운 표시 */}
- {selectedJobIndex !== null && (
-
-
-
- )}
-
- {/* 나이가 선택되면 다음 버튼 표시 */}
- {selectedAgeIndex !== null && (
-
-
-
- )}
-
+
{/* Step 2 */}
diff --git a/app/funnelScreen/FunnelStepOne.tsx b/app/funnelScreen/FunnelStepOne.tsx
new file mode 100644
index 0000000..2e167c3
--- /dev/null
+++ b/app/funnelScreen/FunnelStepOne.tsx
@@ -0,0 +1,105 @@
+import React from 'react';
+import {
+ Button,
+ Layout,
+ Select,
+ SelectItem,
+ Text,
+} from '@ui-kitten/components';
+import * as Animatable from 'react-native-animatable';
+import { StyleSheet } from 'react-native';
+
+const FunnelStepOne = ({
+ selectedJobIndex,
+ selectedAgeIndex,
+ setSelectedJobIndex,
+ setSelectedAgeIndex,
+ jobOptions,
+ ageOptions,
+ setStep,
+ ageSelectRef,
+ nextButtonRef,
+}) => {
+ return (
+
+
+ 직업과 나이를 선택해주세요
+
+
+ {/* 직업 드롭다운 */}
+
+
+ {/* 직업이 선택되면 나이 드롭다운 표시 */}
+ {selectedJobIndex !== null && (
+
+
+
+ )}
+
+ {/* 나이가 선택되면 다음 버튼 표시 */}
+ {selectedAgeIndex !== null && (
+
+
+
+ )}
+
+ );
+};
+
+export default FunnelStepOne;
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ justifyContent: 'center',
+ padding: 20,
+ backgroundColor: '#FFFFFF',
+ },
+ title: {
+ textAlign: 'center',
+ marginBottom: 30,
+ },
+ select: {
+ marginVertical: 10,
+ },
+ button: {
+ marginTop: 30,
+ },
+ animatableView: {
+ width: '100%',
+ },
+});
From c1739cd6403a233a68537cf7438a19af13228b81 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=EA=B3=A0=EB=B3=91=EC=B0=AC?=
<70642609+byungchanKo99@users.noreply.github.com>
Date: Sun, 17 Nov 2024 16:21:14 +0900
Subject: [PATCH 2/5] =?UTF-8?q?[SZ-533]feat:=20=EC=98=A8=EB=B3=B4=EB=94=A9?=
=?UTF-8?q?=20=EB=94=94=EC=9E=90=EC=9D=B8=20=EC=A0=81=EC=9A=A9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
app/_layout.jsx | 143 +++++----
app/funnelScreen/FunnelScreen.jsx | 215 -------------
app/funnelScreen/FunnelStepOne.tsx | 105 -------
app/funnelView/funnelView.jsx | 82 +++++
app/funnelView/stepFiveView.tsx | 192 ++++++++++++
app/funnelView/stepFourView.tsx | 168 ++++++++++
app/funnelView/stepOneView.tsx | 138 ++++++++
app/funnelView/stepThreeView.tsx | 312 +++++++++++++++++++
app/funnelView/stepTwoView.tsx | 142 +++++++++
components/common/molecules/ConfirmModal.tsx | 38 ++-
hooks/auth/useLogin.js | 7 +-
theme/fontStyles.js | 134 +++-----
theme/mapping.json | 9 +
theme/theme.json | 23 +-
14 files changed, 1205 insertions(+), 503 deletions(-)
delete mode 100644 app/funnelScreen/FunnelScreen.jsx
delete mode 100644 app/funnelScreen/FunnelStepOne.tsx
create mode 100644 app/funnelView/funnelView.jsx
create mode 100644 app/funnelView/stepFiveView.tsx
create mode 100644 app/funnelView/stepFourView.tsx
create mode 100644 app/funnelView/stepOneView.tsx
create mode 100644 app/funnelView/stepThreeView.tsx
create mode 100644 app/funnelView/stepTwoView.tsx
diff --git a/app/_layout.jsx b/app/_layout.jsx
index 96dcb22..25f339e 100755
--- a/app/_layout.jsx
+++ b/app/_layout.jsx
@@ -16,6 +16,7 @@ import React from 'react';
import { useTranslation } from 'react-i18next';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { default as mapping } from '../theme/mapping.json';
+import FunnelProvider from '@/contexts/FunnelContext';
const SENTRY_MODE = env.SENTRY_MODE;
Sentry.init({
@@ -53,73 +54,81 @@ const RootLayout = () => {
theme={{ ...eva.light, ...theme }}
customMapping={mapping}
>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/funnelScreen/FunnelScreen.jsx b/app/funnelScreen/FunnelScreen.jsx
deleted file mode 100644
index 16d0566..0000000
--- a/app/funnelScreen/FunnelScreen.jsx
+++ /dev/null
@@ -1,215 +0,0 @@
-/* eslint-disable react/no-unstable-nested-components */
-import React, { useContext } from 'react';
-import { StyleSheet, Platform, Linking } from 'react-native';
-import { useFunnel } from '../../hooks/funnel/useFunnel';
-import {
- Layout,
- Text,
- Button,
- TopNavigation,
- TopNavigationAction,
- Icon,
-} from '@ui-kitten/components';
-import messaging from '@react-native-firebase/messaging';
-import { useRouter } from 'expo-router';
-import { FunnelContext } from '@/contexts/FunnelContext';
-import useModal from '../../hooks/common/useModal';
-import ConfirmModal from '../../components/common/molecules/ConfirmModal';
-import FunnelStepOne from './FunnelStepOne';
-
-const FunnelScreen = () => {
- const { Funnel, setStep, goBack, currentStep } = useFunnel('Step1');
- const route = useRouter();
- const { setFunnelDone } = useContext(FunnelContext);
- const { isVisible, setIsVisible } = useModal();
-
- // 직업과 나이 옵션 데이터
- const jobOptions = ['개발자', '디자이너', '기획자', '마케터', '기타'];
- const ageOptions = ['10대', '20대', '30대', '40대', '50대 이상'];
-
- // 상태 관리
- const [selectedJobIndex, setSelectedJobIndex] = React.useState(null);
- const [selectedAgeIndex, setSelectedAgeIndex] = React.useState(null);
-
- // 알림 권한 요청 함수
- const requestNotificationPermission = async () => {
- try {
- const authStatus = await messaging().hasPermission();
-
- const enabled =
- authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
- authStatus === messaging.AuthorizationStatus.PROVISIONAL;
-
- if (enabled) {
- // 권한이 이미 허용됨
- return true;
- } else {
- if (Platform.OS === 'ios') {
- // iOS에서 권한 요청
- const authStatusOnIOS = await messaging().requestPermission();
- const enabledOnIOS =
- authStatusOnIOS === messaging.AuthorizationStatus.AUTHORIZED ||
- authStatusOnIOS === messaging.AuthorizationStatus.PROVISIONAL;
- return enabledOnIOS;
- } else if (Platform.OS === 'android') {
- // Android에서 버전 확인
- const androidVersion = Platform.Version;
- if (androidVersion >= 33) {
- // Android 13 이상에서 권한 요청
- const result = await messaging().requestPermission();
- const enabledOnAndroid =
- result === messaging.AuthorizationStatus.AUTHORIZED;
- return enabledOnAndroid;
- } else {
- // Android 13 미만은 권한 요청 불필요
- return true;
- }
- } else {
- // 기타 플랫폼 (web 등)
- return false;
- }
- }
- } catch (error) {
- console.error('Notification permission error:', error);
- return false;
- }
- };
-
- // 애니메이션 Ref
- const ageSelectRef = React.useRef(null);
- const nextButtonRef = React.useRef(null);
-
- // 직업이 선택되었을 때 나이 드롭다운 애니메이션 실행
- React.useEffect(() => {
- if (selectedJobIndex !== null && ageSelectRef.current) {
- ageSelectRef.current.fadeInUp(500);
- }
- }, [selectedJobIndex]);
-
- // 나이가 선택되었을 때 다음 버튼 애니메이션 실행
- React.useEffect(() => {
- if (selectedAgeIndex !== null && nextButtonRef.current) {
- nextButtonRef.current.fadeInUp(500);
- }
- }, [selectedAgeIndex]);
-
- // Back Icon Component
- const BackIcon = props => ;
-
- // Back Action Component
- const BackAction = () => (
-
- );
-
- // Handle Back Button Press
- const handleBackAction = () => {
- if (currentStep === 'Step1') {
- route.back();
- } else {
- goBack();
- }
- };
-
- return (
-
- {/* 커스텀 헤더 */}
-
-
- {/* 펀넬 내용 */}
-
- {/* Step 1 */}
-
-
-
-
- {/* Step 2 */}
-
-
-
- 알림을 허용하시겠습니까?
-
-
-
- {
- setIsVisible(false);
- setStep('Step3');
- }}
- onCancel={() => {
- setIsVisible(false);
- Linking.openSettings();
- }}
- titleKey=""
- messageKey=""
- confirmTextKey=""
- cancelTextKey=""
- />
-
-
- {/* Step 3 */}
-
-
-
- 설정이 완료되었습니다!
-
-
-
-
-
-
- );
-};
-
-export default FunnelScreen;
-
-const styles = StyleSheet.create({
- container: {
- flex: 1,
- justifyContent: 'center',
- padding: 20,
- backgroundColor: '#FFFFFF',
- },
- title: {
- textAlign: 'center',
- marginBottom: 30,
- },
- select: {
- marginVertical: 10,
- },
- button: {
- marginTop: 30,
- },
- animatableView: {
- width: '100%',
- },
-});
diff --git a/app/funnelScreen/FunnelStepOne.tsx b/app/funnelScreen/FunnelStepOne.tsx
deleted file mode 100644
index 2e167c3..0000000
--- a/app/funnelScreen/FunnelStepOne.tsx
+++ /dev/null
@@ -1,105 +0,0 @@
-import React from 'react';
-import {
- Button,
- Layout,
- Select,
- SelectItem,
- Text,
-} from '@ui-kitten/components';
-import * as Animatable from 'react-native-animatable';
-import { StyleSheet } from 'react-native';
-
-const FunnelStepOne = ({
- selectedJobIndex,
- selectedAgeIndex,
- setSelectedJobIndex,
- setSelectedAgeIndex,
- jobOptions,
- ageOptions,
- setStep,
- ageSelectRef,
- nextButtonRef,
-}) => {
- return (
-
-
- 직업과 나이를 선택해주세요
-
-
- {/* 직업 드롭다운 */}
-
-
- {/* 직업이 선택되면 나이 드롭다운 표시 */}
- {selectedJobIndex !== null && (
-
-
-
- )}
-
- {/* 나이가 선택되면 다음 버튼 표시 */}
- {selectedAgeIndex !== null && (
-
-
-
- )}
-
- );
-};
-
-export default FunnelStepOne;
-
-const styles = StyleSheet.create({
- container: {
- flex: 1,
- justifyContent: 'center',
- padding: 20,
- backgroundColor: '#FFFFFF',
- },
- title: {
- textAlign: 'center',
- marginBottom: 30,
- },
- select: {
- marginVertical: 10,
- },
- button: {
- marginTop: 30,
- },
- animatableView: {
- width: '100%',
- },
-});
diff --git a/app/funnelView/funnelView.jsx b/app/funnelView/funnelView.jsx
new file mode 100644
index 0000000..d1c7c7b
--- /dev/null
+++ b/app/funnelView/funnelView.jsx
@@ -0,0 +1,82 @@
+/* eslint-disable react/no-unstable-nested-components */
+import React from 'react';
+import { useFunnel } from '../../hooks/funnel/useFunnel';
+import {
+ Layout,
+ TopNavigation,
+ TopNavigationAction,
+ Icon,
+} from '@ui-kitten/components';
+import { useRouter } from 'expo-router';
+import NameInputScreen from './stepOneView';
+import AgeSelectionScreen from './stepTwoView';
+import SleepTimeScreen from './stepThreeView';
+import DelayReasonsScreen from './stepFourView';
+import CompletionScreen from './stepFiveView';
+
+const FunnelScreen = () => {
+ const { Funnel, setStep, goBack, currentStep } = useFunnel('Step1');
+ const route = useRouter();
+
+ const BackIcon = props => ;
+
+ const BackAction = () => (
+
+ );
+
+ const handleBackAction = () => {
+ if (currentStep === 'Step1') {
+ route.back();
+ } else {
+ goBack();
+ }
+ };
+
+ return (
+
+
+
+
+
+ {
+ setStep('Step2');
+ }}
+ />
+
+
+ {
+ setStep('Step3');
+ }}
+ />
+
+ SleepTimeScreen
+
+ {
+ setStep('Step4');
+ }}
+ />
+
+
+ {
+ setStep('Step5');
+ }}
+ />
+
+
+
+
+
+
+ );
+};
+
+export default FunnelScreen;
diff --git a/app/funnelView/stepFiveView.tsx b/app/funnelView/stepFiveView.tsx
new file mode 100644
index 0000000..27b1583
--- /dev/null
+++ b/app/funnelView/stepFiveView.tsx
@@ -0,0 +1,192 @@
+import React, { useEffect, useState } from 'react';
+import {
+ Layout,
+ Text,
+ Button,
+ StyleService,
+ useStyleSheet,
+} from '@ui-kitten/components';
+import { View, Platform, Linking, TouchableOpacity } from 'react-native';
+import * as Animatable from 'react-native-animatable';
+import { scale, verticalScale, moderateScale } from 'react-native-size-matters';
+import { router } from 'expo-router';
+import messaging from '@react-native-firebase/messaging';
+import ConfirmModal from '@/components/common/molecules/ConfirmModal';
+import useModal from '@/hooks/common/useModal';
+
+const requestNotificationPermission = async () => {
+ try {
+ const authStatus = await messaging().hasPermission();
+
+ const enabled =
+ authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
+ authStatus === messaging.AuthorizationStatus.PROVISIONAL;
+
+ if (enabled) {
+ // 권한이 이미 허용됨
+ return true;
+ } else {
+ if (Platform.OS === 'ios') {
+ // iOS에서 권한 요청
+ const authStatusOnIOS = await messaging().requestPermission();
+ const enabledOnIOS =
+ authStatusOnIOS === messaging.AuthorizationStatus.AUTHORIZED ||
+ authStatusOnIOS === messaging.AuthorizationStatus.PROVISIONAL;
+ return enabledOnIOS;
+ } else if (Platform.OS === 'android') {
+ // Android에서 버전 확인
+ const androidVersion = Platform.Version;
+ if (androidVersion >= 33) {
+ // Android 13 이상에서 권한 요청
+ const result = await messaging().requestPermission();
+ const enabledOnAndroid =
+ result === messaging.AuthorizationStatus.AUTHORIZED;
+ return enabledOnAndroid;
+ } else {
+ // Android 13 미만은 권한 요청 불필요
+ return true;
+ }
+ } else {
+ // 기타 플랫폼 (web 등)
+ return false;
+ }
+ }
+ } catch (error) {
+ console.error('Notification permission error:', error);
+ return false;
+ }
+};
+
+const CompletionScreen = () => {
+ const [showNotificationScreen, setShowNotificationScreen] = useState(false);
+ const styles = useStyleSheet(themedStyles);
+ const { isVisible, setIsVisible } = useModal();
+
+ useEffect(() => {
+ // 3초 후에 알림 허용 화면으로 전환
+ const timer = setTimeout(() => {
+ setShowNotificationScreen(true);
+ }, 3000);
+
+ return () => clearTimeout(timer);
+ }, []);
+
+ const handleAllowNotifications = async () => {
+ const granted = await requestNotificationPermission();
+ if (granted) {
+ router.dismissAll();
+ router.replace('/(tabs)');
+ } else {
+ setIsVisible(true);
+ }
+ };
+
+ if (!showNotificationScreen) {
+ return (
+
+
+ 환영합니다 OOO님
+ 가입이 완료되었어요
+
+
+ );
+ }
+
+ return (
+
+
+
+ 할 일을 잊지 않도록{'\n'}알려드릴게요
+
+
+ setIsVisible(true)}>
+ 지금은 괜찮아요
+
+
+
+
+
+ {
+ setIsVisible(false);
+ Linking.openSettings();
+ router.dismissAll();
+ router.replace('/(tabs)');
+ }}
+ onCancel={() => {
+ setIsVisible(false);
+ router.dismissAll();
+ router.replace('/(tabs)');
+ }}
+ titleKey="views.index.AlertModalTitle"
+ messageKey="views.index.AlertModalMessage"
+ confirmTextKey="common.yes"
+ cancelTextKey="common.no"
+ />
+
+
+ );
+};
+
+const themedStyles = StyleService.create({
+ container: {
+ flex: 1,
+ backgroundColor: 'background-basic-color-1',
+ paddingHorizontal: scale(20),
+ },
+ completionContainer: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ welcomeText: {
+ fontSize: moderateScale(18),
+ marginBottom: verticalScale(8),
+ color: 'text-basic-color',
+ },
+ completionText: {
+ fontSize: moderateScale(24),
+ fontWeight: 'bold',
+ color: 'text-basic-color',
+ },
+ notificationContainer: {
+ flex: 1,
+ justifyContent: 'space-between',
+ paddingTop: verticalScale(88),
+ paddingBottom: verticalScale(30),
+ },
+ notificationTitle: {
+ fontSize: moderateScale(28),
+ fontWeight: 'bold',
+ textAlign: 'left',
+ lineHeight: moderateScale(35),
+ },
+ buttonContainer: {
+ gap: verticalScale(16),
+ },
+ skipText: {
+ textAlign: 'center',
+ color: 'text-hint-color',
+ fontSize: moderateScale(14),
+ },
+ button: {
+ borderRadius: moderateScale(8),
+ },
+});
+
+export default CompletionScreen;
diff --git a/app/funnelView/stepFourView.tsx b/app/funnelView/stepFourView.tsx
new file mode 100644
index 0000000..5923b03
--- /dev/null
+++ b/app/funnelView/stepFourView.tsx
@@ -0,0 +1,168 @@
+import React, { useState } from 'react';
+import {
+ Layout,
+ Text,
+ Button,
+ StyleService,
+ useStyleSheet,
+} from '@ui-kitten/components';
+import { View, TouchableOpacity } from 'react-native';
+import { scale, verticalScale, moderateScale } from 'react-native-size-matters';
+import * as Animatable from 'react-native-animatable';
+
+const DelayReasonsScreen = ({ onNext, currentStep = 4, totalSteps = 5 }) => {
+ const [selectedReasons, setSelectedReasons] = useState([]);
+ const styles = useStyleSheet(themedStyles);
+
+ const reasons = [
+ '할 일들이 너무 크게 느껴져요',
+ '꾸준히 이어가기 어려워요',
+ '우선순위를 정하기 어려워요',
+ '동기 부여가 없어요',
+ '집중력이 부족해요',
+ ];
+
+ const toggleReason = index => {
+ setSelectedReasons(prev => {
+ const isSelected = prev.includes(index);
+ if (isSelected) {
+ return prev.filter(i => i !== index);
+ } else if (prev.length < 3) {
+ return [...prev, index];
+ }
+ return prev;
+ });
+ };
+
+ const renderStepIndicators = () => (
+
+ {[...Array(totalSteps)].map((_, index) => (
+
+ ))}
+
+ );
+
+ return (
+
+ {renderStepIndicators()}
+
+ 미루는 이유를 알려주세요
+ 최대 3개까지 선택할 수 있어요
+
+
+ {reasons.map((reason, index) => (
+ toggleReason(index)}
+ >
+
+ {reason}
+
+
+ ))}
+
+ {selectedReasons.length > 0 && (
+
+
+
+ )}
+
+
+ );
+};
+
+const themedStyles = StyleService.create({
+ container: {
+ flex: 1,
+ backgroundColor: 'background-basic-color-1',
+ paddingHorizontal: scale(20),
+ },
+ contentContainer: {
+ flex: 1,
+ justifyContent: 'space-between',
+ paddingBottom: verticalScale(20),
+ },
+ stepContainer: {
+ flexDirection: 'row',
+ justifyContent: 'center',
+ alignItems: 'center',
+ marginBottom: verticalScale(40),
+ },
+ stepIndicator: {
+ width: scale(8),
+ height: scale(8),
+ borderRadius: scale(4),
+ backgroundColor: 'color-basic-400',
+ marginHorizontal: scale(3),
+ },
+ activeStepIndicator: {
+ backgroundColor: 'color-primary-500',
+ width: scale(16),
+ height: scale(8),
+ },
+ title: {
+ textAlign: 'left',
+ fontSize: moderateScale(28),
+ fontWeight: 'bold',
+ marginBottom: verticalScale(8),
+ },
+ subtitle: {
+ textAlign: 'left',
+ fontSize: moderateScale(16),
+ color: 'text-hint-color',
+ marginBottom: verticalScale(30),
+ },
+ reasonsContainer: {
+ gap: verticalScale(10),
+ },
+ reasonOption: {
+ padding: moderateScale(16),
+ borderRadius: moderateScale(8),
+ backgroundColor: 'background-basic-color-2',
+ borderWidth: 1,
+ borderColor: 'transparent',
+ },
+ selectedReason: {
+ backgroundColor: 'color-primary-100',
+ borderColor: 'color-primary-500',
+ },
+ reasonText: {
+ fontSize: moderateScale(16),
+ color: 'text-basic-color',
+ },
+ selectedReasonText: {
+ color: 'color-primary-700',
+ fontWeight: '600',
+ },
+ button: {
+ borderRadius: moderateScale(8),
+ },
+});
+
+export default DelayReasonsScreen;
diff --git a/app/funnelView/stepOneView.tsx b/app/funnelView/stepOneView.tsx
new file mode 100644
index 0000000..57a4fa9
--- /dev/null
+++ b/app/funnelView/stepOneView.tsx
@@ -0,0 +1,138 @@
+import React from 'react';
+import {
+ Layout,
+ Input,
+ Button,
+ Text,
+ StyleService,
+ useStyleSheet,
+} from '@ui-kitten/components';
+import * as Animatable from 'react-native-animatable';
+import { View, KeyboardAvoidingView, Platform } from 'react-native';
+import { scale, verticalScale, moderateScale } from 'react-native-size-matters';
+
+const NameInputScreen = ({ onNext, currentStep = 0, totalSteps = 5 }) => {
+ const [name, setName] = React.useState('');
+ const styles = useStyleSheet(themedStyles);
+
+ const renderStepIndicators = () => {
+ return (
+
+ {[...Array(totalSteps)].map((_, index) => (
+
+ ))}
+
+ );
+ };
+
+ return (
+
+
+ {renderStepIndicators()}
+
+ 이름을 입력해주세요
+
+
+
+
+
+
+ {name !== '' && (
+
+
+
+ )}
+
+
+
+ );
+};
+
+const themedStyles = StyleService.create({
+ keyboardAvoidingView: {
+ flex: 1,
+ },
+ container: {
+ flex: 1,
+ backgroundColor: 'background-basic-color-1',
+ paddingHorizontal: scale(20),
+ },
+ contentContainer: {
+ flex: 1,
+ justifyContent: 'space-between',
+ paddingBottom: verticalScale(20), // 하단 간격
+ },
+ stepContainer: {
+ flexDirection: 'row',
+ justifyContent: 'center',
+ alignItems: 'center',
+ marginBottom: verticalScale(40),
+ },
+ stepIndicator: {
+ width: scale(8),
+ height: scale(8),
+ borderRadius: scale(4),
+ backgroundColor: 'color-basic-400',
+ marginHorizontal: scale(3),
+ },
+ activeStepIndicator: {
+ backgroundColor: 'color-primary-500',
+ width: scale(16),
+ height: scale(8),
+ },
+ title: {
+ textAlign: 'left',
+ marginBottom: verticalScale(30),
+ fontSize: moderateScale(28),
+ fontWeight: 'bold',
+ color: 'text-basic-color',
+ },
+ inputContainer: {},
+ input: {
+ borderRadius: moderateScale(8),
+ backgroundColor: 'background-basic-color-1',
+ borderColor: 'color-basic-400',
+ },
+ inputText: {
+ fontSize: moderateScale(16),
+ },
+ buttonContainer: {},
+ button: {
+ borderRadius: moderateScale(8),
+ backgroundColor: 'color-primary-default',
+ },
+});
+
+export default NameInputScreen;
diff --git a/app/funnelView/stepThreeView.tsx b/app/funnelView/stepThreeView.tsx
new file mode 100644
index 0000000..ca657ce
--- /dev/null
+++ b/app/funnelView/stepThreeView.tsx
@@ -0,0 +1,312 @@
+import React, { useCallback, useMemo, useRef, useState } from 'react';
+import {
+ Layout,
+ Text,
+ Button,
+ StyleService,
+ useStyleSheet,
+} from '@ui-kitten/components';
+import { View, TouchableOpacity, Pressable } from 'react-native';
+import { scale, verticalScale, moderateScale } from 'react-native-size-matters';
+import BottomSheet, {
+ BottomSheetView,
+ BottomSheetBackdrop,
+} from '@gorhom/bottom-sheet';
+import * as Animatable from 'react-native-animatable';
+
+const SleepTimeScreen = ({ onNext, currentStep = 2, totalSteps = 5 }) => {
+ const [selectedTime, setSelectedTime] = useState(null);
+ const [selectedPeriod, setSelectedPeriod] = useState(null);
+ const [currentSheet, setCurrentSheet] = useState(null);
+ const [showTimeSelector, setShowTimeSelector] = useState(false);
+ const styles = useStyleSheet(themedStyles);
+
+ // refs
+ const timeSheetRef = useRef(null);
+ const periodSheetRef = useRef(null);
+
+ // variables
+ const snapPoints = useMemo(() => ['50%'], []);
+ const timeOptions = Array.from({ length: 12 }, (_, i) => `${i + 1}시`);
+ const periodOptions = ['오전', '오후'];
+
+ // callbacks
+ const handleSheetChanges = useCallback(index => {
+ if (index === -1) {
+ setCurrentSheet(null);
+ }
+ }, []);
+
+ const handleClosePress = useCallback(() => {
+ if (currentSheet === 'time') {
+ timeSheetRef.current?.close();
+ } else if (currentSheet === 'period') {
+ periodSheetRef.current?.close();
+ }
+ setCurrentSheet(null);
+ }, [currentSheet]);
+
+ const handlePeriodSelect = period => {
+ setSelectedPeriod(period);
+ periodSheetRef.current?.close();
+ setShowTimeSelector(true);
+ };
+
+ const handleTimeSelect = time => {
+ setSelectedTime(time);
+ timeSheetRef.current?.close();
+ };
+
+ const renderBackdrop = useCallback(
+ props => (
+
+ ),
+ [],
+ );
+
+ const renderStepIndicators = () => (
+
+ {[...Array(totalSteps)].map((_, index) => (
+
+ ))}
+
+ );
+
+ return (
+
+ {renderStepIndicators()}
+
+ 평소 몇시에 주무시나요?
+
+
+ {
+ setCurrentSheet('period');
+ periodSheetRef.current?.expand();
+ }}
+ >
+
+ {selectedPeriod || '시간대'}
+
+ ∨
+
+
+ {showTimeSelector && (
+
+ {
+ setCurrentSheet('time');
+ timeSheetRef.current?.expand();
+ }}
+ >
+
+ {selectedTime || '시간'}
+
+ ∨
+
+
+ )}
+
+
+ {selectedTime && selectedPeriod && (
+
+
+
+ )}
+
+
+ {/* Time Bottom Sheet */}
+
+
+
+ 시간 선택
+
+ ×
+
+
+
+ {timeOptions.map((time, index) => (
+ handleTimeSelect(time)}
+ >
+ {time}
+
+ ))}
+
+
+
+
+ {/* Period Bottom Sheet */}
+
+
+
+ 시간대 선택
+
+ ×
+
+
+
+ {periodOptions.map((period, index) => (
+ handlePeriodSelect(period)}
+ >
+ {period}
+
+ ))}
+
+
+
+
+ );
+};
+
+const themedStyles = StyleService.create({
+ container: {
+ flex: 1,
+ backgroundColor: 'background-basic-color-1',
+ paddingHorizontal: scale(20),
+ },
+ stepContainer: {
+ flexDirection: 'row',
+ justifyContent: 'center',
+ alignItems: 'center',
+ marginBottom: verticalScale(40),
+ },
+ contentContainer: {
+ flex: 1,
+ justifyContent: 'space-between',
+ paddingBottom: verticalScale(20), // 하단 간격
+ },
+ stepIndicator: {
+ width: scale(8),
+ height: scale(8),
+ borderRadius: scale(4),
+ backgroundColor: 'color-basic-400',
+ marginHorizontal: scale(3),
+ },
+ activeStepIndicator: {
+ backgroundColor: 'color-primary-500',
+ width: scale(16),
+ height: scale(8),
+ },
+ title: {
+ textAlign: 'left',
+ marginBottom: verticalScale(30),
+ fontSize: moderateScale(28),
+ fontWeight: 'bold',
+ },
+ selector: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ padding: moderateScale(15),
+ borderRadius: moderateScale(8),
+ backgroundColor: 'background-basic-color-1',
+ borderWidth: 1,
+ borderColor: 'color-basic-400',
+ marginBottom: verticalScale(10),
+ },
+ placeholderText: {
+ color: 'text-hint-color',
+ },
+ selectedText: {
+ color: 'text-basic-color',
+ },
+ arrow: {
+ color: 'color-basic-600',
+ },
+ bottomSheetContainer: {
+ flex: 1,
+ backgroundColor: 'background-basic-color-1',
+ },
+ bottomSheetHeader: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ padding: moderateScale(15),
+ borderBottomWidth: 1,
+ borderBottomColor: 'color-basic-300',
+ },
+ bottomSheetTitle: {
+ fontSize: moderateScale(16),
+ fontWeight: 'bold',
+ },
+ closeButton: {
+ fontSize: moderateScale(24),
+ color: 'color-basic-600',
+ padding: moderateScale(5),
+ },
+ optionsContainer: {
+ padding: moderateScale(15),
+ },
+ optionItem: {
+ padding: moderateScale(15),
+ },
+ optionText: {
+ textAlign: 'center',
+ fontSize: moderateScale(16),
+ },
+ buttonContainer: {},
+ button: {
+ borderRadius: moderateScale(8),
+ },
+ timeGridContainer: {
+ flexDirection: 'row',
+ flexWrap: 'wrap',
+ padding: moderateScale(15),
+ },
+ timeGridItem: {
+ width: '33.33%',
+ padding: moderateScale(10),
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ selectorContainer: {},
+});
+
+export default SleepTimeScreen;
diff --git a/app/funnelView/stepTwoView.tsx b/app/funnelView/stepTwoView.tsx
new file mode 100644
index 0000000..0e51a9f
--- /dev/null
+++ b/app/funnelView/stepTwoView.tsx
@@ -0,0 +1,142 @@
+import React from 'react';
+import {
+ Layout,
+ StyleService,
+ useStyleSheet,
+ Text,
+ Button,
+} from '@ui-kitten/components';
+import { View, TouchableOpacity } from 'react-native';
+import { scale, verticalScale, moderateScale } from 'react-native-size-matters';
+import * as Animatable from 'react-native-animatable';
+
+const AgeSelectionScreen = ({ onNext, currentStep = 1, totalSteps = 5 }) => {
+ const [selectedAge, setSelectedAge] = React.useState(null);
+ const styles = useStyleSheet(themedStyles);
+
+ const ageRanges = ['10대', '20대', '30대', '40대', '50대 이상'];
+
+ const renderStepIndicators = () => {
+ return (
+
+ {[...Array(totalSteps)].map((_, index) => (
+
+ ))}
+
+ );
+ };
+
+ const renderAgeOption = (age, index) => {
+ const isSelected = selectedAge === index;
+
+ return (
+ setSelectedAge(index)}
+ style={[styles.ageOption, isSelected && styles.selectedAgeOption]}
+ >
+
+ {age}
+
+
+ );
+ };
+
+ return (
+
+ {renderStepIndicators()}
+
+ 연령대를 알려주세요
+
+
+
+ {ageRanges.map((age, index) => renderAgeOption(age, index))}
+
+
+ {selectedAge !== null && (
+
+
+
+ )}
+
+
+ );
+};
+
+const themedStyles = StyleService.create({
+ container: {
+ flex: 1,
+ backgroundColor: 'background-basic-color-1',
+ paddingHorizontal: scale(20),
+ },
+ stepContainer: {
+ flexDirection: 'row',
+ justifyContent: 'center',
+ alignItems: 'center',
+ marginBottom: verticalScale(40),
+ },
+ stepIndicator: {
+ width: scale(8),
+ height: scale(8),
+ borderRadius: scale(4),
+ backgroundColor: 'color-basic-400',
+ marginHorizontal: scale(3),
+ },
+ activeStepIndicator: {
+ backgroundColor: 'color-primary-500',
+ width: scale(16),
+ height: scale(8),
+ },
+ title: {
+ textAlign: 'left',
+ marginBottom: verticalScale(30),
+ fontSize: moderateScale(28),
+ fontWeight: 'bold',
+ color: 'text-basic-color',
+ },
+ optionsContainer: {},
+ ageOption: {
+ backgroundColor: 'background-basic-color-2',
+ padding: moderateScale(16),
+ borderRadius: moderateScale(8),
+ marginBottom: verticalScale(10),
+ },
+ selectedAgeOption: {
+ backgroundColor: 'color-primary-100',
+ },
+ ageText: {
+ fontSize: moderateScale(16),
+ color: 'text-basic-color',
+ },
+ selectedAgeText: {
+ color: 'color-primary-700',
+ fontWeight: 'bold',
+ },
+ button: {
+ borderRadius: moderateScale(8),
+ },
+ buttonContainer: {},
+ contentContainer: {
+ flex: 1,
+ justifyContent: 'space-between',
+ paddingBottom: verticalScale(20), // 하단 간격
+ },
+});
+
+export default AgeSelectionScreen;
diff --git a/components/common/molecules/ConfirmModal.tsx b/components/common/molecules/ConfirmModal.tsx
index 93da1f4..aebb0a6 100644
--- a/components/common/molecules/ConfirmModal.tsx
+++ b/components/common/molecules/ConfirmModal.tsx
@@ -2,10 +2,7 @@ import React from 'react';
import { Modal, Layout, Text, Button, useTheme } from '@ui-kitten/components';
import { StyleSheet, View } from 'react-native';
import { useTranslation } from 'react-i18next';
-import {
- heightPercentage,
- widthPercentage,
-} from '../../../utils/responsiveSize';
+import { scale, verticalScale } from 'react-native-size-matters';
import fontStyles from '../../../theme/fontStyles';
interface ConfirmModalProps {
@@ -67,10 +64,21 @@ const ConfirmModal: React.FC = ({
status="basic"
onPress={() => onCancel()}
>
- {t(cancelTextKey)}
+
+ {t(cancelTextKey)}
+
@@ -83,8 +91,8 @@ const styles = StyleSheet.create({
backgroundColor: 'rgba(0, 0, 0, 0.5)',
},
card: {
- height: heightPercentage(165),
- width: widthPercentage(328),
+ height: verticalScale(165),
+ width: scale(328),
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
@@ -95,27 +103,23 @@ const styles = StyleSheet.create({
borderRadius: 16,
},
textContainer: {
- height: heightPercentage(49),
- width: widthPercentage(296),
+ width: scale(296),
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'space-between',
gap: 8,
},
- text: {
- height: heightPercentage(23),
- },
+ text: {},
buttonContainer: {
- height: heightPercentage(52),
- width: widthPercentage(296),
+ width: scale(296),
flexDirection: 'row',
justifyContent: 'space-around',
alignItems: 'center',
gap: 12,
},
button: {
- width: widthPercentage(142),
- height: heightPercentage(52),
+ width: scale(142),
+ height: verticalScale(52),
alignItems: 'center',
justifyContent: 'center',
borderRadius: 12,
diff --git a/hooks/auth/useLogin.js b/hooks/auth/useLogin.js
index 2617300..8ad3c02 100644
--- a/hooks/auth/useLogin.js
+++ b/hooks/auth/useLogin.js
@@ -33,7 +33,12 @@ const useLogin = () => {
setUserId(jwtTokenData.userId);
setIsLoggedIn(true);
- router.push('/(tabs)');
+ if (user.isNew) {
+ router.push('/funnelView/funnelView');
+ } else {
+ router.push('/funnelView/funnelView');
+ // router.push('/(tabs)');
+ }
}
};
diff --git a/theme/fontStyles.js b/theme/fontStyles.js
index 23575ff..69e5e1b 100644
--- a/theme/fontStyles.js
+++ b/theme/fontStyles.js
@@ -16,114 +16,96 @@ const fontStyles = {
H1: {
B_130: {
fontFamily: BOLD,
- fontSize: 28,
- lineHeight: Math.round(28 * 1.3) + 2,
+ fontSize: moderateScale(28),
},
B_100: {
fontFamily: BOLD,
- fontSize: 28,
- lineHeight: 30,
+ fontSize: moderateScale(28),
},
M_130: {
fontFamily: MEDIUM,
- fontSize: 28,
- lineHeight: Math.round(28 * 1.3) + 2,
+ fontSize: moderateScale(28),
},
M_100: {
fontFamily: MEDIUM,
- fontSize: 28,
- lineHeight: 30,
+ fontSize: moderateScale(28),
},
R_130: {
fontFamily: REGULAR,
- fontSize: 28,
- lineHeight: Math.round(28 * 1.3) + 2,
+ fontSize: moderateScale(28),
},
R_100: {
fontFamily: REGULAR,
- fontSize: 28,
- lineHeight: 30,
+ fontSize: moderateScale(28),
},
},
H2: {
B_130: {
fontFamily: BOLD,
- fontSize: 24,
- lineHeight: Math.round(24 * 1.3) + 2,
+ fontSize: moderateScale(24),
},
B_100: {
fontFamily: BOLD,
- fontSize: 24,
- lineHeight: 26,
+ fontSize: moderateScale(24),
},
M_130: {
fontFamily: MEDIUM,
- fontSize: 24,
- lineHeight: Math.round(24 * 1.3) + 2,
+ fontSize: moderateScale(24),
},
M_100: {
fontFamily: MEDIUM,
- fontSize: 24,
- lineHeight: 26,
+ fontSize: moderateScale(24),
},
R_130: {
fontFamily: REGULAR,
- fontSize: 24,
- lineHeight: Math.round(24 * 1.3) + 2,
+ fontSize: moderateScale(24),
},
R_100: {
fontFamily: REGULAR,
- fontSize: 24,
- lineHeight: 26,
+ fontSize: moderateScale(24),
},
},
H3: {
B_130: {
fontFamily: BOLD,
- fontSize: 20,
- lineHeight: Math.round(20 * 1.3) + 2,
+ fontSize: moderateScale(20),
},
B_100: {
fontFamily: BOLD,
- fontSize: 20,
- lineHeight: 22,
+ fontSize: moderateScale(20),
},
M_130: {
fontFamily: MEDIUM,
- fontSize: 20,
- lineHeight: Math.round(20 * 1.3) + 2,
+ fontSize: moderateScale(20),
},
M_100: {
fontFamily: MEDIUM,
- fontSize: 20,
- lineHeight: 22,
+ fontSize: moderateScale(20),
},
R_130: {
fontFamily: REGULAR,
- fontSize: 20,
- lineHeight: Math.round(20 * 1.3) + 2,
+ fontSize: moderateScale(20),
},
R_100: {
fontFamily: REGULAR,
- fontSize: 20,
- lineHeight: 22,
+ fontSize: moderateScale(20),
},
},
},
@@ -132,76 +114,64 @@ const fontStyles = {
S1: {
B_130: {
fontFamily: BOLD,
- fontSize: 18,
- lineHeight: Math.round(18 * 1.3) + 2,
+ fontSize: moderateScale(18),
},
B_100: {
fontFamily: BOLD,
- fontSize: 18,
- lineHeight: 20,
+ fontSize: moderateScale(18),
},
M_130: {
fontFamily: MEDIUM,
- fontSize: 18,
- lineHeight: Math.round(18 * 1.3) + 2,
+ fontSize: moderateScale(18),
},
M_100: {
fontFamily: MEDIUM,
- fontSize: 18,
- lineHeight: 20,
+ fontSize: moderateScale(18),
},
R_130: {
fontFamily: REGULAR,
- fontSize: 18,
- lineHeight: Math.round(18 * 1.3) + 2,
+ fontSize: moderateScale(18),
},
R_100: {
fontFamily: REGULAR,
- fontSize: 18,
- lineHeight: 20,
+ fontSize: moderateScale(18),
},
},
S2: {
B_130: {
fontFamily: BOLD,
- fontSize: 16,
- lineHeight: Math.round(16 * 1.3) + 2,
+ fontSize: moderateScale(16),
},
B_100: {
fontFamily: BOLD,
- fontSize: 16,
- lineHeight: 18,
+ fontSize: moderateScale(16),
},
M_130: {
fontFamily: MEDIUM,
- fontSize: 16,
- lineHeight: Math.round(16 * 1.3) + 2,
+ fontSize: moderateScale(16),
},
M_100: {
fontFamily: MEDIUM,
- fontSize: 16,
- lineHeight: 18,
+ fontSize: moderateScale(30),
},
R_130: {
fontFamily: REGULAR,
- fontSize: 16,
- lineHeight: Math.round(16 * 1.3) + 2,
+ fontSize: moderateScale(16),
},
R_100: {
fontFamily: REGULAR,
- fontSize: 16,
- lineHeight: 18,
+ fontSize: moderateScale(16),
},
},
},
@@ -210,75 +180,64 @@ const fontStyles = {
P1: {
B_130: {
fontFamily: BOLD,
- fontSize: 14,
- lineHeight: Math.round(14 * 1.3) + 2,
+ fontSize: moderateScale(14),
},
B_100: {
fontFamily: BOLD,
- fontSize: 14,
- lineHeight: 16,
+ fontSize: moderateScale(14),
},
M_130: {
fontFamily: MEDIUM,
- fontSize: 14,
- lineHeight: Math.round(14 * 1.3) + 2,
+ fontSize: moderateScale(14),
},
M_100: {
fontFamily: MEDIUM,
- fontSize: moderateScale(14, 0.3),
+ fontSize: moderateScale(14),
},
R_130: {
fontFamily: REGULAR,
- fontSize: 14,
- lineHeight: Math.round(14 * 1.3) + 2,
+ fontSize: moderateScale(14),
},
R_100: {
fontFamily: REGULAR,
- fontSize: 14,
- lineHeight: 16,
+ fontSize: moderateScale(14),
},
},
P2: {
B_130: {
fontFamily: BOLD,
- fontSize: 12,
- lineHeight: Math.round(12 * 1.3) + 2,
+ fontSize: moderateScale(12),
},
B_100: {
fontFamily: BOLD,
- fontSize: 12,
- lineHeight: 14,
+ fontSize: moderateScale(12),
},
M_130: {
fontFamily: MEDIUM,
- fontSize: 12,
- lineHeight: Math.round(12 * 1.3) + 2,
+ fontSize: moderateScale(12),
},
M_100: {
fontFamily: MEDIUM,
- fontSize: 12,
- lineHeight: 14,
+ fontSize: moderateScale(12),
},
R_130: {
fontFamily: REGULAR,
- fontSize: 12,
- lineHeight: Math.round(12 * 1.3) + 2,
+ fontSize: moderateScale(12),
},
R_100: {
fontFamily: REGULAR,
- fontSize: 12,
- lineHeight: 14,
+ fontSize: moderateScale(12),
},
},
},
@@ -286,20 +245,17 @@ const fontStyles = {
Caption: {
B_130: {
fontFamily: BOLD,
- fontSize: 10,
- lineHeight: Math.round(10 * 1.3) + 2,
+ fontSize: moderateScale(10),
},
M_130: {
fontFamily: MEDIUM,
- fontSize: 10,
- lineHeight: Math.round(10 * 1.3) + 2,
+ fontSize: moderateScale(10),
},
R_130: {
fontFamily: REGULAR,
- fontSize: 10,
- lineHeight: Math.round(10 * 1.3) + 2,
+ fontSize: moderateScale(10),
},
},
};
diff --git a/theme/mapping.json b/theme/mapping.json
index a435608..2f1f04e 100644
--- a/theme/mapping.json
+++ b/theme/mapping.json
@@ -243,6 +243,15 @@
}
}
}
+ },
+ "Button": {
+ "appearances": {
+ "variantGroups": {
+ "size": {
+ "large": {}
+ }
+ }
+ }
}
}
}
diff --git a/theme/theme.json b/theme/theme.json
index 49dfd95..e711c77 100644
--- a/theme/theme.json
+++ b/theme/theme.json
@@ -1,13 +1,18 @@
{
- "color-primary-100": "#D6E4FF",
- "color-primary-200": "#ADC8FF",
- "color-primary-300": "#84A9FF",
- "color-primary-400": "#6690FF",
- "color-primary-500": "#3366FF",
- "color-primary-600": "#254EDB",
- "color-primary-700": "#1939B7",
- "color-primary-800": "#102693",
- "color-primary-900": "#091A7A",
+ "color-primary-100": "#E6F0FF",
+ "color-primary-200": "#B3D6FF",
+ "color-primary-300": "#80BBFF",
+ "color-primary-400": "#4DA1FF",
+ "color-primary-500": "#0578FF",
+ "color-primary-600": "#0460CC",
+ "color-primary-700": "#034899",
+ "color-primary-800": "#023066",
+ "color-primary-900": "#011833",
+
+ "background-basic-color-1": "#FFFFFF",
+ "background-basic-color-2": "#F4F6F8",
+ "background-basic-color-3": "#E8EBF0",
+ "background-basic-color-4": "#D9DEE4",
"Black01": "#111111",
"Black02": "#28323C",
From 89e12533be7b6f9c0be904804ff573370fde00ad Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=EA=B3=A0=EB=B3=91=EC=B0=AC?=
<70642609+byungchanKo99@users.noreply.github.com>
Date: Sun, 17 Nov 2024 17:14:23 +0900
Subject: [PATCH 3/5] =?UTF-8?q?[SZ-533]feat:=20=EC=98=A8=EB=B3=B4=EB=94=A9?=
=?UTF-8?q?=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC?=
=?UTF-8?q?=ED=98=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
app/funnelView/funnelView.jsx | 53 ++++-
app/funnelView/stepFiveView.tsx | 279 +++++++++++++-------------
app/funnelView/stepFourView.tsx | 323 +++++++++++++++++++++++--------
app/funnelView/stepLastView.tsx | 202 +++++++++++++++++++
app/funnelView/stepOneView.tsx | 23 ++-
app/funnelView/stepThreeView.tsx | 315 ++++++++----------------------
app/funnelView/stepTwoView.tsx | 25 ++-
7 files changed, 737 insertions(+), 483 deletions(-)
create mode 100644 app/funnelView/stepLastView.tsx
diff --git a/app/funnelView/funnelView.jsx b/app/funnelView/funnelView.jsx
index d1c7c7b..6bf35ca 100644
--- a/app/funnelView/funnelView.jsx
+++ b/app/funnelView/funnelView.jsx
@@ -1,5 +1,5 @@
/* eslint-disable react/no-unstable-nested-components */
-import React from 'react';
+import React, { useState } from 'react';
import { useFunnel } from '../../hooks/funnel/useFunnel';
import {
Layout,
@@ -10,13 +10,20 @@ import {
import { useRouter } from 'expo-router';
import NameInputScreen from './stepOneView';
import AgeSelectionScreen from './stepTwoView';
-import SleepTimeScreen from './stepThreeView';
-import DelayReasonsScreen from './stepFourView';
-import CompletionScreen from './stepFiveView';
+import SleepTimeScreen from './stepFourView';
+import DelayReasonsScreen from './stepFiveView';
+import CompletionScreen from './stepLastView';
+import JobSelectionScreen from './stepThreeView';
const FunnelScreen = () => {
const { Funnel, setStep, goBack, currentStep } = useFunnel('Step1');
const route = useRouter();
+ const [name, setName] = useState('');
+ const [selectedAge, setSelectedAge] = useState(null);
+ const [selectedTime, setSelectedTime] = useState(null);
+ const [selectedPeriod, setSelectedPeriod] = useState(null);
+ const [selectedReasons, setSelectedReasons] = useState([]);
+ const [selectedJob, setSelectedJob] = useState(null);
const BackIcon = props => ;
@@ -47,6 +54,8 @@ const FunnelScreen = () => {
onNext={() => {
setStep('Step2');
}}
+ name={name}
+ setName={setName}
/>
@@ -54,25 +63,49 @@ const FunnelScreen = () => {
onNext={() => {
setStep('Step3');
}}
+ selectedAge={selectedAge}
+ setSelectedAge={setSelectedAge}
/>
- SleepTimeScreen
+ setStep('Step4')}
+ selectedJob={selectedJob}
+ setSelectedJob={setSelectedJob}
+ />
+
+
{
- setStep('Step4');
+ setStep('Step5');
}}
+ selectedTime={selectedTime}
+ setSelectedTime={setSelectedTime}
+ selectedPeriod={selectedPeriod}
+ setSelectedPeriod={setSelectedPeriod}
/>
-
+
{
- setStep('Step5');
+ setStep('StepLast');
}}
+ selectedReasons={selectedReasons}
+ setSelectedReasons={setSelectedReasons}
/>
-
-
+
+
diff --git a/app/funnelView/stepFiveView.tsx b/app/funnelView/stepFiveView.tsx
index 27b1583..5dd07e2 100644
--- a/app/funnelView/stepFiveView.tsx
+++ b/app/funnelView/stepFiveView.tsx
@@ -1,4 +1,4 @@
-import React, { useEffect, useState } from 'react';
+import React from 'react';
import {
Layout,
Text,
@@ -6,139 +6,104 @@ import {
StyleService,
useStyleSheet,
} from '@ui-kitten/components';
-import { View, Platform, Linking, TouchableOpacity } from 'react-native';
-import * as Animatable from 'react-native-animatable';
+import { View, TouchableOpacity } from 'react-native';
import { scale, verticalScale, moderateScale } from 'react-native-size-matters';
-import { router } from 'expo-router';
-import messaging from '@react-native-firebase/messaging';
-import ConfirmModal from '@/components/common/molecules/ConfirmModal';
-import useModal from '@/hooks/common/useModal';
+import * as Animatable from 'react-native-animatable';
-const requestNotificationPermission = async () => {
- try {
- const authStatus = await messaging().hasPermission();
+const DelayReasonsScreen = ({
+ onNext,
+ currentStep = 4,
+ totalSteps = 5,
+ selectedReasons,
+ setSelectedReasons,
+}) => {
+ const styles = useStyleSheet(themedStyles);
+ const [localSelectedReasons, setLocalSelectedReasons] =
+ React.useState(selectedReasons);
- const enabled =
- authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
- authStatus === messaging.AuthorizationStatus.PROVISIONAL;
+ const reasons = [
+ '할 일들이 너무 크게 느껴져요',
+ '꾸준히 이어가기 어려워요',
+ '우선순위를 정하기 어려워요',
+ '동기 부여가 없어요',
+ '집중력이 부족해요',
+ ];
- if (enabled) {
- // 권한이 이미 허용됨
- return true;
- } else {
- if (Platform.OS === 'ios') {
- // iOS에서 권한 요청
- const authStatusOnIOS = await messaging().requestPermission();
- const enabledOnIOS =
- authStatusOnIOS === messaging.AuthorizationStatus.AUTHORIZED ||
- authStatusOnIOS === messaging.AuthorizationStatus.PROVISIONAL;
- return enabledOnIOS;
- } else if (Platform.OS === 'android') {
- // Android에서 버전 확인
- const androidVersion = Platform.Version;
- if (androidVersion >= 33) {
- // Android 13 이상에서 권한 요청
- const result = await messaging().requestPermission();
- const enabledOnAndroid =
- result === messaging.AuthorizationStatus.AUTHORIZED;
- return enabledOnAndroid;
- } else {
- // Android 13 미만은 권한 요청 불필요
- return true;
- }
- } else {
- // 기타 플랫폼 (web 등)
- return false;
+ const toggleReason = index => {
+ setLocalSelectedReasons(prev => {
+ const isSelected = prev.includes(index);
+ if (isSelected) {
+ return prev.filter(i => i !== index);
+ } else if (prev.length < 3) {
+ return [...prev, index];
}
- }
- } catch (error) {
- console.error('Notification permission error:', error);
- return false;
- }
-};
-
-const CompletionScreen = () => {
- const [showNotificationScreen, setShowNotificationScreen] = useState(false);
- const styles = useStyleSheet(themedStyles);
- const { isVisible, setIsVisible } = useModal();
-
- useEffect(() => {
- // 3초 후에 알림 허용 화면으로 전환
- const timer = setTimeout(() => {
- setShowNotificationScreen(true);
- }, 3000);
+ return prev;
+ });
+ };
- return () => clearTimeout(timer);
- }, []);
+ const renderStepIndicators = () => (
+
+ {[...Array(totalSteps)].map((_, index) => (
+
+ ))}
+
+ );
- const handleAllowNotifications = async () => {
- const granted = await requestNotificationPermission();
- if (granted) {
- router.dismissAll();
- router.replace('/(tabs)');
- } else {
- setIsVisible(true);
- }
- };
+ return (
+
+ {renderStepIndicators()}
- if (!showNotificationScreen) {
- return (
-
+ 미루는 이유를 알려주세요
+ 최대 3개까지 선택할 수 있어요
+
- 환영합니다 OOO님
- 가입이 완료되었어요
+ {reasons.map((reason, index) => (
+ toggleReason(index)}
+ >
+
+ {reason}
+
+
+ ))}
-
- );
- }
-
- return (
-
-
-
- 할 일을 잊지 않도록{'\n'}알려드릴게요
-
-
- setIsVisible(true)}>
- 지금은 괜찮아요
-
-
-
-
-
- {
- setIsVisible(false);
- Linking.openSettings();
- router.dismissAll();
- router.replace('/(tabs)');
- }}
- onCancel={() => {
- setIsVisible(false);
- router.dismissAll();
- router.replace('/(tabs)');
- }}
- titleKey="views.index.AlertModalTitle"
- messageKey="views.index.AlertModalMessage"
- confirmTextKey="common.yes"
- cancelTextKey="common.no"
- />
-
+ {localSelectedReasons.length > 0 && (
+
+
+
+ )}
+
);
};
@@ -149,44 +114,66 @@ const themedStyles = StyleService.create({
backgroundColor: 'background-basic-color-1',
paddingHorizontal: scale(20),
},
- completionContainer: {
+ contentContainer: {
flex: 1,
+ justifyContent: 'space-between',
+ paddingBottom: verticalScale(20),
+ },
+ stepContainer: {
+ flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
+ marginBottom: verticalScale(40),
},
- welcomeText: {
- fontSize: moderateScale(18),
- marginBottom: verticalScale(8),
- color: 'text-basic-color',
+ stepIndicator: {
+ width: scale(8),
+ height: scale(8),
+ borderRadius: scale(4),
+ backgroundColor: 'color-basic-400',
+ marginHorizontal: scale(3),
},
- completionText: {
- fontSize: moderateScale(24),
- fontWeight: 'bold',
- color: 'text-basic-color',
+ activeStepIndicator: {
+ backgroundColor: 'color-primary-500',
+ width: scale(16),
+ height: scale(8),
},
- notificationContainer: {
- flex: 1,
- justifyContent: 'space-between',
- paddingTop: verticalScale(88),
- paddingBottom: verticalScale(30),
- },
- notificationTitle: {
+ title: {
+ textAlign: 'left',
fontSize: moderateScale(28),
fontWeight: 'bold',
+ marginBottom: verticalScale(8),
+ },
+ subtitle: {
textAlign: 'left',
- lineHeight: moderateScale(35),
+ fontSize: moderateScale(16),
+ color: 'text-hint-color',
+ marginBottom: verticalScale(30),
},
- buttonContainer: {
- gap: verticalScale(16),
+ reasonsContainer: {
+ gap: verticalScale(10),
},
- skipText: {
- textAlign: 'center',
- color: 'text-hint-color',
- fontSize: moderateScale(14),
+ reasonOption: {
+ padding: moderateScale(16),
+ borderRadius: moderateScale(8),
+ backgroundColor: 'background-basic-color-2',
+ borderWidth: 1,
+ borderColor: 'transparent',
+ },
+ selectedReason: {
+ backgroundColor: 'color-primary-100',
+ borderColor: 'color-primary-500',
+ },
+ reasonText: {
+ fontSize: moderateScale(16),
+ color: 'text-basic-color',
+ },
+ selectedReasonText: {
+ color: 'color-primary-700',
+ fontWeight: '600',
},
button: {
borderRadius: moderateScale(8),
},
});
-export default CompletionScreen;
+export default DelayReasonsScreen;
diff --git a/app/funnelView/stepFourView.tsx b/app/funnelView/stepFourView.tsx
index 5923b03..a489cc8 100644
--- a/app/funnelView/stepFourView.tsx
+++ b/app/funnelView/stepFourView.tsx
@@ -1,4 +1,4 @@
-import React, { useState } from 'react';
+import React, { useCallback, useMemo, useRef, useState } from 'react';
import {
Layout,
Text,
@@ -6,34 +6,80 @@ import {
StyleService,
useStyleSheet,
} from '@ui-kitten/components';
-import { View, TouchableOpacity } from 'react-native';
+import { View, TouchableOpacity, Pressable } from 'react-native';
import { scale, verticalScale, moderateScale } from 'react-native-size-matters';
+import BottomSheet, {
+ BottomSheetView,
+ BottomSheetBackdrop,
+} from '@gorhom/bottom-sheet';
import * as Animatable from 'react-native-animatable';
-const DelayReasonsScreen = ({ onNext, currentStep = 4, totalSteps = 5 }) => {
- const [selectedReasons, setSelectedReasons] = useState([]);
+const SleepTimeScreen = ({
+ onNext,
+ currentStep = 3,
+ totalSteps = 5,
+ selectedTime,
+ setSelectedTime,
+ selectedPeriod,
+ setSelectedPeriod,
+}) => {
+ const [localSelectedTime, setLocalSelectedTime] = useState(selectedTime);
+ const [localSelectedPeriod, setLocalSelectedPeriod] =
+ useState(selectedPeriod);
+ const [currentSheet, setCurrentSheet] = useState(null);
+ const [showTimeSelector, setShowTimeSelector] = useState(
+ localSelectedTime && localSelectedPeriod,
+ );
const styles = useStyleSheet(themedStyles);
- const reasons = [
- '할 일들이 너무 크게 느껴져요',
- '꾸준히 이어가기 어려워요',
- '우선순위를 정하기 어려워요',
- '동기 부여가 없어요',
- '집중력이 부족해요',
- ];
-
- const toggleReason = index => {
- setSelectedReasons(prev => {
- const isSelected = prev.includes(index);
- if (isSelected) {
- return prev.filter(i => i !== index);
- } else if (prev.length < 3) {
- return [...prev, index];
- }
- return prev;
- });
+ // refs
+ const timeSheetRef = useRef(null);
+ const periodSheetRef = useRef(null);
+
+ // variables
+ const snapPoints = useMemo(() => ['50%'], []);
+ const timeOptions = Array.from({ length: 12 }, (_, i) => `${i + 1}시`);
+ const periodOptions = ['오전', '오후'];
+
+ // callbacks
+ const handleSheetChanges = useCallback(index => {
+ if (index === -1) {
+ setCurrentSheet(null);
+ }
+ }, []);
+
+ const handleClosePress = useCallback(() => {
+ if (currentSheet === 'time') {
+ timeSheetRef.current?.close();
+ } else if (currentSheet === 'period') {
+ periodSheetRef.current?.close();
+ }
+ setCurrentSheet(null);
+ }, [currentSheet]);
+
+ const handlePeriodSelect = period => {
+ setLocalSelectedPeriod(period);
+ periodSheetRef.current?.close();
+ setShowTimeSelector(true);
+ };
+
+ const handleTimeSelect = time => {
+ setLocalSelectedTime(time);
+ timeSheetRef.current?.close();
};
+ const renderBackdrop = useCallback(
+ props => (
+
+ ),
+ [],
+ );
+
const renderStepIndicators = () => (
{[...Array(totalSteps)].map((_, index) => (
@@ -52,47 +98,132 @@ const DelayReasonsScreen = ({ onNext, currentStep = 4, totalSteps = 5 }) => {
{renderStepIndicators()}
- 미루는 이유를 알려주세요
- 최대 3개까지 선택할 수 있어요
+ 평소 몇시에 주무시나요?
-
- {reasons.map((reason, index) => (
- toggleReason(index)}
+
+ {
+ setCurrentSheet('period');
+ periodSheetRef.current?.expand();
+ }}
+ >
+
-
+ ∨
+
+
+ {showTimeSelector && (
+
+ {
+ setCurrentSheet('time');
+ timeSheetRef.current?.expand();
+ }}
>
- {reason}
-
-
- ))}
-
- {selectedReasons.length > 0 && (
-
+
+ {localSelectedTime || '시간'}
+
+ ∨
+
+
+ )}
+
+
+ {localSelectedTime && localSelectedPeriod && (
+
)}
+
+ {/* Time Bottom Sheet */}
+
+
+
+ 시간 선택
+
+ ×
+
+
+
+ {timeOptions.map((time, index) => (
+ handleTimeSelect(time)}
+ >
+ {time}
+
+ ))}
+
+
+
+
+ {/* Period Bottom Sheet */}
+
+
+
+ 시간대 선택
+
+ ×
+
+
+
+ {periodOptions.map((period, index) => (
+ handlePeriodSelect(period)}
+ >
+ {period}
+
+ ))}
+
+
+
);
};
@@ -103,17 +234,17 @@ const themedStyles = StyleService.create({
backgroundColor: 'background-basic-color-1',
paddingHorizontal: scale(20),
},
- contentContainer: {
- flex: 1,
- justifyContent: 'space-between',
- paddingBottom: verticalScale(20),
- },
stepContainer: {
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
marginBottom: verticalScale(40),
},
+ contentContainer: {
+ flex: 1,
+ justifyContent: 'space-between',
+ paddingBottom: verticalScale(20), // 하단 간격
+ },
stepIndicator: {
width: scale(8),
height: scale(8),
@@ -128,41 +259,77 @@ const themedStyles = StyleService.create({
},
title: {
textAlign: 'left',
+ marginBottom: verticalScale(30),
fontSize: moderateScale(28),
fontWeight: 'bold',
- marginBottom: verticalScale(8),
},
- subtitle: {
- textAlign: 'left',
- fontSize: moderateScale(16),
+ selector: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ padding: moderateScale(15),
+ borderRadius: moderateScale(8),
+ backgroundColor: 'background-basic-color-1',
+ borderWidth: 1,
+ borderColor: 'color-basic-400',
+ marginBottom: verticalScale(10),
+ },
+ placeholderText: {
color: 'text-hint-color',
- marginBottom: verticalScale(30),
},
- reasonsContainer: {
- gap: verticalScale(10),
+ selectedText: {
+ color: 'text-basic-color',
},
- reasonOption: {
- padding: moderateScale(16),
- borderRadius: moderateScale(8),
- backgroundColor: 'background-basic-color-2',
- borderWidth: 1,
- borderColor: 'transparent',
+ arrow: {
+ color: 'color-basic-600',
},
- selectedReason: {
- backgroundColor: 'color-primary-100',
- borderColor: 'color-primary-500',
+ bottomSheetContainer: {
+ flex: 1,
+ backgroundColor: 'background-basic-color-1',
},
- reasonText: {
+ bottomSheetHeader: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ padding: moderateScale(15),
+ borderBottomWidth: 1,
+ borderBottomColor: 'color-basic-300',
+ },
+ bottomSheetTitle: {
fontSize: moderateScale(16),
- color: 'text-basic-color',
+ fontWeight: 'bold',
},
- selectedReasonText: {
- color: 'color-primary-700',
- fontWeight: '600',
+ closeButton: {
+ fontSize: moderateScale(24),
+ color: 'color-basic-600',
+ padding: moderateScale(5),
},
+ optionsContainer: {
+ padding: moderateScale(15),
+ },
+ optionItem: {
+ padding: moderateScale(15),
+ },
+ optionText: {
+ textAlign: 'center',
+ fontSize: moderateScale(16),
+ },
+ buttonContainer: {},
button: {
borderRadius: moderateScale(8),
},
+ timeGridContainer: {
+ flexDirection: 'row',
+ flexWrap: 'wrap',
+ padding: moderateScale(15),
+ },
+ timeGridItem: {
+ width: '33.33%',
+ padding: moderateScale(10),
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ selectorContainer: {},
});
-export default DelayReasonsScreen;
+export default SleepTimeScreen;
diff --git a/app/funnelView/stepLastView.tsx b/app/funnelView/stepLastView.tsx
new file mode 100644
index 0000000..f686e18
--- /dev/null
+++ b/app/funnelView/stepLastView.tsx
@@ -0,0 +1,202 @@
+import React, { useEffect, useState } from 'react';
+import {
+ Layout,
+ Text,
+ Button,
+ StyleService,
+ useStyleSheet,
+} from '@ui-kitten/components';
+import { View, Platform, Linking, TouchableOpacity } from 'react-native';
+import * as Animatable from 'react-native-animatable';
+import { scale, verticalScale, moderateScale } from 'react-native-size-matters';
+import { router } from 'expo-router';
+import messaging from '@react-native-firebase/messaging';
+import ConfirmModal from '@/components/common/molecules/ConfirmModal';
+import useModal from '@/hooks/common/useModal';
+import { useStorage } from '@/hooks/auth/useStorage';
+
+const requestNotificationPermission = async () => {
+ try {
+ const authStatus = await messaging().hasPermission();
+
+ const enabled =
+ authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
+ authStatus === messaging.AuthorizationStatus.PROVISIONAL;
+
+ if (enabled) {
+ // 권한이 이미 허용됨
+ return true;
+ } else {
+ if (Platform.OS === 'ios') {
+ // iOS에서 권한 요청
+ const authStatusOnIOS = await messaging().requestPermission();
+ const enabledOnIOS =
+ authStatusOnIOS === messaging.AuthorizationStatus.AUTHORIZED ||
+ authStatusOnIOS === messaging.AuthorizationStatus.PROVISIONAL;
+ return enabledOnIOS;
+ } else if (Platform.OS === 'android') {
+ // Android에서 버전 확인
+ const androidVersion = Platform.Version;
+ if (androidVersion >= 33) {
+ // Android 13 이상에서 권한 요청
+ const result = await messaging().requestPermission();
+ const enabledOnAndroid =
+ result === messaging.AuthorizationStatus.AUTHORIZED;
+ return enabledOnAndroid;
+ } else {
+ // Android 13 미만은 권한 요청 불필요
+ return true;
+ }
+ } else {
+ // 기타 플랫폼 (web 등)
+ return false;
+ }
+ }
+ } catch (error) {
+ console.error('Notification permission error:', error);
+ return false;
+ }
+};
+
+const CompletionScreen = ({ name, userInfo }) => {
+ const [showNotificationScreen, setShowNotificationScreen] = useState(false);
+ const styles = useStyleSheet(themedStyles);
+ const { isVisible, setIsVisible } = useModal();
+ const { setItem } = useStorage();
+
+ useEffect(() => {
+ // 3초 후에 알림 허용 화면으로 전환
+ const timer = setTimeout(() => {
+ setShowNotificationScreen(true);
+ }, 3000);
+
+ return () => clearTimeout(timer);
+ }, []);
+
+ const handleAllowNotifications = async () => {
+ const granted = await requestNotificationPermission();
+ if (granted) {
+ router.dismissAll();
+ router.replace('/(tabs)');
+ } else {
+ setIsVisible(true);
+ }
+ };
+
+ if (!showNotificationScreen) {
+ return (
+
+
+ 환영합니다 {name}님
+ 가입이 완료되었어요
+
+
+ );
+ }
+
+ return (
+
+
+
+ 할 일을 잊지 않도록{'\n'}알려드릴게요
+
+
+ {
+ setIsVisible(true);
+ setItem('userInfo', userInfo);
+ }}
+ >
+ 지금은 괜찮아요
+
+
+
+
+
+ {
+ setIsVisible(false);
+ Linking.openSettings();
+ router.dismissAll();
+ router.replace('/(tabs)');
+ }}
+ onCancel={() => {
+ setIsVisible(false);
+ router.dismissAll();
+ router.replace('/(tabs)');
+ }}
+ titleKey="views.index.AlertModalTitle"
+ messageKey="views.index.AlertModalMessage"
+ confirmTextKey="common.yes"
+ cancelTextKey="common.no"
+ />
+
+
+ );
+};
+
+const themedStyles = StyleService.create({
+ container: {
+ flex: 1,
+ backgroundColor: 'background-basic-color-1',
+ paddingHorizontal: scale(20),
+ },
+ completionContainer: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ welcomeText: {
+ fontSize: moderateScale(18),
+ marginBottom: verticalScale(8),
+ color: 'text-basic-color',
+ },
+ completionText: {
+ fontSize: moderateScale(24),
+ fontWeight: 'bold',
+ color: 'text-basic-color',
+ },
+ notificationContainer: {
+ flex: 1,
+ justifyContent: 'space-between',
+ paddingTop: verticalScale(88),
+ paddingBottom: verticalScale(30),
+ },
+ notificationTitle: {
+ fontSize: moderateScale(28),
+ fontWeight: 'bold',
+ textAlign: 'left',
+ lineHeight: moderateScale(35),
+ },
+ buttonContainer: {
+ gap: verticalScale(16),
+ },
+ skipText: {
+ textAlign: 'center',
+ color: 'text-hint-color',
+ fontSize: moderateScale(14),
+ },
+ button: {
+ borderRadius: moderateScale(8),
+ },
+});
+
+export default CompletionScreen;
diff --git a/app/funnelView/stepOneView.tsx b/app/funnelView/stepOneView.tsx
index 57a4fa9..186698d 100644
--- a/app/funnelView/stepOneView.tsx
+++ b/app/funnelView/stepOneView.tsx
@@ -11,9 +11,15 @@ import * as Animatable from 'react-native-animatable';
import { View, KeyboardAvoidingView, Platform } from 'react-native';
import { scale, verticalScale, moderateScale } from 'react-native-size-matters';
-const NameInputScreen = ({ onNext, currentStep = 0, totalSteps = 5 }) => {
- const [name, setName] = React.useState('');
+const NameInputScreen = ({
+ onNext,
+ currentStep = 0,
+ totalSteps = 5,
+ name,
+ setName,
+}) => {
const styles = useStyleSheet(themedStyles);
+ const [inputContent, setInputContent] = React.useState(name);
const renderStepIndicators = () => {
return (
@@ -50,15 +56,15 @@ const NameInputScreen = ({ onNext, currentStep = 0, totalSteps = 5 }) => {
>
- {name !== '' && (
+ {inputContent !== '' && (
{
diff --git a/app/funnelView/stepThreeView.tsx b/app/funnelView/stepThreeView.tsx
index ca657ce..b1f0e0f 100644
--- a/app/funnelView/stepThreeView.tsx
+++ b/app/funnelView/stepThreeView.tsx
@@ -1,206 +1,93 @@
-import React, { useCallback, useMemo, useRef, useState } from 'react';
+import React from 'react';
import {
Layout,
- Text,
- Button,
StyleService,
useStyleSheet,
+ Text,
+ Button,
} from '@ui-kitten/components';
-import { View, TouchableOpacity, Pressable } from 'react-native';
+import { View, TouchableOpacity } from 'react-native';
import { scale, verticalScale, moderateScale } from 'react-native-size-matters';
-import BottomSheet, {
- BottomSheetView,
- BottomSheetBackdrop,
-} from '@gorhom/bottom-sheet';
import * as Animatable from 'react-native-animatable';
-const SleepTimeScreen = ({ onNext, currentStep = 2, totalSteps = 5 }) => {
- const [selectedTime, setSelectedTime] = useState(null);
- const [selectedPeriod, setSelectedPeriod] = useState(null);
- const [currentSheet, setCurrentSheet] = useState(null);
- const [showTimeSelector, setShowTimeSelector] = useState(false);
+const JobSelectionScreen = ({
+ onNext,
+ currentStep = 1,
+ totalSteps = 5,
+ selectedJob,
+ setSelectedJob,
+}) => {
const styles = useStyleSheet(themedStyles);
-
- // refs
- const timeSheetRef = useRef(null);
- const periodSheetRef = useRef(null);
-
- // variables
- const snapPoints = useMemo(() => ['50%'], []);
- const timeOptions = Array.from({ length: 12 }, (_, i) => `${i + 1}시`);
- const periodOptions = ['오전', '오후'];
-
- // callbacks
- const handleSheetChanges = useCallback(index => {
- if (index === -1) {
- setCurrentSheet(null);
- }
- }, []);
-
- const handleClosePress = useCallback(() => {
- if (currentSheet === 'time') {
- timeSheetRef.current?.close();
- } else if (currentSheet === 'period') {
- periodSheetRef.current?.close();
- }
- setCurrentSheet(null);
- }, [currentSheet]);
-
- const handlePeriodSelect = period => {
- setSelectedPeriod(period);
- periodSheetRef.current?.close();
- setShowTimeSelector(true);
- };
-
- const handleTimeSelect = time => {
- setSelectedTime(time);
- timeSheetRef.current?.close();
+ const [selectedItem, setSelectedItem] = React.useState(selectedJob);
+
+ const JobRanges = ['중·고등학생', '대학생', '직장인', '자영업자', '기타'];
+
+ const renderStepIndicators = () => {
+ return (
+
+ {[...Array(totalSteps)].map((_, index) => (
+
+ ))}
+
+ );
};
- const renderBackdrop = useCallback(
- props => (
-
- ),
- [],
- );
+ const renderJobOption = (job, index) => {
+ const isSelected = selectedItem === index;
- const renderStepIndicators = () => (
-
- {[...Array(totalSteps)].map((_, index) => (
-
- ))}
-
- );
+ return (
+ setSelectedItem(index)}
+ style={[styles.ageOption, isSelected && styles.selectedAgeOption]}
+ >
+
+ {job}
+
+
+ );
+ };
return (
{renderStepIndicators()}
- 평소 몇시에 주무시나요?
-
-
- {
- setCurrentSheet('period');
- periodSheetRef.current?.expand();
- }}
- >
-
- {selectedPeriod || '시간대'}
-
- ∨
-
-
- {showTimeSelector && (
-
- {
- setCurrentSheet('time');
- timeSheetRef.current?.expand();
- }}
- >
-
- {selectedTime || '시간'}
-
- ∨
-
-
- )}
-
+ 직업을 알려주세요
- {selectedTime && selectedPeriod && (
+
+
+ {JobRanges.map((age, index) => renderJobOption(age, index))}
+
+
+ {selectedItem !== null && (
-
)}
-
- {/* Time Bottom Sheet */}
-
-
-
- 시간 선택
-
- ×
-
-
-
- {timeOptions.map((time, index) => (
- handleTimeSelect(time)}
- >
- {time}
-
- ))}
-
-
-
-
- {/* Period Bottom Sheet */}
-
-
-
- 시간대 선택
-
- ×
-
-
-
- {periodOptions.map((period, index) => (
- handlePeriodSelect(period)}
- >
- {period}
-
- ))}
-
-
-
);
};
@@ -217,11 +104,6 @@ const themedStyles = StyleService.create({
alignItems: 'center',
marginBottom: verticalScale(40),
},
- contentContainer: {
- flex: 1,
- justifyContent: 'space-between',
- paddingBottom: verticalScale(20), // 하단 간격
- },
stepIndicator: {
width: scale(8),
height: scale(8),
@@ -239,74 +121,35 @@ const themedStyles = StyleService.create({
marginBottom: verticalScale(30),
fontSize: moderateScale(28),
fontWeight: 'bold',
+ color: 'text-basic-color',
},
- selector: {
- flexDirection: 'row',
- justifyContent: 'space-between',
- alignItems: 'center',
- padding: moderateScale(15),
+ optionsContainer: {},
+ ageOption: {
+ backgroundColor: 'background-basic-color-2',
+ padding: moderateScale(16),
borderRadius: moderateScale(8),
- backgroundColor: 'background-basic-color-1',
- borderWidth: 1,
- borderColor: 'color-basic-400',
marginBottom: verticalScale(10),
},
- placeholderText: {
- color: 'text-hint-color',
+ selectedAgeOption: {
+ backgroundColor: 'color-primary-100',
},
- selectedText: {
+ ageText: {
+ fontSize: moderateScale(16),
color: 'text-basic-color',
},
- arrow: {
- color: 'color-basic-600',
- },
- bottomSheetContainer: {
- flex: 1,
- backgroundColor: 'background-basic-color-1',
- },
- bottomSheetHeader: {
- flexDirection: 'row',
- justifyContent: 'space-between',
- alignItems: 'center',
- padding: moderateScale(15),
- borderBottomWidth: 1,
- borderBottomColor: 'color-basic-300',
- },
- bottomSheetTitle: {
- fontSize: moderateScale(16),
+ selectedAgeText: {
+ color: 'color-primary-700',
fontWeight: 'bold',
},
- closeButton: {
- fontSize: moderateScale(24),
- color: 'color-basic-600',
- padding: moderateScale(5),
- },
- optionsContainer: {
- padding: moderateScale(15),
- },
- optionItem: {
- padding: moderateScale(15),
- },
- optionText: {
- textAlign: 'center',
- fontSize: moderateScale(16),
- },
- buttonContainer: {},
button: {
borderRadius: moderateScale(8),
},
- timeGridContainer: {
- flexDirection: 'row',
- flexWrap: 'wrap',
- padding: moderateScale(15),
- },
- timeGridItem: {
- width: '33.33%',
- padding: moderateScale(10),
- alignItems: 'center',
- justifyContent: 'center',
+ buttonContainer: {},
+ contentContainer: {
+ flex: 1,
+ justifyContent: 'space-between',
+ paddingBottom: verticalScale(20), // 하단 간격
},
- selectorContainer: {},
});
-export default SleepTimeScreen;
+export default JobSelectionScreen;
diff --git a/app/funnelView/stepTwoView.tsx b/app/funnelView/stepTwoView.tsx
index 0e51a9f..a233096 100644
--- a/app/funnelView/stepTwoView.tsx
+++ b/app/funnelView/stepTwoView.tsx
@@ -10,9 +10,15 @@ import { View, TouchableOpacity } from 'react-native';
import { scale, verticalScale, moderateScale } from 'react-native-size-matters';
import * as Animatable from 'react-native-animatable';
-const AgeSelectionScreen = ({ onNext, currentStep = 1, totalSteps = 5 }) => {
- const [selectedAge, setSelectedAge] = React.useState(null);
+const AgeSelectionScreen = ({
+ onNext,
+ currentStep = 1,
+ totalSteps = 5,
+ selectedAge,
+ setSelectedAge,
+}) => {
const styles = useStyleSheet(themedStyles);
+ const [selectedItem, setSelectedItem] = React.useState(selectedAge);
const ageRanges = ['10대', '20대', '30대', '40대', '50대 이상'];
@@ -33,12 +39,12 @@ const AgeSelectionScreen = ({ onNext, currentStep = 1, totalSteps = 5 }) => {
};
const renderAgeOption = (age, index) => {
- const isSelected = selectedAge === index;
+ const isSelected = selectedItem === index;
return (
setSelectedAge(index)}
+ onPress={() => setSelectedItem(index)}
style={[styles.ageOption, isSelected && styles.selectedAgeOption]}
>
@@ -63,13 +69,20 @@ const AgeSelectionScreen = ({ onNext, currentStep = 1, totalSteps = 5 }) => {
{ageRanges.map((age, index) => renderAgeOption(age, index))}
- {selectedAge !== null && (
+ {selectedItem !== null && (
-
+ {
+ onNext();
+ setSelectedAge(selectedItem);
+ }}
+ >
다음
From 969aba6ddd04613cb853bebabfd04edd32bb2c49 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=EA=B3=A0=EB=B3=91=EC=B0=AC?=
<70642609+byungchanKo99@users.noreply.github.com>
Date: Sun, 17 Nov 2024 17:21:15 +0900
Subject: [PATCH 4/5] =?UTF-8?q?[SZ-533]feat:=20=EC=8B=A0=EA=B7=9C=EC=9C=A0?=
=?UTF-8?q?=EC=A0=80=EB=A7=8C=20=EC=98=A8=EB=B3=B4=EB=94=A9=EC=9C=BC?=
=?UTF-8?q?=EB=A1=9C=20=EA=B0=80=EB=8A=94=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC?=
=?UTF-8?q?=ED=98=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
hooks/auth/useLogin.js | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/hooks/auth/useLogin.js b/hooks/auth/useLogin.js
index 8ad3c02..bc8be1e 100644
--- a/hooks/auth/useLogin.js
+++ b/hooks/auth/useLogin.js
@@ -32,12 +32,10 @@ const useLogin = () => {
await setAsyncStorageLoginInfo(jwtTokenData, user);
setUserId(jwtTokenData.userId);
setIsLoggedIn(true);
-
- if (user.isNew) {
+ if (jwtTokenData.isNew) {
router.push('/funnelView/funnelView');
} else {
- router.push('/funnelView/funnelView');
- // router.push('/(tabs)');
+ router.push('/(tabs)');
}
}
};
From 7e92f53c96fb7886427044f5e4b0dea80e962725 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=EA=B3=A0=EB=B3=91=EC=B0=AC?=
<70642609+byungchanKo99@users.noreply.github.com>
Date: Sun, 17 Nov 2024 17:33:15 +0900
Subject: [PATCH 5/5] =?UTF-8?q?[SZ-533]fix:=20=EC=97=90=EB=9F=AC=20?=
=?UTF-8?q?=EB=B0=9C=EA=B2=AC=20=EB=B0=8F=20=ED=95=B4=EA=B2=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 미루는 이유가 하나 부족했음
- 마지막에 다음에 할래요 시 로컬에 스트링으로 저장하지 않았음
- 세번째 화면에 인디케이터가 두번째를 나타내고 있었음
---
app/funnelView/stepFiveView.tsx | 1 +
app/funnelView/stepLastView.tsx | 2 +-
app/funnelView/stepThreeView.tsx | 2 +-
3 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/app/funnelView/stepFiveView.tsx b/app/funnelView/stepFiveView.tsx
index 5dd07e2..8a5145e 100644
--- a/app/funnelView/stepFiveView.tsx
+++ b/app/funnelView/stepFiveView.tsx
@@ -27,6 +27,7 @@ const DelayReasonsScreen = ({
'우선순위를 정하기 어려워요',
'동기 부여가 없어요',
'집중력이 부족해요',
+ '심리적으로 불안해요',
];
const toggleReason = index => {
diff --git a/app/funnelView/stepLastView.tsx b/app/funnelView/stepLastView.tsx
index f686e18..34fe839 100644
--- a/app/funnelView/stepLastView.tsx
+++ b/app/funnelView/stepLastView.tsx
@@ -112,7 +112,7 @@ const CompletionScreen = ({ name, userInfo }) => {
{
setIsVisible(true);
- setItem('userInfo', userInfo);
+ setItem('userInfo', JSON.stringify(userInfo));
}}
>
지금은 괜찮아요
diff --git a/app/funnelView/stepThreeView.tsx b/app/funnelView/stepThreeView.tsx
index b1f0e0f..d4b2515 100644
--- a/app/funnelView/stepThreeView.tsx
+++ b/app/funnelView/stepThreeView.tsx
@@ -12,7 +12,7 @@ import * as Animatable from 'react-native-animatable';
const JobSelectionScreen = ({
onNext,
- currentStep = 1,
+ currentStep = 2,
totalSteps = 5,
selectedJob,
setSelectedJob,