Skip to content

Commit

Permalink
프로필 > 각 탭을 페이지 컴포넌트로 분리 (#233)
Browse files Browse the repository at this point in the history
* ✨ #232 - ProfileTab 컴포넌트 생성

* ✨ #232 - 나의 프로필 페이지/탭 목록 상수 추가

* ♻️ #232 - 프로필 > 활동, 일기, 북마크 페이지 컴포넌트로 분리

* ✨ #232 - 분리된 프로필 페이지에 NavBar 적용

* ✨ #232 - 다른 사용자 프로필 페이지/탭 목록 상수 추가

* ♻️ #232 - 다른 사용자 프로필 > 활동, 일기 페이지 컴포넌트로 분리

* ✨ #232 - 분리된 다른 사용자 프로필 페이지에 NavBar 적용

* ✨ #232 - ProfileTab username prop 추가 및 pathname 분기처리

* 💄 #232 - ActivitiesContainer 여백 수정

* 🔥 #232 - 사용하지 않는 코드 제거

* 🐛 #232 - Received "true" for a non-boolean attribute error 해결

* ♻️ #232 - ProfileTab 불필요한 prop 제거 및 코드 개선

* ♻️ #232 - 프로필 페이지 공통 레이아웃 컴포넌트 생성 및 적용
  • Loading branch information
Bori-github authored May 26, 2024
1 parent a366a43 commit baeab5b
Show file tree
Hide file tree
Showing 18 changed files with 370 additions and 248 deletions.
36 changes: 0 additions & 36 deletions src/components/common/Tab.tsx

This file was deleted.

1 change: 0 additions & 1 deletion src/components/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ export * from './FloatingMenuButton';
export * from './HighlightText';
export * from './ResponsiveImage';
export * from './Seo';
export * from './Tab';
export * from './Loading';
export * from './ObserverTarget';
export * from './Popover';
10 changes: 9 additions & 1 deletion src/components/layouts/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,20 @@ import { useRouter } from 'next/router';
import Navbar from './Navbar';
import type { ReactJSXElement } from '@emotion/react/types/jsx-namespace';
import type { ReactNode } from 'react';
import { PAGE_PATH } from 'constants/common';

interface LayoutProps {
children?: ReactNode;
}

const SHOW_NAVBAR_PAGES = ['/', '/profile', '/profile/[username]'];
const SHOW_NAVBAR_PAGES = [
PAGE_PATH.main,
PAGE_PATH.profile.index,
PAGE_PATH.profile.diaries,
PAGE_PATH.profile.bookmarks,
'/profile/[username]',
'/profile/[username]/diaries',
];

const Layout = ({ children }: LayoutProps): ReactJSXElement => {
const { pathname } = useRouter();
Expand Down
2 changes: 1 addition & 1 deletion src/components/layouts/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ const Navbar = () => {
<NavigationList>
{NAVIGATION_LIST.map((navigation) => {
const { label, href, icon } = navigation;
const isActive = pathname === href;
const isActive = pathname.split('/')[1] === href.split('/')[1];

return (
<li key={`navigation-item-${label}`}>
Expand Down
1 change: 1 addition & 0 deletions src/components/profile/ActivitiesContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ const ActivitiesCalendarHeader = styled.header`
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 24px;
padding: 0 20px 0 50px;
`;

Expand Down
25 changes: 25 additions & 0 deletions src/components/profile/ProfileLayout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { ProfileContainer } from './ProfileContainer';
import { ProfileTab } from './ProfileTab';
import type { PropsWithChildren } from 'react';
import { Seo } from 'components/common';

interface ProfileLayoutProps extends PropsWithChildren {
isMyProfile: boolean;
username: string;
}

export const ProfileLayout = ({
children,
isMyProfile,
username,
}: ProfileLayoutProps) => {
return (
<>
<Seo title={`${isMyProfile ? username : ''} 프로필 | a daily diary`} />
<ProfileContainer username={username} isMyProfile={isMyProfile} />

<ProfileTab username={isMyProfile ? undefined : username} />
{children}
</>
);
};
46 changes: 46 additions & 0 deletions src/components/profile/ProfileTab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import styled from '@emotion/styled';
import Link from 'next/link';
import { useRouter } from 'next/router';
import { MY_PROFILE_TAB_LIST, YOUR_PROFILE_TAB_LIST } from 'constants/profile';

interface ProfileTabProps {
username?: string;
}

export const ProfileTab = ({ username }: ProfileTabProps) => {
const { pathname } = useRouter();

const isMyProfile = username === undefined;
const tabList = isMyProfile
? MY_PROFILE_TAB_LIST
: YOUR_PROFILE_TAB_LIST(username);
const convertPathname = isMyProfile
? pathname
: pathname.replace('[username]', username);

return (
<TabList>
{tabList.map((tab) => {
const { id, title } = tab;
return (
<Tab key={`tab-list-${id}`} active={id === convertPathname}>
<Link href={id}>{title}</Link>
</Tab>
);
})}
</TabList>
);
};

const TabList = styled.ul`
display: flex;
padding: 14px 14px 0;
`;

const Tab = styled.li<{ active: boolean }>`
padding: 10px 1px 8px;
margin: 0 14px;
${({ theme }) => theme.fonts.headline_04};
border-bottom: 2px solid
${({ theme, active }) => (active ? theme.colors.primary_00 : 'transparent')};
`;
8 changes: 5 additions & 3 deletions src/components/profile/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
export * from './ProfileContainer';
export * from './SelectProfileImage';
export * from './ProfileImage';
export * from './ActivitiesContainer';
export * from './ActivityDetail';
export * from './ActivitiesCalendar';
export * from './ActivitiesInformation';
export * from './ProfileContainer';
export * from './ProfileImage';
export * from './ProfileLayout';
export * from './ProfileTab';
export * from './SelectProfileImage';
3 changes: 3 additions & 0 deletions src/constants/common/page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@ export const PAGE_PATH = {
profile: {
index: '/profile',
edit: '/profile/edit',
diaries: '/profile/diaries',
bookmarks: '/profile/bookmarks',
username: (username: string) => `/profile/${username}`,
badges: (username: string) => `/profile/${username}/badges`,
usernameDiaries: (username: string) => `/profile/${username}/diaries`,
},

account: {
Expand Down
1 change: 1 addition & 0 deletions src/constants/profile/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './images';
export * from './tab';
14 changes: 14 additions & 0 deletions src/constants/profile/tab.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { PAGE_PATH } from 'constants/common';

export const MY_PROFILE_TAB_LIST = [
{ id: PAGE_PATH.profile.index, title: '활동' },
{ id: PAGE_PATH.profile.diaries, title: '일기' },
{ id: PAGE_PATH.profile.bookmarks, title: '북마크' },
];

export const YOUR_PROFILE_TAB_LIST = (username: string) => {
return [
{ id: PAGE_PATH.profile.username(username), title: '활동' },
{ id: PAGE_PATH.profile.usernameDiaries(username), title: '일기' },
];
};
1 change: 0 additions & 1 deletion src/hooks/common/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
export * from './useBeforeLeave';
export * from './useClickOutside';
export * from './useTabIndicator';
export * from './useModal';
export * from './useIntersectionObserver';
export * from './useSearchKeywordStorage';
34 changes: 0 additions & 34 deletions src/hooks/common/useTabIndicator.ts

This file was deleted.

93 changes: 93 additions & 0 deletions src/pages/profile/[username]/diaries.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { QueryClient, dehydrate } from '@tanstack/react-query';
import { isAxiosError } from 'axios';
import { getServerSession } from 'next-auth';
import type {
GetServerSideProps,
InferGetServerSidePropsType,
NextPage,
} from 'next';
import * as api from 'api';
import { FullPageLoading, ObserverTarget } from 'components/common';
import { DiariesContainer } from 'components/diary';
import EmptyDiary from 'components/diary/EmptyDiary';
import { ProfileLayout } from 'components/profile';
import { PAGE_PATH } from 'constants/common';
import { queryKeys } from 'constants/services';
import { useIntersectionObserver } from 'hooks/common';
import { useUserDiaries } from 'hooks/services';
import { authOptions } from 'pages/api/auth/[...nextauth]';

const YourProfileDiaries: NextPage<
InferGetServerSidePropsType<typeof getServerSideProps>
> = ({ username }) => {
const {
userDiariesData,
isLoading: isUserDiariesLoading,
isError: isUserDiariesError,
fetchNextPage: fetchUserDiariesNextPage,
} = useUserDiaries(username);
const { setTargetRef: setUserDiariesTargetRef } = useIntersectionObserver({
onIntersect: fetchUserDiariesNextPage,
});

if (userDiariesData === undefined) {
return <FullPageLoading />;
}

return (
<ProfileLayout isMyProfile={false} username={username}>
<DiariesContainer
title={`${username} 프로필 - 일기`}
diariesData={userDiariesData}
empty={<EmptyDiary text="일기가 없습니다." />}
/>
<ObserverTarget
targetRef={setUserDiariesTargetRef}
isLoading={isUserDiariesLoading}
isError={isUserDiariesError}
/>
</ProfileLayout>
);
};

export const getServerSideProps = (async (context) => {
const { req, res, params } = context;
const username = params?.username as string;

const session = await getServerSession(req, res, authOptions);

if (session === null) {
return {
redirect: {
destination: PAGE_PATH.account.login,
permanent: false,
},
};
}

const { accessToken } = session.user;

const headers = {
headers: {
Authorization: `Bearer ${accessToken}`,
},
};

const queryClient = new QueryClient();

try {
await queryClient.fetchQuery([queryKeys.users, username], async () => {
return await api.getProfileByUsername({ username, config: headers });
});
} catch (error) {
if (isAxiosError(error)) {
return {
notFound: true,
};
}
}

return { props: { dehydratedState: dehydrate(queryClient), username } };
}) satisfies GetServerSideProps;

export default YourProfileDiaries;
Loading

0 comments on commit baeab5b

Please sign in to comment.