Skip to content

Commit 033fdfc

Browse files
authored
Merge pull request #15 from JECT-Study/feat/mypage-api-connect-2
Feat/mypage api connect 2
2 parents b25f0ef + 8329413 commit 033fdfc

File tree

21 files changed

+1006
-139
lines changed

21 files changed

+1006
-139
lines changed

app/(tabs)/index.tsx

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import "dayjs/locale/ko";
55
import { BlurView } from "expo-blur";
66
import { LinearGradient } from "expo-linear-gradient";
77
import { useRouter } from "expo-router";
8+
import * as SecureStore from "expo-secure-store";
89
import { StatusBar } from "expo-status-bar";
910
import {
1011
ActivityIndicator,
@@ -30,6 +31,7 @@ import { PerformanceIcon } from "@/components/icons/PerformanceIcon";
3031
import SearchIcon from "@/components/icons/SearchIcon";
3132
import { BACKEND_URL } from "@/constants/ApiUrls";
3233
import { authApi, publicApi } from "@/features/axios/axiosInstance";
34+
import useUserStore from "@/stores/useUserStore";
3335
import { ensureMinLoadingTime } from "@/utils/loadingUtils";
3436

3537
// dayjs 한국어 로케일 설정
@@ -288,10 +290,35 @@ export default function HomeScreen() {
288290
useState<boolean>(false);
289291
const [isScrolled, setIsScrolled] = useState<boolean>(false);
290292

291-
const [isLoggedIn, setIsLoggedIn] = useState<boolean>(false); //! 🌟 임시 로그인 여부 상태
293+
const [isLoggedIn, setIsLoggedIn] = useState<boolean>(false);
294+
const { nickname } = useUserStore();
292295

293296
const router = useRouter();
294297

298+
// 토큰 존재 여부로 로그인 상태 확인
299+
const checkLoginStatus = useCallback(async () => {
300+
try {
301+
const accessToken = await SecureStore.getItemAsync("accessToken");
302+
const refreshToken = await SecureStore.getItemAsync("refreshToken");
303+
304+
console.log("🔍 토큰 확인:", {
305+
accessToken: accessToken ? "존재" : "없음",
306+
refreshToken: refreshToken ? "존재" : "없음",
307+
nickname,
308+
isLoggedIn: !!accessToken
309+
});
310+
311+
setIsLoggedIn(!!accessToken);
312+
} catch (error) {
313+
console.log("❌ 토큰 확인 중 에러:", error);
314+
setIsLoggedIn(false);
315+
}
316+
}, [nickname]);
317+
318+
useEffect(() => {
319+
checkLoginStatus();
320+
}, [checkLoginStatus]);
321+
295322
const weekDays = getWeekDays();
296323

297324
const chunkedRecommendationsData = chunkArray(recommendationsData, 3);
@@ -544,9 +571,10 @@ export default function HomeScreen() {
544571
<View className="gap-y-[34px]">
545572
{/* 맞춤 콘텐츠 */}
546573
<View className="relative px-[18px] py-2.5">
547-
{/* 🌟 로그인 시 닉네임이 이름이 표시되어야 합니다. */}
548574
<Text className="text-xl font-semibold text-black">
549-
OO님을 위한 맞춤 콘텐츠
575+
{isLoggedIn && nickname
576+
? `${nickname}님을 위한 맞춤 콘텐츠`
577+
: "맞춤 콘텐츠"}
550578
</Text>
551579

552580
<View className="mb-5 mt-3 flex-row gap-x-2.5">
@@ -602,9 +630,6 @@ export default function HomeScreen() {
602630
/>
603631
)}
604632

605-
{/* 🌟 로그인하지 않은 경우 맞춤 콘텐츠를 볼 수 없게 하는 오버레이입니다. */}
606-
{/* 🌟 비로그인 상태에서는 이 오버레이가 보여야 하고 로그인 된 상태에서는 이 오버레이가 보이지 않아야 합니다. */}
607-
{/* 🌟 현재는 임시로 로그인 여부 역할을 하는 isLoggedIn 상태를 통해 로그인 여부를 확인하고 있습니다. */}
608633
{!isLoggedIn && (
609634
<BlurView
610635
intensity={8}

app/(tabs)/my/edit_profile.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { useEffect } from "react";
2+
13
import { router } from "expo-router";
24
import { SafeAreaView } from "react-native";
35

@@ -7,11 +9,19 @@ import CustomHeader from "@/components/ui/CustomHeader";
79
import {
810
useApplyEditProfile,
911
useCancelEditProfile,
12+
useInitializeFromUserStore,
1013
} from "@/stores/useEditProfileStore";
1114

1215
export default function EditProfile() {
1316
const cancelEdit = useCancelEditProfile();
1417
const applyEdit = useApplyEditProfile();
18+
const initializeFromUserStore = useInitializeFromUserStore();
19+
20+
// 페이지 진입 시 userStore의 정보로 초기화
21+
useEffect(() => {
22+
initializeFromUserStore();
23+
}, [initializeFromUserStore]);
24+
1525
return (
1626
<SafeAreaView className="w-full flex-1 items-center bg-white">
1727
<CustomHeader

app/(tabs)/my/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import usePageNavigation from "@/hooks/usePageNavigation";
88
export default function MyScreen() {
99
const { goEditProfile } = usePageNavigation();
1010
return (
11-
<SafeAreaView className="w-full flex-1 bg-white" edges={["bottom"]}>
11+
<SafeAreaView className="w-full flex-1 bg-white">
1212
<Text className="ml-6 mt-6 text-[18px]">마이페이지</Text>
1313
<UserInfo />
1414
<Pressable

app/(tabs)/my/like.tsx

Lines changed: 38 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,23 @@
1-
import { useEffect, useState } from "react";
2-
3-
import axios, { AxiosError } from "axios";
41
import { router } from "expo-router";
5-
import { FlatList, RefreshControl, SafeAreaView, View } from "react-native";
2+
import {
3+
FlatList,
4+
RefreshControl,
5+
SafeAreaView,
6+
View,
7+
Text,
8+
} from "react-native";
69

710
import LikeFilter from "@/components/mypage/LikeFilter";
811
import CustomHeader from "@/components/ui/CustomHeader";
912
import PostBlock from "@/components/ui/PostBlock";
10-
import { UsersFavoriteUrl } from "@/constants/ApiUrls";
11-
import { DUMMY_EVENT_POSTS } from "@/constants/DummyLike";
13+
import { useLike } from "@/hooks/useLike";
1214
import useLikeRefresh from "@/hooks/useLikeRefresh";
1315
import { useCategorySelected } from "@/stores/useCategoryStore";
1416

1517
export default function Like() {
1618
const { refresh, onRefresh } = useLikeRefresh();
17-
const [infodummy, setInfodummy] = useState(DUMMY_EVENT_POSTS);
1819
const selected = useCategorySelected();
19-
20-
// TODO : api test, 에러로 확인 필요.
21-
useEffect(() => {
22-
const fetchResult = async () => {
23-
try {
24-
const params = {
25-
page: "0",
26-
limit: "10",
27-
// category: "FESTIVAL",
28-
};
29-
const url = `${UsersFavoriteUrl}?${new URLSearchParams(params).toString()}`;
30-
console.log("요청 주소 ", url); // ← 실제 주소 로그
31-
32-
const response = await axios.get(UsersFavoriteUrl, { params });
33-
console.log("api response", response);
34-
} catch (e) {
35-
const axiosError = e as AxiosError;
36-
alert(`${axiosError.message}`);
37-
}
38-
};
39-
fetchResult();
40-
}, []);
20+
const { favorites, loading, loadMore } = useLike(selected);
4121

4222
return (
4323
<SafeAreaView className="w-full flex-1 items-center bg-white">
@@ -51,24 +31,35 @@ export default function Like() {
5131
<View className="flex w-full items-start p-6">
5232
<LikeFilter />
5333
</View>
54-
<FlatList
55-
className="w-full flex-1 px-6 py-2"
56-
data={
57-
selected === "all"
58-
? infodummy
59-
: infodummy.filter((item) => item.category === selected)
60-
}
61-
renderItem={({ item }) => <PostBlock info={item} />}
62-
keyExtractor={(item) => item.id as unknown as string}
63-
onEndReached={() => {
64-
// TODO 여기에 무한 스크롤 관련 로직 추가
65-
console.log("여기에 무한 스크롤 관련 로직 추가");
66-
}}
67-
onEndReachedThreshold={0.8}
68-
refreshControl={
69-
<RefreshControl refreshing={refresh} onRefresh={onRefresh} />
70-
}
71-
/>
34+
{favorites.length === 0 && !loading ? (
35+
<View className="flex-1 items-center justify-center">
36+
<Text className="text-base text-gray-500">
37+
일치하는 내용이 없습니다.
38+
</Text>
39+
</View>
40+
) : (
41+
<FlatList
42+
className="w-full flex-1 px-6 py-2"
43+
data={favorites}
44+
renderItem={({ item }) => (
45+
<PostBlock
46+
info={{
47+
img_url: item.image || "",
48+
title: item.title,
49+
address: item.address,
50+
start_date: item.startDate,
51+
end_date: item.endDate,
52+
}}
53+
/>
54+
)}
55+
keyExtractor={(item) => item.contentId.toString()}
56+
onEndReached={loadMore}
57+
onEndReachedThreshold={0.8}
58+
refreshControl={
59+
<RefreshControl refreshing={refresh} onRefresh={onRefresh} />
60+
}
61+
/>
62+
)}
7263
</SafeAreaView>
7364
);
7465
}

app/(tabs)/my/plan.tsx

Lines changed: 119 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,130 @@
1-
import { useEffect } from "react";
1+
import React, { useCallback } from "react";
22

3-
import axios, { AxiosError } from "axios";
3+
import dayjs from "dayjs";
4+
import isSameOrAfter from "dayjs/plugin/isSameOrAfter";
5+
import isSameOrBefore from "dayjs/plugin/isSameOrBefore";
6+
import timezone from "dayjs/plugin/timezone";
47
import { router } from "expo-router";
5-
import { SafeAreaView, View, Text } from "react-native";
8+
import { FlatList, RefreshControl, View, Text } from "react-native";
9+
import { CalendarProvider, ExpandableCalendar } from "react-native-calendars";
10+
import { SafeAreaView } from "react-native-safe-area-context";
611

12+
import CommonCalendar from "@/components/ui/CommonCalendar";
713
import CustomHeader from "@/components/ui/CustomHeader";
8-
import { UsersScheduleUrl } from "@/constants/ApiUrls";
14+
import PostBlock from "@/components/ui/PostBlock";
15+
// import { primaryColor } from "@/constants/CalendarTheme";
16+
import useLikeRefresh from "@/hooks/useLikeRefresh";
17+
import { usePlan } from "@/hooks/usePlan";
18+
19+
// dayjs 플러그인 설정
20+
dayjs.extend(timezone);
21+
dayjs.extend(isSameOrBefore);
22+
dayjs.extend(isSameOrAfter);
923

1024
export default function Plan() {
11-
useEffect(() => {
12-
const getSchedule = async () => {
13-
try {
14-
const response = await axios(`${UsersScheduleUrl}/check`, {
15-
params: {
16-
month: "2025-06", // TODO: 실제 날짜로 변경
17-
},
18-
});
19-
console.log(response);
20-
} catch (e) {
21-
const axiosError = e as AxiosError;
22-
console.error(axiosError);
23-
}
24-
};
25-
getSchedule();
26-
// test
27-
}, []);
25+
const { refresh, onRefresh } = useLikeRefresh();
26+
const {
27+
schedules,
28+
scheduleDates,
29+
loading,
30+
hasMore,
31+
selectedDate,
32+
loadMore,
33+
handleDateChange,
34+
handleMonthChange,
35+
} = usePlan();
36+
37+
const getSelectedDateSchedules = useCallback(() => {
38+
const selectedDayjs = dayjs(selectedDate);
39+
40+
return schedules.filter((item) => {
41+
const startDate = dayjs(item.startDate);
42+
const endDate = dayjs(item.endDate);
43+
44+
return (
45+
selectedDayjs.isSameOrAfter(startDate, "day") &&
46+
selectedDayjs.isSameOrBefore(endDate, "day")
47+
);
48+
});
49+
}, [schedules, selectedDate]);
50+
51+
// 월별 일정 데이터를 생성 (scheduleDates 기반)
52+
const getMonthlySchedules = useCallback(() => {
53+
return scheduleDates.map((dateString, index) => ({
54+
id: index + 1,
55+
startDate: dateString,
56+
endDate: dateString,
57+
title: "일정",
58+
}));
59+
}, [scheduleDates]);
2860

2961
return (
30-
<>
31-
<SafeAreaView className="w-full flex-1 bg-white">
32-
<CustomHeader
33-
title="나의 일정"
34-
isCommit={false}
35-
cancel={() => router.replace("/my")}
62+
<SafeAreaView className="flex-1 bg-white">
63+
<CustomHeader
64+
title="나의 일정"
65+
isCommit={false}
66+
cancel={() => {
67+
router.replace("/my");
68+
}}
69+
/>
70+
71+
<CalendarProvider
72+
date={selectedDate}
73+
disableAutoDaySelection={[
74+
ExpandableCalendar.navigationTypes.MONTH_ARROWS,
75+
ExpandableCalendar.navigationTypes.WEEK_ARROWS,
76+
ExpandableCalendar.navigationTypes.MONTH_SCROLL,
77+
ExpandableCalendar.navigationTypes.WEEK_SCROLL,
78+
]}
79+
>
80+
<CommonCalendar
81+
selectedDate={selectedDate}
82+
schedules={getMonthlySchedules()}
83+
onDateChange={handleDateChange}
84+
onMonthChange={(month) => {
85+
console.log("Month changed to:", month);
86+
handleMonthChange(month.dateString.slice(0, 7));
87+
}}
88+
monthRangeLimit={2}
89+
showStatusBar={false}
90+
closeOnDayPress={false}
91+
disablePan={true}
92+
animateScroll={true}
93+
hideKnob={true}
3694
/>
37-
<View>
38-
<Text>해당 위치에 달력 컴포넌트를 활용</Text>
39-
<Text>컨텐츠 일정과 동일한 구성, 데이터만 달리</Text>
40-
</View>
41-
</SafeAreaView>
42-
</>
95+
96+
{getSelectedDateSchedules().length === 0 && !loading ? (
97+
<View className="mt-8 flex-1 items-center justify-center">
98+
<Text className="text-base text-gray-500">
99+
선택한 날짜에 일정이 없습니다.
100+
</Text>
101+
</View>
102+
) : (
103+
<FlatList
104+
className="mt-4 px-6"
105+
data={getSelectedDateSchedules()}
106+
renderItem={({ item }) => (
107+
<PostBlock
108+
info={{
109+
img_url: item.image || "",
110+
title: item.title,
111+
address: item.address,
112+
start_date: item.startDate,
113+
end_date: item.endDate,
114+
}}
115+
/>
116+
)}
117+
keyExtractor={(item) => item.contentId.toString()}
118+
onEndReached={loadMore}
119+
onEndReachedThreshold={0.8}
120+
refreshControl={
121+
<RefreshControl refreshing={refresh} onRefresh={onRefresh} />
122+
}
123+
removeClippedSubviews={true}
124+
initialNumToRender={10}
125+
/>
126+
)}
127+
</CalendarProvider>
128+
</SafeAreaView>
43129
);
44130
}

app/(tabs)/my/terms.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,12 @@ export default function Terms() {
2525
/>
2626
<MyPageMenu
2727
title="개인 정보 처리방침"
28-
onPress={() => console.log("개인정보 처리 방침")}
28+
onPress={() => router.push("/my/terms/service_privacy")}
2929
chevron={true}
3030
/>
3131
<MyPageMenu
3232
title="위치기반 서비스 이용약관"
33-
onPress={() => console.log("위치기반 서비스 이용약관")}
33+
onPress={() => router.push("/my/terms/service_location")}
3434
chevron={true}
3535
/>
3636
</View>

0 commit comments

Comments
 (0)