Skip to content
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
Binary file removed public/logo.png
Binary file not shown.
6 changes: 3 additions & 3 deletions src/service/feature/channel/api/channelAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,12 @@ export const getDMDetail = async (channelId: number): Promise<DMDetail> => {
return res.data.data;
};

export const getDMList = async (): Promise<DMList[]> => {
export const getDMList = async (): Promise<DMDetail[]> => {
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<DMDetail> => {
const res = await axios.post(`/channels/members`, { memberIds });
return res.data.data;
};
31 changes: 28 additions & 3 deletions src/service/feature/channel/hook/query/useChannelQuery.ts
Original file line number Diff line number Diff line change
@@ -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<ChannelResponse>({
Expand All @@ -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('문제가 발생했어요. 다시 시도해주세요.');
},
});
};
25 changes: 14 additions & 11 deletions src/view/components/common/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -263,20 +263,23 @@ Modal.Footer = ({

return (
<div className=' h-[48px] flex bg-[#2F3136] px-[0.75rem] justify-between items-center rounded-b-[4px]'>
<button
onClick={handleClose}
className='text-[#DCDDDE] text-[10px] px-[0.94rem] py-[0.5rem] rounded-[0.13275rem] hover:bg-[#404249]'
>
{backBtnText ? backBtnText : 'Back'}
</button>
<div className='flex'>
{onClose ? (
<button
onClick={handleSubmit}
className='bg-[#5865F2] text-white px-[0.94rem] py-[0.5rem] rounded-[0.13275rem] text-[10px] hover:bg-[#4752C4]'
onClick={handleClose}
className='text-[#DCDDDE] text-[10px] px-[0.94rem] py-[0.5rem] rounded-[0.13275rem] hover:bg-[#404249]'
>
{submitBtnText ? submitBtnText : 'Okay'}
{backBtnText ? backBtnText : 'Back'}
</button>
</div>
) : (
''
)}

<button
onClick={handleSubmit}
className={`bg-[#5865F2] text-white px-[0.94rem] py-[0.5rem] rounded-[0.13275rem] text-[10px] hover:bg-[#4752C4] ${!onClose && 'flex-1'}`}
>
{submitBtnText ? submitBtnText : 'Okay'}
</button>
</div>
);
};
Expand Down
2 changes: 1 addition & 1 deletion src/view/layout/profile/UserProfileBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const UserProfileBar = () => {
<Avatar
src={profile?.avatarUrl || undefined}
alt={profile?.nickname}
fallback='/logo.png'
fallback={require('@assets/img/logo/chatflow.png')}
size={32}
/>
<div>
Expand Down
13 changes: 2 additions & 11 deletions src/view/layout/sidebar/channel/DirectChannelSidebar.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<SidebarLayout>
<ChannelNavigation />
Expand All @@ -16,13 +13,7 @@ const DirectChannelSidebar = () => {
<p className='text-[13px] text-neutral-400 font-bold'>
DIRECT MESSAGES
</p>
<button
className='w-[18px] h-[18px]'
onClick={handlePlus}
type='button'
>
<Plus size={18} color='#a3a3a3' />
</button>
<CreateDMModal />
</div>
<DirectMessages />
</div>
Expand Down
103 changes: 103 additions & 0 deletions src/view/layout/sidebar/components/channel/CreateDMModal.tsx
Original file line number Diff line number Diff line change
@@ -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<string[]>([]);

// 내 모든 친구 불러오기
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 <div>error</div>;

return (
<Modal.Root>
<Modal.Trigger>
<button className='w-[18px] h-[18px]' type='button'>
<Plus size={18} color='#a3a3a3' />
</button>
</Modal.Trigger>
<Modal.Portal>
<Modal.Overlay />
<Modal.Content className='max-w-[442px] w-[442px]'>
<Modal.Header>
<Modal.Title
isCloseBtn
className='mx-6 mt-4 font-bold justify-between'
>
친구 선택하기
</Modal.Title>
</Modal.Header>
<Modal.Body>
<SearchFriends setKeyword={setKeyword} keyword={keyword} />
<div className='min-h-[60px] max-h-[645px]'>
{isLoading ? (
<SkeletonDMList className='w-full' size={4} />
) : (
searchData?.map((member) => (
<Item member={member} key={member.friendshipId}>
<button
onClick={() => handleToggle(member.friendshipInfo.id)}
className='w-[22px] h-[22px] flex items-center justify-center text-white'
>
{checkList.includes(member.friendshipInfo.id) ? (
<SquareCheckBig size={22} className='text-primary' />
) : (
<Square
size={22}
className='text-neutral-400 hover:text-primary'
/>
)}
</button>
</Item>
))
)}
</div>
</Modal.Body>
<Modal.Footer
onSubmit={handleSubmit}
submitBtnText='DM 생성'
></Modal.Footer>
</Modal.Content>
</Modal.Portal>
</Modal.Root>
);
};

export default CreateDMModal;
8 changes: 4 additions & 4 deletions src/view/layout/sidebar/components/channel/DirectMessages.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ const DirectMessages = () => {

return (
<div className='flex flex-col items-start gap-[2px]'>
{data?.map((channel) => (
{data?.map((item) => (
<DMUserCard
key={channel.name}
isActive={userId === channel.chatId}
channel={channel}
key={item.channel.name}
isActive={userId === item.channel.chatId}
data={item}
/>
))}
</div>
Expand Down
21 changes: 17 additions & 4 deletions src/view/layout/sidebar/components/channel/InviteFriendModal.tsx
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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
* 이미 맴버인 인원들 제외시키기.
Expand All @@ -46,8 +52,8 @@ const InviteFriendModal = ({
return (
<Modal.Root>
<Modal.Trigger>
<button
className="flex h-8 w-full items-center justify-center rounded text-sm font-medium bg-blurple hover:bg-[#4752C4] text-white transition-colors duration-200">초대하기
<button className='flex h-8 w-full items-center justify-center rounded text-sm font-medium bg-blurple hover:bg-[#4752C4] text-white transition-colors duration-200'>
초대하기
</button>
</Modal.Trigger>
<Modal.Portal>
Expand All @@ -65,7 +71,14 @@ const InviteFriendModal = ({
<SearchFriends setKeyword={setKeyword} keyword={keyword} />
<div className='min-h-[60px] max-h-[645px]'>
{searchData?.map((member) => (
<Item member={member} teamId={team.id} key={member.id} />
<Item member={member} key={member.friendshipId}>
<button
className='border border-[#43A25A] px-4 rounded-[8px] text-white hover:bg-[#43A25A]'
onClick={() => handleInvite(member.friendshipInfo.id)}
>
초대
</button>
</Item>
))}
</div>
</Modal.Body>
Expand Down
59 changes: 33 additions & 26 deletions src/view/layout/sidebar/components/channel/Item.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className='flex justify-between mx-4 my-2'>
<div className='flex items-center'>
<img
className='w-8 h-8 mr-[10px] rounded-full'
src={member.friendshipInfo.avatarUrl || '/logo.png'}
alt={member.friendshipInfo.name}
/>
<p className='text-[#b9bbbe]'>{member.friendshipInfo.name}</p>
<div className='flex h-[42px] rounded-[8px] cursor-pointer items-center w-full hover:bg-[#41424b] justify-between'>
<div className='flex'>
<div className='w-8 h-8 my-[5px] ml-2 block items-center justify-center relative mr-3'>
<div>
<img
className='rounded-full'
src={
member.friendshipInfo.avatarUrl ||
require('@assets/img/logo/chatflow.png')
}
alt={'logo'}
/>
<div
className={`absolute right-0 bottom-[-2px] w-[12px] h-[12px] border-2 border-[#2e3036] ${member.friendshipInfo.state === 'ONLINE' ? 'bg-lime-500' : 'bg-slate-500'} rounded-full`}
></div>
</div>
</div>
<div className='flex items-start flex-col justify-center overflow-hidden whitespace-nowrap'>
<p
className={`w-[151px] text-lg m-0 h-[18px] flex items-center text-neutral-400 hover:text-white `}
>
{member.friendshipInfo.name}
</p>
</div>
</div>
<button
className='border border-[#43A25A] px-4 rounded-[8px] text-white hover:bg-[#43A25A]'
onClick={handleInvite}
>
초대
</button>
<div className='mr-2'>{children}</div>
</div>
);
};
Expand Down
25 changes: 16 additions & 9 deletions src/view/layout/sidebar/components/skeletons/SkeletonDMList.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
const SkeletonDMList = () => {
const SkeletonDMList = ({
className,
size = 7,
}: {
className?: string;
size?: number;
}) => {
return (
<div className='flex flex-col items-start gap-[2px] w-[203px]'>
<div className='bg-chat-hover h-[42px] rounded-[8px] w-full animate-pulse'></div>
<div className='bg-chat-hover h-[42px] rounded-[8px] w-full animate-pulse'></div>
<div className='bg-chat-hover h-[42px] rounded-[8px] w-full animate-pulse'></div>
<div className='bg-chat-hover h-[42px] rounded-[8px] w-full animate-pulse'></div>
<div className='bg-chat-hover h-[42px] rounded-[8px] w-full animate-pulse'></div>
<div className='bg-chat-hover h-[42px] rounded-[8px] w-full animate-pulse'></div>
<div className='bg-chat-hover h-[42px] rounded-[8px] w-full animate-pulse'></div>
<div
className={`flex flex-col items-start gap-[2px] w-[203px] ${className}`}
>
{Array.from({ length: size }, (_, i) => (
<div
key={i}
className='bg-chat-hover h-[42px] rounded-[8px] w-full animate-pulse'
></div>
))}
</div>
);
};
Expand Down
Loading
Loading