From d6a4b3c4193fbdd0c83f407490e0f0ac160b80df Mon Sep 17 00:00:00 2001 From: Ryou_Changyu Date: Mon, 14 Dec 2020 20:12:43 +0900 Subject: [PATCH 001/102] Refactor: update directory structure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 디렉토리 디자인 수정 --- frontend/.storybook/preview.js | 2 +- frontend/src/atom/ChannelCard/hash.svg | 7 - frontend/src/atom/ChannelCard/locker.svg | 24 -- .../ActionBar/ActionBar.js | 10 +- .../ActionBar/index.js | 0 .../AddReactionButton/AddReactionButton.js | 10 +- .../AddReactionButton/index.js | 0 .../ChannelHeader/ChannelHeader.js | 18 +- .../ChannelHeader/index.js | 0 .../ChannelList/ChannelList.js | 2 +- .../ChannelList/index.js | 0 .../ChannelStarBtn/ChannelStarBtn.js | 6 +- .../ChannelStarBtn/ChannelStarBtn.stories.js | 0 .../ChannelStarBtn/index.js | 0 .../ChatMessage/ChatMessage.js | 8 +- .../ChatMessage/ChatMessage.stories.js | 0 .../ChatMessage/index.js | 0 .../ChatRoom/ChatRoom.js | 13 +- .../{organism => container}/ChatRoom/index.js | 0 .../CreateWorkspaceInitChannel.js | 10 +- .../CreateWorkspaceInitChannel.stories.js | 0 .../CreateWorkspaceInitChannel/index.js | 0 .../CreateWorkspaceName.js | 8 +- .../CreateWorkspaceName.stories.js | 0 .../CreateWorkspaceName/index.js | 0 .../FilePreview/FilePreview.js | 4 +- .../FilePreview/FilePreview.stories.js | 0 .../FilePreview/index.js | 0 .../FileUploader/FileUploader.js | 0 .../FileUploader/FileUploader.stories.js | 0 .../FileUploader/index.js | 0 .../ImgPreview/ImgPreview.js | 4 +- .../ImgPreview/ImgPreview.stories.js | 0 .../ImgPreview/index.js | 0 .../MessageEditor}/MessageEditor.js | 2 +- frontend/src/container/MessageEditor/index.js | 1 + .../CreateChannelModal/CreateChannelModal.js | 26 +-- .../CreateChannelModal.stories.js | 0 .../Modal}/CreateChannelModal/index.js | 0 .../InviteUserToChannelModal.js | 22 +- .../Modal}/InviteUserToChannelModal/index.js | 0 .../InviteWorkspaceModal.js | 14 +- .../InviteWorkspaceModal.stories.js | 0 .../Modal}/InviteWorkspaceModal/index.js | 0 .../MyWorkspaceSection/MyWorkspaceSection.js | 4 +- .../MyWorkspaceSection.stories.js | 0 .../MyWorkspaceSection/index.js | 0 .../SearchUserCard/SearchUserCard.js | 5 +- .../SearchUserCard/index.js | 0 .../SectionLabel/SectionLabel.js | 12 +- .../SectionLabel/SectionLabel.stories.js | 0 .../SectionLabel/index.js | 0 .../ThreadReactionCard/ThreadReactionCard.js | 6 +- .../ThreadReactionCard/index.js | 0 .../ThreadReactionList/ThreadReactionList.js | 6 +- frontend/src/organism/index.js | 0 .../src/page/WorkspacePage/WorkspacePage.js | 12 +- .../page/createWorkspace/CreateWorkspace.js | 4 +- frontend/src/page/login/Login.js | 6 +- .../page/selectWorkspace/SelectWorkspace.js | 4 +- .../Button/AddButton/AddButton.js | 0 .../Button/AddButton/Addbutton.stories.js | 0 .../Button/AddButton/index.js | 0 .../src/{atom => presenter}/Button/Button.js | 0 .../Button/Button.stories.js | 0 .../Button/ToggleButton/ToggleButton.js | 0 .../ToggleButton/ToggleButton.stories.js | 0 .../Button/ToggleButton/index.js | 0 .../src/{atom => presenter}/Button/index.js | 0 .../ChannelCard/ChannelCard.js | 0 .../ChannelCard/ChannelCard.stories.js | 0 .../{atom => presenter}/ChannelCard/index.js | 0 .../ChannelListHeader/ChannelListHeader.js | 8 +- .../ChannelListHeader/index.js | 0 .../ChannelMemberThumbnail.js | 0 .../ChannelMemberThumbnail/index.js | 0 .../ChannelPinBtn/ChannelPinBtn.js | 0 .../ChannelPinBtn/index.js | 0 .../ChannelTopicBtn/ChannelTopicBtn.js | 0 .../ChannelTopicBtn/index.js | 0 .../ChatContent/ChatContent.js | 0 .../{atom => presenter}/ChatContent/index.js | 0 .../Description/Description.js | 0 .../Description/Description.stories.js | 0 .../{atom => presenter}/Description/index.js | 0 .../DirectMessageCard/DirectMessageCard.js | 0 .../DirectMessageCard.stories.js | 0 .../DirectMessageCard/index.js | 0 .../EmojiModal/EmojiModal.js | 0 .../{atom => presenter}/EmojiModal/index.js | 0 .../GlobalStyle/GlobalStyle.js | 0 .../{atom => presenter}/GlobalStyle/index.js | 0 frontend/src/{atom => presenter}/Icon/Icon.js | 0 .../{atom => presenter}/Icon/Icon.stories.js | 0 .../src/{atom => presenter}/Icon/index.js | 0 .../src/{atom => presenter}/Input/Input.js | 0 .../Input/Input.stories.js | 0 .../src/{atom => presenter}/Input/index.js | 0 .../LoginButton/LoginButton.js | 0 .../LoginButton/LoginButton.stories.js | 0 .../{atom => presenter}/LoginButton/index.js | 0 .../MainDescription/MainDescription.js | 0 .../MainDescription.stories.js | 0 .../MainDescription/index.js | 0 .../src/{atom => presenter}/Modal/Modal.js | 0 .../src/{atom => presenter}/Modal/index.js | 0 .../ModalInputSection/ModalInputSection.js | 0 .../ModalInputSection.stories.js | 4 +- .../ModalInputSection/index.js | 0 .../MyWorkspace/MyWorkspace.js | 6 +- .../MyWorkspace/MyWorkspace.stories.js | 0 .../MyWorkspace/index.js | 0 .../NewWorkspaceSection.js | 8 +- .../NewWorkspaceSection.stories.js | 0 .../NewWorkspaceSection/index.js | 0 .../SearchUserList/SearchUserList.js | 2 +- .../SearchUserList/index.js | 0 .../SelectedUserCard/SelectedUserCard.js | 0 .../SelectedUserCard/index.js | 0 .../SelectedUserList/SelectedUserList.js | 2 +- .../SelectedUserList/index.js | 0 .../SideMenuCard/SideMenuCard.js | 23 +- .../{atom => presenter}/SideMenuCard/index.js | 0 .../SideMenuList/SideMenuList.js | 2 +- .../SideMenuList/index.js | 0 .../SlackImage/SlackImage.js | 0 .../SlackImage/SlackImage.stories.js | 0 .../{atom => presenter}/SlackImage/index.js | 0 .../ThreadReactionList/ThreadReactionList.js | 41 ++++ .../ThreadReactionList/index.js | 0 .../src/{atom => presenter}/Title/Title.js | 0 .../Title/Title.stories.js | 0 .../src/{atom => presenter}/Title/index.js | 0 .../UserActive/UserActive.js | 0 .../UserActive/UserActive.stories.js | 0 .../{atom => presenter}/UserActive/index.js | 0 .../UserProfileImg/UserProfileImg.js | 0 .../UserProfileImg/UserProfileImg.stories.js | 0 .../UserProfileImg/index.js | 0 frontend/src/stories/Button.js | 54 ----- frontend/src/stories/Button.stories.js | 36 --- frontend/src/stories/Header.js | 62 ------ frontend/src/stories/Header.stories.js | 18 -- frontend/src/stories/Introduction.stories.mdx | 207 ------------------ frontend/src/stories/Page.js | 93 -------- frontend/src/stories/Page.stories.js | 21 -- frontend/src/stories/assets/code-brackets.svg | 1 - frontend/src/stories/assets/colors.svg | 1 - frontend/src/stories/assets/comments.svg | 1 - frontend/src/stories/assets/direction.svg | 1 - frontend/src/stories/assets/flow.svg | 1 - frontend/src/stories/assets/plugin.svg | 1 - frontend/src/stories/assets/repo.svg | 1 - frontend/src/stories/assets/stackalt.svg | 1 - frontend/src/stories/button.css | 30 --- frontend/src/stories/header.css | 26 --- frontend/src/stories/page.css | 69 ------ 157 files changed, 170 insertions(+), 800 deletions(-) delete mode 100644 frontend/src/atom/ChannelCard/hash.svg delete mode 100644 frontend/src/atom/ChannelCard/locker.svg rename frontend/src/{organism => container}/ActionBar/ActionBar.js (90%) rename frontend/src/{organism => container}/ActionBar/index.js (100%) rename frontend/src/{atom => container}/AddReactionButton/AddReactionButton.js (83%) rename frontend/src/{atom => container}/AddReactionButton/index.js (100%) rename frontend/src/{organism => container}/ChannelHeader/ChannelHeader.js (87%) rename frontend/src/{organism => container}/ChannelHeader/index.js (100%) rename frontend/src/{organism => container}/ChannelList/ChannelList.js (97%) rename frontend/src/{organism => container}/ChannelList/index.js (100%) rename frontend/src/{atom => container}/ChannelStarBtn/ChannelStarBtn.js (93%) rename frontend/src/{atom => container}/ChannelStarBtn/ChannelStarBtn.stories.js (100%) rename frontend/src/{atom => container}/ChannelStarBtn/index.js (100%) rename frontend/src/{organism => container}/ChatMessage/ChatMessage.js (94%) rename frontend/src/{organism => container}/ChatMessage/ChatMessage.stories.js (100%) rename frontend/src/{organism => container}/ChatMessage/index.js (100%) rename frontend/src/{organism => container}/ChatRoom/ChatRoom.js (94%) rename frontend/src/{organism => container}/ChatRoom/index.js (100%) rename frontend/src/{organism => container}/CreateWorkspaceInitChannel/CreateWorkspaceInitChannel.js (90%) rename frontend/src/{organism => container}/CreateWorkspaceInitChannel/CreateWorkspaceInitChannel.stories.js (100%) rename frontend/src/{organism => container}/CreateWorkspaceInitChannel/index.js (100%) rename frontend/src/{organism => container}/CreateWorkspaceName/CreateWorkspaceName.js (92%) rename frontend/src/{organism => container}/CreateWorkspaceName/CreateWorkspaceName.stories.js (100%) rename frontend/src/{organism => container}/CreateWorkspaceName/index.js (100%) rename frontend/src/{organism => container}/FilePreview/FilePreview.js (96%) rename frontend/src/{organism => container}/FilePreview/FilePreview.stories.js (100%) rename frontend/src/{organism => container}/FilePreview/index.js (100%) rename frontend/src/{organism => container}/FileUploader/FileUploader.js (100%) rename frontend/src/{organism => container}/FileUploader/FileUploader.stories.js (100%) rename frontend/src/{organism => container}/FileUploader/index.js (100%) rename frontend/src/{organism => container}/ImgPreview/ImgPreview.js (96%) rename frontend/src/{organism => container}/ImgPreview/ImgPreview.stories.js (100%) rename frontend/src/{organism => container}/ImgPreview/index.js (100%) rename frontend/src/{organism/messageEditor => container/MessageEditor}/MessageEditor.js (93%) create mode 100644 frontend/src/container/MessageEditor/index.js rename frontend/src/{organism => container/Modal}/CreateChannelModal/CreateChannelModal.js (88%) rename frontend/src/{organism => container/Modal}/CreateChannelModal/CreateChannelModal.stories.js (100%) rename frontend/src/{organism => container/Modal}/CreateChannelModal/index.js (100%) rename frontend/src/{organism => container/Modal}/InviteUserToChannelModal/InviteUserToChannelModal.js (88%) rename frontend/src/{organism => container/Modal}/InviteUserToChannelModal/index.js (100%) rename frontend/src/{organism => container/Modal}/InviteWorkspaceModal/InviteWorkspaceModal.js (81%) rename frontend/src/{organism => container/Modal}/InviteWorkspaceModal/InviteWorkspaceModal.stories.js (100%) rename frontend/src/{organism => container/Modal}/InviteWorkspaceModal/index.js (100%) rename frontend/src/{organism => container}/MyWorkspaceSection/MyWorkspaceSection.js (86%) rename frontend/src/{organism => container}/MyWorkspaceSection/MyWorkspaceSection.stories.js (100%) rename frontend/src/{organism => container}/MyWorkspaceSection/index.js (100%) rename frontend/src/{atom => container}/SearchUserCard/SearchUserCard.js (95%) rename frontend/src/{atom => container}/SearchUserCard/index.js (100%) rename frontend/src/{organism => container}/SectionLabel/SectionLabel.js (94%) rename frontend/src/{organism => container}/SectionLabel/SectionLabel.stories.js (100%) rename frontend/src/{organism => container}/SectionLabel/index.js (100%) rename frontend/src/{atom => container}/ThreadReactionCard/ThreadReactionCard.js (91%) rename frontend/src/{atom => container}/ThreadReactionCard/index.js (100%) rename frontend/src/{organism => container}/ThreadReactionList/ThreadReactionList.js (82%) delete mode 100644 frontend/src/organism/index.js rename frontend/src/{atom => presenter}/Button/AddButton/AddButton.js (100%) rename frontend/src/{atom => presenter}/Button/AddButton/Addbutton.stories.js (100%) rename frontend/src/{atom => presenter}/Button/AddButton/index.js (100%) rename frontend/src/{atom => presenter}/Button/Button.js (100%) rename frontend/src/{atom => presenter}/Button/Button.stories.js (100%) rename frontend/src/{atom => presenter}/Button/ToggleButton/ToggleButton.js (100%) rename frontend/src/{atom => presenter}/Button/ToggleButton/ToggleButton.stories.js (100%) rename frontend/src/{atom => presenter}/Button/ToggleButton/index.js (100%) rename frontend/src/{atom => presenter}/Button/index.js (100%) rename frontend/src/{atom => presenter}/ChannelCard/ChannelCard.js (100%) rename frontend/src/{atom => presenter}/ChannelCard/ChannelCard.stories.js (100%) rename frontend/src/{atom => presenter}/ChannelCard/index.js (100%) rename frontend/src/{atom => presenter}/ChannelListHeader/ChannelListHeader.js (87%) rename frontend/src/{atom => presenter}/ChannelListHeader/index.js (100%) rename frontend/src/{atom => presenter}/ChannelMemberThumbnail/ChannelMemberThumbnail.js (100%) rename frontend/src/{atom => presenter}/ChannelMemberThumbnail/index.js (100%) rename frontend/src/{atom => presenter}/ChannelPinBtn/ChannelPinBtn.js (100%) rename frontend/src/{atom => presenter}/ChannelPinBtn/index.js (100%) rename frontend/src/{atom => presenter}/ChannelTopicBtn/ChannelTopicBtn.js (100%) rename frontend/src/{atom => presenter}/ChannelTopicBtn/index.js (100%) rename frontend/src/{atom => presenter}/ChatContent/ChatContent.js (100%) rename frontend/src/{atom => presenter}/ChatContent/index.js (100%) rename frontend/src/{atom => presenter}/Description/Description.js (100%) rename frontend/src/{atom => presenter}/Description/Description.stories.js (100%) rename frontend/src/{atom => presenter}/Description/index.js (100%) rename frontend/src/{atom => presenter}/DirectMessageCard/DirectMessageCard.js (100%) rename frontend/src/{atom => presenter}/DirectMessageCard/DirectMessageCard.stories.js (100%) rename frontend/src/{atom => presenter}/DirectMessageCard/index.js (100%) rename frontend/src/{atom => presenter}/EmojiModal/EmojiModal.js (100%) rename frontend/src/{atom => presenter}/EmojiModal/index.js (100%) rename frontend/src/{atom => presenter}/GlobalStyle/GlobalStyle.js (100%) rename frontend/src/{atom => presenter}/GlobalStyle/index.js (100%) rename frontend/src/{atom => presenter}/Icon/Icon.js (100%) rename frontend/src/{atom => presenter}/Icon/Icon.stories.js (100%) rename frontend/src/{atom => presenter}/Icon/index.js (100%) rename frontend/src/{atom => presenter}/Input/Input.js (100%) rename frontend/src/{atom => presenter}/Input/Input.stories.js (100%) rename frontend/src/{atom => presenter}/Input/index.js (100%) rename frontend/src/{atom => presenter}/LoginButton/LoginButton.js (100%) rename frontend/src/{atom => presenter}/LoginButton/LoginButton.stories.js (100%) rename frontend/src/{atom => presenter}/LoginButton/index.js (100%) rename frontend/src/{atom => presenter}/MainDescription/MainDescription.js (100%) rename frontend/src/{atom => presenter}/MainDescription/MainDescription.stories.js (100%) rename frontend/src/{atom => presenter}/MainDescription/index.js (100%) rename frontend/src/{atom => presenter}/Modal/Modal.js (100%) rename frontend/src/{atom => presenter}/Modal/index.js (100%) rename frontend/src/{organism => presenter}/ModalInputSection/ModalInputSection.js (100%) rename frontend/src/{organism => presenter}/ModalInputSection/ModalInputSection.stories.js (88%) rename frontend/src/{organism => presenter}/ModalInputSection/index.js (100%) rename frontend/src/{organism => presenter}/MyWorkspace/MyWorkspace.js (88%) rename frontend/src/{organism => presenter}/MyWorkspace/MyWorkspace.stories.js (100%) rename frontend/src/{organism => presenter}/MyWorkspace/index.js (100%) rename frontend/src/{organism => presenter}/NewWorkspaceSection/NewWorkspaceSection.js (76%) rename frontend/src/{organism => presenter}/NewWorkspaceSection/NewWorkspaceSection.stories.js (100%) rename frontend/src/{organism => presenter}/NewWorkspaceSection/index.js (100%) rename frontend/src/{organism => presenter}/SearchUserList/SearchUserList.js (94%) rename frontend/src/{organism => presenter}/SearchUserList/index.js (100%) rename frontend/src/{atom => presenter}/SelectedUserCard/SelectedUserCard.js (100%) rename frontend/src/{atom => presenter}/SelectedUserCard/index.js (100%) rename frontend/src/{organism => presenter}/SelectedUserList/SelectedUserList.js (92%) rename frontend/src/{organism => presenter}/SelectedUserList/index.js (100%) rename frontend/src/{atom => presenter}/SideMenuCard/SideMenuCard.js (75%) rename frontend/src/{atom => presenter}/SideMenuCard/index.js (100%) rename frontend/src/{organism => presenter}/SideMenuList/SideMenuList.js (94%) rename frontend/src/{organism => presenter}/SideMenuList/index.js (100%) rename frontend/src/{atom => presenter}/SlackImage/SlackImage.js (100%) rename frontend/src/{atom => presenter}/SlackImage/SlackImage.stories.js (100%) rename frontend/src/{atom => presenter}/SlackImage/index.js (100%) create mode 100644 frontend/src/presenter/ThreadReactionList/ThreadReactionList.js rename frontend/src/{organism => presenter}/ThreadReactionList/index.js (100%) rename frontend/src/{atom => presenter}/Title/Title.js (100%) rename frontend/src/{atom => presenter}/Title/Title.stories.js (100%) rename frontend/src/{atom => presenter}/Title/index.js (100%) rename frontend/src/{atom => presenter}/UserActive/UserActive.js (100%) rename frontend/src/{atom => presenter}/UserActive/UserActive.stories.js (100%) rename frontend/src/{atom => presenter}/UserActive/index.js (100%) rename frontend/src/{atom => presenter}/UserProfileImg/UserProfileImg.js (100%) rename frontend/src/{atom => presenter}/UserProfileImg/UserProfileImg.stories.js (100%) rename frontend/src/{atom => presenter}/UserProfileImg/index.js (100%) delete mode 100644 frontend/src/stories/Button.js delete mode 100644 frontend/src/stories/Button.stories.js delete mode 100644 frontend/src/stories/Header.js delete mode 100644 frontend/src/stories/Header.stories.js delete mode 100644 frontend/src/stories/Introduction.stories.mdx delete mode 100644 frontend/src/stories/Page.js delete mode 100644 frontend/src/stories/Page.stories.js delete mode 100644 frontend/src/stories/assets/code-brackets.svg delete mode 100644 frontend/src/stories/assets/colors.svg delete mode 100644 frontend/src/stories/assets/comments.svg delete mode 100644 frontend/src/stories/assets/direction.svg delete mode 100644 frontend/src/stories/assets/flow.svg delete mode 100644 frontend/src/stories/assets/plugin.svg delete mode 100644 frontend/src/stories/assets/repo.svg delete mode 100644 frontend/src/stories/assets/stackalt.svg delete mode 100644 frontend/src/stories/button.css delete mode 100644 frontend/src/stories/header.css delete mode 100644 frontend/src/stories/page.css diff --git a/frontend/.storybook/preview.js b/frontend/.storybook/preview.js index 53f3601e..32ea0760 100644 --- a/frontend/.storybook/preview.js +++ b/frontend/.storybook/preview.js @@ -1,5 +1,5 @@ import { addDecorator } from '@storybook/react' -import GlobalStyle from '../src/atom/GlobalStyle/GlobalStyle' +import GlobalStyle from '../src/presenter/GlobalStyle/GlobalStyle' import { RecoilRoot } from 'recoil' import { MemoryRouter } from 'react-router-dom' export const parameters = { diff --git a/frontend/src/atom/ChannelCard/hash.svg b/frontend/src/atom/ChannelCard/hash.svg deleted file mode 100644 index 718dbf99..00000000 --- a/frontend/src/atom/ChannelCard/hash.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - Svg Vector Icons : http://www.onlinewebfonts.com/icon - - \ No newline at end of file diff --git a/frontend/src/atom/ChannelCard/locker.svg b/frontend/src/atom/ChannelCard/locker.svg deleted file mode 100644 index 49c955c6..00000000 --- a/frontend/src/atom/ChannelCard/locker.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - -Created by potrace 1.15, written by Peter Selinger 2001-2017 - - - - - diff --git a/frontend/src/organism/ActionBar/ActionBar.js b/frontend/src/container/ActionBar/ActionBar.js similarity index 90% rename from frontend/src/organism/ActionBar/ActionBar.js rename to frontend/src/container/ActionBar/ActionBar.js index 5d1f047c..e33ce0f3 100644 --- a/frontend/src/organism/ActionBar/ActionBar.js +++ b/frontend/src/container/ActionBar/ActionBar.js @@ -1,7 +1,7 @@ import React from 'react' import styled from 'styled-components' -import EmojiModal from '../../atom/EmojiModal' -import Icon from '../../atom/Icon' +import EmojiModal from '../../presenter/EmojiModal' +import Icon from '../../presenter/Icon' import { COLOR } from '../../constant/style' import { SMILE, @@ -12,10 +12,10 @@ import { } from '../../constant/icon' import calcEmojiModalLocation from '../../util/calculateEmojiModalLocation' import { modalRecoil } from '../../store' -import { useRecoilState } from 'recoil' +import { useSetRecoilState } from 'recoil' -function ActionBar({ setOpenModal, chatId, updateReactionHandler }) { - const [modal, setModal] = useRecoilState(modalRecoil) +function ActionBar({ setOpenModal, updateReactionHandler }) { + const setModal = useSetRecoilState(modalRecoil) const closeHandler = () => { setOpenModal(false) diff --git a/frontend/src/organism/ActionBar/index.js b/frontend/src/container/ActionBar/index.js similarity index 100% rename from frontend/src/organism/ActionBar/index.js rename to frontend/src/container/ActionBar/index.js diff --git a/frontend/src/atom/AddReactionButton/AddReactionButton.js b/frontend/src/container/AddReactionButton/AddReactionButton.js similarity index 83% rename from frontend/src/atom/AddReactionButton/AddReactionButton.js rename to frontend/src/container/AddReactionButton/AddReactionButton.js index d881f4a6..35b60a8d 100644 --- a/frontend/src/atom/AddReactionButton/AddReactionButton.js +++ b/frontend/src/container/AddReactionButton/AddReactionButton.js @@ -1,16 +1,16 @@ import React from 'react' -import styled, { css } from 'styled-components' -import { useRecoilState } from 'recoil' +import styled from 'styled-components' +import { useSetRecoilState } from 'recoil' import { modalRecoil } from '../../store' -import EmojiModal from '../EmojiModal' +import EmojiModal from '../../presenter/EmojiModal' -import Icon from '../Icon' +import Icon from '../../presenter/Icon' import { PLUS, SMILE } from '../../constant/icon' import { COLOR } from '../../constant/style' import calcEmojiModalLocation from '../../util/calculateEmojiModalLocation' function AddReactionButton({ updateReactionHandler }) { - const [modal, setModal] = useRecoilState(modalRecoil) + const setModal = useSetRecoilState(modalRecoil) const closeHandler = () => { setModal(null) diff --git a/frontend/src/atom/AddReactionButton/index.js b/frontend/src/container/AddReactionButton/index.js similarity index 100% rename from frontend/src/atom/AddReactionButton/index.js rename to frontend/src/container/AddReactionButton/index.js diff --git a/frontend/src/organism/ChannelHeader/ChannelHeader.js b/frontend/src/container/ChannelHeader/ChannelHeader.js similarity index 87% rename from frontend/src/organism/ChannelHeader/ChannelHeader.js rename to frontend/src/container/ChannelHeader/ChannelHeader.js index 7e0bfeef..7753403c 100644 --- a/frontend/src/organism/ChannelHeader/ChannelHeader.js +++ b/frontend/src/container/ChannelHeader/ChannelHeader.js @@ -1,16 +1,16 @@ -import React, { useEffect } from 'react' +import React from 'react' import styled from 'styled-components' -import { useSetRecoilState, useRecoilValue } from 'recoil' +import { useSetRecoilState } from 'recoil' -import Icon from '../../atom/Icon' +import Icon from '../../presenter/Icon' import { ADDUSER, INFOCIRCLE } from '../../constant/icon' -import ChannelCard from '../../atom/ChannelCard' -import ChannelStarBtn from '../../atom/ChannelStarBtn' -import ChannelPinBtn from '../../atom/ChannelPinBtn' -import ChannelTopicBtn from '../../atom/ChannelTopicBtn' -import ChannelMemberThumbnail from '../../atom/ChannelMemberThumbnail' +import ChannelCard from '../../presenter/ChannelCard' +import ChannelStarBtn from '../ChannelStarBtn' +import ChannelPinBtn from '../../presenter/ChannelPinBtn' +import ChannelTopicBtn from '../../presenter/ChannelTopicBtn' +import ChannelMemberThumbnail from '../../presenter/ChannelMemberThumbnail' import { modalRecoil } from '../../store' -import InviteUserToChannelModal from '../InviteUserToChannelModal' +import InviteUserToChannelModal from '../Modal/InviteUserToChannelModal' import { COLOR } from '../../constant/style' import useChannelInfo from '../../hooks/useChannelInfo' import { isEmpty } from '../../util' diff --git a/frontend/src/organism/ChannelHeader/index.js b/frontend/src/container/ChannelHeader/index.js similarity index 100% rename from frontend/src/organism/ChannelHeader/index.js rename to frontend/src/container/ChannelHeader/index.js diff --git a/frontend/src/organism/ChannelList/ChannelList.js b/frontend/src/container/ChannelList/ChannelList.js similarity index 97% rename from frontend/src/organism/ChannelList/ChannelList.js rename to frontend/src/container/ChannelList/ChannelList.js index f0f2aa34..b5888214 100644 --- a/frontend/src/organism/ChannelList/ChannelList.js +++ b/frontend/src/container/ChannelList/ChannelList.js @@ -3,7 +3,7 @@ import { useHistory } from 'react-router' import styled from 'styled-components' import { toast } from 'react-toastify' import SectionLabel from '../SectionLabel' -import SideMenuList from '../SideMenuList' +import SideMenuList from '../../presenter/SideMenuList' import { useRecoilValue } from 'recoil' import { isEmpty } from '../../util' import { workspaceRecoil } from '../../store' diff --git a/frontend/src/organism/ChannelList/index.js b/frontend/src/container/ChannelList/index.js similarity index 100% rename from frontend/src/organism/ChannelList/index.js rename to frontend/src/container/ChannelList/index.js diff --git a/frontend/src/atom/ChannelStarBtn/ChannelStarBtn.js b/frontend/src/container/ChannelStarBtn/ChannelStarBtn.js similarity index 93% rename from frontend/src/atom/ChannelStarBtn/ChannelStarBtn.js rename to frontend/src/container/ChannelStarBtn/ChannelStarBtn.js index c333ee85..b33cc373 100644 --- a/frontend/src/atom/ChannelStarBtn/ChannelStarBtn.js +++ b/frontend/src/container/ChannelStarBtn/ChannelStarBtn.js @@ -5,17 +5,17 @@ import styled from 'styled-components' import { toast } from 'react-toastify' import { COLOR } from '../../constant/style' -import Icon from '../Icon' +import Icon from '../../presenter/Icon' import { workspaceRecoil } from '../../store' import { STAR, COLOREDSTAR } from '../../constant/icon' -import { atom, useRecoilState, useRecoilValue } from 'recoil' +import { useRecoilValue } from 'recoil' import { isEmpty } from '../../util' import useChannelList from '../../hooks/useChannelList' function ChannelStarBtn({ channel }) { const section = channel.sectionName const { channelId } = useParams() - const [Channels, setChannels] = useChannelList() + const [, setChannels] = useChannelList() const [sectionInfo, setSectionInfo] = useState(section) const workspaceUserInfo = useRecoilValue(workspaceRecoil) diff --git a/frontend/src/atom/ChannelStarBtn/ChannelStarBtn.stories.js b/frontend/src/container/ChannelStarBtn/ChannelStarBtn.stories.js similarity index 100% rename from frontend/src/atom/ChannelStarBtn/ChannelStarBtn.stories.js rename to frontend/src/container/ChannelStarBtn/ChannelStarBtn.stories.js diff --git a/frontend/src/atom/ChannelStarBtn/index.js b/frontend/src/container/ChannelStarBtn/index.js similarity index 100% rename from frontend/src/atom/ChannelStarBtn/index.js rename to frontend/src/container/ChannelStarBtn/index.js diff --git a/frontend/src/organism/ChatMessage/ChatMessage.js b/frontend/src/container/ChatMessage/ChatMessage.js similarity index 94% rename from frontend/src/organism/ChatMessage/ChatMessage.js rename to frontend/src/container/ChatMessage/ChatMessage.js index 5924e19e..0f0e828c 100644 --- a/frontend/src/organism/ChatMessage/ChatMessage.js +++ b/frontend/src/container/ChatMessage/ChatMessage.js @@ -1,8 +1,8 @@ import React, { useState, forwardRef } from 'react' import styled from 'styled-components' -import UserProfileImg from '../../atom/UserProfileImg' -import ChatContent from '../../atom/ChatContent' -import ThreadReactionList from '../ThreadReactionList' +import UserProfileImg from '../../presenter/UserProfileImg' +import ChatContent from '../../presenter/ChatContent' +import ThreadReactionList from '../../presenter/ThreadReactionList' import ActionBar from '../ActionBar' import { SIZE, COLOR } from '../../constant/style' import { workspaceRecoil, socketRecoil } from '../../store' @@ -36,7 +36,7 @@ const ChatMessage = forwardRef( const updateReactionHandler = emoji => { let done = false - reactions.map((reaction, idx) => { + reactions.map(reaction => { if (reaction.emoji === emoji.native || reaction.emoji === emoji) { if (reaction.set) { updateReaction({ diff --git a/frontend/src/organism/ChatMessage/ChatMessage.stories.js b/frontend/src/container/ChatMessage/ChatMessage.stories.js similarity index 100% rename from frontend/src/organism/ChatMessage/ChatMessage.stories.js rename to frontend/src/container/ChatMessage/ChatMessage.stories.js diff --git a/frontend/src/organism/ChatMessage/index.js b/frontend/src/container/ChatMessage/index.js similarity index 100% rename from frontend/src/organism/ChatMessage/index.js rename to frontend/src/container/ChatMessage/index.js diff --git a/frontend/src/organism/ChatRoom/ChatRoom.js b/frontend/src/container/ChatRoom/ChatRoom.js similarity index 94% rename from frontend/src/organism/ChatRoom/ChatRoom.js rename to frontend/src/container/ChatRoom/ChatRoom.js index 5cec90e9..90869da6 100644 --- a/frontend/src/organism/ChatRoom/ChatRoom.js +++ b/frontend/src/container/ChatRoom/ChatRoom.js @@ -5,12 +5,8 @@ import { 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, - currentChannelInfoRecoil, -} from '../../store' +import MessageEditor from '../MessageEditor/MessageEditor' +import { workspaceRecoil, socketRecoil } from '../../store' import ChannelHeader from '../ChannelHeader' const ChatRoom = () => { @@ -19,7 +15,6 @@ const ChatRoom = () => { const messageEndRef = useRef(null) const [targetState, setTargetState] = useState() const workspaceUserInfo = useRecoilValue(workspaceRecoil) - const channelInfo = useRecoilValue(currentChannelInfoRecoil) const { workspaceId, channelId } = useParams() const socket = useRecoilValue(socketRecoil) const [messages, setMessages] = useState([]) @@ -64,10 +59,10 @@ const ChatRoom = () => { if (reaction.type === false) { return messages } - return messages.map((message, idx) => { + return messages.map(message => { if (message._id === reaction.chatId) { message.reactions && - message.reactions.map((item, idx) => { + message.reactions.map(item => { if (item.emoji === reaction.emoji) { if (reaction.type) { item.users = [ diff --git a/frontend/src/organism/ChatRoom/index.js b/frontend/src/container/ChatRoom/index.js similarity index 100% rename from frontend/src/organism/ChatRoom/index.js rename to frontend/src/container/ChatRoom/index.js diff --git a/frontend/src/organism/CreateWorkspaceInitChannel/CreateWorkspaceInitChannel.js b/frontend/src/container/CreateWorkspaceInitChannel/CreateWorkspaceInitChannel.js similarity index 90% rename from frontend/src/organism/CreateWorkspaceInitChannel/CreateWorkspaceInitChannel.js rename to frontend/src/container/CreateWorkspaceInitChannel/CreateWorkspaceInitChannel.js index eabc55f4..b500bcf9 100644 --- a/frontend/src/organism/CreateWorkspaceInitChannel/CreateWorkspaceInitChannel.js +++ b/frontend/src/container/CreateWorkspaceInitChannel/CreateWorkspaceInitChannel.js @@ -1,12 +1,12 @@ -import React, { useState, useRef } from 'react' +import React, { useState } from 'react' import styled from 'styled-components' -import Description from '../../atom/Description/Description' -import MainDescription from '../../atom/MainDescription' -import Button from '../../atom/Button' +import Description from '../../presenter/Description/Description' +import MainDescription from '../../presenter/MainDescription' +import Button from '../../presenter/Button' import request from '../../util/request' import { useHistory } from 'react-router' import { toast } from 'react-toastify' -import Input from '../../atom/Input' +import Input from '../../presenter/Input' import { COLOR } from '../../constant/style' const MAX_CHANNEL_NAME = 80 diff --git a/frontend/src/organism/CreateWorkspaceInitChannel/CreateWorkspaceInitChannel.stories.js b/frontend/src/container/CreateWorkspaceInitChannel/CreateWorkspaceInitChannel.stories.js similarity index 100% rename from frontend/src/organism/CreateWorkspaceInitChannel/CreateWorkspaceInitChannel.stories.js rename to frontend/src/container/CreateWorkspaceInitChannel/CreateWorkspaceInitChannel.stories.js diff --git a/frontend/src/organism/CreateWorkspaceInitChannel/index.js b/frontend/src/container/CreateWorkspaceInitChannel/index.js similarity index 100% rename from frontend/src/organism/CreateWorkspaceInitChannel/index.js rename to frontend/src/container/CreateWorkspaceInitChannel/index.js diff --git a/frontend/src/organism/CreateWorkspaceName/CreateWorkspaceName.js b/frontend/src/container/CreateWorkspaceName/CreateWorkspaceName.js similarity index 92% rename from frontend/src/organism/CreateWorkspaceName/CreateWorkspaceName.js rename to frontend/src/container/CreateWorkspaceName/CreateWorkspaceName.js index 44c81e01..397fdd68 100644 --- a/frontend/src/organism/CreateWorkspaceName/CreateWorkspaceName.js +++ b/frontend/src/container/CreateWorkspaceName/CreateWorkspaceName.js @@ -1,9 +1,9 @@ import React, { useState, useRef } from 'react' import styled from 'styled-components' -import Description from '../../atom/Description/Description' -import MainDescription from '../../atom/MainDescription' -import Button from '../../atom/Button/' -import Input from '../../atom/Input' +import Description from '../../presenter/Description/Description' +import MainDescription from '../../presenter/MainDescription' +import Button from '../../presenter/Button/' +import Input from '../../presenter/Input' import { debounce } from '../../util' import { checkDuplicateWorkspaceName } from '../../api/createWorkspace' import { COLOR } from '../../constant/style' diff --git a/frontend/src/organism/CreateWorkspaceName/CreateWorkspaceName.stories.js b/frontend/src/container/CreateWorkspaceName/CreateWorkspaceName.stories.js similarity index 100% rename from frontend/src/organism/CreateWorkspaceName/CreateWorkspaceName.stories.js rename to frontend/src/container/CreateWorkspaceName/CreateWorkspaceName.stories.js diff --git a/frontend/src/organism/CreateWorkspaceName/index.js b/frontend/src/container/CreateWorkspaceName/index.js similarity index 100% rename from frontend/src/organism/CreateWorkspaceName/index.js rename to frontend/src/container/CreateWorkspaceName/index.js diff --git a/frontend/src/organism/FilePreview/FilePreview.js b/frontend/src/container/FilePreview/FilePreview.js similarity index 96% rename from frontend/src/organism/FilePreview/FilePreview.js rename to frontend/src/container/FilePreview/FilePreview.js index 2292c62f..1630b7b2 100644 --- a/frontend/src/organism/FilePreview/FilePreview.js +++ b/frontend/src/container/FilePreview/FilePreview.js @@ -1,10 +1,10 @@ import React, { useState, useEffect } from 'react' import styled from 'styled-components' import request from '../../util/request' -import Icon from '../../atom/Icon' +import Icon from '../../presenter/Icon' import { CLOSE, FILE } from '../../constant/icon' import { COLOR } from '../../constant/style' -import Button from '../../atom/Button/Button' +import Button from '../../presenter/Button/Button' function FilePreview({ type, fileId, setIsRender }) { const [fileData, setFileData] = useState({}) diff --git a/frontend/src/organism/FilePreview/FilePreview.stories.js b/frontend/src/container/FilePreview/FilePreview.stories.js similarity index 100% rename from frontend/src/organism/FilePreview/FilePreview.stories.js rename to frontend/src/container/FilePreview/FilePreview.stories.js diff --git a/frontend/src/organism/FilePreview/index.js b/frontend/src/container/FilePreview/index.js similarity index 100% rename from frontend/src/organism/FilePreview/index.js rename to frontend/src/container/FilePreview/index.js diff --git a/frontend/src/organism/FileUploader/FileUploader.js b/frontend/src/container/FileUploader/FileUploader.js similarity index 100% rename from frontend/src/organism/FileUploader/FileUploader.js rename to frontend/src/container/FileUploader/FileUploader.js diff --git a/frontend/src/organism/FileUploader/FileUploader.stories.js b/frontend/src/container/FileUploader/FileUploader.stories.js similarity index 100% rename from frontend/src/organism/FileUploader/FileUploader.stories.js rename to frontend/src/container/FileUploader/FileUploader.stories.js diff --git a/frontend/src/organism/FileUploader/index.js b/frontend/src/container/FileUploader/index.js similarity index 100% rename from frontend/src/organism/FileUploader/index.js rename to frontend/src/container/FileUploader/index.js diff --git a/frontend/src/organism/ImgPreview/ImgPreview.js b/frontend/src/container/ImgPreview/ImgPreview.js similarity index 96% rename from frontend/src/organism/ImgPreview/ImgPreview.js rename to frontend/src/container/ImgPreview/ImgPreview.js index b479de4d..358c701e 100644 --- a/frontend/src/organism/ImgPreview/ImgPreview.js +++ b/frontend/src/container/ImgPreview/ImgPreview.js @@ -1,10 +1,10 @@ import React, { useState, useEffect } from 'react' import styled from 'styled-components' import request from '../../util/request' -import Icon from '../../atom/Icon' +import Icon from '../../presenter/Icon' import { CLOSE } from '../../constant/icon' import { COLOR } from '../../constant/style' -import Button from '../../atom/Button' +import Button from '../../presenter/Button' function ImgPreview({ type, fileId, setIsRender }) { const [fileData, setFileData] = useState({}) diff --git a/frontend/src/organism/ImgPreview/ImgPreview.stories.js b/frontend/src/container/ImgPreview/ImgPreview.stories.js similarity index 100% rename from frontend/src/organism/ImgPreview/ImgPreview.stories.js rename to frontend/src/container/ImgPreview/ImgPreview.stories.js diff --git a/frontend/src/organism/ImgPreview/index.js b/frontend/src/container/ImgPreview/index.js similarity index 100% rename from frontend/src/organism/ImgPreview/index.js rename to frontend/src/container/ImgPreview/index.js diff --git a/frontend/src/organism/messageEditor/MessageEditor.js b/frontend/src/container/MessageEditor/MessageEditor.js similarity index 93% rename from frontend/src/organism/messageEditor/MessageEditor.js rename to frontend/src/container/MessageEditor/MessageEditor.js index 6516a811..f2de8fcf 100644 --- a/frontend/src/organism/messageEditor/MessageEditor.js +++ b/frontend/src/container/MessageEditor/MessageEditor.js @@ -1,6 +1,6 @@ import React, { useState } from 'react' -import Input from '../../atom/Input' +import Input from '../../presenter/Input' function MessageEditor({ channelTitle, sendMessage }) { const [message, setMessage] = useState('') diff --git a/frontend/src/container/MessageEditor/index.js b/frontend/src/container/MessageEditor/index.js new file mode 100644 index 00000000..41b1b24b --- /dev/null +++ b/frontend/src/container/MessageEditor/index.js @@ -0,0 +1 @@ +export { default } from './MessageEditor' diff --git a/frontend/src/organism/CreateChannelModal/CreateChannelModal.js b/frontend/src/container/Modal/CreateChannelModal/CreateChannelModal.js similarity index 88% rename from frontend/src/organism/CreateChannelModal/CreateChannelModal.js rename to frontend/src/container/Modal/CreateChannelModal/CreateChannelModal.js index 4ac2bcf0..99ffbc96 100644 --- a/frontend/src/organism/CreateChannelModal/CreateChannelModal.js +++ b/frontend/src/container/Modal/CreateChannelModal/CreateChannelModal.js @@ -3,19 +3,19 @@ import styled from 'styled-components' import { useRecoilValue } from 'recoil' import { useHistory, useParams } from 'react-router-dom' -import { workspaceRecoil } from '../../store' -import Modal from '../../atom/Modal' -import Title from '../../atom/Title' -import Icon from '../../atom/Icon' -import Input from '../../atom/Input' -import Button from '../../atom/Button' -import { CLOSE, HASHTAG, LOCK } from '../../constant/icon' -import { debounce } from '../../util' -import { COLOR } from '../../constant/style' -import ModalInputSection from '../ModalInputSection' -import { checkDuplicateChannelName, createChannel } from '../../api/channel' -import ToggleButton from '../../atom/Button/ToggleButton' -import useChannelList from '../../hooks/useChannelList' +import { workspaceRecoil } from '../../../store' +import Modal from '../../../presenter/Modal' +import Title from '../../../presenter/Title' +import Icon from '../../../presenter/Icon' +import Input from '../../../presenter/Input' +import Button from '../../../presenter/Button' +import { CLOSE, HASHTAG, LOCK } from '../../../constant/icon' +import { debounce } from '../../../util' +import { COLOR } from '../../../constant/style' +import ModalInputSection from '../../../presenter/ModalInputSection' +import { checkDuplicateChannelName, createChannel } from '../../../api/channel' +import ToggleButton from '../../../presenter/Button/ToggleButton' +import useChannelList from '../../../hooks/useChannelList' const MAX_CHANNEL_NAME = 80 const MAX_CHANNEL_DESCRIPTION = 250 diff --git a/frontend/src/organism/CreateChannelModal/CreateChannelModal.stories.js b/frontend/src/container/Modal/CreateChannelModal/CreateChannelModal.stories.js similarity index 100% rename from frontend/src/organism/CreateChannelModal/CreateChannelModal.stories.js rename to frontend/src/container/Modal/CreateChannelModal/CreateChannelModal.stories.js diff --git a/frontend/src/organism/CreateChannelModal/index.js b/frontend/src/container/Modal/CreateChannelModal/index.js similarity index 100% rename from frontend/src/organism/CreateChannelModal/index.js rename to frontend/src/container/Modal/CreateChannelModal/index.js diff --git a/frontend/src/organism/InviteUserToChannelModal/InviteUserToChannelModal.js b/frontend/src/container/Modal/InviteUserToChannelModal/InviteUserToChannelModal.js similarity index 88% rename from frontend/src/organism/InviteUserToChannelModal/InviteUserToChannelModal.js rename to frontend/src/container/Modal/InviteUserToChannelModal/InviteUserToChannelModal.js index 7e91ddd2..d4a106c4 100644 --- a/frontend/src/organism/InviteUserToChannelModal/InviteUserToChannelModal.js +++ b/frontend/src/container/Modal/InviteUserToChannelModal/InviteUserToChannelModal.js @@ -1,20 +1,20 @@ import React, { useState, useRef } from 'react' import styled from 'styled-components' import { useSetRecoilState, useRecoilValue } from 'recoil' -import { modalRecoil, socketRecoil } from '../../store' +import { modalRecoil, socketRecoil } from '../../../store' import { useParams } from 'react-router-dom' -import Button from '../../atom/Button' -import Icon from '../../atom/Icon' -import { LOCK, HASHTAG, CLOSE } from '../../constant/icon' -import { debounce } from '../../util' -import request from '../../util/request' -import Modal from '../../atom/Modal' -import SearchUserList from '../SearchUserList' -import SelectedUserList from '../SelectedUserList' -import useChannelInfo from '../../hooks/useChannelInfo' +import Button from '../../../presenter/Button' +import Icon from '../../../presenter/Icon' +import { LOCK, HASHTAG, CLOSE } from '../../../constant/icon' +import { debounce } from '../../../util' +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' function InviteUserToChannelModal({ handleClose }) { - const [channelInfo, updateChannelInfo] = useChannelInfo() + const [channelInfo] = useChannelInfo() const setModal = useSetRecoilState(modalRecoil) const socket = useRecoilValue(socketRecoil) const [searchResult, setSearchResult] = useState(null) diff --git a/frontend/src/organism/InviteUserToChannelModal/index.js b/frontend/src/container/Modal/InviteUserToChannelModal/index.js similarity index 100% rename from frontend/src/organism/InviteUserToChannelModal/index.js rename to frontend/src/container/Modal/InviteUserToChannelModal/index.js diff --git a/frontend/src/organism/InviteWorkspaceModal/InviteWorkspaceModal.js b/frontend/src/container/Modal/InviteWorkspaceModal/InviteWorkspaceModal.js similarity index 81% rename from frontend/src/organism/InviteWorkspaceModal/InviteWorkspaceModal.js rename to frontend/src/container/Modal/InviteWorkspaceModal/InviteWorkspaceModal.js index 4754067e..623720eb 100644 --- a/frontend/src/organism/InviteWorkspaceModal/InviteWorkspaceModal.js +++ b/frontend/src/container/Modal/InviteWorkspaceModal/InviteWorkspaceModal.js @@ -1,12 +1,12 @@ import React, { useEffect, useState } from 'react' -import Modal from '../../atom/Modal' +import Modal from '../../../presenter/Modal' import styled from 'styled-components' -import Button from '../../atom/Button' -import Title from '../../atom/Title' -import Icon from '../../atom/Icon' -import { CLOSE } from '../../constant/icon' -import { COLOR } from '../../constant/style' -import { inviteWorkspace } from '../../api/workspace' +import Button from '../../../presenter/Button' +import Title from '../../../presenter/Title' +import Icon from '../../../presenter/Icon' +import { CLOSE } from '../../../constant/icon' +import { COLOR } from '../../../constant/style' +import { inviteWorkspace } from '../../../api/workspace' import { useParams } from 'react-router-dom' const baseURL = diff --git a/frontend/src/organism/InviteWorkspaceModal/InviteWorkspaceModal.stories.js b/frontend/src/container/Modal/InviteWorkspaceModal/InviteWorkspaceModal.stories.js similarity index 100% rename from frontend/src/organism/InviteWorkspaceModal/InviteWorkspaceModal.stories.js rename to frontend/src/container/Modal/InviteWorkspaceModal/InviteWorkspaceModal.stories.js diff --git a/frontend/src/organism/InviteWorkspaceModal/index.js b/frontend/src/container/Modal/InviteWorkspaceModal/index.js similarity index 100% rename from frontend/src/organism/InviteWorkspaceModal/index.js rename to frontend/src/container/Modal/InviteWorkspaceModal/index.js diff --git a/frontend/src/organism/MyWorkspaceSection/MyWorkspaceSection.js b/frontend/src/container/MyWorkspaceSection/MyWorkspaceSection.js similarity index 86% rename from frontend/src/organism/MyWorkspaceSection/MyWorkspaceSection.js rename to frontend/src/container/MyWorkspaceSection/MyWorkspaceSection.js index 1e4323ee..3aab3b21 100644 --- a/frontend/src/organism/MyWorkspaceSection/MyWorkspaceSection.js +++ b/frontend/src/container/MyWorkspaceSection/MyWorkspaceSection.js @@ -1,6 +1,6 @@ import React, { useEffect, useState } from 'react' -import MainDescription from '../../atom/MainDescription/MainDescription' -import MyWorkspace from '../MyWorkspace' +import MainDescription from '../../presenter/MainDescription/MainDescription' +import MyWorkspace from '../../presenter/MyWorkspace' import request from '../../util/request' const MyWorkspaceSection = () => { diff --git a/frontend/src/organism/MyWorkspaceSection/MyWorkspaceSection.stories.js b/frontend/src/container/MyWorkspaceSection/MyWorkspaceSection.stories.js similarity index 100% rename from frontend/src/organism/MyWorkspaceSection/MyWorkspaceSection.stories.js rename to frontend/src/container/MyWorkspaceSection/MyWorkspaceSection.stories.js diff --git a/frontend/src/organism/MyWorkspaceSection/index.js b/frontend/src/container/MyWorkspaceSection/index.js similarity index 100% rename from frontend/src/organism/MyWorkspaceSection/index.js rename to frontend/src/container/MyWorkspaceSection/index.js diff --git a/frontend/src/atom/SearchUserCard/SearchUserCard.js b/frontend/src/container/SearchUserCard/SearchUserCard.js similarity index 95% rename from frontend/src/atom/SearchUserCard/SearchUserCard.js rename to frontend/src/container/SearchUserCard/SearchUserCard.js index 79750e45..b55f9758 100644 --- a/frontend/src/atom/SearchUserCard/SearchUserCard.js +++ b/frontend/src/container/SearchUserCard/SearchUserCard.js @@ -1,9 +1,8 @@ import React, { useState, useEffect } from 'react' import styled, { css } from 'styled-components' -import UserActive from '../UserActive' -import UserProfileImg from '../UserProfileImg' +import UserActive from '../../presenter/UserActive' import { COLOR } from '../../constant/style' -import Icon from '../Icon' +import Icon from '../../presenter/Icon' import { CHECK } from '../../constant/icon' function SearchUserCard({ userInfo, state, setState }) { diff --git a/frontend/src/atom/SearchUserCard/index.js b/frontend/src/container/SearchUserCard/index.js similarity index 100% rename from frontend/src/atom/SearchUserCard/index.js rename to frontend/src/container/SearchUserCard/index.js diff --git a/frontend/src/organism/SectionLabel/SectionLabel.js b/frontend/src/container/SectionLabel/SectionLabel.js similarity index 94% rename from frontend/src/organism/SectionLabel/SectionLabel.js rename to frontend/src/container/SectionLabel/SectionLabel.js index 6a16dcc9..e9a81aa8 100644 --- a/frontend/src/organism/SectionLabel/SectionLabel.js +++ b/frontend/src/container/SectionLabel/SectionLabel.js @@ -1,17 +1,17 @@ import React, { useState } from 'react' import { useParams } from 'react-router' import styled, { keyframes, css } from 'styled-components' -import Icon from '../../atom/Icon' +import Icon from '../../presenter/Icon' import { ELLIPSISV, PLUS } from '../../constant/icon' import { Link } from 'react-router-dom' import { COLOR, SIZE } from '../../constant/style' -import ChannelCard from '../../atom/ChannelCard' -import DirectMessageCard from '../../atom/DirectMessageCard' -import AddButton from '../../atom/Button/AddButton' +import ChannelCard from '../../presenter/ChannelCard' +import DirectMessageCard from '../../presenter/DirectMessageCard' +import AddButton from '../../presenter/Button/AddButton' import { modalRecoil } from '../../store' import { useSetRecoilState } from 'recoil' -import InviteWorkspaceModal from '../InviteWorkspaceModal/' -import CreateChannelModal from '../CreateChannelModal/CreateChannelModal' +import InviteWorkspaceModal from '../Modal/InviteWorkspaceModal' +import CreateChannelModal from '../Modal/CreateChannelModal/CreateChannelModal' function SectionLabel(props) { const [isOpen, setIsOpen] = useState(true) diff --git a/frontend/src/organism/SectionLabel/SectionLabel.stories.js b/frontend/src/container/SectionLabel/SectionLabel.stories.js similarity index 100% rename from frontend/src/organism/SectionLabel/SectionLabel.stories.js rename to frontend/src/container/SectionLabel/SectionLabel.stories.js diff --git a/frontend/src/organism/SectionLabel/index.js b/frontend/src/container/SectionLabel/index.js similarity index 100% rename from frontend/src/organism/SectionLabel/index.js rename to frontend/src/container/SectionLabel/index.js diff --git a/frontend/src/atom/ThreadReactionCard/ThreadReactionCard.js b/frontend/src/container/ThreadReactionCard/ThreadReactionCard.js similarity index 91% rename from frontend/src/atom/ThreadReactionCard/ThreadReactionCard.js rename to frontend/src/container/ThreadReactionCard/ThreadReactionCard.js index fbc3f650..05432e5b 100644 --- a/frontend/src/atom/ThreadReactionCard/ThreadReactionCard.js +++ b/frontend/src/container/ThreadReactionCard/ThreadReactionCard.js @@ -1,11 +1,11 @@ import React, { useState, useEffect } from 'react' import styled from 'styled-components' -import { useRecoilState } from 'recoil' +import { useRecoilValue } from 'recoil' import { workspaceRecoil } from '../../store' import { COLOR } from '../../constant/style' -function ThreadReactionCard({ reaction, chatId, updateReactionHandler }) { - const [userInfo, setUserInfo] = useRecoilState(workspaceRecoil) +function ThreadReactionCard({ reaction, updateReactionHandler }) { + const userInfo = useRecoilValue(workspaceRecoil) const [myReaction, setMyReaction] = useState(false) useEffect(() => { diff --git a/frontend/src/atom/ThreadReactionCard/index.js b/frontend/src/container/ThreadReactionCard/index.js similarity index 100% rename from frontend/src/atom/ThreadReactionCard/index.js rename to frontend/src/container/ThreadReactionCard/index.js diff --git a/frontend/src/organism/ThreadReactionList/ThreadReactionList.js b/frontend/src/container/ThreadReactionList/ThreadReactionList.js similarity index 82% rename from frontend/src/organism/ThreadReactionList/ThreadReactionList.js rename to frontend/src/container/ThreadReactionList/ThreadReactionList.js index d1c3bc85..57f471f7 100644 --- a/frontend/src/organism/ThreadReactionList/ThreadReactionList.js +++ b/frontend/src/container/ThreadReactionList/ThreadReactionList.js @@ -1,7 +1,7 @@ import React from 'react' -import styled, { css } from 'styled-components' -import ThreadReactionCard from '../../atom/ThreadReactionCard' -import AddReactionButton from '../../atom/AddReactionButton' +import styled from 'styled-components' +import ThreadReactionCard from '../../container/ThreadReactionCard' +import AddReactionButton from '../AddReactionButton' function ThreadReactionList({ reactions, chatId, updateReactionHandler }) { const renderReactionCard = reactions.map((reaction, idx) => { diff --git a/frontend/src/organism/index.js b/frontend/src/organism/index.js deleted file mode 100644 index e69de29b..00000000 diff --git a/frontend/src/page/WorkspacePage/WorkspacePage.js b/frontend/src/page/WorkspacePage/WorkspacePage.js index a77f8e0d..d4c5f165 100644 --- a/frontend/src/page/WorkspacePage/WorkspacePage.js +++ b/frontend/src/page/WorkspacePage/WorkspacePage.js @@ -5,11 +5,11 @@ import { useRecoilValue } from 'recoil' import { modalRecoil } from '../../store' import { throttle } from '../../util' -import ChannelList from '../../organism/ChannelList' -import ChannelListHeader from '../../atom/ChannelListHeader' -import ChatRoom from '../../organism/ChatRoom' +import ChannelList from '../../container/ChannelList' +import ChannelListHeader from '../../presenter/ChannelListHeader' +import ChatRoom from '../../container/ChatRoom' import { COLOR } from '../../constant/style' -import Icon from '../../atom/Icon' +import Icon from '../../presenter/Icon' import { TOOLS } from '../../constant/icon' import useWorkspace from '../../hooks/useWorkspace' import useSocket from '../../hooks/useSocket' @@ -18,7 +18,7 @@ function WorkspacePage() { const { channelId } = useParams() const [lineWidth, setLineWidth] = useState(20) const modal = useRecoilValue(modalRecoil) - useWorkspace() + const [workspaceUserInfo] = useWorkspace() useSocket() const moveLine = e => { if (e.pageX === 0) return false @@ -57,7 +57,7 @@ function WorkspacePage() { - + diff --git a/frontend/src/page/createWorkspace/CreateWorkspace.js b/frontend/src/page/createWorkspace/CreateWorkspace.js index 17ddaf4b..ff1ad332 100644 --- a/frontend/src/page/createWorkspace/CreateWorkspace.js +++ b/frontend/src/page/createWorkspace/CreateWorkspace.js @@ -1,7 +1,7 @@ import React, { useState } from 'react' import styled from 'styled-components' -import CreateWorkspacecName from '../../organism/CreateWorkspaceName' -import CreateWorkspaceInitChannel from '../../organism/CreateWorkspaceInitChannel' +import CreateWorkspacecName from '../../container/CreateWorkspaceName' +import CreateWorkspaceInitChannel from '../../container/CreateWorkspaceInitChannel' const CreateWorkspace = () => { const [workspaceName, setWorkspaceName] = useState('') diff --git a/frontend/src/page/login/Login.js b/frontend/src/page/login/Login.js index 6861aeb5..05baf8b6 100644 --- a/frontend/src/page/login/Login.js +++ b/frontend/src/page/login/Login.js @@ -1,10 +1,10 @@ import React from 'react' import { useHistory } from 'react-router-dom' -import LoginButton from '../../atom/LoginButton/LoginButton' +import LoginButton from '../../presenter/LoginButton/LoginButton' import { GITHUB } from '../../constant/icon' -import Icon from '../../atom/Icon' +import Icon from '../../presenter/Icon' import styled from 'styled-components' -import SlackIcon from '../../atom/SlackImage' +import SlackIcon from '../../presenter/SlackImage' const baseURL = process.env.NODE_ENV === 'development' diff --git a/frontend/src/page/selectWorkspace/SelectWorkspace.js b/frontend/src/page/selectWorkspace/SelectWorkspace.js index bd0ab7fe..53771a42 100644 --- a/frontend/src/page/selectWorkspace/SelectWorkspace.js +++ b/frontend/src/page/selectWorkspace/SelectWorkspace.js @@ -1,7 +1,7 @@ import React from 'react' import styled from 'styled-components' -import NewWorkspaceSection from '../../organism/NewWorkspaceSection' -import MyWorkspaceSection from '../../organism/MyWorkspaceSection' +import NewWorkspaceSection from '../../presenter/NewWorkspaceSection' +import MyWorkspaceSection from '../../container/MyWorkspaceSection' const SelectWorkspace = () => { return ( diff --git a/frontend/src/atom/Button/AddButton/AddButton.js b/frontend/src/presenter/Button/AddButton/AddButton.js similarity index 100% rename from frontend/src/atom/Button/AddButton/AddButton.js rename to frontend/src/presenter/Button/AddButton/AddButton.js diff --git a/frontend/src/atom/Button/AddButton/Addbutton.stories.js b/frontend/src/presenter/Button/AddButton/Addbutton.stories.js similarity index 100% rename from frontend/src/atom/Button/AddButton/Addbutton.stories.js rename to frontend/src/presenter/Button/AddButton/Addbutton.stories.js diff --git a/frontend/src/atom/Button/AddButton/index.js b/frontend/src/presenter/Button/AddButton/index.js similarity index 100% rename from frontend/src/atom/Button/AddButton/index.js rename to frontend/src/presenter/Button/AddButton/index.js diff --git a/frontend/src/atom/Button/Button.js b/frontend/src/presenter/Button/Button.js similarity index 100% rename from frontend/src/atom/Button/Button.js rename to frontend/src/presenter/Button/Button.js diff --git a/frontend/src/atom/Button/Button.stories.js b/frontend/src/presenter/Button/Button.stories.js similarity index 100% rename from frontend/src/atom/Button/Button.stories.js rename to frontend/src/presenter/Button/Button.stories.js diff --git a/frontend/src/atom/Button/ToggleButton/ToggleButton.js b/frontend/src/presenter/Button/ToggleButton/ToggleButton.js similarity index 100% rename from frontend/src/atom/Button/ToggleButton/ToggleButton.js rename to frontend/src/presenter/Button/ToggleButton/ToggleButton.js diff --git a/frontend/src/atom/Button/ToggleButton/ToggleButton.stories.js b/frontend/src/presenter/Button/ToggleButton/ToggleButton.stories.js similarity index 100% rename from frontend/src/atom/Button/ToggleButton/ToggleButton.stories.js rename to frontend/src/presenter/Button/ToggleButton/ToggleButton.stories.js diff --git a/frontend/src/atom/Button/ToggleButton/index.js b/frontend/src/presenter/Button/ToggleButton/index.js similarity index 100% rename from frontend/src/atom/Button/ToggleButton/index.js rename to frontend/src/presenter/Button/ToggleButton/index.js diff --git a/frontend/src/atom/Button/index.js b/frontend/src/presenter/Button/index.js similarity index 100% rename from frontend/src/atom/Button/index.js rename to frontend/src/presenter/Button/index.js diff --git a/frontend/src/atom/ChannelCard/ChannelCard.js b/frontend/src/presenter/ChannelCard/ChannelCard.js similarity index 100% rename from frontend/src/atom/ChannelCard/ChannelCard.js rename to frontend/src/presenter/ChannelCard/ChannelCard.js diff --git a/frontend/src/atom/ChannelCard/ChannelCard.stories.js b/frontend/src/presenter/ChannelCard/ChannelCard.stories.js similarity index 100% rename from frontend/src/atom/ChannelCard/ChannelCard.stories.js rename to frontend/src/presenter/ChannelCard/ChannelCard.stories.js diff --git a/frontend/src/atom/ChannelCard/index.js b/frontend/src/presenter/ChannelCard/index.js similarity index 100% rename from frontend/src/atom/ChannelCard/index.js rename to frontend/src/presenter/ChannelCard/index.js diff --git a/frontend/src/atom/ChannelListHeader/ChannelListHeader.js b/frontend/src/presenter/ChannelListHeader/ChannelListHeader.js similarity index 87% rename from frontend/src/atom/ChannelListHeader/ChannelListHeader.js rename to frontend/src/presenter/ChannelListHeader/ChannelListHeader.js index 82fe9325..1b4d95ee 100644 --- a/frontend/src/atom/ChannelListHeader/ChannelListHeader.js +++ b/frontend/src/presenter/ChannelListHeader/ChannelListHeader.js @@ -1,16 +1,12 @@ import React from 'react' -import { useRecoilValue } from 'recoil' import styled from 'styled-components' -import { workspaceRecoil } from '../../store' -import Icon from '../../atom/Icon' +import Icon from '../Icon' import { EDIT, CHEVRONDOWN } from '../../constant/icon' const ICON_SIZE = 13 const ICON_COLOR = '#19181F' -function ChannelListHeader() { - const workspaceUserInfo = useRecoilValue(workspaceRecoil) - +function ChannelListHeader({ workspaceUserInfo }) { return workspaceUserInfo !== null ? ( diff --git a/frontend/src/atom/ChannelListHeader/index.js b/frontend/src/presenter/ChannelListHeader/index.js similarity index 100% rename from frontend/src/atom/ChannelListHeader/index.js rename to frontend/src/presenter/ChannelListHeader/index.js diff --git a/frontend/src/atom/ChannelMemberThumbnail/ChannelMemberThumbnail.js b/frontend/src/presenter/ChannelMemberThumbnail/ChannelMemberThumbnail.js similarity index 100% rename from frontend/src/atom/ChannelMemberThumbnail/ChannelMemberThumbnail.js rename to frontend/src/presenter/ChannelMemberThumbnail/ChannelMemberThumbnail.js diff --git a/frontend/src/atom/ChannelMemberThumbnail/index.js b/frontend/src/presenter/ChannelMemberThumbnail/index.js similarity index 100% rename from frontend/src/atom/ChannelMemberThumbnail/index.js rename to frontend/src/presenter/ChannelMemberThumbnail/index.js diff --git a/frontend/src/atom/ChannelPinBtn/ChannelPinBtn.js b/frontend/src/presenter/ChannelPinBtn/ChannelPinBtn.js similarity index 100% rename from frontend/src/atom/ChannelPinBtn/ChannelPinBtn.js rename to frontend/src/presenter/ChannelPinBtn/ChannelPinBtn.js diff --git a/frontend/src/atom/ChannelPinBtn/index.js b/frontend/src/presenter/ChannelPinBtn/index.js similarity index 100% rename from frontend/src/atom/ChannelPinBtn/index.js rename to frontend/src/presenter/ChannelPinBtn/index.js diff --git a/frontend/src/atom/ChannelTopicBtn/ChannelTopicBtn.js b/frontend/src/presenter/ChannelTopicBtn/ChannelTopicBtn.js similarity index 100% rename from frontend/src/atom/ChannelTopicBtn/ChannelTopicBtn.js rename to frontend/src/presenter/ChannelTopicBtn/ChannelTopicBtn.js diff --git a/frontend/src/atom/ChannelTopicBtn/index.js b/frontend/src/presenter/ChannelTopicBtn/index.js similarity index 100% rename from frontend/src/atom/ChannelTopicBtn/index.js rename to frontend/src/presenter/ChannelTopicBtn/index.js diff --git a/frontend/src/atom/ChatContent/ChatContent.js b/frontend/src/presenter/ChatContent/ChatContent.js similarity index 100% rename from frontend/src/atom/ChatContent/ChatContent.js rename to frontend/src/presenter/ChatContent/ChatContent.js diff --git a/frontend/src/atom/ChatContent/index.js b/frontend/src/presenter/ChatContent/index.js similarity index 100% rename from frontend/src/atom/ChatContent/index.js rename to frontend/src/presenter/ChatContent/index.js diff --git a/frontend/src/atom/Description/Description.js b/frontend/src/presenter/Description/Description.js similarity index 100% rename from frontend/src/atom/Description/Description.js rename to frontend/src/presenter/Description/Description.js diff --git a/frontend/src/atom/Description/Description.stories.js b/frontend/src/presenter/Description/Description.stories.js similarity index 100% rename from frontend/src/atom/Description/Description.stories.js rename to frontend/src/presenter/Description/Description.stories.js diff --git a/frontend/src/atom/Description/index.js b/frontend/src/presenter/Description/index.js similarity index 100% rename from frontend/src/atom/Description/index.js rename to frontend/src/presenter/Description/index.js diff --git a/frontend/src/atom/DirectMessageCard/DirectMessageCard.js b/frontend/src/presenter/DirectMessageCard/DirectMessageCard.js similarity index 100% rename from frontend/src/atom/DirectMessageCard/DirectMessageCard.js rename to frontend/src/presenter/DirectMessageCard/DirectMessageCard.js diff --git a/frontend/src/atom/DirectMessageCard/DirectMessageCard.stories.js b/frontend/src/presenter/DirectMessageCard/DirectMessageCard.stories.js similarity index 100% rename from frontend/src/atom/DirectMessageCard/DirectMessageCard.stories.js rename to frontend/src/presenter/DirectMessageCard/DirectMessageCard.stories.js diff --git a/frontend/src/atom/DirectMessageCard/index.js b/frontend/src/presenter/DirectMessageCard/index.js similarity index 100% rename from frontend/src/atom/DirectMessageCard/index.js rename to frontend/src/presenter/DirectMessageCard/index.js diff --git a/frontend/src/atom/EmojiModal/EmojiModal.js b/frontend/src/presenter/EmojiModal/EmojiModal.js similarity index 100% rename from frontend/src/atom/EmojiModal/EmojiModal.js rename to frontend/src/presenter/EmojiModal/EmojiModal.js diff --git a/frontend/src/atom/EmojiModal/index.js b/frontend/src/presenter/EmojiModal/index.js similarity index 100% rename from frontend/src/atom/EmojiModal/index.js rename to frontend/src/presenter/EmojiModal/index.js diff --git a/frontend/src/atom/GlobalStyle/GlobalStyle.js b/frontend/src/presenter/GlobalStyle/GlobalStyle.js similarity index 100% rename from frontend/src/atom/GlobalStyle/GlobalStyle.js rename to frontend/src/presenter/GlobalStyle/GlobalStyle.js diff --git a/frontend/src/atom/GlobalStyle/index.js b/frontend/src/presenter/GlobalStyle/index.js similarity index 100% rename from frontend/src/atom/GlobalStyle/index.js rename to frontend/src/presenter/GlobalStyle/index.js diff --git a/frontend/src/atom/Icon/Icon.js b/frontend/src/presenter/Icon/Icon.js similarity index 100% rename from frontend/src/atom/Icon/Icon.js rename to frontend/src/presenter/Icon/Icon.js diff --git a/frontend/src/atom/Icon/Icon.stories.js b/frontend/src/presenter/Icon/Icon.stories.js similarity index 100% rename from frontend/src/atom/Icon/Icon.stories.js rename to frontend/src/presenter/Icon/Icon.stories.js diff --git a/frontend/src/atom/Icon/index.js b/frontend/src/presenter/Icon/index.js similarity index 100% rename from frontend/src/atom/Icon/index.js rename to frontend/src/presenter/Icon/index.js diff --git a/frontend/src/atom/Input/Input.js b/frontend/src/presenter/Input/Input.js similarity index 100% rename from frontend/src/atom/Input/Input.js rename to frontend/src/presenter/Input/Input.js diff --git a/frontend/src/atom/Input/Input.stories.js b/frontend/src/presenter/Input/Input.stories.js similarity index 100% rename from frontend/src/atom/Input/Input.stories.js rename to frontend/src/presenter/Input/Input.stories.js diff --git a/frontend/src/atom/Input/index.js b/frontend/src/presenter/Input/index.js similarity index 100% rename from frontend/src/atom/Input/index.js rename to frontend/src/presenter/Input/index.js diff --git a/frontend/src/atom/LoginButton/LoginButton.js b/frontend/src/presenter/LoginButton/LoginButton.js similarity index 100% rename from frontend/src/atom/LoginButton/LoginButton.js rename to frontend/src/presenter/LoginButton/LoginButton.js diff --git a/frontend/src/atom/LoginButton/LoginButton.stories.js b/frontend/src/presenter/LoginButton/LoginButton.stories.js similarity index 100% rename from frontend/src/atom/LoginButton/LoginButton.stories.js rename to frontend/src/presenter/LoginButton/LoginButton.stories.js diff --git a/frontend/src/atom/LoginButton/index.js b/frontend/src/presenter/LoginButton/index.js similarity index 100% rename from frontend/src/atom/LoginButton/index.js rename to frontend/src/presenter/LoginButton/index.js diff --git a/frontend/src/atom/MainDescription/MainDescription.js b/frontend/src/presenter/MainDescription/MainDescription.js similarity index 100% rename from frontend/src/atom/MainDescription/MainDescription.js rename to frontend/src/presenter/MainDescription/MainDescription.js diff --git a/frontend/src/atom/MainDescription/MainDescription.stories.js b/frontend/src/presenter/MainDescription/MainDescription.stories.js similarity index 100% rename from frontend/src/atom/MainDescription/MainDescription.stories.js rename to frontend/src/presenter/MainDescription/MainDescription.stories.js diff --git a/frontend/src/atom/MainDescription/index.js b/frontend/src/presenter/MainDescription/index.js similarity index 100% rename from frontend/src/atom/MainDescription/index.js rename to frontend/src/presenter/MainDescription/index.js diff --git a/frontend/src/atom/Modal/Modal.js b/frontend/src/presenter/Modal/Modal.js similarity index 100% rename from frontend/src/atom/Modal/Modal.js rename to frontend/src/presenter/Modal/Modal.js diff --git a/frontend/src/atom/Modal/index.js b/frontend/src/presenter/Modal/index.js similarity index 100% rename from frontend/src/atom/Modal/index.js rename to frontend/src/presenter/Modal/index.js diff --git a/frontend/src/organism/ModalInputSection/ModalInputSection.js b/frontend/src/presenter/ModalInputSection/ModalInputSection.js similarity index 100% rename from frontend/src/organism/ModalInputSection/ModalInputSection.js rename to frontend/src/presenter/ModalInputSection/ModalInputSection.js diff --git a/frontend/src/organism/ModalInputSection/ModalInputSection.stories.js b/frontend/src/presenter/ModalInputSection/ModalInputSection.stories.js similarity index 88% rename from frontend/src/organism/ModalInputSection/ModalInputSection.stories.js rename to frontend/src/presenter/ModalInputSection/ModalInputSection.stories.js index 21964e6a..3098370a 100644 --- a/frontend/src/organism/ModalInputSection/ModalInputSection.stories.js +++ b/frontend/src/presenter/ModalInputSection/ModalInputSection.stories.js @@ -1,8 +1,8 @@ import React from 'react' import ModalInputSection from './ModalInputSection' -import Input from '../../atom/Input' +import Input from '../Input' import { action } from '@storybook/addon-actions' -import Icon from '../../atom/Icon' +import Icon from '../Icon' import { HASHTAG } from '../../constant/icon' export default { title: 'Organism/Modal/ModalInputSection', diff --git a/frontend/src/organism/ModalInputSection/index.js b/frontend/src/presenter/ModalInputSection/index.js similarity index 100% rename from frontend/src/organism/ModalInputSection/index.js rename to frontend/src/presenter/ModalInputSection/index.js diff --git a/frontend/src/organism/MyWorkspace/MyWorkspace.js b/frontend/src/presenter/MyWorkspace/MyWorkspace.js similarity index 88% rename from frontend/src/organism/MyWorkspace/MyWorkspace.js rename to frontend/src/presenter/MyWorkspace/MyWorkspace.js index 02123663..66685664 100644 --- a/frontend/src/organism/MyWorkspace/MyWorkspace.js +++ b/frontend/src/presenter/MyWorkspace/MyWorkspace.js @@ -1,9 +1,9 @@ import React from 'react' -import Icon from '../../atom/Icon' +import Icon from '../Icon' import { ADDRESSBOOK } from '../../constant/icon' import styled from 'styled-components' -import MainDescription from '../../atom/MainDescription' -import Button from '../../atom/Button' +import MainDescription from '../MainDescription' +import Button from '../Button' import { COLOR } from '../../constant/style' import { useHistory } from 'react-router-dom' diff --git a/frontend/src/organism/MyWorkspace/MyWorkspace.stories.js b/frontend/src/presenter/MyWorkspace/MyWorkspace.stories.js similarity index 100% rename from frontend/src/organism/MyWorkspace/MyWorkspace.stories.js rename to frontend/src/presenter/MyWorkspace/MyWorkspace.stories.js diff --git a/frontend/src/organism/MyWorkspace/index.js b/frontend/src/presenter/MyWorkspace/index.js similarity index 100% rename from frontend/src/organism/MyWorkspace/index.js rename to frontend/src/presenter/MyWorkspace/index.js diff --git a/frontend/src/organism/NewWorkspaceSection/NewWorkspaceSection.js b/frontend/src/presenter/NewWorkspaceSection/NewWorkspaceSection.js similarity index 76% rename from frontend/src/organism/NewWorkspaceSection/NewWorkspaceSection.js rename to frontend/src/presenter/NewWorkspaceSection/NewWorkspaceSection.js index d62015e7..a24423c7 100644 --- a/frontend/src/organism/NewWorkspaceSection/NewWorkspaceSection.js +++ b/frontend/src/presenter/NewWorkspaceSection/NewWorkspaceSection.js @@ -1,11 +1,11 @@ import React from 'react' import { useHistory } from 'react-router-dom' import styled from 'styled-components' -import Button from '../../atom/Button' -import Description from '../../atom/Description/Description' -import MainDescription from '../../atom/MainDescription/MainDescription' +import Button from '../Button' +import Description from '../Description/Description' +import MainDescription from '../MainDescription/MainDescription' -const NewWorkspaceSection = ({ handler }) => { +const NewWorkspaceSection = () => { const history = useHistory() return ( <> diff --git a/frontend/src/organism/NewWorkspaceSection/NewWorkspaceSection.stories.js b/frontend/src/presenter/NewWorkspaceSection/NewWorkspaceSection.stories.js similarity index 100% rename from frontend/src/organism/NewWorkspaceSection/NewWorkspaceSection.stories.js rename to frontend/src/presenter/NewWorkspaceSection/NewWorkspaceSection.stories.js diff --git a/frontend/src/organism/NewWorkspaceSection/index.js b/frontend/src/presenter/NewWorkspaceSection/index.js similarity index 100% rename from frontend/src/organism/NewWorkspaceSection/index.js rename to frontend/src/presenter/NewWorkspaceSection/index.js diff --git a/frontend/src/organism/SearchUserList/SearchUserList.js b/frontend/src/presenter/SearchUserList/SearchUserList.js similarity index 94% rename from frontend/src/organism/SearchUserList/SearchUserList.js rename to frontend/src/presenter/SearchUserList/SearchUserList.js index 312dbd7c..a2d27079 100644 --- a/frontend/src/organism/SearchUserList/SearchUserList.js +++ b/frontend/src/presenter/SearchUserList/SearchUserList.js @@ -1,6 +1,6 @@ import React from 'react' import styled from 'styled-components' -import SearchUserCard from '../../atom/SearchUserCard' +import SearchUserCard from '../../container/SearchUserCard' import { COLOR } from '../../constant/style' function SearchUserList({ searchResult, state, setState }) { diff --git a/frontend/src/organism/SearchUserList/index.js b/frontend/src/presenter/SearchUserList/index.js similarity index 100% rename from frontend/src/organism/SearchUserList/index.js rename to frontend/src/presenter/SearchUserList/index.js diff --git a/frontend/src/atom/SelectedUserCard/SelectedUserCard.js b/frontend/src/presenter/SelectedUserCard/SelectedUserCard.js similarity index 100% rename from frontend/src/atom/SelectedUserCard/SelectedUserCard.js rename to frontend/src/presenter/SelectedUserCard/SelectedUserCard.js diff --git a/frontend/src/atom/SelectedUserCard/index.js b/frontend/src/presenter/SelectedUserCard/index.js similarity index 100% rename from frontend/src/atom/SelectedUserCard/index.js rename to frontend/src/presenter/SelectedUserCard/index.js diff --git a/frontend/src/organism/SelectedUserList/SelectedUserList.js b/frontend/src/presenter/SelectedUserList/SelectedUserList.js similarity index 92% rename from frontend/src/organism/SelectedUserList/SelectedUserList.js rename to frontend/src/presenter/SelectedUserList/SelectedUserList.js index 09be345a..672bb445 100644 --- a/frontend/src/organism/SelectedUserList/SelectedUserList.js +++ b/frontend/src/presenter/SelectedUserList/SelectedUserList.js @@ -1,6 +1,6 @@ import React from 'react' import styled from 'styled-components' -import SelectedUserCard from '../../atom/SelectedUserCard' +import SelectedUserCard from '../SelectedUserCard' function SelectedUserList({ inviteUserList, setInviteUserList }) { const renderSelectedUserList = diff --git a/frontend/src/organism/SelectedUserList/index.js b/frontend/src/presenter/SelectedUserList/index.js similarity index 100% rename from frontend/src/organism/SelectedUserList/index.js rename to frontend/src/presenter/SelectedUserList/index.js diff --git a/frontend/src/atom/SideMenuCard/SideMenuCard.js b/frontend/src/presenter/SideMenuCard/SideMenuCard.js similarity index 75% rename from frontend/src/atom/SideMenuCard/SideMenuCard.js rename to frontend/src/presenter/SideMenuCard/SideMenuCard.js index 5cee8645..d02b3aae 100644 --- a/frontend/src/atom/SideMenuCard/SideMenuCard.js +++ b/frontend/src/presenter/SideMenuCard/SideMenuCard.js @@ -1,28 +1,21 @@ -import React, { useState, useEffect } from 'react' +import React from 'react' import { useParams } from 'react-router' import { Link } from 'react-router-dom' import styled from 'styled-components' import Icon from '../Icon' -function SideMenuCard({ icon, color, children, linkUrl }) { +function SideMenuCard({ icon, children, linkUrl }) { const parameters = useParams() - const [currentChannel, setCurrentChannel] = useState(false) - - useEffect(() => { - if (parameters.channelId === linkUrl) { - setCurrentChannel(true) - } else { - setCurrentChannel(false) - } - }, [parameters]) - return ( - + - + - + {children} diff --git a/frontend/src/atom/SideMenuCard/index.js b/frontend/src/presenter/SideMenuCard/index.js similarity index 100% rename from frontend/src/atom/SideMenuCard/index.js rename to frontend/src/presenter/SideMenuCard/index.js diff --git a/frontend/src/organism/SideMenuList/SideMenuList.js b/frontend/src/presenter/SideMenuList/SideMenuList.js similarity index 94% rename from frontend/src/organism/SideMenuList/SideMenuList.js rename to frontend/src/presenter/SideMenuList/SideMenuList.js index 1149b966..f7d0d47e 100644 --- a/frontend/src/organism/SideMenuList/SideMenuList.js +++ b/frontend/src/presenter/SideMenuList/SideMenuList.js @@ -1,6 +1,6 @@ import React from 'react' import styled from 'styled-components' -import SideMenuCard from '../../atom/SideMenuCard' +import SideMenuCard from '../SideMenuCard' import { COMMENTDOTS, COMMENTS, diff --git a/frontend/src/organism/SideMenuList/index.js b/frontend/src/presenter/SideMenuList/index.js similarity index 100% rename from frontend/src/organism/SideMenuList/index.js rename to frontend/src/presenter/SideMenuList/index.js diff --git a/frontend/src/atom/SlackImage/SlackImage.js b/frontend/src/presenter/SlackImage/SlackImage.js similarity index 100% rename from frontend/src/atom/SlackImage/SlackImage.js rename to frontend/src/presenter/SlackImage/SlackImage.js diff --git a/frontend/src/atom/SlackImage/SlackImage.stories.js b/frontend/src/presenter/SlackImage/SlackImage.stories.js similarity index 100% rename from frontend/src/atom/SlackImage/SlackImage.stories.js rename to frontend/src/presenter/SlackImage/SlackImage.stories.js diff --git a/frontend/src/atom/SlackImage/index.js b/frontend/src/presenter/SlackImage/index.js similarity index 100% rename from frontend/src/atom/SlackImage/index.js rename to frontend/src/presenter/SlackImage/index.js diff --git a/frontend/src/presenter/ThreadReactionList/ThreadReactionList.js b/frontend/src/presenter/ThreadReactionList/ThreadReactionList.js new file mode 100644 index 00000000..427d7b46 --- /dev/null +++ b/frontend/src/presenter/ThreadReactionList/ThreadReactionList.js @@ -0,0 +1,41 @@ +import React from 'react' +import styled from 'styled-components' +import ThreadReactionCard from '../../container/ThreadReactionCard' +import AddReactionButton from '../../container/AddReactionButton' + +function ThreadReactionList({ reactions, chatId, updateReactionHandler }) { + const renderReactionCard = reactions.map((reaction, idx) => { + return ( + + ) + }) + + return ( + + {renderReactionCard} + + + ) +} + +const ThreadReactionListStyle = styled.div` + width: 100%; + height: 100%; + display: flex; + flex-wrap: wrap; + flex-direction: row; + justify-content: flex-start; + align-items: center; + padding: 0 10px; + border-radius: 5px; +` + +export default ThreadReactionList diff --git a/frontend/src/organism/ThreadReactionList/index.js b/frontend/src/presenter/ThreadReactionList/index.js similarity index 100% rename from frontend/src/organism/ThreadReactionList/index.js rename to frontend/src/presenter/ThreadReactionList/index.js diff --git a/frontend/src/atom/Title/Title.js b/frontend/src/presenter/Title/Title.js similarity index 100% rename from frontend/src/atom/Title/Title.js rename to frontend/src/presenter/Title/Title.js diff --git a/frontend/src/atom/Title/Title.stories.js b/frontend/src/presenter/Title/Title.stories.js similarity index 100% rename from frontend/src/atom/Title/Title.stories.js rename to frontend/src/presenter/Title/Title.stories.js diff --git a/frontend/src/atom/Title/index.js b/frontend/src/presenter/Title/index.js similarity index 100% rename from frontend/src/atom/Title/index.js rename to frontend/src/presenter/Title/index.js diff --git a/frontend/src/atom/UserActive/UserActive.js b/frontend/src/presenter/UserActive/UserActive.js similarity index 100% rename from frontend/src/atom/UserActive/UserActive.js rename to frontend/src/presenter/UserActive/UserActive.js diff --git a/frontend/src/atom/UserActive/UserActive.stories.js b/frontend/src/presenter/UserActive/UserActive.stories.js similarity index 100% rename from frontend/src/atom/UserActive/UserActive.stories.js rename to frontend/src/presenter/UserActive/UserActive.stories.js diff --git a/frontend/src/atom/UserActive/index.js b/frontend/src/presenter/UserActive/index.js similarity index 100% rename from frontend/src/atom/UserActive/index.js rename to frontend/src/presenter/UserActive/index.js diff --git a/frontend/src/atom/UserProfileImg/UserProfileImg.js b/frontend/src/presenter/UserProfileImg/UserProfileImg.js similarity index 100% rename from frontend/src/atom/UserProfileImg/UserProfileImg.js rename to frontend/src/presenter/UserProfileImg/UserProfileImg.js diff --git a/frontend/src/atom/UserProfileImg/UserProfileImg.stories.js b/frontend/src/presenter/UserProfileImg/UserProfileImg.stories.js similarity index 100% rename from frontend/src/atom/UserProfileImg/UserProfileImg.stories.js rename to frontend/src/presenter/UserProfileImg/UserProfileImg.stories.js diff --git a/frontend/src/atom/UserProfileImg/index.js b/frontend/src/presenter/UserProfileImg/index.js similarity index 100% rename from frontend/src/atom/UserProfileImg/index.js rename to frontend/src/presenter/UserProfileImg/index.js diff --git a/frontend/src/stories/Button.js b/frontend/src/stories/Button.js deleted file mode 100644 index 75c2c31a..00000000 --- a/frontend/src/stories/Button.js +++ /dev/null @@ -1,54 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import './button.css' - -/** - * Primary UI component for user interaction - */ -export const Button = ({ primary, backgroundColor, size, label, ...props }) => { - const mode = primary - ? 'storybook-button--primary' - : 'storybook-button--secondary' - return ( - - ) -} - -Button.propTypes = { - /** - * Is this the principal call to action on the page? - */ - primary: PropTypes.bool, - /** - * What background color to use - */ - backgroundColor: PropTypes.string, - /** - * How large should the button be? - */ - size: PropTypes.oneOf(['small', 'medium', 'large']), - /** - * Button contents - */ - label: PropTypes.string.isRequired, - /** - * Optional click handler - */ - onClick: PropTypes.func, -} - -Button.defaultProps = { - backgroundColor: null, - primary: false, - size: 'medium', - onClick: undefined, -} diff --git a/frontend/src/stories/Button.stories.js b/frontend/src/stories/Button.stories.js deleted file mode 100644 index 3d78ceda..00000000 --- a/frontend/src/stories/Button.stories.js +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react' - -import { Button } from './Button' - -export default { - title: 'Example/Button', - component: Button, - argTypes: { - backgroundColor: { control: 'color' }, - }, -} - -const Template = args => + handleFileInput(e)} - > - + > ) } +const StyeldInput = styled.input` + width: 1px; + height: 1px; +` + export default FileUploader diff --git a/frontend/src/container/FileUploader/FileUploader.stories.js b/frontend/src/container/FileUploader/FileUploader.stories.js index b477fa6c..a1ea0831 100644 --- a/frontend/src/container/FileUploader/FileUploader.stories.js +++ b/frontend/src/container/FileUploader/FileUploader.stories.js @@ -1,10 +1,51 @@ -import React from 'react' +import React, { useState, useEffect } from 'react' import { storiesOf } from '@storybook/react' import FileUploader from './FileUploader' +import styled from 'styled-components' +import FilePreview from '../FilePreview' +import ImgPreview from '../ImgPreview' +import { isEmpty, isImage } from '../../util/index' +import { ToastContainer } from 'react-toastify' +import 'react-toastify/dist/ReactToastify.css' const stories = storiesOf('Organism', module) const TestComponent = () => { - return + const [fileData, setFileData] = useState(null) + const [isRender, setIsRender] = useState(false) + + useEffect(() => { + if (!isEmpty(fileData)) setIsRender(true) + }, [fileData]) + + const renderPreview = () => { + return isImage(fileData?.fileType) ? ( + + ) : ( + + ) + } + + return ( + <> + + {isRender && renderPreview()} + + + ) } stories.add('FileUploader', () => ) + +const StyledDiv = styled.div` + min-width: 300px; + min-height: 200px; + background: gray; +` From e183fb1dcb496bc4c275567feb6b83e19680b661 Mon Sep 17 00:00:00 2001 From: rockpell Date: Tue, 15 Dec 2020 22:16:28 +0900 Subject: [PATCH 029/102] Fix: file, image preview fileId error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fileId가 없을 때 생기는 문제 수정 --- frontend/src/container/FilePreview/FilePreview.js | 11 +++++++---- .../container/FileUploader/FileUploader.stories.js | 4 ++-- frontend/src/container/ImgPreview/ImgPreview.js | 13 ++++++++----- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/frontend/src/container/FilePreview/FilePreview.js b/frontend/src/container/FilePreview/FilePreview.js index 1630b7b2..4a00a333 100644 --- a/frontend/src/container/FilePreview/FilePreview.js +++ b/frontend/src/container/FilePreview/FilePreview.js @@ -5,16 +5,19 @@ import Icon from '../../presenter/Icon' import { CLOSE, FILE } from '../../constant/icon' import { COLOR } from '../../constant/style' import Button from '../../presenter/Button/Button' +import { isEmpty } from '../../util' function FilePreview({ type, fileId, setIsRender }) { const [fileData, setFileData] = useState({}) const [isHover, setIsHover] = useState(false) useEffect(() => { - ;(async () => { - const { data } = (await request.GET('/api/file', { fileId })) || {} - setFileData(data?.data) - })() + if (!isEmpty(fileId)) { + ;(async () => { + const { data } = (await request.GET('/api/file', { fileId })) || {} + setFileData(data?.data) + })() + } }, [fileId]) const enterMouseHandle = () => { diff --git a/frontend/src/container/FileUploader/FileUploader.stories.js b/frontend/src/container/FileUploader/FileUploader.stories.js index a1ea0831..8f679b92 100644 --- a/frontend/src/container/FileUploader/FileUploader.stories.js +++ b/frontend/src/container/FileUploader/FileUploader.stories.js @@ -4,7 +4,7 @@ import FileUploader from './FileUploader' import styled from 'styled-components' import FilePreview from '../FilePreview' import ImgPreview from '../ImgPreview' -import { isEmpty, isImage } from '../../util/index' +import { isImage } from '../../util/index' import { ToastContainer } from 'react-toastify' import 'react-toastify/dist/ReactToastify.css' @@ -15,7 +15,7 @@ const TestComponent = () => { const [isRender, setIsRender] = useState(false) useEffect(() => { - if (!isEmpty(fileData)) setIsRender(true) + if (fileData) setIsRender(true) }, [fileData]) const renderPreview = () => { diff --git a/frontend/src/container/ImgPreview/ImgPreview.js b/frontend/src/container/ImgPreview/ImgPreview.js index 358c701e..4e9e57f6 100644 --- a/frontend/src/container/ImgPreview/ImgPreview.js +++ b/frontend/src/container/ImgPreview/ImgPreview.js @@ -5,16 +5,19 @@ import Icon from '../../presenter/Icon' import { CLOSE } from '../../constant/icon' import { COLOR } from '../../constant/style' import Button from '../../presenter/Button' +import { isEmpty } from '../../util' function ImgPreview({ type, fileId, setIsRender }) { const [fileData, setFileData] = useState({}) const [isHover, setIsHover] = useState(false) useEffect(() => { - ;(async () => { - const { data } = (await request.GET('/api/file', { fileId })) || {} - setFileData(data?.data) - })() + if (!isEmpty(fileId)) { + ;(async () => { + const { data } = (await request.GET('/api/file', { fileId })) || {} + setFileData(data?.data) + })() + } }, [fileId]) const enterMouseHandle = () => { @@ -76,7 +79,7 @@ const StyledDiv = styled.div` const StyledImg = styled.img` max-width: ${({ type }) => { - return type === 'input' ? '50px' : '300px' + return type === 'input' ? '80px' : '300px' }}; height: auto; border-radius: 2%; From 6eae0c7140c51c951e081ee7ef1ad2df7e1f4ad8 Mon Sep 17 00:00:00 2001 From: rockpell Date: Tue, 15 Dec 2020 22:17:43 +0900 Subject: [PATCH 030/102] Feat: add fileId, fileType to chat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chat에 fileId, fileType추가 --- backend/chatServer.js | 4 +++- backend/model/Chat.js | 7 +++++++ backend/service/chat.js | 10 ++++++++-- frontend/src/container/ChatRoom/ChatRoom.js | 4 +++- 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/backend/chatServer.js b/backend/chatServer.js index 8b7f91e6..06b716e2 100644 --- a/backend/chatServer.js +++ b/backend/chatServer.js @@ -28,11 +28,13 @@ namespace.on('connection', socket => { ) }) socket.on('new message', async data => { - const { contents, channelId } = data + const { contents, channelId, fileId, fileType } = data const { data: result } = await createChatMessage({ creator: workspaceUserInfoId, channelId, contents, + fileId, + fileType, }) namespace.in(channelId).emit('new message', { message: { ...data, _id: result._id, createdAt: result.createdAt }, diff --git a/backend/model/Chat.js b/backend/model/Chat.js index c138b0cb..6bee4ff4 100644 --- a/backend/model/Chat.js +++ b/backend/model/Chat.js @@ -27,6 +27,13 @@ const chatSchema = mongoose.Schema( type: Boolean, default: false, }, + fileId: { + type: Schema.Types.ObjectId, + ref: 'File', + }, + fileType: { + type: String, + }, }, { timestamps: true }, ) diff --git a/backend/service/chat.js b/backend/service/chat.js index 67e8791f..5f3e9eed 100644 --- a/backend/service/chat.js +++ b/backend/service/chat.js @@ -13,10 +13,16 @@ const getChatMessages = async ({ channelId, currentCursor, fromDate }) => { success: true, } } -const createChatMessage = async ({ channelId, creator, contents }) => { +const createChatMessage = async ({ + channelId, + creator, + contents, + fileId, + fileType, +}) => { verifyRequiredParams(channelId, creator, contents) const result = await dbErrorHandler(() => - Chat.create({ channel: channelId, creator, contents }), + Chat.create({ channel: channelId, creator, contents, fileId, fileType }), ) return { data: result } } diff --git a/frontend/src/container/ChatRoom/ChatRoom.js b/frontend/src/container/ChatRoom/ChatRoom.js index 90869da6..83036975 100644 --- a/frontend/src/container/ChatRoom/ChatRoom.js +++ b/frontend/src/container/ChatRoom/ChatRoom.js @@ -41,10 +41,12 @@ const ChatRoom = () => { targetRef.scrollIntoView() } - const sendMessage = message => { + const sendMessage = (message, fileData) => { const chat = { contents: message, channelId, + fileId: fileData.fileId, + fileType: fileData.fileType, userInfo: { _id: workspaceUserInfo._id, displayName: workspaceUserInfo.displayName, From cb7d59eaa43287daa92a6d201f415940f24807e1 Mon Sep 17 00:00:00 2001 From: rockpell Date: Tue, 15 Dec 2020 22:18:28 +0900 Subject: [PATCH 031/102] Feat: add fileUploader to MessageEditor --- .../container/MessageEditor/MessageEditor.js | 42 +++++++++++++++++-- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/frontend/src/container/MessageEditor/MessageEditor.js b/frontend/src/container/MessageEditor/MessageEditor.js index f2de8fcf..9a9e347a 100644 --- a/frontend/src/container/MessageEditor/MessageEditor.js +++ b/frontend/src/container/MessageEditor/MessageEditor.js @@ -1,18 +1,48 @@ -import React, { useState } from 'react' - +import React, { useState, useEffect } from 'react' import Input from '../../presenter/Input' +import FileUploader from '../FileUploader' +import styled from 'styled-components' +import FilePreview from '../FilePreview' +import ImgPreview from '../ImgPreview' +import { isEmpty, isImage } from '../../util/index' function MessageEditor({ channelTitle, sendMessage }) { const [message, setMessage] = useState('') + const [fileData, setFileData] = useState(null) + const [isRender, setIsRender] = useState(false) + + useEffect(() => { + if (fileData) setIsRender(true) + }, [fileData]) + const handleInput = e => { setMessage(e.target.value) } const handleKey = e => { - if (e.key === 'Enter' && e.target.value) { - sendMessage(message) + if (e.key === 'Enter' && (e.target.value || fileData)) { + sendMessage(message, fileData) setMessage('') + setFileData(null) + setIsRender(false) } } + + const renderPreview = () => { + return isImage(fileData?.fileType) ? ( + + ) : ( + + ) + } + return (
{/* TODO markdown, chat action 적용 필요 */} +
{isRender && renderPreview()}
+
+ +
) } From 081fab8e7a58bf6380dcaad65930f3de454e689d Mon Sep 17 00:00:00 2001 From: jongwon Date: Tue, 15 Dec 2020 22:30:07 +0900 Subject: [PATCH 032/102] Feat: add style to view thread components --- .../src/container/ChatMessage/ChatMessage.js | 31 +++++++++++++------ .../ViewThreadButton/ViewThreadButton.js | 9 ++++-- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/frontend/src/container/ChatMessage/ChatMessage.js b/frontend/src/container/ChatMessage/ChatMessage.js index bf480fa1..97e4cb5c 100644 --- a/frontend/src/container/ChatMessage/ChatMessage.js +++ b/frontend/src/container/ChatMessage/ChatMessage.js @@ -1,14 +1,15 @@ import React, { useState, forwardRef } from 'react' -import { Link, useParams } from 'react-router-dom' +import { NavLink, useParams } from 'react-router-dom' import styled from 'styled-components' +import { useRecoilValue } from 'recoil' import UserProfileImg from '../../presenter/UserProfileImg' import ChatContent from '../../presenter/ChatContent' import ThreadReactionList from '../../presenter/ThreadReactionList' import ActionBar from '../ActionBar' +import ViewThreadButton from '../../presenter/Button/ViewThreadButton' import { isEmpty } from '../../util' import { SIZE, COLOR } from '../../constant/style' import { workspaceRecoil, socketRecoil } from '../../store' -import { useRecoilValue } from 'recoil' const ChatMessage = forwardRef( ( @@ -62,7 +63,7 @@ const ChatMessage = forwardRef( return ( setHover(true)} @@ -80,7 +81,7 @@ const ChatMessage = forwardRef( contents={contents} /> - {/* TODO thread Reaction 구현 */} + {!isEmpty(reactions) && ( )} - {/* TODO view thread reply 구현 */} + {type !== 'reply' && !isEmpty(reply) && ( - - view thread - + + + + + )} - {/* TODO Action bar 구현 */} {(hover || openModal) && ( { /> )), )} - {reply.length} {reply.length === 1 ? 'reply' : 'replies'} + + {reply.length} {reply.length === 1 ? 'reply' : 'replies'} + ) }) @@ -28,5 +31,7 @@ const ViewThreadButton = memo(({ reply }) => { const ViewThreadContainer = styled.div` display: flex; ` -const ProfileArea = styled.div`` +const ReplyCounts = styled.div` + margin-left: 5px; +` export default ViewThreadButton From 780bd72adaf48816f807f4e1d53cded13c67c473 Mon Sep 17 00:00:00 2001 From: rockpell Date: Tue, 15 Dec 2020 22:39:09 +0900 Subject: [PATCH 033/102] Feat: add preview to ChatMessage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ImgPreview, FilePreview 컴포넌트를 ChatMessage에서 렌더링 --- .../src/container/ChatMessage/ChatMessage.js | 37 ++++++++++++++++++- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/frontend/src/container/ChatMessage/ChatMessage.js b/frontend/src/container/ChatMessage/ChatMessage.js index 0f0e828c..a4c4e2db 100644 --- a/frontend/src/container/ChatMessage/ChatMessage.js +++ b/frontend/src/container/ChatMessage/ChatMessage.js @@ -8,10 +8,23 @@ import { SIZE, COLOR } from '../../constant/style' import { workspaceRecoil, socketRecoil } from '../../store' import { useRecoilValue } from 'recoil' import { useParams } from 'react-router-dom' +import { isEmpty, isImage } from '../../util/index' +import ImgPreview from '../ImgPreview' +import FilePreview from '../FilePreview' const ChatMessage = forwardRef( ( - { userInfo, reply, reactions, _id, createdAt, contents, type = 'chat' }, + { + userInfo, + reply, + reactions, + _id, + createdAt, + contents, + type = 'chat', + fileId, + fileType, + }, ref, ) => { const { channelId } = useParams() @@ -59,6 +72,26 @@ const ChatMessage = forwardRef( } } + const renderFilePreview = () => { + if (isEmpty(fileType) || isEmpty(fileId)) { + return + } + return isImage(fileType) ? ( + + ) : ( + + ) + } + + const renderContent = () => { + return ( + <> +
{contents}
+ {renderFilePreview()} + + ) + } + return ( {/* TODO thread Reaction 구현 */} From a43c1c6552f44b147f380193d61f5556e08d0e66 Mon Sep 17 00:00:00 2001 From: rockpell Date: Tue, 15 Dec 2020 23:27:38 +0900 Subject: [PATCH 034/102] Fix: createChatMessage params MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit createChatMessage 파라미터 허용 조건에 fileId와 fileType 추가 --- backend/service/chat.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/service/chat.js b/backend/service/chat.js index 5f3e9eed..34f14323 100644 --- a/backend/service/chat.js +++ b/backend/service/chat.js @@ -20,7 +20,8 @@ const createChatMessage = async ({ fileId, fileType, }) => { - verifyRequiredParams(channelId, creator, contents) + const fileValue = fileId || fileType + verifyRequiredParams(channelId, creator, contents || fileValue) const result = await dbErrorHandler(() => Chat.create({ channel: channelId, creator, contents, fileId, fileType }), ) From 26eb9a1a92e3a68192bf5ce72f9554c4124c21ae Mon Sep 17 00:00:00 2001 From: rockpell Date: Tue, 15 Dec 2020 23:30:53 +0900 Subject: [PATCH 035/102] Feat: delete file duplicate upload MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 메시지를 보내기전에 업로드를 여러번 하는 경우 전에 업로드했던 파일을 삭제하는 기능 추가 --- frontend/src/container/FileUploader/FileUploader.js | 7 +++++-- frontend/src/container/MessageEditor/MessageEditor.js | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/frontend/src/container/FileUploader/FileUploader.js b/frontend/src/container/FileUploader/FileUploader.js index 0bf6d803..eb9f6f28 100644 --- a/frontend/src/container/FileUploader/FileUploader.js +++ b/frontend/src/container/FileUploader/FileUploader.js @@ -8,14 +8,17 @@ import { toast } from 'react-toastify' const fileContentType = 'multipart/form-data' -function FileUploader({ setFileData }) { +function FileUploader({ fileData, setFileData }) { const fileInput = useRef(null) - const handleFileInput = async e => { + if (!e.target.files[0]) return if (e.target.files[0].size > 8192000) { toast.error('8MB 이하의 파일만 업로드 할 수 있습니다!') return } + if (fileData !== null) { + await request.DELETE('/api/file', { fileId: fileData.fileId }) + } await handlePost(e.target.files[0]) } const handlePost = async selectedFile => { diff --git a/frontend/src/container/MessageEditor/MessageEditor.js b/frontend/src/container/MessageEditor/MessageEditor.js index 9a9e347a..56a44903 100644 --- a/frontend/src/container/MessageEditor/MessageEditor.js +++ b/frontend/src/container/MessageEditor/MessageEditor.js @@ -54,7 +54,7 @@ function MessageEditor({ channelTitle, sendMessage }) { {/* TODO markdown, chat action 적용 필요 */}
{isRender && renderPreview()}
- +
) From 143c170ff7b17c5e4159547e4176e2175d847aa2 Mon Sep 17 00:00:00 2001 From: jongwon Date: Wed, 16 Dec 2020 02:01:22 +0900 Subject: [PATCH 036/102] Feat: add markdown editor --- frontend/package-lock.json | 353 +++++++++++++++++- frontend/package.json | 5 + .../container/MessageEditor/MessageEditor.js | 47 ++- 3 files changed, 389 insertions(+), 16 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index cb053d74..495d15c0 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -7775,6 +7775,14 @@ "warning": "^4.0.3" } }, + "cross-fetch": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.0.6.tgz", + "integrity": "sha512-KBPUbqgFjzWlVcURG+Svp9TlhA5uliYtiNx/0r8nv0pdypeQCRJ9IaSIc3q/x3q8t3F75cHuwxVql1HFGHCNJQ==", + "requires": { + "node-fetch": "2.6.1" + } + }, "cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", @@ -8178,6 +8186,11 @@ "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" }, + "decorate-component-with-props": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/decorate-component-with-props/-/decorate-component-with-props-1.1.0.tgz", + "integrity": "sha512-tTYQojixN64yK3/WBODMfvss/zbmyUx9HQXhzSxZiSiofeekVeRyyuToy9BCiTMrVEIKWxTcla2t3y5qdaUF7Q==" + }, "dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", @@ -8613,6 +8626,244 @@ } } }, + "draft": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/draft/-/draft-0.2.3.tgz", + "integrity": "sha1-9ELdhr1R2B87/Vtf3OD9mcxpzJ4=" + }, + "draft-js": { + "version": "0.11.7", + "resolved": "https://registry.npmjs.org/draft-js/-/draft-js-0.11.7.tgz", + "integrity": "sha512-ne7yFfN4sEL82QPQEn80xnADR8/Q6ALVworbC5UOSzOvjffmYfFsr3xSZtxbIirti14R7Y33EZC5rivpLgIbsg==", + "requires": { + "fbjs": "^2.0.0", + "immutable": "~3.7.4", + "object-assign": "^4.1.1" + } + }, + "draft-js-checkable-list-item": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/draft-js-checkable-list-item/-/draft-js-checkable-list-item-2.0.6.tgz", + "integrity": "sha512-YHnGr3rKSFfqXGcHqp8SGees5Y/KAsHoyknoDRM1Gyal3B+duiFjmYvxIZlSYnWIcHgH4pQpPDQJJ9aaPmsvNw==", + "requires": { + "draft-js-modifiers": "^0.1.5" + } + }, + "draft-js-markdown-plugin": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/draft-js-markdown-plugin/-/draft-js-markdown-plugin-3.0.5.tgz", + "integrity": "sha512-DNqKUJw2eHFGQL/GOKTrDqqe4pwOoH6wzJHkJ7APNQCoCHTrG4pD5ueiO9AuNMbaQTaeCtsj9QwUMs1YBwAQCg==", + "requires": { + "decorate-component-with-props": "^1.1.0", + "draft-js": "^0.10.4", + "draft-js-checkable-list-item": "^2.0.6", + "draft-js-prism-plugin": "^0.1.3", + "immutable": "~3.7.4", + "react-click-outside": "^3.0.1" + }, + "dependencies": { + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" + }, + "draft-js": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/draft-js/-/draft-js-0.10.5.tgz", + "integrity": "sha512-LE6jSCV9nkPhfVX2ggcRLA4FKs6zWq9ceuO/88BpXdNCS7mjRTgs0NsV6piUCJX9YxMsB9An33wnkMmU2sD2Zg==", + "requires": { + "fbjs": "^0.8.15", + "immutable": "~3.7.4", + "object-assign": "^4.1.0" + } + }, + "fbjs": { + "version": "0.8.17", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz", + "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=", + "requires": { + "core-js": "^1.0.0", + "isomorphic-fetch": "^2.1.1", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^0.7.18" + } + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "requires": { + "asap": "~2.0.3" + } + } + } + }, + "draft-js-markdown-shortcuts-plugin": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/draft-js-markdown-shortcuts-plugin/-/draft-js-markdown-shortcuts-plugin-0.6.1.tgz", + "integrity": "sha512-VZbq6WATsNNHH8wqUbYuY0ijHcb1o2V9cN2eeMEAHgr92RJmGGi2UfgxI9O7ycWhTwgS9KZ17AE76C1R/rYR6Q==", + "requires": { + "decorate-component-with-props": "^1.1.0", + "draft-js": "~0.11.5", + "draft-js-checkable-list-item": "^3.0.4", + "draft-js-prism-plugin": "^0.1.3", + "immutable": "~3.8.2" + }, + "dependencies": { + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" + }, + "draft-js-checkable-list-item": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/draft-js-checkable-list-item/-/draft-js-checkable-list-item-3.0.4.tgz", + "integrity": "sha512-rHTErKYTYoBRtSCxKfSz+PivbtXp0KqmjC2Ms3u9QHlx9c40qsbajdSz+1TfPXaWP8FAB5EeynZ2qYPRgltPeQ==", + "requires": { + "draft-js-modifiers": "~0.2.2", + "immutable": "~3.7.4" + }, + "dependencies": { + "immutable": { + "version": "3.7.6", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz", + "integrity": "sha1-E7TTyxK++hVIKib+Gy665kAHHks=" + } + } + }, + "draft-js-modifiers": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/draft-js-modifiers/-/draft-js-modifiers-0.2.2.tgz", + "integrity": "sha512-64zeY74S4um+NO5HI66scZAX6fufdpZlr6htI3ttvNkTw5Jx/3r7+ETKJ/PTnGWtzUTC7TVkeF6mP7oqtUBUQw==", + "requires": { + "draft-js": "~0.10.5", + "immutable": "~3.7.4" + }, + "dependencies": { + "draft-js": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/draft-js/-/draft-js-0.10.5.tgz", + "integrity": "sha512-LE6jSCV9nkPhfVX2ggcRLA4FKs6zWq9ceuO/88BpXdNCS7mjRTgs0NsV6piUCJX9YxMsB9An33wnkMmU2sD2Zg==", + "requires": { + "fbjs": "^0.8.15", + "immutable": "~3.7.4", + "object-assign": "^4.1.0" + } + }, + "immutable": { + "version": "3.7.6", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz", + "integrity": "sha1-E7TTyxK++hVIKib+Gy665kAHHks=" + } + } + }, + "fbjs": { + "version": "0.8.17", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz", + "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=", + "requires": { + "core-js": "^1.0.0", + "isomorphic-fetch": "^2.1.1", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^0.7.18" + } + }, + "immutable": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz", + "integrity": "sha1-wkOZUUVbs5kT2vKBN28VMOEErfM=" + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "requires": { + "asap": "~2.0.3" + } + } + } + }, + "draft-js-modifiers": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/draft-js-modifiers/-/draft-js-modifiers-0.1.5.tgz", + "integrity": "sha512-UVbTvlbFSOlJ4LHNK68yflR1k6UyBge0o89DM0/YA8w5PXI+bExMTkByRcwhV5XIWYDgboHWLQSXjao02dW4mQ==", + "requires": { + "draft-js": "~0.10.0", + "immutable": "~3.7.4" + }, + "dependencies": { + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" + }, + "draft-js": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/draft-js/-/draft-js-0.10.5.tgz", + "integrity": "sha512-LE6jSCV9nkPhfVX2ggcRLA4FKs6zWq9ceuO/88BpXdNCS7mjRTgs0NsV6piUCJX9YxMsB9An33wnkMmU2sD2Zg==", + "requires": { + "fbjs": "^0.8.15", + "immutable": "~3.7.4", + "object-assign": "^4.1.0" + } + }, + "fbjs": { + "version": "0.8.17", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz", + "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=", + "requires": { + "core-js": "^1.0.0", + "isomorphic-fetch": "^2.1.1", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^0.7.18" + } + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "requires": { + "asap": "~2.0.3" + } + } + } + }, + "draft-js-plugins-editor": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/draft-js-plugins-editor/-/draft-js-plugins-editor-3.0.0.tgz", + "integrity": "sha512-bFEL0FUIPg9VK3KSeBZ3D+uMqQEVe4Cv7++LWCMASRH02jy6x2f87NRxSZLzTQES5+oL6Qg+OEUlaTn409145A==", + "requires": { + "immutable": "~3.7.4", + "prop-types": "^15.5.8" + } + }, + "draft-js-prism": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/draft-js-prism/-/draft-js-prism-1.0.6.tgz", + "integrity": "sha512-9iNPPr6/vaC9K60DtVes1JGDZ9uD0vZ7/8i6de5cZEzbftOj/ijIGplEV0dTFT/q8U+bY1uR1ikQevjRh2pEpQ==", + "requires": { + "extend": "^3.0.0", + "immutable": "*" + } + }, + "draft-js-prism-plugin": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/draft-js-prism-plugin/-/draft-js-prism-plugin-0.1.3.tgz", + "integrity": "sha512-w/agtisitO7SCHBBqa5vfkwy7DlhfKMCak6ivE4GpSV97MYLUIuRtafP1ei5tCMwGkJxJDKMv3Bgrd5miG55ZA==", + "requires": { + "draft-js-prism": "^1.0.6", + "react": "*" + } + }, "duplexer": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", @@ -8752,6 +9003,24 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, + "encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "requires": { + "iconv-lite": "^0.6.2" + }, + "dependencies": { + "iconv-lite": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", + "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } + } + }, "end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -9991,6 +10260,36 @@ "bser": "2.1.1" } }, + "fbjs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-2.0.0.tgz", + "integrity": "sha512-8XA8ny9ifxrAWlyhAbexXcs3rRMtxWcs3M0lctLfB49jRDHiaxj+Mo0XxbwE7nKZYzgCFoq64FS+WFd4IycPPQ==", + "requires": { + "core-js": "^3.6.4", + "cross-fetch": "^3.0.4", + "fbjs-css-vars": "^1.0.0", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^0.7.18" + }, + "dependencies": { + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "requires": { + "asap": "~2.0.3" + } + } + } + }, + "fbjs-css-vars": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz", + "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==" + }, "figgy-pudding": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", @@ -11534,6 +11833,11 @@ "resolved": "https://registry.npmjs.org/immer/-/immer-7.0.9.tgz", "integrity": "sha512-Vs/gxoM4DqNAYR7pugIxi0Xc8XAun/uy7AQu4fLLqaTBHxjOP9pJ266Q9MWA/ly4z6rAFZbvViOtihxUZ7O28A==" }, + "immutable": { + "version": "3.7.6", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz", + "integrity": "sha1-E7TTyxK++hVIKib+Gy665kAHHks=" + }, "import-cwd": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz", @@ -11620,9 +11924,9 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, "inline-style-parser": { "version": "0.1.1", @@ -12134,6 +12438,26 @@ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" }, + "isomorphic-fetch": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", + "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", + "requires": { + "node-fetch": "^1.0.1", + "whatwg-fetch": ">=0.10.0" + }, + "dependencies": { + "node-fetch": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", + "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", + "requires": { + "encoding": "^0.1.11", + "is-stream": "^1.0.1" + } + } + } + }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -14183,8 +14507,7 @@ "node-fetch": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", - "dev": true + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" }, "node-forge": { "version": "0.10.0", @@ -16502,6 +16825,21 @@ "whatwg-fetch": "^3.4.1" } }, + "react-click-outside": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/react-click-outside/-/react-click-outside-3.0.1.tgz", + "integrity": "sha512-d0KWFvBt+esoZUF15rL2UBB7jkeAqLU8L/Ny35oLK6fW6mIbOv/ChD+ExF4sR9PD26kVx+9hNfD0FTIqRZEyRQ==", + "requires": { + "hoist-non-react-statics": "^2.1.1" + }, + "dependencies": { + "hoist-non-react-statics": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz", + "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==" + } + } + }, "react-color": { "version": "2.19.3", "resolved": "https://registry.npmjs.org/react-color/-/react-color-2.19.3.tgz", @@ -19962,6 +20300,11 @@ "is-typedarray": "^1.0.0" } }, + "ua-parser-js": { + "version": "0.7.23", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.23.tgz", + "integrity": "sha512-m4hvMLxgGHXG3O3fQVAyyAQpZzDOvwnhOTjYz5Xmr7r/+LpkNy3vJXdVRWgd1TkAb7NGROZuSy96CrlNVjA7KA==" + }, "unfetch": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 01166c21..819bf0d3 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -13,6 +13,11 @@ "@testing-library/react": "^11.1.0", "@testing-library/user-event": "^12.1.10", "axios": "^0.21.0", + "draft": "^0.2.3", + "draft-js": "^0.11.7", + "draft-js-markdown-plugin": "^3.0.5", + "draft-js-markdown-shortcuts-plugin": "^0.6.1", + "draft-js-plugins-editor": "^3.0.0", "emoji-mart": "^3.0.0", "qs": "^6.9.4", "react": "^17.0.1", diff --git a/frontend/src/container/MessageEditor/MessageEditor.js b/frontend/src/container/MessageEditor/MessageEditor.js index f2de8fcf..56dcbd9e 100644 --- a/frontend/src/container/MessageEditor/MessageEditor.js +++ b/frontend/src/container/MessageEditor/MessageEditor.js @@ -1,26 +1,51 @@ import React, { useState } from 'react' +import Editor from 'draft-js-plugins-editor' +import createMarkdownShortcutsPlugin from 'draft-js-markdown-shortcuts-plugin' +import { + ContentState, + EditorState, + getDefaultKeyBinding, + convertToRaw, +} from 'draft-js' -import Input from '../../presenter/Input' +const plugins = [createMarkdownShortcutsPlugin()] function MessageEditor({ channelTitle, sendMessage }) { - const [message, setMessage] = useState('') + const [message, setMessage] = useState(EditorState.createEmpty()) const handleInput = e => { - setMessage(e.target.value) + setMessage(e) } - const handleKey = e => { - if (e.key === 'Enter' && e.target.value) { - sendMessage(message) - setMessage('') + + const keyBindingFn = e => { + if (e.key === 'Enter') return 'send-message' + return getDefaultKeyBinding(e) + } + const handleKey = command => { + if (command === 'send-message' && message.getCurrentContent().hasText()) { + sendMessage(JSON.stringify(convertToRaw(message.getCurrentContent()))) + setMessage( + EditorState.moveFocusToEnd( + EditorState.push( + message, + ContentState.createFromText(''), + 'remove-range', + ), + ), + ) } } + return (
- + {/* TODO markdown, chat action 적용 필요 */}
) From 3d3877478f12bf93309c39532496a42903dd0542 Mon Sep 17 00:00:00 2001 From: jongwon Date: Wed, 16 Dec 2020 02:01:59 +0900 Subject: [PATCH 037/102] Feat: add markdown render --- .../src/presenter/ChatContent/ChatContent.js | 44 +++++++++++-------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/frontend/src/presenter/ChatContent/ChatContent.js b/frontend/src/presenter/ChatContent/ChatContent.js index caec6c80..d23953c3 100644 --- a/frontend/src/presenter/ChatContent/ChatContent.js +++ b/frontend/src/presenter/ChatContent/ChatContent.js @@ -1,24 +1,30 @@ -import React from 'react' +import React, { memo } from 'react' import styled from 'styled-components' +import { convertFromRaw, Editor, EditorState } from 'draft-js' import { COLOR } from '../../constant/style' -const ChatContent = ({ - displayName, - createdAt, - contents, - handleProfileModal, -}) => { - return ( - - - - {displayName} - - {createdAt} - - {contents} - - ) -} + +const ChatContent = memo( + ({ displayName, createdAt, contents, handleProfileModal }) => { + return ( + + + + {displayName} + + {createdAt} + + + + + + ) + }, +) const StyledChatContent = styled.div` width: 100%; display: flex; From 54b4af2ec0986efd4090493b94790d57ecd88065 Mon Sep 17 00:00:00 2001 From: jongwon Date: Wed, 16 Dec 2020 02:22:22 +0900 Subject: [PATCH 038/102] Refactor: update editor style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 실제 slack과 유사한 인풋 창으로 변경 --- .../container/MessageEditor/MessageEditor.js | 37 +++++++++++++------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/frontend/src/container/MessageEditor/MessageEditor.js b/frontend/src/container/MessageEditor/MessageEditor.js index 56dcbd9e..c4530c6a 100644 --- a/frontend/src/container/MessageEditor/MessageEditor.js +++ b/frontend/src/container/MessageEditor/MessageEditor.js @@ -7,6 +7,9 @@ import { getDefaultKeyBinding, convertToRaw, } from 'draft-js' +import styled from 'styled-components' +import 'draft-js/dist/Draft.css' +import { COLOR } from '../../constant/style' const plugins = [createMarkdownShortcutsPlugin()] @@ -36,19 +39,29 @@ function MessageEditor({ channelTitle, sendMessage }) { } return ( -
- + + + - {/* TODO markdown, chat action 적용 필요 */} -
+ {/* TODO markdown, chat action 적용 필요 */} + + ) } - +const MessageEditorContainer = styled.div` + padding: 20px; + background-color: ${COLOR.WHITE}; +` +const MessageEditorArea = styled.div` + border: 1px solid ${COLOR.LIGHT_GRAY}; + padding: 10px; + border-radius: 5px; +` export default MessageEditor From b9d111f41c330ce040849b8fe482f07d9df50489 Mon Sep 17 00:00:00 2001 From: rockpell Date: Wed, 16 Dec 2020 03:03:36 +0900 Subject: [PATCH 039/102] Fix: remove console.log --- frontend/src/container/ChannelList/ChannelList.js | 1 - frontend/src/container/SectionLabel/SectionLabel.js | 1 - frontend/src/presenter/DirectMessageCard/DirectMessageCard.js | 1 - frontend/src/presenter/UserProfileImg/UserProfileImg.js | 1 - 4 files changed, 4 deletions(-) diff --git a/frontend/src/container/ChannelList/ChannelList.js b/frontend/src/container/ChannelList/ChannelList.js index 6a792ce2..9caed1a9 100644 --- a/frontend/src/container/ChannelList/ChannelList.js +++ b/frontend/src/container/ChannelList/ChannelList.js @@ -33,7 +33,6 @@ function ChannelList() { channels.forEach(channel => { if (channel.sectionName == null) { if (channel.channelId.channelType === 2) { - console.log('Direct messages: ', channel) checkHasKeyAndSetKeyInMap(sectionMap, 'Direct messages', channel) } else { console.log('channel: ', channel) diff --git a/frontend/src/container/SectionLabel/SectionLabel.js b/frontend/src/container/SectionLabel/SectionLabel.js index 0cc78311..cf58066e 100644 --- a/frontend/src/container/SectionLabel/SectionLabel.js +++ b/frontend/src/container/SectionLabel/SectionLabel.js @@ -19,7 +19,6 @@ function SectionLabel(props) { const { sectionName, lists } = props const { channelId, workspaceId } = useParams() const setModal = useSetRecoilState(modalRecoil) - const openSection = () => { setIsOpen(!isOpen) } diff --git a/frontend/src/presenter/DirectMessageCard/DirectMessageCard.js b/frontend/src/presenter/DirectMessageCard/DirectMessageCard.js index b449fb77..3617f3ec 100644 --- a/frontend/src/presenter/DirectMessageCard/DirectMessageCard.js +++ b/frontend/src/presenter/DirectMessageCard/DirectMessageCard.js @@ -7,7 +7,6 @@ import UserProfileImg from '../UserProfileImg' function DirectMessageCard(props) { const directMessage = props.directMessage - console.log('directMessage: ', directMessage) return ( diff --git a/frontend/src/presenter/UserProfileImg/UserProfileImg.js b/frontend/src/presenter/UserProfileImg/UserProfileImg.js index 23cf2700..1faf8880 100644 --- a/frontend/src/presenter/UserProfileImg/UserProfileImg.js +++ b/frontend/src/presenter/UserProfileImg/UserProfileImg.js @@ -3,7 +3,6 @@ import styled from 'styled-components' import UserActive from '../UserActive' const UserProfileImg = ({ user, size, showActive, type = 'default' }) => { - console.log('user: ', user) return ( From 35696b7793d6f11e4842ea69defebdedaf55eb91 Mon Sep 17 00:00:00 2001 From: rockpell Date: Wed, 16 Dec 2020 03:06:57 +0900 Subject: [PATCH 040/102] Feat: findChannelIdByName api --- backend/controller/channel/channel.js | 8 ++++++++ backend/controller/channel/index.js | 2 ++ backend/service/channel.js | 13 ++++++++++++- 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/backend/controller/channel/channel.js b/backend/controller/channel/channel.js index c7a1ba73..91ec0b45 100644 --- a/backend/controller/channel/channel.js +++ b/backend/controller/channel/channel.js @@ -65,6 +65,13 @@ const checkDuplicate = asyncWrapper(async (req, res) => { return res.status(code).json({ success, data }) }) +const findChannelIdByName = asyncWrapper(async (req, res) => { + const { code, success, data } = await service.findChannelIdByName({ + ...req.query, + }) + return res.status(code).json({ success, data }) +}) + module.exports = { getChannelList, getChannelHeaderInfo, @@ -73,4 +80,5 @@ module.exports = { updateChannelSection, createChannel, checkDuplicate, + findChannelIdByName, } diff --git a/backend/controller/channel/index.js b/backend/controller/channel/index.js index 59ada502..e4eeab12 100644 --- a/backend/controller/channel/index.js +++ b/backend/controller/channel/index.js @@ -13,6 +13,8 @@ router.post('/', Auth, controller.createChannel) /* GET /api/channle/{channelId}/info get channel header info */ router.get('/:channelId/info', controller.getChannelHeaderInfo) +router.get('/info', controller.findChannelIdByName) + /* POST /api/channle/invite invite user to channel */ router.post('/invite', controller.inviteUser) diff --git a/backend/service/channel.js b/backend/service/channel.js index 1495cc18..c0ea2cfc 100644 --- a/backend/service/channel.js +++ b/backend/service/channel.js @@ -35,7 +35,6 @@ const checkDuplicate = async ({ title, workspaceId }) => { WorkspaceUserInfo.findOne({ workspaceId, displayName: title }), ), ]) - console.log('checkDuplicate result: ', result) return { code: statusCode.OK, data: result.every(v => v === null), @@ -130,6 +129,17 @@ const updateChannelSectionDB = async ({ } } +const findChannelIdByName = async ({ title }) => { + verifyRequiredParams(title) + const channelData = await dbErrorHandler(() => Channel.findOne({ title })) + + return { + code: statusCode.OK, + data: channelData._id, + success: true, + } +} + module.exports = { createChannel, checkDuplicate, @@ -138,4 +148,5 @@ module.exports = { inviteUserDB, muteChannelDB, updateChannelSectionDB, + findChannelIdByName, } From 0394fc320f410e86bc860469117b1152cbee5563 Mon Sep 17 00:00:00 2001 From: rockpell Date: Wed, 16 Dec 2020 03:07:41 +0900 Subject: [PATCH 041/102] Feat: findChannelIdByName api --- frontend/src/api/channel.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/frontend/src/api/channel.js b/frontend/src/api/channel.js index 622d5ef2..93e29f93 100644 --- a/frontend/src/api/channel.js +++ b/frontend/src/api/channel.js @@ -23,3 +23,8 @@ export const getChannelHeaderInfo = async ({ }) return data.result } + +export const findChannelIdByName = async ({ title }) => { + const { data } = await Request.GET(`/api/channel/info?title=${title}`) + return data?.data +} From 102ac9a59bb3d81f10aaef80813a69ac191c2bf5 Mon Sep 17 00:00:00 2001 From: rockpell Date: Wed, 16 Dec 2020 03:08:41 +0900 Subject: [PATCH 042/102] Feat: getWorkspaceUserInfoByInfoId api --- backend/controller/workspace/index.js | 1 + backend/controller/workspace/workspace.js | 7 +++++++ backend/service/workspace.js | 14 ++++++++++++++ frontend/src/api/workspace.js | 7 +++++++ 4 files changed, 29 insertions(+) diff --git a/backend/controller/workspace/index.js b/backend/controller/workspace/index.js index 19624d9c..cb4a16ed 100644 --- a/backend/controller/workspace/index.js +++ b/backend/controller/workspace/index.js @@ -9,5 +9,6 @@ router.post('/invite', Auth, controller.invite) router.get('/invite/:code', Auth, controller.invited) router.get('/check-duplicate-name', Auth, controller.checkDuplicateName) router.get('/info/:workspaceId', Auth, controller.getWorkspaceUserInfo) +router.get('/info', Auth, controller.getWorkspaceUserInfoByInfoId) module.exports = router diff --git a/backend/controller/workspace/workspace.js b/backend/controller/workspace/workspace.js index cb0fd5c8..b91b7b49 100644 --- a/backend/controller/workspace/workspace.js +++ b/backend/controller/workspace/workspace.js @@ -50,3 +50,10 @@ exports.getWorkspaceUserInfo = asyncWrapper(async (req, res) => { }) return res.status(code).json({ success, data }) }) + +exports.getWorkspaceUserInfoByInfoId = asyncWrapper(async (req, res) => { + const { code, success, data } = await service.getWorkspaceUserInfoByInfoId({ + ...req.query, + }) + return res.status(code).json({ success, data }) +}) diff --git a/backend/service/workspace.js b/backend/service/workspace.js index 1d7f491f..99c0f5ba 100644 --- a/backend/service/workspace.js +++ b/backend/service/workspace.js @@ -33,6 +33,7 @@ const createWorkspace = async params => { fullName: findedUser.fullName, displayName: findedUser.fullName, profileUrl: findedUser.profileUrl, + isActive: false, }), ) const channelData = await dbErrorHandler(() => @@ -118,6 +119,7 @@ const invited = async ({ userId, code }) => { fullName: findedUser?.fullName, displayName: findedUser?.fullName, profileUrl: findedUser?.profileUrl, + isActive: false, }), ) const workspaceData = await dbErrorHandler(() => @@ -175,6 +177,17 @@ const getWorkspaceUserInfo = async ({ userId, workspaceId }) => { } } +const getWorkspaceUserInfoByInfoId = async ({ workspaceUserInfoId }) => { + const workspaceUserInfoData = await dbErrorHandler(() => + WorkspaceUserInfo.getWorkspaceUserInfo(workspaceUserInfoId), + ) + return { + code: statusCode.OK, + data: workspaceUserInfoData[0], + success: true, + } +} + module.exports = { createWorkspace, getWorkspaces, @@ -182,4 +195,5 @@ module.exports = { invited, checkDuplicateName, getWorkspaceUserInfo, + getWorkspaceUserInfoByInfoId, } diff --git a/frontend/src/api/workspace.js b/frontend/src/api/workspace.js index 11c0ad0f..a8cb3c76 100644 --- a/frontend/src/api/workspace.js +++ b/frontend/src/api/workspace.js @@ -4,6 +4,13 @@ export const getWorkspaceUserInfo = async ({ workspaceId }) => { return data.data } +export const getWorkspaceUserInfoByInfoId = async ({ workspaceUserInfoId }) => { + const { data } = await Request.GET( + `/api/workspace/info?workspaceUserInfoId=${workspaceUserInfoId}`, + ) + return data.data +} + export const inviteWorkspace = async ({ workspaceId }) => { const { data } = await Request.POST('/api/workspace/invite', { workspaceId: workspaceId, From 58543c78f3dbe1df6c28efd03f8d27690505cd86 Mon Sep 17 00:00:00 2001 From: rockpell Date: Wed, 16 Dec 2020 03:11:06 +0900 Subject: [PATCH 043/102] Feat: InviteUserToChannelModal dm mode inviteDM --- .../InviteUserToChannelModal.js | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/frontend/src/container/Modal/InviteUserToChannelModal/InviteUserToChannelModal.js b/frontend/src/container/Modal/InviteUserToChannelModal/InviteUserToChannelModal.js index da88f316..cc2d62e9 100644 --- a/frontend/src/container/Modal/InviteUserToChannelModal/InviteUserToChannelModal.js +++ b/frontend/src/container/Modal/InviteUserToChannelModal/InviteUserToChannelModal.js @@ -15,7 +15,8 @@ import SelectedUserList from '../../../presenter/SelectedUserList' import useChannelInfo from '../../../hooks/useChannelInfo' import dmTitleGenerator from '../../../util/dmTitleGenerator' import { workspaceRecoil } from '../../../store' -import { checkDuplicateChannelName, createChannel } from '../../../api/channel' +import { createChannel, findChannelIdByName } from '../../../api/channel' +import { getWorkspaceUserInfoByInfoId } from '../../../api/workspace' import useChannelList from '../../../hooks/useChannelList' function InviteUserToChannelModal({ handleClose, type = 'channel' }) { @@ -58,9 +59,16 @@ function InviteUserToChannelModal({ handleClose, type = 'channel' }) { const inviteToDM = async () => { if (!inviteUserList.length) return - const title = dmTitleGenerator(inviteUserList) - console.log('title: ', title) - console.log('workspaceUserInfoId: ', workspaceUserInfoId) + const userInfoData = await getWorkspaceUserInfoByInfoId({ + workspaceUserInfoId, + }) + const title = dmTitleGenerator([...inviteUserList, userInfoData]) + const findedChannelId = await findChannelIdByName({ title }) + if (findedChannelId) { + history.push(`/workspace/${workspaceId}/${findedChannelId}`) + setModal(null) + return + } const channelId = await createChannel({ title: title, creator: workspaceUserInfoId, @@ -69,12 +77,6 @@ function InviteUserToChannelModal({ handleClose, type = 'channel' }) { workspaceId, }) - // title: channelName, - // creator: workspaceUserInfoId, - // channelType: isPrivate ? 0 : 1, - // description: channelDescription, - // workspaceId, - // 채널 생성해줘야함 const { data } = await request.POST('/api/channel/invite', { channelId: channelId, workspaceUserInfoId: inviteUserList.map(user => user._id), @@ -91,13 +93,9 @@ function InviteUserToChannelModal({ handleClose, type = 'channel' }) { updateChannelList() history.push(`/workspace/${workspaceId}/${channelId}`) - // } else { - // // history.push(`/workspace/${workspaceId}/${channelId}`) - // } } const inviteUser = async () => { - console.log('type: ', type) if (type === 'channel') return await inviteToChannel() else if (type === 'DM') return await inviteToDM() } From 895be7b15b895acdf1d3bb11c3f3a75d1c803b7e Mon Sep 17 00:00:00 2001 From: jongwon Date: Wed, 16 Dec 2020 06:11:21 +0900 Subject: [PATCH 044/102] Fix: Message editor losing focus bug --- .../src/container/MessageEditor/MessageEditor.js | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/frontend/src/container/MessageEditor/MessageEditor.js b/frontend/src/container/MessageEditor/MessageEditor.js index c4530c6a..813e0a32 100644 --- a/frontend/src/container/MessageEditor/MessageEditor.js +++ b/frontend/src/container/MessageEditor/MessageEditor.js @@ -1,4 +1,4 @@ -import React, { useState } from 'react' +import React, { useState, useRef } from 'react' import Editor from 'draft-js-plugins-editor' import createMarkdownShortcutsPlugin from 'draft-js-markdown-shortcuts-plugin' import { @@ -11,13 +11,9 @@ import styled from 'styled-components' import 'draft-js/dist/Draft.css' import { COLOR } from '../../constant/style' -const plugins = [createMarkdownShortcutsPlugin()] - function MessageEditor({ channelTitle, sendMessage }) { + const plugins = useRef([createMarkdownShortcutsPlugin()]) const [message, setMessage] = useState(EditorState.createEmpty()) - const handleInput = e => { - setMessage(e) - } const keyBindingFn = e => { if (e.key === 'Enter') return 'send-message' @@ -37,15 +33,14 @@ function MessageEditor({ channelTitle, sendMessage }) { ) } } - return ( From 67c540f757fdd49f71e0e6e50104333c757fd13e Mon Sep 17 00:00:00 2001 From: jongwon Date: Wed, 16 Dec 2020 06:29:48 +0900 Subject: [PATCH 045/102] Refactor: edit view thread button style --- frontend/src/container/ChatMessage/ChatMessage.js | 2 +- .../Button/ViewThreadButton/ViewThreadButton.js | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/frontend/src/container/ChatMessage/ChatMessage.js b/frontend/src/container/ChatMessage/ChatMessage.js index 97e4cb5c..9414885d 100644 --- a/frontend/src/container/ChatMessage/ChatMessage.js +++ b/frontend/src/container/ChatMessage/ChatMessage.js @@ -152,7 +152,6 @@ const StyledMessageContainer = styled.div` const ViewThreadBarStyle = styled.div` width: auto; - height: 30px; display: flex; flex-direction: row; ` @@ -171,6 +170,7 @@ const ThreadReactionStyle = styled.div` const StyleLink = styled(NavLink)` text-decoration: none; color: ${COLOR.STARBLUE}; + background-color: ${COLOR.WHITE}; padding: 5px; margin-left: 15px; &:hover { diff --git a/frontend/src/presenter/Button/ViewThreadButton/ViewThreadButton.js b/frontend/src/presenter/Button/ViewThreadButton/ViewThreadButton.js index e9ea7094..002baa0e 100644 --- a/frontend/src/presenter/Button/ViewThreadButton/ViewThreadButton.js +++ b/frontend/src/presenter/Button/ViewThreadButton/ViewThreadButton.js @@ -1,11 +1,13 @@ import React, { memo } from 'react' import styled from 'styled-components' import UserProfileImg from '../../UserProfileImg' -import { COLOR } from '../../../constant/style' +import calculateTime from '../../../util/calculateTime' import { go, Lazy, take, map } from '../../../util/fx' +import { COLOR } from '../../../constant/style' const MAX_NUMBER_OF_PROFILES = 5 const SMALL_SIZE = 24 -const ViewThreadButton = memo(({ reply }) => { +const ViewThreadButton = memo(({ reply = [] }) => { + const [lastReply] = reply.slice(-1) return ( {go( @@ -22,7 +24,10 @@ const ViewThreadButton = memo(({ reply }) => { )), )} - {reply.length} {reply.length === 1 ? 'reply' : 'replies'} + {reply.length} {reply.length === 1 ? 'reply ' : 'replies '} + + {calculateTime(lastReply?.createdAt)} + ) @@ -34,4 +39,7 @@ const ViewThreadContainer = styled.div` const ReplyCounts = styled.div` margin-left: 5px; ` +const LastModifiedTime = styled.span` + color: ${COLOR.GRAY}; +` export default ViewThreadButton From c966b9985a0ba5fc2027cf64afd9c675e9465ba7 Mon Sep 17 00:00:00 2001 From: rockpell Date: Wed, 16 Dec 2020 12:01:59 +0900 Subject: [PATCH 046/102] Fix: fileData error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fileData가 없을 경우 생기는 에러 수정 --- frontend/src/container/ChatRoom/ChatRoom.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/container/ChatRoom/ChatRoom.js b/frontend/src/container/ChatRoom/ChatRoom.js index 83036975..c9f03992 100644 --- a/frontend/src/container/ChatRoom/ChatRoom.js +++ b/frontend/src/container/ChatRoom/ChatRoom.js @@ -45,8 +45,8 @@ const ChatRoom = () => { const chat = { contents: message, channelId, - fileId: fileData.fileId, - fileType: fileData.fileType, + fileId: fileData?.fileId, + fileType: fileData?.fileType, userInfo: { _id: workspaceUserInfo._id, displayName: workspaceUserInfo.displayName, From 5304f150391d2a17674b41e5a35cfa77f7ab1179 Mon Sep 17 00:00:00 2001 From: rockpell Date: Wed, 16 Dec 2020 13:06:24 +0900 Subject: [PATCH 047/102] Fix: remove console.log --- frontend/src/container/ChannelList/ChannelList.js | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/container/ChannelList/ChannelList.js b/frontend/src/container/ChannelList/ChannelList.js index 9caed1a9..b5888214 100644 --- a/frontend/src/container/ChannelList/ChannelList.js +++ b/frontend/src/container/ChannelList/ChannelList.js @@ -35,7 +35,6 @@ function ChannelList() { if (channel.channelId.channelType === 2) { checkHasKeyAndSetKeyInMap(sectionMap, 'Direct messages', channel) } else { - console.log('channel: ', channel) checkHasKeyAndSetKeyInMap(sectionMap, 'Channels', channel) } } else { From 5ea8dcace75b54ee3f3d4b7ece2f7193cb29b3a6 Mon Sep 17 00:00:00 2001 From: jongwon Date: Wed, 16 Dec 2020 20:49:30 +0900 Subject: [PATCH 048/102] Refactor: message editor and overall layout style --- .../container/ChannelHeader/ChannelHeader.js | 2 +- frontend/src/container/ChatRoom/ChatRoom.js | 1 + .../container/SideThreadBar/SideThreadBar.js | 24 +++++++------------ frontend/src/index.js | 13 ++-------- .../src/page/WorkspacePage/WorkspacePage.js | 4 ++-- .../src/presenter/GlobalStyle/GlobalStyle.js | 8 ++++++- 6 files changed, 22 insertions(+), 30 deletions(-) diff --git a/frontend/src/container/ChannelHeader/ChannelHeader.js b/frontend/src/container/ChannelHeader/ChannelHeader.js index 7753403c..352a5737 100644 --- a/frontend/src/container/ChannelHeader/ChannelHeader.js +++ b/frontend/src/container/ChannelHeader/ChannelHeader.js @@ -66,7 +66,7 @@ function ChannelHeader() { const ChannelHeaderStyle = styled.div` width: 100%; height: auto; - margin: auto 20px; + margin: 0 20px; display: flex; flex-direction: row; justify-content: space-between; diff --git a/frontend/src/container/ChatRoom/ChatRoom.js b/frontend/src/container/ChatRoom/ChatRoom.js index da52ecfa..1046ed83 100644 --- a/frontend/src/container/ChatRoom/ChatRoom.js +++ b/frontend/src/container/ChatRoom/ChatRoom.js @@ -180,6 +180,7 @@ const ChatHeader = styled.div` height: 60px; background: ${COLOR.BACKGROUND_CONTENTS}; border: 1px solid rgba(255, 255, 255, 0.1); + box-sizing: border-box; ` const ChatContents = styled.div` diff --git a/frontend/src/container/SideThreadBar/SideThreadBar.js b/frontend/src/container/SideThreadBar/SideThreadBar.js index 1c0373ee..b0a10206 100644 --- a/frontend/src/container/SideThreadBar/SideThreadBar.js +++ b/frontend/src/container/SideThreadBar/SideThreadBar.js @@ -103,9 +103,9 @@ function SideThreadBar() { const SideThreadBarStyle = styled.div` width: auto; - height: calc(100% - 1px); - background: ${COLOR.BACKGROUND_CONTENTS}; + height: 100%; border-bottom: 1px solid rgba(255, 255, 255, 0.1); + box-sizing: border-box; ` const SideBarHeader = styled.div` @@ -121,7 +121,9 @@ const SideBarHeader = styled.div` justify-content: flex-start; align-items: center; border: 1px solid rgba(255, 255, 255, 0.1); + box-sizing: border-box; border-right: 0; + background-color: ${COLOR.BACKGROUND_CONTENTS}; ` const CloseBtn = styled.div` @@ -132,33 +134,25 @@ const CloseBtn = styled.div` const SideBarContents = styled.div` width: auto; - height: calc(100% - 63px); - color: ${COLOR.LABEL_SELECT_TEXT}; + height: calc(100% - 60px); + overflow-y: auto; border: 1px solid rgba(255, 255, 255, 0.1); border-right: 0; + box-sizing: border-box; ` const ChatContent = styled.div` width: auto; - max-height: 30%; min-height: 20%; border: 1px solid rgba(255, 255, 255, 0.1); border-right: 0; overflow-y: auto; ` -const ReplyContents = styled.div` - height: 60%; - overflow-x: auto; - overflow-y: auto; -` +const ReplyContents = styled.div`` const MessageEditorArea = styled.div` - height: calc(10% - 3px); - - display: flex; - justify-content: center; - align-items: flex-end; + /* height: calc(10% - 3px); */ ` export default SideThreadBar diff --git a/frontend/src/index.js b/frontend/src/index.js index 045159d2..2a794c57 100644 --- a/frontend/src/index.js +++ b/frontend/src/index.js @@ -1,13 +1,13 @@ import React from 'react' import ReactDOM from 'react-dom' -import { createGlobalStyle } from 'styled-components' import './index.css' import WorkspacePage from './page/WorkspacePage' -import { BrowserRouter, Route, Switch } from 'react-router-dom' +import { BrowserRouter, Route } from 'react-router-dom' import reportWebVitals from './reportWebVitals' import LoginPage from './page/login/Login' import CreateWorkspace from './page/createWorkspace/CreateWorkspace' import SelectWorkspace from './page/selectWorkspace/SelectWorkspace' +import GlobalStyle from './presenter/GlobalStyle' import Auth from './hooks/Auth' import GithubOAuth from './hooks/GithubOAuth' import { RecoilRoot } from 'recoil' @@ -42,15 +42,6 @@ const App = () => { ) } -const GlobalStyle = createGlobalStyle` - body { - padding: 0px; - margin: 0px; - height: 100%; - width: 100%; - } -` - ReactDOM.render(, document.getElementById('root')) // If you want to start measuring performance in your app, pass a function // to log results (for example: reportWebVitals(console.log)) diff --git a/frontend/src/page/WorkspacePage/WorkspacePage.js b/frontend/src/page/WorkspacePage/WorkspacePage.js index 49b396f5..112b5f49 100644 --- a/frontend/src/page/WorkspacePage/WorkspacePage.js +++ b/frontend/src/page/WorkspacePage/WorkspacePage.js @@ -97,8 +97,7 @@ const ConstructionPage = SideBarWidth => { } const PageStyle = styled.div` - width: 100vw; - height: 100vh; + height: 100%; display: flex; flex-direction: column; ` @@ -129,6 +128,7 @@ const ChannelListHeaderArea = styled.div` background: ${COLOR.BACKGROUND_CHANNEL_LIST}; color: ${COLOR.LABEL_DEFAULT_TEXT}; border: 1px solid rgba(255, 255, 255, 0.1); + box-sizing: border-box; ` const ChannelListArea = styled.div` diff --git a/frontend/src/presenter/GlobalStyle/GlobalStyle.js b/frontend/src/presenter/GlobalStyle/GlobalStyle.js index bd626392..01ee2a53 100644 --- a/frontend/src/presenter/GlobalStyle/GlobalStyle.js +++ b/frontend/src/presenter/GlobalStyle/GlobalStyle.js @@ -1,12 +1,18 @@ import { createGlobalStyle } from 'styled-components' const GlobalStyle = createGlobalStyle` + html{ + height: 100%; + } body { padding: 0px; margin: 0px; height: 100%; width: 100%; - } + } + #root{ + height:100%; + } ` export default GlobalStyle From 80b0e23ef19a879dc0b1c82ea6dd8f38afdae80a Mon Sep 17 00:00:00 2001 From: rockpell Date: Thu, 17 Dec 2020 01:08:22 +0900 Subject: [PATCH 049/102] Refactor: preview component use fileData MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fileId를 이용하여 데이터를 가져오던 방식에서 fileData를 가져오도록 변경 --- .../src/container/FilePreview/FilePreview.js | 11 +++++---- .../FilePreview/FilePreview.stories.js | 14 +++++++++-- .../container/FileUploader/FileUploader.js | 3 ++- .../FileUploader/FileUploader.stories.js | 4 +++- .../src/container/ImgPreview/ImgPreview.js | 23 +++++++++++-------- .../ImgPreview/ImgPreview.stories.js | 14 +++++++++-- 6 files changed, 48 insertions(+), 21 deletions(-) diff --git a/frontend/src/container/FilePreview/FilePreview.js b/frontend/src/container/FilePreview/FilePreview.js index 4a00a333..3dfce007 100644 --- a/frontend/src/container/FilePreview/FilePreview.js +++ b/frontend/src/container/FilePreview/FilePreview.js @@ -7,18 +7,19 @@ import { COLOR } from '../../constant/style' import Button from '../../presenter/Button/Button' import { isEmpty } from '../../util' -function FilePreview({ type, fileId, setIsRender }) { +function FilePreview({ type, setIsRender, file }) { const [fileData, setFileData] = useState({}) const [isHover, setIsHover] = useState(false) useEffect(() => { - if (!isEmpty(fileId)) { + if (!isEmpty(file)) { ;(async () => { - const { data } = (await request.GET('/api/file', { fileId })) || {} + const { data } = + (await request.GET('/api/file', { fileId: file?.fileId })) || {} setFileData(data?.data) })() } - }, [fileId]) + }, [file]) const enterMouseHandle = () => { setIsHover(true) @@ -30,7 +31,7 @@ function FilePreview({ type, fileId, setIsRender }) { const handleDelete = async () => { setIsRender(false) - await request.DELETE('/api/file', { fileId }) + await request.DELETE('/api/file', { fileId: file?.fileId }) } const deleteButton = () => { diff --git a/frontend/src/container/FilePreview/FilePreview.stories.js b/frontend/src/container/FilePreview/FilePreview.stories.js index 04d6acbd..d9fbc7c6 100644 --- a/frontend/src/container/FilePreview/FilePreview.stories.js +++ b/frontend/src/container/FilePreview/FilePreview.stories.js @@ -11,14 +11,24 @@ const Template = args => <>{isRender && } export const inputFilePreview = Template.bind({}) inputFilePreview.args = { type: 'input', // input, message - fileId: '5fd6ea342d026a63752cd31b', setIsRender: () => { isRender = false }, + file: { + fileId: '5fda2f97e882860557e592fc', + fileName: '5주 그룹프로젝트 4주차 데모 및 피어세션 그룹 안내.pdf', + fileType: 'application/pdf', + creator: '5fd81c4d630674160961baf4', + }, } export const messageFilePreview = Template.bind({}) messageFilePreview.args = { type: 'message', // input, message - fileId: '5fd6ea342d026a63752cd31b', + file: { + fileId: '5fda2f97e882860557e592fc', + fileName: '5주 그룹프로젝트 4주차 데모 및 피어세션 그룹 안내.pdf', + fileType: 'application/pdf', + creator: '5fd81c4d630674160961baf4', + }, } diff --git a/frontend/src/container/FileUploader/FileUploader.js b/frontend/src/container/FileUploader/FileUploader.js index eb9f6f28..ebfa2972 100644 --- a/frontend/src/container/FileUploader/FileUploader.js +++ b/frontend/src/container/FileUploader/FileUploader.js @@ -5,6 +5,7 @@ import Button from '../../presenter/Button' import Icon from '../../presenter/Icon' import { CLIP } from '../../constant/icon' import { toast } from 'react-toastify' +import { isEmpty } from '../../util' const fileContentType = 'multipart/form-data' @@ -16,7 +17,7 @@ function FileUploader({ fileData, setFileData }) { toast.error('8MB 이하의 파일만 업로드 할 수 있습니다!') return } - if (fileData !== null) { + if (!isEmpty(fileData)) { await request.DELETE('/api/file', { fileId: fileData.fileId }) } await handlePost(e.target.files[0]) diff --git a/frontend/src/container/FileUploader/FileUploader.stories.js b/frontend/src/container/FileUploader/FileUploader.stories.js index 8f679b92..beec263a 100644 --- a/frontend/src/container/FileUploader/FileUploader.stories.js +++ b/frontend/src/container/FileUploader/FileUploader.stories.js @@ -23,12 +23,14 @@ const TestComponent = () => { ) : ( ) @@ -38,7 +40,7 @@ const TestComponent = () => { <> {isRender && renderPreview()} - + ) } diff --git a/frontend/src/container/ImgPreview/ImgPreview.js b/frontend/src/container/ImgPreview/ImgPreview.js index 4e9e57f6..96a6f230 100644 --- a/frontend/src/container/ImgPreview/ImgPreview.js +++ b/frontend/src/container/ImgPreview/ImgPreview.js @@ -7,18 +7,21 @@ import { COLOR } from '../../constant/style' import Button from '../../presenter/Button' import { isEmpty } from '../../util' -function ImgPreview({ type, fileId, setIsRender }) { - const [fileData, setFileData] = useState({}) +function ImgPreview({ type, setIsRender, file }) { + // const [fileData, setFileData] = useState({}) + const [fileURL, setFileURL] = useState(null) const [isHover, setIsHover] = useState(false) useEffect(() => { - if (!isEmpty(fileId)) { + if (!isEmpty(file)) { ;(async () => { - const { data } = (await request.GET('/api/file', { fileId })) || {} - setFileData(data?.data) + const { data } = + (await request.GET('/api/file', { fileId: file?.fileId })) || {} + // console.log('data: ', data) + setFileURL(data?.data?.url) })() } - }, [fileId]) + }, [file]) const enterMouseHandle = () => { setIsHover(true) @@ -30,7 +33,7 @@ function ImgPreview({ type, fileId, setIsRender }) { const handleDelete = async () => { setIsRender(false) - await request.DELETE('/api/file', { fileId }) + await request.DELETE('/api/file', { fileId: file.fileId }) } const deleteButton = () => { @@ -47,7 +50,7 @@ function ImgPreview({ type, fileId, setIsRender }) { return ( { - if (fileData) window.open(fileData.url, '_blank') + if (file) window.open(file.url, '_blank') }} > Click to Download @@ -58,8 +61,8 @@ function ImgPreview({ type, fileId, setIsRender }) { return ( {isHover && diff --git a/frontend/src/container/ImgPreview/ImgPreview.stories.js b/frontend/src/container/ImgPreview/ImgPreview.stories.js index 9e5ea405..77a5723a 100644 --- a/frontend/src/container/ImgPreview/ImgPreview.stories.js +++ b/frontend/src/container/ImgPreview/ImgPreview.stories.js @@ -11,7 +11,12 @@ const Template = args => <>{isRender && } export const inputImgPreview = Template.bind({}) inputImgPreview.args = { type: 'input', // input, message - fileId: '5fd5dc7d8c8a82245fa0ab38', + file: { + fileId: '5fda2fe2e882860557e592fd', + fileName: '죠르디.jpg', + fileType: 'image/jpeg', + creator: '5fd81c4d630674160961baf4', + }, setIsRender: () => { isRender = false }, @@ -20,5 +25,10 @@ inputImgPreview.args = { export const messageImgPreview = Template.bind({}) messageImgPreview.args = { type: 'message', // input, message - fileId: '5fd5dc7d8c8a82245fa0ab38', + file: { + fileId: '5fda2fe2e882860557e592fd', + fileName: '죠르디.jpg', + fileType: 'image/jpeg', + creator: '5fd81c4d630674160961baf4', + }, } From 9791d819b9453b076ba3657c19bae278a13db85d Mon Sep 17 00:00:00 2001 From: rockpell Date: Thu, 17 Dec 2020 01:24:22 +0900 Subject: [PATCH 050/102] Feat: add file url to uploadFile api --- backend/service/file.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/backend/service/file.js b/backend/service/file.js index a43f04c6..dbc6220f 100644 --- a/backend/service/file.js +++ b/backend/service/file.js @@ -28,13 +28,13 @@ const uploadFile = async ({ file, userId }) => { verifyRequiredParams(file, userId) const fileName = `${file.fieldname}-${Date.now()}-${file.originalname}` - const result = await S3.putObject({ + await S3.putObject({ Bucket: BUCKETNAME, Key: fileName, ACL: 'public-read', Body: file.buffer, }).promise() - + const url = `${process.env.S3_ENDPOINT}/${process.env.S3_BUCKETNAME}/${fileName}` const data = await dbErrorHandler(() => File.create({ name: fileName, @@ -42,8 +42,10 @@ const uploadFile = async ({ file, userId }) => { path: '/', fileType: file.mimetype, creator: userId, + url: url, }), ) + return { code: statusCode.OK, data: { @@ -51,6 +53,7 @@ const uploadFile = async ({ file, userId }) => { fileName: data.originalName, fileType: data.fileType, creator: data.creator, + url: data.url, }, success: true, } From 5571348abe175af801cc756b084c38e738d5236c Mon Sep 17 00:00:00 2001 From: rockpell Date: Thu, 17 Dec 2020 01:25:24 +0900 Subject: [PATCH 051/102] Feat: add url to File Model --- backend/model/File.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backend/model/File.js b/backend/model/File.js index 0e0cbe52..bfdb0afb 100644 --- a/backend/model/File.js +++ b/backend/model/File.js @@ -19,6 +19,9 @@ const fileSchema = mongoose.Schema( type: Schema.Types.ObjectId, ref: 'User', }, + url: { + type: String, + }, }, { timestamps: true }, ) From 3a0df1369ff607f7ba8453662990c229abed8deb Mon Sep 17 00:00:00 2001 From: rockpell Date: Thu, 17 Dec 2020 01:25:44 +0900 Subject: [PATCH 052/102] Refactor: remove useEffect from Preview Component --- .../src/container/FilePreview/FilePreview.js | 16 ++-------------- frontend/src/container/ImgPreview/ImgPreview.js | 16 +--------------- .../src/container/MessageEditor/MessageEditor.js | 12 ++---------- 3 files changed, 5 insertions(+), 39 deletions(-) diff --git a/frontend/src/container/FilePreview/FilePreview.js b/frontend/src/container/FilePreview/FilePreview.js index 3dfce007..c064932c 100644 --- a/frontend/src/container/FilePreview/FilePreview.js +++ b/frontend/src/container/FilePreview/FilePreview.js @@ -5,22 +5,10 @@ import Icon from '../../presenter/Icon' import { CLOSE, FILE } from '../../constant/icon' import { COLOR } from '../../constant/style' import Button from '../../presenter/Button/Button' -import { isEmpty } from '../../util' function FilePreview({ type, setIsRender, file }) { - const [fileData, setFileData] = useState({}) const [isHover, setIsHover] = useState(false) - useEffect(() => { - if (!isEmpty(file)) { - ;(async () => { - const { data } = - (await request.GET('/api/file', { fileId: file?.fileId })) || {} - setFileData(data?.data) - })() - } - }, [file]) - const enterMouseHandle = () => { setIsHover(true) } @@ -48,7 +36,7 @@ function FilePreview({ type, setIsRender, file }) { return ( { - if (fileData) window.open(fileData.url, '_blank') + if (file) window.open(file?.url, '_blank') }} > Click to Download @@ -60,7 +48,7 @@ function FilePreview({ type, setIsRender, file }) { - {fileData?.originalName} + {file?.originalName} {isHover && (type === 'input' diff --git a/frontend/src/container/ImgPreview/ImgPreview.js b/frontend/src/container/ImgPreview/ImgPreview.js index 96a6f230..fde23712 100644 --- a/frontend/src/container/ImgPreview/ImgPreview.js +++ b/frontend/src/container/ImgPreview/ImgPreview.js @@ -5,24 +5,10 @@ import Icon from '../../presenter/Icon' import { CLOSE } from '../../constant/icon' import { COLOR } from '../../constant/style' import Button from '../../presenter/Button' -import { isEmpty } from '../../util' function ImgPreview({ type, setIsRender, file }) { - // const [fileData, setFileData] = useState({}) - const [fileURL, setFileURL] = useState(null) const [isHover, setIsHover] = useState(false) - useEffect(() => { - if (!isEmpty(file)) { - ;(async () => { - const { data } = - (await request.GET('/api/file', { fileId: file?.fileId })) || {} - // console.log('data: ', data) - setFileURL(data?.data?.url) - })() - } - }, [file]) - const enterMouseHandle = () => { setIsHover(true) } @@ -62,7 +48,7 @@ function ImgPreview({ type, setIsRender, file }) { {isHover && diff --git a/frontend/src/container/MessageEditor/MessageEditor.js b/frontend/src/container/MessageEditor/MessageEditor.js index c275e10b..db3b1218 100644 --- a/frontend/src/container/MessageEditor/MessageEditor.js +++ b/frontend/src/container/MessageEditor/MessageEditor.js @@ -55,17 +55,9 @@ function MessageEditor({ channelTitle, sendMessage }) { const renderPreview = () => { return isImage(fileData?.fileType) ? ( - + ) : ( - + ) } From c1d5b9eae02b7f46d75c1274a4d85253bb58ae86 Mon Sep 17 00:00:00 2001 From: rockpell Date: Thu, 17 Dec 2020 01:49:59 +0900 Subject: [PATCH 053/102] Refactor: fileId, fileType in Chat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fileId와 fileType을 Chat에서 제거하고 file 오브젝트를 보관하도록 변경 --- backend/chatServer.js | 5 ++-- backend/model/Chat.js | 8 ++--- backend/service/chat.js | 18 +++++------ .../src/container/ChatMessage/ChatMessage.js | 11 ++++--- frontend/src/container/ChatRoom/ChatRoom.js | 5 ++-- .../src/container/FilePreview/FilePreview.js | 5 ++-- .../container/FileUploader/FileUploader.js | 10 ++++--- .../FileUploader/FileUploader.stories.js | 20 +++++++------ .../src/container/ImgPreview/ImgPreview.js | 5 ++-- .../container/MessageEditor/MessageEditor.js | 30 ++++++++++++------- 10 files changed, 62 insertions(+), 55 deletions(-) diff --git a/backend/chatServer.js b/backend/chatServer.js index d777571d..cd453fa7 100644 --- a/backend/chatServer.js +++ b/backend/chatServer.js @@ -28,13 +28,12 @@ namespace.on('connection', socket => { ) }) socket.on('new message', async data => { - const { contents, channelId, fileId, fileType } = data + const { contents, channelId, file } = data const { data: result } = await createChatMessage({ creator: workspaceUserInfoId, channelId, contents, - fileId, - fileType, + file, }) namespace.in(channelId).emit('new message', { message: { diff --git a/backend/model/Chat.js b/backend/model/Chat.js index c31f3911..f4fd83d7 100644 --- a/backend/model/Chat.js +++ b/backend/model/Chat.js @@ -27,12 +27,8 @@ const chatSchema = mongoose.Schema( type: Boolean, default: false, }, - fileId: { - type: Schema.Types.ObjectId, - ref: 'File', - }, - fileType: { - type: String, + file: { + type: Object, }, }, { timestamps: true }, diff --git a/backend/service/chat.js b/backend/service/chat.js index 89493920..a8ac6b87 100644 --- a/backend/service/chat.js +++ b/backend/service/chat.js @@ -13,17 +13,15 @@ const getChatMessages = async ({ channelId, currentCursor, fromDate }) => { success: true, } } -const createChatMessage = async ({ - channelId, - creator, - contents, - fileId, - fileType, -}) => { - const fileValue = fileId || fileType - verifyRequiredParams(channelId, creator, contents || fileValue) +const createChatMessage = async ({ channelId, creator, contents, file }) => { + verifyRequiredParams(channelId, creator, contents || file) const result = await dbErrorHandler(() => - Chat.create({ channel: channelId, creator, contents, fileId, fileType }), + Chat.create({ + channel: channelId, + creator, + contents, + file, + }), ) return { data: result } } diff --git a/frontend/src/container/ChatMessage/ChatMessage.js b/frontend/src/container/ChatMessage/ChatMessage.js index 5973721f..73b85f7b 100644 --- a/frontend/src/container/ChatMessage/ChatMessage.js +++ b/frontend/src/container/ChatMessage/ChatMessage.js @@ -23,8 +23,7 @@ const ChatMessage = forwardRef( createdAt, contents, type = 'chat', - fileId, - fileType, + file, }, ref, ) => { @@ -74,13 +73,13 @@ const ChatMessage = forwardRef( } const renderFilePreview = () => { - if (isEmpty(fileType) || isEmpty(fileId)) { + if (isEmpty(file)) { return } - return isImage(fileType) ? ( - + return isImage(file?.fileType) ? ( + ) : ( - + ) } diff --git a/frontend/src/container/ChatRoom/ChatRoom.js b/frontend/src/container/ChatRoom/ChatRoom.js index 92db59d3..c4557887 100644 --- a/frontend/src/container/ChatRoom/ChatRoom.js +++ b/frontend/src/container/ChatRoom/ChatRoom.js @@ -41,12 +41,11 @@ const ChatRoom = ({ width }) => { targetRef.scrollIntoView() } - const sendMessage = (message, fileData) => { + const sendMessage = (message, file) => { const chat = { contents: message, channelId, - fileId: fileData?.fileId, - fileType: fileData?.fileType, + file: file, userInfo: { _id: workspaceUserInfo._id, displayName: workspaceUserInfo.displayName, diff --git a/frontend/src/container/FilePreview/FilePreview.js b/frontend/src/container/FilePreview/FilePreview.js index c064932c..287e8285 100644 --- a/frontend/src/container/FilePreview/FilePreview.js +++ b/frontend/src/container/FilePreview/FilePreview.js @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react' +import React, { useState } from 'react' import styled from 'styled-components' import request from '../../util/request' import Icon from '../../presenter/Icon' @@ -6,7 +6,7 @@ import { CLOSE, FILE } from '../../constant/icon' import { COLOR } from '../../constant/style' import Button from '../../presenter/Button/Button' -function FilePreview({ type, setIsRender, file }) { +function FilePreview({ type, setIsRender, file, setFile }) { const [isHover, setIsHover] = useState(false) const enterMouseHandle = () => { @@ -20,6 +20,7 @@ function FilePreview({ type, setIsRender, file }) { const handleDelete = async () => { setIsRender(false) await request.DELETE('/api/file', { fileId: file?.fileId }) + setFile(null) } const deleteButton = () => { diff --git a/frontend/src/container/FileUploader/FileUploader.js b/frontend/src/container/FileUploader/FileUploader.js index ebfa2972..19b2dbec 100644 --- a/frontend/src/container/FileUploader/FileUploader.js +++ b/frontend/src/container/FileUploader/FileUploader.js @@ -9,7 +9,7 @@ import { isEmpty } from '../../util' const fileContentType = 'multipart/form-data' -function FileUploader({ fileData, setFileData }) { +function FileUploader({ file, setFile }) { const fileInput = useRef(null) const handleFileInput = async e => { if (!e.target.files[0]) return @@ -17,8 +17,9 @@ function FileUploader({ fileData, setFileData }) { toast.error('8MB 이하의 파일만 업로드 할 수 있습니다!') return } - if (!isEmpty(fileData)) { - await request.DELETE('/api/file', { fileId: fileData.fileId }) + if (!isEmpty(file)) { + await request.DELETE('/api/file', { fileId: file.fileId }) + setFile(null) } await handlePost(e.target.files[0]) } @@ -31,7 +32,8 @@ function FileUploader({ fileData, setFileData }) { formData, fileContentType, ) - setFileData(data.data) + console.log('data.data: ', data.data) + setFile(data.data) } } diff --git a/frontend/src/container/FileUploader/FileUploader.stories.js b/frontend/src/container/FileUploader/FileUploader.stories.js index beec263a..c6768b52 100644 --- a/frontend/src/container/FileUploader/FileUploader.stories.js +++ b/frontend/src/container/FileUploader/FileUploader.stories.js @@ -11,26 +11,28 @@ import 'react-toastify/dist/ReactToastify.css' const stories = storiesOf('Organism', module) const TestComponent = () => { - const [fileData, setFileData] = useState(null) + const [file, setFile] = useState(null) const [isRender, setIsRender] = useState(false) useEffect(() => { - if (fileData) setIsRender(true) - }, [fileData]) + if (file) setIsRender(true) + }, [file]) const renderPreview = () => { - return isImage(fileData?.fileType) ? ( + return isImage(file?.fileType) ? ( ) : ( ) @@ -40,7 +42,7 @@ const TestComponent = () => { <> {isRender && renderPreview()} - + ) } diff --git a/frontend/src/container/ImgPreview/ImgPreview.js b/frontend/src/container/ImgPreview/ImgPreview.js index fde23712..8ae4e900 100644 --- a/frontend/src/container/ImgPreview/ImgPreview.js +++ b/frontend/src/container/ImgPreview/ImgPreview.js @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react' +import React, { useState } from 'react' import styled from 'styled-components' import request from '../../util/request' import Icon from '../../presenter/Icon' @@ -6,7 +6,7 @@ import { CLOSE } from '../../constant/icon' import { COLOR } from '../../constant/style' import Button from '../../presenter/Button' -function ImgPreview({ type, setIsRender, file }) { +function ImgPreview({ type, setIsRender, file, setFile }) { const [isHover, setIsHover] = useState(false) const enterMouseHandle = () => { @@ -20,6 +20,7 @@ function ImgPreview({ type, setIsRender, file }) { const handleDelete = async () => { setIsRender(false) await request.DELETE('/api/file', { fileId: file.fileId }) + setFile(null) } const deleteButton = () => { diff --git a/frontend/src/container/MessageEditor/MessageEditor.js b/frontend/src/container/MessageEditor/MessageEditor.js index db3b1218..8e906f46 100644 --- a/frontend/src/container/MessageEditor/MessageEditor.js +++ b/frontend/src/container/MessageEditor/MessageEditor.js @@ -18,12 +18,12 @@ import { isImage } from '../../util/index' function MessageEditor({ channelTitle, sendMessage }) { const plugins = useRef([createMarkdownShortcutsPlugin()]) const [message, setMessage] = useState(EditorState.createEmpty()) - const [fileData, setFileData] = useState(null) + const [file, setFile] = useState(null) const [isRender, setIsRender] = useState(false) useEffect(() => { - if (fileData) setIsRender(true) - }, [fileData]) + if (file) setIsRender(true) + }, [file]) const keyBindingFn = e => { if (e.key === 'Enter') return 'send-message' @@ -33,11 +33,11 @@ function MessageEditor({ channelTitle, sendMessage }) { const handleKey = command => { if ( command === 'send-message' && - (message.getCurrentContent().hasText() || fileData) + (message.getCurrentContent().hasText() || file) ) { sendMessage( JSON.stringify(convertToRaw(message.getCurrentContent())), - fileData, + file, ) setMessage( EditorState.moveFocusToEnd( @@ -48,16 +48,26 @@ function MessageEditor({ channelTitle, sendMessage }) { ), ), ) - setFileData(null) + setFile(null) setIsRender(false) } } const renderPreview = () => { - return isImage(fileData?.fileType) ? ( - + return isImage(file?.fileType) ? ( + ) : ( - + ) } @@ -74,7 +84,7 @@ function MessageEditor({ channelTitle, sendMessage }) { />
{isRender && renderPreview()}
- +
{/* TODO markdown, chat action 적용 필요 */}
From 72e881b6f6e9e3163ff70a22657e6384b54a9589 Mon Sep 17 00:00:00 2001 From: rockpell Date: Thu, 17 Dec 2020 02:27:07 +0900 Subject: [PATCH 054/102] Refactor: remove File model, remove getFileURL --- backend/controller/file/file.js | 7 ----- backend/controller/file/index.js | 1 - backend/model/File.js | 29 ------------------ backend/service/file.js | 50 +++++--------------------------- 4 files changed, 7 insertions(+), 80 deletions(-) delete mode 100644 backend/model/File.js diff --git a/backend/controller/file/file.js b/backend/controller/file/file.js index 95ca2092..5e75dfbb 100644 --- a/backend/controller/file/file.js +++ b/backend/controller/file/file.js @@ -1,13 +1,6 @@ import { asyncWrapper } from '../../util' import service from '../../service/file' -exports.getFileURL = asyncWrapper(async (req, res) => { - const { code, success, data } = await service.getFileURL({ - ...req.query, - }) - return res.status(code).json({ success, data }) -}) - exports.uploadFile = asyncWrapper(async (req, res) => { const { code, success, data } = await service.uploadFile({ file: req.file, diff --git a/backend/controller/file/index.js b/backend/controller/file/index.js index e4ab4763..35d07ddc 100644 --- a/backend/controller/file/index.js +++ b/backend/controller/file/index.js @@ -7,7 +7,6 @@ const multer = require('multer') const storage = multer.memoryStorage() const uploader = multer({ storage: storage }) -router.get('/', Auth, controller.getFileURL) router.post('/', Auth, uploader.single('file'), controller.uploadFile) router.delete('/', Auth, controller.deleteFile) diff --git a/backend/model/File.js b/backend/model/File.js deleted file mode 100644 index bfdb0afb..00000000 --- a/backend/model/File.js +++ /dev/null @@ -1,29 +0,0 @@ -const mongoose = require('mongoose') -const Schema = mongoose.Schema - -const fileSchema = mongoose.Schema( - { - path: { - type: String, - }, - fileType: { - type: String, - }, - name: { - type: String, - }, - originalName: { - type: String, - }, - creator: { - type: Schema.Types.ObjectId, - ref: 'User', - }, - url: { - type: String, - }, - }, - { timestamps: true }, -) -const File = mongoose.model('File', fileSchema) -module.exports = { File } diff --git a/backend/service/file.js b/backend/service/file.js index dbc6220f..94a05bba 100644 --- a/backend/service/file.js +++ b/backend/service/file.js @@ -1,28 +1,6 @@ -import { verifyRequiredParams, dbErrorHandler } from '../util/' +import { verifyRequiredParams } from '../util/' import statusCode from '../util/statusCode' import { S3, BUCKETNAME } from '../config/s3' -import { File } from '../model/File' -import mongoose from 'mongoose' -const ObjectId = mongoose.Types.ObjectId - -const getFileURL = async ({ fileId }) => { - verifyRequiredParams(fileId) - - const { name, originalName } = await dbErrorHandler(() => - File.findOne({ - _id: ObjectId(fileId), - }), - ) - - return { - code: statusCode.OK, - data: { - url: `${process.env.S3_ENDPOINT}/${process.env.S3_BUCKETNAME}/${name}`, - originalName: originalName, - }, - success: true, - } -} const uploadFile = async ({ file, userId }) => { verifyRequiredParams(file, userId) @@ -35,35 +13,22 @@ const uploadFile = async ({ file, userId }) => { Body: file.buffer, }).promise() const url = `${process.env.S3_ENDPOINT}/${process.env.S3_BUCKETNAME}/${fileName}` - const data = await dbErrorHandler(() => - File.create({ + + return { + code: statusCode.OK, + data: { name: fileName, originalName: file.originalname, - path: '/', fileType: file.mimetype, creator: userId, url: url, - }), - ) - - return { - code: statusCode.OK, - data: { - fileId: data._id, - fileName: data.originalName, - fileType: data.fileType, - creator: data.creator, - url: data.url, }, success: true, } } -const deleteFile = async ({ fileId }) => { - verifyRequiredParams(fileId) - const { name } = await dbErrorHandler(() => - File.findOneAndDelete({ _id: ObjectId(fileId) }), - ) +const deleteFile = async ({ name }) => { + verifyRequiredParams(name) await S3.deleteObject({ Bucket: BUCKETNAME, Key: name, @@ -73,6 +38,5 @@ const deleteFile = async ({ fileId }) => { module.exports = { uploadFile, - getFileURL, deleteFile, } From 4249aeb6dc5803d52134f312187619305cc51c8d Mon Sep 17 00:00:00 2001 From: rockpell Date: Thu, 17 Dec 2020 02:27:51 +0900 Subject: [PATCH 055/102] Fix: file delete error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 파일 삭제 api 호출시 생기던 파라미터 에러 수정 --- frontend/src/container/FilePreview/FilePreview.js | 2 +- frontend/src/container/FileUploader/FileUploader.js | 3 +-- frontend/src/container/ImgPreview/ImgPreview.js | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/frontend/src/container/FilePreview/FilePreview.js b/frontend/src/container/FilePreview/FilePreview.js index 287e8285..b0937bed 100644 --- a/frontend/src/container/FilePreview/FilePreview.js +++ b/frontend/src/container/FilePreview/FilePreview.js @@ -19,7 +19,7 @@ function FilePreview({ type, setIsRender, file, setFile }) { const handleDelete = async () => { setIsRender(false) - await request.DELETE('/api/file', { fileId: file?.fileId }) + await request.DELETE('/api/file', { name: file?.name }) setFile(null) } diff --git a/frontend/src/container/FileUploader/FileUploader.js b/frontend/src/container/FileUploader/FileUploader.js index 19b2dbec..1b8396b2 100644 --- a/frontend/src/container/FileUploader/FileUploader.js +++ b/frontend/src/container/FileUploader/FileUploader.js @@ -18,7 +18,7 @@ function FileUploader({ file, setFile }) { return } if (!isEmpty(file)) { - await request.DELETE('/api/file', { fileId: file.fileId }) + await request.DELETE('/api/file', { name: file.name }) setFile(null) } await handlePost(e.target.files[0]) @@ -32,7 +32,6 @@ function FileUploader({ file, setFile }) { formData, fileContentType, ) - console.log('data.data: ', data.data) setFile(data.data) } } diff --git a/frontend/src/container/ImgPreview/ImgPreview.js b/frontend/src/container/ImgPreview/ImgPreview.js index 8ae4e900..26212d92 100644 --- a/frontend/src/container/ImgPreview/ImgPreview.js +++ b/frontend/src/container/ImgPreview/ImgPreview.js @@ -19,7 +19,7 @@ function ImgPreview({ type, setIsRender, file, setFile }) { const handleDelete = async () => { setIsRender(false) - await request.DELETE('/api/file', { fileId: file.fileId }) + await request.DELETE('/api/file', { name: file.name }) setFile(null) } @@ -48,7 +48,7 @@ function ImgPreview({ type, setIsRender, file, setFile }) { return ( From 56d931795008e314db7f4e02e9df4b5214b3ae0c Mon Sep 17 00:00:00 2001 From: rockpell Date: Thu, 17 Dec 2020 02:28:27 +0900 Subject: [PATCH 056/102] Fix: file null in ChatMessage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ChatMessage 내부에 file null 필드가 생기는 문제 수정 --- backend/service/chat.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/service/chat.js b/backend/service/chat.js index a8ac6b87..d8a459a1 100644 --- a/backend/service/chat.js +++ b/backend/service/chat.js @@ -20,7 +20,7 @@ const createChatMessage = async ({ channelId, creator, contents, file }) => { channel: channelId, creator, contents, - file, + file: file === null ? undefined : file, }), ) return { data: result } From 6145c00637e556661a2581fa0e6008d8e4f2c8dc Mon Sep 17 00:00:00 2001 From: Ryou_Changyu Date: Thu, 17 Dec 2020 02:43:00 +0900 Subject: [PATCH 057/102] Chore: update immer dependency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit immer 패키지 추가 --- frontend/package-lock.json | 11 ++++++++--- frontend/package.json | 1 + 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index cb053d74..8f279677 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -11530,9 +11530,9 @@ "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==" }, "immer": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/immer/-/immer-7.0.9.tgz", - "integrity": "sha512-Vs/gxoM4DqNAYR7pugIxi0Xc8XAun/uy7AQu4fLLqaTBHxjOP9pJ266Q9MWA/ly4z6rAFZbvViOtihxUZ7O28A==" + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/immer/-/immer-8.0.0.tgz", + "integrity": "sha512-jm87NNBAIG4fHwouilCHIecFXp5rMGkiFrAuhVO685UnMAlOneEAnOyzPt8OnP47TC11q/E7vpzZe0WvwepFTg==" }, "import-cwd": { "version": "2.1.0", @@ -16617,6 +16617,11 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, + "immer": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/immer/-/immer-7.0.9.tgz", + "integrity": "sha512-Vs/gxoM4DqNAYR7pugIxi0Xc8XAun/uy7AQu4fLLqaTBHxjOP9pJ266Q9MWA/ly4z6rAFZbvViOtihxUZ7O28A==" + }, "path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 01166c21..19c0dc0f 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -14,6 +14,7 @@ "@testing-library/user-event": "^12.1.10", "axios": "^0.21.0", "emoji-mart": "^3.0.0", + "immer": "^8.0.0", "qs": "^6.9.4", "react": "^17.0.1", "react-dom": "^17.0.1", From 95d8190d61c3ddfca0533fd0b473c9cf2e7dc7b4 Mon Sep 17 00:00:00 2001 From: rockpell Date: Thu, 17 Dec 2020 02:54:51 +0900 Subject: [PATCH 058/102] Refactor: merg ImgPreview, FilePreview MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 이미지 미리보기 컴포넌트를 파일 미리보기 컴포넌트와 병합 --- .../src/container/ChatMessage/ChatMessage.js | 11 +- .../src/container/FilePreview/FilePreview.js | 76 ++++++++++--- .../FilePreview/FilePreview.stories.js | 53 +++++++-- .../FileUploader/FileUploader.stories.js | 12 +- .../src/container/ImgPreview/ImgPreview.js | 105 ------------------ .../ImgPreview/ImgPreview.stories.js | 34 ------ frontend/src/container/ImgPreview/index.js | 1 - .../container/MessageEditor/MessageEditor.js | 11 +- 8 files changed, 109 insertions(+), 194 deletions(-) delete mode 100644 frontend/src/container/ImgPreview/ImgPreview.js delete mode 100644 frontend/src/container/ImgPreview/ImgPreview.stories.js delete mode 100644 frontend/src/container/ImgPreview/index.js diff --git a/frontend/src/container/ChatMessage/ChatMessage.js b/frontend/src/container/ChatMessage/ChatMessage.js index 73b85f7b..8e2603bd 100644 --- a/frontend/src/container/ChatMessage/ChatMessage.js +++ b/frontend/src/container/ChatMessage/ChatMessage.js @@ -10,7 +10,6 @@ import ViewThreadButton from '../../presenter/Button/ViewThreadButton' import { isEmpty, isImage } from '../../util' import { SIZE, COLOR } from '../../constant/style' import { workspaceRecoil, socketRecoil } from '../../store' -import ImgPreview from '../ImgPreview' import FilePreview from '../FilePreview' const ChatMessage = forwardRef( @@ -73,14 +72,8 @@ const ChatMessage = forwardRef( } const renderFilePreview = () => { - if (isEmpty(file)) { - return - } - return isImage(file?.fileType) ? ( - - ) : ( - - ) + if (isEmpty(file)) return + return } return ( diff --git a/frontend/src/container/FilePreview/FilePreview.js b/frontend/src/container/FilePreview/FilePreview.js index b0937bed..dc8c22ad 100644 --- a/frontend/src/container/FilePreview/FilePreview.js +++ b/frontend/src/container/FilePreview/FilePreview.js @@ -4,7 +4,8 @@ import request from '../../util/request' import Icon from '../../presenter/Icon' import { CLOSE, FILE } from '../../constant/icon' import { COLOR } from '../../constant/style' -import Button from '../../presenter/Button/Button' +import Button from '../../presenter/Button' +import { isImage } from '../../util' function FilePreview({ type, setIsRender, file, setFile }) { const [isHover, setIsHover] = useState(false) @@ -19,7 +20,7 @@ function FilePreview({ type, setIsRender, file, setFile }) { const handleDelete = async () => { setIsRender(false) - await request.DELETE('/api/file', { name: file?.name }) + await request.DELETE('/api/file', { name: file.name }) setFile(null) } @@ -37,32 +38,66 @@ function FilePreview({ type, setIsRender, file, setFile }) { return ( { - if (file) window.open(file?.url, '_blank') + if (file) window.open(file.url, '_blank') }} > Click to Download ) } - return ( - - - - - {file?.originalName} - + + const renderImgPreview = () => { + return ( + + {isHover && (type === 'input' ? deleteButton() : type === 'message' ? downloadButton() : null)} - - - ) + + ) + } + + const renderFilePreview = () => { + return ( + + + + + {file?.originalName} + + {isHover && + (type === 'input' + ? deleteButton() + : type === 'message' + ? downloadButton() + : null)} + + + ) + } + + return isImage(file?.fileType) ? renderImgPreview() : renderFilePreview() } -const StyledDiv = styled.div` +const StyledImgDiv = styled.div` + display: inline-block; + position: relative; +` + +const StyledFileDiv = styled.div` display: inline-block; position: relative; border: 1px solid ${COLOR.LIGHT_GRAY}; @@ -76,6 +111,15 @@ const FlexDiv = styled.div` padding: 5px 5px 5px 10px; ` +const StyledImg = styled.img` + max-width: ${({ type }) => { + return type === 'input' ? '80px' : '300px' + }}; + height: auto; + border-radius: 2%; + background: white; +` + const ButtonDiv = styled.div` display: inline-block; position: absolute; @@ -97,8 +141,8 @@ const DownloadDiv = styled.div` const ClickToDownloadSpan = styled.span` position: absolute; - bottom: 0px; - font-size: 12px; + bottom: 5px; + left: 10px; color: ${COLOR.GRAY}; ` diff --git a/frontend/src/container/FilePreview/FilePreview.stories.js b/frontend/src/container/FilePreview/FilePreview.stories.js index d9fbc7c6..886a108e 100644 --- a/frontend/src/container/FilePreview/FilePreview.stories.js +++ b/frontend/src/container/FilePreview/FilePreview.stories.js @@ -8,17 +8,51 @@ export default { let isRender = true const Template = args => <>{isRender && } -export const inputFilePreview = Template.bind({}) -inputFilePreview.args = { +export const inputImgPreview = Template.bind({}) +inputImgPreview.args = { type: 'input', // input, message + file: { + fileId: '5fda2fe2e882860557e592fd', + name: 'file-1608140339770-슬기.jpg', + originalName: '슬기.jpg', + fileType: 'image/jpeg', + creator: '5fd81c4d630674160961baf4', + url: + 'https://kr.object.ncloudstorage.com/slack-clone-files/file-1608140339770-슬기.jpg', + }, setIsRender: () => { isRender = false }, +} + +export const messageImgPreview = Template.bind({}) +messageImgPreview.args = { + type: 'message', // input, message file: { - fileId: '5fda2f97e882860557e592fc', - fileName: '5주 그룹프로젝트 4주차 데모 및 피어세션 그룹 안내.pdf', - fileType: 'application/pdf', + fileId: '5fda2fe2e882860557e592fd', + name: 'file-1608140339770-슬기.jpg', + originalName: '슬기.jpg', + fileType: 'image/jpeg', creator: '5fd81c4d630674160961baf4', + url: + 'https://kr.object.ncloudstorage.com/slack-clone-files/file-1608140339770-슬기.jpg', + }, +} + +export const inputFilePreview = Template.bind({}) +inputFilePreview.args = { + type: 'input', // input, message + file: { + fileId: '5fda2fe2e882860557e592fd', + name: 'file-1608140450582-test.zip', + originalName: 'test.zip', + fileType: 'application/x-zip-compressed', + creator: '5fd81c4d630674160961baf4', + url: + 'https://kr.object.ncloudstorage.com/slack-clone-files/file-1608140450582-test.zip', + }, + setIsRender: () => { + isRender = false }, } @@ -26,9 +60,12 @@ export const messageFilePreview = Template.bind({}) messageFilePreview.args = { type: 'message', // input, message file: { - fileId: '5fda2f97e882860557e592fc', - fileName: '5주 그룹프로젝트 4주차 데모 및 피어세션 그룹 안내.pdf', - fileType: 'application/pdf', + fileId: '5fda2fe2e882860557e592fd', + name: 'file-1608140450582-test.zip', + originalName: 'test.zip', + fileType: 'application/x-zip-compressed', creator: '5fd81c4d630674160961baf4', + url: + 'https://kr.object.ncloudstorage.com/slack-clone-files/file-1608140450582-test.zip', }, } diff --git a/frontend/src/container/FileUploader/FileUploader.stories.js b/frontend/src/container/FileUploader/FileUploader.stories.js index c6768b52..b5901067 100644 --- a/frontend/src/container/FileUploader/FileUploader.stories.js +++ b/frontend/src/container/FileUploader/FileUploader.stories.js @@ -3,8 +3,6 @@ import { storiesOf } from '@storybook/react' import FileUploader from './FileUploader' import styled from 'styled-components' import FilePreview from '../FilePreview' -import ImgPreview from '../ImgPreview' -import { isImage } from '../../util/index' import { ToastContainer } from 'react-toastify' import 'react-toastify/dist/ReactToastify.css' @@ -19,15 +17,7 @@ const TestComponent = () => { }, [file]) const renderPreview = () => { - return isImage(file?.fileType) ? ( - - ) : ( + return ( { - setIsHover(true) - } - - const leaveMouseHandle = () => { - setIsHover(false) - } - - const handleDelete = async () => { - setIsRender(false) - await request.DELETE('/api/file', { name: file.name }) - setFile(null) - } - - const deleteButton = () => { - return ( - - - - ) - } - - const downloadButton = () => { - return ( - { - if (file) window.open(file.url, '_blank') - }} - > - Click to Download - - ) - } - - return ( - - - {isHover && - (type === 'input' - ? deleteButton() - : type === 'message' - ? downloadButton() - : null)} - - ) -} - -const StyledDiv = styled.div` - display: inline-block; - position: relative; -` - -const StyledImg = styled.img` - max-width: ${({ type }) => { - return type === 'input' ? '80px' : '300px' - }}; - height: auto; - border-radius: 2%; - background: white; -` - -const ButtonDiv = styled.div` - display: inline-block; - position: absolute; - right: 0; - top: 0; - background: white; - width: -webkit-fit-content; - height: -webkit-fit-content; - box-sizing: border-box; -` - -const DownloadDiv = styled.div` - position: absolute; - top: 0; - width: 100%; - height: 100%; - cursor: pointer; -` - -const ClickToDownloadSpan = styled.span` - position: absolute; - bottom: 5px; - left: 10px; - color: ${COLOR.GRAY}; -` - -export default ImgPreview diff --git a/frontend/src/container/ImgPreview/ImgPreview.stories.js b/frontend/src/container/ImgPreview/ImgPreview.stories.js deleted file mode 100644 index 77a5723a..00000000 --- a/frontend/src/container/ImgPreview/ImgPreview.stories.js +++ /dev/null @@ -1,34 +0,0 @@ -import React from 'react' -import ImgPreview from './ImgPreview' - -export default { - title: 'Organism/ImgPreview', - component: ImgPreview, -} -let isRender = true -const Template = args => <>{isRender && } - -export const inputImgPreview = Template.bind({}) -inputImgPreview.args = { - type: 'input', // input, message - file: { - fileId: '5fda2fe2e882860557e592fd', - fileName: '죠르디.jpg', - fileType: 'image/jpeg', - creator: '5fd81c4d630674160961baf4', - }, - setIsRender: () => { - isRender = false - }, -} - -export const messageImgPreview = Template.bind({}) -messageImgPreview.args = { - type: 'message', // input, message - file: { - fileId: '5fda2fe2e882860557e592fd', - fileName: '죠르디.jpg', - fileType: 'image/jpeg', - creator: '5fd81c4d630674160961baf4', - }, -} diff --git a/frontend/src/container/ImgPreview/index.js b/frontend/src/container/ImgPreview/index.js deleted file mode 100644 index 11607d6e..00000000 --- a/frontend/src/container/ImgPreview/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './ImgPreview' diff --git a/frontend/src/container/MessageEditor/MessageEditor.js b/frontend/src/container/MessageEditor/MessageEditor.js index 8e906f46..15408fe5 100644 --- a/frontend/src/container/MessageEditor/MessageEditor.js +++ b/frontend/src/container/MessageEditor/MessageEditor.js @@ -12,8 +12,6 @@ import 'draft-js/dist/Draft.css' import { COLOR } from '../../constant/style' import FileUploader from '../FileUploader' import FilePreview from '../FilePreview' -import ImgPreview from '../ImgPreview' -import { isImage } from '../../util/index' function MessageEditor({ channelTitle, sendMessage }) { const plugins = useRef([createMarkdownShortcutsPlugin()]) @@ -54,14 +52,7 @@ function MessageEditor({ channelTitle, sendMessage }) { } const renderPreview = () => { - return isImage(file?.fileType) ? ( - - ) : ( + return ( Date: Thu, 17 Dec 2020 03:03:23 +0900 Subject: [PATCH 059/102] Fix: add reaction on new reply bug fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 새로 받은 댓글에 리액션 추가시 발생하는 버그 수정 --- backend/chatServer.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/backend/chatServer.js b/backend/chatServer.js index 43a36408..16706a0d 100644 --- a/backend/chatServer.js +++ b/backend/chatServer.js @@ -35,7 +35,12 @@ namespace.on('connection', socket => { contents, }) namespace.in(channelId).emit('new message', { - message: { ...data, _id: result._id, createdAt: result.createdAt }, + message: { + ...data, + _id: result._id, + createdAt: result.createdAt, + reactions: [], + }, }) }) socket.on('new reply', async data => { @@ -52,11 +57,12 @@ namespace.on('connection', socket => { _id: result._id, createdAt: result.createdAt, chatId: parentId, + reactions: [], }, }) }) socket.on('update reaction', async data => { - const { emoji, chatId, userInfo, channelId, type } = data + const { emoji, chatId, userInfo, channelId, type, parentId } = data //1 = add, 0 = remove const result = type === 1 @@ -78,6 +84,7 @@ namespace.on('connection', socket => { workspaceUserInfoId: userInfo._id, displayName: userInfo.displayName, type: result ? type : false, + parentId: parentId, }, }) }) From e0d4056dfecc22528ca4cd8442fa5a1883a2147f Mon Sep 17 00:00:00 2001 From: Ryou_Changyu Date: Thu, 17 Dec 2020 03:04:31 +0900 Subject: [PATCH 060/102] Refactor: add parentId props MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit parentId props를 추가해 update reaction시 parentId 값을 사용 할 수 있도록 변경 --- frontend/src/container/ChatMessage/ChatMessage.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/frontend/src/container/ChatMessage/ChatMessage.js b/frontend/src/container/ChatMessage/ChatMessage.js index fdf4cebb..8c020c32 100644 --- a/frontend/src/container/ChatMessage/ChatMessage.js +++ b/frontend/src/container/ChatMessage/ChatMessage.js @@ -12,7 +12,16 @@ import { useRecoilValue } from 'recoil' const ChatMessage = forwardRef( ( - { userInfo, reply, reactions, _id, createdAt, contents, type = 'chat' }, + { + userInfo, + reply, + reactions, + _id, + createdAt, + parentId, + contents, + type = 'chat', + }, ref, ) => { const { workspaceId, channelId } = useParams() @@ -26,6 +35,7 @@ const ChatMessage = forwardRef( emoji, chatId, channelId, + parentId, type, userInfo: { _id: workspaceUserInfo._id, @@ -38,7 +48,7 @@ const ChatMessage = forwardRef( const updateReactionHandler = emoji => { let done = false reactions.map(reaction => { - if (reaction.emoji === emoji.native || reaction.emoji === emoji) { + if (reaction.emoji === emoji) { if (reaction.set) { updateReaction({ emoji: emoji.native || emoji, From cbac8b5a86c22dc38ebc580ddb7d825c50863423 Mon Sep 17 00:00:00 2001 From: Ryou_Changyu Date: Thu, 17 Dec 2020 03:05:54 +0900 Subject: [PATCH 061/102] Refactor: update reaction update logic on chatRoom MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit reaction을 실시간 업데이트 하는 로직 수정 --- frontend/src/container/ChatRoom/ChatRoom.js | 62 +++++---------------- 1 file changed, 15 insertions(+), 47 deletions(-) diff --git a/frontend/src/container/ChatRoom/ChatRoom.js b/frontend/src/container/ChatRoom/ChatRoom.js index da52ecfa..43021bea 100644 --- a/frontend/src/container/ChatRoom/ChatRoom.js +++ b/frontend/src/container/ChatRoom/ChatRoom.js @@ -1,13 +1,14 @@ import React, { useEffect, useState, useRef, useCallback } from 'react' import styled from 'styled-components' import { useParams } from 'react-router-dom' -import { useRecoilState, useRecoilValue } from 'recoil' +import { 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 ChannelHeader from '../ChannelHeader' +import { hasMyReaction, chageReactionState } from '../../util/reactionUpdate' const ChatRoom = ({ width }) => { const viewport = useRef(null) @@ -16,6 +17,7 @@ const ChatRoom = ({ width }) => { const [targetState, setTargetState] = useState() const workspaceUserInfo = useRecoilValue(workspaceRecoil) const { workspaceId, channelId } = useParams() + const params = useParams() const socket = useRecoilValue(socketRecoil) const [messages, setMessages] = useState([]) const load = useRef(false) @@ -27,7 +29,10 @@ const ChatRoom = ({ width }) => { channelId, currentCursor, }) - setMessages(messages => [...newMessages, ...messages]) + setMessages(messages => [ + ...hasMyReaction(newMessages, workspaceUserInfo), + ...messages, + ]) load.current = false } @@ -54,55 +59,18 @@ const ChatRoom = ({ width }) => { socket.emit('new message', chat) } - const chageReactionState = (messages, reaction) => { - let done = false - if (reaction.type === false) { - return messages - } - return messages.map(message => { - if (message._id === reaction.chatId) { - message.reactions && - message.reactions.map(item => { - if (item.emoji === reaction.emoji) { - if (reaction.type) { - item.users = [ - ...item.users, - { - _id: reaction.workspaceUserInfoId, - displayName: reaction.displayName, - }, - ] - } else { - item.users.map((user, idx) => { - if (user._id === reaction.workspaceUserInfoId) { - item.users.splice(idx, 1) - } - }) - } - done = true - } - }) - if (!done && reaction.type === 1) { - message.reactions.push({ - emoji: reaction.emoji, - users: [ - { - _id: reaction.workspaceUserInfoId, - displayName: reaction.displayName, - }, - ], - }) - } - } - return message - }) - } + useEffect(() => { + setMessages(messages => [...hasMyReaction(messages, workspaceUserInfo)]) + }, [workspaceUserInfo]) useEffect(() => { if (socket) { socket.on('new message', ({ message }) => { if (message.channelId === channelId) - setMessages(messages => [...messages, message]) + setMessages(messages => [ + ...messages, + ...hasMyReaction([message], workspaceUserInfo), + ]) if (message.userInfo._id === workspaceUserInfo._id) scrollTo() }) socket.on('update reaction', ({ reaction }) => { @@ -115,7 +83,7 @@ const ChatRoom = ({ width }) => { socket.off('update reaction') } } - }, [socket, channelId]) + }, [socket, channelId, params]) useEffect(() => { const option = { From 1c7ec67e4f438ce5bade9ebfebe89ad444e47e05 Mon Sep 17 00:00:00 2001 From: Ryou_Changyu Date: Thu, 17 Dec 2020 03:06:47 +0900 Subject: [PATCH 062/102] Refactor: update ThreadReactionCard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ThreadReactionCard 내에서 유저의 리액션 여부 판단하는 로직 삭제 --- .../ThreadReactionCard/ThreadReactionCard.js | 29 ++----------------- 1 file changed, 2 insertions(+), 27 deletions(-) diff --git a/frontend/src/container/ThreadReactionCard/ThreadReactionCard.js b/frontend/src/container/ThreadReactionCard/ThreadReactionCard.js index a2687e96..fec735d3 100644 --- a/frontend/src/container/ThreadReactionCard/ThreadReactionCard.js +++ b/frontend/src/container/ThreadReactionCard/ThreadReactionCard.js @@ -1,38 +1,13 @@ -import React, { useState, useEffect } from 'react' +import React from 'react' import styled from 'styled-components' -import { useRecoilValue } from 'recoil' -import { workspaceRecoil } from '../../store' import { COLOR } from '../../constant/style' function ThreadReactionCard({ reaction, updateReactionHandler }) { - const userInfo = useRecoilValue(workspaceRecoil) - const [myReaction, setMyReaction] = useState(false) - - useEffect(() => { - setMyReaction(hasMyReaction()) - }, [reaction.users.length, userInfo]) - - const hasMyReaction = () => { - if (reaction.users[0] === undefined) { - reaction.set = false - return false - } - const result = reaction.users.every(user => { - return user?._id !== userInfo?._id - }) - if (!result) { - reaction.set = true - } else { - reaction.set = false - } - return !result - } - return ( reaction.users.length !== 0 && ( updateReactionHandler(reaction.emoji)} - myReaction={myReaction} + myReaction={reaction.set} > {reaction.emoji} {reaction.users.length} From 140bb2f5fce25be025c6cc317025aedb5a234e3b Mon Sep 17 00:00:00 2001 From: Ryou_Changyu Date: Thu, 17 Dec 2020 03:07:40 +0900 Subject: [PATCH 063/102] Refactor: update EmojiModal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit EmojiModal에서 callback에 전달하는 변수 수정 --- frontend/src/presenter/EmojiModal/EmojiModal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/presenter/EmojiModal/EmojiModal.js b/frontend/src/presenter/EmojiModal/EmojiModal.js index 57d51b4b..91533ff3 100644 --- a/frontend/src/presenter/EmojiModal/EmojiModal.js +++ b/frontend/src/presenter/EmojiModal/EmojiModal.js @@ -6,7 +6,7 @@ import { SIZE } from '../../constant/style' function EmojiModal({ sendHandler, closeHandler, axisX, axisY }) { const addEmoji = emoji => { - if (emoji !== null) sendHandler(emoji) + if (emoji !== null) sendHandler(emoji.native) closeHandler() } const stopPropagation = e => { From 9c76d18a7e5cf53af997594b65f4052297cb87bc Mon Sep 17 00:00:00 2001 From: Ryou_Changyu Date: Thu, 17 Dec 2020 03:08:25 +0900 Subject: [PATCH 064/102] Feat: add reaction realtime update util MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit reaction 실시간 업데이트를 위한 유틸 추가 --- frontend/src/util/reactionUpdate.js | 59 +++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 frontend/src/util/reactionUpdate.js diff --git a/frontend/src/util/reactionUpdate.js b/frontend/src/util/reactionUpdate.js new file mode 100644 index 00000000..19c05a12 --- /dev/null +++ b/frontend/src/util/reactionUpdate.js @@ -0,0 +1,59 @@ +import produce from 'immer' + +export const chageReactionState = (messages, reaction) => + produce(messages, draft => { + let done = false + if (reaction.type === false) return + + draft.forEach(chat => { + if (chat._id === reaction.chatId) { + chat.reactions.forEach(element => { + if (element.emoji === reaction.emoji) { + if (reaction.type) { + element.users.push({ + _id: reaction.workspaceUserInfoId, + displayName: reaction.displayName, + }) + element.set = true + } else { + element.users.forEach((user, idx) => { + if (user._id === reaction.workspaceUserInfoId) { + element.users.splice(idx, 1) + element.set = false + } + }) + } + done = true + } + }) + if (!done && reaction.type === 1) { + chat.reactions.push({ + emoji: reaction.emoji, + users: [ + { + _id: reaction.workspaceUserInfoId, + displayName: reaction.displayName, + }, + ], + set: true, + }) + } + } + }) + }) + +export const hasMyReaction = (messages, workspaceUserInfo) => + produce(messages, draft => { + draft?.forEach(message => { + if (message?.reactions?.length === 0) return + message.reactions.forEach(reaction => { + reaction.set = false + if (reaction.users.length === 0) return + + const result = reaction.users.every(user => { + return user?._id !== workspaceUserInfo?._id + }) + if (!result) reaction.set = true + }) + }) + }) From e924a28ff18f5d0c74b9519d3206a5be0c0bae72 Mon Sep 17 00:00:00 2001 From: Ryou_Changyu Date: Thu, 17 Dec 2020 03:08:58 +0900 Subject: [PATCH 065/102] Refactor: update SideThreadBar reaction update logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SideThreadBar에서 reaction을 update하는 로직 수정 --- .../container/SideThreadBar/SideThreadBar.js | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/frontend/src/container/SideThreadBar/SideThreadBar.js b/frontend/src/container/SideThreadBar/SideThreadBar.js index 1c0373ee..88d5dd1a 100644 --- a/frontend/src/container/SideThreadBar/SideThreadBar.js +++ b/frontend/src/container/SideThreadBar/SideThreadBar.js @@ -9,13 +9,14 @@ import MessageEditor from '../MessageEditor' import Icon from '../../presenter/Icon' import { CLOSE } from '../../constant/icon' import { workspaceRecoil, socketRecoil } from '../../store' +import { hasMyReaction, chageReactionState } from '../../util/reactionUpdate' function SideThreadBar() { const { workspaceId, channelId, chatId } = useParams() const [sidebarChat, setSidebarChat] = useState(null) const [replyContent, setReplyContent] = useState(null) const socket = useRecoilValue(socketRecoil) - const messageEndRef = useRef(null) + const messageEndRef = useRef() const workspaceUserInfo = useRecoilValue(workspaceRecoil) const history = useHistory() @@ -27,8 +28,8 @@ function SideThreadBar() { chatId, }) if (data) { - setReplyContent(data.reply) - setSidebarChat(data) + setReplyContent(hasMyReaction(data.reply, workspaceUserInfo)) + setSidebarChat(hasMyReaction([data], workspaceUserInfo)) } if (data === false) history.push(`/workspace/${workspaceId}/${channelId}`) } @@ -38,20 +39,29 @@ function SideThreadBar() { } const scrollTo = (targetRef = messageEndRef.current) => { - targetRef.scrollIntoView() + if (targetRef) targetRef.scrollIntoView() } useEffect(() => { - socket && + if (socket) { socket.on('new reply', ({ message }) => { if (message.chatId === chatId) { setReplyContent(messages => [...messages, message]) } scrollTo() }) + socket.on('update reaction', ({ reaction }) => { + if (reaction.chatId === chatId) + setSidebarChat(chat => chageReactionState(chat, reaction)) + if (reaction.parentId) { + setReplyContent(reply => chageReactionState(reply, reaction)) + } + }) + } return () => { if (socket) { - socket.off('new reply') + socket.off('new message') + socket.off('update reaction') } } }, [socket, chatId]) @@ -70,6 +80,12 @@ function SideThreadBar() { socket.emit('new reply', reply) } + useEffect(() => { + setReplyContent(reply => hasMyReaction(reply, workspaceUserInfo)) + setSidebarChat(chat => hasMyReaction(chat, workspaceUserInfo)) + scrollTo() + }, [workspaceUserInfo]) + useEffect(() => { if (chatId !== undefined) loadReplyMessage(workspaceId, channelId, chatId) }, [chatId]) @@ -84,7 +100,7 @@ function SideThreadBar() { - {sidebarChat && } + {sidebarChat && } {replyContent && From d75e79e2346444ec8e92d2d2287b490da3bdab6a Mon Sep 17 00:00:00 2001 From: rockpell Date: Thu, 17 Dec 2020 03:52:39 +0900 Subject: [PATCH 066/102] Docs: file api MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit getFileURL api 삭제 --- backend/docs/swagger.yaml | 87 ++++++++------------------------------- 1 file changed, 17 insertions(+), 70 deletions(-) diff --git a/backend/docs/swagger.yaml b/backend/docs/swagger.yaml index aea229e1..ba7d8ed0 100644 --- a/backend/docs/swagger.yaml +++ b/backend/docs/swagger.yaml @@ -1353,17 +1353,17 @@ paths: default: description: Default error sample response - '/api/file/{fileId}': - summary: get chat messages - description: get chat messages - get: + '/api/file': + summary: file upload + description: file upload + post: tags: - file operationId: '' parameters: - - name: fileId - in: path - description: fileId + - name: file + in: query + description: input form으로 생성된 file object required: true explode: true schema: @@ -1380,11 +1380,16 @@ paths: data: type: object properties: - url: + name: type: string originalName: type: string - + fileType: + type: string + creator: + type: string + url: + type: string description: success response '400': content: @@ -1411,9 +1416,9 @@ paths: - file operationId: '' parameters: - - name: fileId - in: path - description: fileId + - name: name + in: query + description: file name required: true explode: true schema: @@ -1449,64 +1454,6 @@ paths: description: unauthorized default: description: Default error sample response - '/api/file': - summary: file upload - description: file upload - post: - tags: - - file - operationId: '' - parameters: - - name: file - in: query - description: file - required: true - explode: true - schema: - type: string - responses: - '200': - content: - application/json: - schema: - type: object - properties: - success: - type: string - data: - type: object - properties: - fileId: - type: string - fileName: - type: string - fileType: - type: string - creator: - type: string - etag: - type: string - description: success response - '400': - content: - application/json: - schema: - type: object - properties: - success: - type: boolean - description: not modified - '401': - content: - application/json: - schema: - type: object - properties: - success: - type: boolean - description: unauthorized - default: - description: Default error sample response components: schemas: From 962aa04dc00f791671d236323570ba080a5ecb6c Mon Sep 17 00:00:00 2001 From: Ryou_Changyu Date: Thu, 17 Dec 2020 04:14:05 +0900 Subject: [PATCH 067/102] Feat: add git action auto deploy script MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git action을 이용한 자동배포 스크립트 작성 --- .github/workflows/deploy.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .github/workflows/deploy.yml diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 00000000..51d2fe04 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,30 @@ +name: deploy + +# Controls when the action will run. Triggers the workflow on push or pull request +# events but only for the main branch +on: + push: + branches: master + +jobs: + build: + name: Build and Deploy + runs-on: ubuntu-latest + steps: + - name: Deploy backend server + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.BACKEND_HOST }} + username: ${{ secrets.BACKEND_USERNAME }} + password: ${{ secrets.BACKEND_PASSWORD }} + script: | + bash deploy-backend.sh + + - name: Deploy frontend server + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.FRONTEND_HOST }} + username: ${{ secrets.FRONTEND_USERNAME }} + password: ${{ secrets.FRONTEND_PASSWORD }} + script: | + bash deploy-frontend.sh From 324dc03a56ec82f8aa0a0d1934b1e8f08db3e747 Mon Sep 17 00:00:00 2001 From: Ryou_Changyu Date: Thu, 17 Dec 2020 04:35:00 +0900 Subject: [PATCH 068/102] Fix: InviteWorkspaceModal textarea warning fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit InviteWorkspaceModal의 textarea에 onChange 혹은 readOnly 옵션을 설정해주지 않아 경고가 발생하는 문제 수정 --- .../Modal/InviteWorkspaceModal/InviteWorkspaceModal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/container/Modal/InviteWorkspaceModal/InviteWorkspaceModal.js b/frontend/src/container/Modal/InviteWorkspaceModal/InviteWorkspaceModal.js index 623720eb..9fc28ba5 100644 --- a/frontend/src/container/Modal/InviteWorkspaceModal/InviteWorkspaceModal.js +++ b/frontend/src/container/Modal/InviteWorkspaceModal/InviteWorkspaceModal.js @@ -36,7 +36,7 @@ const InviteWorkspaceModal = ({ handleClose }) => { - + From 97c6bbf334d77a0641aeebdfa02abbcfca60ecf3 Mon Sep 17 00:00:00 2001 From: Ryou_Changyu Date: Thu, 17 Dec 2020 04:36:08 +0900 Subject: [PATCH 069/102] Fix: channelId exception bug fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit url의 channelId가 ObjectId가 아닌 메뉴의 이름인 경우 데이터 요청을 하지 않도록 조건 분기처리 --- frontend/src/api/channel.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/frontend/src/api/channel.js b/frontend/src/api/channel.js index 622d5ef2..8e082721 100644 --- a/frontend/src/api/channel.js +++ b/frontend/src/api/channel.js @@ -18,6 +18,14 @@ export const getChannelHeaderInfo = async ({ channelId, workspaceUserInfoId, }) => { + if ( + channelId === 'threads' || + channelId === 'all-dms' || + channelId === 'saved-page' || + channelId === 'activity-page' || + channelId === 'more' + ) + return null const { data } = await Request.GET(`/api/channel/${channelId}/info`, { workspaceUserInfoId, }) From 6230aa43fed56cd9135f9b7be0f149c5c1963aeb Mon Sep 17 00:00:00 2001 From: Ryou_Changyu Date: Thu, 17 Dec 2020 04:53:16 +0900 Subject: [PATCH 070/102] Refactor: update createdAt timeZone MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 작성시간 후 흐른 시간 표기 유틸 추가 - 기존 utc 기준으로 표기되어져 있는 createdAt을 한국시간에 맞춰 변경해 표기 - hover시 정확한 시간이 표기되도록 title 추가 --- frontend/src/presenter/ChatContent/ChatContent.js | 9 ++++++++- frontend/src/util/calculateTime.js | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/frontend/src/presenter/ChatContent/ChatContent.js b/frontend/src/presenter/ChatContent/ChatContent.js index d23953c3..28833352 100644 --- a/frontend/src/presenter/ChatContent/ChatContent.js +++ b/frontend/src/presenter/ChatContent/ChatContent.js @@ -2,6 +2,7 @@ import React, { memo } from 'react' import styled from 'styled-components' import { convertFromRaw, Editor, EditorState } from 'draft-js' import { COLOR } from '../../constant/style' +import calculateTime from '../../util/calculateTime' const ChatContent = memo( ({ displayName, createdAt, contents, handleProfileModal }) => { @@ -11,7 +12,13 @@ const ChatContent = memo( {displayName} - {createdAt} + + {calculateTime(createdAt)} + Date: Thu, 17 Dec 2020 05:16:33 +0900 Subject: [PATCH 071/102] Refactor: update favicon and index title MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit favicon slack icon으로 변경 및 index title을 Slack-겁나 빠른 슬랙 프로젝트로 수정 --- frontend/public/favicon.ico | Bin 3870 -> 107764 bytes frontend/public/index.html | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/public/favicon.ico b/frontend/public/favicon.ico index a11777cc471a4344702741ab1c8a588998b1311a..c3219dc218f1d61fef342eec1594dc29094460be 100644 GIT binary patch literal 107764 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 baj7?x%VD!5Cxk2YbYLNVeiXvvpMCWYo=by@ diff --git a/frontend/public/index.html b/frontend/public/index.html index 94b634da..478a9652 100644 --- a/frontend/public/index.html +++ b/frontend/public/index.html @@ -24,10 +24,10 @@ work correctly both with client-side routing and a non-root public URL. Learn how to configure a non-root public URL by running `npm run build`. --> - React App + Slack-겁나 빠른 슬랙 프로젝트 - +