From 4a3105ea54b5ca60e2d2e7dd8f9b2f2e178b35f7 Mon Sep 17 00:00:00 2001 From: hzoou Date: Mon, 16 Dec 2019 17:00:15 +0900 Subject: [PATCH 01/54] =?UTF-8?q?chore:=20=EB=9D=BC=EC=9D=B4=EB=B8=8C=20?= =?UTF-8?q?=ED=83=AD=20=EC=BB=A8=ED=85=8C=EC=9D=B4=EB=84=88=20=EA=B5=AC?= =?UTF-8?q?=EC=A1=B0=20=EB=B0=8F=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 라이브 탭 컨테이너의 폴더를 분리하였고, 사소한 스타일을 수정했습니다. --- .../Project/LiveUserProfile/index.js | 4 +- .../Project/LiveUserProfile/style.js | 18 +++- cocode/src/constants/theme.js | 7 +- .../LiveOff.js => LiveOffTab/index.js} | 13 +-- .../containers/Project/LiveOffTab/style.js | 52 ++++++++++++ .../containers/Project/LiveOnTab/close.svg | 4 + .../{LiveTab/LiveOn.js => LiveOnTab/index.js} | 15 ++-- .../src/containers/Project/LiveOnTab/style.js | 83 +++++++++++++++++++ .../src/containers/Project/LiveTab/index.js | 18 ++-- .../src/containers/Project/LiveTab/style.js | 72 +--------------- 10 files changed, 190 insertions(+), 96 deletions(-) rename cocode/src/containers/Project/{LiveTab/LiveOff.js => LiveOffTab/index.js} (57%) create mode 100644 cocode/src/containers/Project/LiveOffTab/style.js create mode 100644 cocode/src/containers/Project/LiveOnTab/close.svg rename cocode/src/containers/Project/{LiveTab/LiveOn.js => LiveOnTab/index.js} (74%) create mode 100644 cocode/src/containers/Project/LiveOnTab/style.js diff --git a/cocode/src/components/Project/LiveUserProfile/index.js b/cocode/src/components/Project/LiveUserProfile/index.js index d2691b77..608dc2a0 100644 --- a/cocode/src/components/Project/LiveUserProfile/index.js +++ b/cocode/src/components/Project/LiveUserProfile/index.js @@ -18,7 +18,7 @@ function LiveUserProfile({ username, avatar }) { function LiveUsers({ owner: { username, avatar }, participants = [] }) { return ( - <> + OWNERS USERS @@ -31,7 +31,7 @@ function LiveUsers({ owner: { username, avatar }, participants = [] }) { /> ); })} - + ); } diff --git a/cocode/src/components/Project/LiveUserProfile/style.js b/cocode/src/components/Project/LiveUserProfile/style.js index 8f4a7679..5bad1c07 100644 --- a/cocode/src/components/Project/LiveUserProfile/style.js +++ b/cocode/src/components/Project/LiveUserProfile/style.js @@ -1,6 +1,12 @@ import styled from 'styled-components'; import { TAB_CONTAINER_THEME, LIVE_TAB_THEME } from 'constants/theme'; +const Container = styled.div` + & { + margin: 1rem 0; + } +`; + const Title = styled.h1` & { color: ${TAB_CONTAINER_THEME.tabContainerTitleColor}; @@ -12,13 +18,14 @@ const Title = styled.h1` const UserProfile = styled.div` & { display: flex; + margin: 0.7rem 0; } `; const UserName = styled.div` & { align-self: center; - margin-left: 0.4rem; + margin-left: 0.5rem; font-weight: 100; font-size: 1rem; } @@ -38,4 +45,11 @@ const SelfLabel = styled(UserName)` } `; -export { Title, UserProfile, UserName, UserAvatar, SelfLabel }; +export { + Container, + Title, + UserProfile, + UserName, + UserAvatar, + SelfLabel +}; diff --git a/cocode/src/constants/theme.js b/cocode/src/constants/theme.js index aa4cf2ca..9aa9a006 100644 --- a/cocode/src/constants/theme.js +++ b/cocode/src/constants/theme.js @@ -100,12 +100,13 @@ const INFO_TAB_THEME = { }; const LIVE_TAB_THEME = { - liveButtonBGColorHover: '#C74040B2', - liveButtonBGColor: '#880000', + liveButtonBGColorHover: '#880000', + liveButtonBGColor: '#880000ba', liveFontColor: '#bcbcbc', liveStatusLabelColor: '#c74040b3', liveLinkBGColor: '#000000', - liveSelfLabelColor: '#333333' + liveSelfLabelColor: '#333333', + liveCircleBGColor: '#ffffff' }; const FILE_TAB_THEME = { diff --git a/cocode/src/containers/Project/LiveTab/LiveOff.js b/cocode/src/containers/Project/LiveOffTab/index.js similarity index 57% rename from cocode/src/containers/Project/LiveTab/LiveOff.js rename to cocode/src/containers/Project/LiveOffTab/index.js index 36732394..cd446c29 100644 --- a/cocode/src/containers/Project/LiveTab/LiveOff.js +++ b/cocode/src/containers/Project/LiveOffTab/index.js @@ -5,13 +5,16 @@ const OFF_BUTTON_LABEL = 'Go Live'; const OFF_DESCRIPTION = 'Invite others to live edit this coconut with you. We’re doing it live!'; -function LiveOff({ onClick }) { +function LiveOffTab({ onClick }) { return ( - <> + {OFF_DESCRIPTION} - {OFF_BUTTON_LABEL} - + + + {OFF_BUTTON_LABEL} + + ); } -export default LiveOff; +export default LiveOffTab; diff --git a/cocode/src/containers/Project/LiveOffTab/style.js b/cocode/src/containers/Project/LiveOffTab/style.js new file mode 100644 index 00000000..5cb0fa7a --- /dev/null +++ b/cocode/src/containers/Project/LiveOffTab/style.js @@ -0,0 +1,52 @@ +import styled from 'styled-components'; +import { LIVE_TAB_THEME } from 'constants/theme'; + +const Container = styled.div` + & { + margin: 0.7rem 1rem; + } +`; + +const Description = styled.div` + & { + color: ${LIVE_TAB_THEME.liveFontColor}; + font-size: 1rem; + font-weight: 100; + margin-bottom: 1rem; + } +`; + +const Button = styled.button` + & { + display: flex; + align-items: center; + justify-content: center; + width: -webkit-fill-available; + padding: 0.7rem 2.2rem; + border-radius: 0.7rem; + background-color: ${LIVE_TAB_THEME.liveButtonBGColor}; + font-size: 1rem; + font-weight: 400; + } + + &:hover { + background-color: ${LIVE_TAB_THEME.liveButtonBGColorHover}; + } +`; + +const Circle = styled.div` + & { + width: 0.5rem; + height: 0.5rem; + margin-right: 0.5rem; + background-color: ${LIVE_TAB_THEME.liveCircleBGColor}; + border-radius: 50%; + } +`; + +export { + Container, + Description, + Button, + Circle +}; diff --git a/cocode/src/containers/Project/LiveOnTab/close.svg b/cocode/src/containers/Project/LiveOnTab/close.svg new file mode 100644 index 00000000..6adde6f1 --- /dev/null +++ b/cocode/src/containers/Project/LiveOnTab/close.svg @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/cocode/src/containers/Project/LiveTab/LiveOn.js b/cocode/src/containers/Project/LiveOnTab/index.js similarity index 74% rename from cocode/src/containers/Project/LiveTab/LiveOn.js rename to cocode/src/containers/Project/LiveOnTab/index.js index 3e0e03ac..aca18226 100644 --- a/cocode/src/containers/Project/LiveTab/LiveOn.js +++ b/cocode/src/containers/Project/LiveOnTab/index.js @@ -1,6 +1,8 @@ import React, { useContext } from 'react'; import { LiveContext } from 'contexts'; import * as Styled from './style'; + +import close from './close.svg'; import LiveUserProfile from 'components/Project/LiveUserProfile'; const LIVE_STATUS_LABEL = 'You’ve gone live!'; @@ -8,21 +10,24 @@ const ON_BUTTON_LABEL = 'Stop Live'; const ON_DESCRIPTION = 'Share this link with others to invite them to the live.'; -function LiveOn({ onClick }) { +function LiveOnTab({ onClick }) { const { url, participants, owner } = useContext(LiveContext); return ( - <> + {LIVE_STATUS_LABEL} {ON_DESCRIPTION} {url} - {ON_BUTTON_LABEL} + + + {ON_BUTTON_LABEL} + - + ); } -export default LiveOn; +export default LiveOnTab; diff --git a/cocode/src/containers/Project/LiveOnTab/style.js b/cocode/src/containers/Project/LiveOnTab/style.js new file mode 100644 index 00000000..bbad87a1 --- /dev/null +++ b/cocode/src/containers/Project/LiveOnTab/style.js @@ -0,0 +1,83 @@ +import styled from 'styled-components'; +import { LIVE_TAB_THEME } from 'constants/theme'; + +const Container = styled.div` + & { + margin: 0.7rem 1rem; + } +`; + +const Description = styled.div` + & { + margin-bottom: 1rem; + color: ${LIVE_TAB_THEME.liveFontColor}; + font-size: 1rem; + font-weight: 100; + } +`; + +const Button = styled.button` + & { + display: flex; + align-items: center; + justify-content: center; + width: -webkit-fill-available; + padding: 0.7rem 2.2rem; + border-radius: 0.7rem; + background-color: ${LIVE_TAB_THEME.liveButtonBGColor}; + font-size: 1rem; + font-weight: 400; + } + + &:hover { + background-color: ${LIVE_TAB_THEME.liveButtonBGColorHover}; + } +`; + +const Close = styled.img` + & { + width: 0.8rem; + height: 0.8rem; + margin-right: 0.5rem; + } +`; + +const LiveStatusLabel = styled.div` + & { + display: flex; + margin-bottom: 1rem; + color: ${LIVE_TAB_THEME.liveStatusLabelColor}; + font-size: 1.1rem; + font-weight: lighter; + } +`; + +const LiveStatusSpan = styled.span` + & { + background-color: ${LIVE_TAB_THEME.liveStatusLabelColor}; + margin: auto 1rem auto 0.3rem; + width: 0.6rem; + height: 0.6rem; + border-radius: 50%; + } +`; + +const LinkURL = styled.div` + & { + padding: 0.5rem 1rem; + margin-bottom: 0.5rem; + background-color: ${LIVE_TAB_THEME.liveLinkBGColor}; + color: ${LIVE_TAB_THEME.liveFontColor}; + font-size: 0.9rem; + } +`; + +export { + Container, + Description, + Button, + Close, + LiveStatusLabel, + LiveStatusSpan, + LinkURL +}; diff --git a/cocode/src/containers/Project/LiveTab/index.js b/cocode/src/containers/Project/LiveTab/index.js index d58cc615..30c9e2ed 100644 --- a/cocode/src/containers/Project/LiveTab/index.js +++ b/cocode/src/containers/Project/LiveTab/index.js @@ -1,13 +1,13 @@ import React, { useContext } from 'react'; import * as Styled from './style'; import { LiveContext } from 'contexts'; -import LiveOn from './LiveOn'; -import LiveOff from './LiveOff'; import { liveOffActionCreator, fetchLiveActionCreator } from 'actions/Live'; +import LiveOffTab from 'containers/Project/LiveOffTab'; +import LiveOnTab from 'containers/Project/LiveOnTab'; import avatar from 'components/Common/UserProfile/avatar.jpeg'; -const TAB_TATILE = 'LIVE'; +const TAB_TITLE = 'LIVE'; const dummy = { url: 'https://cocode.com/live/', @@ -26,7 +26,7 @@ const dummy = { } ], owner: { - username: 'lallaheeee', + username: 'hzoou', avatar } }; @@ -40,14 +40,14 @@ function LiveTab() { }; return ( <> - {TAB_TATILE} - + {TAB_TITLE} +
{url ? ( - + ) : ( - + )} - +
); } diff --git a/cocode/src/containers/Project/LiveTab/style.js b/cocode/src/containers/Project/LiveTab/style.js index 67666437..d2378806 100644 --- a/cocode/src/containers/Project/LiveTab/style.js +++ b/cocode/src/containers/Project/LiveTab/style.js @@ -1,15 +1,5 @@ import styled from 'styled-components'; -import { TAB_CONTAINER_THEME, LIVE_TAB_THEME } from 'constants/theme'; - -const Wrapper = styled.div` - & { - padding: 0.7rem 1rem; - } - - & > * { - padding: 0.5rem 0; - } -`; +import { TAB_CONTAINER_THEME } from 'constants/theme'; const Title = styled.h1` & { @@ -21,64 +11,6 @@ const Title = styled.h1` } `; -const Description = styled.div` - & { - color: ${LIVE_TAB_THEME.liveFontColor}; - font-size: 1rem; - font-weight: 100; - } -`; - -const Button = styled.button` - & { - width: -webkit-fill-available; - margin: 1rem 0; - padding: 1rem 2.2rem; - border-radius: 0.3rem; - background-color: ${LIVE_TAB_THEME.liveButtonBGColor}; - font-size: 1rem; - font-weight: 400; - } - - &:hover { - background-color: ${LIVE_TAB_THEME.liveButtonBGColorHover}; - } -`; - -const LiveStatusLabel = styled.div` - & { - display: flex; - color: ${LIVE_TAB_THEME.liveStatusLabelColor}; - font-size: 1.1rem; - font-weight: lighter; - } -`; - -const LiveStatusSpan = styled.span` - & { - background-color: ${LIVE_TAB_THEME.liveStatusLabelColor}; - margin: auto 1rem auto 0.3rem; - width: 0.6rem; - height: 0.6rem; - border-radius: 50%; - } -`; - -const LinkURL = styled.div` - & { - background-color: ${LIVE_TAB_THEME.liveLinkBGColor}; - color: ${LIVE_TAB_THEME.liveFontColor}; - font-size: 0.9rem; - padding-left: 1rem; - } -`; - export { - Wrapper, - Title, - Description, - Button, - LiveStatusLabel, - LiveStatusSpan, - LinkURL + Title }; From 24bd80b3983daa2f00b6dc5632e87b9fb720e0c6 Mon Sep 17 00:00:00 2001 From: hzoou Date: Mon, 16 Dec 2019 17:30:34 +0900 Subject: [PATCH 02/54] =?UTF-8?q?chore:=20socket=20=ED=81=B4=EB=9D=BC?= =?UTF-8?q?=EC=9D=B4=EC=96=B8=ED=8A=B8=20=EC=84=A4=EC=B9=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 라이브 기능을 제공하기 위해 웹 소켓을 설치했습니다. --- cocode/package.json | 1 + cocode/yarn.lock | 176 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 175 insertions(+), 2 deletions(-) diff --git a/cocode/package.json b/cocode/package.json index b7884a5f..14dc6c4c 100644 --- a/cocode/package.json +++ b/cocode/package.json @@ -54,6 +54,7 @@ "react-router-dom": "^5.1.2", "react-scripts": "^3.2.0", "react-style-proptype": "^3.2.2", + "socket.io-client": "^2.3.0", "styled-components": "^4.4.1", "webpack": "^4.41.2", "worker-loader": "^2.0.0" diff --git a/cocode/yarn.lock b/cocode/yarn.lock index bebc62d7..24d05277 100644 --- a/cocode/yarn.lock +++ b/cocode/yarn.lock @@ -2505,6 +2505,11 @@ adjust-sourcemap-loader@2.0.0: object-path "0.11.4" regex-parser "2.2.10" +after@0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" + integrity sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8= + aggregate-error@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.0.1.tgz#db2fe7246e536f40d9b5442a39e117d7dd6a24e0" @@ -2792,6 +2797,11 @@ array.prototype.flatmap@^1.2.1: es-abstract "^1.15.0" function-bind "^1.1.1" +arraybuffer.slice@~0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675" + integrity sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog== + arrify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" @@ -3358,11 +3368,21 @@ babylon@^6.18.0: resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ== +backo2@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" + integrity sha1-MasayLEpNjRj41s+u2n038+6eUc= + balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= +base64-arraybuffer@0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8" + integrity sha1-c5JncZI7Whl0etZmqlzUv5xunOg= + base64-js@^1.0.2: version "1.3.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" @@ -3398,6 +3418,13 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" +better-assert@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/better-assert/-/better-assert-1.0.2.tgz#40866b9e1b9e0b55b481894311e68faffaebc522" + integrity sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI= + dependencies: + callsite "1.0.0" + big.js@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" @@ -3408,6 +3435,11 @@ binary-extensions@^1.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== +blob@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.5.tgz#d680eeef25f8cd91ad533f5b01eed48e64caf683" + integrity sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig== + bluebird@^3.3.5, bluebird@^3.5.5: version "3.7.1" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.1.tgz#df70e302b471d7473489acf26a93d63b53f874de" @@ -3710,6 +3742,11 @@ caller-path@^2.0.0: dependencies: caller-callsite "^2.0.0" +callsite@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/callsite/-/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20" + integrity sha1-KAOY5dZkvXQDi28JBRU+borxvCA= + callsites@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" @@ -4119,11 +4156,26 @@ compare-func@^1.3.1: array-ify "^1.0.0" dot-prop "^3.0.0" +component-bind@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1" + integrity sha1-AMYIq33Nk4l8AAllGx06jh5zu9E= + +component-emitter@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" + integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY= + component-emitter@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== +component-inherit@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143" + integrity sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM= + compose-function@3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/compose-function/-/compose-function-3.0.3.tgz#9ed675f13cc54501d30950a486ff6a7ba3ab185f" @@ -4782,7 +4834,7 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.8, debug@^2.6. dependencies: ms "2.0.0" -debug@=3.1.0: +debug@=3.1.0, debug@~3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== @@ -4796,7 +4848,7 @@ debug@^3.0.0, debug@^3.1.1, debug@^3.2.5, debug@^3.2.6: dependencies: ms "^2.1.1" -debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: +debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@~4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== @@ -5322,6 +5374,34 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0: dependencies: once "^1.4.0" +engine.io-client@~3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-3.4.0.tgz#82a642b42862a9b3f7a188f41776b2deab643700" + integrity sha512-a4J5QO2k99CM2a0b12IznnyQndoEvtA4UAldhGzKqnHf42I3Qs2W5SPnDvatZRcMaNZs4IevVicBPayxYt6FwA== + dependencies: + component-emitter "1.2.1" + component-inherit "0.0.3" + debug "~4.1.0" + engine.io-parser "~2.2.0" + has-cors "1.1.0" + indexof "0.0.1" + parseqs "0.0.5" + parseuri "0.0.5" + ws "~6.1.0" + xmlhttprequest-ssl "~1.5.4" + yeast "0.1.2" + +engine.io-parser@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-2.2.0.tgz#312c4894f57d52a02b420868da7b5c1c84af80ed" + integrity sha512-6I3qD9iUxotsC5HEMuuGsKA0cXerGz+4uGcXQEkfBidgKf0amsjrrtwcbwK/nzpZBxclXlV7gGl9dgWvu4LF6w== + dependencies: + after "0.8.2" + arraybuffer.slice "~0.0.7" + base64-arraybuffer "0.1.5" + blob "0.0.5" + has-binary2 "~1.0.2" + enhanced-resolve@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz#41c7e0bfdfe74ac1ffe1e57ad6a5c6c9f3742a7f" @@ -6680,6 +6760,18 @@ has-ansi@^2.0.0: dependencies: ansi-regex "^2.0.0" +has-binary2@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-binary2/-/has-binary2-1.0.3.tgz#7776ac627f3ea77250cfc332dab7ddf5e4f5d11d" + integrity sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw== + dependencies: + isarray "2.0.1" + +has-cors@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39" + integrity sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk= + has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -7144,6 +7236,11 @@ indexes-of@^1.0.1: resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc= +indexof@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" + integrity sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10= + infer-owner@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" @@ -7684,6 +7781,11 @@ isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= +isarray@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e" + integrity sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4= + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -9570,6 +9672,11 @@ object-assign@4.1.1, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^ resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= +object-component@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/object-component/-/object-component-0.0.3.tgz#f0c69aa50efc95b866c186f400a33769cb2f1291" + integrity sha1-8MaapQ78lbhmwYb0AKM3acsvEpE= + object-copy@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" @@ -9983,6 +10090,20 @@ parse5@5.1.0: resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.0.tgz#c59341c9723f414c452975564c7c00a68d58acd2" integrity sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ== +parseqs@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d" + integrity sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0= + dependencies: + better-assert "~1.0.0" + +parseuri@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/parseuri/-/parseuri-0.0.5.tgz#80204a50d4dbb779bfdc6ebe2778d90e4bce320a" + integrity sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo= + dependencies: + better-assert "~1.0.0" + parseurl@~1.3.2, parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -12628,6 +12749,35 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" +socket.io-client@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-2.3.0.tgz#14d5ba2e00b9bcd145ae443ab96b3f86cbcc1bb4" + integrity sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA== + dependencies: + backo2 "1.0.2" + base64-arraybuffer "0.1.5" + component-bind "1.0.0" + component-emitter "1.2.1" + debug "~4.1.0" + engine.io-client "~3.4.0" + has-binary2 "~1.0.2" + has-cors "1.1.0" + indexof "0.0.1" + object-component "0.0.3" + parseqs "0.0.5" + parseuri "0.0.5" + socket.io-parser "~3.3.0" + to-array "0.1.4" + +socket.io-parser@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.3.0.tgz#2b52a96a509fdf31440ba40fed6094c7d4f1262f" + integrity sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng== + dependencies: + component-emitter "1.2.1" + debug "~3.1.0" + isarray "2.0.1" + sockjs-client@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.3.0.tgz#12fc9d6cb663da5739d3dc5fb6e8687da95cb177" @@ -13353,6 +13503,11 @@ tmpl@1.0.x: resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" integrity sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE= +to-array@0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890" + integrity sha1-F+bBH3PdTz10zaek/zI46a2b+JA= + to-arraybuffer@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" @@ -14357,6 +14512,13 @@ ws@^6.1.2, ws@^6.2.1: dependencies: async-limiter "~1.0.0" +ws@~6.1.0: + version "6.1.4" + resolved "https://registry.yarnpkg.com/ws/-/ws-6.1.4.tgz#5b5c8800afab925e94ccb29d153c8d02c1776ef9" + integrity sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA== + dependencies: + async-limiter "~1.0.0" + xml-name-validator@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" @@ -14367,6 +14529,11 @@ xmlchars@^2.1.1: resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== +xmlhttprequest-ssl@~1.5.4: + version "1.5.5" + resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e" + integrity sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4= + xregexp@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.0.0.tgz#e698189de49dd2a18cc5687b05e17c8e43943020" @@ -14490,3 +14657,8 @@ yargs@^13.3.0: which-module "^2.0.0" y18n "^4.0.0" yargs-parser "^13.1.1" + +yeast@0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" + integrity sha1-AI4G2AlDIMNy28L47XagymyKxBk= From 18db8bdd4425b193234d0d639ff8675b22486c5f Mon Sep 17 00:00:00 2001 From: hzoou Date: Mon, 16 Dec 2019 21:55:59 +0900 Subject: [PATCH 03/54] =?UTF-8?q?chore:=20=ED=99=98=EA=B2=BD=EB=B3=80?= =?UTF-8?q?=EC=88=98=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 라이브 서버를 환경변수에 등록했습니다. --- cocode/dev.env | 7 +++++++ cocode/src/config/index.js | 19 ++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/cocode/dev.env b/cocode/dev.env index 26366660..da0b0cf0 100644 --- a/cocode/dev.env +++ b/cocode/dev.env @@ -1,4 +1,5 @@ SKIP_PREFLIGHT_CHECK=true + DEV_API_SERVER_IP= PROD_API_SERVER_IP= @@ -7,3 +8,9 @@ DEV_DEPENDENCY_SERVER_IP= PROD_COCONUT_SERVER_IP= DEV_COCONUT_SERVER_IP= + +PROD_LIVE_SERVER_IP= +DEV_LIVE_SERVER_IP= + +PROD_COCODE_SERVER_IP= +DEV_COCODE_SERVER_IP= \ No newline at end of file diff --git a/cocode/src/config/index.js b/cocode/src/config/index.js index 3e7fdc94..7984ec25 100644 --- a/cocode/src/config/index.js +++ b/cocode/src/config/index.js @@ -13,6 +13,16 @@ const COCONUT_SERVER = ? process.env.PROD_COCONUT_SERVER_IP : process.env.DEV_COCONUT_SERVER_IP; +const LIVE_SERVER = + process.env.NODE_ENV === 'production' + ? process.env.PROD_LIVE_SERVER_IP + : process.env.DEV_LIVE_SERVER_IP; + +const COCODE_SERVER = + process.env.NODE_ENV === 'production' + ? process.env.PROD_COCODE_SERVER_IP + : process.env.DEV_COCODE_SERVER_IP; + const DEFAULT_REQUEST_OPTION = { withCredentials: true, mode: 'cors', @@ -32,4 +42,11 @@ const DEPENDENCY = { modules: `${DEPENDENCY_SERVER}/modules` }; -export { DEFAULT_REQUEST_OPTION, API, DEPENDENCY, COCONUT_SERVER }; +export { + DEFAULT_REQUEST_OPTION, + API, + DEPENDENCY, + COCONUT_SERVER, + LIVE_SERVER, + COCODE_SERVER +}; From a22b8cd751aabf2b56a20de7f5fbaf6864da5cb1 Mon Sep 17 00:00:00 2001 From: hzoou Date: Wed, 18 Dec 2019 16:21:16 +0900 Subject: [PATCH 04/54] =?UTF-8?q?chore:=20=EB=9D=BC=EC=9D=B4=EB=B8=8C=20?= =?UTF-8?q?=EC=BB=A8=ED=85=8D=EC=8A=A4=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 라이브 컨텍스트에서 사용하는 action, reducer, store를 수정하였습니다. --- cocode/src/actions/Live.js | 17 +++++------- cocode/src/actions/types.js | 6 ++--- cocode/src/reducers/LiveReducer.js | 43 +++++++++++++----------------- cocode/src/stores/LiveStore.js | 29 +++++++++++++------- 4 files changed, 47 insertions(+), 48 deletions(-) diff --git a/cocode/src/actions/Live.js b/cocode/src/actions/Live.js index e9ca1565..b04f9e52 100644 --- a/cocode/src/actions/Live.js +++ b/cocode/src/actions/Live.js @@ -1,31 +1,26 @@ import { - FETCH_LIVE, LIVE_ON, LIVE_OFF, LIVE_JOIN_USER, - LIVE_LEFT_USER + LIVE_LEAVE_USER } from './types'; -function fetchLiveActionCreator(payload) { - return { type: FETCH_LIVE, payload }; -} function liveOnActionCreator(payload) { return { type: LIVE_ON, payload }; } -function liveOffActionCreator(payload) { - return { type: LIVE_OFF, payload }; +function liveOffActionCreator() { + return { type: LIVE_OFF }; } function liveJoinUserActionCreator(payload) { return { type: LIVE_JOIN_USER, payload }; } -function liveLeftUserActionCreator(payload) { - return { type: LIVE_LEFT_USER, payload }; +function liveLeaveUserActionCreator(payload) { + return { type: LIVE_LEAVE_USER, payload }; } export { - fetchLiveActionCreator, liveOnActionCreator, liveOffActionCreator, liveJoinUserActionCreator, - liveLeftUserActionCreator + liveLeaveUserActionCreator }; diff --git a/cocode/src/actions/types.js b/cocode/src/actions/types.js index d57ac920..18cb6ee5 100644 --- a/cocode/src/actions/types.js +++ b/cocode/src/actions/types.js @@ -24,11 +24,10 @@ const UPDATE_COCONUT_NAME = 'updateCoconutName'; const DELETE_COCONUT = 'deleteCoconut'; //Live -const FETCH_LIVE = 'fetchLive'; const LIVE_ON = 'liveOn'; const LIVE_OFF = 'liveOff'; const LIVE_JOIN_USER = 'liveJoinUser'; -const LIVE_LEFT_USER = 'liveLeftUser'; +const LIVE_LEAVE_USER = 'liveLeaveUser'; export { API_READY, @@ -49,10 +48,9 @@ export { FETCH_COCONUT, UPDATE_COCONUT_NAME, DELETE_COCONUT, - FETCH_LIVE, LIVE_ON, LIVE_OFF, LIVE_JOIN_USER, - LIVE_LEFT_USER, + LIVE_LEAVE_USER, SAVE_FILE }; diff --git a/cocode/src/reducers/LiveReducer.js b/cocode/src/reducers/LiveReducer.js index dbc21e3a..0509c9ab 100644 --- a/cocode/src/reducers/LiveReducer.js +++ b/cocode/src/reducers/LiveReducer.js @@ -1,49 +1,44 @@ import { - FETCH_LIVE, LIVE_ON, LIVE_OFF, LIVE_JOIN_USER, - LIVE_LEFT_USER + LIVE_LEAVE_USER } from 'actions/types'; -const fetchLive = (state, { url, participants, owner }) => ({ +const liveOn = (state, { url, socket, project, owner }) => { + return ({ + ...state, + url, + socket, + project, + owner, + participants: [] + }); +}; + +const liveOff = (state) => ({ ...state, - url, - participants, - owner -}); - -const liveOn = (state, { owner }) => ({ - ...state, - owner, - participants: [] -}); - -const liveOff = () => ({ - url: null, + socket: null, owner: undefined, participants: [] }); -const joinUser = (state, { joinUser }) => ({ +const joinUser = (state, { participants }) => ({ ...state, - participants: [...state.participants, joinUser] + participants, }); -const leftUser = (state, { leftUser }) => ({ +const leaveUser = (state, { participants }) => ({ ...state, - participants: state.participants.filter( - ({ username }) => username !== leftUser.username - ) + participants, }); function LiveReducer(state, { type, payload }) { const reducers = { - [FETCH_LIVE]: fetchLive, [LIVE_ON]: liveOn, [LIVE_OFF]: liveOff, [LIVE_JOIN_USER]: joinUser, - [LIVE_LEFT_USER]: leftUser + [LIVE_LEAVE_USER]: leaveUser }; const reducer = reducers[type]; diff --git a/cocode/src/stores/LiveStore.js b/cocode/src/stores/LiveStore.js index 7aeba0e4..8d37dfb2 100644 --- a/cocode/src/stores/LiveStore.js +++ b/cocode/src/stores/LiveStore.js @@ -1,20 +1,31 @@ import React, { useReducer } from 'react'; import LiveReducer from 'reducers/LiveReducer'; import { LiveContext } from 'contexts'; +import { LIVE_SERVER } from 'config'; function LiveStore({ children }) { - const [{ url, participants, owner }, dispatchLive] = useReducer( - LiveReducer, - { - url: null, - owner: undefined, - participants: [] - } - ); + const initialValue = { + liveServer: LIVE_SERVER, + url: '', + project: {}, + socket: null, + owner: undefined, + participants: [] + }; + + const [live, dispatchLive] = useReducer(LiveReducer, initialValue); + const { liveServer, url, socket, participants, owner } = live; return ( {children} From 9e41895ea247ee3b0ce286e08747b00cad88c462 Mon Sep 17 00:00:00 2001 From: hzoou Date: Wed, 18 Dec 2019 16:38:06 +0900 Subject: [PATCH 05/54] =?UTF-8?q?feat:=20=ED=94=84=EB=A1=9C=EC=A0=9D?= =?UTF-8?q?=ED=8A=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EB=9D=BC=EC=9D=B4=EB=B8=8C=20=EA=B8=B0=EB=8A=A5=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 라이브 기능을 제공하는 라이브 페이지를 생성하기 위해 프로젝트 페이지에서 라이브 관련 컴포넌트들을 삭제했습니다. LiveOffTab에서 go live 버튼을 클릭하면 useHistory를 사용해서 라이브 페이지로 이동시켰습니다. --- .../Project/LiveUserProfile/index.js | 38 --------- .../Project/LiveUserProfile/style.js | 55 ------------ .../containers/Project/LiveOffTab/index.js | 11 ++- .../containers/Project/LiveOnTab/close.svg | 4 - .../src/containers/Project/LiveOnTab/index.js | 33 -------- .../src/containers/Project/LiveOnTab/style.js | 83 ------------------- .../src/containers/Project/LiveTab/index.js | 40 +-------- cocode/src/pages/Project/index.js | 33 ++++---- 8 files changed, 25 insertions(+), 272 deletions(-) delete mode 100644 cocode/src/components/Project/LiveUserProfile/index.js delete mode 100644 cocode/src/components/Project/LiveUserProfile/style.js delete mode 100644 cocode/src/containers/Project/LiveOnTab/close.svg delete mode 100644 cocode/src/containers/Project/LiveOnTab/index.js delete mode 100644 cocode/src/containers/Project/LiveOnTab/style.js diff --git a/cocode/src/components/Project/LiveUserProfile/index.js b/cocode/src/components/Project/LiveUserProfile/index.js deleted file mode 100644 index 608dc2a0..00000000 --- a/cocode/src/components/Project/LiveUserProfile/index.js +++ /dev/null @@ -1,38 +0,0 @@ -import React, { useContext } from 'react'; -import * as Styled from './style'; -import { UserContext } from 'contexts'; - -function LiveUserProfile({ username, avatar }) { - const { user } = useContext(UserContext); - - return ( - - - {username} - {user.username === username && ( - (you) - )} - - ); -} - -function LiveUsers({ owner: { username, avatar }, participants = [] }) { - return ( - - OWNERS - - USERS - {participants.map(({ username, avatar }, index) => { - return ( - - ); - })} - - ); -} - -export default LiveUsers; diff --git a/cocode/src/components/Project/LiveUserProfile/style.js b/cocode/src/components/Project/LiveUserProfile/style.js deleted file mode 100644 index 5bad1c07..00000000 --- a/cocode/src/components/Project/LiveUserProfile/style.js +++ /dev/null @@ -1,55 +0,0 @@ -import styled from 'styled-components'; -import { TAB_CONTAINER_THEME, LIVE_TAB_THEME } from 'constants/theme'; - -const Container = styled.div` - & { - margin: 1rem 0; - } -`; - -const Title = styled.h1` - & { - color: ${TAB_CONTAINER_THEME.tabContainerTitleColor}; - font-size: ${TAB_CONTAINER_THEME.tabContainerTitleSize}; - font-weight: ${TAB_CONTAINER_THEME.tabContainerTitleWeight}; - } -`; - -const UserProfile = styled.div` - & { - display: flex; - margin: 0.7rem 0; - } -`; - -const UserName = styled.div` - & { - align-self: center; - margin-left: 0.5rem; - font-weight: 100; - font-size: 1rem; - } -`; - -const UserAvatar = styled.img` - & { - width: 2rem; - height: 2rem; - border-radius: 0.3rem; - } -`; - -const SelfLabel = styled(UserName)` - & { - color: ${LIVE_TAB_THEME.liveSelfLabelColor}; - } -`; - -export { - Container, - Title, - UserProfile, - UserName, - UserAvatar, - SelfLabel -}; diff --git a/cocode/src/containers/Project/LiveOffTab/index.js b/cocode/src/containers/Project/LiveOffTab/index.js index cd446c29..21adb062 100644 --- a/cocode/src/containers/Project/LiveOffTab/index.js +++ b/cocode/src/containers/Project/LiveOffTab/index.js @@ -1,15 +1,20 @@ -import * as Styled from './style'; import React from 'react'; +import { useParams, useHistory } from 'react-router-dom'; +import * as Styled from './style'; const OFF_BUTTON_LABEL = 'Go Live'; const OFF_DESCRIPTION = 'Invite others to live edit this coconut with you. We’re doing it live!'; -function LiveOffTab({ onClick }) { +function LiveOffTab() { + const history = useHistory(); + const { projectId } = useParams(); + const handleConnectSocket = () => history.replace(`../live/${projectId}`); + return ( {OFF_DESCRIPTION} - + {OFF_BUTTON_LABEL} diff --git a/cocode/src/containers/Project/LiveOnTab/close.svg b/cocode/src/containers/Project/LiveOnTab/close.svg deleted file mode 100644 index 6adde6f1..00000000 --- a/cocode/src/containers/Project/LiveOnTab/close.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - \ No newline at end of file diff --git a/cocode/src/containers/Project/LiveOnTab/index.js b/cocode/src/containers/Project/LiveOnTab/index.js deleted file mode 100644 index aca18226..00000000 --- a/cocode/src/containers/Project/LiveOnTab/index.js +++ /dev/null @@ -1,33 +0,0 @@ -import React, { useContext } from 'react'; -import { LiveContext } from 'contexts'; -import * as Styled from './style'; - -import close from './close.svg'; -import LiveUserProfile from 'components/Project/LiveUserProfile'; - -const LIVE_STATUS_LABEL = 'You’ve gone live!'; -const ON_BUTTON_LABEL = 'Stop Live'; -const ON_DESCRIPTION = - 'Share this link with others to invite them to the live.'; - -function LiveOnTab({ onClick }) { - const { url, participants, owner } = useContext(LiveContext); - - return ( - - - - {LIVE_STATUS_LABEL} - - {ON_DESCRIPTION} - {url} - - - {ON_BUTTON_LABEL} - - - - ); -} - -export default LiveOnTab; diff --git a/cocode/src/containers/Project/LiveOnTab/style.js b/cocode/src/containers/Project/LiveOnTab/style.js deleted file mode 100644 index bbad87a1..00000000 --- a/cocode/src/containers/Project/LiveOnTab/style.js +++ /dev/null @@ -1,83 +0,0 @@ -import styled from 'styled-components'; -import { LIVE_TAB_THEME } from 'constants/theme'; - -const Container = styled.div` - & { - margin: 0.7rem 1rem; - } -`; - -const Description = styled.div` - & { - margin-bottom: 1rem; - color: ${LIVE_TAB_THEME.liveFontColor}; - font-size: 1rem; - font-weight: 100; - } -`; - -const Button = styled.button` - & { - display: flex; - align-items: center; - justify-content: center; - width: -webkit-fill-available; - padding: 0.7rem 2.2rem; - border-radius: 0.7rem; - background-color: ${LIVE_TAB_THEME.liveButtonBGColor}; - font-size: 1rem; - font-weight: 400; - } - - &:hover { - background-color: ${LIVE_TAB_THEME.liveButtonBGColorHover}; - } -`; - -const Close = styled.img` - & { - width: 0.8rem; - height: 0.8rem; - margin-right: 0.5rem; - } -`; - -const LiveStatusLabel = styled.div` - & { - display: flex; - margin-bottom: 1rem; - color: ${LIVE_TAB_THEME.liveStatusLabelColor}; - font-size: 1.1rem; - font-weight: lighter; - } -`; - -const LiveStatusSpan = styled.span` - & { - background-color: ${LIVE_TAB_THEME.liveStatusLabelColor}; - margin: auto 1rem auto 0.3rem; - width: 0.6rem; - height: 0.6rem; - border-radius: 50%; - } -`; - -const LinkURL = styled.div` - & { - padding: 0.5rem 1rem; - margin-bottom: 0.5rem; - background-color: ${LIVE_TAB_THEME.liveLinkBGColor}; - color: ${LIVE_TAB_THEME.liveFontColor}; - font-size: 0.9rem; - } -`; - -export { - Container, - Description, - Button, - Close, - LiveStatusLabel, - LiveStatusSpan, - LinkURL -}; diff --git a/cocode/src/containers/Project/LiveTab/index.js b/cocode/src/containers/Project/LiveTab/index.js index 30c9e2ed..57a01850 100644 --- a/cocode/src/containers/Project/LiveTab/index.js +++ b/cocode/src/containers/Project/LiveTab/index.js @@ -1,52 +1,16 @@ -import React, { useContext } from 'react'; +import React from 'react'; import * as Styled from './style'; -import { LiveContext } from 'contexts'; -import { liveOffActionCreator, fetchLiveActionCreator } from 'actions/Live'; import LiveOffTab from 'containers/Project/LiveOffTab'; -import LiveOnTab from 'containers/Project/LiveOnTab'; -import avatar from 'components/Common/UserProfile/avatar.jpeg'; const TAB_TITLE = 'LIVE'; -const dummy = { - url: 'https://cocode.com/live/', - participants: [ - { - username: 'basiltoast', - avatar - }, - { - username: 'basiltoast', - avatar - }, - { - username: 'basiltoast', - avatar - } - ], - owner: { - username: 'hzoou', - avatar - } -}; - function LiveTab() { - const { url, dispatchLive } = useContext(LiveContext); - - const handleTurnLive = () => { - if (url) dispatchLive(liveOffActionCreator()); - else dispatchLive(fetchLiveActionCreator(dummy)); - }; return ( <> {TAB_TITLE}
- {url ? ( - - ) : ( - - )} +
); diff --git a/cocode/src/pages/Project/index.js b/cocode/src/pages/Project/index.js index 8cfc5dae..0d453cb5 100644 --- a/cocode/src/pages/Project/index.js +++ b/cocode/src/pages/Project/index.js @@ -20,7 +20,6 @@ import useFetch from 'hooks/useFetch'; import { reactTemplate } from 'template/react'; import copyProject from 'template/copyProject'; import { getProjectInfoAPICreator, forkProjectAPICreator } from 'apis/Project'; -import { LiveStore } from 'stores'; import parseProject from 'pages/Project/parseProject'; const DEFAULT_CLICKED_TAB_INDEX = 0; @@ -95,24 +94,22 @@ function Project() { setClickedTabIndex }} > - -
- {isFetched && ( - - - - - - - - +
+ {isFetched && ( + + + + + + + - - )} - + + + )} ); } From d8c485d6cf14e14d7cd791c206dc576cfcf75e25 Mon Sep 17 00:00:00 2001 From: hzoou Date: Wed, 18 Dec 2019 16:48:56 +0900 Subject: [PATCH 06/54] =?UTF-8?q?feat:=20=EB=9D=BC=EC=9D=B4=EB=B8=8C=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 라이브 기능을 제공하기 위해 라이브 페이지를 생성했습니다. 라이브 페이지를 노출할 라이브 라우터를 추가했습니다, --- cocode/src/App.js | 20 +++- cocode/src/components/Live/LiveUsers/index.js | 34 ++++++ cocode/src/components/Live/LiveUsers/style.js | 55 +++++++++ cocode/src/constants/theme.js | 2 +- .../containers/Live/DependencyTab/index.js | 44 ++++++++ .../containers/Live/DependencyTab/style.js | 47 ++++++++ cocode/src/containers/Live/Editor/index.js | 100 +++++++++++++++++ cocode/src/containers/Live/Editor/style.js | 14 +++ .../src/containers/Live/ExplorerTab/index.js | 104 ++++++++++++++++++ .../src/containers/Live/ExplorerTab/style.js | 66 +++++++++++ .../src/containers/Live/LiveOnTab/close.svg | 4 + cocode/src/containers/Live/LiveOnTab/index.js | 32 ++++++ cocode/src/containers/Live/LiveOnTab/style.js | 98 +++++++++++++++++ cocode/src/containers/Live/LiveTab/index.js | 19 ++++ cocode/src/containers/Live/LiveTab/style.js | 16 +++ .../src/containers/Live/TabBar/dependency.svg | 4 + .../src/containers/Live/TabBar/explorer.svg | 4 + cocode/src/containers/Live/TabBar/index.js | 44 ++++++++ cocode/src/containers/Live/TabBar/live.svg | 4 + cocode/src/containers/Live/TabBar/style.js | 15 +++ .../src/containers/Live/TabContainer/index.js | 26 +++++ .../src/containers/Live/TabContainer/style.js | 10 ++ cocode/src/pages/Live/index.js | 84 ++++++++++++++ cocode/src/pages/Live/style.js | 16 +++ cocode/src/pages/index.js | 3 +- 25 files changed, 858 insertions(+), 7 deletions(-) create mode 100644 cocode/src/components/Live/LiveUsers/index.js create mode 100644 cocode/src/components/Live/LiveUsers/style.js create mode 100644 cocode/src/containers/Live/DependencyTab/index.js create mode 100644 cocode/src/containers/Live/DependencyTab/style.js create mode 100644 cocode/src/containers/Live/Editor/index.js create mode 100644 cocode/src/containers/Live/Editor/style.js create mode 100644 cocode/src/containers/Live/ExplorerTab/index.js create mode 100644 cocode/src/containers/Live/ExplorerTab/style.js create mode 100644 cocode/src/containers/Live/LiveOnTab/close.svg create mode 100644 cocode/src/containers/Live/LiveOnTab/index.js create mode 100644 cocode/src/containers/Live/LiveOnTab/style.js create mode 100644 cocode/src/containers/Live/LiveTab/index.js create mode 100644 cocode/src/containers/Live/LiveTab/style.js create mode 100644 cocode/src/containers/Live/TabBar/dependency.svg create mode 100644 cocode/src/containers/Live/TabBar/explorer.svg create mode 100644 cocode/src/containers/Live/TabBar/index.js create mode 100644 cocode/src/containers/Live/TabBar/live.svg create mode 100644 cocode/src/containers/Live/TabBar/style.js create mode 100644 cocode/src/containers/Live/TabContainer/index.js create mode 100644 cocode/src/containers/Live/TabContainer/style.js create mode 100644 cocode/src/pages/Live/index.js create mode 100644 cocode/src/pages/Live/style.js diff --git a/cocode/src/App.js b/cocode/src/App.js index 8f6fe5d1..ac387a14 100644 --- a/cocode/src/App.js +++ b/cocode/src/App.js @@ -3,14 +3,21 @@ import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'; import { ThemeProvider } from 'styled-components'; import { DEFAULT_THEME } from 'constants/theme'; - -import GlobalStyle from 'components/Common/GlobalStyle'; - -import { Home, DashBoard, Project, History, Coconut, NotFound } from 'pages'; - import UserContext from 'contexts/UserContext'; import useFetch from 'hooks/useFetch'; import { getUserAPICreator } from 'apis/User'; +import { LiveStore } from 'stores'; + +import GlobalStyle from 'components/Common/GlobalStyle'; +import { + Home, + DashBoard, + Project, + History, + Coconut, + NotFound, + Live +} from 'pages'; function App() { const [user, setUser] = useState(null); @@ -30,6 +37,9 @@ function App() { + + + diff --git a/cocode/src/components/Live/LiveUsers/index.js b/cocode/src/components/Live/LiveUsers/index.js new file mode 100644 index 00000000..d8c48a7a --- /dev/null +++ b/cocode/src/components/Live/LiveUsers/index.js @@ -0,0 +1,34 @@ +import React from 'react'; +import * as Styled from './style'; + +function LiveUserProfile({ username, avatar }) { + return ( + + + {username} + + ); +} + +function LiveUsers({ owner, participants }) { + if (!participants.length) participants = []; + + return ( + + OWNERS + + USERS + {participants.map(({ username, avatar }, index) => { + return ( + + ); + })} + + ); +} + +export default LiveUsers; diff --git a/cocode/src/components/Live/LiveUsers/style.js b/cocode/src/components/Live/LiveUsers/style.js new file mode 100644 index 00000000..5bad1c07 --- /dev/null +++ b/cocode/src/components/Live/LiveUsers/style.js @@ -0,0 +1,55 @@ +import styled from 'styled-components'; +import { TAB_CONTAINER_THEME, LIVE_TAB_THEME } from 'constants/theme'; + +const Container = styled.div` + & { + margin: 1rem 0; + } +`; + +const Title = styled.h1` + & { + color: ${TAB_CONTAINER_THEME.tabContainerTitleColor}; + font-size: ${TAB_CONTAINER_THEME.tabContainerTitleSize}; + font-weight: ${TAB_CONTAINER_THEME.tabContainerTitleWeight}; + } +`; + +const UserProfile = styled.div` + & { + display: flex; + margin: 0.7rem 0; + } +`; + +const UserName = styled.div` + & { + align-self: center; + margin-left: 0.5rem; + font-weight: 100; + font-size: 1rem; + } +`; + +const UserAvatar = styled.img` + & { + width: 2rem; + height: 2rem; + border-radius: 0.3rem; + } +`; + +const SelfLabel = styled(UserName)` + & { + color: ${LIVE_TAB_THEME.liveSelfLabelColor}; + } +`; + +export { + Container, + Title, + UserProfile, + UserName, + UserAvatar, + SelfLabel +}; diff --git a/cocode/src/constants/theme.js b/cocode/src/constants/theme.js index 9aa9a006..faa64866 100644 --- a/cocode/src/constants/theme.js +++ b/cocode/src/constants/theme.js @@ -105,7 +105,7 @@ const LIVE_TAB_THEME = { liveFontColor: '#bcbcbc', liveStatusLabelColor: '#c74040b3', liveLinkBGColor: '#000000', - liveSelfLabelColor: '#333333', + liveSelfLabelColor: '#676767', liveCircleBGColor: '#ffffff' }; diff --git a/cocode/src/containers/Live/DependencyTab/index.js b/cocode/src/containers/Live/DependencyTab/index.js new file mode 100644 index 00000000..d027436c --- /dev/null +++ b/cocode/src/containers/Live/DependencyTab/index.js @@ -0,0 +1,44 @@ +import React, { useContext } from 'react'; +import * as Styled from './style'; + +import CoconutSpinner from 'components/Common/CoconutSpinner'; +import Dependency from 'components/Project/Dependency'; +import DependencyNow from 'components/Project/DependencyNow'; +import DependencySearch from 'components/Project/DependencySearch'; + +import ProjectContext from 'contexts/ProjectContext'; + +const TabTitleFirst = 'DEPENDENCIES'; +const TabTitleSecond = 'SEARCH DEPENDENCY'; + +function InstallingDisplay() { + return ( + + + + Please wait to install module... + + + ); +} + +function DependencyTab() { + const { project } = useContext(ProjectContext); + const { dependencyInstalling } = project; + + return ( + + {dependencyInstalling && } + + + + + + + + + + ); +} + +export default DependencyTab; diff --git a/cocode/src/containers/Live/DependencyTab/style.js b/cocode/src/containers/Live/DependencyTab/style.js new file mode 100644 index 00000000..f036d895 --- /dev/null +++ b/cocode/src/containers/Live/DependencyTab/style.js @@ -0,0 +1,47 @@ +import styled from 'styled-components'; + +const Frame = styled.div` + & { + position: relative; + + height: 100%; + } + + & > * { + width: 100%; + } +`; + +const InstallingDisplay = styled.div` + & { + position: absolute; + z-index: 1; + + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + + height: 100%; + width: 100%; + + background-color: rgba(0, 0, 0, 0.7); + } +`; + +const InstallPhrase = styled.p` + & { + margin-top: 2rem; + + font-size: 1rem; + font-weight: lighter; + } +`; + +const DependencyArea = styled.div` + & { + position: absolute; + } +`; + +export { Frame, InstallingDisplay, InstallPhrase, DependencyArea }; diff --git a/cocode/src/containers/Live/Editor/index.js b/cocode/src/containers/Live/Editor/index.js new file mode 100644 index 00000000..a3c8bcd1 --- /dev/null +++ b/cocode/src/containers/Live/Editor/index.js @@ -0,0 +1,100 @@ +import React, { useState, useContext, useEffect } from 'react'; +import { useParams } from 'react-router-dom'; +import * as Styled from './style'; + +import FileTabBar from 'components/Project/FileTabBar'; +import MonacoEditor from 'components/Project/MonacoEditor'; + +import { UserContext, ProjectContext } from 'contexts'; +import { + updateCodeActionCreator, + saveFileActionCreator +} from 'actions/Project'; + +import useFetch from 'hooks/useFetch'; +import { updateFileAPICreator } from 'apis/File'; + +import { isPressCtrlAndS } from 'utils/keyDownEvent'; + +// Constatnts +let timer; +const DEBOUNCING_TIME = 800; + +function Editor({ handleForkCoconut }) { + const { user } = useContext(UserContext); + const { projectId } = useParams(); + const { project, dispatchProject } = useContext(ProjectContext); + const [code, setCode] = useState(project.editingCode); + const [isEditorMounted, setIsEditorMounted] = useState(false); + const [_, setRequest] = useFetch({}); + + const [fileSelectFlag, setFileSelectFlag] = useState(undefined); + const { selectedFileId } = project; + + const handleOnChangeCodeInMonaco = (_, changedCode) => { + if (timer) clearTimeout(timer); + + timer = setTimeout(() => { + setCode(changedCode); + }, DEBOUNCING_TIME); + }; + + const handleChangedSelectedFile = () => setCode(project.editingCode); + + const handleRequestUpdateCode = () => { + if (!isEditorMounted) return; + + const updateFileAPI = updateFileAPICreator(projectId, selectedFileId, { + contents: project.editingCode + }); + setRequest(updateFileAPI); + }; + + const handleOnKeyDown = e => { + if (!isPressCtrlAndS(e)) return; + + e.preventDefault(); + const { files, selectedFileId } = project; + if (!files[selectedFileId].isEditing) return; + + if (user.username !== project.author) { + handleForkCoconut(); + return; + } + + handleRequestUpdateCode(); + dispatchProject(saveFileActionCreator()); + }; + + const handleUpdateCode = () => { + if (fileSelectFlag !== selectedFileId) { + setFileSelectFlag(selectedFileId); + return; + } + const updateCodeAction = updateCodeActionCreator({ + changedCode: code + }); + dispatchProject(updateCodeAction); + }; + + useEffect(handleUpdateCode, [code]); + useEffect(handleChangedSelectedFile, [project.selectedFileId]); + + const handleEditorDidMount = () => setIsEditorMounted(true); + + return ( + + + + + ); +} + +export default Editor; diff --git a/cocode/src/containers/Live/Editor/style.js b/cocode/src/containers/Live/Editor/style.js new file mode 100644 index 00000000..24eb50c5 --- /dev/null +++ b/cocode/src/containers/Live/Editor/style.js @@ -0,0 +1,14 @@ +import styled from 'styled-components'; + +const Editor = styled.section` + & { + display: flex; + flex-direction: column; + } + + .Stretch-width { + flex-grow: 2; + } +`; + +export { Editor }; diff --git a/cocode/src/containers/Live/ExplorerTab/index.js b/cocode/src/containers/Live/ExplorerTab/index.js new file mode 100644 index 00000000..a40810e8 --- /dev/null +++ b/cocode/src/containers/Live/ExplorerTab/index.js @@ -0,0 +1,104 @@ +import React, { useState, useContext } from 'react'; +import * as Styled from './style'; + +import { + NewFolderIcon, + NewFileIcon +} from 'components/Project/ExplorerTabIcons'; +import Directory from 'components/Project/Directory'; +import NewFile from 'components/Project/NewFile'; + +import ProjectContext from 'contexts/ProjectContext'; +import { + selectFileActionCreator, + updateFileNameActionCreator, + deleteFileActionCreator, + moveFileActionCreator +} from 'actions/Project'; + +const TAB_TITLE = 'EXPLORER'; + +function TabHeader({ handleCreateFile }) { + return ( + + {TAB_TITLE} + + + + + + ); +} + +function ExplorerTab() { + const [isNewFileCreating, setIsNewFileCreating] = useState(false); + const [createFileType, setCreateFileType] = useState(null); + + const { project, dispatchProject } = useContext(ProjectContext); + const { files, root } = project; + const childIdsInRoot = files[root].child; + + const handleCreateFile = type => { + setCreateFileType(type); + setIsNewFileCreating(true); + }; + + const handleEndCreateFile = () => setIsNewFileCreating(false); + + const handleSelectFile = selectedFileId => { + const selectFileAction = selectFileActionCreator({ selectedFileId }); + dispatchProject(selectFileAction); + }; + + const handleEditFileName = (selectedFileId, changedName) => { + const updateFileNameAction = updateFileNameActionCreator({ + selectedFileId, + changedName + }); + dispatchProject(updateFileNameAction); + }; + + const handleDeleteFile = deleteFileId => { + const deleteFileAction = deleteFileActionCreator({ deleteFileId }); + dispatchProject(deleteFileAction); + }; + + const handleMoveFile = (directoryId, fileId) => { + const moveFileAction = moveFileActionCreator({ + directoryId, + fileId + }); + dispatchProject(moveFileAction); + }; + + return ( + <> + + {isNewFileCreating && ( + + )} + + + + + ); +} + +export default ExplorerTab; diff --git a/cocode/src/containers/Live/ExplorerTab/style.js b/cocode/src/containers/Live/ExplorerTab/style.js new file mode 100644 index 00000000..e9937060 --- /dev/null +++ b/cocode/src/containers/Live/ExplorerTab/style.js @@ -0,0 +1,66 @@ +import styled from 'styled-components'; +import { + TAB_CONTAINER_THEME, + EXPLORER_TAB_CONTAINER_THEME +} from 'constants/theme'; + +const { + explorerTabContainerSelectedFileBGColor, +} = 'EXPLORER_TAB_CONTAINER_THEME'; +const { + tabContainerHeaderBGColor, + tabContainerTitleColor, + tabContainerTitleSize, + tabContainerTitleWeight +} = 'TAB_CONTAINER_THEME'; + +const TabBody = styled.div` + & { + height: 100%; + } + + .Is-selected-file { + background-color: ${explorerTabContainerSelectedFileBGColor}; + } +`; + +const TabHeader = styled.header` + & { + display: flex; + flex-direction: row; + background-color: ${tabContainerHeaderBGColor}; + } + + .Tab-header-Side-icons { + margin: auto 0; + margin-left: auto; + margin-right: 1rem; + } +`; + +const Title = styled.h1` + & { + padding: 0.7rem 1rem; + + color: ${tabContainerTitleColor}; + font-size: ${tabContainerTitleSize}; + font-weight: ${tabContainerTitleWeight}; + } +`; + +const SideIcons = styled.span` + & { + display: flex; + flex-direction: row; + + margin-left: auto; + } + + & > svg { + margin: 0 0.2rem; + + cursor: pointer; + } +`; + +export { TabHeader, TabBody, Title, SideIcons }; diff --git a/cocode/src/containers/Live/LiveOnTab/close.svg b/cocode/src/containers/Live/LiveOnTab/close.svg new file mode 100644 index 00000000..6adde6f1 --- /dev/null +++ b/cocode/src/containers/Live/LiveOnTab/close.svg @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/cocode/src/containers/Live/LiveOnTab/index.js b/cocode/src/containers/Live/LiveOnTab/index.js new file mode 100644 index 00000000..413866cc --- /dev/null +++ b/cocode/src/containers/Live/LiveOnTab/index.js @@ -0,0 +1,32 @@ +import React from 'react'; +import * as Styled from './style'; + +import close from './close.svg'; +import LiveUsers from 'components/Live/LiveUsers'; + +const LIVE_STATUS_LABEL = 'You’ve gone live!'; +const ON_BUTTON_LABEL = 'Stop Live'; +const ON_DESCRIPTION = + 'Share this link with others to invite them to the live.'; + +function LiveOnTab() { + return ( + + + + {LIVE_STATUS_LABEL} + + {ON_DESCRIPTION} + + url + + + + {ON_BUTTON_LABEL} + + + + ); +} + +export default LiveOnTab; diff --git a/cocode/src/containers/Live/LiveOnTab/style.js b/cocode/src/containers/Live/LiveOnTab/style.js new file mode 100644 index 00000000..f0f13352 --- /dev/null +++ b/cocode/src/containers/Live/LiveOnTab/style.js @@ -0,0 +1,98 @@ +import styled from 'styled-components'; +import { LIVE_TAB_THEME } from 'constants/theme'; + +const Container = styled.div` + & { + margin: 0.7rem 1rem; + } +`; + +const Description = styled.div` + & { + margin-bottom: 1rem; + color: ${LIVE_TAB_THEME.liveFontColor}; + font-size: 1rem; + font-weight: 100; + } +`; + +const Button = styled.button` + & { + display: flex; + align-items: center; + justify-content: center; + width: -webkit-fill-available; + padding: 0.7rem 2.2rem; + border-radius: 0.7rem; + background-color: ${LIVE_TAB_THEME.liveButtonBGColor}; + font-size: 1rem; + font-weight: 400; + } + + &:hover { + background-color: ${LIVE_TAB_THEME.liveButtonBGColorHover}; + } +`; + +const Close = styled.img` + & { + width: 0.8rem; + height: 0.8rem; + margin-right: 0.5rem; + } +`; + +const LiveStatusLabel = styled.div` + & { + display: flex; + margin-bottom: 1rem; + color: ${LIVE_TAB_THEME.liveStatusLabelColor}; + font-size: 1.1rem; + font-weight: lighter; + } +`; + +const LiveStatusSpan = styled.span` + & { + background-color: ${LIVE_TAB_THEME.liveStatusLabelColor}; + margin: auto 1rem auto 0.3rem; + width: 0.6rem; + height: 0.6rem; + border-radius: 50%; + } +`; + +const LinkURL = styled.div` + & { + display: flex; + width: 100%; + overflow: auto; + padding: 0.5rem 1rem; + margin-bottom: 0.5rem; + align-items: center; + background-color: ${LIVE_TAB_THEME.liveLinkBGColor}; + color: ${LIVE_TAB_THEME.liveFontColor}; + font-size: 0.9rem; + } +`; + +const Copy = styled.img` + & { + width: 0.8rem; + height: 0.8rem; + cursor: pointer; + margin-right: 0.5rem; + user-select: none; + } +`; + +export { + Container, + Description, + Button, + Close, + LiveStatusLabel, + LiveStatusSpan, + LinkURL, + Copy +}; diff --git a/cocode/src/containers/Live/LiveTab/index.js b/cocode/src/containers/Live/LiveTab/index.js new file mode 100644 index 00000000..b2f78b93 --- /dev/null +++ b/cocode/src/containers/Live/LiveTab/index.js @@ -0,0 +1,19 @@ +import React from 'react'; +import * as Styled from './style'; + +import LiveOnTab from 'containers/Live/LiveOnTab'; + +const TAB_TITLE = 'LIVE'; + +function LiveTab() { + return ( + <> + {TAB_TITLE} +
+ +
+ + ); +} + +export default LiveTab; diff --git a/cocode/src/containers/Live/LiveTab/style.js b/cocode/src/containers/Live/LiveTab/style.js new file mode 100644 index 00000000..d2378806 --- /dev/null +++ b/cocode/src/containers/Live/LiveTab/style.js @@ -0,0 +1,16 @@ +import styled from 'styled-components'; +import { TAB_CONTAINER_THEME } from 'constants/theme'; + +const Title = styled.h1` + & { + padding: 0.7rem 1rem; + color: ${TAB_CONTAINER_THEME.tabContainerTitleColor}; + font-size: ${TAB_CONTAINER_THEME.tabContainerTitleSize}; + font-weight: ${TAB_CONTAINER_THEME.tabContainerTitleWeight}; + background-color: ${TAB_CONTAINER_THEME.tabContainerHeaderBGColor}; + } +`; + +export { + Title +}; diff --git a/cocode/src/containers/Live/TabBar/dependency.svg b/cocode/src/containers/Live/TabBar/dependency.svg new file mode 100644 index 00000000..285034f5 --- /dev/null +++ b/cocode/src/containers/Live/TabBar/dependency.svg @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/cocode/src/containers/Live/TabBar/explorer.svg b/cocode/src/containers/Live/TabBar/explorer.svg new file mode 100644 index 00000000..5f7c2792 --- /dev/null +++ b/cocode/src/containers/Live/TabBar/explorer.svg @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/cocode/src/containers/Live/TabBar/index.js b/cocode/src/containers/Live/TabBar/index.js new file mode 100644 index 00000000..f2bc82c5 --- /dev/null +++ b/cocode/src/containers/Live/TabBar/index.js @@ -0,0 +1,44 @@ +import React, { useContext } from 'react'; +import * as Styled from './style'; + +import ProjectContext from 'contexts/ProjectContext'; +import TabIcon from 'components/Project/TabIcon'; +import Explorer from './explorer.svg'; +import Live from './live.svg'; + +function TabBar({ theme }) { + const { clickedTabIndex, setClickedTabIndex } = useContext(ProjectContext); + + const handleSetClickedIndex = index => setClickedTabIndex(index); + + const tabIcons = [ + { + name: 'explorer', + icon: Explorer + }, + { + name: 'live', + icon: Live, + } + ]; + + return ( + + {tabIcons.map(({ name, icon }, index) => { + return ( + + ); + })} + + ); +} + +export default TabBar; diff --git a/cocode/src/containers/Live/TabBar/live.svg b/cocode/src/containers/Live/TabBar/live.svg new file mode 100644 index 00000000..79bd94bb --- /dev/null +++ b/cocode/src/containers/Live/TabBar/live.svg @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/cocode/src/containers/Live/TabBar/style.js b/cocode/src/containers/Live/TabBar/style.js new file mode 100644 index 00000000..f1aa8450 --- /dev/null +++ b/cocode/src/containers/Live/TabBar/style.js @@ -0,0 +1,15 @@ +import styled from 'styled-components'; + +const TabBar = styled.nav` + & { + min-width: 4rem; + display: inline-flex; + flex-direction: column; + justify-content: flex-start; + align-items: center; + + background-color: ${({ tabBarBGColor }) => tabBarBGColor}; + } +`; + +export { TabBar }; diff --git a/cocode/src/containers/Live/TabContainer/index.js b/cocode/src/containers/Live/TabContainer/index.js new file mode 100644 index 00000000..c425d30b --- /dev/null +++ b/cocode/src/containers/Live/TabContainer/index.js @@ -0,0 +1,26 @@ +import React, { useEffect, useContext } from 'react'; +import * as Styled from './style'; + +import ProjectContext from 'contexts/ProjectContext'; + +import ExplorerTab from '../ExplorerTab'; +import LiveTab from '../LiveTab'; + +function TabContainer() { + const { clickedTabIndex } = useContext(ProjectContext); + + const tapMapping = { + 0: , + 1: + }; + + const renderTab = () => tapMapping[clickedTabIndex]; + + useEffect(() => { + renderTab(); + }, [clickedTabIndex]); + + return {renderTab()}; +} + +export default TabContainer; diff --git a/cocode/src/containers/Live/TabContainer/style.js b/cocode/src/containers/Live/TabContainer/style.js new file mode 100644 index 00000000..1c75d251 --- /dev/null +++ b/cocode/src/containers/Live/TabContainer/style.js @@ -0,0 +1,10 @@ +import styled from 'styled-components'; +import { TAB_CONTAINER_THEME } from 'constants/theme'; + +const Container = styled.section` + & { + background-color: ${TAB_CONTAINER_THEME.tabContainerBGColor}; + } +`; + +export { Container }; diff --git a/cocode/src/pages/Live/index.js b/cocode/src/pages/Live/index.js new file mode 100644 index 00000000..3e792f2a --- /dev/null +++ b/cocode/src/pages/Live/index.js @@ -0,0 +1,84 @@ +import React, { + useReducer, + useEffect, + useState, +} from 'react'; +import { useParams } from 'react-router-dom'; +import * as Styled from './style'; + +import Header from 'containers/Common/Header'; +import TabBar from 'containers/Live/TabBar'; +import TabContainer from 'containers/Live/TabContainer'; +import Editor from 'containers/Live/Editor'; +import BrowserV2 from 'components/Project/BrowserV2'; +import { SplitPaneContainer } from 'components/Common/SplitPane'; + +import { ProjectContext } from 'contexts'; +import ProjectReducer from 'reducers/ProjectReducer'; +import { fetchProjectActionCreator } from 'actions/Project'; + +import { TAB_BAR_THEME } from 'constants/theme'; +import useFetch from 'hooks/useFetch'; +import { getProjectInfoAPICreator } from 'apis/Project'; + +const DEFAULT_CLICKED_TAB_INDEX = 0; + +function Live() { + const { projectId } = useParams(); + const [{ data, loading, error }, setRequest] = useFetch({}); + const [isFetched, setIsFetched] = useState(false); + const [clickedTabIndex, setClickedTabIndex] = useState( + DEFAULT_CLICKED_TAB_INDEX + ); + const [project, dispatchProject] = useReducer(ProjectReducer, {}); + + const handleFetchProject = () => { + const getProjectInfoAPI = getProjectInfoAPICreator(projectId); + setRequest(getProjectInfoAPI); + }; + + const handleSetProjectState = project => { + const fetchProjectAction = fetchProjectActionCreator({ project }); + dispatchProject(fetchProjectAction); + setIsFetched(true); + }; + + const handleSetProject = () => { + if (!data) return; + if (!isFetched) handleSetProjectState(data); + }; + + useEffect(handleFetchProject, []); + useEffect(handleSetProject, [data, isFetched]); + + // //TODO loading 컴포넌트 만들기 + if (loading) return

Loading...

; + if (error) return

다시 시도해주세요.

; + + return ( + +
+ {isFetched && ( + + + + + + + + + + + )} + + ); +} + +export default Live; diff --git a/cocode/src/pages/Live/style.js b/cocode/src/pages/Live/style.js new file mode 100644 index 00000000..f290ce40 --- /dev/null +++ b/cocode/src/pages/Live/style.js @@ -0,0 +1,16 @@ +import styled from 'styled-components'; + +const Main = styled.main` + & { + display: flex; + flex-direction: row; + + height: 88vh; + + .Project-main-stretch { + flex-grow: 2; + } + } +`; + +export { Main }; diff --git a/cocode/src/pages/index.js b/cocode/src/pages/index.js index 61fa81d7..a0c1576e 100644 --- a/cocode/src/pages/index.js +++ b/cocode/src/pages/index.js @@ -5,5 +5,6 @@ import History from './History'; import Version1 from './Version1'; import Coconut from './Coconut'; import NotFound from './NotFound'; +import Live from './Live'; -export { Home, DashBoard, Project, History, Version1, Coconut, NotFound }; +export { Home, DashBoard, Project, History, Version1, Coconut, NotFound, Live }; From 94d873e53f8b9bb7140ee85cd92aa3e3a753292a Mon Sep 17 00:00:00 2001 From: hzoou Date: Wed, 18 Dec 2019 17:02:08 +0900 Subject: [PATCH 07/54] =?UTF-8?q?feat:=20#41=20=EB=9D=BC=EC=9D=B4=EB=B8=8C?= =?UTF-8?q?=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=B5=9C=EC=B4=88=20=EC=A0=91?= =?UTF-8?q?=EC=86=8D=20=EC=8B=9C=20=EC=86=8C=EC=BC=93=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 라이브 페이지에 최초 접속 시 소켓과의 통신을 시작했습니다. 통신된 후에 방 존재 여부와 상관없이 소켓에게 방을 만들겠다는 이벤트를 요청했습니다. --- cocode/src/pages/Live/index.js | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/cocode/src/pages/Live/index.js b/cocode/src/pages/Live/index.js index 3e792f2a..1e5cd21b 100644 --- a/cocode/src/pages/Live/index.js +++ b/cocode/src/pages/Live/index.js @@ -2,9 +2,12 @@ import React, { useReducer, useEffect, useState, + useContext, + useCallback } from 'react'; import { useParams } from 'react-router-dom'; import * as Styled from './style'; +import io from 'socket.io-client'; import Header from 'containers/Common/Header'; import TabBar from 'containers/Live/TabBar'; @@ -13,7 +16,7 @@ import Editor from 'containers/Live/Editor'; import BrowserV2 from 'components/Project/BrowserV2'; import { SplitPaneContainer } from 'components/Common/SplitPane'; -import { ProjectContext } from 'contexts'; +import { LiveContext, ProjectContext, UserContext } from 'contexts'; import ProjectReducer from 'reducers/ProjectReducer'; import { fetchProjectActionCreator } from 'actions/Project'; @@ -22,11 +25,15 @@ import useFetch from 'hooks/useFetch'; import { getProjectInfoAPICreator } from 'apis/Project'; const DEFAULT_CLICKED_TAB_INDEX = 0; +let socket; function Live() { const { projectId } = useParams(); + const { user } = useContext(UserContext); + const { liveServer } = useContext(LiveContext); const [{ data, loading, error }, setRequest] = useFetch({}); const [isFetched, setIsFetched] = useState(false); + const [isConnected, setIsConnected] = useState(false); const [clickedTabIndex, setClickedTabIndex] = useState( DEFAULT_CLICKED_TAB_INDEX ); @@ -48,7 +55,19 @@ function Live() { if (!isFetched) handleSetProjectState(data); }; + const handleConnected = () => { + socket.emit('createRoom', { user, projectId, project }); + }; + + const handleConnectSocket = useCallback(() => { + if (!Object.keys(project).length || isConnected || !user) return; + setIsConnected(true); + socket = io(liveServer); + socket.on('connected', handleConnected); + }, [project]); + useEffect(handleFetchProject, []); + useEffect(handleConnectSocket, [project]); useEffect(handleSetProject, [data, isFetched]); // //TODO loading 컴포넌트 만들기 @@ -81,4 +100,4 @@ function Live() { ); } -export default Live; +export default Live; \ No newline at end of file From fa30c48ace45da6d08d3d65760fccfc4e1d7737a Mon Sep 17 00:00:00 2001 From: hzoou Date: Wed, 18 Dec 2019 17:05:55 +0900 Subject: [PATCH 08/54] =?UTF-8?q?feat:=20#43=20=EB=9D=BC=EC=9D=B4=EB=B8=8C?= =?UTF-8?q?=20url=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 이미 존재하는 라이브에 접속하는 경우이거나, 라이브를 생성한 경우 liveContext를 갱신하고 고유한 url을 생성했습니다. --- cocode/src/pages/Live/index.js | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/cocode/src/pages/Live/index.js b/cocode/src/pages/Live/index.js index 1e5cd21b..3170b1dc 100644 --- a/cocode/src/pages/Live/index.js +++ b/cocode/src/pages/Live/index.js @@ -19,8 +19,12 @@ import { SplitPaneContainer } from 'components/Common/SplitPane'; import { LiveContext, ProjectContext, UserContext } from 'contexts'; import ProjectReducer from 'reducers/ProjectReducer'; import { fetchProjectActionCreator } from 'actions/Project'; +import { + liveOnActionCreator, +} from 'actions/Live'; import { TAB_BAR_THEME } from 'constants/theme'; +import { COCODE_SERVER } from 'config'; import useFetch from 'hooks/useFetch'; import { getProjectInfoAPICreator } from 'apis/Project'; @@ -30,7 +34,7 @@ let socket; function Live() { const { projectId } = useParams(); const { user } = useContext(UserContext); - const { liveServer } = useContext(LiveContext); + const { liveServer, dispatchLive } = useContext(LiveContext); const [{ data, loading, error }, setRequest] = useFetch({}); const [isFetched, setIsFetched] = useState(false); const [isConnected, setIsConnected] = useState(false); @@ -59,11 +63,37 @@ function Live() { socket.emit('createRoom', { user, projectId, project }); }; + const handleAlreadyExistRoom = ({ host, project, participants }) => { + dispatchLive( + liveOnActionCreator({ + socket, + url: `${COCODE_SERVER}/live/${projectId}`, + owner: host, + project, + participants + }) + ); + }; + + const handleSuccessCreatedRoom = ({ project, participants }) => { + dispatchLive( + liveOnActionCreator({ + socket, + url: `${COCODE_SERVER}/live/${projectId}`, + owner: user, + project, + participants + }) + ); + }; + const handleConnectSocket = useCallback(() => { if (!Object.keys(project).length || isConnected || !user) return; setIsConnected(true); socket = io(liveServer); socket.on('connected', handleConnected); + socket.on('alreadyExistRoom', handleAlreadyExistRoom); + socket.on('successCreatedRoom', handleSuccessCreatedRoom); }, [project]); useEffect(handleFetchProject, []); From 81f7c5631d093a1332ae327a037eb579d1792b09 Mon Sep 17 00:00:00 2001 From: hzoou Date: Wed, 18 Dec 2019 17:08:10 +0900 Subject: [PATCH 09/54] =?UTF-8?q?feat:=20=EB=8B=A4=EB=A5=B8=20=EC=9C=A0?= =?UTF-8?q?=EC=A0=80=EA=B0=80=20=EC=A0=91=EC=86=8D=ED=95=9C=20=EA=B2=BD?= =?UTF-8?q?=EC=9A=B0=EC=97=90=20=EB=8C=80=ED=95=9C=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 다른 유저가 접속했다는 소켓 이벤트가 발생했을 때 liveContext를 갱신하는 로직을 구현했습니다. --- cocode/src/pages/Live/index.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cocode/src/pages/Live/index.js b/cocode/src/pages/Live/index.js index 3170b1dc..4a5701c3 100644 --- a/cocode/src/pages/Live/index.js +++ b/cocode/src/pages/Live/index.js @@ -21,6 +21,7 @@ import ProjectReducer from 'reducers/ProjectReducer'; import { fetchProjectActionCreator } from 'actions/Project'; import { liveOnActionCreator, + liveJoinUserActionCreator, } from 'actions/Live'; import { TAB_BAR_THEME } from 'constants/theme'; @@ -87,6 +88,14 @@ function Live() { ); }; + const handleJoinUser = ({ participants }) => { + dispatchLive( + liveJoinUserActionCreator({ + participants + }) + ); + }; + const handleConnectSocket = useCallback(() => { if (!Object.keys(project).length || isConnected || !user) return; setIsConnected(true); @@ -94,6 +103,7 @@ function Live() { socket.on('connected', handleConnected); socket.on('alreadyExistRoom', handleAlreadyExistRoom); socket.on('successCreatedRoom', handleSuccessCreatedRoom); + socket.on('joinUser', handleJoinUser); }, [project]); useEffect(handleFetchProject, []); From 242fc499f2f12a94a6ad53fcf1e5dd9b0ee68e8d Mon Sep 17 00:00:00 2001 From: hzoou Date: Wed, 18 Dec 2019 17:10:31 +0900 Subject: [PATCH 10/54] =?UTF-8?q?feat:=20=EB=8B=A4=EB=A5=B8=20=EC=9C=A0?= =?UTF-8?q?=EC=A0=80=EA=B0=80=20=ED=87=B4=EC=9E=A5=ED=95=9C=20=EA=B2=BD?= =?UTF-8?q?=EC=9A=B0=EC=97=90=20=EB=8C=80=ED=95=9C=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 다른 유저가 퇴장했다는 소켓 이벤트가 발생했을 때 liveContext를 갱신하는 로직을 구현했습니다. --- cocode/src/pages/Live/index.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cocode/src/pages/Live/index.js b/cocode/src/pages/Live/index.js index 4a5701c3..e276201f 100644 --- a/cocode/src/pages/Live/index.js +++ b/cocode/src/pages/Live/index.js @@ -22,6 +22,7 @@ import { fetchProjectActionCreator } from 'actions/Project'; import { liveOnActionCreator, liveJoinUserActionCreator, + liveLeaveUserActionCreator } from 'actions/Live'; import { TAB_BAR_THEME } from 'constants/theme'; @@ -96,6 +97,14 @@ function Live() { ); }; + const handleLeaveUser = ({ participants }) => { + dispatchLive( + liveLeaveUserActionCreator({ + participants + }) + ); + }; + const handleConnectSocket = useCallback(() => { if (!Object.keys(project).length || isConnected || !user) return; setIsConnected(true); @@ -104,6 +113,7 @@ function Live() { socket.on('alreadyExistRoom', handleAlreadyExistRoom); socket.on('successCreatedRoom', handleSuccessCreatedRoom); socket.on('joinUser', handleJoinUser); + socket.on('leaveUser', handleLeaveUser); }, [project]); useEffect(handleFetchProject, []); From 081da41475db033d5a48a190735816ef06ae147e Mon Sep 17 00:00:00 2001 From: hzoou Date: Wed, 18 Dec 2019 17:11:28 +0900 Subject: [PATCH 11/54] =?UTF-8?q?feat:=20=EC=86=8C=EC=BC=93=20=EC=97=B0?= =?UTF-8?q?=EA=B2=B0=EC=9D=B4=20=EB=81=8A=EA=B8=B4=20=EA=B2=BD=EC=9A=B0?= =?UTF-8?q?=EC=97=90=20=EB=8C=80=ED=95=9C=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 호스트가 소켓 연결을 끊은 경우 liveContext를 갱신하는 로직을 구현했습니다. --- cocode/src/pages/Live/index.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cocode/src/pages/Live/index.js b/cocode/src/pages/Live/index.js index e276201f..5dda2417 100644 --- a/cocode/src/pages/Live/index.js +++ b/cocode/src/pages/Live/index.js @@ -21,6 +21,7 @@ import ProjectReducer from 'reducers/ProjectReducer'; import { fetchProjectActionCreator } from 'actions/Project'; import { liveOnActionCreator, + liveOffActionCreator, liveJoinUserActionCreator, liveLeaveUserActionCreator } from 'actions/Live'; @@ -105,6 +106,11 @@ function Live() { ); }; + const handleCloseSocket = () => { + socket.close(); + dispatchLive(liveOffActionCreator()); + }; + const handleConnectSocket = useCallback(() => { if (!Object.keys(project).length || isConnected || !user) return; setIsConnected(true); @@ -114,6 +120,7 @@ function Live() { socket.on('successCreatedRoom', handleSuccessCreatedRoom); socket.on('joinUser', handleJoinUser); socket.on('leaveUser', handleLeaveUser); + socket.on('close', handleCloseSocket); }, [project]); useEffect(handleFetchProject, []); From a493f3c3b5741106923ba66ec52b05ee40101a69 Mon Sep 17 00:00:00 2001 From: hzoou Date: Wed, 18 Dec 2019 17:25:03 +0900 Subject: [PATCH 12/54] =?UTF-8?q?feat:=20#44=20=EB=9D=BC=EC=9D=B4=EB=B8=8C?= =?UTF-8?q?=EC=97=90=20=EC=A0=91=EC=86=8D=EC=A4=91=EC=9D=B8=20=EC=9C=A0?= =?UTF-8?q?=EC=A0=80=20=EB=85=B8=EC=B6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 해당 라이브에 접속중인 유저를 라이브 탭 컨테이너에 노출했습니다. --- cocode/src/components/Live/LiveUsers/index.js | 8 +++++++- cocode/src/containers/Live/LiveOnTab/index.js | 18 ++++++++++++++---- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/cocode/src/components/Live/LiveUsers/index.js b/cocode/src/components/Live/LiveUsers/index.js index d8c48a7a..108b8459 100644 --- a/cocode/src/components/Live/LiveUsers/index.js +++ b/cocode/src/components/Live/LiveUsers/index.js @@ -1,11 +1,17 @@ -import React from 'react'; +import React, { useContext } from 'react'; import * as Styled from './style'; +import { UserContext } from 'contexts'; function LiveUserProfile({ username, avatar }) { + const { user } = useContext(UserContext); + return ( {username} + {user.username === username && ( + (you) + )} ); } diff --git a/cocode/src/containers/Live/LiveOnTab/index.js b/cocode/src/containers/Live/LiveOnTab/index.js index 413866cc..d84ef24d 100644 --- a/cocode/src/containers/Live/LiveOnTab/index.js +++ b/cocode/src/containers/Live/LiveOnTab/index.js @@ -1,5 +1,6 @@ -import React from 'react'; +import React, { useContext } from 'react'; import * as Styled from './style'; +import { LiveContext, UserContext } from 'contexts'; import close from './close.svg'; import LiveUsers from 'components/Live/LiveUsers'; @@ -10,6 +11,11 @@ const ON_DESCRIPTION = 'Share this link with others to invite them to the live.'; function LiveOnTab() { + const { user } = useContext(UserContext); + const { url, participants, owner } = useContext( + LiveContext + ); + return ( @@ -18,15 +24,19 @@ function LiveOnTab() { {ON_DESCRIPTION} - url + {url} + {user === owner ? ( {ON_BUTTON_LABEL} - + ) : ''} + {owner ? ( + + ) : ''} ); } -export default LiveOnTab; +export default LiveOnTab; \ No newline at end of file From 2024accc097bc09870efd80d7af989d904dbb8f0 Mon Sep 17 00:00:00 2001 From: hzoou Date: Wed, 18 Dec 2019 17:42:08 +0900 Subject: [PATCH 13/54] =?UTF-8?q?feat:=20#42=20=EB=9D=BC=EC=9D=B4=EB=B8=8C?= =?UTF-8?q?=20=EA=B8=B0=EB=8A=A5=20=EC=A4=91=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stop Live 버튼을 클릭하면 라이브 기능이 중지되고, 소켓과의 연결을 해지했습니다. 라이브 기능이 중지되면 해당 프로젝트 페이지로 이동시켰습니다. --- cocode/src/containers/Live/LiveOnTab/index.js | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/cocode/src/containers/Live/LiveOnTab/index.js b/cocode/src/containers/Live/LiveOnTab/index.js index d84ef24d..a5a7c187 100644 --- a/cocode/src/containers/Live/LiveOnTab/index.js +++ b/cocode/src/containers/Live/LiveOnTab/index.js @@ -1,20 +1,31 @@ import React, { useContext } from 'react'; +import { useParams, useHistory } from 'react-router-dom'; import * as Styled from './style'; import { LiveContext, UserContext } from 'contexts'; import close from './close.svg'; import LiveUsers from 'components/Live/LiveUsers'; +import { liveOffActionCreator } from 'actions/Live'; + const LIVE_STATUS_LABEL = 'You’ve gone live!'; const ON_BUTTON_LABEL = 'Stop Live'; const ON_DESCRIPTION = 'Share this link with others to invite them to the live.'; function LiveOnTab() { + const history = useHistory(); + const { projectId } = useParams(); const { user } = useContext(UserContext); - const { url, participants, owner } = useContext( - LiveContext - ); + const { url, socket, participants, owner, dispatchLive } = useContext(LiveContext); + + const handleCloseSocket = () => dispatchLive(liveOffActionCreator()); + + const handleDisconnectSocket = () => { + socket.emit('close'); + socket.on('close', handleCloseSocket); + history.replace(`../project/${projectId}`); + }; return ( @@ -27,7 +38,7 @@ function LiveOnTab() { {url} {user === owner ? ( - + {ON_BUTTON_LABEL} From c741b0408ac3163d1e4dee8f88563d9b0c5fe300 Mon Sep 17 00:00:00 2001 From: hzoou Date: Wed, 18 Dec 2019 17:44:06 +0900 Subject: [PATCH 14/54] =?UTF-8?q?feat:=20#285=20=EB=A7=81=ED=81=AC=20?= =?UTF-8?q?=ED=81=B4=EB=A6=BD=EB=B3=B4=EB=93=9C=EB=A1=9C=20=EB=B3=B5?= =?UTF-8?q?=EC=82=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 라이브 탭에서 제공된 url을 클립보드로 복사하는 기능을 구현했습니다. --- cocode/src/containers/Live/LiveOnTab/copy.svg | 4 ++++ cocode/src/containers/Live/LiveOnTab/index.js | 10 ++++++++-- cocode/src/utils/domControl.js | 10 +++++++++- 3 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 cocode/src/containers/Live/LiveOnTab/copy.svg diff --git a/cocode/src/containers/Live/LiveOnTab/copy.svg b/cocode/src/containers/Live/LiveOnTab/copy.svg new file mode 100644 index 00000000..912733d5 --- /dev/null +++ b/cocode/src/containers/Live/LiveOnTab/copy.svg @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/cocode/src/containers/Live/LiveOnTab/index.js b/cocode/src/containers/Live/LiveOnTab/index.js index a5a7c187..61529764 100644 --- a/cocode/src/containers/Live/LiveOnTab/index.js +++ b/cocode/src/containers/Live/LiveOnTab/index.js @@ -1,12 +1,14 @@ -import React, { useContext } from 'react'; +import React, { useContext, useRef } from 'react'; import { useParams, useHistory } from 'react-router-dom'; import * as Styled from './style'; import { LiveContext, UserContext } from 'contexts'; +import copy from './copy.svg'; import close from './close.svg'; import LiveUsers from 'components/Live/LiveUsers'; import { liveOffActionCreator } from 'actions/Live'; +import { copyToClipboard } from 'utils/domControl'; const LIVE_STATUS_LABEL = 'You’ve gone live!'; const ON_BUTTON_LABEL = 'Stop Live'; @@ -14,6 +16,7 @@ const ON_DESCRIPTION = 'Share this link with others to invite them to the live.'; function LiveOnTab() { + const link = useRef(); const history = useHistory(); const { projectId } = useParams(); const { user } = useContext(UserContext); @@ -27,6 +30,8 @@ function LiveOnTab() { history.replace(`../project/${projectId}`); }; + const handleCopyLink = () => copyToClipboard(link.current); + return ( @@ -34,7 +39,8 @@ function LiveOnTab() { {LIVE_STATUS_LABEL} {ON_DESCRIPTION} - + + {url} {user === owner ? ( diff --git a/cocode/src/utils/domControl.js b/cocode/src/utils/domControl.js index 13b6378e..e717734f 100644 --- a/cocode/src/utils/domControl.js +++ b/cocode/src/utils/domControl.js @@ -7,4 +7,12 @@ function changeDivEditable(node, status) { if (status) node.focus(); } -export { selectAllTextAboutFocusedDom, changeDivEditable }; +function copyToClipboard(node) { + const range = document.createRange(); + range.selectNode(node); + window.getSelection().removeAllRanges(); + window.getSelection().addRange(range); + document.execCommand('copy'); +} + +export { selectAllTextAboutFocusedDom, changeDivEditable, copyToClipboard }; From 8a1a0b241799d4f2ea9966dabb882de5f0d7bbdf Mon Sep 17 00:00:00 2001 From: lallaheeee Date: Wed, 18 Dec 2019 21:56:51 +0900 Subject: [PATCH 15/54] =?UTF-8?q?fix:=20=ED=94=84=EB=A1=9C=EC=A0=9D?= =?UTF-8?q?=ED=8A=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=B0=8F=20=EC=97=90?= =?UTF-8?q?=EB=94=94=ED=84=B0=20=ED=8F=AC=ED=81=AC=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 프로젝트 페이지에서 props로 넘겨주던 포크 요청 함수를 info 탭과 live 탭에서도 사용할 예정이기에 컨텍스트로 변경했습니다. --- cocode/src/containers/Project/Editor/index.js | 17 +++++++---------- cocode/src/pages/Project/index.js | 18 ++++++++++-------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/cocode/src/containers/Project/Editor/index.js b/cocode/src/containers/Project/Editor/index.js index 89839b54..00039c69 100644 --- a/cocode/src/containers/Project/Editor/index.js +++ b/cocode/src/containers/Project/Editor/index.js @@ -5,7 +5,7 @@ import * as Styled from './style'; import FileTabBar from 'components/Project/FileTabBar'; import MonacoEditor from 'components/Project/MonacoEditor'; -import { UserContext, ProjectContext } from 'contexts'; +import { ProjectContext } from 'contexts'; import { updateCodeActionCreator, saveFileActionCreator @@ -20,10 +20,11 @@ import { isPressCtrlAndS } from 'utils/keyDownEvent'; let timer; const DEBOUNCING_TIME = 800; -function Editor({ handleForkCoconut }) { - const { user } = useContext(UserContext); +function Editor() { const { projectId } = useParams(); - const { project, dispatchProject } = useContext(ProjectContext); + const { project, dispatchProject, forkCoconut } = useContext( + ProjectContext + ); const [code, setCode] = useState(project.editingCode); const [isEditorMounted, setIsEditorMounted] = useState(false); const [_, setRequest] = useFetch({}); @@ -50,8 +51,6 @@ function Editor({ handleForkCoconut }) { setRequest(updateFileAPI); }; - const isNotMyProject = !user || user.username !== project.author; - const handleOnKeyDown = e => { if (!isPressCtrlAndS(e)) return; @@ -59,10 +58,8 @@ function Editor({ handleForkCoconut }) { const { files, selectedFileId } = project; if (!files[selectedFileId].isEditing) return; - if (isNotMyProject) { - handleForkCoconut(); - return; - } + const isProgress = forkCoconut(); + if (isProgress) return; handleRequestUpdateCode(); dispatchProject(saveFileActionCreator()); diff --git a/cocode/src/pages/Project/index.js b/cocode/src/pages/Project/index.js index e72860be..0ea67c8a 100644 --- a/cocode/src/pages/Project/index.js +++ b/cocode/src/pages/Project/index.js @@ -37,14 +37,18 @@ function Project() { ); const [project, dispatchProject] = useReducer(ProjectReducer, {}); - const handleForkCoconut = () => { + const isNotMyProject = !user || user.username !== project.author; + + const forkCoconut = () => { + if (!isNotMyProject) return false; + const username = user ? user.username : 'anonymous'; const parsedProject = parseProject(project, username); const forkProjectInfoAPI = forkProjectAPICreator(parsedProject); setRequest(forkProjectInfoAPI); handleSetProjectState(parsedProject); - return project; + return true; }; const handleFetchProject = () => { @@ -100,7 +104,8 @@ function Project() { project, dispatchProject, clickedTabIndex, - setClickedTabIndex + setClickedTabIndex, + forkCoconut }} >
@@ -109,11 +114,8 @@ function Project() { - - + + From ded198dcad482ebc792900c16b5f2c60b83ce0c6 Mon Sep 17 00:00:00 2001 From: BasilToast <33782602+BasilToast@users.noreply.github.com> Date: Thu, 19 Dec 2019 01:17:36 +0900 Subject: [PATCH 16/54] =?UTF-8?q?feat:=20live-editor=20=EA=B8=B0=EB=B3=B8?= =?UTF-8?q?=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 라이브 에디터에서 소켓통신에 사용하는 기본적인 이벤트를 정의하였습니다. --- cocode/src/containers/Live/Editor/index.js | 130 +++++++++++++++++++-- 1 file changed, 123 insertions(+), 7 deletions(-) diff --git a/cocode/src/containers/Live/Editor/index.js b/cocode/src/containers/Live/Editor/index.js index a3c8bcd1..7892b195 100644 --- a/cocode/src/containers/Live/Editor/index.js +++ b/cocode/src/containers/Live/Editor/index.js @@ -1,11 +1,11 @@ -import React, { useState, useContext, useEffect } from 'react'; +import React, { useState, useContext, useEffect, useRef } from 'react'; import { useParams } from 'react-router-dom'; import * as Styled from './style'; import FileTabBar from 'components/Project/FileTabBar'; import MonacoEditor from 'components/Project/MonacoEditor'; -import { UserContext, ProjectContext } from 'contexts'; +import { LiveContext, UserContext, ProjectContext } from 'contexts'; import { updateCodeActionCreator, saveFileActionCreator @@ -16,14 +16,51 @@ import { updateFileAPICreator } from 'apis/File'; import { isPressCtrlAndS } from 'utils/keyDownEvent'; + +class CursorWidget { + constructor(editor, userName, position) { + this.editor = editor; + this.id = userName; + this.domNode = null; + this.position = position; + } + + getId() { + return this.id; + } + + getDomNode() { + if (!this.domNode) { + this.domNode = document.createElement('div'); + this.domNode.innerHTML = this.id; + this.domNode.style.background = 'grey'; + this.domNode.id = this.id; + } + return this.domNode; + } + + getPosition() { + return { + position: this.position, + preference: [0] + }; + } + updatePosition(position) { + this.position = position; + this.editor.layoutContentWidget(this); + } +} + // Constatnts let timer; const DEBOUNCING_TIME = 800; +const userCursor = {}; function Editor({ handleForkCoconut }) { const { user } = useContext(UserContext); const { projectId } = useParams(); const { project, dispatchProject } = useContext(ProjectContext); + const { socket, dispatchLive } = useContext(LiveContext); const [code, setCode] = useState(project.editingCode); const [isEditorMounted, setIsEditorMounted] = useState(false); const [_, setRequest] = useFetch({}); @@ -31,12 +68,16 @@ function Editor({ handleForkCoconut }) { const [fileSelectFlag, setFileSelectFlag] = useState(undefined); const { selectedFileId } = project; + const editorRef = useRef(); + const isBusy = useRef(true); + const pendingEvent = useRef(false); + const handleOnChangeCodeInMonaco = (_, changedCode) => { - if (timer) clearTimeout(timer); + // if (timer) clearTimeout(timer); - timer = setTimeout(() => { - setCode(changedCode); - }, DEBOUNCING_TIME); + // timer = setTimeout(() => { + // setCode(changedCode); + // }, DEBOUNCING_TIME); }; const handleChangedSelectedFile = () => setCode(project.editingCode); @@ -77,10 +118,84 @@ function Editor({ handleForkCoconut }) { dispatchProject(updateCodeAction); }; + const handleEmit = (e, timeStamp) => { + if (isBusy.current) return; + if (!timeStamp) timeStamp = Date(); + if (pendingEvent.current) handleEmit(e, timeStamp); + const change = e.changes[0]; + const operation = { + rangeLength: change.rangeLength, + rangeOffset: change.rangeOffset, + text: change.text.replace(/\r\n/g, '\n'), + timeStamp: timeStamp + }; + socket.emit('change', operation); + }; + + const handleCursor = e => { + socket.emit('moveCursor', e.position); + }; + + const handleEditorDidMount = (_, editor) => { + editorRef.current = editor; + editor.onDidChangeModelContent(handleEmit); + editor.onDidChangeCursorPosition(handleCursor); + setIsEditorMounted(true); + }; + useEffect(handleUpdateCode, [code]); useEffect(handleChangedSelectedFile, [project.selectedFileId]); - const handleEditorDidMount = () => setIsEditorMounted(true); + useEffect(() => { + if (!socket) return; + isBusy.current = false; + socket.on('change', (socketId, op) => { + if (socket.id === socketId) { + setTimeout(() => { + pendingEvent.current = false; + }, 10); + } else { + const rangeOffset = op.rangeOffset; + const rangeLength = op.rangeLength; + const text = op.text; + + const startPosition = editorRef.current + .getModel() + .getPositionAt(rangeOffset); + const endPosition = editorRef.current + .getModel() + .getPositionAt(rangeOffset + rangeLength); + isBusy.current = true; + editorRef.current.executeEdits(socketId, [ + { + range: { + startLineNumber: startPosition.lineNumber, + startColumn: startPosition.column, + endLineNumber: endPosition.lineNumber, + endColumn: endPosition.column + }, + text, + forceMoveMarkers: true + } + ]); + isBusy.current = false; + } + }); + + socket.on('moveCursor', (username, position) => { + if (!userCursor[username]) { + const widget = new CursorWidget( + editorRef.current, + username, + position + ); + userCursor[username] = widget; + editorRef.current.addContentWidget(widget); + return; + } + userCursor[username].updatePosition(position); + }); + }, [socket]); return ( @@ -97,4 +212,5 @@ function Editor({ handleForkCoconut }) { ); } + export default Editor; From a540f4bb95a0b1351c5958d2698b0a1396cc7d93 Mon Sep 17 00:00:00 2001 From: hzoou Date: Thu, 19 Dec 2019 16:29:03 +0900 Subject: [PATCH 17/54] =?UTF-8?q?chore:=20=ED=94=84=EB=A1=9C=EC=A0=9D?= =?UTF-8?q?=ED=8A=B8=20=EC=B9=B4=EB=93=9C=20=EC=8A=A4=ED=83=80=EC=9D=BC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 프로젝트 카드의 커서 스타일을 포인터로 변경했습니다. --- cocode/src/components/DashBoard/ProjectCard/style.js | 1 + 1 file changed, 1 insertion(+) diff --git a/cocode/src/components/DashBoard/ProjectCard/style.js b/cocode/src/components/DashBoard/ProjectCard/style.js index 3deb81ad..445e5505 100644 --- a/cocode/src/components/DashBoard/ProjectCard/style.js +++ b/cocode/src/components/DashBoard/ProjectCard/style.js @@ -15,6 +15,7 @@ const ProjectArticle = styled.article` background-color: ${PROJECT_CARD_THEME.cardBackgroundColor}; border-radius: 1rem; + cursor: pointer; } `; From fefdf0e2033fb421f8bf08e29685a94adac13de0 Mon Sep 17 00:00:00 2001 From: lallaheeee Date: Thu, 19 Dec 2019 16:39:35 +0900 Subject: [PATCH 18/54] =?UTF-8?q?fix:=20=ED=94=84=EB=A1=9C=EC=A0=9D?= =?UTF-8?q?=ED=8A=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=EC=9D=98=20=20=ED=86=A0?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=95=8C=EB=A6=BC=20=EC=83=81=EC=88=98=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 토스트의 알림 메세지를 상수로 분리했습니다. --- cocode/src/constants/notificationMessage.js | 5 +++++ cocode/src/pages/Project/index.js | 8 +++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/cocode/src/constants/notificationMessage.js b/cocode/src/constants/notificationMessage.js index e4b69809..ab7767bf 100644 --- a/cocode/src/constants/notificationMessage.js +++ b/cocode/src/constants/notificationMessage.js @@ -15,6 +15,9 @@ const CONFIRM_DELETE_COCONUT = 'Are you delete this coconut?'; const LOADING_DASHBOARD = 'Please wait to fetch coconuts'; +const SUCCESS_FORK = 'Forked Coconut, Success!'; +const CONFLICT_FORK ='already forked! enjoy Coconut!'; + export { FAIL_INSTALL_DEPENDENCY, FAIL_TO_MOVE_FILE, @@ -29,4 +32,6 @@ export { CONFIRM_DELETE_FILE, CONFIRM_DELETE_COCONUT, LOADING_DASHBOARD, + SUCCESS_FORK, + CONFLICT_FORK, }; diff --git a/cocode/src/pages/Project/index.js b/cocode/src/pages/Project/index.js index 0ea67c8a..ce7bb89a 100644 --- a/cocode/src/pages/Project/index.js +++ b/cocode/src/pages/Project/index.js @@ -23,6 +23,7 @@ import copyProject from 'template/copyProject'; import { getProjectInfoAPICreator, forkProjectAPICreator } from 'apis/Project'; import parseProject from 'pages/Project/parseProject'; import { CREATED, CONFLICT } from 'constants/statusCode'; +import { SUCCESS_FORK, CONFLICT_FORK } from 'constants/notificationMessage'; const DEFAULT_CLICKED_TAB_INDEX = 0; @@ -69,17 +70,14 @@ function Project() { }; const handleChangeHistoryAtForked = () => { - if (status === CONFLICT) { - addToast.error('already forked! enjoy Coconut '); - } - + if (status === CONFLICT) addToast.error(CONFLICT_FORK); if (status !== CREATED) return; projectId !== 'new' ? history.push(`../project/${data._id}`) : history.replace(`../project/${data._id}`); - addToast.info('Forked Coconut, Success !'); + addToast.info(SUCCESS_FORK); }; const handleSetProject = () => { From 45c790400eb57366d72f4e8f9adc19490586f94f Mon Sep 17 00:00:00 2001 From: lallaheeee Date: Thu, 19 Dec 2019 16:42:25 +0900 Subject: [PATCH 19/54] =?UTF-8?q?fix:=20=EC=9D=B8=ED=8F=AC=20=ED=83=AD,=20?= =?UTF-8?q?=EB=9D=BC=EC=9D=B4=EB=B8=8C=20=ED=83=AD=EC=97=90=20=ED=8F=AC?= =?UTF-8?q?=ED=81=AC=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 자신의 프로젝트가 아니거나 새로운 프로젝트일 때 인포 탭에서 프로젝트의 정보를 수정하거나 라이브를 시작할 때 프로젝트를 포크하는 기능을 추가했습니다. --- .../src/containers/Project/InfoTab/index.js | 7 ++++ .../containers/Project/LiveOffTab/index.js | 11 +++++-- cocode/src/pages/Project/index.js | 32 +++++++++++++------ 3 files changed, 38 insertions(+), 12 deletions(-) diff --git a/cocode/src/containers/Project/InfoTab/index.js b/cocode/src/containers/Project/InfoTab/index.js index 2fcab4b0..e6e36c33 100644 --- a/cocode/src/containers/Project/InfoTab/index.js +++ b/cocode/src/containers/Project/InfoTab/index.js @@ -29,6 +29,7 @@ function Info({ setRequest, dispatchProject }) { + const { forkCoconut } = useContext(ProjectContext); const input = useRef(); const [isEditable, setIsEditable] = useState(false); const [value, setValue] = useState(content); @@ -44,6 +45,12 @@ function Info({ const newContent = event.currentTarget.textContent; if (content === newContent) return; setValue(event.currentTarget.textContent); + + const isProgress = forkCoconut({ + info: { [title]: newContent } + }); + if (isProgress) return; + setRequest( updateCoconutsAPICreator(projectId, { [title]: newContent }) ); diff --git a/cocode/src/containers/Project/LiveOffTab/index.js b/cocode/src/containers/Project/LiveOffTab/index.js index 21adb062..2aeb4341 100644 --- a/cocode/src/containers/Project/LiveOffTab/index.js +++ b/cocode/src/containers/Project/LiveOffTab/index.js @@ -1,6 +1,7 @@ -import React from 'react'; +import React, { useContext } from 'react'; import { useParams, useHistory } from 'react-router-dom'; import * as Styled from './style'; +import { ProjectContext } from 'contexts'; const OFF_BUTTON_LABEL = 'Go Live'; const OFF_DESCRIPTION = @@ -9,7 +10,13 @@ const OFF_DESCRIPTION = function LiveOffTab() { const history = useHistory(); const { projectId } = useParams(); - const handleConnectSocket = () => history.replace(`../live/${projectId}`); + const { forkCoconut } = useContext(ProjectContext); + const handleConnectSocket = () => { + const idOfNewProject = forkCoconut({ live: true }); + if (idOfNewProject) return; + + history.replace(`../live/${projectId}`); + }; return ( diff --git a/cocode/src/pages/Project/index.js b/cocode/src/pages/Project/index.js index ce7bb89a..12a81c52 100644 --- a/cocode/src/pages/Project/index.js +++ b/cocode/src/pages/Project/index.js @@ -29,27 +29,40 @@ const DEFAULT_CLICKED_TAB_INDEX = 0; function Project() { const { user } = useContext(UserContext); - const history = useHistory(); const { projectId } = useParams(); + const history = useHistory(); const [{ data, loading, error, status }, setRequest] = useFetch({}); + const [isLive, setIsLive] = useState(false); const [isFetched, setIsFetched] = useState(false); const [clickedTabIndex, setClickedTabIndex] = useState( DEFAULT_CLICKED_TAB_INDEX ); const [project, dispatchProject] = useReducer(ProjectReducer, {}); - const isNotMyProject = !user || user.username !== project.author; - const forkCoconut = () => { + const forkCoconut = ({ live, info }) => { if (!isNotMyProject) return false; - const username = user ? user.username : 'anonymous'; - const parsedProject = parseProject(project, username); + const parsedProject = pretreatBeforeFork({ live, info }); const forkProjectInfoAPI = forkProjectAPICreator(parsedProject); setRequest(forkProjectInfoAPI); handleSetProjectState(parsedProject); - return true; + return parsedProject._id; + }; + + const pretreatBeforeFork = ({ live, info }) => { + if (live) setIsLive(true); + + const username = user ? user.username : 'anonymous'; + let parsedProject = parseProject(project, username); + + if (info) { + Object.entries(info).forEach(([title, value]) => { + parsedProject[title] = value; + }); + } + return parsedProject; }; const handleFetchProject = () => { @@ -73,9 +86,8 @@ function Project() { if (status === CONFLICT) addToast.error(CONFLICT_FORK); if (status !== CREATED) return; - projectId !== 'new' - ? history.push(`../project/${data._id}`) - : history.replace(`../project/${data._id}`); + const url = isLive ? `../live/${data._id}` : `../project/${data._id}`; + projectId !== 'new' ? history.push(url) : history.replace(url); addToast.info(SUCCESS_FORK); }; @@ -113,7 +125,7 @@ function Project() { - + From dd56e10b3ff5935ffec2e16701069d80f6869b16 Mon Sep 17 00:00:00 2001 From: hzoou Date: Thu, 19 Dec 2019 17:01:55 +0900 Subject: [PATCH 20/54] =?UTF-8?q?feat:=20#23=20=ED=97=A4=EB=8D=94=EC=97=90?= =?UTF-8?q?=20=ED=94=84=EB=A1=9C=EC=A0=9D=ED=8A=B8=20=EC=9D=B4=EB=A6=84=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 프로젝트 및 라이브 페이지에서 헤더에 해당 프로젝트 이름을 노출했습니다. --- cocode/src/containers/Common/Header/index.js | 10 ++++------ cocode/src/containers/Common/Header/style.js | 21 ++++---------------- cocode/src/pages/Live/index.js | 2 +- cocode/src/pages/Project/index.js | 2 +- 4 files changed, 10 insertions(+), 25 deletions(-) diff --git a/cocode/src/containers/Common/Header/index.js b/cocode/src/containers/Common/Header/index.js index fe8cf8dd..f70d82a2 100644 --- a/cocode/src/containers/Common/Header/index.js +++ b/cocode/src/containers/Common/Header/index.js @@ -12,7 +12,7 @@ import LoginModalBody from 'components/Common/LoginModalBody'; import UserContext from 'contexts/UserContext'; -function Header() { +function Header({ name }) { const history = useHistory(); const { user } = useContext(UserContext); const [isSignInModalOpen, setIsSignInModalOpen] = useState(false); @@ -42,10 +42,8 @@ function Header() { - {/* - History - */} - + {name || ''} +
{user ? ( )} - +
); } diff --git a/cocode/src/containers/Common/Header/style.js b/cocode/src/containers/Common/Header/style.js index 61ab4b5c..d4a6cab9 100644 --- a/cocode/src/containers/Common/Header/style.js +++ b/cocode/src/containers/Common/Header/style.js @@ -4,7 +4,7 @@ const Header = styled.header` & { display: flex; flex-direction: row; - justify-content: flex-start; + justify-content: space-between; align-items: center; height: ${({ theme }) => theme.headerHeight}; @@ -14,22 +14,9 @@ const Header = styled.header` } `; -const HeaderCategory = styled.button` +const ProjectName = styled.div` & { - margin-left: 1.5rem; - - font-size: 1.4rem; - font-weight: 100; - } - - &:hover { - color: ${({ theme }) => theme.mainColor}; - } -`; - -const HeaderRightSideArea = styled.div` - & { - margin-left: auto; + font-size: 1.3rem; } `; @@ -44,4 +31,4 @@ const SignInButton = styled.button` } `; -export { Header, SignInButton, HeaderCategory, HeaderRightSideArea }; +export { Header, SignInButton, ProjectName }; diff --git a/cocode/src/pages/Live/index.js b/cocode/src/pages/Live/index.js index 5dda2417..e35c6631 100644 --- a/cocode/src/pages/Live/index.js +++ b/cocode/src/pages/Live/index.js @@ -140,7 +140,7 @@ function Live() { setClickedTabIndex }} > -
+
{isFetched && ( diff --git a/cocode/src/pages/Project/index.js b/cocode/src/pages/Project/index.js index e72860be..6d359ed7 100644 --- a/cocode/src/pages/Project/index.js +++ b/cocode/src/pages/Project/index.js @@ -103,7 +103,7 @@ function Project() { setClickedTabIndex }} > -
+
{isFetched && ( From e7fb11d32da22e9e6756da5c8521191ed8d2063e Mon Sep 17 00:00:00 2001 From: hzoou Date: Thu, 19 Dec 2019 17:04:07 +0900 Subject: [PATCH 21/54] =?UTF-8?q?chore:=20=ED=94=84=EB=A1=9C=EC=A0=9D?= =?UTF-8?q?=ED=8A=B8=20=EB=B0=8F=20=EB=9D=BC=EC=9D=B4=EB=B8=8C=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=EC=97=90=EC=84=9C=20=ED=97=A4=EB=8D=94=20?= =?UTF-8?q?=EB=86=92=EC=9D=B4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 프로젝트 및 라이브 페이지에서 헤더 높이를 수정하였고, 그에 따라 메인 컨테이너의 높이 또한 수정했습니다. --- cocode/src/constants/theme.js | 1 + cocode/src/containers/Common/Header/index.js | 2 +- cocode/src/containers/Common/Header/style.js | 3 ++- cocode/src/pages/Live/style.js | 2 +- cocode/src/pages/Project/style.js | 2 +- 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/cocode/src/constants/theme.js b/cocode/src/constants/theme.js index 91961424..5157fdff 100644 --- a/cocode/src/constants/theme.js +++ b/cocode/src/constants/theme.js @@ -6,6 +6,7 @@ const DEFAULT_THEME = { textColor: '#ffffff', exceptHeaderHeight: '88vh', + headerMinHeight: '9vh', headerHeight: '12vh' }; diff --git a/cocode/src/containers/Common/Header/index.js b/cocode/src/containers/Common/Header/index.js index f70d82a2..ac2e3a72 100644 --- a/cocode/src/containers/Common/Header/index.js +++ b/cocode/src/containers/Common/Header/index.js @@ -38,7 +38,7 @@ function Header({ name }) { ]; return ( - + diff --git a/cocode/src/containers/Common/Header/style.js b/cocode/src/containers/Common/Header/style.js index d4a6cab9..d0c5f304 100644 --- a/cocode/src/containers/Common/Header/style.js +++ b/cocode/src/containers/Common/Header/style.js @@ -7,7 +7,8 @@ const Header = styled.header` justify-content: space-between; align-items: center; - height: ${({ theme }) => theme.headerHeight}; + height: ${({ theme, isMinHeight }) => + isMinHeight ? theme.headerMinHeight : theme.headerHeight}; background-color: ${({ theme }) => theme.backgroundColor}; padding: 2rem 2.3rem; diff --git a/cocode/src/pages/Live/style.js b/cocode/src/pages/Live/style.js index f290ce40..2a84f910 100644 --- a/cocode/src/pages/Live/style.js +++ b/cocode/src/pages/Live/style.js @@ -5,7 +5,7 @@ const Main = styled.main` display: flex; flex-direction: row; - height: 88vh; + height: 91vh; .Project-main-stretch { flex-grow: 2; diff --git a/cocode/src/pages/Project/style.js b/cocode/src/pages/Project/style.js index f290ce40..2a84f910 100644 --- a/cocode/src/pages/Project/style.js +++ b/cocode/src/pages/Project/style.js @@ -5,7 +5,7 @@ const Main = styled.main` display: flex; flex-direction: row; - height: 88vh; + height: 91vh; .Project-main-stretch { flex-grow: 2; From 906c658c60158fe010f37e2ba0e0ef7e19a8cd72 Mon Sep 17 00:00:00 2001 From: hzoou Date: Thu, 19 Dec 2019 17:25:24 +0900 Subject: [PATCH 22/54] =?UTF-8?q?feat:=20#290=20=EB=9D=BC=EC=9D=B4?= =?UTF-8?q?=EB=B8=8C=EA=B0=80=20=EC=A2=85=EB=A3=8C=EB=90=9C=20=EA=B2=BD?= =?UTF-8?q?=EC=9A=B0=20=ED=86=A0=EC=8A=A4=ED=8A=B8=20=EB=85=B8=EC=B6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 호스트가 라이브를 종료한 경우 해당 라이브에 참여한 게스트에게 해당 라이브가 종료되었음을 알려주는 토스트를 추가했습니다. --- cocode/src/constants/notificationMessage.js | 3 +++ cocode/src/pages/Live/index.js | 3 +++ 2 files changed, 6 insertions(+) diff --git a/cocode/src/constants/notificationMessage.js b/cocode/src/constants/notificationMessage.js index e4b69809..1136a107 100644 --- a/cocode/src/constants/notificationMessage.js +++ b/cocode/src/constants/notificationMessage.js @@ -15,6 +15,8 @@ const CONFIRM_DELETE_COCONUT = 'Are you delete this coconut?'; const LOADING_DASHBOARD = 'Please wait to fetch coconuts'; +const SHUT_DOWN_LIVE_SHARE = 'The live share has been shut down'; + export { FAIL_INSTALL_DEPENDENCY, FAIL_TO_MOVE_FILE, @@ -29,4 +31,5 @@ export { CONFIRM_DELETE_FILE, CONFIRM_DELETE_COCONUT, LOADING_DASHBOARD, + SHUT_DOWN_LIVE_SHARE }; diff --git a/cocode/src/pages/Live/index.js b/cocode/src/pages/Live/index.js index 5dda2417..5e3ab650 100644 --- a/cocode/src/pages/Live/index.js +++ b/cocode/src/pages/Live/index.js @@ -15,6 +15,7 @@ import TabContainer from 'containers/Live/TabContainer'; import Editor from 'containers/Live/Editor'; import BrowserV2 from 'components/Project/BrowserV2'; import { SplitPaneContainer } from 'components/Common/SplitPane'; +import addToast from 'components/Common/Toast'; import { LiveContext, ProjectContext, UserContext } from 'contexts'; import ProjectReducer from 'reducers/ProjectReducer'; @@ -30,6 +31,7 @@ import { TAB_BAR_THEME } from 'constants/theme'; import { COCODE_SERVER } from 'config'; import useFetch from 'hooks/useFetch'; import { getProjectInfoAPICreator } from 'apis/Project'; +import { SHUT_DOWN_LIVE_SHARE } from 'constants/notificationMessage'; const DEFAULT_CLICKED_TAB_INDEX = 0; let socket; @@ -109,6 +111,7 @@ function Live() { const handleCloseSocket = () => { socket.close(); dispatchLive(liveOffActionCreator()); + addToast.error(SHUT_DOWN_LIVE_SHARE); }; const handleConnectSocket = useCallback(() => { From cc06b7679b90230bec4fa6c29a0192f26988048c Mon Sep 17 00:00:00 2001 From: hzoou Date: Thu, 19 Dec 2019 17:28:37 +0900 Subject: [PATCH 23/54] =?UTF-8?q?chore:=20=ED=81=B4=EB=A6=BD=EB=B3=B4?= =?UTF-8?q?=EB=93=9C=20=EA=B8=B0=EB=8A=A5=EC=9D=84=20=EC=9C=84=ED=95=9C=20?= =?UTF-8?q?=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 클립보드 기능을 지원하기 위해 해당 영역에 셀렉션을 설정해주었습니다. --- cocode/src/containers/Live/LiveOnTab/style.js | 1 + 1 file changed, 1 insertion(+) diff --git a/cocode/src/containers/Live/LiveOnTab/style.js b/cocode/src/containers/Live/LiveOnTab/style.js index f0f13352..29a21573 100644 --- a/cocode/src/containers/Live/LiveOnTab/style.js +++ b/cocode/src/containers/Live/LiveOnTab/style.js @@ -73,6 +73,7 @@ const LinkURL = styled.div` background-color: ${LIVE_TAB_THEME.liveLinkBGColor}; color: ${LIVE_TAB_THEME.liveFontColor}; font-size: 0.9rem; + user-select: text; } `; From 425aa69fd2af96b946c993e0755940e819c74ff9 Mon Sep 17 00:00:00 2001 From: lallaheeee Date: Thu, 19 Dec 2019 17:29:57 +0900 Subject: [PATCH 24/54] =?UTF-8?q?chore:=20=EC=97=90=EB=9F=AC=20=EB=B0=9C?= =?UTF-8?q?=EC=83=9D=EC=8B=9C=20=EC=97=90=EB=9F=AC=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EB=A6=AC=EB=8B=A4=EC=9D=B4=EB=A0=89=ED=8A=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 에러시 에러 페이지로 리다이렉트를 추가했습니다. --- cocode/src/pages/Live/index.js | 4 ++-- cocode/src/pages/Project/index.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cocode/src/pages/Live/index.js b/cocode/src/pages/Live/index.js index 5dda2417..13070c2f 100644 --- a/cocode/src/pages/Live/index.js +++ b/cocode/src/pages/Live/index.js @@ -129,7 +129,7 @@ function Live() { // //TODO loading 컴포넌트 만들기 if (loading) return

Loading...

; - if (error) return

다시 시도해주세요.

; + if (error) history.push('/weAreSorry'); return ( Loading...

; - if (error) return

다시 시도해주세요.

; + if (error) history.push('/weAreSorry'); return ( Date: Thu, 19 Dec 2019 17:34:05 +0900 Subject: [PATCH 25/54] =?UTF-8?q?refactor:=20=EB=A1=9C=EB=94=A9=20?= =?UTF-8?q?=EC=8A=A4=ED=94=BC=EB=84=88=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 기존의 페이지 이동에 필요한 로딩 스피너가 중복되어 컨테이너 컴포넌트로 분리했습니다. --- .../containers/Common/LoadingSpinner/index.js | 14 +++++++++ .../containers/Common/LoadingSpinner/style.js | 29 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 cocode/src/containers/Common/LoadingSpinner/index.js create mode 100644 cocode/src/containers/Common/LoadingSpinner/style.js diff --git a/cocode/src/containers/Common/LoadingSpinner/index.js b/cocode/src/containers/Common/LoadingSpinner/index.js new file mode 100644 index 00000000..c9ef1127 --- /dev/null +++ b/cocode/src/containers/Common/LoadingSpinner/index.js @@ -0,0 +1,14 @@ +import React from 'react'; +import * as Styled from './style'; +import CoconutSpinner from 'components/Common/CoconutSpinner'; + +function LoadingSpinner(message) { + return ( + + + {message} + + ); +} + +export default LoadingSpinner; diff --git a/cocode/src/containers/Common/LoadingSpinner/style.js b/cocode/src/containers/Common/LoadingSpinner/style.js new file mode 100644 index 00000000..9de6309b --- /dev/null +++ b/cocode/src/containers/Common/LoadingSpinner/style.js @@ -0,0 +1,29 @@ +import styled from 'styled-components'; + +const LoadingDisplay = styled.div` + & { + position: absolute; + z-index: 1; + + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + + height: 100%; + width: 100%; + + background-color: rgba(0, 0, 0, 0.7); + } +`; + +const LoadingPhrase = styled.p` + & { + margin-top: 2rem; + + font-size: 3rem; + font-weight: 200; + } +`; + +export { LoadingDisplay, LoadingPhrase }; From c828de658c4078ac5f8c50fc0a26b22306d2a73e Mon Sep 17 00:00:00 2001 From: lallaheeee Date: Thu, 19 Dec 2019 17:39:14 +0900 Subject: [PATCH 26/54] =?UTF-8?q?chore:=20=EB=8C=80=EC=8B=9C=EB=B3=B4?= =?UTF-8?q?=EB=93=9C=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=9D=B4=EB=8F=99?= =?UTF-8?q?=EC=8B=9C=20=EB=A1=9C=EB=94=A9=EC=8A=A4=ED=94=BC=EB=84=88?= =?UTF-8?q?=EB=A5=BC=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 기존에 대시보드페이지 안에서 선언된 로딩스피너를 컨테이너 컴포넌트의 로딩스피너로 변경했습니다. --- .../containers/Common/LoadingSpinner/index.js | 2 +- cocode/src/pages/DashBoard/index.js | 18 +++++------------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/cocode/src/containers/Common/LoadingSpinner/index.js b/cocode/src/containers/Common/LoadingSpinner/index.js index c9ef1127..f6dfb12d 100644 --- a/cocode/src/containers/Common/LoadingSpinner/index.js +++ b/cocode/src/containers/Common/LoadingSpinner/index.js @@ -2,7 +2,7 @@ import React from 'react'; import * as Styled from './style'; import CoconutSpinner from 'components/Common/CoconutSpinner'; -function LoadingSpinner(message) { +function LoadingSpinner({ message }) { return ( diff --git a/cocode/src/pages/DashBoard/index.js b/cocode/src/pages/DashBoard/index.js index 3b0cd468..10c55023 100644 --- a/cocode/src/pages/DashBoard/index.js +++ b/cocode/src/pages/DashBoard/index.js @@ -2,8 +2,7 @@ import React, { useContext, useEffect, useReducer } from 'react'; import { useHistory } from 'react-router-dom'; import ProjectCardList from 'containers/DashBoard/ProjectCardList'; import Header from 'containers/Common/Header'; -import CoconutSpinner from 'components/Common/CoconutSpinner'; -import * as Styled from './style'; +import LoadingSpinner from 'containers/Common/LoadingSpinner'; import { UserContext, DashBoardContext } from 'contexts'; import DashBoardReducer from 'reducers/DashboardReducer'; @@ -12,20 +11,11 @@ import { getCoconutsAPICreator } from 'apis/DashBoard'; import { fetchCoconutActionCreator } from 'actions/Dashboard'; import { LOADING_DASHBOARD } from 'constants/notificationMessage'; -function LoadingSpinner() { - return ( - - - {LOADING_DASHBOARD} - - ); -} - function DashBoard() { const { user } = useContext(UserContext); const history = useHistory(); const [coconuts, dispatchDashboard] = useReducer(DashBoardReducer, []); - const [{ data, loading, error }, setRequest] = useFetch({}); + const [{ data, error }, setRequest] = useFetch({}); const handleRequestGetCoconutAPI = () => { user && setRequest(getCoconutsAPICreator(user.username)); @@ -37,7 +27,9 @@ function DashBoard() { useEffect(handleRequestGetCoconutAPI, [user]); useEffect(handleSetDashBoardState, [data]); - if (loading) return ; + const loading = true; + + if (loading) return ; if (error) history.push('/weAreSorry'); return ( From b7fe9e35425459e8fe50cacf926c265a8c731f82 Mon Sep 17 00:00:00 2001 From: hzoou Date: Thu, 19 Dec 2019 17:44:04 +0900 Subject: [PATCH 27/54] =?UTF-8?q?fix:=20=EC=BD=94=EB=93=9C=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 코드리뷰를 반영했습니다. --- cocode/src/pages/Live/index.js | 2 +- cocode/src/pages/Project/index.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cocode/src/pages/Live/index.js b/cocode/src/pages/Live/index.js index e35c6631..9560eeef 100644 --- a/cocode/src/pages/Live/index.js +++ b/cocode/src/pages/Live/index.js @@ -140,7 +140,7 @@ function Live() { setClickedTabIndex }} > -
+
{isFetched && ( diff --git a/cocode/src/pages/Project/index.js b/cocode/src/pages/Project/index.js index 6d359ed7..5b7bdcba 100644 --- a/cocode/src/pages/Project/index.js +++ b/cocode/src/pages/Project/index.js @@ -103,7 +103,7 @@ function Project() { setClickedTabIndex }} > -
+
{isFetched && ( From 3e788ad9c99895f57290721b1d2578545b9b7558 Mon Sep 17 00:00:00 2001 From: lallaheeee Date: Thu, 19 Dec 2019 18:35:24 +0900 Subject: [PATCH 28/54] =?UTF-8?q?chore:=20=ED=95=84=EC=9A=94=EC=97=86?= =?UTF-8?q?=EB=8A=94=20=EB=B3=80=EC=88=98=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 필요없는 변수를 제거했습니다 --- cocode/src/pages/DashBoard/index.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cocode/src/pages/DashBoard/index.js b/cocode/src/pages/DashBoard/index.js index 10c55023..be8129f8 100644 --- a/cocode/src/pages/DashBoard/index.js +++ b/cocode/src/pages/DashBoard/index.js @@ -15,7 +15,7 @@ function DashBoard() { const { user } = useContext(UserContext); const history = useHistory(); const [coconuts, dispatchDashboard] = useReducer(DashBoardReducer, []); - const [{ data, error }, setRequest] = useFetch({}); + const [{ data, loading, error }, setRequest] = useFetch({}); const handleRequestGetCoconutAPI = () => { user && setRequest(getCoconutsAPICreator(user.username)); @@ -27,8 +27,6 @@ function DashBoard() { useEffect(handleRequestGetCoconutAPI, [user]); useEffect(handleSetDashBoardState, [data]); - const loading = true; - if (loading) return ; if (error) history.push('/weAreSorry'); From 89f3e0af9365b278b74d29167dc928b2f3fc0ee5 Mon Sep 17 00:00:00 2001 From: lallaheeee Date: Thu, 19 Dec 2019 18:24:54 +0900 Subject: [PATCH 29/54] =?UTF-8?q?chore:=20=ED=95=84=EC=9A=94=EC=97=86?= =?UTF-8?q?=EB=8A=94=20=ED=8C=8C=EC=9D=BC=20=EB=B0=8F=20=EB=B3=80=EC=88=98?= =?UTF-8?q?=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 대시보드에서 더이상 사용하지 않는 style.js를 지우고 테스트를 위해 추가했던 변수를 삭제했습니다. --- cocode/src/constants/notificationMessage.js | 8 ++++-- cocode/src/pages/DashBoard/style.js | 29 --------------------- cocode/src/pages/Live/index.js | 5 ++-- cocode/src/pages/Project/index.js | 10 ++++--- 4 files changed, 16 insertions(+), 36 deletions(-) delete mode 100644 cocode/src/pages/DashBoard/style.js diff --git a/cocode/src/constants/notificationMessage.js b/cocode/src/constants/notificationMessage.js index ab7767bf..7bcf49d8 100644 --- a/cocode/src/constants/notificationMessage.js +++ b/cocode/src/constants/notificationMessage.js @@ -14,9 +14,11 @@ const CONFIRM_DELETE_FILE = 'Are you delete this file?'; const CONFIRM_DELETE_COCONUT = 'Are you delete this coconut?'; const LOADING_DASHBOARD = 'Please wait to fetch coconuts'; +const LOADING_PROJECT = 'Please wait to fetch coconut'; +const LOADING_LIVE = 'Please wait to connect live share'; const SUCCESS_FORK = 'Forked Coconut, Success!'; -const CONFLICT_FORK ='already forked! enjoy Coconut!'; +const CONFLICT_FORK = 'Already forked! Enjoy Coconut!'; export { FAIL_INSTALL_DEPENDENCY, @@ -32,6 +34,8 @@ export { CONFIRM_DELETE_FILE, CONFIRM_DELETE_COCONUT, LOADING_DASHBOARD, + LOADING_PROJECT, + LOADING_LIVE, SUCCESS_FORK, - CONFLICT_FORK, + CONFLICT_FORK }; diff --git a/cocode/src/pages/DashBoard/style.js b/cocode/src/pages/DashBoard/style.js deleted file mode 100644 index 9de6309b..00000000 --- a/cocode/src/pages/DashBoard/style.js +++ /dev/null @@ -1,29 +0,0 @@ -import styled from 'styled-components'; - -const LoadingDisplay = styled.div` - & { - position: absolute; - z-index: 1; - - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - - height: 100%; - width: 100%; - - background-color: rgba(0, 0, 0, 0.7); - } -`; - -const LoadingPhrase = styled.p` - & { - margin-top: 2rem; - - font-size: 3rem; - font-weight: 200; - } -`; - -export { LoadingDisplay, LoadingPhrase }; diff --git a/cocode/src/pages/Live/index.js b/cocode/src/pages/Live/index.js index 13070c2f..11b3dbb1 100644 --- a/cocode/src/pages/Live/index.js +++ b/cocode/src/pages/Live/index.js @@ -13,6 +13,7 @@ import Header from 'containers/Common/Header'; import TabBar from 'containers/Live/TabBar'; import TabContainer from 'containers/Live/TabContainer'; import Editor from 'containers/Live/Editor'; +import LoadingSpinner from 'containers/Common/LoadingSpinner'; import BrowserV2 from 'components/Project/BrowserV2'; import { SplitPaneContainer } from 'components/Common/SplitPane'; @@ -30,6 +31,7 @@ import { TAB_BAR_THEME } from 'constants/theme'; import { COCODE_SERVER } from 'config'; import useFetch from 'hooks/useFetch'; import { getProjectInfoAPICreator } from 'apis/Project'; +import { LOADING_LIVE } from 'constants/notificationMessage'; const DEFAULT_CLICKED_TAB_INDEX = 0; let socket; @@ -127,8 +129,7 @@ function Live() { useEffect(handleConnectSocket, [project]); useEffect(handleSetProject, [data, isFetched]); - // //TODO loading 컴포넌트 만들기 - if (loading) return

Loading...

; + if (loading) return ; if (error) history.push('/weAreSorry'); return ( diff --git a/cocode/src/pages/Project/index.js b/cocode/src/pages/Project/index.js index 607562c6..7a83db28 100644 --- a/cocode/src/pages/Project/index.js +++ b/cocode/src/pages/Project/index.js @@ -6,6 +6,7 @@ import Header from 'containers/Common/Header'; import TabBar from 'containers/Project/TabBar'; import TabContainer from 'containers/Project/TabContainer'; import Editor from 'containers/Project/Editor'; +import LoadingSpinner from 'containers/Common/LoadingSpinner'; import BrowserV2 from 'components/Project/BrowserV2'; import { SplitPaneContainer } from 'components/Common/SplitPane'; import addToast from 'components/Common/Toast'; @@ -23,7 +24,11 @@ import copyProject from 'template/copyProject'; import { getProjectInfoAPICreator, forkProjectAPICreator } from 'apis/Project'; import parseProject from 'pages/Project/parseProject'; import { CREATED, CONFLICT } from 'constants/statusCode'; -import { SUCCESS_FORK, CONFLICT_FORK } from 'constants/notificationMessage'; +import { + SUCCESS_FORK, + CONFLICT_FORK, + LOADING_PROJECT +} from 'constants/notificationMessage'; const DEFAULT_CLICKED_TAB_INDEX = 0; @@ -104,8 +109,7 @@ function Project() { useEffect(handleChangeHistoryAtForked, [status]); - // //TODO loading 컴포넌트 만들기 - if (loading) return

Loading...

; + if (loading) return ; if (error) history.push('/weAreSorry'); return ( From cd6fa5be99e8f8a298f0c58afef1a4fb82d84436 Mon Sep 17 00:00:00 2001 From: lallaheeee Date: Thu, 19 Dec 2019 19:02:43 +0900 Subject: [PATCH 30/54] =?UTF-8?q?fix:=20app.js=EC=9D=98=20=EB=9D=BC?= =?UTF-8?q?=EC=9A=B0=ED=8C=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 라이브 스토어 추가로 에러페이지 이동이 되지 않아 라우팅을 수정했습니다. --- cocode/src/App.js | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/cocode/src/App.js b/cocode/src/App.js index 83360f82..a4bcb8cd 100644 --- a/cocode/src/App.js +++ b/cocode/src/App.js @@ -9,14 +9,7 @@ import { getUserAPICreator } from 'apis/User'; import { LiveStore } from 'stores'; import GlobalStyle from 'components/Common/GlobalStyle'; -import { - Home, - DashBoard, - Project, - History, - NotFound, - Live -} from 'pages'; +import { Home, DashBoard, Project, History, NotFound, Live } from 'pages'; function App() { const [user, setUser] = useState(null); @@ -35,9 +28,11 @@ function App() { - - - + + + + + From 95871d892128515c8bf5069e02134f20ea89d2c9 Mon Sep 17 00:00:00 2001 From: hzoou Date: Thu, 19 Dec 2019 23:11:36 +0900 Subject: [PATCH 31/54] =?UTF-8?q?chore:=20=EB=8C=80=EC=8B=9C=EB=B3=B4?= =?UTF-8?q?=EB=93=9C=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=86=92=EC=9D=B4=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 대시보드 페이지의 높이를 수정했습니다. --- cocode/src/containers/DashBoard/ProjectCardList/style.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocode/src/containers/DashBoard/ProjectCardList/style.js b/cocode/src/containers/DashBoard/ProjectCardList/style.js index 72c9ab4c..bd6b1488 100644 --- a/cocode/src/containers/DashBoard/ProjectCardList/style.js +++ b/cocode/src/containers/DashBoard/ProjectCardList/style.js @@ -1,7 +1,7 @@ import styled from 'styled-components'; const Main = styled.main` - height: 100%; + height: 88vh; padding: 3rem; `; From f7d16c50e1c8fa15fdac448ecf3f78ec3e8aa601 Mon Sep 17 00:00:00 2001 From: hzoou Date: Thu, 19 Dec 2019 23:16:57 +0900 Subject: [PATCH 32/54] =?UTF-8?q?fix:=20#250=20=EB=A1=9C=EA=B7=B8=EC=95=84?= =?UTF-8?q?=EC=9B=83=20=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 로그아웃이 제대로 되지 않았던 버그를 수정하였습니다. 해당 버그는 디스크에 저장된 캐시에서 해당 유저에 대한 정보를 가져와서 로그아웃이 제대로 일어나지 않는 버그였습니다. 따라서 해당 버그를 수정하기 위해 api 통신을 요청할때 캐시를 사용하지 않겠다는 헤더를 추가하여 수정하였습니다. --- cocode/src/App.js | 2 +- cocode/src/config/index.js | 3 ++- cocode/src/containers/Common/Header/index.js | 4 +++- cocode/src/utils/deleteCookie.js | 1 - 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/cocode/src/App.js b/cocode/src/App.js index 83360f82..b9eade09 100644 --- a/cocode/src/App.js +++ b/cocode/src/App.js @@ -27,7 +27,7 @@ function App() { }, [data]); return ( - + diff --git a/cocode/src/config/index.js b/cocode/src/config/index.js index 7984ec25..1ebce12e 100644 --- a/cocode/src/config/index.js +++ b/cocode/src/config/index.js @@ -24,9 +24,10 @@ const COCODE_SERVER = : process.env.DEV_COCODE_SERVER_IP; const DEFAULT_REQUEST_OPTION = { + headers: {'Cache-Control': 'no-cache'}, withCredentials: true, mode: 'cors', - credentials: 'include' + credentials: 'include', }; const API = { diff --git a/cocode/src/containers/Common/Header/index.js b/cocode/src/containers/Common/Header/index.js index ac2e3a72..479b7f16 100644 --- a/cocode/src/containers/Common/Header/index.js +++ b/cocode/src/containers/Common/Header/index.js @@ -14,7 +14,7 @@ import UserContext from 'contexts/UserContext'; function Header({ name }) { const history = useHistory(); - const { user } = useContext(UserContext); + const { user, setUser } = useContext(UserContext); const [isSignInModalOpen, setIsSignInModalOpen] = useState(false); const handleOpenSignInModal = () => setIsSignInModalOpen(true); @@ -24,6 +24,8 @@ function Header({ name }) { const confirm = window.confirm('로그아웃 하시겠습니까?'); if (!confirm) return; deleteCookie('jwt'); + setUser(null); + history.replace('../'); }; const profileDropDownMenuItems = [ diff --git a/cocode/src/utils/deleteCookie.js b/cocode/src/utils/deleteCookie.js index ac26e1d5..a02f9ba0 100644 --- a/cocode/src/utils/deleteCookie.js +++ b/cocode/src/utils/deleteCookie.js @@ -2,7 +2,6 @@ import { DELETE_COOKIE_VALUE } from 'constants/cookie'; function deleteCookie(key) { document.cookie = key + DELETE_COOKIE_VALUE; - window.location.reload(); }; export default deleteCookie; \ No newline at end of file From 4f3b5c2c6ac4a98947f80b2ec86270311fd85340 Mon Sep 17 00:00:00 2001 From: hzoou Date: Thu, 19 Dec 2019 23:28:00 +0900 Subject: [PATCH 33/54] =?UTF-8?q?fix:=20=EC=BD=94=EB=93=9C=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 코드리뷰를 반영했습니다. --- cocode/src/containers/Common/Header/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cocode/src/containers/Common/Header/index.js b/cocode/src/containers/Common/Header/index.js index 479b7f16..f11ad798 100644 --- a/cocode/src/containers/Common/Header/index.js +++ b/cocode/src/containers/Common/Header/index.js @@ -12,6 +12,8 @@ import LoginModalBody from 'components/Common/LoginModalBody'; import UserContext from 'contexts/UserContext'; +const CONFIRM_LOGOUT = '로그아웃 하시겠습니까?'; + function Header({ name }) { const history = useHistory(); const { user, setUser } = useContext(UserContext); @@ -21,7 +23,7 @@ function Header({ name }) { const handleCloseSignInModal = () => setIsSignInModalOpen(false); const handleClickDashBoard = () => history.push('/dashboard'); const handleSignOut = () => { - const confirm = window.confirm('로그아웃 하시겠습니까?'); + const confirm = window.confirm(CONFIRM_LOGOUT); if (!confirm) return; deleteCookie('jwt'); setUser(null); From e90b8dabebc9d4d8b5184e3bbc77e9d063a94bd9 Mon Sep 17 00:00:00 2001 From: Basiltoast Date: Fri, 20 Dec 2019 00:18:43 +0900 Subject: [PATCH 34/54] =?UTF-8?q?feat:=20cocode=20live-editor=20=ED=94=84?= =?UTF-8?q?=EB=A1=9C=ED=86=A0=ED=83=80=EC=9E=85=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cocode/src/actions/Project.js | 5 + cocode/src/actions/types.js | 2 + cocode/src/containers/Live/Editor/index.js | 268 +++++++++++---------- cocode/src/reducers/ProjectReducer.js | 16 ++ cocode/src/utils/monacoWidget.js | 46 ++++ 5 files changed, 205 insertions(+), 132 deletions(-) create mode 100644 cocode/src/utils/monacoWidget.js diff --git a/cocode/src/actions/Project.js b/cocode/src/actions/Project.js index 29d4ca36..0c84f278 100644 --- a/cocode/src/actions/Project.js +++ b/cocode/src/actions/Project.js @@ -1,6 +1,7 @@ import { UPDATE_PROJECT_INFO, UPDATE_CODE, + UPDATE_CODE_FROM_FILE_ID, FETCH_PROJECT, SELECT_FILE, UPDATE_FILE_NAME, @@ -23,6 +24,9 @@ function fetchProjectActionCreator(payload) { function updateCodeActionCreator(payload) { return { type: UPDATE_CODE, payload }; } +function updateCodeFromFileIdActionCreator(payload) { + return { type: UPDATE_CODE_FROM_FILE_ID, payload }; +} function selectFileActionCreator(payload) { return { type: SELECT_FILE, payload }; @@ -59,6 +63,7 @@ function saveFileActionCreator(payload) { export { updateProjectInfoActionCreator, updateCodeActionCreator, + updateCodeFromFileIdActionCreator, fetchProjectActionCreator, selectFileActionCreator, updateFileNameActionCreator, diff --git a/cocode/src/actions/types.js b/cocode/src/actions/types.js index cc616f8b..6532f671 100644 --- a/cocode/src/actions/types.js +++ b/cocode/src/actions/types.js @@ -7,6 +7,7 @@ const API_FAIL = 'API_FAILURE'; // Project const UPDATE_PROJECT_INFO = 'updateProjectInfo'; const UPDATE_CODE = 'updateCode'; +const UPDATE_CODE_FROM_FILE_ID = 'updateCodeFromFileId'; const FETCH_PROJECT = 'fetchProject'; const SELECT_FILE = 'selectFile'; const CREATE_FILE = 'createFile'; @@ -35,6 +36,7 @@ export { API_FAIL, UPDATE_PROJECT_INFO, UPDATE_CODE, + UPDATE_CODE_FROM_FILE_ID, FETCH_PROJECT, SELECT_FILE, UPDATE_FILE_NAME, diff --git a/cocode/src/containers/Live/Editor/index.js b/cocode/src/containers/Live/Editor/index.js index 7892b195..0124c446 100644 --- a/cocode/src/containers/Live/Editor/index.js +++ b/cocode/src/containers/Live/Editor/index.js @@ -8,103 +8,81 @@ import MonacoEditor from 'components/Project/MonacoEditor'; import { LiveContext, UserContext, ProjectContext } from 'contexts'; import { updateCodeActionCreator, - saveFileActionCreator + updateCodeFromFileIdActionCreator } from 'actions/Project'; import useFetch from 'hooks/useFetch'; -import { updateFileAPICreator } from 'apis/File'; - -import { isPressCtrlAndS } from 'utils/keyDownEvent'; - - -class CursorWidget { - constructor(editor, userName, position) { - this.editor = editor; - this.id = userName; - this.domNode = null; - this.position = position; - } - - getId() { - return this.id; - } - - getDomNode() { - if (!this.domNode) { - this.domNode = document.createElement('div'); - this.domNode.innerHTML = this.id; - this.domNode.style.background = 'grey'; - this.domNode.id = this.id; - } - return this.domNode; - } - - getPosition() { - return { - position: this.position, - preference: [0] - }; - } - updatePosition(position) { - this.position = position; - this.editor.layoutContentWidget(this); - } -} -// Constatnts +import { CursorWidget } from 'utils/monacoWidget'; + let timer; -const DEBOUNCING_TIME = 800; +const DEBOUNCING_TIME = 1000; + +const MAX_RANGE = { + startLineNumber: 1, + startColumn: 1, + endLineNumber: 9999, + endColumn: 9999 +}; + const userCursor = {}; function Editor({ handleForkCoconut }) { const { user } = useContext(UserContext); const { projectId } = useParams(); const { project, dispatchProject } = useContext(ProjectContext); - const { socket, dispatchLive } = useContext(LiveContext); + const { socket } = useContext(LiveContext); const [code, setCode] = useState(project.editingCode); const [isEditorMounted, setIsEditorMounted] = useState(false); const [_, setRequest] = useFetch({}); const [fileSelectFlag, setFileSelectFlag] = useState(undefined); - const { selectedFileId } = project; + const { selectedFileId, files } = project; const editorRef = useRef(); const isBusy = useRef(true); const pendingEvent = useRef(false); + const selectedRef = useRef(); + const filesRef = useRef(); const handleOnChangeCodeInMonaco = (_, changedCode) => { - // if (timer) clearTimeout(timer); + if (timer) clearTimeout(timer); - // timer = setTimeout(() => { - // setCode(changedCode); - // }, DEBOUNCING_TIME); + timer = setTimeout(() => { + setCode(changedCode); + }, DEBOUNCING_TIME); }; - const handleChangedSelectedFile = () => setCode(project.editingCode); - - const handleRequestUpdateCode = () => { - if (!isEditorMounted) return; - - const updateFileAPI = updateFileAPICreator(projectId, selectedFileId, { - contents: project.editingCode - }); - setRequest(updateFileAPI); + const handleChnageSelectedFileMonaco = ( + source, + text, + range = MAX_RANGE + ) => { + isBusy.current = true; + if (editorRef.current) { + editorRef.current.executeEdits(source, [ + { + range, + text, + forceMoveMarkers: true + } + ]); + } + setTimeout(() => { + isBusy.current = false; + }, 0); }; - const handleOnKeyDown = e => { - if (!isPressCtrlAndS(e)) return; - - e.preventDefault(); - const { files, selectedFileId } = project; - if (!files[selectedFileId].isEditing) return; - - if (user.username !== project.author) { - handleForkCoconut(); - return; - } + const handleChangedSelectedFile = () => { + if (!project) return; + if (!filesRef.current) return; + selectedRef.current = selectedFileId; + setCode(project.editingCode); - handleRequestUpdateCode(); - dispatchProject(saveFileActionCreator()); + handleChnageSelectedFileMonaco( + 'changeFile', + filesRef.current[selectedFileId].contents + ); }; const handleUpdateCode = () => { @@ -118,27 +96,30 @@ function Editor({ handleForkCoconut }) { dispatchProject(updateCodeAction); }; - const handleEmit = (e, timeStamp) => { - if (isBusy.current) return; - if (!timeStamp) timeStamp = Date(); - if (pendingEvent.current) handleEmit(e, timeStamp); - const change = e.changes[0]; - const operation = { - rangeLength: change.rangeLength, - rangeOffset: change.rangeOffset, - text: change.text.replace(/\r\n/g, '\n'), - timeStamp: timeStamp + const handleEmit = (e, timeStamp) => { + if (isBusy.current) return; + if (!timeStamp) timeStamp = Date(); + // if (pendingEvent.current) handleEmit(e, timeStamp); + const change = e.changes[0]; + const operation = { + rangeLength: change.rangeLength, + rangeOffset: change.rangeOffset, + text: change.text.replace(/\r\n/g, '\n'), + timeStamp: timeStamp }; - socket.emit('change', operation); - }; + if (!socket) return; + // pendingEvent.current = true; + socket.emit('change', selectedRef.current, operation); + }; - const handleCursor = e => { - socket.emit('moveCursor', e.position); + const handleCursor = e => { + if (!socket) return; + socket.emit('moveCursor', selectedRef.current, e.position); }; const handleEditorDidMount = (_, editor) => { editorRef.current = editor; - editor.onDidChangeModelContent(handleEmit); + editor.onDidChangeModelContent(handleEmit); editor.onDidChangeCursorPosition(handleCursor); setIsEditorMounted(true); }; @@ -147,70 +128,93 @@ function Editor({ handleForkCoconut }) { useEffect(handleChangedSelectedFile, [project.selectedFileId]); useEffect(() => { + if (!isEditorMounted) return; + selectedRef.current = selectedFileId; + isBusy.current = true; + handleChnageSelectedFileMonaco('initial', project.editingCode); + }, [isEditorMounted]); + + useEffect(() => { + //initialize if (!socket) return; + if (!isEditorMounted) return; isBusy.current = false; - socket.on('change', (socketId, op) => { - if (socket.id === socketId) { - setTimeout(() => { - pendingEvent.current = false; - }, 10); - } else { - const rangeOffset = op.rangeOffset; - const rangeLength = op.rangeLength; - const text = op.text; - - const startPosition = editorRef.current - .getModel() - .getPositionAt(rangeOffset); - const endPosition = editorRef.current - .getModel() - .getPositionAt(rangeOffset + rangeLength); - isBusy.current = true; - editorRef.current.executeEdits(socketId, [ - { - range: { - startLineNumber: startPosition.lineNumber, - startColumn: startPosition.column, - endLineNumber: endPosition.lineNumber, - endColumn: endPosition.column - }, - text, - forceMoveMarkers: true - } - ]); - isBusy.current = false; - } - }); + filesRef.current = JSON.parse(JSON.stringify(files)); + socket.on('change', handleOnChangeCode); + socket.on('moveCursor', handleMoveCursor); + }, [socket, isEditorMounted]); + + const handleOnChangeCode = (socketId, fileId, op) => { + if (socket.id === socketId) { + setTimeout(() => { + pendingEvent.current = false; + }, 10); + return; + } - socket.on('moveCursor', (username, position) => { - if (!userCursor[username]) { - const widget = new CursorWidget( - editorRef.current, - username, - position - ); - userCursor[username] = widget; - editorRef.current.addContentWidget(widget); - return; - } - userCursor[username].updatePosition(position); + if (selectedRef.current !== fileId) { + const originCode = filesRef.current[fileId].contents; + + const str1 = originCode.slice(0, op.rangeOffset); + const str2 = originCode.slice(op.rangeOffset + op.rangeLength); + const changedCode = `${str1}${op.text}${str2}`; + filesRef.current[fileId].contents = changedCode; + const updateCodeFromFileIdAction = updateCodeFromFileIdActionCreator( + { + fileId, + changedCode + } + ); + dispatchProject(updateCodeFromFileIdAction); + return; + } + + const rangeOffset = op.rangeOffset; + const rangeLength = op.rangeLength; + const text = op.text; + + const startPosition = editorRef.current + .getModel() + .getPositionAt(rangeOffset); + const endPosition = editorRef.current + .getModel() + .getPositionAt(rangeOffset + rangeLength); + + handleChnageSelectedFileMonaco(socketId, text, { + startLineNumber: startPosition.lineNumber, + startColumn: startPosition.column, + endLineNumber: endPosition.lineNumber, + endColumn: endPosition.column }); - }, [socket]); + }; + + const handleMoveCursor = (username, fileId, position) => { + if (!userCursor[username]) { + const widget = new CursorWidget( + editorRef.current, + username, + position + ); + userCursor[username] = widget; + editorRef.current.addContentWidget(widget); + } + if (selectedRef.current === fileId) + userCursor[username].showCursor(position); + else userCursor[username].hiddenCursor(); + }; return ( ); } - export default Editor; diff --git a/cocode/src/reducers/ProjectReducer.js b/cocode/src/reducers/ProjectReducer.js index d36d9d81..dea3c481 100644 --- a/cocode/src/reducers/ProjectReducer.js +++ b/cocode/src/reducers/ProjectReducer.js @@ -2,6 +2,7 @@ import { UPDATE_PROJECT_INFO, UPDATE_CODE, + UPDATE_CODE_FROM_FILE_ID, FETCH_PROJECT, SELECT_FILE, CREATE_FILE, @@ -109,6 +110,20 @@ const updateCode = (state, { changedCode }) => { }; }; +const updateCodeFromFileId = (state, { fileId, changedCode }) => { + return { + ...state, + files: { + ...state.files, + [fileId]: { + ...state.files[fileId], + contents: changedCode, + isEditing: true + } + } + }; +}; + // Select file const selectFile = (state, { selectedFileId }) => { return { @@ -314,6 +329,7 @@ function ProjectReducer(state, { type, payload }) { [UPDATE_PROJECT_INFO]: updateProjectInfo, [FETCH_PROJECT]: fetchProject, [UPDATE_CODE]: updateCode, + [UPDATE_CODE_FROM_FILE_ID]: updateCodeFromFileId, [SELECT_FILE]: selectFile, [UPDATE_FILE_NAME]: updateFileName, [CREATE_FILE]: createFile, diff --git a/cocode/src/utils/monacoWidget.js b/cocode/src/utils/monacoWidget.js new file mode 100644 index 00000000..83bbe24a --- /dev/null +++ b/cocode/src/utils/monacoWidget.js @@ -0,0 +1,46 @@ +class CursorWidget { + constructor(editor, userName, position) { + this.editor = editor; + this.id = userName; + this.domNode = null; + this.position = position; + } + + getId() { + return this.id; + } + + getDomNode() { + if (!this.domNode) { + this.domNode = document.createElement('div'); + this.domNode.innerHTML = this.id; + this.domNode.style.background = 'grey'; + this.domNode.id = this.id; + } + return this.domNode; + } + + getPosition() { + return { + position: this.position, + preference: [0] + }; + } + updatePosition(position) { + this.position = position; + this.editor.layoutContentWidget(this); + } + + showCursor(position) { + this.domNode.style.visibility = 'inherit'; + this.position = position; + this.editor.layoutContentWidget(this); + } + + hiddenCursor() { + this.domNode.style.visibility = 'hidden'; + this.editor.layoutContentWidget(this); + } +} + +export { CursorWidget }; From 624491a2ac423fd2b5db95c363ada07f8559db66 Mon Sep 17 00:00:00 2001 From: lallaheeee Date: Fri, 20 Dec 2019 00:54:56 +0900 Subject: [PATCH 35/54] =?UTF-8?q?feat:=20sign=20in=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 대시보드에서 로그인하지 않았을 때 사용하려고 Sign in 페이지를 작성했습니다. --- cocode/src/App.js | 2 ++ cocode/src/containers/SignIn/index.js | 20 ++++++++++++ cocode/src/containers/SignIn/style.js | 44 +++++++++++++++++++++++++++ cocode/src/pages/SignIn/index.js | 21 +++++++++++++ cocode/src/pages/index.js | 3 +- 5 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 cocode/src/containers/SignIn/index.js create mode 100644 cocode/src/containers/SignIn/style.js create mode 100644 cocode/src/pages/SignIn/index.js diff --git a/cocode/src/App.js b/cocode/src/App.js index a4bcb8cd..5c04255b 100644 --- a/cocode/src/App.js +++ b/cocode/src/App.js @@ -10,6 +10,7 @@ import { LiveStore } from 'stores'; import GlobalStyle from 'components/Common/GlobalStyle'; import { Home, DashBoard, Project, History, NotFound, Live } from 'pages'; +import SignIn from './pages/SignIn'; function App() { const [user, setUser] = useState(null); @@ -34,6 +35,7 @@ function App() { + diff --git a/cocode/src/containers/SignIn/index.js b/cocode/src/containers/SignIn/index.js new file mode 100644 index 00000000..858eaced --- /dev/null +++ b/cocode/src/containers/SignIn/index.js @@ -0,0 +1,20 @@ +import React from 'react'; +import * as Styled from './style'; +import Github from 'components/Common/LoginModalBody/github.svg'; +import { API } from 'config'; + +function SignIn() { + const handleClickLoginButton = () => (window.location.href = API.login); + + return ( + + Sign In + + + Sign In With GitHub + + + ); +} + +export default SignIn; diff --git a/cocode/src/containers/SignIn/style.js b/cocode/src/containers/SignIn/style.js new file mode 100644 index 00000000..26908997 --- /dev/null +++ b/cocode/src/containers/SignIn/style.js @@ -0,0 +1,44 @@ +import styled from 'styled-components'; + +const Logo = styled.img` + padding-right: 1rem; + padding-top: 0.3rem; + + height: 1.5rem; + filter: invert(1); +`; + +const Wrapper = styled.div` + & { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + + height: 70vh; + padding: 5rem; + } +`; + +const Title = styled.h1` + & { + text-align: center; + font-size: 3rem; + font-weight: 700; + } +`; + +const LoginButton = styled.button` + & { + margin: 2rem 0 1.5rem 0; + padding: 1rem 2.5rem; + + font-size: 1.5rem; + + background-color: white; + color: black; + border-radius: 0.5rem; + } +`; + +export { Logo, Wrapper, LoginButton, Title }; diff --git a/cocode/src/pages/SignIn/index.js b/cocode/src/pages/SignIn/index.js new file mode 100644 index 00000000..cc65d251 --- /dev/null +++ b/cocode/src/pages/SignIn/index.js @@ -0,0 +1,21 @@ +import React, { useContext } from 'react'; +import { useHistory } from 'react-router-dom'; +import Header from 'containers/Common/Header'; +import SignInContainer from 'containers/SignIn'; +import { UserContext } from 'contexts'; + +function SignIn() { + const { user } = useContext(UserContext); + const history = useHistory(); + + if (user) history.replace('../'); + + return ( + <> +
+ + + ); +} + +export default SignIn; diff --git a/cocode/src/pages/index.js b/cocode/src/pages/index.js index e272822b..c9010d95 100644 --- a/cocode/src/pages/index.js +++ b/cocode/src/pages/index.js @@ -5,5 +5,6 @@ import History from './History'; import Version1 from './Version1'; import NotFound from './NotFound'; import Live from './Live'; +import SignIn from './SignIn'; -export { Home, DashBoard, Project, History, Version1, NotFound, Live }; +export { Home, DashBoard, Project, History, Version1, NotFound, Live, SignIn }; From 1719f07bee1b36be14a4a765a90a73c3dd1aa8b8 Mon Sep 17 00:00:00 2001 From: Basiltoast Date: Fri, 20 Dec 2019 01:18:28 +0900 Subject: [PATCH 36/54] =?UTF-8?q?feat:=20=EC=BD=94=EB=93=9C=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit setTimeout에서 사용하던 상수를 정의하였습니다. --- cocode/src/containers/Live/Editor/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cocode/src/containers/Live/Editor/index.js b/cocode/src/containers/Live/Editor/index.js index 0124c446..c75558fc 100644 --- a/cocode/src/containers/Live/Editor/index.js +++ b/cocode/src/containers/Live/Editor/index.js @@ -17,6 +17,7 @@ import { CursorWidget } from 'utils/monacoWidget'; let timer; const DEBOUNCING_TIME = 1000; +const EVENT_DELAY = 10; const MAX_RANGE = { startLineNumber: 1, @@ -148,7 +149,7 @@ function Editor({ handleForkCoconut }) { if (socket.id === socketId) { setTimeout(() => { pendingEvent.current = false; - }, 10); + }, EVENT_DELAY); return; } From 5276d86d3c7084f5cad34d50f089857139f29259 Mon Sep 17 00:00:00 2001 From: lallaheeee Date: Fri, 20 Dec 2019 01:25:57 +0900 Subject: [PATCH 37/54] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EC=97=AC=EB=B6=80=EB=A5=BC=20=EC=8B=9D=EB=B3=84=ED=95=98?= =?UTF-8?q?=EB=A0=A4=EA=B3=A0=20=EC=BF=A0=ED=82=A4=EB=A5=BC=20=EC=96=BB?= =?UTF-8?q?=EC=96=B4=EC=98=A4=EB=8A=94=20=EC=9C=A0=ED=8B=B8=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 대시보드에서 로그인 여부를 식별하려고 쿠키를 얻어오는 유틸을 작성했습니다. --- cocode/src/containers/Common/Header/index.js | 2 +- cocode/src/utils/controlCookie.js | 13 +++++++++++++ cocode/src/utils/deleteCookie.js | 7 ------- 3 files changed, 14 insertions(+), 8 deletions(-) create mode 100644 cocode/src/utils/controlCookie.js delete mode 100644 cocode/src/utils/deleteCookie.js diff --git a/cocode/src/containers/Common/Header/index.js b/cocode/src/containers/Common/Header/index.js index f11ad798..0b935fc5 100644 --- a/cocode/src/containers/Common/Header/index.js +++ b/cocode/src/containers/Common/Header/index.js @@ -2,7 +2,7 @@ import React, { useState, useContext } from 'react'; import * as Styled from './style'; import { Link, useHistory } from 'react-router-dom'; -import deleteCookie from 'utils/deleteCookie'; +import { deleteCookie } from 'utils/controlCookie'; import Logo from 'components/Common/Logo'; import Modal from 'components/Common/Modal'; diff --git a/cocode/src/utils/controlCookie.js b/cocode/src/utils/controlCookie.js new file mode 100644 index 00000000..a380f642 --- /dev/null +++ b/cocode/src/utils/controlCookie.js @@ -0,0 +1,13 @@ +import { DELETE_COOKIE_VALUE } from 'constants/cookie'; + +function deleteCookie(key) { + document.cookie = key + DELETE_COOKIE_VALUE; +} + +//참고: https://cofs.tistory.com/363 +function getCookie(name) { + const value = document.cookie.match('(^|;) ?' + name + '=([^;]*)(;|$)'); + return value ? value[2] : null; +} + +export { deleteCookie, getCookie }; diff --git a/cocode/src/utils/deleteCookie.js b/cocode/src/utils/deleteCookie.js deleted file mode 100644 index a02f9ba0..00000000 --- a/cocode/src/utils/deleteCookie.js +++ /dev/null @@ -1,7 +0,0 @@ -import { DELETE_COOKIE_VALUE } from 'constants/cookie'; - -function deleteCookie(key) { - document.cookie = key + DELETE_COOKIE_VALUE; -}; - -export default deleteCookie; \ No newline at end of file From b04fc3aff861eeddf372886cc8a69368fc36d9cd Mon Sep 17 00:00:00 2001 From: hzoou Date: Fri, 20 Dec 2019 01:28:22 +0900 Subject: [PATCH 38/54] =?UTF-8?q?refactor:=20=EC=98=A4=ED=83=88=EC=9E=90?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20=ED=8C=8C=EC=9D=BC=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 오탈자를 수정했으며 사용하지 않는 파일들을 삭제했습니다. --- cocode/src/App.js | 5 +- cocode/src/apis/Dependency.js | 14 ----- .../src/components/Common/DropZone/index.js | 2 +- .../components/DashBoard/ProjectCard/index.js | 2 +- .../src/components/Project/BrowserV1/index.js | 63 ------------------- .../src/components/Project/BrowserV1/style.js | 12 ---- .../src/components/Project/BrowserV2/index.js | 2 +- .../src/components/Project/Directory/index.js | 12 ++-- cocode/src/components/Project/File/index.js | 16 ++--- .../components/Project/FileTabBar/index.js | 2 +- .../src/components/Project/NewFile/index.js | 12 ++-- cocode/src/config/index.js | 10 --- cocode/src/constants/fileImagesSrc.js | 15 ++--- cocode/src/containers/Common/Header/index.js | 2 +- .../containers/Live/DependencyTab/index.js | 2 +- .../src/containers/Live/ExplorerTab/index.js | 2 +- cocode/src/containers/Live/TabBar/index.js | 2 +- .../src/containers/Live/TabContainer/index.js | 3 +- .../containers/Project/DependencyTab/index.js | 2 +- .../containers/Project/ExplorerTab/index.js | 4 +- .../src/containers/Project/InfoTab/index.js | 2 +- .../src/containers/Project/LiveTab/index.js | 2 +- cocode/src/containers/Project/TabBar/index.js | 2 +- .../containers/Project/TabContainer/index.js | 2 +- cocode/src/hooks/useFetch.js | 2 +- cocode/src/pages/DashBoard/index.js | 2 +- cocode/src/pages/History/index.js | 22 ------- cocode/src/pages/Live/index.js | 2 +- cocode/src/pages/Project/index.js | 9 ++- cocode/src/pages/Version1/index.js | 52 --------------- cocode/src/pages/Version1/style.js | 16 ----- cocode/src/pages/index.js | 4 +- ...ashboardReducer.js => DashBoardReducer.js} | 0 cocode/src/reducers/index.js | 6 ++ cocode/src/stores/LiveStore.js | 2 +- cocode/src/utils/deleteCookie.js | 2 +- 36 files changed, 59 insertions(+), 252 deletions(-) delete mode 100644 cocode/src/apis/Dependency.js delete mode 100644 cocode/src/components/Project/BrowserV1/index.js delete mode 100644 cocode/src/components/Project/BrowserV1/style.js delete mode 100644 cocode/src/pages/History/index.js delete mode 100644 cocode/src/pages/Version1/index.js delete mode 100644 cocode/src/pages/Version1/style.js rename cocode/src/reducers/{DashboardReducer.js => DashBoardReducer.js} (100%) diff --git a/cocode/src/App.js b/cocode/src/App.js index 29cd482a..4693a057 100644 --- a/cocode/src/App.js +++ b/cocode/src/App.js @@ -3,13 +3,13 @@ import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'; import { ThemeProvider } from 'styled-components'; import { DEFAULT_THEME } from 'constants/theme'; -import UserContext from 'contexts/UserContext'; +import { UserContext } from 'contexts'; import useFetch from 'hooks/useFetch'; import { getUserAPICreator } from 'apis/User'; import { LiveStore } from 'stores'; import GlobalStyle from 'components/Common/GlobalStyle'; -import { Home, DashBoard, Project, History, NotFound, Live } from 'pages'; +import { Home, DashBoard, Project, NotFound, Live } from 'pages'; function App() { const [user, setUser] = useState(null); @@ -33,7 +33,6 @@ function App() { - diff --git a/cocode/src/apis/Dependency.js b/cocode/src/apis/Dependency.js deleted file mode 100644 index d1435fb0..00000000 --- a/cocode/src/apis/Dependency.js +++ /dev/null @@ -1,14 +0,0 @@ -import { DEPENDENCY } from 'config'; - -function getModule(moduleName, moduleVersion) { - return { - url: `${DEPENDENCY.modules}`, - method: 'post', - data: { - moduleName, - moduleVersion - } - }; -} - -export { getModule }; diff --git a/cocode/src/components/Common/DropZone/index.js b/cocode/src/components/Common/DropZone/index.js index f31fd37b..3a065c7a 100644 --- a/cocode/src/components/Common/DropZone/index.js +++ b/cocode/src/components/Common/DropZone/index.js @@ -26,7 +26,7 @@ function DropZone({ draggableComponentOverColor, ...props }) { onDragLeave={handleDragLeave} onDrop={handleDrop} {...props} - > + /> ); } diff --git a/cocode/src/components/DashBoard/ProjectCard/index.js b/cocode/src/components/DashBoard/ProjectCard/index.js index 61fca6ce..12e882db 100644 --- a/cocode/src/components/DashBoard/ProjectCard/index.js +++ b/cocode/src/components/DashBoard/ProjectCard/index.js @@ -15,7 +15,7 @@ import { updateCoconutNameActionCreator, deleteCoconutActionCreator } from 'actions/Dashboard'; -import DashBoardContext from 'contexts/DashBoardContext'; +import { DashBoardContext } from 'contexts'; import useFetch from 'hooks/useFetch'; import { updateCoconutsAPICreator, diff --git a/cocode/src/components/Project/BrowserV1/index.js b/cocode/src/components/Project/BrowserV1/index.js deleted file mode 100644 index 6555dcf3..00000000 --- a/cocode/src/components/Project/BrowserV1/index.js +++ /dev/null @@ -1,63 +0,0 @@ -import React, { useState, useEffect, useContext } from 'react'; -import * as Styled from './style'; - -import ProjectContext from 'contexts/ProjectContext'; -// import { updateCodeActionCreator } from 'actions/Project'; -// import * as bundler from 'bundler'; - -// import * as babel from '@babel/core'; -// import reactPreset from '@babel/preset-react'; - -function BrowserV1({ code, ...props }) { - const { project, dispatchProject } = useContext(ProjectContext); - const { files, entry, selectedFileId } = project; - const [fileSystem, setFileSystem] = useState({}); - // const buildCode = () => { - // try { - // const parsedCode = babel.transform(code, { - // presets: [reactPreset] - // }); - // eval(parsedCode.code); - // } catch (e) { - // console.log(e); - // } - // }; - - // useEffect(buildCode, [files]); - // useEffect(() => { - // const fileTemp = {}; - // Object.keys(bundler.exports).forEach(key => { - // delete bundler.exports[key]; - // }); - // function fileParser(id, path = '') { - // if (files[id].type !== 'directory') { - // fileTemp[`${path}/${files[id].name}`] = { - // contents: files[id].contents - // }; - // bundler.exports[`${path}/${files[id].name}`] = { - // contents: files[id].contents - // }; - // } else { - // files[id].child.forEach(file => { - // fileParser(file, `${path}/${files[id].name}`); - // }); - // } - // } - // if (project) fileParser(project.root); - - // setFileSystem(fileTemp); - // }, [files]); - - // useEffect(() => { - // console.log(fileSystem); - // try { - // bundler.init(); - // bundler.require('/root/src/index'); - // } catch (error) { - // console.log(error); - // } - // }, [fileSystem]); - return ; -} - -export default BrowserV1; diff --git a/cocode/src/components/Project/BrowserV1/style.js b/cocode/src/components/Project/BrowserV1/style.js deleted file mode 100644 index 0fa19cf7..00000000 --- a/cocode/src/components/Project/BrowserV1/style.js +++ /dev/null @@ -1,12 +0,0 @@ -import styled from 'styled-components'; - -const BrowserV1 = styled.div` - & { - height: ${({ height }) => height}; - - background-color: white; - color: black; - } -`; - -export { BrowserV1 }; diff --git a/cocode/src/components/Project/BrowserV2/index.js b/cocode/src/components/Project/BrowserV2/index.js index 702a5276..bda99c4e 100644 --- a/cocode/src/components/Project/BrowserV2/index.js +++ b/cocode/src/components/Project/BrowserV2/index.js @@ -11,7 +11,7 @@ import * as Styled from './style'; import addToast from 'components/Common/Toast'; import CoconutSpinner from 'components/Common/CoconutSpinner'; -import ProjectContext from 'contexts/ProjectContext'; +import { ProjectContext } from 'contexts'; import { installDependencyActionCreator } from 'actions/Project'; diff --git a/cocode/src/components/Project/Directory/index.js b/cocode/src/components/Project/Directory/index.js index 2a9d2813..de1f9c2c 100644 --- a/cocode/src/components/Project/Directory/index.js +++ b/cocode/src/components/Project/Directory/index.js @@ -7,7 +7,7 @@ import DropZone from 'components/Common/DropZone'; import File from 'components/Project/File'; import NewFile from 'components/Project/NewFile'; -import ProjectContext from 'contexts/ProjectContext'; +import { ProjectContext } from 'contexts'; import { EXPLORER_TAB_CONTAINER_THEME } from 'constants/theme'; import * as NOTIFICATION from 'constants/notificationMessage'; @@ -37,11 +37,11 @@ function isProtectedFile({ files, root, entry, fileId }) { return false; } -function isFileNotMoveable({ files, fileId, newParentId }) { +function isFileNotMovable({ files, fileId, newParentId }) { const fileName = files[fileId].name; - const childsOfNewParent = files[newParentId].child; + const childrenOfNewParent = files[newParentId].child; - return childsOfNewParent + return childrenOfNewParent .map(id => files[id].name) .some(name => name === fileName); } @@ -93,7 +93,7 @@ function Directory({ .filter(isNotPackageJSON) : []; - // Evnet handler + // Event handler const handleToggleDirectory = () => setToggleDirectoryOpen(!toggleDirectoryOpen); const handleEditFileName = changedName => { @@ -114,7 +114,7 @@ function Directory({ if ( fileId === id || isProtectedFile({ files, root, entry, fileId }) || - isFileNotMoveable({ files, fileId, newParentId: id }) + isFileNotMovable({ files, fileId, newParentId: id }) ) return addToast.error(NOTIFICATION.FILE_IS_NOT_MOVABLE); diff --git a/cocode/src/components/Project/File/index.js b/cocode/src/components/Project/File/index.js index b51b6177..58cdb1aa 100644 --- a/cocode/src/components/Project/File/index.js +++ b/cocode/src/components/Project/File/index.js @@ -25,7 +25,7 @@ import { deleteFileAPICreator, updateFileAPICreator } from 'apis/File'; import { DELETE_FILE, UPDATE_FILE_NAME } from 'actions/types'; -import ProjectContext from 'contexts/ProjectContext'; +import { ProjectContext } from 'contexts'; // Constants const API_NOTIFICATION = { @@ -34,9 +34,9 @@ const API_NOTIFICATION = { }; function isNotChangeableFileName({ files, changedName, parentId }) { - const childsOfParent = files[parentId].child; + const childrenOfParent = files[parentId].child; - return childsOfParent + return childrenOfParent .map(id => files[id].name) .some(name => name === changedName); } @@ -60,7 +60,7 @@ function File({ const [toggleEdit, setToggleEdit] = useState(false); const [requestedAPI, setRequestedAPI] = useState(null); - const nameEditReferenece = useRef(null); + const nameEditReference = useRef(null); const [{ data, error }, setRequest] = useFetch({}); const { @@ -71,7 +71,7 @@ function File({ const successHandler = { [DELETE_FILE]: handleDeleteFile, [UPDATE_FILE_NAME]: () => { - const changedName = nameEditReferenece.current.textContent; + const changedName = nameEditReference.current.textContent; setFileName(changedName); handleEditFileName(changedName); } @@ -87,7 +87,7 @@ function File({ return; } - changeDivEditable(nameEditReferenece.current, true); + changeDivEditable(nameEditReference.current, true); setToggleEdit(true); }; @@ -138,7 +138,7 @@ function File({ const handleKeyDown = e => { if (e.keyCode === KEY_CODE_ENTER) { setToggleEdit(false); - nameEditReferenece.current.contentEditable = false; + nameEditReference.current.contentEditable = false; } }; @@ -177,7 +177,7 @@ function File({ > { @@ -76,17 +76,17 @@ function NewFile({ depth, type, parentId, handleEndCreateFile }) { type }); dispatchProject(createFileAction); - changeDivEditable(fileNameInputReferenece.current, false); + changeDivEditable(fileNameInputReference.current, false); }; const handleErrorResponse = () => { if (!error) return; addToast.error(NOTIFICATION.FAIL_TO_CREATE_FILE); - changeDivEditable(fileNameInputReferenece.current, false); + changeDivEditable(fileNameInputReference.current, false); }; useEffect(() => { - fileNameInputReferenece.current.focus(); + fileNameInputReference.current.focus(); }, []); useEffect(handleSetNewFileState, [data]); useEffect(handleErrorResponse, [error]); @@ -95,7 +95,7 @@ function NewFile({ depth, type, parentId, handleEndCreateFile }) { `${API_SERVER}/dependency/search?name=${name}` }; -const DEPENDENCY = { - modules: `${DEPENDENCY_SERVER}/modules` -}; - export { DEFAULT_REQUEST_OPTION, API, - DEPENDENCY, COCONUT_SERVER, LIVE_SERVER, COCODE_SERVER diff --git a/cocode/src/constants/fileImagesSrc.js b/cocode/src/constants/fileImagesSrc.js index 8f0fbeef..4361fdd5 100644 --- a/cocode/src/constants/fileImagesSrc.js +++ b/cocode/src/constants/fileImagesSrc.js @@ -1,16 +1,11 @@ const FILE_IMAGES_SRC = { file: 'https://codesandbox.io/static/media/file.6cbc0ce8.svg', directory: 'https://codesandbox.io/static/media/folder.30a30d83.svg', - directoryOpen: - 'https://codesandbox.io/static/media/folder-open.df474ba4.svg', - js: - 'https://cdn.jsdelivr.net/gh/PKief/vscode-material-icon-theme@master/icons/javascript.svg', - css: - 'https://cdn.jsdelivr.net/gh/PKief/vscode-material-icon-theme@master/icons/css.svg', - html: - 'https://cdn.jsdelivr.net/gh/PKief/vscode-material-icon-theme@master/icons/html.svg', - npm: - 'https://cdn.jsdelivr.net/gh/PKief/vscode-material-icon-theme@master/icons/npm.svg' + directoryOpen: 'https://codesandbox.io/static/media/folder-open.df474ba4.svg', + js: 'https://cdn.jsdelivr.net/gh/PKief/vscode-material-icon-theme@master/icons/javascript.svg', + css: 'https://cdn.jsdelivr.net/gh/PKief/vscode-material-icon-theme@master/icons/css.svg', + html: 'https://cdn.jsdelivr.net/gh/PKief/vscode-material-icon-theme@master/icons/html.svg', + npm: 'https://cdn.jsdelivr.net/gh/PKief/vscode-material-icon-theme@master/icons/npm.svg' }; export default FILE_IMAGES_SRC; diff --git a/cocode/src/containers/Common/Header/index.js b/cocode/src/containers/Common/Header/index.js index f11ad798..ad89a848 100644 --- a/cocode/src/containers/Common/Header/index.js +++ b/cocode/src/containers/Common/Header/index.js @@ -10,7 +10,7 @@ import UserProfile from 'components/Common/UserProfile'; import ModalPortal from 'components/Common/ModalPortal'; import LoginModalBody from 'components/Common/LoginModalBody'; -import UserContext from 'contexts/UserContext'; +import { UserContext } from 'contexts'; const CONFIRM_LOGOUT = '로그아웃 하시겠습니까?'; diff --git a/cocode/src/containers/Live/DependencyTab/index.js b/cocode/src/containers/Live/DependencyTab/index.js index d027436c..ec529a8d 100644 --- a/cocode/src/containers/Live/DependencyTab/index.js +++ b/cocode/src/containers/Live/DependencyTab/index.js @@ -6,7 +6,7 @@ import Dependency from 'components/Project/Dependency'; import DependencyNow from 'components/Project/DependencyNow'; import DependencySearch from 'components/Project/DependencySearch'; -import ProjectContext from 'contexts/ProjectContext'; +import { ProjectContext } from 'contexts'; const TabTitleFirst = 'DEPENDENCIES'; const TabTitleSecond = 'SEARCH DEPENDENCY'; diff --git a/cocode/src/containers/Live/ExplorerTab/index.js b/cocode/src/containers/Live/ExplorerTab/index.js index a40810e8..a3afa388 100644 --- a/cocode/src/containers/Live/ExplorerTab/index.js +++ b/cocode/src/containers/Live/ExplorerTab/index.js @@ -8,7 +8,7 @@ import { import Directory from 'components/Project/Directory'; import NewFile from 'components/Project/NewFile'; -import ProjectContext from 'contexts/ProjectContext'; +import { ProjectContext } from 'contexts'; import { selectFileActionCreator, updateFileNameActionCreator, diff --git a/cocode/src/containers/Live/TabBar/index.js b/cocode/src/containers/Live/TabBar/index.js index f2bc82c5..e8caca58 100644 --- a/cocode/src/containers/Live/TabBar/index.js +++ b/cocode/src/containers/Live/TabBar/index.js @@ -1,7 +1,7 @@ import React, { useContext } from 'react'; import * as Styled from './style'; -import ProjectContext from 'contexts/ProjectContext'; +import { ProjectContext } from 'contexts'; import TabIcon from 'components/Project/TabIcon'; import Explorer from './explorer.svg'; import Live from './live.svg'; diff --git a/cocode/src/containers/Live/TabContainer/index.js b/cocode/src/containers/Live/TabContainer/index.js index c425d30b..963ee87a 100644 --- a/cocode/src/containers/Live/TabContainer/index.js +++ b/cocode/src/containers/Live/TabContainer/index.js @@ -1,8 +1,7 @@ import React, { useEffect, useContext } from 'react'; import * as Styled from './style'; -import ProjectContext from 'contexts/ProjectContext'; - +import { ProjectContext } from 'contexts'; import ExplorerTab from '../ExplorerTab'; import LiveTab from '../LiveTab'; diff --git a/cocode/src/containers/Project/DependencyTab/index.js b/cocode/src/containers/Project/DependencyTab/index.js index d027436c..ec529a8d 100644 --- a/cocode/src/containers/Project/DependencyTab/index.js +++ b/cocode/src/containers/Project/DependencyTab/index.js @@ -6,7 +6,7 @@ import Dependency from 'components/Project/Dependency'; import DependencyNow from 'components/Project/DependencyNow'; import DependencySearch from 'components/Project/DependencySearch'; -import ProjectContext from 'contexts/ProjectContext'; +import { ProjectContext } from 'contexts'; const TabTitleFirst = 'DEPENDENCIES'; const TabTitleSecond = 'SEARCH DEPENDENCY'; diff --git a/cocode/src/containers/Project/ExplorerTab/index.js b/cocode/src/containers/Project/ExplorerTab/index.js index 016fc38b..a3afa388 100644 --- a/cocode/src/containers/Project/ExplorerTab/index.js +++ b/cocode/src/containers/Project/ExplorerTab/index.js @@ -8,7 +8,7 @@ import { import Directory from 'components/Project/Directory'; import NewFile from 'components/Project/NewFile'; -import ProjectContext from 'contexts/ProjectContext'; +import { ProjectContext } from 'contexts'; import { selectFileActionCreator, updateFileNameActionCreator, @@ -16,7 +16,7 @@ import { moveFileActionCreator } from 'actions/Project'; -const TAB_TITLE = 'EXPLOLER'; +const TAB_TITLE = 'EXPLORER'; function TabHeader({ handleCreateFile }) { return ( diff --git a/cocode/src/containers/Project/InfoTab/index.js b/cocode/src/containers/Project/InfoTab/index.js index e6e36c33..a35980c0 100644 --- a/cocode/src/containers/Project/InfoTab/index.js +++ b/cocode/src/containers/Project/InfoTab/index.js @@ -4,7 +4,7 @@ import * as Styled from './style'; import Modify from './modify.svg'; import { KEY_CODE_ENTER } from 'constants/keyCode'; -import ProjectContext from 'contexts/ProjectContext'; +import { ProjectContext } from 'contexts'; import { updateProjectInfoActionCreator } from 'actions/Project'; import useFetch from 'hooks/useFetch'; import { diff --git a/cocode/src/containers/Project/LiveTab/index.js b/cocode/src/containers/Project/LiveTab/index.js index 57a01850..113b97b0 100644 --- a/cocode/src/containers/Project/LiveTab/index.js +++ b/cocode/src/containers/Project/LiveTab/index.js @@ -10,7 +10,7 @@ function LiveTab() { <> {TAB_TITLE}
- +
); diff --git a/cocode/src/containers/Project/TabBar/index.js b/cocode/src/containers/Project/TabBar/index.js index ad6e3d1f..aeb86c43 100644 --- a/cocode/src/containers/Project/TabBar/index.js +++ b/cocode/src/containers/Project/TabBar/index.js @@ -1,7 +1,7 @@ import React, { useContext } from 'react'; import * as Styled from './style'; -import ProjectContext from 'contexts/ProjectContext'; +import { ProjectContext } from 'contexts'; import TabIcon from 'components/Project/TabIcon'; diff --git a/cocode/src/containers/Project/TabContainer/index.js b/cocode/src/containers/Project/TabContainer/index.js index de4b2249..d54c8e2f 100644 --- a/cocode/src/containers/Project/TabContainer/index.js +++ b/cocode/src/containers/Project/TabContainer/index.js @@ -1,7 +1,7 @@ import React, { useEffect, useContext } from 'react'; import * as Styled from './style'; -import ProjectContext from 'contexts/ProjectContext'; +import { ProjectContext } from 'contexts'; import InfoTab from '../InfoTab'; import ExplorerTab from '../ExplorerTab'; diff --git a/cocode/src/hooks/useFetch.js b/cocode/src/hooks/useFetch.js index ad3522da..22889a38 100644 --- a/cocode/src/hooks/useFetch.js +++ b/cocode/src/hooks/useFetch.js @@ -1,7 +1,7 @@ import { useState, useEffect, useReducer } from 'react'; import axios from 'axios'; import { DEFAULT_REQUEST_OPTION } from 'config'; -import APIReducer from 'reducers/APIReducer'; +import { APIReducer } from 'reducers'; import { fetchReadyActionCreator, fetchLoadActionCreator, diff --git a/cocode/src/pages/DashBoard/index.js b/cocode/src/pages/DashBoard/index.js index be8129f8..15f4f0ef 100644 --- a/cocode/src/pages/DashBoard/index.js +++ b/cocode/src/pages/DashBoard/index.js @@ -5,7 +5,7 @@ import Header from 'containers/Common/Header'; import LoadingSpinner from 'containers/Common/LoadingSpinner'; import { UserContext, DashBoardContext } from 'contexts'; -import DashBoardReducer from 'reducers/DashboardReducer'; +import { DashBoardReducer } from 'reducers'; import useFetch from 'hooks/useFetch'; import { getCoconutsAPICreator } from 'apis/DashBoard'; import { fetchCoconutActionCreator } from 'actions/Dashboard'; diff --git a/cocode/src/pages/History/index.js b/cocode/src/pages/History/index.js deleted file mode 100644 index 246ca545..00000000 --- a/cocode/src/pages/History/index.js +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react'; -import { Route } from 'react-router-dom'; - -import { Version1 } from 'pages'; -import Header from 'containers/Common/Header'; -import CocodeHistory from 'containers/History/CocodeHistory'; - -function HistoryHome() { - return ; -} - -function History({ match }) { - return ( - <> -
- - - - ); -} - -export default History; diff --git a/cocode/src/pages/Live/index.js b/cocode/src/pages/Live/index.js index b2d57e7d..ea044051 100644 --- a/cocode/src/pages/Live/index.js +++ b/cocode/src/pages/Live/index.js @@ -19,7 +19,7 @@ import { SplitPaneContainer } from 'components/Common/SplitPane'; import addToast from 'components/Common/Toast'; import { LiveContext, ProjectContext, UserContext } from 'contexts'; -import ProjectReducer from 'reducers/ProjectReducer'; +import { ProjectReducer } from 'reducers'; import { fetchProjectActionCreator } from 'actions/Project'; import { liveOnActionCreator, diff --git a/cocode/src/pages/Project/index.js b/cocode/src/pages/Project/index.js index e6c9ddbe..ce1ea936 100644 --- a/cocode/src/pages/Project/index.js +++ b/cocode/src/pages/Project/index.js @@ -11,13 +11,12 @@ import BrowserV2 from 'components/Project/BrowserV2'; import { SplitPaneContainer } from 'components/Common/SplitPane'; import addToast from 'components/Common/Toast'; -import ProjectReducer from 'reducers/ProjectReducer'; -import ProjectContext from 'contexts/ProjectContext'; +import { ProjectReducer } from 'reducers'; +import { ProjectContext, UserContext } from 'contexts'; import { fetchProjectActionCreator } from 'actions/Project'; import { TAB_BAR_THEME } from 'constants/theme'; -import UserContext from 'contexts/UserContext'; import useFetch from 'hooks/useFetch'; import { reactTemplate } from 'template/react'; import copyProject from 'template/copyProject'; @@ -48,7 +47,7 @@ function Project() { const forkCoconut = ({ live, info }) => { if (!isNotMyProject) return false; - const parsedProject = pretreatBeforeFork({ live, info }); + const parsedProject = preTreatBeforeFork({ live, info }); const forkProjectInfoAPI = forkProjectAPICreator(parsedProject); setRequest(forkProjectInfoAPI); @@ -56,7 +55,7 @@ function Project() { return parsedProject._id; }; - const pretreatBeforeFork = ({ live, info }) => { + const preTreatBeforeFork = ({ live, info }) => { if (live) setIsLive(true); const username = user ? user.username : 'anonymous'; diff --git a/cocode/src/pages/Version1/index.js b/cocode/src/pages/Version1/index.js deleted file mode 100644 index 852be306..00000000 --- a/cocode/src/pages/Version1/index.js +++ /dev/null @@ -1,52 +0,0 @@ -import React, { useReducer, useEffect } from 'react'; -import * as Styled from './style'; - -import MonacoEditor from 'components/Project/MonacoEditor'; -import BrowserV1 from 'components/Project/BrowserV1'; -import { SplitPaneContainer } from 'components/Common/SplitPane'; - -import ProjectReducer from 'reducers/ProjectReducer'; - -import reactTemplate from 'template/copyProject'; - -import { - fetchProjectActionCreator, - updateCodeActionCreator -} from 'actions/Project'; - -function Version1() { - const [project, dispatchProject] = useReducer(ProjectReducer, {}); - - const handleFetchProject = () => { - const fetchProjectAction = fetchProjectActionCreator({ - project: reactTemplate() - }); - dispatchProject(fetchProjectAction); - }; - - const handleChangeCode = (_, changedCode) => { - const updateCodeAction = updateCodeActionCreator(changedCode); - dispatchProject(updateCodeAction); - }; - - useEffect(handleFetchProject, []); - - return ( - - - - - - - ); -} - -export default Version1; diff --git a/cocode/src/pages/Version1/style.js b/cocode/src/pages/Version1/style.js deleted file mode 100644 index 7689cf6c..00000000 --- a/cocode/src/pages/Version1/style.js +++ /dev/null @@ -1,16 +0,0 @@ -import styled from 'styled-components'; - -const Main = styled.main` - & { - display: flex; - flex-direction: row; - - height: ${({ theme }) => theme.exceptHeaderHeight}; - } - - .Stretch-item { - width: 100%; - } -`; - -export { Main }; diff --git a/cocode/src/pages/index.js b/cocode/src/pages/index.js index e272822b..ab6021ff 100644 --- a/cocode/src/pages/index.js +++ b/cocode/src/pages/index.js @@ -1,9 +1,7 @@ import Home from './Home'; import DashBoard from './DashBoard'; import Project from './Project'; -import History from './History'; -import Version1 from './Version1'; import NotFound from './NotFound'; import Live from './Live'; -export { Home, DashBoard, Project, History, Version1, NotFound, Live }; +export { Home, DashBoard, Project, NotFound, Live }; diff --git a/cocode/src/reducers/DashboardReducer.js b/cocode/src/reducers/DashBoardReducer.js similarity index 100% rename from cocode/src/reducers/DashboardReducer.js rename to cocode/src/reducers/DashBoardReducer.js diff --git a/cocode/src/reducers/index.js b/cocode/src/reducers/index.js index e69de29b..e334352b 100644 --- a/cocode/src/reducers/index.js +++ b/cocode/src/reducers/index.js @@ -0,0 +1,6 @@ +import APIReducer from './APIReducer'; +import DashBoardReducer from './DashBoardReducer'; +import LiveReducer from './LiveReducer'; +import ProjectReducer from './ProjectReducer'; + +export { APIReducer, DashBoardReducer, LiveReducer, ProjectReducer }; \ No newline at end of file diff --git a/cocode/src/stores/LiveStore.js b/cocode/src/stores/LiveStore.js index 8d37dfb2..d894ebda 100644 --- a/cocode/src/stores/LiveStore.js +++ b/cocode/src/stores/LiveStore.js @@ -1,5 +1,5 @@ import React, { useReducer } from 'react'; -import LiveReducer from 'reducers/LiveReducer'; +import { LiveReducer } from 'reducers'; import { LiveContext } from 'contexts'; import { LIVE_SERVER } from 'config'; diff --git a/cocode/src/utils/deleteCookie.js b/cocode/src/utils/deleteCookie.js index a02f9ba0..271c6c24 100644 --- a/cocode/src/utils/deleteCookie.js +++ b/cocode/src/utils/deleteCookie.js @@ -2,6 +2,6 @@ import { DELETE_COOKIE_VALUE } from 'constants/cookie'; function deleteCookie(key) { document.cookie = key + DELETE_COOKIE_VALUE; -}; +} export default deleteCookie; \ No newline at end of file From 5770ff16c2115478959f0d7074de3084b85a85e5 Mon Sep 17 00:00:00 2001 From: lallaheeee Date: Fri, 20 Dec 2019 01:28:45 +0900 Subject: [PATCH 39/54] =?UTF-8?q?feat:=20#241=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=EC=9D=B4=20=EB=90=98=EC=A7=80=20=EC=95=8A=EC=9D=80=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=EC=97=90=EC=84=9C=20=EB=8C=80=EC=8B=9C?= =?UTF-8?q?=EB=B3=B4=EB=93=9C=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=A0=91?= =?UTF-8?q?=EA=B7=BC=EC=9D=84=20=EB=A7=89=EC=9D=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 홈페이지로 이동 2. 홈페이지로 이동 및 로그인 모달 띄우기 3. 로그인 모달 띄우기 4. 로그인 페이지로 이동 위의 네가지 선택지 중 팀원 간의 협의가 이루어진 4의 방식대로 로그인이 되지 않은 상태에서 로그인 페이지로 이동하도록 구현했습니다. --- cocode/src/App.js | 2 +- cocode/src/pages/DashBoard/index.js | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/cocode/src/App.js b/cocode/src/App.js index 1ee42644..d679a359 100644 --- a/cocode/src/App.js +++ b/cocode/src/App.js @@ -35,7 +35,7 @@ function App() { - + diff --git a/cocode/src/pages/DashBoard/index.js b/cocode/src/pages/DashBoard/index.js index be8129f8..890f003a 100644 --- a/cocode/src/pages/DashBoard/index.js +++ b/cocode/src/pages/DashBoard/index.js @@ -10,6 +10,7 @@ import useFetch from 'hooks/useFetch'; import { getCoconutsAPICreator } from 'apis/DashBoard'; import { fetchCoconutActionCreator } from 'actions/Dashboard'; import { LOADING_DASHBOARD } from 'constants/notificationMessage'; +import { getCookie } from 'utils/controlCookie'; function DashBoard() { const { user } = useContext(UserContext); @@ -24,6 +25,8 @@ function DashBoard() { data && dispatchDashboard(fetchCoconutActionCreator(data)); }; + if (!getCookie('jwt')) history.replace('../signin'); + useEffect(handleRequestGetCoconutAPI, [user]); useEffect(handleSetDashBoardState, [data]); From 5efe1c9e31b6459ee3c8fda523fc813071a67aa3 Mon Sep 17 00:00:00 2001 From: hzoou Date: Fri, 20 Dec 2019 02:02:57 +0900 Subject: [PATCH 40/54] =?UTF-8?q?chore:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=8A=A4=ED=83=80=EC=9D=BC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 로그인 페이지 스타일을 수정했습니다. --- cocode/src/constants/theme.js | 9 ++++++++- cocode/src/containers/SignIn/style.js | 18 ++++++++++++------ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/cocode/src/constants/theme.js b/cocode/src/constants/theme.js index 5157fdff..6c0ff157 100644 --- a/cocode/src/constants/theme.js +++ b/cocode/src/constants/theme.js @@ -127,6 +127,12 @@ const TOAST_THEME = { toastErrorDeco: 'rgba(233,42,61,0.98)' }; +const SIGN_IN_THEME = { + signInButtonBGColor: '#e7e7e7', + signInButtonBGHoverColor: '#ffffff', + signInButtonTextColor: '#000000', +}; + export { DEFAULT_THEME, DROPDOWN_THEME, @@ -140,5 +146,6 @@ export { FILE_TAB_THEME, MONACO_THEME, LIVE_TAB_THEME, - TOAST_THEME + TOAST_THEME, + SIGN_IN_THEME }; diff --git a/cocode/src/containers/SignIn/style.js b/cocode/src/containers/SignIn/style.js index 26908997..8753b853 100644 --- a/cocode/src/containers/SignIn/style.js +++ b/cocode/src/containers/SignIn/style.js @@ -1,10 +1,9 @@ import styled from 'styled-components'; +import { SIGN_IN_THEME } from 'constants/theme'; const Logo = styled.img` - padding-right: 1rem; - padding-top: 0.3rem; - - height: 1.5rem; + height: 1.2rem; + margin-right: 0.8rem; filter: invert(1); `; @@ -30,15 +29,22 @@ const Title = styled.h1` const LoginButton = styled.button` & { + display: flex; + justify-content: center; + align-items: center; margin: 2rem 0 1.5rem 0; padding: 1rem 2.5rem; font-size: 1.5rem; - background-color: white; - color: black; + background-color: ${SIGN_IN_THEME.signInButtonBGColor}; + color: ${SIGN_IN_THEME.signInButtonTextColor}; border-radius: 0.5rem; } + + &:hover { + background-color: ${SIGN_IN_THEME.signInButtonBGHoverColor}; + } `; export { Logo, Wrapper, LoginButton, Title }; From 8def95193a85733cae268ee6f22540d964617338 Mon Sep 17 00:00:00 2001 From: hzoou Date: Fri, 20 Dec 2019 02:10:35 +0900 Subject: [PATCH 41/54] =?UTF-8?q?feat:=20#300=20=EB=AF=B8=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=EC=8B=9C=20=EB=9D=BC=EC=9D=B4=EB=B8=8C=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=A0=91=EA=B7=BC=20=EA=B8=88?= =?UTF-8?q?=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 라이브 페이지에서 로그인을 하지 않은 경우 로그인 페이지로 리다이렉트 시켰습니다. --- cocode/src/pages/Live/index.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cocode/src/pages/Live/index.js b/cocode/src/pages/Live/index.js index ea044051..cfd7d35b 100644 --- a/cocode/src/pages/Live/index.js +++ b/cocode/src/pages/Live/index.js @@ -5,7 +5,7 @@ import React, { useContext, useCallback } from 'react'; -import { useParams } from 'react-router-dom'; +import { useHistory, useParams } from 'react-router-dom'; import * as Styled from './style'; import io from 'socket.io-client'; @@ -33,11 +33,13 @@ import { COCODE_SERVER } from 'config'; import useFetch from 'hooks/useFetch'; import { getProjectInfoAPICreator } from 'apis/Project'; import { SHUT_DOWN_LIVE_SHARE, LOADING_LIVE } from 'constants/notificationMessage'; +import { getCookie } from 'utils/controlCookie'; const DEFAULT_CLICKED_TAB_INDEX = 0; let socket; function Live() { + const history = useHistory(); const { projectId } = useParams(); const { user } = useContext(UserContext); const { liveServer, dispatchLive } = useContext(LiveContext); @@ -49,6 +51,8 @@ function Live() { ); const [project, dispatchProject] = useReducer(ProjectReducer, {}); + if (!getCookie('jwt')) history.replace('../signin'); + const handleFetchProject = () => { const getProjectInfoAPI = getProjectInfoAPICreator(projectId); setRequest(getProjectInfoAPI); From 294a30d3a1b13e2fe2567746ccf2ca3d914b86a2 Mon Sep 17 00:00:00 2001 From: hzoou Date: Fri, 20 Dec 2019 02:58:38 +0900 Subject: [PATCH 42/54] =?UTF-8?q?chore:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=8A=A4=ED=83=80=EC=9D=BC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 로그인 페이지의 스타일을 수정했습니다. --- cocode/src/containers/SignIn/index.js | 4 +++- cocode/src/containers/SignIn/style.js | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/cocode/src/containers/SignIn/index.js b/cocode/src/containers/SignIn/index.js index 858eaced..b67a4a46 100644 --- a/cocode/src/containers/SignIn/index.js +++ b/cocode/src/containers/SignIn/index.js @@ -3,12 +3,14 @@ import * as Styled from './style'; import Github from 'components/Common/LoginModalBody/github.svg'; import { API } from 'config'; +const SIGN_IN_TITLE = 'Sorry, This service requires a login.'; + function SignIn() { const handleClickLoginButton = () => (window.location.href = API.login); return ( - Sign In + {SIGN_IN_TITLE} Sign In With GitHub diff --git a/cocode/src/containers/SignIn/style.js b/cocode/src/containers/SignIn/style.js index 8753b853..cabf42fd 100644 --- a/cocode/src/containers/SignIn/style.js +++ b/cocode/src/containers/SignIn/style.js @@ -23,7 +23,7 @@ const Title = styled.h1` & { text-align: center; font-size: 3rem; - font-weight: 700; + font-weight: 100; } `; From 270006ce9b2e86ce533d1e28b014fb1eb84f52bd Mon Sep 17 00:00:00 2001 From: hzoou Date: Fri, 20 Dec 2019 02:59:30 +0900 Subject: [PATCH 43/54] =?UTF-8?q?fix:=20#304=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= =?UTF-8?q?=20=EB=AA=A8=EB=8B=AC=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 프로젝트 페이지에서 로그인 모달이 split pane에 가려지는 현상을 수정하기 위해 z-index를 추가했습니다. --- cocode/src/components/Common/Modal/style.js | 1 + 1 file changed, 1 insertion(+) diff --git a/cocode/src/components/Common/Modal/style.js b/cocode/src/components/Common/Modal/style.js index 5ed807dd..e5ecf616 100644 --- a/cocode/src/components/Common/Modal/style.js +++ b/cocode/src/components/Common/Modal/style.js @@ -2,6 +2,7 @@ import styled from 'styled-components'; const ModalBackGround = styled.div` & { + z-index: 10; position: fixed; top: 0; left: 0; From b6a49313885914ad5d16e3daffa8a5c5bb383327 Mon Sep 17 00:00:00 2001 From: hzoou Date: Fri, 20 Dec 2019 03:19:57 +0900 Subject: [PATCH 44/54] =?UTF-8?q?feat:=20#303=20=EB=A1=9C=EC=BB=AC=20?= =?UTF-8?q?=EC=8A=A4=ED=86=A0=EB=A6=AC=EC=A7=80=EB=A5=BC=20=ED=86=B5?= =?UTF-8?q?=ED=95=9C=20redirect=20url=20=EA=B4=80=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 로그인을 해야하는 경우, 메인페이지로 이동하는 것이 아니라 원래의 url로 redirect 되어야 하기 때문에 해당 url을 로컬 스토리지에 저장하여 관리하는 로직을 구현하였습니다. --- cocode/src/components/Common/LoginModalBody/index.js | 5 ++++- cocode/src/pages/DashBoard/index.js | 5 ++++- cocode/src/pages/Live/index.js | 5 ++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/cocode/src/components/Common/LoginModalBody/index.js b/cocode/src/components/Common/LoginModalBody/index.js index c086585e..b9ad3c54 100644 --- a/cocode/src/components/Common/LoginModalBody/index.js +++ b/cocode/src/components/Common/LoginModalBody/index.js @@ -6,7 +6,10 @@ import { API } from 'config'; import Github from './github.svg'; function LoginModalBody() { - const handleClickLoginButton = () => (window.location.href = API.login); + const handleClickLoginButton = () => { + window.location.href = API.login; + localStorage.setItem('redirectURL', window.location.href); + }; return ( diff --git a/cocode/src/pages/DashBoard/index.js b/cocode/src/pages/DashBoard/index.js index fe9921b0..6ddcacd3 100644 --- a/cocode/src/pages/DashBoard/index.js +++ b/cocode/src/pages/DashBoard/index.js @@ -25,7 +25,10 @@ function DashBoard() { data && dispatchDashboard(fetchCoconutActionCreator(data)); }; - if (!getCookie('jwt')) history.replace('../signin'); + if (!getCookie('jwt')) { + localStorage.setItem('redirectURL', window.location.href); + history.replace('../signin'); + } useEffect(handleRequestGetCoconutAPI, [user]); useEffect(handleSetDashBoardState, [data]); diff --git a/cocode/src/pages/Live/index.js b/cocode/src/pages/Live/index.js index cfd7d35b..f5d490da 100644 --- a/cocode/src/pages/Live/index.js +++ b/cocode/src/pages/Live/index.js @@ -51,7 +51,10 @@ function Live() { ); const [project, dispatchProject] = useReducer(ProjectReducer, {}); - if (!getCookie('jwt')) history.replace('../signin'); + if (!getCookie('jwt')) { + localStorage.setItem('redirectURL', window.location.href); + history.replace('../signin'); + } const handleFetchProject = () => { const getProjectInfoAPI = getProjectInfoAPICreator(projectId); From eb8255cbfe1924f0904dac0ce9c64bfeaf95a723 Mon Sep 17 00:00:00 2001 From: hzoou Date: Fri, 20 Dec 2019 03:23:25 +0900 Subject: [PATCH 45/54] =?UTF-8?q?feat:=20#303=20=EC=9B=90=EB=9E=98=20url?= =?UTF-8?q?=EB=A1=9C=20=EB=A6=AC=EB=8B=A4=EC=9D=B4=EB=A0=89=ED=8A=B8=20?= =?UTF-8?q?=EC=8B=9C=EC=BC=9C=EC=A3=BC=EB=8A=94=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 로그인 성공 시 로컬 스토리지에 저장되어있던 원래 url로 리다이렉트 시켜주는 페이지를 구현했습니다. --- cocode/src/App.js | 3 ++- cocode/src/pages/Empty/index.js | 18 ++++++++++++++++++ cocode/src/pages/index.js | 3 ++- 3 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 cocode/src/pages/Empty/index.js diff --git a/cocode/src/App.js b/cocode/src/App.js index 97725652..6bad68dc 100644 --- a/cocode/src/App.js +++ b/cocode/src/App.js @@ -9,7 +9,7 @@ import { getUserAPICreator } from 'apis/User'; import { LiveStore } from 'stores'; import GlobalStyle from 'components/Common/GlobalStyle'; -import { Home, DashBoard, Project, NotFound, Live, SignIn } from 'pages'; +import { Home, DashBoard, Project, NotFound, Live, SignIn, Empty } from 'pages'; function App() { const [user, setUser] = useState(null); @@ -34,6 +34,7 @@ function App() { + diff --git a/cocode/src/pages/Empty/index.js b/cocode/src/pages/Empty/index.js new file mode 100644 index 00000000..4aa19349 --- /dev/null +++ b/cocode/src/pages/Empty/index.js @@ -0,0 +1,18 @@ +import React, { useEffect } from 'react'; +import Header from 'containers/Common/Header'; + +function Empty() { + useEffect(() => { + const redirectURL = localStorage.getItem('redirectURL'); + if (redirectURL) { + window.location.href = redirectURL; + localStorage.removeItem('redirectURL'); + } + }, []); + + return ( +
+ ); +} + +export default Empty; \ No newline at end of file diff --git a/cocode/src/pages/index.js b/cocode/src/pages/index.js index 588ac988..ea53e9b0 100644 --- a/cocode/src/pages/index.js +++ b/cocode/src/pages/index.js @@ -4,5 +4,6 @@ import Project from './Project'; import NotFound from './NotFound'; import Live from './Live'; import SignIn from './SignIn'; +import Empty from './Empty'; -export { Home, DashBoard, Project, NotFound, Live, SignIn }; +export { Home, DashBoard, Project, NotFound, Live, SignIn, Empty }; From 9772b0889f853ed2ba14cb0c65b74d43ed46951d Mon Sep 17 00:00:00 2001 From: YukJiSoo Date: Fri, 20 Dec 2019 04:08:20 +0900 Subject: [PATCH 46/54] =?UTF-8?q?chore:=20favicon=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cocode/public/favicon-16x16.png | Bin 0 -> 1313 bytes cocode/public/index.html | 6 ++++++ 2 files changed, 6 insertions(+) create mode 100644 cocode/public/favicon-16x16.png diff --git a/cocode/public/favicon-16x16.png b/cocode/public/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..38f6a74a4d9d10d6ffe2f67e4c912e80a513b303 GIT binary patch literal 1313 zcmZ`%dsvfI7(d&f6eO?D+cW}w--=AuA(|dfwEDsg=~ZxEA1^Qw&0Vp#32}J=v;mW9Cs;CEJP@0B|-&T z5qb!rg4+n;G=xSa2r;V>TB5Hx9l0JMa#n;OItU6>0+vR=)`>7RKd>Y%K}=sM#e7x# z0KJHFs5Ga3d!AN^mB?aOAmoKm?#9q^tyCSxE|f%vdMwolFu*>!w8FWgAOeOViZ@@7 z&JTcyA&L)2X@nR^35PL{%83<5db(ur{N-#PB`-i7#?lEfaP>m=(3S3yq3g0BKd3B~ z$4{H4#!U|(3vNw;u^>6xY_)=2m8GmL$pfQ8Co7c3fw4=I3KII9msOh;IuQqKwSWad z$Xcz|J0pJ-D-u8gV!gqefQ=W&Kx_uB|cmc z#L$Ox6(Rn~*t!h(7wO5r-5N7lh{?akD-0?Hm!S*i0;>mi7Poa>>FB-M+THv6?Xj5$ z)7#ZjWiV4KE+1}HCd0_D3m4T7*$@Z30a?GbfCofA+%1sncjE(+ndT{;5 zo&L^?%3wd>Fh5aTs!E-{fA8+ZWM}{Nu7SZRGtt_7GL7R4ROH5q&i4%T4Gr(g&diCA zD3T@QCqg7&k z%9@6;nOjGENuX@UF8xU2D%xA9jOx*ZB7QvYxemwir&*y$QFT_RRD55p; zXxsh^?c%8D7~_VGWE30M8lP}+Q({s_GA>CWJEO&^(pK4}&dc&Ng);rw3^GzJR%^66 zedgxpvYt0MlV7mukauBAh-Yk literal 0 HcmV?d00001 diff --git a/cocode/public/index.html b/cocode/public/index.html index e0ab6b04..7d41b699 100644 --- a/cocode/public/index.html +++ b/cocode/public/index.html @@ -2,6 +2,12 @@ cocode! - the SaaS IDE + Date: Fri, 20 Dec 2019 04:14:18 +0900 Subject: [PATCH 47/54] =?UTF-8?q?chore:=20build=20=EC=A7=84=ED=96=89=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit coconut에서 보여주는 것으로 변경에 따른 loading page를 제거했습니다. --- .../src/components/Project/BrowserV2/index.js | 22 ------------------- cocode/src/containers/Project/Editor/index.js | 2 +- 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/cocode/src/components/Project/BrowserV2/index.js b/cocode/src/components/Project/BrowserV2/index.js index bda99c4e..b47aa76b 100644 --- a/cocode/src/components/Project/BrowserV2/index.js +++ b/cocode/src/components/Project/BrowserV2/index.js @@ -9,7 +9,6 @@ import { useParams } from 'react-router-dom'; import * as Styled from './style'; import addToast from 'components/Common/Toast'; -import CoconutSpinner from 'components/Common/CoconutSpinner'; import { ProjectContext } from 'contexts'; @@ -38,24 +37,10 @@ function BrowserV2({ ...props }) { false ); const [dependency, setDependency] = useState(undefined); - const [isBuildingCoconut, setIsBuildingCoconut] = useState(true); const iframeReference = useRef(); const { files, root, dependencyInstalling } = project; - const handleComponentDidMount = () => { - window.addEventListener('message', receiveMsgFromChild); - }; - - const receiveMsgFromChild = e => { - const { command, dependency } = e.data; - - const cocodeActions = { buildEnd }; - cocodeActions[command] && cocodeActions[command](dependency); - }; - - const buildEnd = () => setIsBuildingCoconut(false); - const endInstallDependency = useCallback(dependency => { setTimeout(() => { const installDependencyAction = installDependencyActionCreator({ @@ -127,7 +112,6 @@ function BrowserV2({ ...props }) { iframeReference.current.contentWindow.postMessage(data, '*'); }, [project]); - useEffect(handleComponentDidMount, []); useEffect(handleUpdateDependency, [dependencyInstalling]); useEffect(handleUpdateFile, [files]); @@ -136,12 +120,6 @@ function BrowserV2({ ...props }) { return ( - {isBuildingCoconut && ( - - -

Please wait to build complete...

-
- )} Date: Fri, 20 Dec 2019 04:50:09 +0900 Subject: [PATCH 48/54] =?UTF-8?q?feat:=20#307=20=EB=B8=8C=EB=9D=BC?= =?UTF-8?q?=EC=9A=B0=EC=A0=80=EC=97=90=20=EC=A3=BC=EC=86=8C=20=ED=91=9C?= =?UTF-8?q?=EC=8B=9C=EC=A4=84=20=EB=85=B8=EC=B6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 프로젝트 페이지에서 빌드 결과를 나타내는 브라우저에 주소 표시줄을 노출했습니다. --- .../src/components/Project/BrowserV2/index.js | 26 ++++++- .../components/Project/BrowserV2/search.svg | 4 ++ .../src/components/Project/BrowserV2/style.js | 68 ++++++++++++------- cocode/src/constants/theme.js | 8 +-- 4 files changed, 75 insertions(+), 31 deletions(-) create mode 100644 cocode/src/components/Project/BrowserV2/search.svg diff --git a/cocode/src/components/Project/BrowserV2/index.js b/cocode/src/components/Project/BrowserV2/index.js index bda99c4e..6d3ec031 100644 --- a/cocode/src/components/Project/BrowserV2/index.js +++ b/cocode/src/components/Project/BrowserV2/index.js @@ -8,6 +8,7 @@ import React, { import { useParams } from 'react-router-dom'; import * as Styled from './style'; +import search from './search.svg'; import addToast from 'components/Common/Toast'; import CoconutSpinner from 'components/Common/CoconutSpinner'; @@ -24,14 +25,18 @@ import getUpdatedPackageJSON from 'pages/Project/getUpdatedPackageJSON'; import { COCONUT_SERVER } from 'config'; import * as NOTIFICATION from 'constants/notificationMessage'; +import { KEY_CODE_ENTER } from 'constants/keyCode'; // Constants const MIN_WAIT_TIME = 1500; const UPDATE_PROJECT = 'updateProject'; +const PROTOCOL = 'http://'; function BrowserV2({ ...props }) { const { projectId } = useParams(); + const DEFAULT_URL = `${COCONUT_SERVER}/${projectId}`; + const { project, dispatchProject } = useContext(ProjectContext); const [{ data, error }, setRequest] = useFetch({}); const [isReadyToReceiveMessage, setIsReadyToReceiveMessage] = useState( @@ -39,7 +44,9 @@ function BrowserV2({ ...props }) { ); const [dependency, setDependency] = useState(undefined); const [isBuildingCoconut, setIsBuildingCoconut] = useState(true); + const [addressInputURL, setAddressInput] = useState(DEFAULT_URL); const iframeReference = useRef(); + const addressReference = useRef(); const { files, root, dependencyInstalling } = project; @@ -115,6 +122,14 @@ function BrowserV2({ ...props }) { addToast.error(NOTIFICATION.FAIL_INSTALL_DEPENDENCY); }; + const handleAddressInputKeyDown = ({ keyCode, target: { value } }) => { + if (keyCode === KEY_CODE_ENTER) { + const address = value.includes(PROTOCOL) ? value : `${PROTOCOL}${value}`; + setAddressInput(address); + addressReference.current.value = address; + } + }; + const handleIframeOnLoad = useCallback(() => { setIsReadyToReceiveMessage(true); @@ -136,6 +151,15 @@ function BrowserV2({ ...props }) { return ( + + + + {isBuildingCoconut && ( @@ -144,7 +168,7 @@ function BrowserV2({ ...props }) { )} diff --git a/cocode/src/components/Project/BrowserV2/search.svg b/cocode/src/components/Project/BrowserV2/search.svg new file mode 100644 index 00000000..a1739b09 --- /dev/null +++ b/cocode/src/components/Project/BrowserV2/search.svg @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/cocode/src/components/Project/BrowserV2/style.js b/cocode/src/components/Project/BrowserV2/style.js index 5c57a943..4b547453 100644 --- a/cocode/src/components/Project/BrowserV2/style.js +++ b/cocode/src/components/Project/BrowserV2/style.js @@ -1,37 +1,17 @@ import styled from 'styled-components'; +import { BROWSER_THEME } from 'constants/theme'; const Frame = styled.div` & { position: relative; } `; -const ErrorDisplay = styled.div` - & { - position: absolute; - z-index: ${({ errorDescription }) => (errorDescription ? 1 : -1)}; - overflow-x: scroll; - - padding: 1rem; - - height: 100%; - width: 100%; - - background-color: ${({ errorDescription }) => - errorDescription ? 'rgba(0, 0, 0, 0.7)' : 'transparent'}; - - font-size: 2rem; - font-weight: lighter; - } -`; const BrowserV2 = styled.iframe` & { - position: absolute; - - height: 100%; + height: calc(100% - 3.1rem); width: 100%; - - background-color: white; + background-color: ${BROWSER_THEME.iframeBGColor}; } `; @@ -50,15 +30,51 @@ const LoadingOverlay = styled.section` justify-content: center; align-items: center; - background-color: black; + background-color: ${BROWSER_THEME.loadingOverlayBGColor}; p { margin-top: 2rem; - font-size: 3rem; font-weight: lighter; } } `; -export { Frame, ErrorDisplay, BrowserV2, LoadingOverlay }; +const AddressContainer = styled.div` + & { + display: flex; + align-items: center; + height: 3.1rem; + width: 100%; + padding: 0.8rem; + background: ${BROWSER_THEME.browserHeaderBGColor}; + font-size: 1rem; + } +`; + +const AddressInput = styled.input` + & { + width: 100%; + height: 100%; + padding: 0.3rem; + background: ${BROWSER_THEME.addressInputBGColor}; + color: ${BROWSER_THEME.addressInputTextColor}; + } +`; + +const SearchIcon = styled.img` + & { + height: 100%; + padding: 0.4rem 0; + background: ${BROWSER_THEME.addressInputBGColor}; + } +`; + +export { + Frame, + BrowserV2, + LoadingOverlay, + AddressContainer, + AddressInput, + SearchIcon +}; diff --git a/cocode/src/constants/theme.js b/cocode/src/constants/theme.js index 6c0ff157..04a17bec 100644 --- a/cocode/src/constants/theme.js +++ b/cocode/src/constants/theme.js @@ -17,11 +17,11 @@ const DROPDOWN_THEME = { }; const BROWSER_THEME = { + iframeBGColor: '#ffffff', + loadingOverlayBGColor: '#000000', browserHeaderBGColor: '#1d2022', - addressInputBGColor: '#000', - addressInputTextColor: '#fff', - - browserHeight: '88vh' + addressInputBGColor: '#000000', + addressInputTextColor: '#ffffff', }; const TAB_CONTAINER_THEME = { From 961293b3cd56859d3dfccb90d8042eedb5a440c6 Mon Sep 17 00:00:00 2001 From: YukJiSoo Date: Fri, 20 Dec 2019 05:07:02 +0900 Subject: [PATCH 49/54] =?UTF-8?q?chore:=20env=20file=20=EA=B0=B1=EC=8B=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit env file을 최신화하였습니다. --- .travis.yml | 2 +- env.tar.enc | Bin 4624 -> 5136 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 17a41682..65a02d2c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,7 +34,7 @@ jobs: - stage: Deploy if: branch = master AND type = pull_request before_install: - - openssl aes-256-cbc -K $encrypted_278d2d9eb060_key -iv $encrypted_278d2d9eb060_iv -in env.tar.enc -out env.tar -d + - openssl aes-256-cbc -K $encrypted_0395e9fbd9ff_key -iv $encrypted_0395e9fbd9ff_iv -in env.tar.enc -out env.tar -d - tar xvf env.tar install: - sudo apt-get install sshpass diff --git a/env.tar.enc b/env.tar.enc index 59356ecb4008aacd49c489b50f3ffe75f562ea5f..967fc15b62827935363ed30d59bec87e045569f5 100644 GIT binary patch literal 5136 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 Date: Fri, 20 Dec 2019 05:13:42 +0900 Subject: [PATCH 50/54] =?UTF-8?q?fix:=20=ED=8C=8C=EC=9D=BC=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20=EB=B3=80=EA=B2=BD=20=EC=8B=9C=20=EA=B3=B5=EB=B0=B1?= =?UTF-8?q?=EC=9D=B4=EB=82=98=20=EB=B9=88=20=EB=AC=B8=EC=9E=90=EC=9D=B8=20?= =?UTF-8?q?=EA=B2=BD=EC=9A=B0=20=EC=97=90=EB=9F=AC=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 파일이름 변경시 빈공백이나 스페이스바만 입력한 경우 에러처리를 하도록 해주었습니다. --- cocode/src/components/Project/File/index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cocode/src/components/Project/File/index.js b/cocode/src/components/Project/File/index.js index 58cdb1aa..fa2f5f47 100644 --- a/cocode/src/components/Project/File/index.js +++ b/cocode/src/components/Project/File/index.js @@ -34,6 +34,8 @@ const API_NOTIFICATION = { }; function isNotChangeableFileName({ files, changedName, parentId }) { + if (!changedName.trim().length) return true; + const childrenOfParent = files[parentId].child; return childrenOfParent From cc6642e8744fb1daaa5ae44b6b2104cd33016f1d Mon Sep 17 00:00:00 2001 From: hzoou Date: Fri, 20 Dec 2019 05:44:51 +0900 Subject: [PATCH 51/54] =?UTF-8?q?chore:=20=EC=98=A4=ED=83=88=EC=9E=90=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 오탈자를 수정했습니다. --- cocode/src/containers/Live/ExplorerTab/style.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cocode/src/containers/Live/ExplorerTab/style.js b/cocode/src/containers/Live/ExplorerTab/style.js index e9937060..4c6f6aa1 100644 --- a/cocode/src/containers/Live/ExplorerTab/style.js +++ b/cocode/src/containers/Live/ExplorerTab/style.js @@ -6,13 +6,14 @@ import { const { explorerTabContainerSelectedFileBGColor, -} = 'EXPLORER_TAB_CONTAINER_THEME'; +} = EXPLORER_TAB_CONTAINER_THEME; + const { tabContainerHeaderBGColor, tabContainerTitleColor, tabContainerTitleSize, tabContainerTitleWeight -} = 'TAB_CONTAINER_THEME'; +} = TAB_CONTAINER_THEME; const TabBody = styled.div` & { From 7b1f288c6ede29ff515678417ac685586dde9d8b Mon Sep 17 00:00:00 2001 From: hzoou Date: Fri, 20 Dec 2019 05:53:36 +0900 Subject: [PATCH 52/54] =?UTF-8?q?fix:=20=ED=94=84=EB=A1=9C=EC=A0=9D?= =?UTF-8?q?=ED=8A=B8=20=ED=8F=AC=ED=81=AC=20=EC=8B=9C=20=EB=B8=8C=EB=9D=BC?= =?UTF-8?q?=EC=9A=B0=EC=A0=80=20=EC=A3=BC=EC=86=8C=EC=B0=BD=20=EA=B0=B1?= =?UTF-8?q?=EC=8B=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 프로젝트를 포크한 경우 브라우저의 주소창은 갱신되지 않았던 버그를 수정하였습니다. --- cocode/src/components/Project/BrowserV2/index.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cocode/src/components/Project/BrowserV2/index.js b/cocode/src/components/Project/BrowserV2/index.js index 04a2c1e2..fee5f918 100644 --- a/cocode/src/components/Project/BrowserV2/index.js +++ b/cocode/src/components/Project/BrowserV2/index.js @@ -116,6 +116,12 @@ function BrowserV2({ ...props }) { } }; + const handleChangeCurrentURL = () => { + const address = `${COCONUT_SERVER}/${projectId}`; + setAddressInput(address); + addressReference.current.value = address; + }; + const handleIframeOnLoad = useCallback(() => { setIsReadyToReceiveMessage(true); @@ -128,6 +134,7 @@ function BrowserV2({ ...props }) { iframeReference.current.contentWindow.postMessage(data, '*'); }, [project]); + useEffect(handleChangeCurrentURL, [projectId]); useEffect(handleUpdateDependency, [dependencyInstalling]); useEffect(handleUpdateFile, [files]); From 502867bc02ecaf938f663a72cc2f7eab86b667f9 Mon Sep 17 00:00:00 2001 From: hzoou Date: Fri, 20 Dec 2019 06:02:32 +0900 Subject: [PATCH 53/54] =?UTF-8?q?fix:=20https=EC=9D=B4=20=ED=8F=AC?= =?UTF-8?q?=ED=95=A8=EB=90=9C=20=EC=A3=BC=EC=86=8C=EB=A5=BC=20=EC=9E=85?= =?UTF-8?q?=EB=A0=A5=ED=95=98=EB=8A=94=20=EA=B2=BD=EC=9A=B0=EC=97=90=20?= =?UTF-8?q?=EB=8C=80=ED=95=9C=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https 프로토콜이 적용된 url에 앞에 http를 프로토콜을 붙이는 등 올바르게 작동 하지 않았던 버그를 해결했습니다. --- cocode/src/components/Project/BrowserV2/index.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cocode/src/components/Project/BrowserV2/index.js b/cocode/src/components/Project/BrowserV2/index.js index fee5f918..e7c8a6b7 100644 --- a/cocode/src/components/Project/BrowserV2/index.js +++ b/cocode/src/components/Project/BrowserV2/index.js @@ -29,7 +29,7 @@ import { KEY_CODE_ENTER } from 'constants/keyCode'; // Constants const MIN_WAIT_TIME = 1500; const UPDATE_PROJECT = 'updateProject'; -const PROTOCOL = 'http://'; +const PROTOCOLS = ['http://', 'https://']; function BrowserV2({ ...props }) { const { projectId } = useParams(); @@ -59,6 +59,9 @@ function BrowserV2({ ...props }) { }, MIN_WAIT_TIME); }); + const isHaveProtocol = (value) => + PROTOCOLS.some(PROTOCOL => value.includes(PROTOCOL)); + const handleUpdateDependency = () => { if (!isReadyToReceiveMessage) return; if (!dependencyInstalling) return; @@ -110,7 +113,7 @@ function BrowserV2({ ...props }) { const handleAddressInputKeyDown = ({ keyCode, target: { value } }) => { if (keyCode === KEY_CODE_ENTER) { - const address = value.includes(PROTOCOL) ? value : `${PROTOCOL}${value}`; + const address = isHaveProtocol(value) ? value : `${PROTOCOLS[0]}${value}`; setAddressInput(address); addressReference.current.value = address; } From 1305c15aaa21c88ea788ef21c07ebd006647b494 Mon Sep 17 00:00:00 2001 From: YukJiSoo Date: Fri, 20 Dec 2019 06:53:41 +0900 Subject: [PATCH 54/54] =?UTF-8?q?chore:=20env=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=9E=AC=20=EC=95=94=ED=98=B8=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 배포 시 알 수 없는 복호화 오류로 인하여 암호화 과정을 다시 진행했습니다. --- env.tar.enc | Bin 5136 -> 5136 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/env.tar.enc b/env.tar.enc index 967fc15b62827935363ed30d59bec87e045569f5..941672d85e53624df7bbf7138584cf5b21b8d970 100644 GIT binary patch literal 5136 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 yl@QsChm0oU1?r<5*QEhP150U literal 5136 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