Skip to content
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

feat : 포즈피드 및 필터 기능 완성 #24

Merged
merged 54 commits into from
Aug 24, 2023
Merged
Show file tree
Hide file tree
Changes from 53 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
535ed43
💄 style : 바텀시트 레이아웃
seondal Aug 15, 2023
d00d955
♻️ refactor : Selection 컴포넌트 모듈화
seondal Aug 15, 2023
0e77720
💄 style : 필터 바텀시트 요소
seondal Aug 15, 2023
bd5db6b
✨ feat : PrimaryButton 모듈화
seondal Aug 15, 2023
f1e3e88
✨ feat : SelectionTag
seondal Aug 17, 2023
194fddc
✨ feat : 필터 초기화
seondal Aug 17, 2023
50cbe99
🛠 fix : tailwindCSS className에 변수 넣지 않도록 수정
seondal Aug 17, 2023
2850cea
⚙️ set : 전역상태라이브러리 recoil
seondal Aug 17, 2023
d0a76f4
✨ feat : useBottomSheet
seondal Aug 17, 2023
15a412f
♻️ refactor : BottomSheet 모듈화
seondal Aug 17, 2023
ebfcb69
style : 북마크 탭 추가 및 레이아웃 수정
seondal Aug 18, 2023
ee2fae2
♻️ refactor : PhotoList 컴포넌트
seondal Aug 18, 2023
dfb2db7
💄 style : 북마크 버튼
seondal Aug 18, 2023
a60409f
💄 style : 포즈를 보관해보세요 !
seondal Aug 18, 2023
2db4089
♻️ refactor : EmptyCase 컴포넌트화
seondal Aug 18, 2023
efcf987
✂️ edit : selectiontag -> selectionTagList
seondal Aug 22, 2023
a8a8751
Merge branch 'develop' into feature/OZ-38
seondal Aug 22, 2023
b0d0f34
Merge branch 'feature/OZ-38' into feat/OZ-68
seondal Aug 22, 2023
b5ef501
Merge pull request #16 from dnd-side-project/feat/OZ-68
seondal Aug 22, 2023
d192bec
Merge branch 'feature/OZ-38' of https://github.com/dnd-side-project/d…
seondal Aug 22, 2023
208a1a8
💄 style : 레이아웃 오류 수정
seondal Aug 22, 2023
64949c8
💄 style : 스크롤 위한 레이아웃 변경
seondal Aug 22, 2023
d20d233
💄 style : z-index
seondal Aug 22, 2023
9460438
✨ feat : 포즈피드 목업 데이터 연결
seondal Aug 22, 2023
eaa892b
✨ feat : 필터결과 없음 분기처리
seondal Aug 22, 2023
56431a5
🛠 fix : queryProvider 무한 호출 생기게하는 옵션삭제
seondal Aug 22, 2023
5536bbc
✨ feat : 포즈피드 api 연결
seondal Aug 22, 2023
d7a683c
✨ feat : 필터바텀시트 전역으로 관리
seondal Aug 23, 2023
b20f5a5
🏗 folder : Provider 폴더링
seondal Aug 23, 2023
efa28a9
✨ feat : 포즈피드 필터링 기능 구현 완
seondal Aug 23, 2023
1da48ce
✨ feat : 피드에서 태그 클릭하면 필터 사라짐
seondal Aug 23, 2023
2f2d7c1
🛠 fix : 필터 바텀시트 초기화안되는 오류
seondal Aug 23, 2023
cba1302
💄 style : Photo rounded
seondal Aug 23, 2023
9d6b3cd
♻️ refactor : 포즈피드 리팩토링
seondal Aug 23, 2023
f7efa3d
🛠 fix : FrameCount 값 idx와 다른부분 처리
seondal Aug 23, 2023
c92f971
✨ feat : 필터태그 리스트 api 연결
seondal Aug 23, 2023
2e14226
🛠 fix : 태그리스트 overflow
seondal Aug 23, 2023
29b0dc1
🛠 fix : 쿼리 파라미터 순서 이슈
seondal Aug 23, 2023
3002e74
💄 style : 포즈픽 로티 사이즈 가로 맞춤
seondal Aug 23, 2023
c5d7a70
🛠 fix : 포즈톡 디테일 수정
seondal Aug 23, 2023
5e51cc8
🛠 fix : 포즈픽 디테일 수정
seondal Aug 23, 2023
0246cab
Merge branch 'develop' into feature/OZ-38
seondal Aug 23, 2023
79f299a
Merge branch 'feature/OZ-38' into feature/OZ-72
seondal Aug 23, 2023
58dd09f
Merge pull request #23 from dnd-side-project/feature/OZ-72
seondal Aug 23, 2023
4b4533a
🛠 fix : 빌드 에러
seondal Aug 23, 2023
8e89190
🛠 fix : 빌드 에러 2
seondal Aug 23, 2023
7c7d8ae
🛠 fix : 빌드에러 3
seondal Aug 23, 2023
290f9c1
Merge branch 'feature/OZ-38' into feature/OZ-72
seondal Aug 23, 2023
7b44fbe
Merge pull request #25 from dnd-side-project/feature/OZ-72
seondal Aug 23, 2023
658a7e0
fix : auto-assignee 에러 수정
guesung Aug 24, 2023
cd66332
Revert "fix : auto-assignee 에러 수정"
guesung Aug 24, 2023
d456c43
🛠 fix : develop 끌어오기
seondal Aug 24, 2023
bf6a7c1
Merge branch 'feature/OZ-38' of https://github.com/dnd-side-project/d…
seondal Aug 24, 2023
0805c9b
🛠 fix : QueryProvider defaultOption 제거
seondal Aug 24, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"react-dom": "18.2.0",
"react-lottie-player": "^1.5.4",
"react-tooltip": "^5.20.0",
"recoil": "^0.7.7",
"tailwind-merge": "^1.14.0",
"typescript": "5.1.6"
},
Expand Down
10 changes: 4 additions & 6 deletions public/icons/restart.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 13 additions & 1 deletion src/apis/apis.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { PoseDetailResponse, PosePickResponse, PoseTalkResponse } from '.';
import { FilterTagsResponse, PoseFeedResponse, PosePickResponse, PoseTalkResponse } from '.';
import publicApi from './config/publicApi';

