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: 친구 페이지 친구 삭제, 내 초대코드 조회, 친구 추가 API 연동 #28

Merged
merged 14 commits into from
Feb 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ module.exports = {
'@typescript-eslint/strict-boolean-expressions': 'off',
'prettier/prettier': ['error', { endOfLine: 'auto' }],
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-floating-promises': 'off'
},
};
9 changes: 9 additions & 0 deletions src/assets/icons/CheckIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';

const CheckIcon: React.FC<React.SVGProps<SVGSVGElement>> = (props) => (
<svg width="12" height="8" viewBox="0 0 12 8" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
<path d="M1.3938 3.16216L5.02294 6.7913L10.6062 1.20801" stroke={props.stroke ? 'current' : '#F1F2F4'} strokeWidth="1.67" strokeLinecap="round"/>
</svg>
);
export default CheckIcon;

9 changes: 9 additions & 0 deletions src/assets/icons/WarningIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';

const WarningIcon: React.FC<React.SVGProps<SVGSVGElement>> = (props) => (
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
<path fill={props.fill ? "current" : '#FB2414'} fillRule="evenodd" clipRule="evenodd" d="M12 6C12 9.31348 9.3137 12 6 12C2.6863 12 0 9.31348 0 6C0 2.68652 2.6863 0 6 0C9.3137 0 12 2.68652 12 6ZM6 2.6168C6.27671 2.6168 6.50101 2.84121 6.50101 3.11777V6.30527C6.50101 6.58184 6.27671 6.80625 6 6.80625C5.72329 6.80625 5.49899 6.58184 5.49899 6.30527V3.11777C5.49899 2.84121 5.72329 2.6168 6 2.6168ZM6 8.88223C6.27667 8.88223 6.50098 8.65781 6.50098 8.38125C6.50098 8.10469 6.27667 7.88027 6 7.88027C5.72329 7.88027 5.49899 8.10469 5.49899 8.38125C5.49899 8.65781 5.72329 8.88223 6 8.88223Z" />
</svg>
);

export default WarningIcon;
2 changes: 2 additions & 0 deletions src/assets/icons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,5 @@ export { default as DateIcon } from './DateIcon';
export { default as ClockIcon } from './ClockIcon';
export { default as CameraIcon } from './CameraIcon';
export { default as CircleCloseIcon } from './CircleCloseIcon';
export { default as CheckIcon } from './CheckIcon';
export { default as WarningIcon } from './WarningIcon';
5 changes: 4 additions & 1 deletion src/components/atoms/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@ interface ButtonProps {
className?: string;
}

