diff --git a/public/logo.png b/public/logo.png deleted file mode 100644 index 5250af1..0000000 Binary files a/public/logo.png and /dev/null differ diff --git a/src/service/feature/channel/api/channelAPI.ts b/src/service/feature/channel/api/channelAPI.ts index d8eba6b..1f66821 100644 --- a/src/service/feature/channel/api/channelAPI.ts +++ b/src/service/feature/channel/api/channelAPI.ts @@ -78,12 +78,12 @@ export const getDMDetail = async (channelId: number): Promise => { return res.data.data; }; -export const getDMList = async (): Promise => { +export const getDMList = async (): Promise => { const res = await axios.get(`/channels/me`); return res.data.data; }; -export const createDM = async (memberIds: string[]) => { - const res = await axios.post(`/channels/members`, memberIds); +export const createDM = async (memberIds: string[]): Promise => { + const res = await axios.post(`/channels/members`, { memberIds }); return res.data.data; }; diff --git a/src/service/feature/channel/hook/query/useChannelQuery.ts b/src/service/feature/channel/hook/query/useChannelQuery.ts index 3b99034..6b16e62 100644 --- a/src/service/feature/channel/hook/query/useChannelQuery.ts +++ b/src/service/feature/channel/hook/query/useChannelQuery.ts @@ -1,6 +1,12 @@ -import { useQuery } from '@tanstack/react-query'; -import { getChannelList, getDMList } from '@service/feature/channel/api/channelAPI.ts'; -import {ChannelResponse} from '@service/feature/channel/types/channel.ts'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { + createDM, + getChannelList, + getDMList, +} from '@service/feature/channel/api/channelAPI.ts'; +import { ChannelResponse } from '@service/feature/channel/types/channel.ts'; +import { toast } from 'sonner'; +import { useNavigate } from 'react-router-dom'; export const useChannelListQuery = (serverId: string) => { return useQuery({ @@ -18,3 +24,22 @@ export const useDMListQuery = () => { staleTime: 1000 * 60 * 5, }); }; + +export const useCreateDM = () => { + const queryClient = useQueryClient(); + + const navigate = useNavigate(); + return useMutation({ + mutationFn: (memberIds: string[]) => createDM(memberIds), + onSuccess: (data) => { + console.log('DM 생성 응답 데이터: ', data); + navigate(`/channels/@me/${data.channel.chatId}`); + queryClient.invalidateQueries({ queryKey: ['DMList'] }); + toast.success('채팅방을 생성했습니다!'); + // TODO: 추후 기존에 생성된 방은 구분 + }, + onError: () => { + toast.error('문제가 발생했어요. 다시 시도해주세요.'); + }, + }); +}; diff --git a/src/view/components/common/Modal.tsx b/src/view/components/common/Modal.tsx index 2d7092a..94447a0 100644 --- a/src/view/components/common/Modal.tsx +++ b/src/view/components/common/Modal.tsx @@ -263,20 +263,23 @@ Modal.Footer = ({ return (
- -
+ {onClose ? ( -
+ ) : ( + '' + )} + +
); }; diff --git a/src/view/layout/profile/UserProfileBar.tsx b/src/view/layout/profile/UserProfileBar.tsx index 880144a..eac38ea 100644 --- a/src/view/layout/profile/UserProfileBar.tsx +++ b/src/view/layout/profile/UserProfileBar.tsx @@ -15,7 +15,7 @@ const UserProfileBar = () => {
diff --git a/src/view/layout/sidebar/channel/DirectChannelSidebar.tsx b/src/view/layout/sidebar/channel/DirectChannelSidebar.tsx index 83e8eae..e76793f 100644 --- a/src/view/layout/sidebar/channel/DirectChannelSidebar.tsx +++ b/src/view/layout/sidebar/channel/DirectChannelSidebar.tsx @@ -1,12 +1,9 @@ import { SidebarLayout } from '../components/channel/SidebarLayout.tsx'; import ChannelNavigation from '../components/channel/ChannelNavigation.tsx'; import DirectMessages from '../components/channel/DirectMessages.tsx'; -import { Plus } from 'lucide-react'; +import CreateDMModal from '../components/channel/CreateDMModal.tsx'; const DirectChannelSidebar = () => { - const handlePlus = () => { - console.log('plus 버튼 클릭'); - }; return ( @@ -16,13 +13,7 @@ const DirectChannelSidebar = () => {

DIRECT MESSAGES

- +
diff --git a/src/view/layout/sidebar/components/channel/CreateDMModal.tsx b/src/view/layout/sidebar/components/channel/CreateDMModal.tsx new file mode 100644 index 0000000..f7e9831 --- /dev/null +++ b/src/view/layout/sidebar/components/channel/CreateDMModal.tsx @@ -0,0 +1,103 @@ +import Modal from '@components/common/Modal'; +import SearchFriends from '@pages/Friends/components/SearchFriends'; +import { useGetAllFriends } from '@service/feature/friend/hook/useFriendQuery'; +import { FriendData } from '@service/feature/friend/types/friend'; +import { useEffect, useMemo, useState } from 'react'; +import Item from './Item'; +import { Plus, Square, SquareCheckBig } from 'lucide-react'; +import { useCreateDM } from '@service/feature/channel/hook/query/useChannelQuery'; +import { toast } from 'sonner'; +import SkeletonDMList from '../skeletons/SkeletonDMList'; + +const CreateDMModal = () => { + const [keyword, setKeyword] = useState(''); + const [memberList, setMemberList] = useState<[] | FriendData[]>(); + const [checkList, setCheckList] = useState([]); + + // 내 모든 친구 불러오기 + const { data, isLoading, error } = useGetAllFriends(); + + const { mutate } = useCreateDM(); + useEffect(() => { + setMemberList(data); + }, [data]); + + const searchData = useMemo(() => { + return memberList?.filter( + (friend) => + friend.friendshipInfo.name.includes(keyword) || + friend.friendshipInfo.nickname.includes(keyword), + ); + }, [memberList, keyword]); + + const handleToggle = (memberId: string) => { + setCheckList((prev) => + prev.includes(memberId) + ? prev.filter((item) => item !== memberId) + : [...prev, memberId], + ); + }; + + const handleSubmit = () => { + mutate(checkList); + setCheckList([]); + toast.success('채팅방을 생성중입니다...'); + }; + + if (error) return
error
; + + return ( + + + + + + + + + + 친구 선택하기 + + + + +
+ {isLoading ? ( + + ) : ( + searchData?.map((member) => ( + + + + )) + )} +
+
+ +
+
+
+ ); +}; + +export default CreateDMModal; diff --git a/src/view/layout/sidebar/components/channel/DirectMessages.tsx b/src/view/layout/sidebar/components/channel/DirectMessages.tsx index 7ca8a13..f21d330 100644 --- a/src/view/layout/sidebar/components/channel/DirectMessages.tsx +++ b/src/view/layout/sidebar/components/channel/DirectMessages.tsx @@ -14,11 +14,11 @@ const DirectMessages = () => { return (
- {data?.map((channel) => ( + {data?.map((item) => ( ))}
diff --git a/src/view/layout/sidebar/components/channel/InviteFriendModal.tsx b/src/view/layout/sidebar/components/channel/InviteFriendModal.tsx index b07af92..1ae1822 100644 --- a/src/view/layout/sidebar/components/channel/InviteFriendModal.tsx +++ b/src/view/layout/sidebar/components/channel/InviteFriendModal.tsx @@ -1,10 +1,10 @@ import Item from './Item'; -import { v4 as uuidv4 } from 'uuid'; import SearchFriends from '@pages/Friends/components/SearchFriends'; import { useEffect, useMemo, useState } from 'react'; import Modal from '@components/common/Modal'; import { useGetAllFriends } from '@service/feature/friend/hook/useFriendQuery'; import { FriendData } from '@service/feature/friend/types/friend'; +import { useInviteFriendMutation } from '@service/feature/team/hook/mutation/useTeamMemberMutation'; const InviteFriendModal = ({ team, @@ -22,6 +22,12 @@ const InviteFriendModal = ({ // 내 모든 친구 불러오기 const { data, isLoading, error } = useGetAllFriends(); + // 친구 초대 + const { mutate } = useInviteFriendMutation(); + + const handleInvite = (memberId: string) => { + mutate({ teamId: team.id, memberId: memberId }); + }; /** * TODO * 이미 맴버인 인원들 제외시키기. @@ -46,8 +52,8 @@ const InviteFriendModal = ({ return ( - @@ -65,7 +71,14 @@ const InviteFriendModal = ({
{searchData?.map((member) => ( - + + + ))}
diff --git a/src/view/layout/sidebar/components/channel/Item.tsx b/src/view/layout/sidebar/components/channel/Item.tsx index 9ef7c6e..a7591cc 100644 --- a/src/view/layout/sidebar/components/channel/Item.tsx +++ b/src/view/layout/sidebar/components/channel/Item.tsx @@ -1,33 +1,40 @@ import { FriendData } from '@service/feature/friend/types/friend'; -import { useInviteFriendMutation } from '@service/feature/team/hook/mutation/useTeamMemberMutation'; +import { ReactNode } from 'react'; -const Item = ({ teamId, member }: { teamId: string; member: FriendData }) => { - // 친구 초대 - const { mutate } = useInviteFriendMutation(); - - const handleInvite = () => { - mutate({ teamId, memberId: String(member.friendshipInfo.id) }); - }; - - /** - * 기본 이미지 넣기 - */ +const Item = ({ + member, + children, +}: { + member: FriendData; + children: ReactNode; +}) => { return ( -
-
- {member.friendshipInfo.name} -

{member.friendshipInfo.name}

+
+
+
+
+ {'logo'} +
+
+
+
+

+ {member.friendshipInfo.name} +

+
- +
{children}
); }; diff --git a/src/view/layout/sidebar/components/skeletons/SkeletonDMList.tsx b/src/view/layout/sidebar/components/skeletons/SkeletonDMList.tsx index f3e7bb1..f6efc0b 100644 --- a/src/view/layout/sidebar/components/skeletons/SkeletonDMList.tsx +++ b/src/view/layout/sidebar/components/skeletons/SkeletonDMList.tsx @@ -1,13 +1,20 @@ -const SkeletonDMList = () => { +const SkeletonDMList = ({ + className, + size = 7, +}: { + className?: string; + size?: number; +}) => { return ( -
-
-
-
-
-
-
-
+
+ {Array.from({ length: size }, (_, i) => ( +
+ ))}
); }; diff --git a/src/view/layout/sidebar/components/team/ChatServer.tsx b/src/view/layout/sidebar/components/team/ChatServer.tsx index 904103e..bde3149 100644 --- a/src/view/layout/sidebar/components/team/ChatServer.tsx +++ b/src/view/layout/sidebar/components/team/ChatServer.tsx @@ -25,7 +25,11 @@ export default function ChatServer({ ) : ( // TODO: 추후 개인 프로필 사진으로 교체 !server && ( - {'다이렉트 + {'다이렉트 ) )} {server?.iconUrl ? ( diff --git a/src/view/pages/Friends/components/DMUserCard.tsx b/src/view/pages/Friends/components/DMUserCard.tsx index 0f29d8c..87ebb60 100644 --- a/src/view/pages/Friends/components/DMUserCard.tsx +++ b/src/view/pages/Friends/components/DMUserCard.tsx @@ -1,36 +1,28 @@ import { useNavigate } from 'react-router-dom'; -import { DMList } from '@service/feature/channel/types/channel.ts'; +import { DMDetail, DMList } from '@service/feature/channel/types/channel.ts'; +import { useSelector } from 'react-redux'; +import { RootState } from 'src/app/store'; interface UserCardProps { isActive?: boolean; className?: string; - channel: DMList; + data: DMDetail; } -const DMUserCard = ({ channel, isActive, className }: UserCardProps) => { +const DMUserCard = ({ data, isActive, className }: UserCardProps) => { const navigation = useNavigate(); - - // 만약 맴버가 여러명이면 맴버 순서대로 2명 프로필 노출 - // const user = channel.channelMembers; - - // 백엔드 개발 완료되기 전 임시 데이터 - const user = [ - { id: 0, state: 'ONLINE' }, - // { id: 1, state: 'OFFLINE' }, - ]; - - const memberSize = user.length; + const user = useSelector((state: RootState) => state.auth.user); + const users = data.channelMembers.filter((item) => item.id != user?.userId); + const memberSize = users.length; const handleClick = () => { - navigation(`/channels/@me/${channel.chatId}`); + navigation(`/channels/@me/${data.channel.chatId}`); }; return (
@@ -40,44 +32,46 @@ const DMUserCard = ({ channel, isActive, className }: UserCardProps) => {
{channel.name}
) : (
{channel.name}
{channel.name}
)}
-
-

- {channel.name} +

+

+ {data.channel.name}

{memberSize !== 1 ? (
-

+

맴버 {memberSize}명

diff --git a/src/view/pages/Friends/components/FriendCard.tsx b/src/view/pages/Friends/components/FriendCard.tsx index 7b95771..aed322d 100644 --- a/src/view/pages/Friends/components/FriendCard.tsx +++ b/src/view/pages/Friends/components/FriendCard.tsx @@ -6,7 +6,7 @@ import { } from '@service/feature/friend/hook/useFriendQuery'; import MoreMenu from './MoreMenu'; import { ChannelMember } from '@service/feature/channel/types/channel'; -import { useNavigate } from 'react-router-dom'; +import { useCreateDM } from '@service/feature/channel/hook/query/useChannelQuery'; const FriendCard = ({ user, @@ -22,14 +22,13 @@ const FriendCard = ({ openMenuId?: number | null; setOpenMenuId?: React.Dispatch>; }) => { - const navigation = useNavigate(); - const { id, name, state, avatarUrl } = user; + const { mutate } = useCreateDM(); // TODO: 추후 chatId 받아오는 방식 고려. // 현재는 DM 채널과 유저를 매칭시킬 수 있는 방법이 없음. const handleClick = () => { - navigation(`/channels/@me/${id}`); + mutate([id]); }; // 친구 요청 취소 @@ -51,7 +50,7 @@ const FriendCard = ({
{user.name}
{ return (
-
+