From a21e9afbfd86173d40715602a0b00f2224e174ad Mon Sep 17 00:00:00 2001 From: rockpell Date: Sat, 19 Dec 2020 15:36:09 +0900 Subject: [PATCH 01/12] Feat: add logout button to WorkspacePage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 로그아웃 버튼 워크스페이스 페이지에 추가가 --- frontend/src/api/user.js | 6 +++ .../container/GlobalHeader/GlobalHeader.js | 42 +++++++++++++++++++ .../GlobalHeader/GlobalHeader.stories.js | 11 +++++ frontend/src/container/GlobalHeader/index.js | 1 + .../src/page/WorkspacePage/WorkspacePage.js | 10 ++--- 5 files changed, 63 insertions(+), 7 deletions(-) create mode 100644 frontend/src/api/user.js create mode 100644 frontend/src/container/GlobalHeader/GlobalHeader.js create mode 100644 frontend/src/container/GlobalHeader/GlobalHeader.stories.js create mode 100644 frontend/src/container/GlobalHeader/index.js diff --git a/frontend/src/api/user.js b/frontend/src/api/user.js new file mode 100644 index 00000000..2aa81205 --- /dev/null +++ b/frontend/src/api/user.js @@ -0,0 +1,6 @@ +import Request from '../util/request' + +export const signOut = async () => { + const data = await Request.DELETE('/api/user/sign-out') + return data +} diff --git a/frontend/src/container/GlobalHeader/GlobalHeader.js b/frontend/src/container/GlobalHeader/GlobalHeader.js new file mode 100644 index 00000000..e3450aa2 --- /dev/null +++ b/frontend/src/container/GlobalHeader/GlobalHeader.js @@ -0,0 +1,42 @@ +import React, { useRef } from 'react' +import { useHistory } from 'react-router' +import styled from 'styled-components' +import { COLOR } from '../../constant/style' +import Button from '../../presenter/Button' +import { signOut } from '../../api/user' + +function GlobalHeader() { + const history = useHistory() + const isSignout = useRef(false) + + const signOutHandle = async () => { + if (!isSignout.current) { + isSignout.current = true + await signOut() + history.push('/login') + } + } + + return ( + + + + + + ) +} + +export default GlobalHeader + +const StyledDiv = styled.div` + width: 100%; + height: 40px; + background: ${COLOR.GLOBAL_HEADER_BACKGROUND}; +` + +const ButtonDiv = styled.div` + float: right; + margin-right: 20px; +` diff --git a/frontend/src/container/GlobalHeader/GlobalHeader.stories.js b/frontend/src/container/GlobalHeader/GlobalHeader.stories.js new file mode 100644 index 00000000..7b0c7c1e --- /dev/null +++ b/frontend/src/container/GlobalHeader/GlobalHeader.stories.js @@ -0,0 +1,11 @@ +import React from 'react' +import GlobalHeader from './GlobalHeader' +import { storiesOf } from '@storybook/react' + +const stories = storiesOf('Organism/GlobalHeader', module) + +const TestComponent = () => { + return +} + +stories.add('GlobalHeader', () => ) diff --git a/frontend/src/container/GlobalHeader/index.js b/frontend/src/container/GlobalHeader/index.js new file mode 100644 index 00000000..03c59c34 --- /dev/null +++ b/frontend/src/container/GlobalHeader/index.js @@ -0,0 +1 @@ +export { default } from './GlobalHeader.js' diff --git a/frontend/src/page/WorkspacePage/WorkspacePage.js b/frontend/src/page/WorkspacePage/WorkspacePage.js index d77b4792..cb113a10 100644 --- a/frontend/src/page/WorkspacePage/WorkspacePage.js +++ b/frontend/src/page/WorkspacePage/WorkspacePage.js @@ -14,6 +14,7 @@ import { TOOLS } from '../../constant/icon' import useWorkspace from '../../hooks/useWorkspace' import useSocket from '../../hooks/useSocket' import DraggableBoundaryLine from '../../presenter/DraggableBoundaryLine' +import GlobalHeader from '../../container/GlobalHeader' function WorkspacePage() { const { channelId } = useParams() @@ -50,7 +51,7 @@ function WorkspacePage() { return ( {modal} - 글로벌 헤더 위치 + @@ -93,12 +94,7 @@ const PageStyle = styled.div` display: flex; flex-direction: column; ` -const GlobalHeader = styled.div` - display: flex; - width: 100%; - height: 40px; - background: ${COLOR.GLOBAL_HEADER_BACKGROUND}; -` + const MainArea = styled.div` display: flex; flex-direction: row; From 59a9cd2c98af5fc6982bca9019221f7e68b0ba51 Mon Sep 17 00:00:00 2001 From: rockpell Date: Sat, 19 Dec 2020 18:24:43 +0900 Subject: [PATCH 02/12] Chore: react-copy-to-clipboard package --- frontend/package-lock.json | 13 ++++++++++--- frontend/package.json | 1 + 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 684f2990..e5a5c02d 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -7412,7 +7412,6 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz", "integrity": "sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw==", - "dev": true, "requires": { "toggle-selection": "^1.0.6" } @@ -16855,6 +16854,15 @@ "tinycolor2": "^1.4.1" } }, + "react-copy-to-clipboard": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.0.2.tgz", + "integrity": "sha512-/2t5mLMMPuN5GmdXo6TebFa8IoFxZ+KTDDqYhcDm0PhkgEzSxVvIX26G20s1EB02A4h2UZgwtfymZ3lGJm0OLg==", + "requires": { + "copy-to-clipboard": "^3", + "prop-types": "^15.5.8" + } + }, "react-dev-utils": { "version": "11.0.1", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.1.tgz", @@ -20142,8 +20150,7 @@ "toggle-selection": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", - "integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI=", - "dev": true + "integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI=" }, "toidentifier": { "version": "1.0.0", diff --git a/frontend/package.json b/frontend/package.json index 09ecdd78..5d1016ea 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -22,6 +22,7 @@ "immer": "^8.0.0", "qs": "^6.9.4", "react": "^17.0.1", + "react-copy-to-clipboard": "^5.0.2", "react-dom": "^17.0.1", "react-router-dom": "^5.2.0", "react-scripts": "4.0.0", From 553318a0c1276996e6ee7d8fa19c61bee5709e37 Mon Sep 17 00:00:00 2001 From: rockpell Date: Sat, 19 Dec 2020 18:25:16 +0900 Subject: [PATCH 03/12] Feat: Button onClick MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit handleClick이 없다면 onClick을 사용하도록 수정 --- frontend/src/presenter/Button/Button.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/src/presenter/Button/Button.js b/frontend/src/presenter/Button/Button.js index 487289e9..a29d85b1 100644 --- a/frontend/src/presenter/Button/Button.js +++ b/frontend/src/presenter/Button/Button.js @@ -7,10 +7,11 @@ const Button = ({ type = 'default', disabled = false, size = 'default', + onClick, }) => { return ( Date: Sat, 19 Dec 2020 18:29:16 +0900 Subject: [PATCH 04/12] Feat: copy to clipboard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 워크스페이스 초대 모달에 copy to clipboard 버튼 추가 --- .../InviteWorkspaceModal.js | 54 ++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/frontend/src/container/Modal/InviteWorkspaceModal/InviteWorkspaceModal.js b/frontend/src/container/Modal/InviteWorkspaceModal/InviteWorkspaceModal.js index 9fc28ba5..74f51a18 100644 --- a/frontend/src/container/Modal/InviteWorkspaceModal/InviteWorkspaceModal.js +++ b/frontend/src/container/Modal/InviteWorkspaceModal/InviteWorkspaceModal.js @@ -8,6 +8,7 @@ import { CLOSE } from '../../../constant/icon' import { COLOR } from '../../../constant/style' import { inviteWorkspace } from '../../../api/workspace' import { useParams } from 'react-router-dom' +import { CopyToClipboard } from 'react-copy-to-clipboard' const baseURL = process.env.NODE_ENV === 'development' @@ -16,6 +17,8 @@ const baseURL = const InviteWorkspaceModal = ({ handleClose }) => { const [inviteURL, setInviteURL] = useState('생성중...') + const [isHover, setIsHover] = useState(false) + const [isCopy, setIsCopy] = useState(false) const { workspaceId } = useParams() useEffect(() => { @@ -26,6 +29,11 @@ const InviteWorkspaceModal = ({ handleClose }) => { })() }, []) + const hoverLeave = () => { + setIsHover(false) + setIsCopy(false) + } + return ( <> @@ -37,6 +45,20 @@ const InviteWorkspaceModal = ({ handleClose }) => { + { + setIsCopy(true) + }} + > + setIsHover(true)} + onMouseLeave={hoverLeave} + > + {isHover && isCopy && Copied!!!} + + + @@ -52,7 +74,6 @@ const StyledModalContent = styled.div` padding: 15px; text-align: center; ` - const StyeldTextarea = styled.textarea` display: inline; width: 95%; @@ -60,4 +81,35 @@ const StyeldTextarea = styled.textarea` background: ${COLOR.BACKGROUNT_MODAL_GRAY}; ` +const CopyDiv = styled.div` + position: relative; + display: inline-block; +` + +const CopySpan = styled.span` + width: 140px; + background-color: #555; + color: #fff; + text-align: center; + border-radius: 6px; + padding: 5px; + position: absolute; + z-index: 1; + bottom: 120%; + left: 50%; + margin-left: -75px; + opacity: 1; + transition: opacity 0.3s; + ::after { + content: ''; + position: absolute; + top: 100%; + left: 50%; + margin-left: -5px; + border-width: 5px; + border-style: solid; + border-color: #555 transparent transparent; + } +` + export default InviteWorkspaceModal From a80793687513a26f0104b9691a0de59b6b4e169b Mon Sep 17 00:00:00 2001 From: jongwon Date: Sat, 19 Dec 2020 21:51:47 +0900 Subject: [PATCH 05/12] Refactor: change socket event name to constant --- backend/chatServer.js | 23 +++++++++++-------- backend/util/constant.js | 9 ++++++++ frontend/src/constant/index.js | 9 ++++++++ .../src/container/ChatMessage/ChatMessage.js | 6 ++--- frontend/src/container/ChatRoom/ChatRoom.js | 11 +++++---- .../InviteUserToChannelModal.js | 6 ++--- .../container/SideThreadBar/SideThreadBar.js | 12 +++++----- frontend/src/hooks/useSocket.js | 12 +++++----- 8 files changed, 55 insertions(+), 33 deletions(-) create mode 100644 frontend/src/constant/index.js diff --git a/backend/chatServer.js b/backend/chatServer.js index 0f59b4bd..f59ac4f6 100644 --- a/backend/chatServer.js +++ b/backend/chatServer.js @@ -4,6 +4,7 @@ import { createServer } from 'http' import createChatServer from 'socket.io' import { createChatMessage, createReplyMessage } from './service/chat' import { addReaction, removeReaction } from './service/reaction' +import { SOCKET_EVENT } from './util/constant' dotenv() const server = createServer(express()) @@ -20,14 +21,16 @@ namespace.use((socket, next) => { namespace.on('connection', socket => { const { workspaceUserInfoId } = socket.handshake.query socket.join(workspaceUserInfoId) - socket.on('invite channel', ({ channelId, origin, newMember }) => { + socket.on(SOCKET_EVENT.INVITE_CHANNEL, ({ channelId, origin, newMember }) => { origin .concat(newMember) .forEach(member => - namespace.in(member).emit('invited channel', { channelId, newMember }), + namespace + .in(member) + .emit(SOCKET_EVENT.INVITED_CHANNEL, { channelId, newMember }), ) }) - socket.on('new message', async data => { + socket.on(SOCKET_EVENT.NEW_MESSAGE, async data => { const { contents, channelId, file } = data const { data: result } = await createChatMessage({ creator: workspaceUserInfoId, @@ -35,7 +38,7 @@ namespace.on('connection', socket => { contents, file, }) - namespace.in(channelId).emit('new message', { + namespace.in(channelId).emit(SOCKET_EVENT.NEW_MESSAGE, { message: { ...data, _id: result._id, @@ -44,7 +47,7 @@ namespace.on('connection', socket => { }, }) }) - socket.on('new reply', async data => { + socket.on(SOCKET_EVENT.NEW_REPLY, async data => { const { contents, channelId, parentId, file } = data const { data: result } = await createReplyMessage({ creator: workspaceUserInfoId, @@ -53,7 +56,7 @@ namespace.on('connection', socket => { parentId, file, }) - namespace.in(channelId).emit('new reply', { + namespace.in(channelId).emit(SOCKET_EVENT.NEW_REPLY, { message: { ...data, _id: result._id, @@ -63,7 +66,7 @@ namespace.on('connection', socket => { }, }) }) - socket.on('update reaction', async data => { + socket.on(SOCKET_EVENT.UPDAETE_REACTION, async data => { const { emoji, chatId, userInfo, channelId, type, parentId } = data //1 = add, 0 = remove const result = @@ -79,7 +82,7 @@ namespace.on('connection', socket => { emoticon: emoji, }) - namespace.in(channelId).emit('update reaction', { + namespace.in(channelId).emit(SOCKET_EVENT.UPDAETE_REACTION, { reaction: { chatId: chatId, emoji: emoji, @@ -91,10 +94,10 @@ namespace.on('connection', socket => { }) }) - socket.on('join-room', (channelList = []) => { + socket.on(SOCKET_EVENT.JOIN_ROOM, (channelList = []) => { socket.join(channelList) }) - socket.on('leave-room', roomId => { + socket.on(SOCKET_EVENT.LEAVE_ROOM, roomId => { socket.leave(roomId) }) }) diff --git a/backend/util/constant.js b/backend/util/constant.js index 62bfca9b..4abad82c 100644 --- a/backend/util/constant.js +++ b/backend/util/constant.js @@ -1 +1,10 @@ export const MAX_CHAT_MESSAGE = 20 +export const SOCKET_EVENT = { + INVITE_CHANNEL: 'invite channel', + INVITED_CHANNEL: 'invited channel', + NEW_MESSAGE: 'new message', + NEW_REPLY: 'new reply', + UPDAETE_REACTION: 'update reaction', + JOIN_ROOM: 'join room', + LEAVE_ROOM: 'leave room', +} diff --git a/frontend/src/constant/index.js b/frontend/src/constant/index.js new file mode 100644 index 00000000..e1ce9edc --- /dev/null +++ b/frontend/src/constant/index.js @@ -0,0 +1,9 @@ +export const SOCKET_EVENT = { + INVITE_CHANNEL: 'invite channel', + INVITED_CHANNEL: 'invited channel', + NEW_MESSAGE: 'new message', + NEW_REPLY: 'new reply', + UPDAETE_REACTION: 'update reaction', + JOIN_ROOM: 'join room', + LEAVE_ROOM: 'leave room', +} diff --git a/frontend/src/container/ChatMessage/ChatMessage.js b/frontend/src/container/ChatMessage/ChatMessage.js index 1ecbc7b3..7f8f4569 100644 --- a/frontend/src/container/ChatMessage/ChatMessage.js +++ b/frontend/src/container/ChatMessage/ChatMessage.js @@ -7,11 +7,11 @@ import ChatContent from '../../presenter/ChatContent' import ThreadReactionList from '../../presenter/ThreadReactionList' import ActionBar from '../ActionBar' import ViewThreadButton from '../../presenter/Button/ViewThreadButton' -import { isEmpty, isImage } from '../../util' +import { isEmpty } from '../../util' import { SIZE, COLOR } from '../../constant/style' import { workspaceRecoil, socketRecoil } from '../../store' import FilePreview from '../FilePreview' - +import { SOCKET_EVENT } from '../../constant' const ChatMessage = forwardRef( ( { @@ -45,7 +45,7 @@ const ChatMessage = forwardRef( displayName: workspaceUserInfo.displayName, }, } - socket.emit('update reaction', reaction) + socket.emit(SOCKET_EVENT.UPDAETE_REACTION, reaction) } const updateReactionHandler = emoji => { diff --git a/frontend/src/container/ChatRoom/ChatRoom.js b/frontend/src/container/ChatRoom/ChatRoom.js index 27bf0b82..c0bed059 100644 --- a/frontend/src/container/ChatRoom/ChatRoom.js +++ b/frontend/src/container/ChatRoom/ChatRoom.js @@ -13,6 +13,7 @@ import { hasMyReaction, chageReactionState } from '../../util/reactionUpdate' import useChannelInfo from '../../hooks/useChannelInfo' import Icon from '../../presenter/Icon' import { ArrowDown } from '../../constant/icon' +import { SOCKET_EVENT } from '../../constant' const ChatRoom = ({ width }) => { const viewport = useRef(null) @@ -74,7 +75,7 @@ const ChatRoom = ({ width }) => { profileUrl: workspaceUserInfo.profileUrl, }, } - socket.emit('new message', chat) + socket.emit(SOCKET_EVENT.NEW_MESSAGE, chat) } useEffect(() => { @@ -83,7 +84,7 @@ const ChatRoom = ({ width }) => { useEffect(() => { if (socket) { - socket.on('new message', ({ message }) => { + socket.on(SOCKET_EVENT.NEW_MESSAGE, ({ message }) => { if (message.channelId === channelId) { setMessages(messages => [ ...messages, @@ -104,14 +105,14 @@ const ChatRoom = ({ width }) => { if (message.userInfo._id === workspaceUserInfo._id) scrollTo() }) - socket.on('update reaction', ({ reaction }) => { + socket.on(SOCKET_EVENT.UPDAETE_REACTION, ({ reaction }) => { setMessages(messages => chageReactionState(messages, reaction)) }) } return () => { if (socket) { - socket.off('new message') - socket.off('update reaction') + socket.off(SOCKET_EVENT.NEW_MESSAGE) + socket.off(SOCKET_EVENT.UPDAETE_REACTION) } } }, [socket, channelId, document.hidden, params]) diff --git a/frontend/src/container/Modal/InviteUserToChannelModal/InviteUserToChannelModal.js b/frontend/src/container/Modal/InviteUserToChannelModal/InviteUserToChannelModal.js index cc2d62e9..b28cefdd 100644 --- a/frontend/src/container/Modal/InviteUserToChannelModal/InviteUserToChannelModal.js +++ b/frontend/src/container/Modal/InviteUserToChannelModal/InviteUserToChannelModal.js @@ -18,7 +18,7 @@ import { workspaceRecoil } from '../../../store' import { createChannel, findChannelIdByName } from '../../../api/channel' import { getWorkspaceUserInfoByInfoId } from '../../../api/workspace' import useChannelList from '../../../hooks/useChannelList' - +import { SOCKET_EVENT } from '../../../constant' function InviteUserToChannelModal({ handleClose, type = 'channel' }) { const [channelInfo] = useChannelInfo() const setModal = useSetRecoilState(modalRecoil) @@ -48,7 +48,7 @@ function InviteUserToChannelModal({ handleClose, type = 'channel' }) { }) if (data.success) { - socket.emit('invite channel', { + socket.emit(SOCKET_EVENT.INVITE_CHANNEL, { channelId: channelInfo.channelId._id, origin: channelInfo.member.map(user => user._id), newMember: inviteUserList.map(user => user._id), @@ -83,7 +83,7 @@ function InviteUserToChannelModal({ handleClose, type = 'channel' }) { }) if (data.success) { - socket.emit('invite channel', { + socket.emit(SOCKET_EVENT.INVITE_CHANNEL, { channelId: channelId, origin: [], newMember: inviteUserList.map(user => user._id), diff --git a/frontend/src/container/SideThreadBar/SideThreadBar.js b/frontend/src/container/SideThreadBar/SideThreadBar.js index d61c4518..98aaeb30 100644 --- a/frontend/src/container/SideThreadBar/SideThreadBar.js +++ b/frontend/src/container/SideThreadBar/SideThreadBar.js @@ -11,7 +11,7 @@ import { CLOSE } from '../../constant/icon' import { workspaceRecoil, socketRecoil } from '../../store' import { hasMyReaction, chageReactionState } from '../../util/reactionUpdate' import DraggableBoundaryLine from '../../presenter/DraggableBoundaryLine' - +import { SOCKET_EVENT } from '../../constant' function SideThreadBar({ sidebarWidth, setSidebarWidth }) { const { workspaceId, channelId, chatId } = useParams() const [sidebarChat, setSidebarChat] = useState(null) @@ -52,13 +52,13 @@ function SideThreadBar({ sidebarWidth, setSidebarWidth }) { useEffect(() => { if (socket) { - socket.on('new reply', ({ message }) => { + socket.on(SOCKET_EVENT.NEW_REPLY, ({ message }) => { if (message.chatId === chatId) { setReplyContent(messages => [...messages, message]) } scrollTo() }) - socket.on('update reaction', ({ reaction }) => { + socket.on(SOCKET_EVENT.UPDAETE_REACTION, ({ reaction }) => { if (reaction.chatId === chatId) setSidebarChat(chat => chageReactionState(chat, reaction)) if (reaction.parentId) { @@ -68,8 +68,8 @@ function SideThreadBar({ sidebarWidth, setSidebarWidth }) { } return () => { if (socket) { - socket.off('new message') - socket.off('update reaction') + socket.off(SOCKET_EVENT.NEW_REPLY) + socket.off(SOCKET_EVENT.UPDAETE_REACTION) } } }, [socket, chatId]) @@ -86,7 +86,7 @@ function SideThreadBar({ sidebarWidth, setSidebarWidth }) { profileUrl: workspaceUserInfo.profileUrl, }, } - socket.emit('new reply', reply) + socket.emit(SOCKET_EVENT.NEW_REPLY, reply) } useEffect(() => { diff --git a/frontend/src/hooks/useSocket.js b/frontend/src/hooks/useSocket.js index e59c2b77..6eebcbf6 100644 --- a/frontend/src/hooks/useSocket.js +++ b/frontend/src/hooks/useSocket.js @@ -3,6 +3,7 @@ import { socketRecoil, workspaceRecoil } from '../store' import { useParams } from 'react-router-dom' import { useRecoilState, useRecoilValue } from 'recoil' import { isEmpty } from '../util' +import { SOCKET_EVENT } from '../constant' import io from 'socket.io-client' import useChannelInfo from './useChannelInfo' import useChannelList from './useChannelList' @@ -35,11 +36,10 @@ const useSocket = () => { useEffect(() => { if (socket && !isEmpty(channelList)) { socket.emit( - 'join-room', + SOCKET_EVENT.JOIN_ROOM, channelList.map(channel => channel.channelId._id), ) - socket.on('invited channel', ({ channelId, newMember }) => { - console.log('invited') + socket.on(SOCKET_EVENT.INVITED_CHANNEL, ({ channelId, newMember }) => { if (channelId === channelInfo.channelId._id) updateChannelInfo(channelId) if (newMember === workspaceUserInfo._id) setChannels() @@ -48,7 +48,7 @@ const useSocket = () => { return () => { if (socket) socket.emit( - 'leave-room', + SOCKET_EVENT.LEAVE_ROOM, channelList.map(channel => channel.channelId._id), ) } @@ -56,14 +56,14 @@ const useSocket = () => { useEffect(() => { if (socket && !isEmpty(channelInfo) && !isEmpty(workspaceUserInfo)) { - socket.on('invited channel', ({ channelId, newMember }) => { + socket.on(SOCKET_EVENT.INVITED_CHANNEL, ({ channelId, newMember }) => { if (channelId === channelInfo.channelId._id) updateChannelInfo(channelId) if (newMember.includes(workspaceUserInfo._id)) setChannels() }) } return () => { - if (socket) socket.off('invited channel') + if (socket) socket.off(SOCKET_EVENT.INVITED_CHANNEL) } }, [socket, channelInfo, workspaceUserInfo]) From 6bbb48d2f6d3e8b7632e0d8de3319cdb67d038e9 Mon Sep 17 00:00:00 2001 From: jongwon Date: Sun, 20 Dec 2020 05:33:09 +0900 Subject: [PATCH 06/12] Refactor: remove useChannelInfo hook MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 각각의 컴포넌트에서 custom hook을 호출할 때마다 useEffect가 발생하여 불필요한 api 호출이 발생하므로 제거 --- .../container/ChannelHeader/ChannelHeader.js | 7 ++-- frontend/src/container/ChatRoom/ChatRoom.js | 26 +++++++++++--- .../InviteUserToChannelModal.js | 5 ++- frontend/src/hooks/useChannelInfo.js | 29 --------------- frontend/src/hooks/useSocket.js | 35 ++++++++++++++----- 5 files changed, 54 insertions(+), 48 deletions(-) delete mode 100644 frontend/src/hooks/useChannelInfo.js diff --git a/frontend/src/container/ChannelHeader/ChannelHeader.js b/frontend/src/container/ChannelHeader/ChannelHeader.js index 352a5737..9df96314 100644 --- a/frontend/src/container/ChannelHeader/ChannelHeader.js +++ b/frontend/src/container/ChannelHeader/ChannelHeader.js @@ -1,6 +1,6 @@ import React from 'react' import styled from 'styled-components' -import { useSetRecoilState } from 'recoil' +import { useSetRecoilState, useRecoilValue } from 'recoil' import Icon from '../../presenter/Icon' import { ADDUSER, INFOCIRCLE } from '../../constant/icon' @@ -9,15 +9,14 @@ import ChannelStarBtn from '../ChannelStarBtn' import ChannelPinBtn from '../../presenter/ChannelPinBtn' import ChannelTopicBtn from '../../presenter/ChannelTopicBtn' import ChannelMemberThumbnail from '../../presenter/ChannelMemberThumbnail' -import { modalRecoil } from '../../store' +import { modalRecoil, currentChannelInfoRecoil } from '../../store' import InviteUserToChannelModal from '../Modal/InviteUserToChannelModal' import { COLOR } from '../../constant/style' -import useChannelInfo from '../../hooks/useChannelInfo' import { isEmpty } from '../../util' function ChannelHeader() { const setModal = useSetRecoilState(modalRecoil) - const [channelInfo] = useChannelInfo() + const channelInfo = useRecoilValue(currentChannelInfoRecoil) const openAddUserModal = () => { setModal( setModal(null)} />) } diff --git a/frontend/src/container/ChatRoom/ChatRoom.js b/frontend/src/container/ChatRoom/ChatRoom.js index 27bf0b82..ef504b75 100644 --- a/frontend/src/container/ChatRoom/ChatRoom.js +++ b/frontend/src/container/ChatRoom/ChatRoom.js @@ -1,18 +1,22 @@ import React, { useEffect, useState, useRef, useCallback } from 'react' import styled from 'styled-components' import { useParams } from 'react-router-dom' -import { useRecoilValue } from 'recoil' +import { useRecoilState, useRecoilValue } from 'recoil' import ChatMessage from '../ChatMessage' import { COLOR } from '../../constant/style' import { getChatMessage } from '../../api/chat' import MessageEditor from '../MessageEditor/MessageEditor' -import { workspaceRecoil, socketRecoil } from '../../store' +import { + workspaceRecoil, + socketRecoil, + currentChannelInfoRecoil, +} from '../../store' import ChannelHeader from '../ChannelHeader' import { isEmpty } from '../../util' import { hasMyReaction, chageReactionState } from '../../util/reactionUpdate' -import useChannelInfo from '../../hooks/useChannelInfo' import Icon from '../../presenter/Icon' import { ArrowDown } from '../../constant/icon' +import { getChannelHeaderInfo } from '../../api/channel' const ChatRoom = ({ width }) => { const viewport = useRef(null) @@ -23,7 +27,7 @@ const ChatRoom = ({ width }) => { const isAllMessageFetched = useRef(false) const isReading = useRef(false) const workspaceUserInfo = useRecoilValue(workspaceRecoil) - const [channelInfo] = useChannelInfo() + const [channelInfo, setChannelInfo] = useRecoilState(currentChannelInfoRecoil) const { workspaceId, channelId } = useParams() const params = useParams() const socket = useRecoilValue(socketRecoil) @@ -52,6 +56,20 @@ const ChatRoom = ({ width }) => { [messages], ) + const updateChannelInfo = useCallback(async () => { + if (workspaceUserInfo && channelId) + setChannelInfo( + await getChannelHeaderInfo({ + workspaceUserInfoId: workspaceUserInfo._id, + channelId, + }), + ) + }, [channelId, workspaceUserInfo, setChannelInfo]) + + useEffect(() => { + updateChannelInfo() + }, [channelId, workspaceUserInfo, updateChannelInfo]) + useEffect(() => { setMessages([]) isLoading.current = false diff --git a/frontend/src/container/Modal/InviteUserToChannelModal/InviteUserToChannelModal.js b/frontend/src/container/Modal/InviteUserToChannelModal/InviteUserToChannelModal.js index cc2d62e9..4a08b017 100644 --- a/frontend/src/container/Modal/InviteUserToChannelModal/InviteUserToChannelModal.js +++ b/frontend/src/container/Modal/InviteUserToChannelModal/InviteUserToChannelModal.js @@ -12,15 +12,14 @@ import request from '../../../util/request' import Modal from '../../../presenter/Modal' import SearchUserList from '../../../presenter/SearchUserList' import SelectedUserList from '../../../presenter/SelectedUserList' -import useChannelInfo from '../../../hooks/useChannelInfo' import dmTitleGenerator from '../../../util/dmTitleGenerator' -import { workspaceRecoil } from '../../../store' +import { workspaceRecoil, currentChannelInfoRecoil } from '../../../store' import { createChannel, findChannelIdByName } from '../../../api/channel' import { getWorkspaceUserInfoByInfoId } from '../../../api/workspace' import useChannelList from '../../../hooks/useChannelList' function InviteUserToChannelModal({ handleClose, type = 'channel' }) { - const [channelInfo] = useChannelInfo() + const channelInfo = useRecoilValue(currentChannelInfoRecoil) const setModal = useSetRecoilState(modalRecoil) const socket = useRecoilValue(socketRecoil) const [searchResult, setSearchResult] = useState(null) diff --git a/frontend/src/hooks/useChannelInfo.js b/frontend/src/hooks/useChannelInfo.js deleted file mode 100644 index 6425fffe..00000000 --- a/frontend/src/hooks/useChannelInfo.js +++ /dev/null @@ -1,29 +0,0 @@ -import { useEffect } from 'react' -import { currentChannelInfoRecoil, workspaceRecoil } from '../store' -import { useParams } from 'react-router-dom' -import { useRecoilState, useRecoilValue } from 'recoil' -import { getChannelHeaderInfo } from '../api/channel' - -const useChannelInfo = () => { - const { channelId } = useParams() - const [channelInfo, setChannelInfo] = useRecoilState(currentChannelInfoRecoil) - const workspaceInfo = useRecoilValue(workspaceRecoil) - const updateChannelInfo = async channelId => { - if (workspaceInfo) { - setChannelInfo( - await getChannelHeaderInfo({ - workspaceUserInfoId: workspaceInfo._id, - channelId, - }), - ) - } - } - - useEffect(() => { - updateChannelInfo(channelId) - }, [channelId, workspaceInfo]) - - return [channelInfo, updateChannelInfo] -} - -export default useChannelInfo diff --git a/frontend/src/hooks/useSocket.js b/frontend/src/hooks/useSocket.js index e59c2b77..4ada77f4 100644 --- a/frontend/src/hooks/useSocket.js +++ b/frontend/src/hooks/useSocket.js @@ -1,11 +1,15 @@ import { useEffect } from 'react' -import { socketRecoil, workspaceRecoil } from '../store' +import { + currentChannelInfoRecoil, + socketRecoil, + workspaceRecoil, +} from '../store' import { useParams } from 'react-router-dom' import { useRecoilState, useRecoilValue } from 'recoil' import { isEmpty } from '../util' import io from 'socket.io-client' -import useChannelInfo from './useChannelInfo' import useChannelList from './useChannelList' +import { getChannelHeaderInfo } from '../api/channel' const baseURL = process.env.NODE_ENV === 'development' @@ -17,7 +21,17 @@ const useSocket = () => { const workspaceUserInfo = useRecoilValue(workspaceRecoil) const [socket, setSocket] = useRecoilState(socketRecoil) const [channelList, setChannels] = useChannelList() - const [channelInfo, updateChannelInfo] = useChannelInfo() + const [channelInfo, setChannelInfo] = useRecoilState(currentChannelInfoRecoil) + + const updateChannelInfo = async ({ channelId, workspaceUserInfo }) => { + if (workspaceUserInfo && channelId) + setChannelInfo( + await getChannelHeaderInfo({ + workspaceUserInfoId: workspaceUserInfo._id, + channelId, + }), + ) + } useEffect(() => { if (workspaceId && workspaceUserInfo) { @@ -39,9 +53,11 @@ const useSocket = () => { channelList.map(channel => channel.channelId._id), ) socket.on('invited channel', ({ channelId, newMember }) => { - console.log('invited') if (channelId === channelInfo.channelId._id) - updateChannelInfo(channelId) + updateChannelInfo({ + channelId, + workspaceUserInfo, + }) if (newMember === workspaceUserInfo._id) setChannels() }) } @@ -52,20 +68,23 @@ const useSocket = () => { channelList.map(channel => channel.channelId._id), ) } - }, [socket, channelList]) + }, [socket, channelList, updateChannelInfo]) useEffect(() => { if (socket && !isEmpty(channelInfo) && !isEmpty(workspaceUserInfo)) { socket.on('invited channel', ({ channelId, newMember }) => { if (channelId === channelInfo.channelId._id) - updateChannelInfo(channelId) + updateChannelInfo({ + channelId, + workspaceUserInfo, + }) if (newMember.includes(workspaceUserInfo._id)) setChannels() }) } return () => { if (socket) socket.off('invited channel') } - }, [socket, channelInfo, workspaceUserInfo]) + }, [socket, channelInfo, workspaceUserInfo, updateChannelInfo]) return [socket] } From 8c6c8fad0fc7930f51be934d90ff664227bfdb8f Mon Sep 17 00:00:00 2001 From: Ryou_Changyu Date: Sun, 20 Dec 2020 18:00:28 +0900 Subject: [PATCH 07/12] Refactor/set model index MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 모델에 index 설정을 통한 성능 개선 --- backend/model/Channel.js | 1 + backend/model/ChannelConfig.js | 3 +++ backend/model/Chat.js | 6 ++++++ backend/model/Reaction.js | 3 +++ backend/model/Workspace.js | 2 ++ backend/model/WorkspaceUserInfo.js | 3 +++ 6 files changed, 18 insertions(+) diff --git a/backend/model/Channel.js b/backend/model/Channel.js index e53066a1..d17c5eb5 100644 --- a/backend/model/Channel.js +++ b/backend/model/Channel.js @@ -33,6 +33,7 @@ const channelSchema = mongoose.Schema( }, { timestamps: true }, ) +channelSchema.index({ workspaceId: 1, channelType: 1 }) channelSchema.statics.getChannelBrowserData = async function ( workspaceId, diff --git a/backend/model/ChannelConfig.js b/backend/model/ChannelConfig.js index d78f8f03..62ed680e 100644 --- a/backend/model/ChannelConfig.js +++ b/backend/model/ChannelConfig.js @@ -29,6 +29,9 @@ const channelConfigSchema = mongoose.Schema( { timestamps: true }, ) +channelConfigSchema.index({ channelId: 1, workspaceUserInfoId: 1 }) +channelConfigSchema.index({ workspaceUserInfoId: 1 }) + channelConfigSchema.statics.getChannelHeaderInfo = async function ( channelId, workspaceUserInfoId, diff --git a/backend/model/Chat.js b/backend/model/Chat.js index f4fd83d7..b9507f4a 100644 --- a/backend/model/Chat.js +++ b/backend/model/Chat.js @@ -33,6 +33,12 @@ const chatSchema = mongoose.Schema( }, { timestamps: true }, ) + +chatSchema.index({ createdAt: 1, channel: 1 }) +chatSchema.index({ channel: 1, parentId: 1 }) +chatSchema.index({ pinned: 1 }) +chatSchema.index({ parentId: 1 }) + chatSchema.statics.getChatMessages = ({ channelId, currentCursor, fromDate }) => Chat.aggregate([ { $sort: { createdAt: -1 } }, diff --git a/backend/model/Reaction.js b/backend/model/Reaction.js index 488e4bd9..10c809df 100644 --- a/backend/model/Reaction.js +++ b/backend/model/Reaction.js @@ -14,5 +14,8 @@ const reactionSchema = mongoose.Schema({ type: String, }, }) +reactionSchema.index({ workspaceUserInfoId: 1, chatId: 1 }) +reactionSchema.index({ chatId: 1 }) + const Reaction = mongoose.model('Reaction', reactionSchema) module.exports = { Reaction } diff --git a/backend/model/Workspace.js b/backend/model/Workspace.js index 64642a69..416642ab 100644 --- a/backend/model/Workspace.js +++ b/backend/model/Workspace.js @@ -20,5 +20,7 @@ const workspaceSchema = mongoose.Schema( }, { timestamps: true }, ) +workspaceSchema.index({ name: 1 }) + const Workspace = mongoose.model('Workspace', workspaceSchema) module.exports = { Workspace } diff --git a/backend/model/WorkspaceUserInfo.js b/backend/model/WorkspaceUserInfo.js index 77eeff7b..8359c24b 100644 --- a/backend/model/WorkspaceUserInfo.js +++ b/backend/model/WorkspaceUserInfo.js @@ -52,6 +52,9 @@ const workspaceUserInfoSchema = mongoose.Schema( { timestamps: true }, ) +workspaceUserInfoSchema.index({ workspaceId: 1, userId: 1 }) +workspaceUserInfoSchema.index({ userId: 1 }) + workspaceUserInfoSchema.statics.getWorkspaceUserInfo = async function ( workspaceUserInfoId, ) { From 6566fe2fe44766db16da0f2a8db4c23dbaa0bfe4 Mon Sep 17 00:00:00 2001 From: Ryou_Changyu Date: Sun, 20 Dec 2020 18:18:31 +0900 Subject: [PATCH 08/12] Fix: reaction bug fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit reaction 추가/삭제 버그 수정 --- frontend/src/container/ChatRoom/ChatRoom.js | 4 +++- .../src/container/SideThreadBar/SideThreadBar.js | 8 ++++++-- frontend/src/page/WorkspacePage/WorkspacePage.js | 2 +- frontend/src/util/reactionUpdate.js | 13 +++++++++---- 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/frontend/src/container/ChatRoom/ChatRoom.js b/frontend/src/container/ChatRoom/ChatRoom.js index 27bf0b82..c915a5f8 100644 --- a/frontend/src/container/ChatRoom/ChatRoom.js +++ b/frontend/src/container/ChatRoom/ChatRoom.js @@ -105,7 +105,9 @@ const ChatRoom = ({ width }) => { if (message.userInfo._id === workspaceUserInfo._id) scrollTo() }) socket.on('update reaction', ({ reaction }) => { - setMessages(messages => chageReactionState(messages, reaction)) + setMessages(messages => + chageReactionState(messages, reaction, workspaceUserInfo), + ) }) } return () => { diff --git a/frontend/src/container/SideThreadBar/SideThreadBar.js b/frontend/src/container/SideThreadBar/SideThreadBar.js index d61c4518..ff81d8ee 100644 --- a/frontend/src/container/SideThreadBar/SideThreadBar.js +++ b/frontend/src/container/SideThreadBar/SideThreadBar.js @@ -60,9 +60,13 @@ function SideThreadBar({ sidebarWidth, setSidebarWidth }) { }) socket.on('update reaction', ({ reaction }) => { if (reaction.chatId === chatId) - setSidebarChat(chat => chageReactionState(chat, reaction)) + setSidebarChat(chat => + chageReactionState(chat, reaction, workspaceUserInfo), + ) if (reaction.parentId) { - setReplyContent(reply => chageReactionState(reply, reaction)) + setReplyContent(reply => + chageReactionState(reply, reaction, workspaceUserInfo), + ) } }) } diff --git a/frontend/src/page/WorkspacePage/WorkspacePage.js b/frontend/src/page/WorkspacePage/WorkspacePage.js index d77b4792..0ad2604e 100644 --- a/frontend/src/page/WorkspacePage/WorkspacePage.js +++ b/frontend/src/page/WorkspacePage/WorkspacePage.js @@ -43,7 +43,7 @@ function WorkspacePage() { case 'more': return ConstructionPage() default: - return + return workspaceUserInfo && } } diff --git a/frontend/src/util/reactionUpdate.js b/frontend/src/util/reactionUpdate.js index 8cf16b2c..12034277 100644 --- a/frontend/src/util/reactionUpdate.js +++ b/frontend/src/util/reactionUpdate.js @@ -1,6 +1,6 @@ import produce from 'immer' -export const chageReactionState = (messages, reaction) => +export const chageReactionState = (messages, reaction, workspaceUserInfoId) => produce(messages, draft => { let done = false if (reaction.type === false) return @@ -14,12 +14,14 @@ export const chageReactionState = (messages, reaction) => _id: reaction.workspaceUserInfoId, displayName: reaction.displayName, }) - element.set = true + if (reaction.workspaceUserInfoId === workspaceUserInfoId._id) + element.set = true } else { element.users.forEach((user, idx) => { if (user._id === reaction.workspaceUserInfoId) { element.users.splice(idx, 1) - element.set = false + if (reaction.workspaceUserInfoId === workspaceUserInfoId._id) + element.set = false } }) if (element.users.length === 0) { @@ -38,7 +40,10 @@ export const chageReactionState = (messages, reaction) => displayName: reaction.displayName, }, ], - set: true, + set: + reaction.workspaceUserInfoId === workspaceUserInfoId._id + ? true + : false, }) } } From 6fd5c60cfe585c6888b7df5af0f080b4a5409059 Mon Sep 17 00:00:00 2001 From: Ryou_Changyu Date: Sun, 20 Dec 2020 18:33:13 +0900 Subject: [PATCH 09/12] Fix: chageReactionState value bug fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chageReactionState에 전달하는 데이터 수정 --- frontend/src/container/ChatRoom/ChatRoom.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frontend/src/container/ChatRoom/ChatRoom.js b/frontend/src/container/ChatRoom/ChatRoom.js index 236031ee..06f87efc 100644 --- a/frontend/src/container/ChatRoom/ChatRoom.js +++ b/frontend/src/container/ChatRoom/ChatRoom.js @@ -124,7 +124,9 @@ const ChatRoom = ({ width }) => { if (message.userInfo._id === workspaceUserInfo._id) scrollTo() }) socket.on(SOCKET_EVENT.UPDAETE_REACTION, ({ reaction }) => { - setMessages(messages => chageReactionState(messages, reaction)) + setMessages(messages => + chageReactionState(messages, reaction, workspaceUserInfo), + ) }) } return () => { From f3b333bfe11a99a94651038dcaeedef739af7539 Mon Sep 17 00:00:00 2001 From: jongwon Date: Sun, 20 Dec 2020 20:32:54 +0900 Subject: [PATCH 10/12] Refactor: real time update on view thread button --- backend/chatServer.js | 1 + frontend/src/container/ChatRoom/ChatRoom.js | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/backend/chatServer.js b/backend/chatServer.js index f59ac4f6..2e3bc2f9 100644 --- a/backend/chatServer.js +++ b/backend/chatServer.js @@ -44,6 +44,7 @@ namespace.on('connection', socket => { _id: result._id, createdAt: result.createdAt, reactions: [], + reply: [], }, }) }) diff --git a/frontend/src/container/ChatRoom/ChatRoom.js b/frontend/src/container/ChatRoom/ChatRoom.js index 06f87efc..7e33e7c6 100644 --- a/frontend/src/container/ChatRoom/ChatRoom.js +++ b/frontend/src/container/ChatRoom/ChatRoom.js @@ -18,6 +18,7 @@ import Icon from '../../presenter/Icon' import { ArrowDown } from '../../constant/icon' import { getChannelHeaderInfo } from '../../api/channel' import { SOCKET_EVENT } from '../../constant' +import produce from 'immer' const ChatRoom = ({ width }) => { const viewport = useRef(null) @@ -123,6 +124,15 @@ const ChatRoom = ({ width }) => { if (message.userInfo._id === workspaceUserInfo._id) scrollTo() }) + socket.on(SOCKET_EVENT.NEW_REPLY, ({ message }) => { + setMessages(messages => + messages.map(target => + target._id === message.parentId + ? { ...target, reply: [...target.reply, message] } + : target, + ), + ) + }) socket.on(SOCKET_EVENT.UPDAETE_REACTION, ({ reaction }) => { setMessages(messages => chageReactionState(messages, reaction, workspaceUserInfo), @@ -131,11 +141,13 @@ const ChatRoom = ({ width }) => { } return () => { if (socket) { + socket.off(SOCKET_EVENT.NEW_REPLY) socket.off(SOCKET_EVENT.NEW_MESSAGE) socket.off(SOCKET_EVENT.UPDAETE_REACTION) } } }, [socket, channelId, document.hidden, params]) + useEffect(() => { const handleIntersection = (entries, observer) => { entries.forEach(entry => { From b6ec50ffeaee10b6a5479f3479b6e1a5477ab1bd Mon Sep 17 00:00:00 2001 From: jongwon Date: Sun, 20 Dec 2020 20:35:37 +0900 Subject: [PATCH 11/12] Refactor: remove unnecessary module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 사용하지않는 immer 제거 --- frontend/src/container/ChatRoom/ChatRoom.js | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/container/ChatRoom/ChatRoom.js b/frontend/src/container/ChatRoom/ChatRoom.js index 7e33e7c6..19833a6c 100644 --- a/frontend/src/container/ChatRoom/ChatRoom.js +++ b/frontend/src/container/ChatRoom/ChatRoom.js @@ -18,7 +18,6 @@ import Icon from '../../presenter/Icon' import { ArrowDown } from '../../constant/icon' import { getChannelHeaderInfo } from '../../api/channel' import { SOCKET_EVENT } from '../../constant' -import produce from 'immer' const ChatRoom = ({ width }) => { const viewport = useRef(null) From 0aabbe361d8ad6c02ec56b50b8aa964dd9bef56e Mon Sep 17 00:00:00 2001 From: jongwon Date: Sun, 20 Dec 2020 20:54:26 +0900 Subject: [PATCH 12/12] Refactor: change the conditions which unread message appear --- frontend/src/container/ChatRoom/ChatRoom.js | 6 +++--- .../src/presenter/ChannelBrowserCard/ChannelBrowserCard.js | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/frontend/src/container/ChatRoom/ChatRoom.js b/frontend/src/container/ChatRoom/ChatRoom.js index 19833a6c..9ecaaf02 100644 --- a/frontend/src/container/ChatRoom/ChatRoom.js +++ b/frontend/src/container/ChatRoom/ChatRoom.js @@ -108,11 +108,12 @@ const ChatRoom = ({ width }) => { ...messages, ...hasMyReaction([message], workspaceUserInfo), ]) - if (isReading.current && document.hasFocus()) { + if (message.userInfo._id === workspaceUserInfo._id) { setHasUnreadMessage(false) scrollTo() - } else if (message.userInfo._id !== workspaceUserInfo._id) + } else if (!isReading.current && !document.hasFocus()) { setHasUnreadMessage(true) + } } if (document.hidden) { @@ -257,7 +258,6 @@ const UnreadMessage = styled.div` background-color: ${COLOR.STARBLUE}; color: ${COLOR.WHITE}; width: 170px; - height: 50px; margin-left: auto; margin-right: auto; position: sticky; diff --git a/frontend/src/presenter/ChannelBrowserCard/ChannelBrowserCard.js b/frontend/src/presenter/ChannelBrowserCard/ChannelBrowserCard.js index 218eb3f2..6eded927 100644 --- a/frontend/src/presenter/ChannelBrowserCard/ChannelBrowserCard.js +++ b/frontend/src/presenter/ChannelBrowserCard/ChannelBrowserCard.js @@ -40,7 +40,6 @@ function ChannelBrowserCard({ setChannels() handleClose() } - return ( {title} @@ -48,9 +47,9 @@ function ChannelBrowserCard({