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

[5주차] 주효정 미션 제출합니다. #12

Open
wants to merge 37 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
bede292
refactor: typescript 설정
jhj2713 May 2, 2022
9f750f8
style: 컨테이너 추가
jhj2713 May 2, 2022
528658e
feat: 메시지 입력 기능 추가
jhj2713 May 2, 2022
1a96c53
style: inputMessage 스타일 변경
jhj2713 May 2, 2022
892adcc
feat: 사용자 프로필 추가
jhj2713 May 2, 2022
e8a3dd9
feat: 메시지 채팅 추가
jhj2713 May 2, 2022
d719e4a
feat: 메시지 입력 기능
jhj2713 May 2, 2022
2169f53
feat: scrollToBottom 구현
jhj2713 May 2, 2022
bca14b2
feat: context 및 custom hook 추가
jhj2713 May 2, 2022
23134cd
feat: 이모티콘 popover 창 추가
jhj2713 May 2, 2022
b8028db
style: 프로필 추가 및 수정
jhj2713 May 2, 2022
7ad56da
refactor: 중복된 코드 정리 및 삭제
jhj2713 May 2, 2022
a211e94
refactor: userContext 및 일부 네이밍 수정
jhj2713 May 3, 2022
2647c30
chore: Popover 작동 이벤트 변경
jhj2713 May 3, 2022
0df3ce4
feat: alert 창 추가
jhj2713 May 3, 2022
66d3255
refactor: 폴더명 수정 및 일부 코드 리팩토링
jhj2713 May 3, 2022
ba45274
feat: resize context 및 hook 추가
jhj2713 May 5, 2022
214c9d7
chore: 필요없는 코드 삭제
jhj2713 May 5, 2022
3af5dbb
chore: title 수정
jhj2713 May 5, 2022
9372c70
refactor: interface 분리
jhj2713 May 6, 2022
6b5068b
refactor: 불필요한 렌더링 방지
jhj2713 May 6, 2022
bebc1c4
refactor: context 수정 및 recoil 추가
jhj2713 May 6, 2022
cf42c9c
refactor: 코드리뷰 반영
jhj2713 May 9, 2022
60fcd57
feat: router 추가
jhj2713 May 9, 2022
71a5454
feat: chat list 추가
jhj2713 May 9, 2022
5037b1d
feat: friend list 추가
jhj2713 May 9, 2022
3e1ecdb
feat: chatRoom으로 routing 설정
jhj2713 May 9, 2022
52b43bd
feat: setting 화면 추가
jhj2713 May 9, 2022
a3937f7
feat: routing 및 user toggle 구현
jhj2713 May 9, 2022
7bfa09c
feat: 검색 기능 추가
jhj2713 May 9, 2022
f5c16d2
chore: 단위 수정
jhj2713 May 9, 2022
bc69303
chore: 일부 스타일 수정
jhj2713 May 9, 2022
94a8412
chore: list height 수정
jhj2713 May 11, 2022
a91bc3d
feat: 단체 chat 추가
jhj2713 May 11, 2022
48963d4
chore: 불필요한 구문 삭제
jhj2713 May 11, 2022
9f7ee1e
refactor: footer 체크방법 및 filter 수정
jhj2713 May 12, 2022
11bfe1e
refactor: 필요없는 구문 삭제
jhj2713 May 12, 2022
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
383 changes: 374 additions & 9 deletions package-lock.json

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,19 @@
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.1.1",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.4.1",
"@types/node": "^17.0.31",
"@types/react": "^18.0.8",
"@types/react-dom": "^18.0.3",
"@types/styled-components": "^5.1.25",
"react": "^18.1.0",
"react-dom": "^18.1.0",
"react-icons": "^4.3.1",
"react-router-dom": "^6.3.0",
"react-scripts": "5.0.1",
"recoil": "^0.7.2",
"styled-components": "^5.3.5",
"typescript": "^4.6.4",
"web-vitals": "^2.1.4"
},
"scripts": {
Expand Down
Binary file added public/images/DAY6.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/backbutton.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/colde.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/damons year.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/ducky.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/ghost.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/ghost.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/joan.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/keshi.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/kitty.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/krkorklo.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/nell.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/send.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/taeng_ss.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 5 additions & 3 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>React App</title>
<link
href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;500;700&display=swap"
rel="stylesheet"
/>
<title>Messenger</title>
</head>
<body>
<div id="root"></div>
Expand Down
Binary file removed public/logo192.png
Binary file not shown.
Binary file removed public/logo512.png
Binary file not shown.
5 changes: 0 additions & 5 deletions src/App.js

This file was deleted.

65 changes: 65 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import styled, { css } from "styled-components";
import { useRecoilState } from "recoil";
import { resizeState } from "recoil/recoil";
import { useEffect } from "react";
import { Route, Routes } from "react-router-dom";
import { ChatList, ChatFriends, ChatRoom, Setting } from "pages";

const App = () => {
const [isMobile, setIsMobile] = useRecoilState(resizeState);

const _handleResize = () => {
if (window.innerWidth <= 640) {
setIsMobile(true);
} else {
setIsMobile(false);
}
};
Comment on lines +11 to +17

Choose a reason for hiding this comment

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

App 컴포넌트에서 window 값을 계산한 뒤 recoil state에 저장하는군요.
미디어 쿼리 외에 상태값을 이용한 반응형 구현도 괜찮은 것 같아요!


useEffect(() => {
if (window.innerWidth <= 640) {
setIsMobile(true);
}

window.addEventListener("resize", _handleResize);
return () => {
window.removeEventListener("resize", _handleResize);
};
}, []);

return (
<Wrapper>
<Container isMobile={isMobile}>
<Routes>
<Route path="/chatList" element={<ChatList />} />
<Route path="/chatRoom/:id" element={<ChatRoom />} />
<Route path="/" element={<ChatFriends />} />
<Route path="/setting" element={<Setting />} />
</Routes>
</Container>
</Wrapper>
);
};

const Wrapper = styled.div`
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
`;
const Container = styled.div<{ isMobile: boolean }>`
border: 0.05rem solid lightgrey;
${({ isMobile }) =>
isMobile
? css`
height: 100%;
width: 100%;
`
: css`
height: 640px;
width: 360px;
border-radius: 0.5rem;
`}
`;

export default App;
55 changes: 55 additions & 0 deletions src/components/chatFriends/ChatFriendsList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import styled from "styled-components";
import user from "data/user.json";
import List from "components/layout/List";
import Search from "components/common/Search";
import { useState } from "react";

const ChatList = () => {
const totalUser = user.slice(1);
const [showUser, setShowUser] = useState(totalUser);

const filterUser = (text: string): void => {
if (!text.trim()) {
setShowUser(totalUser);
} else {
const filtered = totalUser.filter((user) =>
user.name.toLowerCase().includes(text.trim().toLowerCase())
);
setShowUser(filtered);
}
};
Comment on lines +11 to +20

Choose a reason for hiding this comment

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

Suggested change
const filterUser = (text: string): void => {
if (!text.trim()) {
setShowUser(totalUser);
} else {
const filtered = totalUser.filter((user) =>
user.name.toLowerCase().includes(text.trim().toLowerCase())
);
setShowUser(filtered);
}
};
const filterUser = (text: string): void => {
const filtered = totalUser.filter((user) =>
user.name.toLowerCase().includes(text.trim().toLowerCase())
);
setShowUser(filtered);
};

우선 trim을 통해 공백을 처리하신 점이 인상 깊습니다. 다만, 입력이 없으면 모든 includes 값이 true로 나와서 별도로 전체 보여주기를 처리할 필요가 없더라구요.

Copy link
Member Author

Choose a reason for hiding this comment

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

오 그렇네요! 알려주셔서 감사합니다~!


return (
<Container>
<Search filter={filterUser} />
{showUser.map((user) => (
<List
key={user.id}
link={user.id}
img={user.name}
title={user.name}
subTitle={user.statusMessage}
/>
))}
</Container>
);
};

const Container = styled.section`
display: flex;
flex-direction: column;
height: 67%;
overflow: auto;
padding-top: 1rem;
::-webkit-scrollbar {
width: 0.9rem;
}
::-webkit-scrollbar-thumb {
background-color: rgba(0, 0, 0, 0.2);
border-radius: 1rem;
background-clip: padding-box;
border: 0.3rem solid transparent;
}
`;

export default ChatList;
66 changes: 66 additions & 0 deletions src/components/chatList/MessageChatList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import List from "components/layout/List";
import message from "data/message.json";
import styled from "styled-components";
import { useState } from "react";
import Search from "components/common/Search";
import { IUserType } from "interface";

const MessageChatList = () => {
const [showMessage, setShowMessage] = useState(message);

const filterMessage = (text: string): void => {
if (!text.trim()) {
setShowMessage(message);
} else {
const filtered = message.filter(
(msg) =>
msg.user.filter((user) =>
user.name.toLowerCase().includes(text.trim().toLowerCase())
).length !== 0
);
setShowMessage(filtered);
}
Comment on lines +12 to +22

Choose a reason for hiding this comment

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

Suggested change
if (!text.trim()) {
setShowMessage(message);
} else {
const filtered = message.filter(
(msg) =>
msg.user.filter((user) =>
user.name.toLowerCase().includes(text.trim().toLowerCase())
).length !== 0
);
setShowMessage(filtered);
}
const filtered = message.filter(
(msg) =>
msg.user.filter((user) =>
user.name.toLowerCase().includes(text.trim().toLowerCase())
).length !== 0
);

위와 동일한 내용입니다!

};
const _handleFriendsName = (friends: IUserType[]): string => {
return friends.length === 1
? friends[0].name
: friends.map((user) => user.name).join(", ");
};

return (
<Container>
<Search filter={filterMessage} />
{showMessage.map(
(msg) =>
msg.messages.length !== 0 && (
<List
key={msg.id}
link={msg.id}
img={msg.user[0].name}
title={_handleFriendsName(msg.user)}
subTitle={msg.messages[msg.messages.length - 1].text}

Choose a reason for hiding this comment

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

가장 최근 대화 내역을 채팅방의 서브 타이틀로 보여주도록 구현하신 방식 잘 참고하고 가겠습니다...!!

/>
)
)}
</Container>
);
};

const Container = styled.section`
display: flex;
flex-direction: column;
height: 67%;
overflow: auto;
padding-top: 1rem;
::-webkit-scrollbar {
width: 0.9rem;
}
::-webkit-scrollbar-thumb {
background-color: rgba(0, 0, 0, 0.2);
border-radius: 1rem;
background-clip: padding-box;
border: 0.3rem solid transparent;
}
`;

export default MessageChatList;
76 changes: 76 additions & 0 deletions src/components/common/Alert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import styled from "styled-components";
import { IAlert } from "interface";

const Alert = ({ setVisibleAlert, value }: IAlert) => {
const _handleClick = (): void => {
setVisibleAlert(false);
};

return (
<Wrapper>
<Background onClick={_handleClick} />
<Container>
<AlertButtonBox>
<AlertButton type="button" onClick={_handleClick}>
X
</AlertButton>
</AlertButtonBox>
<AlertContent>{value}</AlertContent>
</Container>
</Wrapper>
);
};

const Wrapper = styled.section`
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 100;
display: flex;
justify-content: center;
align-items: center;
animation: alert-show 0.2s;
@keyframes alert-show {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
`;
const Background = styled.div`
background: #00000020;
width: 100%;
height: 100%;
`;
const Container = styled.section`
position: absolute;
width: 25rem;
height: 9rem;
background: #ffffff;
border-radius: 0.4rem;
`;
const AlertContent = styled.section`
margin: 1.8rem;
font-size: 1.1rem;
`;
const AlertButtonBox = styled.section`
display: flex;
justify-content: flex-end;
margin: 1.5rem;
`;
Comment on lines +56 to +64

Choose a reason for hiding this comment

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

"메세지를 입력해 주세요" Alert Modal을 구현하신 방식도 배우고 갑니다...!

const AlertButton = styled.button`
border: none;
background: none;
font-size: 1.2rem;
cursor: pointer;
:hover {
opacity: 0.5;
transition: 0.15s;
}
`;

export default Alert;
50 changes: 50 additions & 0 deletions src/components/common/Search.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import styled from "styled-components";
import { ISearch } from "interface";
import { AiOutlineSearch } from "react-icons/ai";
import { useRecoilValue } from "recoil";
import { resizeState } from "recoil/recoil";
import useInput from "hooks/useInput";
import { useEffect } from "react";

const Search = ({ filter }: ISearch) => {
const { text, handleTextChange } = useInput("");
const isMobile = useRecoilValue(resizeState);

Choose a reason for hiding this comment

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

컴포넌트를 재사용 하는군요, 아주 멋집니다!

useEffect(() => {
filter(text);
}, [filter, text]);

return (
<Container>
<IconBox>
<AiOutlineSearch size={20} />
</IconBox>
<SearchInput
placeholder="Search..."
value={text}
onChange={handleTextChange}
isMobile={isMobile}
/>
</Container>
);
};

const Container = styled.section`
display: flex;
`;
const IconBox = styled.section`
margin: 0.3rem 0.5rem 0 1rem;
`;
const SearchInput = styled.input<{ isMobile: boolean }>`
border: 0.05rem solid lightgrey;
border-radius: 0.5rem;
padding: 0.5rem;
width: ${({ isMobile }) => (isMobile ? "85%" : "17rem")};
height: 1rem;
font-size: 0.7rem;
:focus {
outline: none;
}
`;

export default Search;
39 changes: 39 additions & 0 deletions src/components/layout/ChatFooter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import styled from "styled-components";
import { Link } from "react-router-dom";
import { AiOutlineUser, AiOutlineSetting } from "react-icons/ai";
import { BiMessage } from "react-icons/bi";

const ChatFooter = ({ path }: { path: string }) => {
return (
<Container>
<IconBox to="/" selected={path === "friends"}>
<AiOutlineUser size={22} />
</IconBox>
Comment on lines +9 to +11

Choose a reason for hiding this comment

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

Link 컴포넌트 안에 아이콘을 넣어서 사용하시는 군요. 저는 버튼 안에 navigate를 넣어서 사용했는데, 같은 기능에 대해서 서로 구현 방법이 다른 점이 인상깊습니다.

<IconBox to="/chatList" selected={path === "chatList"}>
<BiMessage size={22} />
</IconBox>
<IconBox to="/setting" selected={path === "setting"}>
<AiOutlineSetting size={22} />
</IconBox>
</Container>
);
};

const Container = styled.section`
border-top: 0.05rem solid lightgrey;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
`;
const IconBox = styled(Link)<{ selected: boolean }>`
display: flex;
justify-content: center;
margin: 1.3rem;
color: ${({ selected }) => (selected ? "#1986fc" : "#000000")};
cursor: pointer;
:hover {
opacity: 0.7;
transition: 0.15s;
}
`;

export default ChatFooter;
Loading