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 9 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;

2 changes: 1 addition & 1 deletion src/assets/icons/NotificationIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';

const NotificationIcon: React.FC<React.SVGProps<SVGSVGElement>> = (props) => (
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" {...props}>
<g clip-path="url(#clip0_1100_16324)">
<g clipPath="url(#clip0_1100_16324)">
<path
fillRule="evenodd"
clipRule="evenodd"
Expand Down
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
21 changes: 21 additions & 0 deletions src/components/atoms/CheckBox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
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';
return active ? (
<button onClick={onClick} className={`${commonStyle} bg-primary2`}>
<CheckIcon />
</button>
) : (
<button onClick={onClick} className={`${commonStyle} border border-gray5`}>
<CheckIcon stroke="#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 @@ -16,3 +16,4 @@ export { default as RadioButtonField } from './RadioButtonField';
export { default as Input } from './Input';
export { default as MiniButton } from './MiniButton';
export { default as ExclamationAlertSpan } from './ExclamationAlertSpan';
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
72 changes: 53 additions & 19 deletions src/components/templates/AddFriendTemplate.tsx
Original file line number Diff line number Diff line change
@@ -1,47 +1,81 @@
import React, { useEffect, useState } from 'react';
import { LabelRoundBox, WarningLine } from '@/components/molecules';
import React, { useState } from 'react';

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

const MY_INVITATION_CODE = 'AB12CD3EF';
import { queryKeys } from '@/hooks/queries/queryKeys';
import { useAddFriendship } from '@/hooks/queries/friendship';
import { useBaseQuery } from '@/hooks/queries/useBaseQuery';
import useToast from '@/hooks/useToast';

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('친구 추가가 완료되었습니다.', 'success');
},
Copy link
Member

Choose a reason for hiding this comment

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

constants들로 매직스트링들은 관리하는게 좋아보입니당

Copy link
Member Author

Choose a reason for hiding this comment

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

수정 완료 했습니다 😌

});

const onCopy: () => void = () => {
navigator.clipboard
.writeText(myCode)
.then(() => null)
.writeText(myInviteCode?.data?.inviteCode ?? '')
.then(() => {
showToast('초대 코드가 복사되었습니다.', 'success');
})
.catch(() => null);
};

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

const { data: myInviteCode } = useBaseQuery<{ inviteCode: string }>(
queryKeys.MY_INVITE_CODE(),
Copy link
Member

Choose a reason for hiding this comment

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

hook/query 레벨로 이동해야될거같숩니당

Copy link
Member Author

Choose a reason for hiding this comment

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

피드백 감사합니다😊 수정해서 반영해두었습니다 👍🏻

'/users/me/invite-code',
);

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?.data?.inviteCode}
</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