-
Notifications
You must be signed in to change notification settings - Fork 10
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
base: main
Are you sure you want to change the base?
Changes from all commits
bede292
9f750f8
528658e
1a96c53
892adcc
e8a3dd9
d719e4a
2169f53
bca14b2
23134cd
b8028db
7ad56da
a211e94
2647c30
0df3ce4
66d3255
ba45274
214c9d7
3af5dbb
9372c70
6b5068b
bebc1c4
cf42c9c
60fcd57
71a5454
5037b1d
3e1ecdb
52b43bd
a3937f7
7bfa09c
f5c16d2
bc69303
94a8412
a91bc3d
48963d4
9f7ee1e
11bfe1e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
This file was deleted.
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); | ||
} | ||
}; | ||
|
||
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; |
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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
우선 trim을 통해 공백을 처리하신 점이 인상 깊습니다. 다만, 입력이 없으면 모든 includes 값이 true로 나와서 별도로 전체 보여주기를 처리할 필요가 없더라구요. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; |
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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
위와 동일한 내용입니다! |
||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||
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} | ||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; |
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
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; |
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); | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; |
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
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
App 컴포넌트에서 window 값을 계산한 뒤 recoil state에 저장하는군요.
미디어 쿼리 외에 상태값을 이용한 반응형 구현도 괜찮은 것 같아요!