Skip to content

feat: mypage api 연동 (badge history, level) #98

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Sep 30, 2024
2 changes: 1 addition & 1 deletion packages/react-native/src/apis/mutations/useAddTripPlan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export default function useAddTripPlan() {
await queryClient.invalidateQueries({
queryKey: [QUERY_KEYS.TRIP_PLANS],
});
navigate.navigate('TripPlanner/Main');
navigate.goBack();
},
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { useSuspenseQuery } from '@tanstack/react-query';
import useAuthAxios from '@/apis/useAuthAxios';
import { badgePath } from '@/components/common/Badge';
import { BadgeAcquisition } from '@/constants/BADGE_ACQUISITION';
import { City, Region, REVERSE_BADGE_MAPPER } from '@/constants/CITY';
import QUERY_KEYS from '@/constants/QUERY_KEYS';
import { ServerResponse } from '@/types/response';

interface BadgeHistoryResponse {
region: Region;
city: City;
acquisitionType: BadgeAcquisition;
createdAt: string;
}

export default function useBadgeHistoryQuery({
region,
}: {
region: keyof typeof badgePath;
}) {
const badgeRegions = REVERSE_BADGE_MAPPER[region];
const params = Array.isArray(badgeRegions)
? badgeRegions.join(',')
: badgeRegions;
const authAxios = useAuthAxios();

const getBadgeHistory = async () => {
const result = await authAxios.get<ServerResponse<BadgeHistoryResponse[]>>(
`/api/user/badge/acquisition?regions=${params}`,
);
return result.data.result;
};

return useSuspenseQuery({
queryKey: [QUERY_KEYS.BADGE_HISTORY, region],
queryFn: getBadgeHistory,
});
}
50 changes: 5 additions & 45 deletions packages/react-native/src/apis/queries/mypage/useMyBadgeQuery.ts
Original file line number Diff line number Diff line change
@@ -1,67 +1,27 @@
import { useSuspenseQuery } from '@tanstack/react-query';
import useAuthAxios from '@/apis/useAuthAxios';
import { badgePath } from '@/components/common/Badge';
import { Region } from '@/constants/CITY';
import { BADGE_MAPPER, Region } from '@/constants/CITY';
import QUERY_KEYS from '@/constants/QUERY_KEYS';
import { ServerResponse } from '@/types/response';
import unique from '@/utils/unique';

interface BadgeResponse {
region: Region;
count: number;
}

const badgeMapper = (badge: BadgeResponse): keyof typeof badgePath => {
switch (badge.region) {
case Region.SEOUL:
return '서울';
case Region.GYEONGGI:
return '경기';
case Region.INCHEON:
return '인천';
case Region.GANGWON:
return '강원';
case Region.SEJONG:
return '세종';
case Region.DAEJEON:
return '대전';
case Region.GWANGJU:
return '광주';
case Region.DAEGU:
return '대구';
case Region.ULSAN:
return '울산';
case Region.BUSAN:
return '부산';

case Region.CHUNGBUK:
case Region.CHUNGNAM:
return '충청';

case Region.GYEONGBUK:
case Region.GYEONGNAM:
return '경상';

case Region.JEONBUK:
case Region.JEONNAM:
return '전라';

case Region.JEJU:
default:
return '제주';
}
};