export const getPosePick = (peopleCount: number) =>
Expand All @@ -8,3 +8,15 @@ export const getPoseDetail = (poseId: number) =>
publicApi.get<PoseDetailResponse>(`/pose/${poseId}`);

export const getPoseTalk = () => publicApi.get<PoseTalkResponse>('/pose/talk');

export const getPoseFeed = (peopleCount: number, frameCount: number, tags: string) =>
publicApi.get<PoseFeedResponse>(`/pose`, {
params: {
frameCount,
pageNumber: 0,
peopleCount,
tags,
},
});

export const getFilterTag = () => publicApi.get<FilterTagsResponse>('/pose/tags');
24 changes: 22 additions & 2 deletions src/apis/queries.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { UseQueryOptions, useQuery } from '@tanstack/react-query';

import { FilterState } from '@/hooks/useFilterState';
import {
type PosePickResponse,
type PoseTalkResponse,
FilterTagsResponse,
PoseFeedResponse,
PosePickResponse,
PoseTalkResponse,
getFilterTag,
getPoseDetail,
getPoseFeed,
getPosePick,
getPoseTalk,
} from '.';
Expand All @@ -22,3 +27,18 @@ export const usePosePickQuery = (

export const usePoseTalkQuery = (options?: UseQueryOptions<PoseTalkResponse>) =>
useQuery<PoseTalkResponse>(['poseTalk'], getPoseTalk, { enabled: false, ...options });

export const usePoseFeedQuery = (
{ peopleCount, frameCount, tags }: FilterState,
options?: UseQueryOptions<PoseFeedResponse>
) =>
useQuery<PoseFeedResponse>(
['poseFeed', peopleCount, frameCount, tags],
() => getPoseFeed(peopleCount, frameCount, tags.join(',')),
{
...options,
}
);

export const useFilterTagQuery = (options?: UseQueryOptions<FilterTagsResponse>) =>
useQuery<FilterTagsResponse>(['filterTag'], getFilterTag, { ...options });
66 changes: 55 additions & 11 deletions src/apis/type.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,59 @@
export interface PosePickResponse {
poseInfo: {
createdAt: string;
frameCount: number;
imageKey: string;
peopleCount: number;
poseId: number;
source: string;
sourceUrl: string;
tagAttributes: string;
updatedAt: string;
export interface PoseInfo {
createdAt: string;
frameCount: number;
imageKey: string;
peopleCount: number;
poseId: number;
source: string;
sourceUrl: string;
tagAttributes: string;
updatedAt: string;
}

// 포즈피드
interface PoseFeedContentsSort {
empty: boolean;
sorted: boolean;
unsorted: boolean;
}
interface PoseFeedContents {
content: Array<{ poseInfo: PoseInfo }>;
pageable: {
sort: PoseFeedContentsSort;
offset: number;
pageNumber: number;
pageSize: number;
paged: boolean;
unpaged: boolean;
};
number: number;
sort: PoseFeedContentsSort;
size: number;
numberOfElements: number;
first: boolean;
last: boolean;
empty: boolean;
}
export interface PoseFeedResponse {
recommendation: boolean;
filteredContents: PoseFeedContents;
recommendedContents: PoseFeedContents;
}

// 필터 태그
interface FilterTag {
createdAt: string;
updatedAt: string;
attributeId: number;
attribute: string;
}
export interface FilterTagsResponse {
poseTagAttributes: FilterTag[];
}

// 포즈픽
export interface PosePickResponse {
poseInfo: PoseInfo;
}

export interface PoseDetailResponse {
Expand Down
16 changes: 16 additions & 0 deletions src/app/(Main)/bookmark/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import EmptyCase from '../feed/components/EmptyCase';
import PhotoList from '../feed/components/PhotoList';

export default function BookmarkPage() {
return (
<>
<EmptyCase
title={'포즈를 보관해 보세요!'}
text={`북마크 버튼으로 포즈를 보관할 수 있어요.\n포즈피드에서 멋진 포즈를 찾아 보관해 보세요.`}
button={'포즈피드 바로가기'}
path={'/feed'}
/>
<PhotoList />
</>
);
}
1 change: 1 addition & 0 deletions src/app/(Main)/components/Tab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const tabData = [
{ path: '/pick', title: '포즈픽' },
{ path: '/talk', title: '포즈톡' },
{ path: '/feed', title: '포즈피드' },
{ path: '/bookmark', title: '북마크' },
];

export default function Tab() {
Expand Down
29 changes: 29 additions & 0 deletions src/app/(Main)/feed/components/EmptyCase.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import Link from 'next/link';

import { PrimaryButton } from '@/components/Button';
import { Spacing } from '@/components/Spacing';

interface EmptyCase {
title: string;
text: string;
button: string;
path: string;
}

export default function EmptyCase(props: EmptyCase) {
const { title, text, button, path } = props;

return (
<div className="py-80 text-center">
<h4 className="text-secondary">{title}</h4>
<Spacing size={8} />
<p className="text-tertiary">{text}</p>
<Spacing size={32} />
<div className="flex justify-center">
<Link href={path}>
<PrimaryButton text={button} type="secondary" />
</Link>
</div>
</div>
);
}
13 changes: 0 additions & 13 deletions src/app/(Main)/feed/components/Filter.tsx

This file was deleted.

84 changes: 84 additions & 0 deletions src/app/(Main)/feed/components/FilterSheet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { useEffect, useState } from 'react';

import { FilterTagsResponse, useFilterTagQuery } from '@/apis';
import { PrimaryButton } from '@/components/Button';
import BottomSheet from '@/components/Modal/BottomSheet';
import { SelectionBasic, SelectionTagList } from '@/components/Selection';
import { frameCountList, peopleCountList } from '@/constants/filterList';
import { ICON } from '@/constants/icon';
import useBottomSheet from '@/hooks/useBottomSheet';
import useFilterState from '@/hooks/useFilterState';

export default function FilterSheet() {
const { data: tagListData } = useFilterTagQuery();

const { filterState, updateFilterState } = useFilterState();
const { isBottomSheetOpen, closeBottomSheet } = useBottomSheet();

const [countState, setCountState] = useState<number>(0);
const [frameState, setFrameState] = useState<number>(0);
const [tagState, setTagState] = useState<string[]>([]);

function resetFilter() {
setCountState(0);
setFrameState(0);
setTagState([]);
}

function decideFilter() {
updateFilterState({ peopleCount: countState, frameCount: frameState, tags: tagState });
closeBottomSheet();
}

useEffect(() => {
setCountState(filterState.peopleCount);
setFrameState(filterState.frameCount);
setTagState(filterState.tags);
}, [isBottomSheetOpen, filterState]);

function refineTagListData(tagListData: FilterTagsResponse) {
const tagList: string[] = [];
for (const tag of tagListData.poseTagAttributes) {
tagList.push(tag.attribute);
}
return tagList;
}

return (
<BottomSheet>
<section>
<div id="subtitle-2" className="mb-8 text-secondary">
인원 수
</div>
<SelectionBasic data={peopleCountList} state={countState} setState={setCountState} />
</section>
<section>
<div id="subtitle-2" className="mb-8 text-secondary">
프레임 수
</div>
<SelectionBasic data={frameCountList} state={frameState} setState={setFrameState} />
</section>
<section>
<div id="subtitle-2" className="mb-8 text-secondary">
태그
</div>
{tagListData && (
<SelectionTagList
data={refineTagListData(tagListData)}
state={tagState}
setState={setTagState}
/>
)}
</section>
<div className="flex gap-8 py-20 [&>*]:flex-1">
<PrimaryButton
type="outline"
icon={ICON.restart}
text="필터 초기화"
onClick={resetFilter}
/>
<PrimaryButton type="fill" text="포즈보기" onClick={decideFilter} />
</div>
</BottomSheet>
);
}
39 changes: 39 additions & 0 deletions src/app/(Main)/feed/components/FilterTab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import Image from 'next/image';

import Tag from '@/components/Selection/Tag';
import { ICON } from '@/constants/icon';
import useBottomSheet from '@/hooks/useBottomSheet';
import useFilterState from '@/hooks/useFilterState';

export default function FilterTab() {
const { openBottomSheet } = useBottomSheet();
const { selectedFilterItems, deleteSelectedFilterItem } = useFilterState();
const tags = selectedFilterItems();
const isFiltered = tags.length !== 0;

return (
<div className="fixed inset-x-0 top-104 z-10 flex h-56 items-center gap-8 bg-white px-20">
<button
className={`flex min-w-fit items-center gap-8 rounded-8 ${
isFiltered
? 'border-1 border-main-violet bg-main-violet-base text-main-violet'
: 'bg-sub-white'
} px-16 py-9`}
onClick={openBottomSheet}
>
<h5 id="subtitle-2">필터</h5>
<Image src={ICON.carat.down} alt="▾" width={16} height={16} priority />
</button>
{isFiltered && (
<>
<div className="text-divider">|</div>
<div className="flex gap-8 overflow-x-scroll">
{tags.map((tag) => (
<Tag key={tag.value} text={tag.value} onClick={() => deleteSelectedFilterItem(tag)} />
))}
</div>
</>
)}
</div>
);
}
23 changes: 23 additions & 0 deletions src/app/(Main)/feed/components/Photo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import Image from 'next/image';

import { ICON } from '@/constants/icon';

interface Photo {
imageKey?: string;
source?: string;
}

export default function Photo({ imageKey, source }: Photo) {
return (
<div className={`relative z-0 mb-16 inline-block h-fit w-full rounded-8`}>
{imageKey && (
<>
<img src={imageKey} alt={source} className="rounded-8" />
<div className="absolute bottom-6 right-6 h-36 w-36 rounded-24 bg-white bg-opacity-30 p-6">
<Image src={ICON.bookmark.empty} width={24} height={24} alt="🔖" />
</div>
</>
)}
</div>
);
}
24 changes: 24 additions & 0 deletions src/app/(Main)/feed/components/PhotoList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import Photo from './Photo';
import { PoseInfo } from '@/apis';

interface PhotoList {
data?: Array<{ poseInfo: PoseInfo }>;
}

export default function PhotoList({ data }: PhotoList) {
return (
<div className="columns-2 overflow-y-scroll">
{data ? (
data.map((item) => (
<Photo
key={item.poseInfo.poseId}
imageKey={item.poseInfo.imageKey}
source={item.poseInfo.source}
/>
))
) : (
<Photo />
)}
</div>
);
}
3 changes: 0 additions & 3 deletions src/app/(Main)/feed/components/Thumbnails.tsx

This file was deleted.

Loading