Skip to content

Commit

Permalink
Merge pull request #68 from Prography-9th-3team/feat/bookmark-card
Browse files Browse the repository at this point in the history
Feat/북마크 카드
  • Loading branch information
hoongding authored Jul 20, 2024
2 parents dfb9cda + b8887d4 commit b126209
Show file tree
Hide file tree
Showing 11 changed files with 92 additions and 34 deletions.
Binary file removed public/assets/image/empty_image.png
Binary file not shown.
Binary file added public/assets/image/empty_image_1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/image/empty_image_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions public/logo-white.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion public/next.svg

This file was deleted.

1 change: 0 additions & 1 deletion public/vercel.svg

This file was deleted.

1 change: 0 additions & 1 deletion src/app/RouteGuard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ const RouteGuard = () => {
const token = getCookie('accessToken');

if (chrome && chrome.runtime && chrome.runtime.sendMessage) {
console.log(!!token, token ?? '');
chrome.runtime.sendMessage(process.env.NEXT_PUBLIC_EXTENSION_ID, {
isLogin: !!token,
accessToken: token ?? '',
Expand Down
48 changes: 28 additions & 20 deletions src/components/BookmarkCard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import { useBookmarkLike, useGetThumbnailImage } from '@/apis/bookmark';
import { cn } from '@/lib/utils';
import useToastStore from '@/stores/toastStore';
import Image from 'next/image';
import { MouseEvent, useState } from 'react';
import Icon from '../common/Icon';
export interface IBookmarkCard {
Expand Down Expand Up @@ -42,9 +41,11 @@ const BookmarkCard = ({
useGetThumbnailImage(imageUUID);

const { mutateAsync: mutateBookmarkLike } = useBookmarkLike();

const [isLike, setIsLike] = useState(isFavorite);

const bookmarkTitle = title !== '' ? title : url;
const bookmarkSiteName = siteName !== '' ? siteName : url.split('/')[2];

// 북마크 좋아요
const handleToggleLike = (e: MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
Expand All @@ -54,6 +55,7 @@ const BookmarkCard = ({
});
};

// 북마크 링크 복사
const handleCopyUrl = async (e: MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();

Expand All @@ -62,6 +64,11 @@ const BookmarkCard = ({
});
};

// empty 이미지 랜덤
const getRandomNumber = () => {
return Math.random() < 0.5 ? 1 : 2;
};

return (
<div
className='cursor-pointer max-h-[342px] mb-40 flex flex-col gap-20 group'
Expand All @@ -81,37 +88,38 @@ const BookmarkCard = ({
width={650}
/>
) : (
<Image
<img
className='aspect-[296/180] object-cover'
src='/assets/image/empty_image.png'
alt='Empty'
src={`/assets/image/empty_image_${getRandomNumber()}.png`}
alt='썸네일'
width={650}
height={400}
/>
)}
</div>
<div className='flex flex-col gap-6 px-10'>
{categoryNames.length > 0 && (
<span className='body-sm-bold text-primary'>{categoryNames[0]}</span>
)}
<h2 className='body-lg-bold text-text'>{title}</h2>
<h2 className='body-lg-bold text-text truncate'>{bookmarkTitle}</h2>
<p className='body-md text-text-sub truncate'>{memo}</p>
</div>
<div className='relative px-10 flex items-center gap-8'>
<picture>
<img
className='rounded-full'
src={faviconUrl}
alt='파비콘'
width={28}
height={28}
onError={(e) => ((e.target as HTMLImageElement).src = '/logo.svg')}
/>
</picture>
<span className='body-md text-text truncate'>{siteName}</span>
<div className='px-10 flex items-center justify-between gap-8'>
<div className='flex items-center gap-8 truncate'>
<picture>
<img
className='rounded-full min-w-28 h-28'
src={faviconUrl ?? '/logo-white.svg'}
alt='파비콘'
width={28}
height={28}
onError={(e) => ((e.target as HTMLImageElement).src = '/logo-white.svg')}
/>
</picture>
<span className='body-md text-text truncate'>{bookmarkSiteName}</span>
</div>
<div
className={cn([
'absolute right-0 hidden items-center gap-12 bg-surface *:text-icon-minimal group-hover:flex',
'hidden items-center gap-12 *:text-icon-minimal group-hover:flex',
isLike && 'flex',
])}
>
Expand Down
12 changes: 7 additions & 5 deletions src/components/BookmarkItem/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@ const BookmarkItem = ({
const { addToast } = useToastStore();

const { mutateAsync: mutateBookmarkLike } = useBookmarkLike();

const [isLike, setIsLike] = useState(isFavorite);

const bookmarkTitle = title !== '' ? title : url;
const bookmarkSiteName = siteName !== '' ? siteName : url.split('/')[2];

// 북마크 좋아요
const handleToggleLike = (e: MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
Expand All @@ -60,18 +62,18 @@ const BookmarkItem = ({
onClick={onClick}
>
<div className='flex flex-col justify-center gap-4 px-8 overflow-hidden'>
<h2 className='body-md text-text truncate'>{title}</h2>
<h2 className='body-md text-text truncate'>{bookmarkTitle}</h2>
<p className='body-sm text-text-sub truncate'>{memo}</p>
</div>
<div className='flex items-center justify-end gap-8 px-8'>
<span className='body-md text-text truncate'>{siteName}</span>
<span className='body-md text-text truncate'>{bookmarkSiteName}</span>
<img
className='rounded-full'
src={faviconUrl}
src={faviconUrl ?? '/logo-white.svg'}
alt='파비콘'
width={20}
height={20}
onError={(e) => ((e.target as HTMLImageElement).src = '/logo.svg')}
onError={(e) => ((e.target as HTMLImageElement).src = '/logo-white.svg')}
/>
</div>
<div className='flex items-center justify-end px-8'>
Expand Down
2 changes: 1 addition & 1 deletion src/components/ContentBox/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const ContentBox = () => {
} = useBookmarkInfinityAPI({
size: 10,
direction: 'DESC',
property: 'id',
property: queryParam.get('sort') ?? 'id',
categoryId,
isFavorite: queryParam.get('favorite') === 'true',
});
Expand Down
57 changes: 52 additions & 5 deletions src/components/FilterBox/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,20 @@ import { useCategoryList, useSaveCategory } from '@/apis/category';
import useQueryString from '@/hooks/useQueyString';
import { cn } from '@/lib/utils';
import useToastStore from '@/stores/toastStore';
import { ChangeEvent, useRef, useState } from 'react';
import { ChangeEvent, useEffect, useRef, useState } from 'react';
import TabList from '../TabList';
import { Button } from '../common/Button';
import Divider from '../common/Divider';
import Icon from '../common/Icon';
import { Option } from '../common/Option';
import AddCategory from './AddCategory';

const sortList = [
{ label: '최신순', value: 'id' },
{ label: '이름순', value: 'updatedAt' },
{ label: '오래된순', value: 'createdAt' },
];

const FilterBox = () => {
const { queryParam, updateQueryString } = useQueryString();

Expand All @@ -19,12 +26,15 @@ const FilterBox = () => {
const { mutateAsync: mutateSaveCategory } = useSaveCategory();

const isLikeChecked = queryParam.get('favorite') === 'true'; // 종아요 항목 표시
const sortType = queryParam.get('sort') ?? 'id'; // 종아요 항목 표시
const viewType = queryParam.get('view') ?? 'grid'; // list 타입 grid | list

const [isShowSort, setIsShowSort] = useState<boolean>(false);
const [isOpenCategory, setIsOpenCategory] = useState<boolean>(false); // 카테고리 모달 오픈
const [category, setCategory] = useState<string>(''); // 카테고리 텍스트
const [isError, setIsError] = useState<boolean>(false); // 카테고리 에러

const sortTabRef = useRef<HTMLDivElement>(null);
const buttonRef = useRef<HTMLDivElement>(null);

const handleChangeCategory = (e: ChangeEvent<HTMLInputElement>) => {
Expand All @@ -34,10 +44,14 @@ const FilterBox = () => {
setCategory(e.target.value);
};

const handleSelectSortOption = (value: string) => {
updateQueryString('sort', value);
setIsShowSort(false);
};

/**
* TODO :
* - 유효성 검증 case 추가 필요
* - 카테고리 영역 밖 클릭 or esc 닫기
*/
const handleAddCategory = () => {
if (!category) {
Expand All @@ -63,6 +77,19 @@ const FilterBox = () => {
});
};

// 정렬 option 외부 클릭시 닫힘
useEffect(() => {
const handleCloseSortOption = (e: Event) => {
const target = e.target as HTMLElement;

if (isShowSort && !sortTabRef.current?.contains(target)) setIsShowSort(false);
};

window.addEventListener('click', handleCloseSortOption);

return () => window.removeEventListener('click', handleCloseSortOption);
}, [isShowSort]);

return (
<div>
<div className='px-40 flex justify-between'>
Expand Down Expand Up @@ -108,9 +135,29 @@ const FilterBox = () => {
</Button.Label>
</Button>
<div className='flex items-center gap-12'>
<span className='cursor-pointer mr-4 flex items-center gap-4 label-md text-text-minimal'>
최신순 <Icon name='chevronDown_s' className='w-20 h-20 ' />
</span>
{/* 정렬 START */}
<div className='relative' ref={sortTabRef}>
<span
className='cursor-pointer mr-4 flex items-center gap-4 label-md text-text-minimal'
onClick={() => {
setIsShowSort((prev) => !prev);
}}
>
{sortList.find((item) => item.value === sortType)?.label}
<Icon name='chevronDown_s' className='w-20 h-20 ' />
</span>
{isShowSort && (
<div className='absolute top-[calc(100%+8px)] w-[165px] flex flex-col gap-4 p-8 bg-surface rounded-lg shadow-layer z-10'>
{sortList.map((item) => (
<Option key={item.value} onClick={() => handleSelectSortOption(item.value)}>
<Option.Label>{item.label}</Option.Label>
</Option>
))}
</div>
)}
</div>

{/* 정렬 END */}
<button onClick={() => updateQueryString('view', 'grid')}>
<Icon
name='grid'
Expand Down

0 comments on commit b126209

Please sign in to comment.