export default function useMyBadgeQuery() {
const authAxios = useAuthAxios();
const getBadges = async () => {
const result =
await authAxios.get<ServerResponse<BadgeResponse[]>>('/api/user/badge');

const badges = result.data.result.map((badge) => ({
count: badge.count,
badgeRegion: badgeMapper(badge),
badgeRegion: BADGE_MAPPER[badge.region],
}));

return badges;
return unique(badges, 'badgeRegion');
};

return useSuspenseQuery({
Expand Down
23 changes: 23 additions & 0 deletions packages/react-native/src/apis/queries/mypage/useMyLevelQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { useSuspenseQuery } from '@tanstack/react-query';
import useAuthAxios from '@/apis/useAuthAxios';
import QUERY_KEYS from '@/constants/QUERY_KEYS';
import { ServerResponse } from '@/types/response';

interface LevelResponse {
profileLevel: string;
description: string;
}

export default function useMyLevelQuery() {
const authAxios = useAuthAxios();
const getMyLevel = async () => {
const result =
await authAxios.get<ServerResponse<LevelResponse>>('/api/user/level');
return result.data.result;
};

return useSuspenseQuery({
queryKey: [QUERY_KEYS.MY_LEVEL],
queryFn: getMyLevel,
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,8 @@ import { badgePath } from '../common/Badge';
import BadgeListItem from './BadgeListItem';
import BottomSheet from '../common/BottomSheet';

// FIXME: 실제 데이터 받아오기: selectedBadge를 기준으로 데이터를 받아오면 됨
const mockData = [
{ id: 1, title: '안동1', date: '2024. 08. 16.', content: 'SPOT! 퀴즈 정답' },
{ id: 2, title: '안동2', date: '2024. 08. 16.', content: 'SPOT! 퀴즈 정답' },
{ id: 3, title: '안동3', date: '2024. 08. 16.', content: 'SPOT! 퀴즈 정답' },
{ id: 4, title: '안동4', date: '2024. 08. 16.', content: 'SPOT! 퀴즈 정답' },
];

interface BadgeListBottomSheetProps {
selectedBadge?: keyof typeof badgePath;
selectedBadge: keyof typeof badgePath;
onClose: () => void;
}

Expand All @@ -37,24 +29,7 @@ export default function BadgeListBottomSheet({
showsVerticalScrollIndicator={false}
style={{ paddingHorizontal: 16 }}
>
{selectedBadge &&
mockData.map((badgeInfo, index) => (
<>
<BadgeListItem
key={`data-${badgeInfo.id}`}
location={selectedBadge}
title={badgeInfo.title}
date={badgeInfo.date}
content={badgeInfo.content}
/>
{index !== mockData.length - 1 && (
<View
key={`sep-${badgeInfo.id}`}
className="h-[0.5px] bg-[#333333] bg-opacity-30 w-full"
/>
)}
</>
))}
<BadgeListItem selectedBadge={selectedBadge} />
</BottomSheetScrollView>
</BottomSheet>
);
Expand Down
70 changes: 44 additions & 26 deletions packages/react-native/src/components/mypage/BadgeListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,55 @@ import { Font } from 'design-system';
import { View } from 'react-native';
import Badge, { badgePath } from '../common/Badge';
import Spacing from '../common/Spacing';
import withSuspense from '../HOC/withSuspense';
import useBadgeHistoryQuery from '@/apis/queries/mypage/useBadgeHistoryQuery';
import { getDisplayRegion } from '@/utils/getDisplayRegionName';
import { ACQUISITION_MAPPER } from '@/constants/BADGE_ACQUISITION';

interface BadgeListItemProps {
location: keyof typeof badgePath;
title: string;
date: string;
content: string;
selectedBadge: keyof typeof badgePath;
}

export default function BadgeListItem({
location,
title,
date,
content,
}: BadgeListItemProps) {
return (
<View className="flex-row items-center justify-start py-4">
<View className="items-center justify-center mr-3">
<Badge location={location} preventFade width={80} />
export default withSuspense(
function BadgeListItem({ selectedBadge }: BadgeListItemProps) {
const { data: badgeHistory } = useBadgeHistoryQuery({
region: selectedBadge,
});

return badgeHistory.map((badgeInfo, index) => (
<View className="flex-row items-center justify-start py-4" key={index}>
<View className="items-center justify-center mr-3">
<Badge location={selectedBadge} preventFade width={80} />
</View>
<View>
<Font type="title1" color="black">
{getDisplayRegion({
locationEnum: badgeInfo.region,
cityEnum: badgeInfo.city,
onlyCity: true,
}) || ''}
</Font>
<Spacing height={8} />
<Font type="ui-text" color="black">
{badgeInfo.createdAt}
</Font>
<Font type="body3" color="black">
{ACQUISITION_MAPPER[badgeInfo.acquisitionType]}
</Font>
</View>
</View>
<View>
<Font type="title1" color="black">
{title}
</Font>
<Spacing height={8} />
<Font type="ui-text" color="black">
{date}
));
},
{
fallback: (
<View className="flex-1 justify-center items-center">
<Font type="body1" color="black">
잠시만
</Font>
<Font type="body3" color="black">
{content}
<Font type="body1" color="black">
기다려주세요
</Font>
</View>
</View>
);
}
),
},
);
6 changes: 6 additions & 0 deletions packages/react-native/src/constants/BADGE_ACQUISITION.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,9 @@ export enum BadgeAcquisition {
BY_CAMERA_FILTER,
BY_RECORD,
}

export const ACQUISITION_MAPPER: Record<BadgeAcquisition, string> = {
[BadgeAcquisition.BY_QUIZ]: 'Spot! 퀴즈 정답',
[BadgeAcquisition.BY_CAMERA_FILTER]: 'Spot! 카메라 필터',
[BadgeAcquisition.BY_RECORD]: '소중한 여행 기록 작성',
};
20 changes: 20 additions & 0 deletions packages/react-native/src/constants/CITY.ts
Original file line number Diff line number Diff line change
Expand Up @@ -461,3 +461,23 @@ export const BADGE_MAPPER: Record<Region, keyof typeof badgePath> = {
[Region.BUSAN]: '부산',
[Region.JEJU]: '제주',
};

export const REVERSE_BADGE_MAPPER: Record<
keyof typeof badgePath,
Region | Region[]
> = {
서울: Region.SEOUL,
경기: Region.GYEONGGI,
인천: Region.INCHEON,
강원: Region.GANGWON,
충청: [Region.CHUNGNAM, Region.CHUNGBUK],
세종: Region.SEJONG,
대전: Region.DAEJEON,
전라: [Region.JEONNAM, Region.JEONBUK],
광주: Region.GWANGJU,
경상: [Region.GYEONGBUK, Region.GYEONGNAM],
대구: Region.DAEGU,
울산: Region.ULSAN,
부산: Region.BUSAN,
제주: Region.JEJU,
};
2 changes: 2 additions & 0 deletions packages/react-native/src/constants/QUERY_KEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ const QUERY_KEYS = {
SEARCH: 'search',
MY_SPOTS: 'mySpots',
MY_BADGES: 'myBadges',
MY_LEVEL: 'myLevel',
BADGE_HISTORY: 'badgeHistory',
SCHEDULES: 'schedules',
};

Expand Down
4 changes: 3 additions & 1 deletion packages/react-native/src/pages/MyPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { StackNavigation } from '@/types/navigation';
import CogWheelIcon from '@/assets/CogWheelIcon';
import useProfileImage from '@/hooks/useProfileImage';
import withSuspense from '@/components/HOC/withSuspense';
import useMyLevelQuery from '@/apis/queries/mypage/useMyLevelQuery';

interface MyPageProps {
navigation: StackNavigation<'MyPage/Profile'>;
Expand All @@ -17,6 +18,7 @@ interface MyPageProps {
export default withSuspense(function MyPage({ navigation }: MyPageProps) {
const { profile } = useProfileQuery();
const { ProfileImage } = useProfileImage();
const { data: myLevel } = useMyLevelQuery();

return (
<BackGroundGradient paddingTop={40} withoutScroll>
Expand All @@ -36,7 +38,7 @@ export default withSuspense(function MyPage({ navigation }: MyPageProps) {
</View>
<View className="flex flex-col items-center">
<View>
<Rank content="Beginner" />
<Rank content={myLevel.profileLevel} />
</View>
<View className="mt-2">
<Font type="mainTitle" color="white">
Expand Down
22 changes: 15 additions & 7 deletions packages/react-native/src/pages/MyPage/MyBadge.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import { useState } from 'react';
import { FlatList, View } from 'react-native';
import { Font } from 'design-system';
import Badge from '@/components/common/Badge';
import Badge, { badgePath } from '@/components/common/Badge';
import Spacing from '@/components/common/Spacing';
import useMyBadgeQuery from '@/apis/queries/mypage/useMyBadgeQuery';
import withSuspense from '@/components/HOC/withSuspense';
import BadgeListBottomSheet from '@/components/mypage/BadgeListBottomSheet';

export default withSuspense(
function MyBadge() {
const { data: badges } = useMyBadgeQuery();
const [containerWidth, setContainerWidth] = useState(0);
// const [selectedBadge, setSelectedBadge] = useState<keyof typeof badgePath>();
const [selectedBadge, setSelectedBadge] =
useState<keyof typeof badgePath>();

const numColumns = 3;
const paddingHorizontal = 8;
Expand All @@ -27,7 +29,11 @@ export default withSuspense(
location={item.badgeRegion}
width={(containerWidth - paddingHorizontal * 2) / numColumns}
label={item.badgeRegion}
// onPress={() => setSelectedBadge(item.badgeRegion)}
onPress={() => {
if (item.count) {
setSelectedBadge(item.badgeRegion);
}
}}
count={item.count}
/>
<Spacing height={20} />
Expand All @@ -36,10 +42,12 @@ export default withSuspense(
keyExtractor={(item) => item.badgeRegion}
numColumns={numColumns}
/>
{/* <BadgeListBottomSheet
selectedBadge={selectedBadge}
onClose={() => setSelectedBadge(undefined)}
/> */}
{selectedBadge && (
<BadgeListBottomSheet
selectedBadge={selectedBadge}
onClose={() => setSelectedBadge(undefined)}
/>
)}
</>
);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export default withSuspense(function TripPlannerDetail() {
<Spacing height={10} />
<ScrollView className="px-5 flex-1 ">
<Font.Bold type="body1" color="black">
일정
나의 일정
</Font.Bold>
<Spacing height={10} />
<TouchableOpacity
Expand Down
Loading
Loading