Skip to content

Commit

Permalink
[feat] : 디엠 페이지 1차 구현 (#98)
Browse files Browse the repository at this point in the history
* feat/#86 : dm 타입 정리

* feat/#86 : 디엠 사이드바 목록 조회

* feat/#86 : dm 페이지 더미 데이터 추가

* feat/#86 : dm 페이지 모킹 API 연결
  • Loading branch information
yeona813 authored Feb 26, 2025
1 parent 44f8758 commit 573dec3
Show file tree
Hide file tree
Showing 15 changed files with 295 additions and 131 deletions.
2 changes: 1 addition & 1 deletion frontend/src/apis/channel/dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ interface Reaction {
users: string[];
}

interface Message {
export interface Message {
messageId: string;
user: User;
content: string;
Expand Down
18 changes: 11 additions & 7 deletions frontend/src/apis/dm/dto.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
interface DMParticipant {
userId: string;
username: string;
profileImage: string;
}
import { User } from '@/types/user';
import { Message } from '../channel/dto';

interface DMLastMessage {
senderId: string;
content: string;
createdAt: string;
}

export interface DMItem {
interface DMItem {
dmId: string;
participants: DMParticipant[];
participants: User[];
lastMessage: DMLastMessage;
unreadCount: number;
}
Expand All @@ -21,3 +18,10 @@ export interface DMListResponseDto {
userId: string;
dms: DMItem[];
}

export interface DMResponseDto {
dmId: string;
participants: User[];
isGroup: boolean;
messages: Record<string, Message[]>;
}
7 changes: 6 additions & 1 deletion frontend/src/apis/dm/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { DMListResponseDto } from '@/apis/dm/dto';
import { DMListResponseDto, DMResponseDto } from '@/apis/dm/dto';
import https from '@/lib/https';

export const getDMList = async (): Promise<DMListResponseDto> => {
const { data } = await https.get('/api/dms');
return data;
};

export const getDMMessage = async (dmId: string): Promise<DMResponseDto> => {
const { data } = await https.get(`api/dms/${dmId}`);
return data;
};
40 changes: 40 additions & 0 deletions frontend/src/components/dm/DMContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { useParams } from 'react-router';
import Message from '../common/Message';
import DMInfo from './DMInfo';
import { useGetDMMessages } from '@/hooks/dm/useGetDMMessages';
import DateBadge from '../common/DateBadge';

const DMContent = () => {
const { dmId } = useParams();
const { data, isLoading, isError } = useGetDMMessages(dmId!);

if (isLoading) return <p>로딩중입니다.</p>;
if (isError) return <p>에러</p>;

return (
<div>
{data && data?.participants.length > 0 && (
<DMInfo {...data.participants[0]} />
)}
{data?.messages ? (
Object.entries(data.messages).map(([date, messages]) => (
<div key={date}>
<DateBadge date={date} />
{messages.map(msg => (
<Message
key={msg.messageId}
user={msg.user}
content={msg.content}
createdAt={msg.createdAt}
/>
))}
</div>
))
) : (
<p>메시지 없음</p>
)}
</div>
);
};

export default DMContent;
50 changes: 26 additions & 24 deletions frontend/src/components/dm/sideBar/DMList.tsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,41 @@
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
import { User } from '@/types/user';
import { DMItem } from '@/types/dm';
import clsx from 'clsx';
import { useNavigate, useParams } from 'react-router';

interface DMListProps {
participants: User;
senderId: string;
content: string;
createdAt: string;
unreadCount: number;
}
//@TODO 단체 채팅일 경우 고려해야함

const DMList = ({
participants,
senderId,
content,
createdAt,
unreadCount,
}: DMListProps) => {
const date = new Date(createdAt);
const DMList = ({ dmId, participants, lastMessage, unreadCount }: DMItem) => {
const date = new Date(lastMessage.createdAt);
const { workspaceId, dmId: currentDMId } = useParams();
const navigate = useNavigate();

const month = date.getMonth() + 1;
const day = date.getDate();

return (
<div className="flex gap-2 w-full hover:bg-amber-300 py-4 px-4 border-b-white border-b">
<Avatar className="rounded-lg w-12 h-12">
<div
className={clsx(
'flex w-full gap-2 px-4 py-4 border-b hover:bg-amber-300 border-b-white',
String(currentDMId) === String(dmId) && 'bg-amber-300'
)}
onClick={() => {
navigate(`/workspace/${workspaceId}/dm/${dmId}`);
}}
>
<Avatar className="w-12 h-12 rounded-lg">
<AvatarImage
src={participants.profileImage}
src={participants[0].profileImage}
alt="유저의 프로필이미지"
/>
<AvatarFallback>{participants.username}</AvatarFallback>
<AvatarFallback>{participants[0].username}</AvatarFallback>
</Avatar>
<div className="flex flex-col w-full">
<div className="flex justify-between items-center">
<div className="flex items-center justify-between">
<span className="text-base font-bold text-white">
{participants.displayName}
{participants[0].displayName}
</span>
<div className="flex gap-2 items-center">
<div className="flex items-center gap-2">
<p className="text-sm text-white">{`${month}${day}일`}</p>
{unreadCount > 0 && (
<div className="rounded-full bg-amber-300 px-2 py-[1px] text-sm text-white">
Expand All @@ -45,7 +45,9 @@ const DMList = ({
</div>
</div>
<p className="text-sm text-white">
{participants.userId === senderId ? `나 : ${content}` : content}
{participants[0].userId === lastMessage.senderId
? `나 : ${lastMessage.content}`
: lastMessage.content}
</p>
</div>
</div>
Expand Down
44 changes: 10 additions & 34 deletions frontend/src/components/dm/sideBar/SideBar.tsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,20 @@
import { useParams } from 'react-router';
import DMList from './DMList';
import SideBarHeader from './SideBarHeader';

//여기는 나중에 API 연결하면 바뀔 부분!
const DM_DATA = [
{
participants: {
userId: '1',
username: '안연아',
displayName: '안연아(웹FE)',
profileImage: 'https://github.com/shadcn.png',
isActive: true,
},
senderId: '1',
content: '안녕하세요! 채팅 테스트입니다.',
createdAt: '2025-01-25T12:34:56Z',
unreadCount: 0,
},
{
participants: {
userId: '1',
username: '안연아',
displayName: '안연아(웹FE)',
profileImage: 'https://github.com/shadcn.png',
isActive: true,
},
senderId: '3',
content: '안녕하세요! 채팅 테스트입니다.',
createdAt: '2025-01-25T12:34:56Z',
unreadCount: 2,
},
];
import useGetDMListQuery from '@/hooks/dm/useGetDMListQuery';

const SideBar = () => {
const { workspaceId } = useParams();
const { data, isLoading, isError } = useGetDMListQuery(workspaceId!);

if (isLoading) return <p>로딩중입니다.</p>;
if (isError) return <p>로딩중입니다.</p>;

return (
<div className="flex flex-col h-screen bg-amber-200 gap-2">
<div className="flex flex-col h-screen gap-2 bg-amber-200">
<SideBarHeader />
<div className="flex flex-col">
{DM_DATA.map((dm, index) => (
<DMList key={index} {...dm} />
))}
{data?.dms.map((dm, index) => <DMList key={index} {...dm} />)}
</div>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const MainNavigationSidebar = () => {
const { channelList } = useWorkspaceChannelListQuery(workspaceId!);

return (
<div className=" min-w-16 bg-yellow-200 text-white flex py-3 flex-col items-center border-r-2">
<div className="flex flex-col items-center py-3 text-white bg-yellow-200 border-r-2 min-w-16">
{icons.map((item, index) => {
return (
<WorkspaceIconButton
Expand All @@ -27,7 +27,7 @@ const MainNavigationSidebar = () => {
}
}
if (item.type === 'DM') {
navigate(`/workspace/${workspaceId}/dm`);
navigate(`/workspace/${workspaceId}/dm/1`);
}
if (item.type === 'ETC') console.log('더보기 클릭');
}}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { DMItem } from '@/apis/dm/dto';
import { Button } from '@/components/ui/button';
import WorkspaceAccordionSection from '@/components/workspace/WorkspaceAccordionList';
import WorkspaceDirectMessageListItem from '@/components/workspace/WorkspaceChannelPanel/WorkspaceDirectMessageListItem';
import { DMItem } from '@/types/dm';
import { MdOutlineAddBox } from 'react-icons/md';

interface WorkspaceDMsProps {
Expand All @@ -16,7 +16,7 @@ const WorkspaceDMs = ({ dmList, onAddColleauge }: WorkspaceDMsProps) => {
<WorkspaceDirectMessageListItem dm={dm} key={index} />
))}
<Button
className=" w-full bg-transparent shadow-none hover:bg-gray-50 flex justify-start text-xs py-0"
className="flex justify-start w-full py-0 text-xs bg-transparent shadow-none hover:bg-gray-50"
onClick={onAddColleauge}
>
{<MdOutlineAddBox className="text-2xl" />}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,55 +1,37 @@
import { Button } from '@/components/ui/button';

interface DMParticipant {
userId: string;
username: string;
profileImage: string;
}

interface LastMessage {
senderId: string;
content: string;
createdAt: string;
}

interface DirectMessage {
dmId: string;
participants: DMParticipant[];
lastMessage: LastMessage;
unreadCount: number;
}
import { DMItem } from '@/types/dm';

interface WorkspaceDirectMessageListItemProps {
dm: DirectMessage;
dm: DMItem;
}

const WorkspaceDirectMessageListItem = ({
dm,
}: WorkspaceDirectMessageListItemProps) => {
return (
<Button className="w-full shadow-none flex items-center justify-start text-xs rounded-lg bg-transparent hover:bg-transparent">
<div className="relative w-6 h-6 flex-shrink-0 ">
<Button className="flex items-center justify-start w-full text-xs bg-transparent rounded-lg shadow-none hover:bg-transparent">
<div className="relative flex-shrink-0 w-6 h-6 ">
<img
src={dm.participants[0]?.profileImage}
className="w-5 h-5 rounded-full"
/>
{dm.participants.length > 1 ? (
<span className="absolute -bottom-1 -right-1 w-6 h-6 flex items-center justify-center text-xs text-white rounded-full">
<span className="absolute flex items-center justify-center w-6 h-6 text-xs text-white rounded-full -bottom-1 -right-1">
{dm.participants.length}
</span>
) : (
<div className="absolute -bottom-1 -right-0 w-3 h-3 flex items-center justify-center text-xs bg-green-400 text-white rounded-full"></div>
<div className="absolute flex items-center justify-center w-3 h-3 text-xs text-white bg-green-400 rounded-full -bottom-1 -right-0"></div>
)}
</div>

<div className="flex-1 min-w-0">
<span className="text-start block overflow-hidden whitespace-nowrap text-ellipsis truncate">
<span className="block overflow-hidden truncate text-start whitespace-nowrap text-ellipsis">
{dm.participants.map(participant => participant.username).join(', ')}
</span>
</div>

{dm.unreadCount > 0 && (
<div className="w-6 h-6 bg-purple-300 text-white flex items-center justify-center rounded-full">
<div className="flex items-center justify-center w-6 h-6 text-white bg-purple-300 rounded-full">
{dm.unreadCount}
</div>
)}
Expand Down
10 changes: 10 additions & 0 deletions frontend/src/hooks/dm/useGetDMMessages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { useQuery } from '@tanstack/react-query';
import { getDMMessage } from '@/apis/dm';

export const useGetDMMessages = (dmId: string) => {
return useQuery({
queryKey: ['dmMessage', dmId],
queryFn: () => getDMMessage(dmId),
enabled: !!dmId,
});
};
Loading

0 comments on commit 573dec3

Please sign in to comment.