Skip to content

Commit

Permalink
Merge pull request #71 from pobu-team/develop
Browse files Browse the repository at this point in the history
develop -> main
  • Loading branch information
himyne authored Nov 19, 2023
2 parents 8173dea + 885fa8b commit 747de93
Show file tree
Hide file tree
Showing 42 changed files with 575 additions and 192 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ module.exports = {
'**/*.test.tsx',
],
}],
'react/require-default-props': 'off',
'import/extensions': ['error', 'ignorePackages', {
js: 'never',
jsx: 'never',
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"typescript": "^5.0.2"
},
"dependencies": {
"@tanstack/react-query": "^5.7.0",
"axios": "^1.4.0",
"framer-motion": "^10.16.4",
"react": "^18.2.0",
Expand Down
7 changes: 6 additions & 1 deletion src/components/category/CategoryButtons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import useDragScroll from '../../hooks/useDragScroll';
import useFetchCategoryTags from '../../hooks/useFetchCategoryTags';
import CATEGORY from '../../types/CategoryType';
import addGaEvent from '../../utils/addGaEvent';
import { Tag } from '../../types/Tag';

const ButtonContainer = styled.div`
overflow: scroll;
Expand Down Expand Up @@ -56,13 +57,17 @@ export default function CategoryButtons({ selectedTagId, setSelectedTagId, categ
addGaEvent(`Category Tab - ${selectorName}`);
};

const handleClickButton = (tag:Tag) => {
handleClick(tag.tagId, tag.selectorName);
};

return (
<ButtonContainer ref={ref}>
{tags.map((tag) => (
<CategoryButton
key={tag.tagId}
type="button"
onClick={() => handleClick(tag.tagId, tag.selectorName)}
onClick={() => handleClickButton(tag)}
active={selectedTagId === tag.tagId}
>
{tag.selectorName}
Expand Down
4 changes: 1 addition & 3 deletions src/components/category/CategoryPose.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,7 @@ export default function CategoryPose({ category }: {category:CATEGORY}) {
category={category}
/>
<PoseContainer>
<React.Suspense fallback={<div>Loading...</div>}>
<CategoryPoseList category={category} selectedTagId={selectedTagId} />
</React.Suspense>
<CategoryPoseList category={category} selectedTagId={selectedTagId} />
</PoseContainer>
{isLogInModalShowing && <LoginModal />}
</Container>
Expand Down
67 changes: 58 additions & 9 deletions src/components/category/CategoryPoseList.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import styled from 'styled-components';
import { useLocation } from 'react-router';
import useFetchCategoryPoses from '../../hooks/useFetchCategoryPoses';
import { useEffect } from 'react';
import { useInfiniteQuery } from '@tanstack/react-query';
import CATEGORY from '../../types/CategoryType';
import PoseList from '../common/PoseList';
import useFetchLikeList from '../../hooks/useFetchLikeList';
import { apiService } from '../../service/ApiService';
import { ALL_PEOPLE_TAG } from '../../constant/tagId';
import useIntersectionObserver from '../../hooks/useIntersectionObserver';
import Pose from '../select/Pose';

interface CategoryPoseListProps {
category: CATEGORY;
Expand All @@ -12,19 +16,64 @@ interface CategoryPoseListProps {

const Container = styled.div<{isPopular: boolean}>`
margin-top: ${(props) => (props.isPopular ? '5.7rem' : '10rem')};
margin-bottom: 15px;
`;

const GridContainer = styled.div`
display: grid;
grid-template-columns: 1fr 1fr;
justify-content: center;
grid-auto-rows: auto;
column-gap: 10px;
row-gap: 16px;
`;

export default function CategoryPoseList({ category, selectedTagId }: CategoryPoseListProps) {
const poses = useFetchCategoryPoses(category, selectedTagId);
const likePoseIdArr = useFetchLikeList();
const location = useLocation();

useEffect(() => {
window.onbeforeunload = function pushRefresh() {
window.scrollTo(0, 0);
};
}, []);

const {
data,
fetchNextPage,
} = useInfiniteQuery({
queryKey: ['poses', category, selectedTagId],
queryFn: async ({ pageParam = 0 }) => {
const response = selectedTagId === ALL_PEOPLE_TAG
? (await apiService.fetchPose([], pageParam))
: (await apiService.fetchPose([selectedTagId], pageParam));
return response;
},
initialPageParam: 0,
getNextPageParam: (lastPage) => (
lastPage.pagination.hasNext
? lastPage.pagination.page + 1
: undefined),
});
const { bottomRef } = useIntersectionObserver(fetchNextPage);
const poses = data?.pages.map((x) => x.data).flat();

useEffect(() => {
window.scrollTo(0, 0);
}, [selectedTagId]);

return (
<Container isPopular={location.pathname === '/category/popular'}>
<PoseList
poses={poses}
likePoseIdArr={likePoseIdArr}
/>
</Container>
<>
<Container isPopular={location.pathname === '/category/popular'}>
<GridContainer>
{poses && (
poses.map((pose) => (
<Pose key={pose.poseId} pose={pose} likePoseIdArr={likePoseIdArr} />
))
)}
</GridContainer>
</Container>
<div ref={bottomRef} />
</>
);
}
15 changes: 10 additions & 5 deletions src/components/common/PoseList.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
import styled from 'styled-components';
import { PoseType } from '../../types/PoseType';
import { PoseInfo } from '../../types/PoseType';
import Pose from '../select/Pose';

const Container = styled.div`
column-count: 2;
display: grid;
grid-template-columns: 1fr 1fr;
justify-content: center;
grid-auto-rows: auto;
column-gap: 10px;
row-gap: 16px;
`;

export default function PoseList({ poses, likePoseIdArr }: {
poses:PoseType[],
poses:PoseInfo[],
likePoseIdArr: string[];
}) {
return (
<Container>
{poses.map((pose: PoseType) => (
{poses.map((pose) => (
<Pose
key={pose.poseId}
poseId={pose.poseId}
pose={pose}
likePoseIdArr={likePoseIdArr}
/>
))}
Expand Down
2 changes: 1 addition & 1 deletion src/components/detail/PoseDetail.styles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export const ButtonContainer = styled.div`
width: 180px;
padding: 0;
height: 60px;
margin: 30px 0;
margin: 24px 0;
color: ${(props) => props.theme.colors.text};
background: ${(props) => props.theme.colors.detailButton};
Expand Down
100 changes: 81 additions & 19 deletions src/components/detail/PoseDetail.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import styled from 'styled-components';
import { useRecoilValue } from 'recoil';
import { useState } from 'react';
import { ButtonContainer, TagButtonContainer } from './PoseDetail.styles';
import { PoseWithIdSelector } from '../../recoil/poseState';
import { PoseInfo } from '../../types/PoseType';
Expand All @@ -14,6 +15,11 @@ import PoseImage from './PoseImage';
import imageDownload from '../../utils/downloadImage';
import LoginModal from '../../ui/LoginModal';
import { isLogInModalShowingAtom } from '../../recoil/loginState';
import DeleteIcon from '../svg/DeleteIcon';
import BoxModal from '../../ui/BoxModal';
import Loading from '../common/Loading';
import useFetchMyPoses from '../../hooks/useFetchMyPoses';
import { useDeletePose } from '../../queries/poses';

type PoseDetailProps = {
poseId: (string | undefined);
Expand All @@ -28,18 +34,55 @@ const Container = styled.div`
}
`;

const ButtonSection = styled.section`
position: fixed;
left: 50%;
translate: -50%;
bottom: 0;
width: 100vw;
max-width: 375px;
display: flex;
flex-direction: column;
align-items: center;
padding-inline: ${(props) => props.theme.sizes.contentPadding};
@media screen and (max-width: 340px) {
padding: 1.2rem;
}
`;

const DeleteButton = styled.button`
margin-top: 1.6rem;
display: flex;
align-items: center;
padding: 1.1rem 2rem 1.1rem 1.6rem;
border-radius: 40px;
background-color: ${(props) => props.theme.colors.backgroundSecondary};
border: 1px solid ${(props) => props.theme.colors.lineNormal};
color: ${(props) => props.theme.colors.textNormal};
font-weight: 500;
font-size: 1.6rem;
line-height: 2.6rem;
cursor: pointer;
`;

export default function PoseDetail({ poseId }: PoseDetailProps) {
const poseInfo: PoseInfo = useRecoilValue(PoseWithIdSelector(poseId));
const isLogInModalShowing = useRecoilValue(isLogInModalShowingAtom);
const [isDeleteModalShowing, setIsDeleteModalShowing] = useState(false);
const tagArr = poseInfo.tags.map((tag: Tag) => tag.selectorName);

const { isLoading, data: myPoses } = useFetchMyPoses();
const mutate = useDeletePose();
sortTag(tagArr);

const imageUrl = `${process.env.REACT_APP_API_BASE_URL}${poseInfo.imageUrl}`;
const handleClickDownloadButton = () => {
imageDownload({ imageUrl, poseId: poseInfo.poseId });
};

if (isLoading) {
<Loading />;
}

return (
<Container>
<TagButtonContainer>
Expand All @@ -51,25 +94,44 @@ export default function PoseDetail({ poseId }: PoseDetailProps) {
))}
</TagButtonContainer>
<PoseImage poseInfo={poseInfo} />
<ButtonContainer>
<button
type="button"
onClick={handleClickDownloadButton}
>
<DownloadIcon />
포즈 다운로드
</button>
<button
type="button"
onClick={async () => {
await shareLink(poseInfo.imageUrl, poseInfo.poseId);
addGaEvent('Share Pose');
<ButtonSection>
{myPoses?.find((item) => item.poseId === poseId)
&& (
<DeleteButton onClick={() => setIsDeleteModalShowing(true)}>
<DeleteIcon width={24} height={24} />
<span>삭제하기</span>
</DeleteButton>
)}
<ButtonContainer>
<button
type="button"
onClick={handleClickDownloadButton}
>
<DownloadIcon />
포즈 다운로드
</button>
<button
type="button"
onClick={async () => {
await shareLink(poseInfo.imageUrl, poseInfo.poseId);
addGaEvent('Share Pose');
}}
>
<ShareIcon />
포즈 공유하기
</button>
</ButtonContainer>
</ButtonSection>
{isDeleteModalShowing
&& (
<BoxModal
handleClose={() => setIsDeleteModalShowing(false)}
handleClick={() => {
mutate(poseId ?? '');
}}
>
<ShareIcon />
포즈 공유하기
</button>
</ButtonContainer>
text="등록한 포즈를 삭제하시겠어요?"
/>
)}
{isLogInModalShowing && <LoginModal />}
</Container>
);
Expand Down
3 changes: 2 additions & 1 deletion src/components/footer/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import { useReadLocalStorage } from 'usehooks-ts';

const ButtonContainer = styled.div<{ active: boolean}>`
width: 100%;
height: 6.4rem;
height: 7rem;
margin-bottom: .3rem;
display: flex;
justify-content: center;
align-items: center;
Expand Down
4 changes: 2 additions & 2 deletions src/components/header/BackButton.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { To, useNavigate } from 'react-router';
import { useNavigate } from 'react-router';

import styled from 'styled-components';

Expand Down Expand Up @@ -33,7 +33,7 @@ const Button = styled.button`
cursor: pointer;
`;

export default function BackButton({ location } : {location: string}) {
export default function BackButton({ location = '' } : {location?: string}) {
const navigate = useNavigate();

const isDarkMode = useReadLocalStorage('darkMode');
Expand Down
9 changes: 2 additions & 7 deletions src/components/header/TitleHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,11 @@ interface TitleHeaderProps {
backButton? : string,
}

export default function TitleHeader({ title, backButton }: TitleHeaderProps) {
const backButtonLocation = backButton ?? '';
export default function TitleHeader({ title, backButton = '' }: TitleHeaderProps) {
return (
<Container>
<BackButton location={backButtonLocation} />
<BackButton location={backButton} />
<h1>{title}</h1>
</Container>
);
}

TitleHeader.defaultProps = {
backButton: '',
};
Loading

0 comments on commit 747de93

Please sign in to comment.