const Button: React.FC<ButtonProps> = ({ text, className, onClick }) => {
const Button: React.FC<
ButtonProps & React.ButtonHTMLAttributes<HTMLButtonElement>
> = ({ text, className, onClick, ...props }) => {
return (
<button
className={`p-18 gap-12 rounded-12 heading4-semibold ${className}`}
onClick={onClick}
{...props}
>
{text}
</button>
Expand Down
18 changes: 18 additions & 0 deletions src/components/atoms/CheckBox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { CheckIcon } from '@/assets/icons';
import React from 'react';

const CheckBox: React.FC<{ active: boolean; onClick: () => void }> = ({
active = false,
onClick,
}) => {
const commonStyle =
'flex justify-center items-center w-[20px] h-[20px] rounded-full';
const buttonClassName = `${commonStyle} ${active ? 'bg-primary2' : 'border border-gray5'}`;
return (
<button onClick={onClick} className={buttonClassName}>
<CheckIcon stroke={active ? '#F1F2F4' : '#9299AA'} />
</button>
);
};

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이런 형태도 가능할 것 같네요..!🙇‍♂️

Suggested change
const CheckBox: React.FC<{ active: boolean; onClick: () => void }> = ({
active = false,
onClick,
}) => {
const commonStyle = 'flex justify-center items-center w-20 h-20 rounded-full';
const buttonClassName = `${commonStyle} ${active ? 'bg-primary2' : 'border border-gray5'}`;
return (
<button onClick={onClick} className={buttonClassName}>
<CheckIcon stroke={active ? '기본값 혹은 undefined' : '#9299AA'} />
</button>
);
};
export default CheckBox;

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

분기 파편화 이슈가 생길까봐 고민 했었는데 공유 주신 코드가 훨씬 간결해보이네요 👍🏻
반영해두었습니다☺️ 감사해요!🙇‍♀️

export default CheckBox;
1 change: 1 addition & 0 deletions src/components/atoms/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ export { default as Input } from './Input';
export { default as MiniButton } from './MiniButton';
export { default as ExclamationAlertSpan } from './ExclamationAlertSpan';
export { default as Lottie } from './Lottie';
export { default as CheckBox } from './CheckBox';
14 changes: 14 additions & 0 deletions src/components/molecules/FriendshipHeaderSettingButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react';
import { SettingIcon } from '@/assets/icons';

const FriendshipHeaderSettingButton: React.FC<{ onClick: () => void }> = ({
onClick,
}) => {
return (
<button onClick={onClick}>
<SettingIcon />
</button>
);
};

export default FriendshipHeaderSettingButton;
2 changes: 1 addition & 1 deletion src/components/molecules/LabelRoundBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const LabelRoundBox: React.FC<{
return (
<div className="mb-[20px] px-[20px] py-[16px] bg-white rounded-[12px]">
<p className="mb-[12px] body1-semibold text-gray8">{label}</p>
<div className="flex">{content}</div>
{content}
</div>
);
};
Expand Down
13 changes: 13 additions & 0 deletions src/components/molecules/WarningLine.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from 'react';
import { WarningIcon } from '@/assets/icons';

const WarningLine: React.FC<{ text: string }> = ({ text }) => {
return (
<div className="flex items-center">
<WarningIcon />
<p className="ml-[4px] body2-regular text-point4">{text}</p>
</div>
);
};

export default WarningLine;
1 change: 1 addition & 0 deletions src/components/molecules/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ export { default as NavWhiteBoxItem } from './NavWhiteBoxItem';
export { default as VerticalLabelValue } from './VerticalLabelValue';
export { default as ShareInfoRowItem } from './ShareInfoRowItem';
export { default as LabelRoundBox } from './LabelRoundBox';
export { default as WarningLine } from './WarningLine';
39 changes: 24 additions & 15 deletions src/components/organisms/FriendListItem.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,47 @@
import { AngleIcon } from '@/assets/icons';
import { type ProfileEnum } from '@/types/common';
import { returnProfileImg } from '@/utils/returnProfileImg';
import Image from 'next/image';
import React from 'react';
import { CheckBox } from '@/components/atoms';
import { type FriendshipData } from '@/types/friendship';

const FriendListItem: React.FC<{
name: string;
count: number;
profileEnum: ProfileEnum;
}> = ({ name, count, profileEnum }) => {
data: FriendshipData;
possibleDelete: boolean;
onClick: () => void;
active: boolean;
}> = ({ data, possibleDelete, onClick, active }) => {
return (
<div className="flex p-[16px] mb-[12px] justify-between items-center bg-white rounded-[12px]">
<div className="flex items-center">
{/* TODO profile img ENUM res 데이터로 교체 */}
<Image
src={returnProfileImg(profileEnum)}
src={returnProfileImg('GREEN')}
width={40}
height={40}
className="w-[40px] h-[40px] aspect-square"
alt="친구 프로필"
/>
<div className="ml-[16px]">
<p className="mb-[4px] heading4-semibold text-gray7">{name}</p>
<p className="mb-[4px] heading4-semibold text-gray7">
{data.nickname}
</p>
<p className="body2-medium text-gray5">
냉장고 식자재 목록 {count}개
냉장고 식자재 목록 {data.ingredientCount}개
</p>
</div>
</div>
<AngleIcon
width={16}
height={16}
fill="#CCCFD7"
transform="rotate(180)"
className="z-0"
/>
{possibleDelete ? (
<CheckBox onClick={onClick} active={active} />
) : (
<AngleIcon
width={16}
height={16}
fill="#CCCFD7"
transform="rotate(180)"
className="z-0"
/>
)}
</div>
);
};
Expand Down
71 changes: 53 additions & 18 deletions src/components/templates/AddFriendTemplate.tsx
Original file line number Diff line number Diff line change
@@ -1,47 +1,82 @@
import React, { useEffect, useState } from 'react';
import { LabelRoundBox, WarningLine } from '@/components/molecules';
import React, { useState } from 'react';
import {
useAddFriendship,
useGetMyInviteCode,
} from '@/hooks/queries/friendship';

import { BulletNoticeBox } from '../organisms';
import { LabelRoundBox } from '../molecules';
import { BulletNoticeBox } from '@/components/organisms';
import { MiniButton } from '@/components/atoms';
import useToast from '@/hooks/useToast';

const MY_INVITATION_CODE = 'AB12CD3EF';
const FRIEND_ADD_SUCCESS_MESSAGE = '친구 추가가 완료되었습니다.';
const CODE_COPY_SUCCESS_MESSAGE = '초대 코드가 복사되었습니다.';

const AddFriendTemplate: React.FC = () => {
const [myCode, setMyCode] = useState<string>('');
const [friendInviteCode, setFriendInviteCode] = useState<string>('');
const [warningVisible, setWarningVisible] = useState<boolean>(false);
const { showToast } = useToast();
const addFriendship = useAddFriendship({
onSuccess: () => {
showToast(FRIEND_ADD_SUCCESS_MESSAGE, 'success');
},
});

const onCopy: () => void = () => {
navigator.clipboard
.writeText(myCode)
.then(() => null)
.writeText(myInviteCode ?? '')
.then(() => {
showToast(CODE_COPY_SUCCESS_MESSAGE, 'success');
})
.catch(() => null);
};

useEffect(() => {
setMyCode(MY_INVITATION_CODE);
}, []);
const onAddFriend = () => {
if (friendInviteCode.length < 9) {
setWarningVisible(true);
} else {
addFriendship.mutate({ inviteCode: friendInviteCode });
}
};

const { inviteCode: myInviteCode } = useGetMyInviteCode();

return (
<div className="pt-[72px] px-[20px]">
<LabelRoundBox
label="내 초대 코드"
content={
<>
<div className="flex">
<span className="flex-1 outline-none mr-[10px] border-none p-[10px] bg-gray1 rounded-[6px] text-gray8 body1-medium">
{myCode}
{myInviteCode ?? ''}
</span>
<MiniButton label="복사" onClick={onCopy} variant="active" />
</>
</div>
}
/>
<LabelRoundBox
label="상대 초대 코드 입력"
content={
<>
<input
placeholder="상대 초대 코드를 입력해주세요."
className="flex-1 outline-none mr-[10px] border-none p-[10px] bg-gray1 rounded-[6px] text-gray8 body1-medium"
/>
<MiniButton label="추가" onClick={onCopy} variant="clickable" />
<div className="flex mb-[8px]">
<input
placeholder="상대 초대 코드를 입력해주세요."
className={`flex-1 mr-[10px] p-[10px] bg-gray1 rounded-[6px] text-gray8 body1-medium ${warningVisible ? 'border border-point4' : 'border-none'}`}
value={friendInviteCode}
onChange={(e) => {
setFriendInviteCode(e.target.value);
}}
maxLength={10}
/>
<MiniButton
label="추가"
onClick={onAddFriend}
variant="clickable"
/>
</div>
{warningVisible ? (
<WarningLine text="9-10자리 초대 코드를 입력해주세요." />
) : null}
</>
}
/>
Expand Down
Loading
Loading