From 965c1d17e9c45f5e056595b199d484057454f81c Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Mon, 25 Mar 2024 02:19:58 +0900 Subject: [PATCH 01/81] =?UTF-8?q?Docs=20:=20figma=EB=A5=BC=20=EB=B3=B4?= =?UTF-8?q?=EA=B3=A0=20=EA=B5=AC=ED=98=84=20=EA=B8=B0=EB=8A=A5=EC=97=90=20?= =?UTF-8?q?=EB=8C=80=ED=95=9C=20=ED=8C=8C=EC=95=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + make.md | 39 +++++++++++++++++++++++++++++++++++++++ package.json | 3 ++- 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 make.md diff --git a/.gitignore b/.gitignore index 4d29575..448863b 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ npm-debug.log* yarn-debug.log* yarn-error.log* + diff --git a/make.md b/make.md new file mode 100644 index 0000000..7836262 --- /dev/null +++ b/make.md @@ -0,0 +1,39 @@ +# 구현을 해야할 기능 +- 사용자가 input에 focus를 하면 전체의 너비가 줄어들어야 함 +- 기본적으로 모바일 뷰를 기준으로 하기에 전체 너비를 375px, 높이를 812로 제한하고 시작하면 됨 +- 기존의 사용자를 토글하면 사용자가 바뀌고 채팅이 뒤바뀌어서 보여야 함. +- 기존의 메시지가 많아지면 자연스럽게 스크롤이 되어야 함 +- 디자인 시스템: 자주 사용되는 디자인 속성을 변수와 같은 시스템으로 구현해야 함 +- 더블 클릭하면 하트 감정 표시가 보여질 수 있어야 함 => 나중에 디자이너 분께서 단순 하트가 아닌 따봉, 웃는 사람의 얼굴 등을 보여주는 기능을 요구할 수도 있음 +- 추가적으로 본인의 메시지에는 더블 클릭을 하더라도 하트가 적용이 되면 안됨 -> 더블 클릭을 어떻게 인지할 것인가? 애초에 dom 속성 중에서 `domDoubleClick` 속성이 존재함 +- 하트를 구현해야 함 -> 하트가 붙어야 하는지 아닌지를 boolean 타입의 상태로 선언해(typescript), 조건부 렌더링을 진행 => 꼭 상태로 선언할 필요는 없음 어차피 localStorage, json 파일을 모두 사용할 것이므로 그냥 객체의 속성에 boolean 필드를 삽입하는 방식으 택하기 +- Typescript 적용 +- 사용자가 아직 아무것도 입력하지 않았을 경우 메시지 전송 버튼은 비활성화 되어 있어야 함 --> **상태**를 통해 UI를 다르게 렌더링 해주어야 함 +- 하단 input에서 + 버튼을 누르면 + 가 이미지, 음성 이미지로 변환될 필요가 있음 input 박스 영역은 `flex - grow : 1` 속성을 부여하여 남는 부분을 모두 차지하도록 함 +- 사용자가 제시한 날짜에 따라서 날짜 표시가 있고, 그 다음에 메시지들이 진행되어야 함. +- 카카오톡, 라인과 다르게 왼쪽 오른쪽으로 상대방과의 메시지가 엇갈려서 나타나는 것이 아니라, 왼쪽에 항상 몰려있는 구조임. 메시지의 길이에 따라서 메시지 박스 높이가 조절이 되도록 `height: 속성은 auto` 를 부여하는 것이 좋음 + + +### 나누면 좋을만한 파일 +- 기본적으로 styled-components 모듈을 사용할 것이기 때문에 정민님꼐서 Iphone, Images, TabBar, Gloabl, FriendPage, ProfilePage, ChatroomPage 등으로 나눠놓은 컴포넌트를 따로 파일로 분리하고 export하면 이를 import하여 객체 지향형 느낌으로 사용 + +### 디자인 시스템 +- 자주 사용되는 색상들을 변수로 활용하고 여기에는 동일한 색상이더라도 opacity의 개념이 도입될 수도 있음 + + + +### 컴포넌트 분리의 기준 +- 일단 figma에서 제시한 컴포넌트들을 최대한 복사 + 붙여넣기 식으로 활용하고, 추후에 활용을 할 떄 제대로 적용이 안된 것들은 다시 `styled()` 함수를 이용하여 다시 스타일링 진행 +- 그냥 단순 이미지를 렌더링해주는 컴포넌트를 imageComponent 라는 디렉터리에 한 파일로 그냥 싸악 적용해주고 끝낸다. 나중에 이를 활용하는 헤더 등의 컴포넌트에서 이들을 받아서 사용한다 +- 매 화면에 고정적으로 등장하는 iphone Status Bar(시간, 와이파이, 배터리를 가지고 있는 헤더)와 homeIndicator는 따로 fixed 컴포넌트로 만들어두고 매번 활용하는 구조. 가장 위에는 `iphoneStatusBar`, 가장 아래에는 `homeIndicator(z-index를 높게 설정)`가 위치하는 구조 + + +### 정민님 figma 기본 구조 +- 기본적으로 항상 많이 쓰이는 것들은 fixed, 유동적으로 구성되는 요소들은 scroll이라고 표현하심 +- input box에 포커싱이 되면 위의 고정 헤더들은 가만히 있고, 나머지 전체 요소들의 높이가 자연스럽게 줄어들어야 한다 + + +### 사용자 시나리오 +1. 전체 탭 바를 통해 이동할 수 있는 구간은 3군데임 : 친구 & 메시지 & 내 프로필 +2. 친구와의 메시지 탭을 누르면 바로 메시지 url로 넘어감 : `message/{친구명}`의 주소로 처리됨 +3. <- arrow를 통해서 이전의 `/message` 주소로 리디렉션됨. diff --git a/package.json b/package.json index ea335d3..a0adc25 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,8 @@ "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", - "eject": "react-scripts eject" + "eject": "react-scripts eject", + "push": "git push origin Programming-Seungwan:Programming-Seungwan" }, "eslintConfig": { "extends": [ From a2f0ef0276cb131265258b9a35bf1e76cffeb618 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Tue, 26 Mar 2024 00:49:53 +0900 Subject: [PATCH 02/81] =?UTF-8?q?Feat=20:=20styled-components=EB=A5=BC=20?= =?UTF-8?q?=ED=86=B5=ED=95=B4=20figma=EC=9D=98=20=EB=94=94=EC=9E=90?= =?UTF-8?q?=EC=9D=B8=20=EC=8B=9C=EC=8A=A4=ED=85=9C=20=EA=B5=AC=EC=B6=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. ThemeProvider 컴포넌트 이용 --- .prettierrc | 6 +++ make.md | 7 ++- package-lock.json | 124 ++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + src/App.tsx | 15 ++++-- src/styles/theme.js | 35 +++++++++++++ 6 files changed, 181 insertions(+), 7 deletions(-) create mode 100644 .prettierrc create mode 100644 src/styles/theme.js diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..0a72520 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "trailingComma": "es5", + "tabWidth": 2, + "semi": true, + "singleQuote": true +} diff --git a/make.md b/make.md index 7836262..c33359d 100644 --- a/make.md +++ b/make.md @@ -1,8 +1,8 @@ # 구현을 해야할 기능 - 사용자가 input에 focus를 하면 전체의 너비가 줄어들어야 함 - 기본적으로 모바일 뷰를 기준으로 하기에 전체 너비를 375px, 높이를 812로 제한하고 시작하면 됨 -- 기존의 사용자를 토글하면 사용자가 바뀌고 채팅이 뒤바뀌어서 보여야 함. -- 기존의 메시지가 많아지면 자연스럽게 스크롤이 되어야 함 +- 기존의 사용자를 토글하면 사용자가 바뀌고 채팅이 뒤바뀌어서 보여야 함. => me와 other 속성 2개를 선언함 +- 기존의 메시지가 많아지면 자연스럽게 스크롤이 되어야 함 => `overflow : scroll` 속성을 통해서 해결 - 디자인 시스템: 자주 사용되는 디자인 속성을 변수와 같은 시스템으로 구현해야 함 - 더블 클릭하면 하트 감정 표시가 보여질 수 있어야 함 => 나중에 디자이너 분께서 단순 하트가 아닌 따봉, 웃는 사람의 얼굴 등을 보여주는 기능을 요구할 수도 있음 - 추가적으로 본인의 메시지에는 더블 클릭을 하더라도 하트가 적용이 되면 안됨 -> 더블 클릭을 어떻게 인지할 것인가? 애초에 dom 속성 중에서 `domDoubleClick` 속성이 존재함 @@ -12,6 +12,7 @@ - 하단 input에서 + 버튼을 누르면 + 가 이미지, 음성 이미지로 변환될 필요가 있음 input 박스 영역은 `flex - grow : 1` 속성을 부여하여 남는 부분을 모두 차지하도록 함 - 사용자가 제시한 날짜에 따라서 날짜 표시가 있고, 그 다음에 메시지들이 진행되어야 함. - 카카오톡, 라인과 다르게 왼쪽 오른쪽으로 상대방과의 메시지가 엇갈려서 나타나는 것이 아니라, 왼쪽에 항상 몰려있는 구조임. 메시지의 길이에 따라서 메시지 박스 높이가 조절이 되도록 `height: 속성은 auto` 를 부여하는 것이 좋음 +- homeIndicator는 채팅 입력 박스가 올라오더라도 아래에 고정되어 있어야 함 ### 나누면 좋을만한 파일 @@ -19,6 +20,7 @@ ### 디자인 시스템 - 자주 사용되는 색상들을 변수로 활용하고 여기에는 동일한 색상이더라도 opacity의 개념이 도입될 수도 있음 +- styled-components나, styled-system 모듈은 디자인 시스템을 구축할 수 있게 도와준다 using ThemeProvider나 후자의 utility first 클래스명을 이용! @@ -26,6 +28,7 @@ - 일단 figma에서 제시한 컴포넌트들을 최대한 복사 + 붙여넣기 식으로 활용하고, 추후에 활용을 할 떄 제대로 적용이 안된 것들은 다시 `styled()` 함수를 이용하여 다시 스타일링 진행 - 그냥 단순 이미지를 렌더링해주는 컴포넌트를 imageComponent 라는 디렉터리에 한 파일로 그냥 싸악 적용해주고 끝낸다. 나중에 이를 활용하는 헤더 등의 컴포넌트에서 이들을 받아서 사용한다 - 매 화면에 고정적으로 등장하는 iphone Status Bar(시간, 와이파이, 배터리를 가지고 있는 헤더)와 homeIndicator는 따로 fixed 컴포넌트로 만들어두고 매번 활용하는 구조. 가장 위에는 `iphoneStatusBar`, 가장 아래에는 `homeIndicator(z-index를 높게 설정)`가 위치하는 구조 +- 해당 컴포넌트가 상황에 따라서 위치나 크기 등이 가변적인지 아닌지에 따라서 `header`, `AppMain`, `footer`로 나뉘어야 함 ### 정민님 figma 기본 구조 diff --git a/package-lock.json b/package-lock.json index c27bbe4..b518622 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-scripts": "5.0.1", + "styled-components": "^6.1.8", "typescript": "^4.9.5", "web-vitals": "^2.1.4" } @@ -2288,6 +2289,24 @@ "postcss-selector-parser": "^6.0.10" } }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz", + "integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==", + "dependencies": { + "@emotion/memoize": "^0.8.1" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + }, + "node_modules/@emotion/unitless": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.0.tgz", + "integrity": "sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw==" + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -4290,6 +4309,11 @@ "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==" }, + "node_modules/@types/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-n4sx2bqL0mW1tvDf/loQ+aMX7GQD3lc3fkCMC55VFNDu/vBOabO+LTIeXKM14xK0ppk5TUGcWRjiSpIlUpghKw==" + }, "node_modules/@types/testing-library__jest-dom": { "version": "5.14.9", "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.9.tgz", @@ -5750,6 +5774,14 @@ "node": ">= 6" } }, + "node_modules/camelize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/caniuse-api": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", @@ -6182,6 +6214,14 @@ "postcss": "^8.4" } }, + "node_modules/css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", + "engines": { + "node": ">=4" + } + }, "node_modules/css-declaration-sorter": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz", @@ -6372,6 +6412,16 @@ "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" }, + "node_modules/css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "dependencies": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, "node_modules/css-tree": { "version": "1.0.0-alpha.37", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", @@ -15719,6 +15769,11 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -16260,6 +16315,70 @@ "webpack": "^5.0.0" } }, + "node_modules/styled-components": { + "version": "6.1.8", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.8.tgz", + "integrity": "sha512-PQ6Dn+QxlWyEGCKDS71NGsXoVLKfE1c3vApkvDYS5KAK+V8fNWGhbSUEo9Gg2iaID2tjLXegEW3bZDUGpofRWw==", + "dependencies": { + "@emotion/is-prop-valid": "1.2.1", + "@emotion/unitless": "0.8.0", + "@types/stylis": "4.2.0", + "css-to-react-native": "3.2.0", + "csstype": "3.1.2", + "postcss": "8.4.31", + "shallowequal": "1.1.0", + "stylis": "4.3.1", + "tslib": "2.5.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/styled-components" + }, + "peerDependencies": { + "react": ">= 16.8.0", + "react-dom": ">= 16.8.0" + } + }, + "node_modules/styled-components/node_modules/csstype": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" + }, + "node_modules/styled-components/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/styled-components/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + }, "node_modules/stylehacks": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", @@ -16275,6 +16394,11 @@ "postcss": "^8.2.15" } }, + "node_modules/stylis": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.1.tgz", + "integrity": "sha512-EQepAV+wMsIaGVGX1RECzgrcqRRU/0sYOHkeLsZ3fzHaHXZy4DaOOX0vOlGQdlsjkh3mFHAIlVimpwAs4dslyQ==" + }, "node_modules/sucrase": { "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", diff --git a/package.json b/package.json index a0adc25..84dcd32 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-scripts": "5.0.1", + "styled-components": "^6.1.8", "typescript": "^4.9.5", "web-vitals": "^2.1.4" }, diff --git a/src/App.tsx b/src/App.tsx index bd79c18..8955a25 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,9 +1,14 @@ +import { ThemeProvider } from 'styled-components'; +import theme from './styles/theme'; + function App() { - return ( -
-

19기 프론트엔드 파이팅!!! 디자인과 사이좋게 지내요~~~

-
- ); + return ( + +
+

19기 프론트엔드 파이팅!!! 디자인과 사이좋게 지내요~~~

+
+
+ ); } export default App; diff --git a/src/styles/theme.js b/src/styles/theme.js new file mode 100644 index 0000000..3b8678b --- /dev/null +++ b/src/styles/theme.js @@ -0,0 +1,35 @@ +const theme = { + color: { + key: '#5865F2', + white: '#FFFFFF', + grayLight: '#F2F3F5', + grayMedium: '#EBEBEB', + grayDark: '#4E5058', + black: '#060607', + }, + textStyle: { + fontSize: { + h1: '32', + h2: '20', + h3: '16', + body1: '15', + body2: '15', + label: '13', + caption: '10', + }, + lineHeight: { + h1: '150', + h2: '150', + h3: '150', + body1: '150', + body2: '150', + label: '150', + caption: '150', + }, + }, + gridStyle: { + display: 'grid', + }, +}; + +export default theme; From f2e5ee27e48164b1feb14f4b2660467bcfd9b347 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Tue, 26 Mar 2024 00:58:43 +0900 Subject: [PATCH 03/81] Chore : apply reset.css MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. styled-reset 모듈을 통해 컴포넌트 방식으로 적용 --- package-lock.json | 12 ++++++++++++ package.json | 1 + public/index.html | 23 +---------------------- src/App.tsx | 14 +++++++++----- src/styles/globalStyles.jsx | 9 +++++++++ 5 files changed, 32 insertions(+), 27 deletions(-) create mode 100644 src/styles/globalStyles.jsx diff --git a/package-lock.json b/package-lock.json index b518622..e877f5d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "react-dom": "^18.2.0", "react-scripts": "5.0.1", "styled-components": "^6.1.8", + "styled-reset": "^4.5.2", "typescript": "^4.9.5", "web-vitals": "^2.1.4" } @@ -16379,6 +16380,17 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, + "node_modules/styled-reset": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/styled-reset/-/styled-reset-4.5.2.tgz", + "integrity": "sha512-dbAaaVEhweBs2FGfqGBdW6oMcMK8238C2X5KCxBhUQJX92m/QyUfzRADOXhdXiXNkIPELtMCd72YY9eCdORfIw==", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "styled-components": ">=4.0.0 || >=5.0.0 || >=6.0.0" + } + }, "node_modules/stylehacks": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", diff --git a/package.json b/package.json index 84dcd32..2c9d5cc 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "react-dom": "^18.2.0", "react-scripts": "5.0.1", "styled-components": "^6.1.8", + "styled-reset": "^4.5.2", "typescript": "^4.9.5", "web-vitals": "^2.1.4" }, diff --git a/public/index.html b/public/index.html index aa069f2..bd47f3e 100644 --- a/public/index.html +++ b/public/index.html @@ -10,34 +10,13 @@ content="Web site created using create-react-app" /> - + < - React App
- diff --git a/src/App.tsx b/src/App.tsx index 8955a25..a569a40 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,13 +1,17 @@ import { ThemeProvider } from 'styled-components'; import theme from './styles/theme'; +import GlobalStyles from './styles/globalStyles'; function App() { return ( - -
-

19기 프론트엔드 파이팅!!! 디자인과 사이좋게 지내요~~~

-
-
+ <> + + +
+

19기 프론트엔드 파이팅!!! 디자인과 사이좋게 지내요~~~

+
+
+ ); } diff --git a/src/styles/globalStyles.jsx b/src/styles/globalStyles.jsx new file mode 100644 index 0000000..9d057d6 --- /dev/null +++ b/src/styles/globalStyles.jsx @@ -0,0 +1,9 @@ +import { createGlobalStyle } from 'styled-components'; +import reset from 'styled-reset'; + +const GlobalStyles = createGlobalStyle` + ${reset} + box-sizing: border-box; +`; + +export default GlobalStyles; From 7e331b4ea037c4b5b8d74370606bb6e1e2a14ac5 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Tue, 26 Mar 2024 01:17:59 +0900 Subject: [PATCH 04/81] =?UTF-8?q?Style=20:=20import=20=EB=AC=B8=EC=97=90?= =?UTF-8?q?=20=EB=8C=80=ED=95=9C=20=EC=A0=88=EB=8C=80=20=EA=B2=BD=EB=A1=9C?= =?UTF-8?q?=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tsconfig.json | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index a273b0c..cca83f9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,11 +1,7 @@ { "compilerOptions": { "target": "es5", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], + "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, @@ -18,9 +14,17 @@ "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, - "jsx": "react-jsx" + "jsx": "react-jsx", + "baseUrl": ".", + "paths": { + "@components/*": ["./components/*"], + "@utils": ["./utils/*"], + "@styles": ["./styles/*"], + "@hooks": ["./hooks/*"], + "@pages": ["./pages/*"], + "@assets": ["./assets/*"], + "@context": ["./context/*"] + } }, - "include": [ - "src" - ] + "include": ["src"] } From ec862534e6af82c7a18738a0430fee9709dea8f6 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Tue, 26 Mar 2024 02:02:50 +0900 Subject: [PATCH 05/81] =?UTF-8?q?Fix=20:=20craco=20runner=EB=A5=BC=20?= =?UTF-8?q?=ED=86=B5=ED=95=9C=20ts=20=EC=83=81=EB=8C=80=EA=B2=BD=EB=A1=9C?= =?UTF-8?q?=20webpack=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 기존의 react-scripts 러너는 상대 경로를 설정해주더라도 알아서 이를 씹고 CRA의 기본 설정으로 경로를 인식하여 오류가 생김 2. tsconfig.paths.json 파일에 기존의 설정을 해주고 @craco/craco 모듈과 react-app-alias 모듈을 설치 3. craco.config.json 파일에 craco 관련 설정을 해줌 4. 애플리케이션 러너 환경 자체를 react-scripts가 아닌, craco 가 돌릴 수 있게 설정 --- craco.config.js | 14 + package-lock.json | 246 ++++++++++++++++++ package.json | 12 +- public/index.html | 1 - src/App.tsx | 5 +- src/components/test/Test.tsx | 7 + .../{globalStyles.jsx => globalStyles.tsx} | 0 tsconfig.json | 12 +- tsconfig.paths.json | 14 + 9 files changed, 293 insertions(+), 18 deletions(-) create mode 100644 craco.config.js create mode 100644 src/components/test/Test.tsx rename src/styles/{globalStyles.jsx => globalStyles.tsx} (100%) create mode 100644 tsconfig.paths.json diff --git a/craco.config.js b/craco.config.js new file mode 100644 index 0000000..9f9c45d --- /dev/null +++ b/craco.config.js @@ -0,0 +1,14 @@ +const { CracoAliasPlugin } = require('react-app-alias'); + +module.exports = { + plugins: [ + { + plugin: CracoAliasPlugin, + options: { + source: 'tsconfig', + baseUrl: './src', + tsConfigPath: './tsconfig.paths.json', + }, + }, + ], +}; diff --git a/package-lock.json b/package-lock.json index e877f5d..e561ae0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "react-messenger-19th", "version": "0.1.0", "dependencies": { + "@craco/craco": "^7.1.0", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", @@ -22,6 +23,9 @@ "styled-reset": "^4.5.2", "typescript": "^4.9.5", "web-vitals": "^2.1.4" + }, + "devDependencies": { + "react-app-alias": "^2.2.2" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -2020,6 +2024,49 @@ "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" }, + "node_modules/@craco/craco": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@craco/craco/-/craco-7.1.0.tgz", + "integrity": "sha512-oRAcPIKYrfPXp9rSzlsDNeOaVtDiKhoyqSXUoqiK24jCkHr4T8m/a2f74yXIzCbIheoUWDOIfWZyRgFgT+cpqA==", + "dependencies": { + "autoprefixer": "^10.4.12", + "cosmiconfig": "^7.0.1", + "cosmiconfig-typescript-loader": "^1.0.0", + "cross-spawn": "^7.0.3", + "lodash": "^4.17.21", + "semver": "^7.3.7", + "webpack-merge": "^5.8.0" + }, + "bin": { + "craco": "dist/bin/craco.js" + }, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "react-scripts": "^5.0.0" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@csstools/normalize.css": { "version": "12.1.1", "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-12.1.1.tgz", @@ -3994,6 +4041,26 @@ "node": ">=10.13.0" } }, + "node_modules/@tsconfig/node10": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.10.tgz", + "integrity": "sha512-PiaIWIoPvO6qm6t114ropMCagj6YAF24j9OkCA2mJDXFnlionEwhsBCJ8yek4aib575BI3OkART/90WsgHgLWw==" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==" + }, "node_modules/@types/aria-query": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", @@ -5937,6 +6004,19 @@ "wrap-ansi": "^7.0.0" } }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -6177,6 +6257,29 @@ "node": ">=10" } }, + "node_modules/cosmiconfig-typescript-loader": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-1.0.9.tgz", + "integrity": "sha512-tRuMRhxN4m1Y8hP9SNYfz7jRwt8lZdWxdjg/ohg5esKmsndJIn4yT96oJVcf5x0eA11taXl+sIp+ielu529k6g==", + "dependencies": { + "cosmiconfig": "^7", + "ts-node": "^10.7.0" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "peerDependencies": { + "@types/node": "*", + "cosmiconfig": ">=7", + "typescript": ">=3" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -6887,6 +6990,14 @@ "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/diff-sequences": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", @@ -8355,6 +8466,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "bin": { + "flat": "cli.js" + } + }, "node_modules/flat-cache": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", @@ -9687,6 +9806,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-potential-custom-element-name": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", @@ -9864,6 +9994,14 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", @@ -12353,6 +12491,11 @@ "semver": "bin/semver.js" } }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + }, "node_modules/makeerror": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", @@ -14747,6 +14890,12 @@ "node": ">=0.10.0" } }, + "node_modules/react-app-alias": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/react-app-alias/-/react-app-alias-2.2.2.tgz", + "integrity": "sha512-mkebUkGLEBA8A8jripu5h1e3cccGl8wWHCUmyJo43/KhaN91DO3qyCLWGWneogqkG4PWhp2JHtlCJ06YSdHVYQ==", + "dev": true + }, "node_modules/react-app-polyfill": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-3.0.0.tgz", @@ -15770,6 +15919,17 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/shallowequal": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", @@ -16898,6 +17058,61 @@ "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/acorn-walk": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ts-node/node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + }, "node_modules/tsconfig-paths": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", @@ -17265,6 +17480,11 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==" + }, "node_modules/v8-to-istanbul": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", @@ -17631,6 +17851,19 @@ "node": ">=10.13.0" } }, + "node_modules/webpack-merge": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", + "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", + "dependencies": { + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/webpack-sources": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", @@ -17811,6 +18044,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/wildcard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==" + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -18300,6 +18538,14 @@ "node": ">=10" } }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "engines": { + "node": ">=6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 2c9d5cc..949b996 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { + "@craco/craco": "^7.1.0", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", @@ -19,10 +20,10 @@ "web-vitals": "^2.1.4" }, "scripts": { - "start": "react-scripts start", - "build": "react-scripts build", - "test": "react-scripts test", - "eject": "react-scripts eject", + "start": "craco start", + "build": "craco build", + "test": "craco test", + "eject": "craco eject", "push": "git push origin Programming-Seungwan:Programming-Seungwan" }, "eslintConfig": { @@ -42,5 +43,8 @@ "last 1 firefox version", "last 1 safari version" ] + }, + "devDependencies": { + "react-app-alias": "^2.2.2" } } diff --git a/public/index.html b/public/index.html index bd47f3e..d94b676 100644 --- a/public/index.html +++ b/public/index.html @@ -10,7 +10,6 @@ content="Web site created using create-react-app" /> - < React App diff --git a/src/App.tsx b/src/App.tsx index a569a40..2d1a463 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,15 +1,14 @@ import { ThemeProvider } from 'styled-components'; import theme from './styles/theme'; import GlobalStyles from './styles/globalStyles'; +import Test from '@components/test/Test.tsx'; function App() { return ( <> -
-

19기 프론트엔드 파이팅!!! 디자인과 사이좋게 지내요~~~

-
+
); diff --git a/src/components/test/Test.tsx b/src/components/test/Test.tsx new file mode 100644 index 0000000..74789ff --- /dev/null +++ b/src/components/test/Test.tsx @@ -0,0 +1,7 @@ +import styled from 'styled-components'; + +const StyledTestDiv = styled.div``; + +export default function Test() { + return This is test div!!; +} diff --git a/src/styles/globalStyles.jsx b/src/styles/globalStyles.tsx similarity index 100% rename from src/styles/globalStyles.jsx rename to src/styles/globalStyles.tsx diff --git a/tsconfig.json b/tsconfig.json index cca83f9..f37122d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,4 +1,5 @@ { + "extends": "./tsconfig.paths.json", "compilerOptions": { "target": "es5", "lib": ["dom", "dom.iterable", "esnext"], @@ -15,16 +16,7 @@ "isolatedModules": true, "noEmit": true, "jsx": "react-jsx", - "baseUrl": ".", - "paths": { - "@components/*": ["./components/*"], - "@utils": ["./utils/*"], - "@styles": ["./styles/*"], - "@hooks": ["./hooks/*"], - "@pages": ["./pages/*"], - "@assets": ["./assets/*"], - "@context": ["./context/*"] - } + "allowImportingTsExtensions": true }, "include": ["src"] } diff --git a/tsconfig.paths.json b/tsconfig.paths.json new file mode 100644 index 0000000..16ae388 --- /dev/null +++ b/tsconfig.paths.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "baseUrl": "./src", + "paths": { + "@components/*": ["./components/*"], + "@assets/*": ["./assets/*"], + "@styles/*": ["./styles/*"], + "@context/*": ["./context/*"], + "@hooks/*": ["./hooks/*"], + "@utils/*": ["./utils/*"], + "@pages/*": ["./pages/*"] + } + } +} From 513ca0f3b3664d0e8227741c3cd94398c481d5b9 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Tue, 26 Mar 2024 14:15:31 +0900 Subject: [PATCH 06/81] Feat : make default entire app layout --- public/index.html | 2 +- src/App.css | 38 ------------------------ src/App.tsx | 8 ++--- src/components/AppMain.tsx | 7 +++++ src/components/fixed/HomeIndicator.tsx | 3 ++ src/components/fixed/IphoneStatusBar.tsx | 3 ++ src/components/test/Test.tsx | 7 ----- src/index.css | 13 -------- src/index.tsx | 9 +++--- src/styles/globalStyles.tsx | 23 +++++++++++++- 10 files changed, 44 insertions(+), 69 deletions(-) delete mode 100644 src/App.css create mode 100644 src/components/AppMain.tsx create mode 100644 src/components/fixed/HomeIndicator.tsx create mode 100644 src/components/fixed/IphoneStatusBar.tsx delete mode 100644 src/components/test/Test.tsx delete mode 100644 src/index.css diff --git a/public/index.html b/public/index.html index d94b676..444f3b5 100644 --- a/public/index.html +++ b/public/index.html @@ -14,7 +14,7 @@ React App - +
diff --git a/src/App.css b/src/App.css deleted file mode 100644 index 74b5e05..0000000 --- a/src/App.css +++ /dev/null @@ -1,38 +0,0 @@ -.App { - text-align: center; -} - -.App-logo { - height: 40vmin; - pointer-events: none; -} - -@media (prefers-reduced-motion: no-preference) { - .App-logo { - animation: App-logo-spin infinite 20s linear; - } -} - -.App-header { - background-color: #282c34; - min-height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: calc(10px + 2vmin); - color: white; -} - -.App-link { - color: #61dafb; -} - -@keyframes App-logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} diff --git a/src/App.tsx b/src/App.tsx index 2d1a463..87041cd 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,14 +1,14 @@ import { ThemeProvider } from 'styled-components'; -import theme from './styles/theme'; -import GlobalStyles from './styles/globalStyles'; -import Test from '@components/test/Test.tsx'; +import theme from '@styles/theme'; +import GlobalStyles from '@styles/globalStyles'; +import AppMain from '@components/AppMain'; function App() { return ( <> - + ); diff --git a/src/components/AppMain.tsx b/src/components/AppMain.tsx new file mode 100644 index 0000000..0bc0799 --- /dev/null +++ b/src/components/AppMain.tsx @@ -0,0 +1,7 @@ +import styled from 'styled-components'; + +const StyledAppMain = styled.main``; + +export default function AppMain() { + return This is app main!; +} diff --git a/src/components/fixed/HomeIndicator.tsx b/src/components/fixed/HomeIndicator.tsx new file mode 100644 index 0000000..5534dff --- /dev/null +++ b/src/components/fixed/HomeIndicator.tsx @@ -0,0 +1,3 @@ +export default function HomeIndicator() { + return; +} diff --git a/src/components/fixed/IphoneStatusBar.tsx b/src/components/fixed/IphoneStatusBar.tsx new file mode 100644 index 0000000..d19f67e --- /dev/null +++ b/src/components/fixed/IphoneStatusBar.tsx @@ -0,0 +1,3 @@ +export default function IphoneStatusBar() { + return; +} diff --git a/src/components/test/Test.tsx b/src/components/test/Test.tsx deleted file mode 100644 index 74789ff..0000000 --- a/src/components/test/Test.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import styled from 'styled-components'; - -const StyledTestDiv = styled.div``; - -export default function Test() { - return This is test div!!; -} diff --git a/src/index.css b/src/index.css deleted file mode 100644 index ec2585e..0000000 --- a/src/index.css +++ /dev/null @@ -1,13 +0,0 @@ -body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', - monospace; -} diff --git a/src/index.tsx b/src/index.tsx index d10be77..88e8a31 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,13 +1,12 @@ import React from 'react'; import ReactDOM from 'react-dom/client'; -import './index.css'; import App from './App'; const root = ReactDOM.createRoot( - document.getElementById('root') as HTMLElement + document.getElementById('root') as HTMLElement ); root.render( - - - + + + ); diff --git a/src/styles/globalStyles.tsx b/src/styles/globalStyles.tsx index 9d057d6..389259c 100644 --- a/src/styles/globalStyles.tsx +++ b/src/styles/globalStyles.tsx @@ -3,7 +3,28 @@ import reset from 'styled-reset'; const GlobalStyles = createGlobalStyle` ${reset} - box-sizing: border-box; + * { + box-sizing: border-box; + } + body { + margin: 0 auto; + height: 812px; + width: 375px; + } + #root { + height: 100%; + width: 100%; + } + + /* 스크롤 바 관련 UI를 제거하는 속성 원하는 요소에 scroll-box 클래스를 부여하면 됨 */ +.scroll-box { + -ms-overflow-style: none; +} + +.scroll-box::-webkit-scrollbar { + display: none; +} + `; export default GlobalStyles; From ce221fe5d17d9fe47d49d82b6b3b4b7395e92d84 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Tue, 26 Mar 2024 18:27:21 +0900 Subject: [PATCH 07/81] Feat : make iphone status bar UI layout --- public/icons/MobileSignal.png | Bin 0 -> 199 bytes public/icons/Type=Back.png | Bin 0 -> 324 bytes public/icons/Type=Call.png | Bin 0 -> 450 bytes public/icons/Type=Edit.png | Bin 0 -> 349 bytes public/icons/Type=Emoji.png | Bin 0 -> 570 bytes public/icons/Type=Friend.png | Bin 0 -> 486 bytes public/icons/Type=Go.png | Bin 0 -> 346 bytes public/icons/Type=MessageL.png | Bin 0 -> 402 bytes public/icons/Type=MessageS.png | Bin 0 -> 355 bytes public/icons/Type=NewFriend.png | Bin 0 -> 503 bytes public/icons/Type=NewMessage.png | Bin 0 -> 471 bytes public/icons/Type=Plus.png | Bin 0 -> 205 bytes public/icons/Type=Search.png | Bin 0 -> 336 bytes public/icons/Type=Send.png | Bin 0 -> 289 bytes public/icons/Type=Video.png | Bin 0 -> 301 bytes public/icons/Wifi.png | Bin 0 -> 295 bytes public/icons/_StatusBar-battery.png | Bin 0 -> 315 bytes src/components/AppMain.tsx | 13 ++++++++-- .../{ => HomeIndicator}/HomeIndicator.tsx | 0 src/components/fixed/IphoneStatusBar.tsx | 3 --- .../fixed/IphoneStatusBar/IphoneStatusBar.tsx | 23 ++++++++++++++++++ .../IphoneStatusBar/IphoneStatusLeftDiv.tsx | 16 ++++++++++++ .../IphoneStatusBar/IphoneStatusRightDiv.tsx | 23 ++++++++++++++++++ .../IphoneStatusBar/IphoneStatusbarImage.tsx | 11 +++++++++ src/styles/globalStyles.tsx | 5 ++++ 25 files changed, 89 insertions(+), 5 deletions(-) create mode 100644 public/icons/MobileSignal.png create mode 100644 public/icons/Type=Back.png create mode 100644 public/icons/Type=Call.png create mode 100644 public/icons/Type=Edit.png create mode 100644 public/icons/Type=Emoji.png create mode 100644 public/icons/Type=Friend.png create mode 100644 public/icons/Type=Go.png create mode 100644 public/icons/Type=MessageL.png create mode 100644 public/icons/Type=MessageS.png create mode 100644 public/icons/Type=NewFriend.png create mode 100644 public/icons/Type=NewMessage.png create mode 100644 public/icons/Type=Plus.png create mode 100644 public/icons/Type=Search.png create mode 100644 public/icons/Type=Send.png create mode 100644 public/icons/Type=Video.png create mode 100644 public/icons/Wifi.png create mode 100644 public/icons/_StatusBar-battery.png rename src/components/fixed/{ => HomeIndicator}/HomeIndicator.tsx (100%) delete mode 100644 src/components/fixed/IphoneStatusBar.tsx create mode 100644 src/components/fixed/IphoneStatusBar/IphoneStatusBar.tsx create mode 100644 src/components/fixed/IphoneStatusBar/IphoneStatusLeftDiv.tsx create mode 100644 src/components/fixed/IphoneStatusBar/IphoneStatusRightDiv.tsx create mode 100644 src/components/fixed/IphoneStatusBar/IphoneStatusbarImage.tsx diff --git a/public/icons/MobileSignal.png b/public/icons/MobileSignal.png new file mode 100644 index 0000000000000000000000000000000000000000..78aedfc032a950f86c0c1817000a255f3db85cb3 GIT binary patch literal 199 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3HGrh2HJ~Qk(@Ik;M!Q+`=Ht$S`Y;1W=H% zILO_JVcj{Imp~3nx}&cn1H;CC?mvmFKz@v;i(^Oy z;}tL5J4I^?RK%CIACUDt^0Z_}bM(i9e(&ve-*(nE zGfF)jGHwnf1YwQM@MP0Oxm>J0BVkwBAnVeOt+1 zBqpfs6nE literal 0 HcmV?d00001 diff --git a/public/icons/Type=Call.png b/public/icons/Type=Call.png new file mode 100644 index 0000000000000000000000000000000000000000..1b72e17bef94105e92c2ddf8069e6aee783cfd84 GIT binary patch literal 450 zcmV;z0X_bSP)kEsn1%h#3I@=PJoLy?N5zGd>%8ZmZ_u}^9aQyu{^>$WnVXAlczA#2 zUj)eikn+9M``VW>%fRL}teu({-_(6bmxBe?VSXQ=5)EG7p7 swSLA&mpsCfK>ylk+fO!aGyH$6Z&b{386xvVJpcdz07*qoM6N<$f+-5Wr~m)} literal 0 HcmV?d00001 diff --git a/public/icons/Type=Edit.png b/public/icons/Type=Edit.png new file mode 100644 index 0000000000000000000000000000000000000000..ab7333faa4f9f538ffa2ba2176bb37034b91e864 GIT binary patch literal 349 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1|)ksWqE-VXMsm#F#`j)FbFd;%$g$s6l5$8 za(7}_cTVOdki(Mh=a);xbl1ne=)a=^K^QUS{fN{%* z-G3jxu#xDylU-sN*{C^AbhYc0jWdduJ-ljY8|C7?^h&X>=J$`` z8+cCF_3#(n*Zwwl*^GT@57$U8TmQ4rDf$dUYlKgZAv4RZdAey0Tqm!H89V6NM(gx` t+a02~|IZgWX15;)gA|_X9l3v$`Nghuw?9oi`9R+@c)I$ztaD0e0szbVi#7lN literal 0 HcmV?d00001 diff --git a/public/icons/Type=Emoji.png b/public/icons/Type=Emoji.png new file mode 100644 index 0000000000000000000000000000000000000000..30cba5f1f4e5f92b1f27ef8d3e5665886e9be12a GIT binary patch literal 570 zcmV-A0>%A_P)|8%Rzd-T=Cks}}%IATu^}q_o5eEMWaV*07V`T~LvEvS zRZ(#~=~xfbiXrExZyPkUNKHp6GL(sStIcRJmG^42ugd-f-`pU#&k_VUumAu607*qo IM6N<$f=b`+#sB~S literal 0 HcmV?d00001 diff --git a/public/icons/Type=Friend.png b/public/icons/Type=Friend.png new file mode 100644 index 0000000000000000000000000000000000000000..d3890945d45e99fdccb2c5519e743be66e9b3cb7 GIT binary patch literal 486 zcmV@P)37DM{3TMHv%@4wq9%O~ z7K||u6~+mPvPYY~H3GVl2C+)hul)wOJpznDH3%hGusUFjhxIa;!2cb9^nC9*53-`_h>2rbG+%Q86GfN?^`aXcy(r2{O*i`75o z8DAl^C(LyU&i*(sMpK>BXo%7@#au82EkPZX>%1;)EqdmsgXK_L%$HN>h}PLH9T07- c6aB994bBpKl(&)VkpKVy07*qoM6N<$f@7D+X#fBK literal 0 HcmV?d00001 diff --git a/public/icons/Type=Go.png b/public/icons/Type=Go.png new file mode 100644 index 0000000000000000000000000000000000000000..f4354bb4a6ca33ec1db2df0fdfb7a6eb3fc04ac3 GIT binary patch literal 346 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjoCO|{#S9GG!XV7ZFl!D-1!HlL zyA#8@b22Z19F}xPUq=Rpjs4tz5?O)#_nt0}Ar*{wr~2|W83?q-YjaPNVaz`ux}#yH zYT?tfB`?q2Yf!FWv0#c87hCSu^ilnch}o}6X}6wytxb#ydT>3}V9IhF6_ z{hxaA6*n#`Z!q=Oje0V{R^DjG|Md?yGX_80%rNOgMuUkB*8Ygr+Ar*{or`#@VHV|-KU)J#9rH}`E8GC@^ zB}XssOO7#(bGdvtrySsyluOPHyWwZRFYSGD{&^nes)F}jv0^)lSxwV}CE7M#t6;R4 zmRK^!FI8vy_I0n<^J_B;_RaV?Rnkyzvf#=kI;V5CHNOAt9eGb*DYtjc zHVN>yxIFE%Qc7ACBK$evli}t;LKsOYhCozpiqwej(5Bd z?fJ^d_Q0&#b4Kw2g@?cQHw7qNebG5p^_F#8+;mUL6S7qgQohYxD4^AD*!OVj{@7#Y vfrZoD<7BsZ$C%&EP2QGX`)aRE{VCzS%luA!^EuG~3F? literal 0 HcmV?d00001 diff --git a/public/icons/Type=NewFriend.png b/public/icons/Type=NewFriend.png new file mode 100644 index 0000000000000000000000000000000000000000..24c7699b5c9a476f0728223efb1e1d98f474d87e GIT binary patch literal 503 zcmVqF;^a(a7R#;C`0mGNOt9>jz zxE5ZQK=(APp;T!v!d>9VJGY(p`TS)A%72`B4YGR!gYv2;BO20Nth?Pq+=FYk0RnO(aNWbjE(3qgv#0 zl3&rSEwgMJ2%qG5u-l=kTj0U6AA{8qq_N6PWU6Qh7eKtS-76y3edyA8w+S*y|3Hs& t?CP`DK)tVMJcUs;avF!Ub literal 0 HcmV?d00001 diff --git a/public/icons/Type=NewMessage.png b/public/icons/Type=NewMessage.png new file mode 100644 index 0000000000000000000000000000000000000000..ae2906619bcf69f98997b9c4dfde087bc8592717 GIT binary patch literal 471 zcmV;|0Vw{7P)1Pg1EoA0X_zqqF3L52 zP{Absvi&nm`&>GxR`=lW2(K^Ck0^^!%0Wj1Z)l(v&|~_G)bmCLxI#naeOlRVA<-Pz zwGAu)ZMvqq2^Dg@0`RYt0y^(CeVC@WG!J3~bS@BSkRk2HX`F?hQAxvX~8*-%yUF^-@5~j#sTSAMsu^4XF?~v+0eAZ?LDJ&bAY(S{hF2f|(jkmGi!Ptz=eR zP>`l<<`_7giKT8yh{BfD8PD@&_Whde%By|;^9s}RhfI^6%)d9uHcxTgRy}*6yNAqO d*?;BUVc@K=V2h5hN&tG8!PC{xWt~$(69CY8d|?0p literal 0 HcmV?d00001 diff --git a/public/icons/Type=Send.png b/public/icons/Type=Send.png new file mode 100644 index 0000000000000000000000000000000000000000..5864b4050d25c7134547358114cf4faca5472610 GIT binary patch literal 289 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjoCO|{#S9GG!XV7ZFl!D-1!HlL zyA#8@b22Z19F}xPUq=Rpjs4tz5?O)#ZJsWUAr*{wCk65{849?VvoNYNvd?LmTqHE1 zfRBOo!4jbf0h|vKX0-WgG-gL_`dw&#a!Gvc`nO>zp^O4IPV_wzYUdi`n>fW85X-hV`P5G81^+0xdyGdF! z!?zf=vh&|=&069-=jLRQ2ku+c|L*b3kMesey}en~ZdZe`V*#Uf&2y%6EaKt?Y8`qH gd<0?+be|K}?mzW7#&q&;pbr>4UHx3vIVCg!09+MmNB{r; literal 0 HcmV?d00001 diff --git a/public/icons/Type=Video.png b/public/icons/Type=Video.png new file mode 100644 index 0000000000000000000000000000000000000000..f7dcf2be3b06fe9681956973ca26443391ed004a GIT binary patch literal 301 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1|)ksWqE-VXMsm#F#`j)FbFd;%$g$s6l5$8 za(7}_cTVOdki(Mh=F_pR*xTQ=@9f4U5&E8KXa<;Jw( z^5^9d9D?aLe_olo<;>EgKK2t*F?S=JUHAfD?Bks6#N%eJcI%z!G;`J`YRSne*<8h} zPwkzTXCGRhaNvEFyzZYP^4zak_0&Y;Qg>}SFtI@+{%P3lz$p?Pf*zHgEH{LARI8 z9OUlAumdKI;Vst059}!s{jB1 literal 0 HcmV?d00001 diff --git a/public/icons/_StatusBar-battery.png b/public/icons/_StatusBar-battery.png new file mode 100644 index 0000000000000000000000000000000000000000..cfa050658994cfc8770e4b91666847f147ebe590 GIT binary patch literal 315 zcmeAS@N?(olHy`uVBq!ia0vp^G9Wew8<5=9&bJCkaTa()7Bet#3xhBt!>l*8o|0J>k`4>E0978G?_fFZ!cQ8Pt&A(GEDe*{B(nXhu zFP0HDUG1Ob)R=|21v`%%I?%xJ|NXtL+kLh><3DY6p8Wa#<$3cQctkB)Oe2J3`3e~G zU$XN^A6}Ov@p{X{6W9I+Hq`EDVyFrTI;~}^ z#qZqyJw4@eiA(uqmu>4dXEyB*$eXq7#ewrjfT#{Oc=^>$7@y)hRQJPe+$elF{r G5}E)^!FSUD literal 0 HcmV?d00001 diff --git a/src/components/AppMain.tsx b/src/components/AppMain.tsx index 0bc0799..403737b 100644 --- a/src/components/AppMain.tsx +++ b/src/components/AppMain.tsx @@ -1,7 +1,16 @@ import styled from 'styled-components'; +import IphoneStatusBar from '@components/fixed/IphoneStatusBar/IphoneStatusBar'; -const StyledAppMain = styled.main``; +const StyledAppMain = styled.main` + position: relative; + display: flex; + flex-direction: column; +`; export default function AppMain() { - return This is app main!; + return ( + + + + ); } diff --git a/src/components/fixed/HomeIndicator.tsx b/src/components/fixed/HomeIndicator/HomeIndicator.tsx similarity index 100% rename from src/components/fixed/HomeIndicator.tsx rename to src/components/fixed/HomeIndicator/HomeIndicator.tsx diff --git a/src/components/fixed/IphoneStatusBar.tsx b/src/components/fixed/IphoneStatusBar.tsx deleted file mode 100644 index d19f67e..0000000 --- a/src/components/fixed/IphoneStatusBar.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function IphoneStatusBar() { - return; -} diff --git a/src/components/fixed/IphoneStatusBar/IphoneStatusBar.tsx b/src/components/fixed/IphoneStatusBar/IphoneStatusBar.tsx new file mode 100644 index 0000000..39068e5 --- /dev/null +++ b/src/components/fixed/IphoneStatusBar/IphoneStatusBar.tsx @@ -0,0 +1,23 @@ +import styled from 'styled-components'; +import IphoneStatusLeftDiv from '@components/fixed/IphoneStatusBar/IphoneStatusLeftDiv'; +import IphoneStatusRightDiv from '@components/fixed/IphoneStatusBar/IphoneStatusRightDiv'; + +const StyledIphoneStatusBar = styled.div` + position: absolute; + top: 0; + width: 375px; + height: 47px; + display: flex; + justify-content: space-between; + align-items: center; + column-gap: 195px; +`; + +export default function IphoneStatusBar() { + return ( + + + + + ); +} diff --git a/src/components/fixed/IphoneStatusBar/IphoneStatusLeftDiv.tsx b/src/components/fixed/IphoneStatusBar/IphoneStatusLeftDiv.tsx new file mode 100644 index 0000000..86f6f17 --- /dev/null +++ b/src/components/fixed/IphoneStatusBar/IphoneStatusLeftDiv.tsx @@ -0,0 +1,16 @@ +import styled from 'styled-components'; + +const StyledIphoneStatusLeftDiv = styled.div` + width: 54px; + height: 21px; + text-align: center; + padding-top: 5px; + padding-bottom: 3px; + font-size: 17px; + font-weight: 700; + margin-left: 25px; +`; + +export default function IphoneStatusLeftDiv() { + return 9:41; +} diff --git a/src/components/fixed/IphoneStatusBar/IphoneStatusRightDiv.tsx b/src/components/fixed/IphoneStatusBar/IphoneStatusRightDiv.tsx new file mode 100644 index 0000000..960c01c --- /dev/null +++ b/src/components/fixed/IphoneStatusBar/IphoneStatusRightDiv.tsx @@ -0,0 +1,23 @@ +import styled from 'styled-components'; +import { + StyledBatteryImage, + StyledMobileSignalImage, + StyledWifiImage, +} from './IphoneStatusbarImage'; + +const StyledIphoneStatusRightDiv = styled.div` + display: flex; + align-items: center; + margin-right: 23.6px; + display: center; +`; + +export default function IphoneStatusRightDiv() { + return ( + + + + + + ); +} diff --git a/src/components/fixed/IphoneStatusBar/IphoneStatusbarImage.tsx b/src/components/fixed/IphoneStatusBar/IphoneStatusbarImage.tsx new file mode 100644 index 0000000..0be428c --- /dev/null +++ b/src/components/fixed/IphoneStatusBar/IphoneStatusbarImage.tsx @@ -0,0 +1,11 @@ +import styled from 'styled-components'; + +export const StyledBatteryImage = styled.img` + margin-left: 7px; +`; + +export const StyledMobileSignalImage = styled.img``; + +export const StyledWifiImage = styled.img` + margin-left: 8px; +`; diff --git a/src/styles/globalStyles.tsx b/src/styles/globalStyles.tsx index 389259c..6e3d7c6 100644 --- a/src/styles/globalStyles.tsx +++ b/src/styles/globalStyles.tsx @@ -6,7 +6,12 @@ const GlobalStyles = createGlobalStyle` * { box-sizing: border-box; } + html { + background-color: #E5E6EB; + } body { + background-color: #FFFFFF; + opacity: 0.95; margin: 0 auto; height: 812px; width: 375px; From 2968ce76b458e7e3fa46a3a18a366f92275e657e Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Wed, 27 Mar 2024 14:32:22 +0900 Subject: [PATCH 08/81] Feat : make chat head default UI layout --- public/icons/GitHub.png | Bin 0 -> 969 bytes public/icons/Instagram.png | Bin 0 -> 2202 bytes ...ype=Primary, Status=Activated, Size=40.png | Bin 0 -> 1116 bytes ...e=Primary, Status=Deactivated, Size=40.png | Bin 0 -> 1115 bytes ...e=Secondary, Status=Activated, Size=40.png | Bin 0 -> 798 bytes public/icons/callCircle.png | Bin 0 -> 941 bytes public/icons/chatBalloon.png | Bin 0 -> 836 bytes public/icons/dicord.png | Bin 0 -> 1373 bytes public/icons/dicord_2.png | Bin 0 -> 1538 bytes public/icons/dicord_3.png | Bin 0 -> 1505 bytes public/icons/iPhoneHomeIndicator.png | Bin 0 -> 283 bytes src/components/AppMain.tsx | 7 +++- src/components/fixed/ChatHead/ChatHead.tsx | 19 +++++++++ .../ChatHead/ChatHeadNav/ChatHeadNav.tsx | 20 +++++++++ .../ChatHead/ChatHeadNav/ChatHeadNavLeft.tsx | 39 ++++++++++++++++++ .../ChatHead/ChatHeadNav/ChatHeadNavRight.tsx | 29 +++++++++++++ .../IphoneStatusBar/IphoneStatusBar.tsx | 8 ++-- .../IphoneStatusBar/IphoneStatusLeftDiv.tsx | 0 .../IphoneStatusBar/IphoneStatusRightDiv.tsx | 0 .../IphoneStatusBar/IphoneStatusbarImage.tsx | 0 .../fixed/HomeIndicator/HomeIndicator.tsx | 19 ++++++++- src/styles/{theme.js => theme.ts} | 0 22 files changed, 134 insertions(+), 7 deletions(-) create mode 100644 public/icons/GitHub.png create mode 100644 public/icons/Instagram.png create mode 100644 public/icons/Type=Primary, Status=Activated, Size=40.png create mode 100644 public/icons/Type=Primary, Status=Deactivated, Size=40.png create mode 100644 public/icons/Type=Secondary, Status=Activated, Size=40.png create mode 100644 public/icons/callCircle.png create mode 100644 public/icons/chatBalloon.png create mode 100644 public/icons/dicord.png create mode 100644 public/icons/dicord_2.png create mode 100644 public/icons/dicord_3.png create mode 100644 public/icons/iPhoneHomeIndicator.png create mode 100644 src/components/fixed/ChatHead/ChatHead.tsx create mode 100644 src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNav.tsx create mode 100644 src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavLeft.tsx create mode 100644 src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavRight.tsx rename src/components/fixed/{ => ChatHead}/IphoneStatusBar/IphoneStatusBar.tsx (61%) rename src/components/fixed/{ => ChatHead}/IphoneStatusBar/IphoneStatusLeftDiv.tsx (100%) rename src/components/fixed/{ => ChatHead}/IphoneStatusBar/IphoneStatusRightDiv.tsx (100%) rename src/components/fixed/{ => ChatHead}/IphoneStatusBar/IphoneStatusbarImage.tsx (100%) rename src/styles/{theme.js => theme.ts} (100%) diff --git a/public/icons/GitHub.png b/public/icons/GitHub.png new file mode 100644 index 0000000000000000000000000000000000000000..3381cff7abebe3e8a7b980cb25a34c0a7dc64ba8 GIT binary patch literal 969 zcmV;)12+7LP) zyYGAcbLMtG_kQ1ekNf!NS5woOnR9+~X3m^5F~|Uah&ZDYEfx!$o}R+~{yveYfoSYm;PELlBk`kz{u7<|OMu?A(H>MbM@csRblarH}mX=1IGSnC$92gkD z*Vk7)^{7c;ZEYEVU!R0I(U~zF#nnz)Kd3gy%MMXY=pGj9&mtrSq;V6GczAe# zjg1YEBhVY+LZoAFZx3#6Zg`&)SXo&SE;js{X~6Av3+21JyS@&&p|rGA;ijytEPQ%; z!bm}3O_x%xzoMdokJEcDyvaR1Js2&Fjg1LykZsfX`MJloNEtHj&3FUWpcVJRG zqqsQ3iBcB@1qDL+*Vh-A91aKkZ%`^1!Q=6ODIp;tq($L?rDdU+8P+0$(eZ<#20j3%k3!gVHjk*W=@3 zO`~3~7cVX@cmkrjP|bLHdOANEi2#8LPyd98i3u(fWile8hMJlhL-oS=__(x2BEa%_ z5bOE*c`lc`admZ7mB8WQp{{Y-fPeQv1PJ6}2&ig4KR>yg&WpT3q*!`YC2)OxtxI5M zXD4Wlq9&Y~nUN;9w6vhjW<#U6zrWWsPMeTJSe6H#J-Mv2vy;n4M@RXERm0=sV@SqT z2@qI?gzS}6DAKACXiPqzj*>n&YS8={k;1@m43$7@Yb(CIyl9y~-M=B>_V$(&sjRHj rJOUakE-vQ9?8(Uq8i7=n{}uiL0+?@jl9!P-00000NkvXXu0mjf2yCC(LjX6M;OyYjgR=l zs4@N!On4|T64R72rD;ha#zsgQqM%he!_av@?o8)(A7^jAefB=*o+%;x;AGb9efHjG zt#5s6?X~w6q@KZ&F8Q^_w?yRTtPXc(wcebA%H*|@JQUa>r$gI!?|B`vA96gNry$e< zX#OdxtqQya4fTrh@OT*N;R3E5jcrQY`FUI8Gb;)YYDg)UD7OX*aE7!qjIe3kjpbZF z!@i`wGB_epP8wsrR`&{Sd^`d0>(bs!6gENRiyA$L^Ju*>gTgKknJqpOTqXXo?x5rJU4W<-kL|xP!}ZNl8Se;fYqonKTVB3DM5E;iv z9qQ|o?_j)fnekdA^6l;LPJ9r!ni+NYdFtSJR~GWM9T2^q2=uxiwCa!%i1(_7w=W;5 zS5Gs6rY@1tO`|(Tj{5PNS*coS;lKc7~cWfrb89Q~c20c6redJX#w*q})3bMZk zILlTO*tuz|;kr(`SVc1#O`1ui88-qjaf_bS#mfND_^vj1$4PBpVPiD5k>0Ovh1^tv z`owe42Tnle=*)W6GE=MBbzKF=Kny@j09mjMh-1NbF<+%|Z?5TmAB`Wo#TtKY3F-%D zAG7-$L-a5N-i#eHGSFvL961h5k8!;LUU9;;vy;&G4O_krzlYxtpsZQv!bXaLc;{3j zAp`VMT&yHU2CLYgvHdN~-t8sZKm89XUxq2%&_)8=QWeggw`;nKzz*)Fk&3I-VJcx1 znkS?%2TYuW`_dqd#D+k8dgV{s!<91sI z;?G6^;Bm2EZ-$__1hmiD?=P{P;kwN%t$X`{p;I(KgUT4@4xpG81H0|F6N`|EDYMIM zj(9Nx5~ZC5VPnE0j>?OacE+@1GT$X(iTk-cp9qdVVaI=XC%2<-*B8k~hBSL*geN5) zxXrG4_GRmRBy`G4otR>b=MwSIK?R-6?9K%mT1WusPKLu6cA|%V8YLfkw*gQ73AlZL zK(ba2eSk(e-^b$gI>Yvp$Eb&4^=w{AMm7S7LS-s$K?m9ZC@@=dT%1jXpo<7Q)@%qO z2iBkm4?uo&4A|Na{P<^h%QHR&9K0WRgN%CS6y$+pLfz(aL)k4I#ovHn-gqbxK!=Gp zi$yCzEKax_@a)mrMk!G?QEKO*fAMkX7yby$z1}ojnTCGkcQpQ4g7`N>ykfT@S%|{W z;Q-7myPJ%?{+R3z(8j|(#}g%^qn)H@SWvF{Fi*1gv4(SvDfi}k6UT^2UTB}X6WS0} zyuv2pMS@<=L$?+oSMWTaXBaD7-*DdU-e$x4|5>X?#Vq&)P&7AC-l8+k*!VyB?+(aE zz5@OCBUG|r!TM<4q2p^`W5RChE>0BbaWRC>^9;ywfpKV_;~iz-)*I}1r~YHNxdWlK zr#&g~xH4x%(FiAAeh64$A{AItzx-#$dn=W2yxmDhN=#@>eKKvCh`N|F-P}DrJjQz% z)+=f3_jj=OQ~Yp&5eS5)hy zsCzS~kW>320F;P6vqP>v0DSbfRM3xi^(=53fAaz0nK52~7!K88L7?4$eJ;>>IVm~p zGj$;`Ze;!$-~?|mJLtvcJp^(Mw<@WW>_!7R)kZ&n=CCbu%13nzu2e+W3iof0(kr&Phf|FSA{u z%u(5N8_P1ip4vnBxv~(^2`ilyr3Psakf#K75@iaMzDL&Y8jXH+$URHP;pIL@Wur+5VkUCV zP;5BblG?UG3P@7kZQq8I^b`o9z@#k@M4mKds};|~r)BN_C*DG+eze?+&cy)~)gff$ zy);thrG+W2?Zr8CZTxr+h}j7>s#>!lfVnPQ@-%<_v9J}^yy=!{OqMTTs(cd5<=@E2 cQ01^tWC~>Hw5Jafr zNFb1qph6f5YSU2AU_^;f0*Rdj$2ZJuI0KIDyEAvrC!Kch_D240W_SLK&>~Vp3rSqH z5}>e;h!RK`Cxp1HA;G+bR8WWo0(t{V<+2l-bI=0N%gaYcC>|*-4x>dQHW?2n@Rb82 zoX$>k7TgNCA}GWSXhCC6DDaie-uZjF*-K3co;<&B z%SWx%CxDolncO_uaJ}9`z1NBhk9eAdHh>ROex+>nKc4QMuU%911pX>6zJNB76ChwB z5;h*yTU$p^%!)t>Xg`wj+qDmWGjVG0>i`e;U;}|jqcfASO!eNLq=uGEsFcODCTt*> z(EL)l(qr$(pC+!9$H0Ny^~+JXeX|P=9tcBFAb@yhM`+Bx*Bbnxz4uPScQczH8eLQR zr_UBVc=8Qiy)Hu#ST0w(MT=`1T+^N-o<9Fpop1R^!G(cHU>c2dDD8i36DQpWJ$t@h zTR{=!wan!Ei0i2aJLts zx9=)YDv{%Reh?*sq_J!7wJSXT&x>G~f3J6E$kn@DeCqcnLeR$~i%S3>K3d=&7{QZ+ zU+2X4fJgN1Fkt)tiZ33wDKNJhuH?MzQE}SH;mz4zE@6{Ce5eDq{Hwr;=*6o=aDoD# zAf_yqeEZfaLZZf2OuT)o((Xn~Y;H63y&K2@d&UmGo=>fXa0@Xx3Cf-bo<0#_GkQCj zzY=q=X)1)c*2r_2Xo3gq+wJZ^v`*{?JwEgyWzkDQJnB|Re45h%_)}t4u=3c%DzCY^ zM4yAx^@W%XF8FosfML+cD)jexw5eCuV~%Cxa#! zS+z3DVZ)K2IO4#G*)S?;8T4+S=FFA*E#poypFWc&CK=7eYegNh0wKqgvJ>3}nN*9W zfq}XBO4xYciqrNLy!SW-?6(P4{e@`JB1*$XgD`EA6a41TW~X8qH>M5>+yWV|Z6?+2 z&2Wa(*4BrBkn*#Wu|2iV>xP6#xpIu#A9-jWaw}1*zurRg_S@Mn8B%N2RZtMK&fbYN zZ1FV>sxx=B;2C|gdM<$oB@hTZ9co6d- i=^STF*{Rn4hWHM>M9=leYZXrb0000iK~#7F#h6WK z6G0Tm-+ZL2p-C$VDuosrFcs?;LP13+dhk>)3Vwm2hyg90^q?0Ho;-L}Aqs*AD|l28 zdhn)X zPGY8#$)19)ot8ipmbDiKB=CM{0i4n~U7-yHUcIvzlSLl)>aj+kM?uik`!~Tkw|TAt zFWs6y*eb7W2BTzV>{V!Dv+=qDaY;>?8xjH{&=yFxn{|FmCW*Cvgw~tBXy;F~PaGn< z&>{6kHVDLJMFVIv=#VS7bA#*O*9g3Pv(W4Jo^!(%GIsvvVyyaIMPMTFCkRx;VVhBh zCXQ#od9Db|%WzzP2)Y?Reh`G`PeU+#sssFj014z|K3*dby;l;Vw~=vwAA@KV;O-5> zSE0Dnju*=U(R(_EpTDeBzWXf{uhfb_-H`HWytv_=Z$gPIA69mKR7R}QN=(PbQepH! zoWt(u#_$G1P>fPrZ283VU9jkw{Xd=3>4{JT_cQXjSj$8g*mL& z)RbmR9*l;MbKiwIaGuZbx6i|wOSiLH2W3x`l|zZ&!pf-qH`FJTVLs`F40i1nzdsAe#ebu;>#w$+uzd=-XB)f)M@ z))uMkyAbJ2WvSWcwill~^WmykkJ@|7B%i80I$zL>jLRw5W@x~k`dHOes=15bz3iUB zUI}ay_}lMaL~5Ez8X5&t1A(-X~*)jRy_0Cz>d7!~L)p^*VihH8beb52DhGDH< zKX5&sn&=gwP>Oo>D0)nNPIr;etd6jz1Xgo>)Ql`GL6kBXR(V?x8&6JV3l@7*tM%V(`BdA^aMRZ+#Apnl&qKm6Ij58 zzxkdbGXrsqu@y(Z8ASpq@t3EkrzhEhcB z#bVK=7#ODvo=hgW)oRs*fR-;N@`DVckIKztGTnSW-;exzQ3gq29W+~{0EoSgD)gwp z@p#;XwJuM!*wF{WqMOa8TPl^VL&t@6!q2{uBCCbqz(3nhbp{HQQ~C$ZI;BG|o6R;b z<Vqln_7W3WPwm9>kqfLOkz|yzw0=pv94d z8$uvkk2GVkScv%RPjQ_z!!*sNs5vXqU||H2V{L4~I_XP|1)hEuH_}*>B6HH|K-u473r0SZ1!`+S;F|Kpubm(A%>--n!^{+NrAgI_UE*(K3H52;$9zqADGQxRllWl1Gqb%&l^D< zp?Qzzc@F-wCOso^TA#e)3(W^D%i^ovck;ejHM}U>Vd9vh2`f7-FMEjmO^RTv&1SPI zFUn(MPfC*qgMp2M%q2bC?Avr2Y%vz*RIAmO<*Nu@Xq?XV c;=YEs2lI?3&Xpqz-2eap07*qoM6N<$f(vGH(EtDd literal 0 HcmV?d00001 diff --git a/public/icons/callCircle.png b/public/icons/callCircle.png new file mode 100644 index 0000000000000000000000000000000000000000..ce1b50109f5c19b905e004cbf36fd6d0426f8edd GIT binary patch literal 941 zcmV;e15*5nP)DR)mr9ad2f1^(+&o$}HpB0I#=DfL#-K_j{ZrBHV>sDp-j zPzs5b5K21i(khy4x+t!}{_Fi_cb468=9`I`^#{8<-^||gOViq`J?1=IWL~^b%L*>{2S)y3)R)tQ+0K92|M#jHi(sUIz7ab4{C(ACYGcL zkH<60jzooaWKQg5;$gnLOU^J>RaG@=rif%huCUy5ykdo10ILyYH9CyckPXQFZju9- z3v+I5ZTa=jBptcpHhCGDff7;)IkNm~rO0(o1hkweh5!jyX>tG@h2+jVgs;L@i-`WX zxb&H3X6NZ3nWW(d_o=4l7&!s&3?D+FD!|eYi;Q2sdc&XB6pO7<{LeN!GDl94N~P58 zEhF|U%b=MdR=&9OsqEhD$9YO756B5P7jA%CW>5M>hO;cY9w2u?1+}!xjG)Ns7t3x4 z$+C*uK~QG-{UQsaWV?R#3ib6|BBua1l28#DH8-Cu8wUbssjI7lT!I1zgdHy6KT|e# zntVEVWh;w|7tWW3VRiK<#pB!L7T|#J-~jTzp37zTo;`nIsKncMA83ZR=zlnv@PtC4 zYkYA+WYyB5P;70THaCB()Py=PPd0#+hVI=tv@4JkM&tzw z5&`E&nOox&S>%D<32a3>=^+o$b7X&Y3=k z{3(xm&PGF0x-{b zV`F2!9cwo^7e?@MK@|o#_V@Qkv~ke^#Dm*oMhK$N|e^O+k$jJLS3rcw3(2 zRgakw+jc-hBogt~*4E#@XXVr!^c9{K$(ybZ8H1;Wn5^i3)&d>?Juw!H^xX8?OM8Rx+uoD(3slz2bwP2)-E)0 z)#?siFbNtG0)>P?7kqioI72HP<~`_4PXatLciuVg-uv!-Pa#P(8jV=L-(MaK23bW> zGW^ko!(j~1c--W%!M`W`{3sj_@5kfurp&yG1hLlZ^>RGp-=c>L_sx$CcxR*$3R6I>R@-G|tD=|6X?lK8btDl-Hmmxp5euBmTrS9GfTP`R=k5O^0f+~;$0a{SU~X`v)9L0V0f6O}6+c8+ zbFSE~x8Zm(K~i!^4srh@kI1p|adN=18NrY*n7S8rUi=@N#N-BhO-M2F*dE^XiGDGME&#{ixMy?O#kZH~a{)Z~LIS z_$dsHipl`Zl#pprM9a>lrUD^mgDDm6bnbvhp0kLgsFYZlZ&ue3!lPSvS}1~*k(}iE z{?0Grpv8?d!L|!{=XwH-60_38Ota;U2<@1Y6Q1zo!G7M@$7R89MiRe0Z7Ddbg0Y3n zzLtW;VrJibn=0m!a9DbEi0B?c1xerr9HKz3 z+QO#N0pAGFBJu~hMo3|k%#VKlvA^B4IU8c>cQ#>VZHH`41p|R^GOW%_SZI3?1phOF z;0h#ut~s(L_Ta8z59o*x*pLiRi`d=bKy1})*4>p2T2QhCkHg2%;yn(~gE@>FKeX)k@(Obc493V|DU z#*Jt6L<$_(>JYV1Ww(6VD2lrKrPH!f$8u#~BQJ~1&(cG`wNJHAb#&FPY?N$rAIM;x zcY`bW>u4{y71f*P&3q(I?qy(tTZO_~y2CDCD(NrlP{UHyZyqZB6lTSg zvxsSf%~LOY3o0ArvVd*ipUWx_Ujp4a^J!Ig{O~@#Z5q2JqgHI=^8CuamNMcy>m zmE60Y7p*=%R@8f*_ZZfMdj)U2&faAoygahOH= z>`=;id+rs<81A_YcFw%^NEG?nF|0N#x2`!2ys9mqMcj0uwgjjm>@ga=XkmG6g}k=3 zTT}CNglVp*9CHd&HERws3ieC4WEC4yWuR-W5;F`H$usaDta*9Nkz*drOtr3-@AwTl zk>r4aa#f)YVRtg|I1&faC2_z|<0>Lw+wDFPJxC^%bAA3Mx=-PTz9CBJ`lX1kXZ$(3 z|IWSxn_LP!cnr1>2T&AnRQV2ec6d1Wr%nd~^; z%@21O30OY}D;cS_7)8X_?8l5f7B8_;hP(D2#5PYJK{(>PVz!@W*2yHohhVo^^Yt;c zq_pcP?Rr(d72E8qA+X1VF*_$VmCnTs?nv^Isa1$K=D+!m;rC0dBNbxVgv>3hT WLahs%>IZc+1j2rbK-UJvpZo{grEx?6 literal 0 HcmV?d00001 diff --git a/public/icons/dicord_2.png b/public/icons/dicord_2.png new file mode 100644 index 0000000000000000000000000000000000000000..bb064565ea8a03e59d665130bb7afb5d40bb354a GIT binary patch literal 1538 zcmbu9`9Bj30LEvLV===bX6PO1b){S*H!D}My=v&qmHTcTm`#py6SmYyOpI5=$LMui z3o-XCcVUiDxzqCM^ZoLcW zD&$+)QSb*}cEAAuAkkmq0#I@!jz%sV`jQEtya%>?RJgs2ZHxhc$~4dqR~`U>C(FXj z_>WMowHzmJn7u^%;f4C~@z0+XCNvu58y(y**>pGcokwGi{`qQ&RJX>Fk33=|gLnbA z628RQg44riN~lytD!9h6)U7%JT7b5|l==8d4o+%yOy8%?JTviT_Vg%t^#sD3oUvoi zkzStcEF?Q(2Fdi@*ix-(>5`C67E`trIjP7GBE|Apz6Hv=MrBkOSzz-J|8?$?jFRNo z(6;;;X#$Y1Y77x~rA4XHf~LQq%x9T<;Y$e_E9*JC@DpW11>=J{**}gSTXt4A*-$ce z;JLQCjXhN-Y85@_1i@jU|+5f<4BrV_TGPK~~u1H9#0GVfNtFwGbE6Ir55<_qKPeN1mb!$(iP1d@&| z0L?Ksq{2iN7VF;M)ZK*@qiS1Hst-DE%VA2WdLcG@cEzX?BlUrsEAvWB%KJ~}2%vnC zLZJ3g6Q4~f_9Zt&tvu=6;g8W^kk$QhdUZd`v=O(<^ zj|u-v9z11iBNeYGyVQ*e&OsP_?> z8=9{v>XXJg9{^A2J62p7p|s>P^p~tS8npqbk@EM3nv)&}aV1s`jXmnAd^4+=tL*2%iFLFG>Xz4_Jq$uz~F3BX0wm zRQ*y0UvKpa~2U~GbMeDh`36Kx$bJ+N%SGGGJX$}&!CUv8**ZH-)tYNW9{Lvk= z2{n4&pxX$uy~eYv`7jVyTc+KM*YSakHw}>cl4PRPt7R`OFGV5#PcW%$b&dL|kA>V< z=57>b%q+(Yk6u16d+|H}fNwI?48qBNnH>NCa2guu!p;)K+brx(-4XgtI z01(d~F#u3#{vgC7_kmAwUtV7yu?5g^8@AIq0Pl!5RLwB zUs)*Ts^gD`+}TPI-{=WU#TCGO*UqwsQdIAOqJUDx?fNORY+WlE9hlAC)R_Rq<8=UA z9P4Z6_#c)r>7SXc_GjvGvsih!s>yaKYxE-3j}Rfybo=m`Fe-5>6{7m=diTpDAu7qQ z$lWueqZ8zW40PkY$a`QhA3*ni;iZW=SS1=oVEs{YEL@vSGGzV6>Q*F{;;bOGgHEk- z8R!yZ=0bWf_cP(EI!A1|P=k9qT!Y!rbpP0vh4RD|4u!l_A4jq&sM110#vU_>lB~uOIPr~FVMk07JV+lFiq1w7^kQ2JX(klT+t0|A1|RvYm$^35 z-xivI<+Zj3xu2WU(-v&?N^!e*6WBrvT*2pcP^L-(-zTRBdeTy|ETuIkzGvOkXcVbH%j!~$&wZi1a`<$e3i!R z9*?c&Ru(369e}`kQs0htt2R+l%^e{!9{kXuLaEi#g{k~rb@Y=!;<+Oq8s^n)UpCeT+bb=7%fW&xEWe`CXEl|xv`*Xc z3iaMq`ig=l~ePWfPU7$R4+S^7MAKzK4v zhuY>_94>CVkG18qvs2p;A>l4hOeCxeD{W55(t|*gH#{MyH#cH`rC;(3rXtK60&*bx z{FbKU>*`W^)D(H}@Z=hAF;)D_L{(;8R{8X&Ev1_6I50BM>SXNtMlr;g|1^1V_G9tS zK1NS!+U~WjM^Z)69fnSj-~DKPF%8TXVbR_~+A~B3%w6A`i2v4BgV~FQ}2kGGNbvq!d6NZXfg7nw^_dPJwclHlmDrZq}d^6 zuQW;1JlEoFK6n!R8Ab6n@w=P5y4=Jg%&D3WlgmqVim4sfz@+9^j?l};fm$t!r3;(& tvJ&BQr=u&21>{VYTYnF&|Fe$5{V_J`Ydx47aYkf-p`Mv;wYKB){{Vvw)>;4n literal 0 HcmV?d00001 diff --git a/public/icons/iPhoneHomeIndicator.png b/public/icons/iPhoneHomeIndicator.png new file mode 100644 index 0000000000000000000000000000000000000000..57597475a2318cad8cebf363fe5ad68c785c951a GIT binary patch literal 283 zcmeAS@N?(olHy`uVBq!ia0y~yU@Ql+l{nadfk$L90|U1(2s1Lwnj--e zWGoJHcVbv~PUa<$!;&U>cv7h@-A}f%;!PCVtq=ND7bwklZ4gxF}_G&8_O=w`5 zAi$|{*h@x$^TdgzmV9!&B_<-%|IZFI*qpBn)Px5-VAp(c&w!^r>h;!Z7OXC3ESDEX zPOHA4`* - + + ); } diff --git a/src/components/fixed/ChatHead/ChatHead.tsx b/src/components/fixed/ChatHead/ChatHead.tsx new file mode 100644 index 0000000..c1b5632 --- /dev/null +++ b/src/components/fixed/ChatHead/ChatHead.tsx @@ -0,0 +1,19 @@ +import styled from 'styled-components'; +import IphoneStatusBar from '@components/fixed/ChatHead/IphoneStatusBar/IphoneStatusBar'; +import ChatHeadNav from '@components/fixed/ChatHead/ChatHeadNav/ChatHeadNav'; + +const StyledChatHead = styled.div` + display: flex; + flex-direction: column; + width: 100%; + height: 111px; +`; + +export default function ChatHead() { + return ( + + + + + ); +} diff --git a/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNav.tsx b/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNav.tsx new file mode 100644 index 0000000..1b214a7 --- /dev/null +++ b/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNav.tsx @@ -0,0 +1,20 @@ +import styled from 'styled-components'; +import ChatHeadNavLeft from '@components/fixed/ChatHead/ChatHeadNav/ChatHeadNavLeft'; +import ChatHeadNavRight from '@components/fixed/ChatHead/ChatHeadNav/ChatHeadNavRight'; + +const StyledChatHeadNav = styled.nav` + flex-grow: 1; + flex-shrink: 0; + display: flex; + align-items: center; + justify-content: space-between; +`; + +export default function ChatHeadNav() { + return ( + + + + + ); +} diff --git a/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavLeft.tsx b/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavLeft.tsx new file mode 100644 index 0000000..e0b62a6 --- /dev/null +++ b/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavLeft.tsx @@ -0,0 +1,39 @@ +import styled from 'styled-components'; + +const StyledChatHeadNavLeft = styled.div` + width: 122px; + height: 32px; + display: flex; + justify-content: space-between; + align-items: center; + margin-left: 16px; +`; + +const StyledChatHeavNavLeftBackImg = styled.img``; + +const StyledChatHeadNavLeftDiscordImage = styled.img` + width: 32px; + height: 32px; + border-radius: 50%; +`; + +const StyledUserNameSpan = styled.span` + font-size: ${(props) => props.theme.textStyle.fontSize.h3}; + font-weight: 600; +`; + +export default function ChatHeadNavLeft() { + return ( + + + + 김정민 + + ); +} diff --git a/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavRight.tsx b/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavRight.tsx new file mode 100644 index 0000000..1a1d503 --- /dev/null +++ b/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavRight.tsx @@ -0,0 +1,29 @@ +import styled from 'styled-components'; + +const StyledChatHeadNavRight = styled.div` + height: 32px; + width: 80px; + display: flex; + justify-content: space-between; + align-items: center; + margin-right: 16px; +`; + +const StyledChatHeadNavRightCallImg = styled.img` + width: 32px; + height: 32px; +`; + +const StyledChatHeadNavRightBalloonImg = styled.img``; + +export default function ChatHeadNavRight() { + return ( + + + + + ); +} diff --git a/src/components/fixed/IphoneStatusBar/IphoneStatusBar.tsx b/src/components/fixed/ChatHead/IphoneStatusBar/IphoneStatusBar.tsx similarity index 61% rename from src/components/fixed/IphoneStatusBar/IphoneStatusBar.tsx rename to src/components/fixed/ChatHead/IphoneStatusBar/IphoneStatusBar.tsx index 39068e5..11607f8 100644 --- a/src/components/fixed/IphoneStatusBar/IphoneStatusBar.tsx +++ b/src/components/fixed/ChatHead/IphoneStatusBar/IphoneStatusBar.tsx @@ -1,10 +1,10 @@ import styled from 'styled-components'; -import IphoneStatusLeftDiv from '@components/fixed/IphoneStatusBar/IphoneStatusLeftDiv'; -import IphoneStatusRightDiv from '@components/fixed/IphoneStatusBar/IphoneStatusRightDiv'; +import IphoneStatusLeftDiv from '@components/fixed/ChatHead/IphoneStatusBar/IphoneStatusLeftDiv'; +import IphoneStatusRightDiv from '@components/fixed/ChatHead/IphoneStatusBar/IphoneStatusRightDiv'; const StyledIphoneStatusBar = styled.div` - position: absolute; - top: 0; + /* position: absolute; */ + /* top: 0; */ width: 375px; height: 47px; display: flex; diff --git a/src/components/fixed/IphoneStatusBar/IphoneStatusLeftDiv.tsx b/src/components/fixed/ChatHead/IphoneStatusBar/IphoneStatusLeftDiv.tsx similarity index 100% rename from src/components/fixed/IphoneStatusBar/IphoneStatusLeftDiv.tsx rename to src/components/fixed/ChatHead/IphoneStatusBar/IphoneStatusLeftDiv.tsx diff --git a/src/components/fixed/IphoneStatusBar/IphoneStatusRightDiv.tsx b/src/components/fixed/ChatHead/IphoneStatusBar/IphoneStatusRightDiv.tsx similarity index 100% rename from src/components/fixed/IphoneStatusBar/IphoneStatusRightDiv.tsx rename to src/components/fixed/ChatHead/IphoneStatusBar/IphoneStatusRightDiv.tsx diff --git a/src/components/fixed/IphoneStatusBar/IphoneStatusbarImage.tsx b/src/components/fixed/ChatHead/IphoneStatusBar/IphoneStatusbarImage.tsx similarity index 100% rename from src/components/fixed/IphoneStatusBar/IphoneStatusbarImage.tsx rename to src/components/fixed/ChatHead/IphoneStatusBar/IphoneStatusbarImage.tsx diff --git a/src/components/fixed/HomeIndicator/HomeIndicator.tsx b/src/components/fixed/HomeIndicator/HomeIndicator.tsx index 5534dff..59d95aa 100644 --- a/src/components/fixed/HomeIndicator/HomeIndicator.tsx +++ b/src/components/fixed/HomeIndicator/HomeIndicator.tsx @@ -1,3 +1,20 @@ +import styled from 'styled-components'; + +const StyledHomeIndicator = styled.div` + width: 100%; + height: auto; + position: absolute; + bottom: 0; + display: flex; + align-items: flex-start; +`; + +const StyledHomeIndicatorImage = styled.img``; + export default function HomeIndicator() { - return; + return ( + + + + ); } diff --git a/src/styles/theme.js b/src/styles/theme.ts similarity index 100% rename from src/styles/theme.js rename to src/styles/theme.ts From e6ad123c5b24d7810bda26960ba95831097c7716 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Wed, 27 Mar 2024 14:50:10 +0900 Subject: [PATCH 09/81] =?UTF-8?q?Fix=20:=20styled-components=EC=9D=98=20cs?= =?UTF-8?q?s=20=EA=B8=B0=EB=8A=A5=EC=9D=84=20=EC=9D=B4=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=97=AC=20=EC=9E=AC=EC=82=AC=EC=9A=A9=EC=84=B1=20=EC=A6=9D?= =?UTF-8?q?=EB=8C=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ChatHead/ChatHeadNav/ChatHeadNavLeft.tsx | 14 +++++++++- .../ChatHead/ChatHeadNav/ChatHeadNavRight.tsx | 5 +++- src/styles/styledComponents.ts | 7 +++++ src/styles/theme.ts | 28 +++++++++---------- 4 files changed, 38 insertions(+), 16 deletions(-) create mode 100644 src/styles/styledComponents.ts diff --git a/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavLeft.tsx b/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavLeft.tsx index e0b62a6..7f55f89 100644 --- a/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavLeft.tsx +++ b/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavLeft.tsx @@ -1,4 +1,5 @@ import styled from 'styled-components'; +import * as ST from '@styles/styledComponents'; const StyledChatHeadNavLeft = styled.div` width: 122px; @@ -9,17 +10,28 @@ const StyledChatHeadNavLeft = styled.div` margin-left: 16px; `; -const StyledChatHeavNavLeftBackImg = styled.img``; +const StyledChatHeavNavLeftBackImg = styled.img` + width: 24px; + height: 24px; + ${ST.hoverCursor} + margin-right: 8px; +`; const StyledChatHeadNavLeftDiscordImage = styled.img` width: 32px; height: 32px; border-radius: 50%; + ${ST.hoverCursor} + margin-left: 8px; + margin-right: 4px; `; const StyledUserNameSpan = styled.span` font-size: ${(props) => props.theme.textStyle.fontSize.h3}; + line-height: ${(props) => props.theme.textStyle.lineHeight.h3}; font-weight: 600; + ${ST.hoverCursor} + margin-left: 4px; `; export default function ChatHeadNavLeft() { diff --git a/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavRight.tsx b/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavRight.tsx index 1a1d503..692c956 100644 --- a/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavRight.tsx +++ b/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavRight.tsx @@ -14,7 +14,10 @@ const StyledChatHeadNavRightCallImg = styled.img` height: 32px; `; -const StyledChatHeadNavRightBalloonImg = styled.img``; +const StyledChatHeadNavRightBalloonImg = styled.img` + width: 32px; + height: 32px; +`; export default function ChatHeadNavRight() { return ( diff --git a/src/styles/styledComponents.ts b/src/styles/styledComponents.ts new file mode 100644 index 0000000..46b0e06 --- /dev/null +++ b/src/styles/styledComponents.ts @@ -0,0 +1,7 @@ +import { css } from 'styled-components'; + +export const hoverCursor = css` + &:hover { + cursor: pointer; + } +`; diff --git a/src/styles/theme.ts b/src/styles/theme.ts index 3b8678b..f3ca950 100644 --- a/src/styles/theme.ts +++ b/src/styles/theme.ts @@ -9,22 +9,22 @@ const theme = { }, textStyle: { fontSize: { - h1: '32', - h2: '20', - h3: '16', - body1: '15', - body2: '15', - label: '13', - caption: '10', + h1: '32px', + h2: '20px', + h3: '16px', + body1: '15px', + body2: '15px', + label: '13px', + caption: '10px', }, lineHeight: { - h1: '150', - h2: '150', - h3: '150', - body1: '150', - body2: '150', - label: '150', - caption: '150', + h1: '150%', + h2: '150%', + h3: '150%', + body1: '150%', + body2: '150%', + label: '150%', + caption: '150%', }, }, gridStyle: { From 93103e63790eb91daa44f346c4cff692ca2371fa Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Thu, 28 Mar 2024 12:37:31 +0900 Subject: [PATCH 10/81] Fix : replace png images with svg images --- make.md | 15 ++++-- public/icons/GitHub.png | Bin 969 -> 0 bytes public/icons/Instagram.png | Bin 2202 -> 0 bytes public/icons/MobileSignal.png | Bin 199 -> 0 bytes public/icons/Type=Back.png | Bin 324 -> 0 bytes public/icons/Type=Call.png | Bin 450 -> 0 bytes public/icons/Type=Edit.png | Bin 349 -> 0 bytes public/icons/Type=Emoji.png | Bin 570 -> 0 bytes public/icons/Type=Friend.png | Bin 486 -> 0 bytes public/icons/Type=Go.png | Bin 346 -> 0 bytes public/icons/Type=MessageL.png | Bin 402 -> 0 bytes public/icons/Type=MessageS.png | Bin 355 -> 0 bytes public/icons/Type=NewFriend.png | Bin 503 -> 0 bytes public/icons/Type=NewMessage.png | Bin 471 -> 0 bytes public/icons/Type=Plus.png | Bin 205 -> 0 bytes ...ype=Primary, Status=Activated, Size=40.png | Bin 1116 -> 0 bytes ...e=Primary, Status=Deactivated, Size=40.png | Bin 1115 -> 0 bytes public/icons/Type=Search.png | Bin 336 -> 0 bytes ...e=Secondary, Status=Activated, Size=40.png | Bin 798 -> 0 bytes public/icons/Type=Send.png | Bin 289 -> 0 bytes public/icons/Type=Video.png | Bin 301 -> 0 bytes public/icons/Wifi.png | Bin 295 -> 0 bytes public/icons/_StatusBar-battery.png | Bin 315 -> 0 bytes public/icons/callCircle.png | Bin 941 -> 0 bytes public/icons/chatBalloon.png | Bin 836 -> 0 bytes public/icons/dicord.png | Bin 1373 -> 0 bytes public/icons/dicord_2.png | Bin 1538 -> 0 bytes public/icons/dicord_3.png | Bin 1505 -> 0 bytes public/icons/iPhoneHomeIndicator.png | Bin 283 -> 0 bytes public/images/GitHub.svg | 9 ++++ public/images/Instagram.svg | 9 ++++ public/images/MobileSignal.svg | 6 +++ public/images/Wifi.svg | 3 ++ public/images/_StatusBar-battery.svg | 5 ++ public/images/call.svg | 3 ++ public/images/circleCall.svg | 4 ++ public/images/circleMessage.svg | 4 ++ public/images/circlePlus.svg | 4 ++ public/images/clearSend.svg | 4 ++ public/images/dicord.svg | 9 ++++ public/images/dicord_2.svg | 9 ++++ public/images/dicord_3.svg | 9 ++++ public/images/discord24.svg | 9 ++++ public/images/discord32.svg | 9 ++++ public/images/discord40.svg | 9 ++++ public/images/discord48.svg | 9 ++++ public/images/discord80.svg | 9 ++++ public/images/edit.svg | 4 ++ public/images/friend.svg | 3 ++ public/images/iPhoneHomeIndicator.svg | 3 ++ public/images/leftArrow.svg | 3 ++ public/images/magnifyingGlass.svg | 3 ++ public/images/messageLarge.svg | 3 ++ public/images/messageSmall.svg | 3 ++ public/images/newFriend.svg | 3 ++ public/images/newMessage.svg | 3 ++ public/images/plus.svg | 3 ++ public/images/rightarrow.svg | 3 ++ public/images/smileEmoji.svg | 3 ++ public/images/staleSend.svg | 6 +++ public/images/video.svg | 3 ++ public/images/whiteSend.svg | 3 ++ src/components/AppMain.tsx | 2 + .../ChatHead/ChatHeadNav/ChatHeadNavLeft.tsx | 10 +--- .../ChatHead/ChatHeadNav/ChatHeadNavRight.tsx | 10 ++-- .../IphoneStatusBar/IphoneStatusRightDiv.tsx | 9 ++-- .../fixed/HomeIndicator/HomeIndicator.tsx | 7 ++- .../non-fixed/ChatInput/ChatInput.tsx | 22 +++++++++ .../non-fixed/ChatInput/ChatInputForm.tsx | 45 ++++++++++++++++++ src/styles/theme.ts | 4 ++ 70 files changed, 273 insertions(+), 23 deletions(-) delete mode 100644 public/icons/GitHub.png delete mode 100644 public/icons/Instagram.png delete mode 100644 public/icons/MobileSignal.png delete mode 100644 public/icons/Type=Back.png delete mode 100644 public/icons/Type=Call.png delete mode 100644 public/icons/Type=Edit.png delete mode 100644 public/icons/Type=Emoji.png delete mode 100644 public/icons/Type=Friend.png delete mode 100644 public/icons/Type=Go.png delete mode 100644 public/icons/Type=MessageL.png delete mode 100644 public/icons/Type=MessageS.png delete mode 100644 public/icons/Type=NewFriend.png delete mode 100644 public/icons/Type=NewMessage.png delete mode 100644 public/icons/Type=Plus.png delete mode 100644 public/icons/Type=Primary, Status=Activated, Size=40.png delete mode 100644 public/icons/Type=Primary, Status=Deactivated, Size=40.png delete mode 100644 public/icons/Type=Search.png delete mode 100644 public/icons/Type=Secondary, Status=Activated, Size=40.png delete mode 100644 public/icons/Type=Send.png delete mode 100644 public/icons/Type=Video.png delete mode 100644 public/icons/Wifi.png delete mode 100644 public/icons/_StatusBar-battery.png delete mode 100644 public/icons/callCircle.png delete mode 100644 public/icons/chatBalloon.png delete mode 100644 public/icons/dicord.png delete mode 100644 public/icons/dicord_2.png delete mode 100644 public/icons/dicord_3.png delete mode 100644 public/icons/iPhoneHomeIndicator.png create mode 100644 public/images/GitHub.svg create mode 100644 public/images/Instagram.svg create mode 100644 public/images/MobileSignal.svg create mode 100644 public/images/Wifi.svg create mode 100644 public/images/_StatusBar-battery.svg create mode 100644 public/images/call.svg create mode 100644 public/images/circleCall.svg create mode 100644 public/images/circleMessage.svg create mode 100644 public/images/circlePlus.svg create mode 100644 public/images/clearSend.svg create mode 100644 public/images/dicord.svg create mode 100644 public/images/dicord_2.svg create mode 100644 public/images/dicord_3.svg create mode 100644 public/images/discord24.svg create mode 100644 public/images/discord32.svg create mode 100644 public/images/discord40.svg create mode 100644 public/images/discord48.svg create mode 100644 public/images/discord80.svg create mode 100644 public/images/edit.svg create mode 100644 public/images/friend.svg create mode 100644 public/images/iPhoneHomeIndicator.svg create mode 100644 public/images/leftArrow.svg create mode 100644 public/images/magnifyingGlass.svg create mode 100644 public/images/messageLarge.svg create mode 100644 public/images/messageSmall.svg create mode 100644 public/images/newFriend.svg create mode 100644 public/images/newMessage.svg create mode 100644 public/images/plus.svg create mode 100644 public/images/rightarrow.svg create mode 100644 public/images/smileEmoji.svg create mode 100644 public/images/staleSend.svg create mode 100644 public/images/video.svg create mode 100644 public/images/whiteSend.svg create mode 100644 src/components/non-fixed/ChatInput/ChatInput.tsx create mode 100644 src/components/non-fixed/ChatInput/ChatInputForm.tsx diff --git a/make.md b/make.md index c33359d..1e9b421 100644 --- a/make.md +++ b/make.md @@ -1,4 +1,5 @@ # 구현을 해야할 기능 + - 사용자가 input에 focus를 하면 전체의 너비가 줄어들어야 함 - 기본적으로 모바일 뷰를 기준으로 하기에 전체 너비를 375px, 높이를 812로 제한하고 시작하면 됨 - 기존의 사용자를 토글하면 사용자가 바뀌고 채팅이 뒤바뀌어서 보여야 함. => me와 other 속성 2개를 선언함 @@ -14,29 +15,33 @@ - 카카오톡, 라인과 다르게 왼쪽 오른쪽으로 상대방과의 메시지가 엇갈려서 나타나는 것이 아니라, 왼쪽에 항상 몰려있는 구조임. 메시지의 길이에 따라서 메시지 박스 높이가 조절이 되도록 `height: 속성은 auto` 를 부여하는 것이 좋음 - homeIndicator는 채팅 입력 박스가 올라오더라도 아래에 고정되어 있어야 함 - ### 나누면 좋을만한 파일 + - 기본적으로 styled-components 모듈을 사용할 것이기 때문에 정민님꼐서 Iphone, Images, TabBar, Gloabl, FriendPage, ProfilePage, ChatroomPage 등으로 나눠놓은 컴포넌트를 따로 파일로 분리하고 export하면 이를 import하여 객체 지향형 느낌으로 사용 ### 디자인 시스템 + - 자주 사용되는 색상들을 변수로 활용하고 여기에는 동일한 색상이더라도 opacity의 개념이 도입될 수도 있음 - styled-components나, styled-system 모듈은 디자인 시스템을 구축할 수 있게 도와준다 using ThemeProvider나 후자의 utility first 클래스명을 이용! - - ### 컴포넌트 분리의 기준 + - 일단 figma에서 제시한 컴포넌트들을 최대한 복사 + 붙여넣기 식으로 활용하고, 추후에 활용을 할 떄 제대로 적용이 안된 것들은 다시 `styled()` 함수를 이용하여 다시 스타일링 진행 - 그냥 단순 이미지를 렌더링해주는 컴포넌트를 imageComponent 라는 디렉터리에 한 파일로 그냥 싸악 적용해주고 끝낸다. 나중에 이를 활용하는 헤더 등의 컴포넌트에서 이들을 받아서 사용한다 - 매 화면에 고정적으로 등장하는 iphone Status Bar(시간, 와이파이, 배터리를 가지고 있는 헤더)와 homeIndicator는 따로 fixed 컴포넌트로 만들어두고 매번 활용하는 구조. 가장 위에는 `iphoneStatusBar`, 가장 아래에는 `homeIndicator(z-index를 높게 설정)`가 위치하는 구조 - 해당 컴포넌트가 상황에 따라서 위치나 크기 등이 가변적인지 아닌지에 따라서 `header`, `AppMain`, `footer`로 나뉘어야 함 - ### 정민님 figma 기본 구조 + - 기본적으로 항상 많이 쓰이는 것들은 fixed, 유동적으로 구성되는 요소들은 scroll이라고 표현하심 - input box에 포커싱이 되면 위의 고정 헤더들은 가만히 있고, 나머지 전체 요소들의 높이가 자연스럽게 줄어들어야 한다 - ### 사용자 시나리오 + 1. 전체 탭 바를 통해 이동할 수 있는 구간은 3군데임 : 친구 & 메시지 & 내 프로필 2. 친구와의 메시지 탭을 누르면 바로 메시지 url로 넘어감 : `message/{친구명}`의 주소로 처리됨 3. <- arrow를 통해서 이전의 `/message` 주소로 리디렉션됨. + +### 해결 포인트 + +1. input 박스 옆에 웃는 아이코은 span 태그 내부에 input과 이모지를 flex item으로 구성하며 해당 길이는 따라서 조정해줘야 함 diff --git a/public/icons/GitHub.png b/public/icons/GitHub.png deleted file mode 100644 index 3381cff7abebe3e8a7b980cb25a34c0a7dc64ba8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 969 zcmV;)12+7LP) zyYGAcbLMtG_kQ1ekNf!NS5woOnR9+~X3m^5F~|Uah&ZDYEfx!$o}R+~{yveYfoSYm;PELlBk`kz{u7<|OMu?A(H>MbM@csRblarH}mX=1IGSnC$92gkD z*Vk7)^{7c;ZEYEVU!R0I(U~zF#nnz)Kd3gy%MMXY=pGj9&mtrSq;V6GczAe# zjg1YEBhVY+LZoAFZx3#6Zg`&)SXo&SE;js{X~6Av3+21JyS@&&p|rGA;ijytEPQ%; z!bm}3O_x%xzoMdokJEcDyvaR1Js2&Fjg1LykZsfX`MJloNEtHj&3FUWpcVJRG zqqsQ3iBcB@1qDL+*Vh-A91aKkZ%`^1!Q=6ODIp;tq($L?rDdU+8P+0$(eZ<#20j3%k3!gVHjk*W=@3 zO`~3~7cVX@cmkrjP|bLHdOANEi2#8LPyd98i3u(fWile8hMJlhL-oS=__(x2BEa%_ z5bOE*c`lc`admZ7mB8WQp{{Y-fPeQv1PJ6}2&ig4KR>yg&WpT3q*!`YC2)OxtxI5M zXD4Wlq9&Y~nUN;9w6vhjW<#U6zrWWsPMeTJSe6H#J-Mv2vy;n4M@RXERm0=sV@SqT z2@qI?gzS}6DAKACXiPqzj*>n&YS8={k;1@m43$7@Yb(CIyl9y~-M=B>_V$(&sjRHj rJOUakE-vQ9?8(Uq8i7=n{}uiL0+?@jl9!P-00000NkvXXu0mjf2yCC(LjX6M;OyYjgR=l zs4@N!On4|T64R72rD;ha#zsgQqM%he!_av@?o8)(A7^jAefB=*o+%;x;AGb9efHjG zt#5s6?X~w6q@KZ&F8Q^_w?yRTtPXc(wcebA%H*|@JQUa>r$gI!?|B`vA96gNry$e< zX#OdxtqQya4fTrh@OT*N;R3E5jcrQY`FUI8Gb;)YYDg)UD7OX*aE7!qjIe3kjpbZF z!@i`wGB_epP8wsrR`&{Sd^`d0>(bs!6gENRiyA$L^Ju*>gTgKknJqpOTqXXo?x5rJU4W<-kL|xP!}ZNl8Se;fYqonKTVB3DM5E;iv z9qQ|o?_j)fnekdA^6l;LPJ9r!ni+NYdFtSJR~GWM9T2^q2=uxiwCa!%i1(_7w=W;5 zS5Gs6rY@1tO`|(Tj{5PNS*coS;lKc7~cWfrb89Q~c20c6redJX#w*q})3bMZk zILlTO*tuz|;kr(`SVc1#O`1ui88-qjaf_bS#mfND_^vj1$4PBpVPiD5k>0Ovh1^tv z`owe42Tnle=*)W6GE=MBbzKF=Kny@j09mjMh-1NbF<+%|Z?5TmAB`Wo#TtKY3F-%D zAG7-$L-a5N-i#eHGSFvL961h5k8!;LUU9;;vy;&G4O_krzlYxtpsZQv!bXaLc;{3j zAp`VMT&yHU2CLYgvHdN~-t8sZKm89XUxq2%&_)8=QWeggw`;nKzz*)Fk&3I-VJcx1 znkS?%2TYuW`_dqd#D+k8dgV{s!<91sI z;?G6^;Bm2EZ-$__1hmiD?=P{P;kwN%t$X`{p;I(KgUT4@4xpG81H0|F6N`|EDYMIM zj(9Nx5~ZC5VPnE0j>?OacE+@1GT$X(iTk-cp9qdVVaI=XC%2<-*B8k~hBSL*geN5) zxXrG4_GRmRBy`G4otR>b=MwSIK?R-6?9K%mT1WusPKLu6cA|%V8YLfkw*gQ73AlZL zK(ba2eSk(e-^b$gI>Yvp$Eb&4^=w{AMm7S7LS-s$K?m9ZC@@=dT%1jXpo<7Q)@%qO z2iBkm4?uo&4A|Na{P<^h%QHR&9K0WRgN%CS6y$+pLfz(aL)k4I#ovHn-gqbxK!=Gp zi$yCzEKax_@a)mrMk!G?QEKO*fAMkX7yby$z1}ojnTCGkcQpQ4g7`N>ykfT@S%|{W z;Q-7myPJ%?{+R3z(8j|(#}g%^qn)H@SWvF{Fi*1gv4(SvDfi}k6UT^2UTB}X6WS0} zyuv2pMS@<=L$?+oSMWTaXBaD7-*DdU-e$x4|5>X?#Vq&)P&7AC-l8+k*!VyB?+(aE zz5@OCBUG|r!TM<4q2p^`W5RChE>0BbaWRC>^9;ywfpKV_;~iz-)*I}1r~YHNxdWlK zr#&g~xH4x%(FiAAeh64$A{AItzx-#$dn=W2yxmDhN=#@>eKKvCh`N|F-P}DrJjQz% z)+=f3_jj=OQ~Yp&5eS5)hy zsCzS~kW>320F;P6vqP>v0DSbfRM3xi^(=53fAaz0nK52~7!K88L7?4$eJ;>>IVm~p zGj$;`Ze;!$-~?|mJLtvcJp^(Mw<@WW>_!7R)kZ&n=CCbu%13nzu2e+W3iof0(kr&Phf|FSA{u z%u(5N8_P1ip4vnBxv~(^2`ilyr3Psakf#K75@iaMzDL&Y8jXH+$URHP;pIL@Wur+5VkUCV zP;5BblG?UG3P@7kZQq8I^b`o9z@#k@M4mKds};|~r)BN_C*DG+eze?+&cy)~)gff$ zy);thrG+W2?Zr8CZTxr+h}j7>s#>!lfVnPQ@-%<_v9J}^yy=!{OqMTTs(cd5<=@E2 cQ z;}tL5J4I^?RK%CIACUDt^0Z_}bM(i9e(&ve-*(nE zGfF)jGHwnf1YwQM@MP0Oxm>J0BVkwBAnVeOt+1 zBqpfs6nE diff --git a/public/icons/Type=Call.png b/public/icons/Type=Call.png deleted file mode 100644 index 1b72e17bef94105e92c2ddf8069e6aee783cfd84..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 450 zcmV;z0X_bSP)kEsn1%h#3I@=PJoLy?N5zGd>%8ZmZ_u}^9aQyu{^>$WnVXAlczA#2 zUj)eikn+9M``VW>%fRL}teu({-_(6bmxBe?VSXQ=5)EG7p7 swSLA&mpsCfK>ylk+fO!aGyH$6Z&b{386xvVJpcdz07*qoM6N<$f+-5Wr~m)} diff --git a/public/icons/Type=Edit.png b/public/icons/Type=Edit.png deleted file mode 100644 index ab7333faa4f9f538ffa2ba2176bb37034b91e864..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 349 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1|)ksWqE-VXMsm#F#`j)FbFd;%$g$s6l5$8 za(7}_cTVOdki(Mh=a);xbl1ne=)a=^K^QUS{fN{%* z-G3jxu#xDylU-sN*{C^AbhYc0jWdduJ-ljY8|C7?^h&X>=J$`` z8+cCF_3#(n*Zwwl*^GT@57$U8TmQ4rDf$dUYlKgZAv4RZdAey0Tqm!H89V6NM(gx` t+a02~|IZgWX15;)gA|_X9l3v$`Nghuw?9oi`9R+@c)I$ztaD0e0szbVi#7lN diff --git a/public/icons/Type=Emoji.png b/public/icons/Type=Emoji.png deleted file mode 100644 index 30cba5f1f4e5f92b1f27ef8d3e5665886e9be12a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 570 zcmV-A0>%A_P)|8%Rzd-T=Cks}}%IATu^}q_o5eEMWaV*07V`T~LvEvS zRZ(#~=~xfbiXrExZyPkUNKHp6GL(sStIcRJmG^42ugd-f-`pU#&k_VUumAu607*qo IM6N<$f=b`+#sB~S diff --git a/public/icons/Type=Friend.png b/public/icons/Type=Friend.png deleted file mode 100644 index d3890945d45e99fdccb2c5519e743be66e9b3cb7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 486 zcmV@P)37DM{3TMHv%@4wq9%O~ z7K||u6~+mPvPYY~H3GVl2C+)hul)wOJpznDH3%hGusUFjhxIa;!2cb9^nC9*53-`_h>2rbG+%Q86GfN?^`aXcy(r2{O*i`75o z8DAl^C(LyU&i*(sMpK>BXo%7@#au82EkPZX>%1;)EqdmsgXK_L%$HN>h}PLH9T07- c6aB994bBpKl(&)VkpKVy07*qoM6N<$f@7D+X#fBK diff --git a/public/icons/Type=Go.png b/public/icons/Type=Go.png deleted file mode 100644 index f4354bb4a6ca33ec1db2df0fdfb7a6eb3fc04ac3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 346 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjoCO|{#S9GG!XV7ZFl!D-1!HlL zyA#8@b22Z19F}xPUq=Rpjs4tz5?O)#_nt0}Ar*{wr~2|W83?q-YjaPNVaz`ux}#yH zYT?tfB`?q2Yf!FWv0#c87hCSu^ilnch}o}6X}6wytxb#ydT>3}V9IhF6_ z{hxaA6*n#`Z!q=Oje0V{R^DjG|Md?yGX_80%rNOgMuUkB*8Ygr+Ar*{or`#@VHV|-KU)J#9rH}`E8GC@^ zB}XssOO7#(bGdvtrySsyluOPHyWwZRFYSGD{&^nes)F}jv0^)lSxwV}CE7M#t6;R4 zmRK^!FI8vy_I0n<^J_B;_RaV?Rnkyzvf#=kI;V5CHNOAt9eGb*DYtjc zHVN>yxIFE%Qc7ACBK$evli}t;LKsOYhCozpiqwej(5Bd z?fJ^d_Q0&#b4Kw2g@?cQHw7qNebG5p^_F#8+;mUL6S7qgQohYxD4^AD*!OVj{@7#Y vfrZoD<7BsZ$C%&EP2QGX`)aRE{VCzS%luA!^EuG~3F? diff --git a/public/icons/Type=NewFriend.png b/public/icons/Type=NewFriend.png deleted file mode 100644 index 24c7699b5c9a476f0728223efb1e1d98f474d87e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 503 zcmVqF;^a(a7R#;C`0mGNOt9>jz zxE5ZQK=(APp;T!v!d>9VJGY(p`TS)A%72`B4YGR!gYv2;BO20Nth?Pq+=FYk0RnO(aNWbjE(3qgv#0 zl3&rSEwgMJ2%qG5u-l=kTj0U6AA{8qq_N6PWU6Qh7eKtS-76y3edyA8w+S*y|3Hs& t?CP`DK)tVMJcUs;avF!Ub diff --git a/public/icons/Type=NewMessage.png b/public/icons/Type=NewMessage.png deleted file mode 100644 index ae2906619bcf69f98997b9c4dfde087bc8592717..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 471 zcmV;|0Vw{7P)1Pg1EoA0X_zqqF3L52 zP{Absvi&nm`&>GxR`=lW2(K^Ck0^^!%0Wj1Z)l(v&|~_G)bmCLxI#naeOlRVA<-Pz zwGAu)ZMvqq2^Dg@0`RYt0y^(CeVC@WG!J3~bS@BSkRk2HX`F?hQAxvX01^tWC~>Hw5Jafr zNFb1qph6f5YSU2AU_^;f0*Rdj$2ZJuI0KIDyEAvrC!Kch_D240W_SLK&>~Vp3rSqH z5}>e;h!RK`Cxp1HA;G+bR8WWo0(t{V<+2l-bI=0N%gaYcC>|*-4x>dQHW?2n@Rb82 zoX$>k7TgNCA}GWSXhCC6DDaie-uZjF*-K3co;<&B z%SWx%CxDolncO_uaJ}9`z1NBhk9eAdHh>ROex+>nKc4QMuU%911pX>6zJNB76ChwB z5;h*yTU$p^%!)t>Xg`wj+qDmWGjVG0>i`e;U;}|jqcfASO!eNLq=uGEsFcODCTt*> z(EL)l(qr$(pC+!9$H0Ny^~+JXeX|P=9tcBFAb@yhM`+Bx*Bbnxz4uPScQczH8eLQR zr_UBVc=8Qiy)Hu#ST0w(MT=`1T+^N-o<9Fpop1R^!G(cHU>c2dDD8i36DQpWJ$t@h zTR{=!wan!Ei0i2aJLts zx9=)YDv{%Reh?*sq_J!7wJSXT&x>G~f3J6E$kn@DeCqcnLeR$~i%S3>K3d=&7{QZ+ zU+2X4fJgN1Fkt)tiZ33wDKNJhuH?MzQE}SH;mz4zE@6{Ce5eDq{Hwr;=*6o=aDoD# zAf_yqeEZfaLZZf2OuT)o((Xn~Y;H63y&K2@d&UmGo=>fXa0@Xx3Cf-bo<0#_GkQCj zzY=q=X)1)c*2r_2Xo3gq+wJZ^v`*{?JwEgyWzkDQJnB|Re45h%_)}t4u=3c%DzCY^ zM4yAx^@W%XF8FosfML+cD)jexw5eCuV~%Cxa#! zS+z3DVZ)K2IO4#G*)S?;8T4+S=FFA*E#poypFWc&CK=7eYegNh0wKqgvJ>3}nN*9W zfq}XBO4xYciqrNLy!SW-?6(P4{e@`JB1*$XgD`EA6a41TW~X8qH>M5>+yWV|Z6?+2 z&2Wa(*4BrBkn*#Wu|2iV>xP6#xpIu#A9-jWaw}1*zurRg_S@Mn8B%N2RZtMK&fbYN zZ1FV>sxx=B;2C|gdM<$oB@hTZ9co6d- i=^STF*{Rn4hWHM>M9=leYZXrb0000iK~#7F#h6WK z6G0Tm-+ZL2p-C$VDuosrFcs?;LP13+dhk>)3Vwm2hyg90^q?0Ho;-L}Aqs*AD|l28 zdhn)X zPGY8#$)19)ot8ipmbDiKB=CM{0i4n~U7-yHUcIvzlSLl)>aj+kM?uik`!~Tkw|TAt zFWs6y*eb7W2BTzV>{V!Dv+=qDaY;>?8xjH{&=yFxn{|FmCW*Cvgw~tBXy;F~PaGn< z&>{6kHVDLJMFVIv=#VS7bA#*O*9g3Pv(W4Jo^!(%GIsvvVyyaIMPMTFCkRx;VVhBh zCXQ#od9Db|%WzzP2)Y?Reh`G`PeU+#sssFj014z|K3*dby;l;Vw~=vwAA@KV;O-5> zSE0Dnju*=U(R(_EpTDeBzWXf{uhfb_-H`HWytv_=Z$gPIA69mKR7R}QN=(PbQepH! zoWt(u#_$G1P>fPrZ283VU9jkw{Xd=3>4{JT_cQXjSj$8g*mL& z)RbmR9*l;MbKiwIaGuZbx6i|wOSiLH2W3x`l|zZ&!pf-qH`FJTVLs`F40i1nzdsAe#ebu;>#w$+uzd=-XB)f)M@ z))uMkyAbJ2WvSWcwill~^WmykkJ@|7B%i80I$zL>jLRw5W@x~k`dHOes=15bz3iUB zUI}ay_}lMaL~5Ez8X5&t1A(-X~*)jRy_0Cz>d7!~L)p^*VihH8beb52DhGDH< zKX5&sn&=gwP>Oo>D0)nNPIr;etd6jz1Xgo>)Ql`GL6kBXR(V?x8&6J~8*-%yUF^-@5~j#sTSAMsu^4XF?~v+0eAZ?LDJ&bAY(S{hF2f|(jkmGi!Ptz=eR zP>`l<<`_7giKT8yh{BfD8PD@&_Whde%By|;^9s}RhfI^6%)d9uHcxTgRy}*6yNAqO d*?;BUVc@K=V2h5hN&tG8!PC{xWt~$(69CY8d|?0p diff --git a/public/icons/Type=Secondary, Status=Activated, Size=40.png b/public/icons/Type=Secondary, Status=Activated, Size=40.png deleted file mode 100644 index 1c5325af902758760a7748fa141ae6be900d5531..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 798 zcmV+(1L6FMP)V3l@7*tM%V(`BdA^aMRZ+#Apnl&qKm6Ij58 zzxkdbGXrsqu@y(Z8ASpq@t3EkrzhEhcB z#bVK=7#ODvo=hgW)oRs*fR-;N@`DVckIKztGTnSW-;exzQ3gq29W+~{0EoSgD)gwp z@p#;XwJuM!*wF{WqMOa8TPl^VL&t@6!q2{uBCCbqz(3nhbp{HQQ~C$ZI;BG|o6R;b z<Vqln_7W3WPwm9>kqfLOkz|yzw0=pv94d z8$uvkk2GVkScv%RPjQ_z!!*sNs5vXqU||H2V{L4~I_XP|1)hEuH_}*>B6HH|K-u473r0SZ1!`+S;F|Kpubm(A%>--n!^{+NrAgI_UE*(K3H52;$9zqADGQxRllWl1Gqb%&l^D< zp?Qzzc@F-wCOso^TA#e)3(W^D%i^ovck;ejHM}U>Vd9vh2`f7-FMEjmO^RTv&1SPI zFUn(MPfC*qgMp2M%q2bC?Avr2Y%vz*RIAmO<*Nu@Xq?XV c;=YEs2lI?3&Xpqz-2eap07*qoM6N<$f(vGH(EtDd diff --git a/public/icons/Type=Send.png b/public/icons/Type=Send.png deleted file mode 100644 index 5864b4050d25c7134547358114cf4faca5472610..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 289 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjoCO|{#S9GG!XV7ZFl!D-1!HlL zyA#8@b22Z19F}xPUq=Rpjs4tz5?O)#ZJsWUAr*{wCk65{849?VvoNYNvd?LmTqHE1 zfRBOo!4jbf0h|vKX0-WgG-gL_`dw&#a!Gvc`nO>zp^O4IPV_wzYUdi`n>fW85X-hV`P5G81^+0xdyGdF! z!?zf=vh&|=&069-=jLRQ2ku+c|L*b3kMesey}en~ZdZe`V*#Uf&2y%6EaKt?Y8`qH gd<0?+be|K}?mzW7#&q&;pbr>4UHx3vIVCg!09+MmNB{r; diff --git a/public/icons/Type=Video.png b/public/icons/Type=Video.png deleted file mode 100644 index f7dcf2be3b06fe9681956973ca26443391ed004a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 301 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1|)ksWqE-VXMsm#F#`j)FbFd;%$g$s6l5$8 za(7}_cTVOdki(Mh=F_pR*xTQ=@9f4U5&E8KXa<;Jw( z^5^9d9D?aLe_olo<;>EgKK2t*F?S=JUHAfD?Bks6#N%eJcI%z!G;`J`YRSne*<8h} zPwkzTXCGRhaNvEFyzZYP^4zak_0&Y;Qg>}SFtI@+{%P3lz$p?Pf*zHgEH{LARI8 z9OUlAumdKI;Vst059}!s{jB1 diff --git a/public/icons/_StatusBar-battery.png b/public/icons/_StatusBar-battery.png deleted file mode 100644 index cfa050658994cfc8770e4b91666847f147ebe590..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 315 zcmeAS@N?(olHy`uVBq!ia0vp^G9Wew8<5=9&bJCkaTa()7Bet#3xhBt!>l*8o|0J>k`4>E0978G?_fFZ!cQ8Pt&A(GEDe*{B(nXhu zFP0HDUG1Ob)R=|21v`%%I?%xJ|NXtL+kLh><3DY6p8Wa#<$3cQctkB)Oe2J3`3e~G zU$XN^A6}Ov@p{X{6W9I+Hq`EDVyFrTI;~}^ z#qZqyJw4@eiA(uqmu>4dXEyB*$eXq7#ewrjfT#{Oc=^>$7@y)hRQJPe+$elF{r G5}E)^!FSUD diff --git a/public/icons/callCircle.png b/public/icons/callCircle.png deleted file mode 100644 index ce1b50109f5c19b905e004cbf36fd6d0426f8edd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 941 zcmV;e15*5nP)DR)mr9ad2f1^(+&o$}HpB0I#=DfL#-K_j{ZrBHV>sDp-j zPzs5b5K21i(khy4x+t!}{_Fi_cb468=9`I`^#{8<-^||gOViq`J?1=IWL~^b%L*>{2S)y3)R)tQ+0K92|M#jHi(sUIz7ab4{C(ACYGcL zkH<60jzooaWKQg5;$gnLOU^J>RaG@=rif%huCUy5ykdo10ILyYH9CyckPXQFZju9- z3v+I5ZTa=jBptcpHhCGDff7;)IkNm~rO0(o1hkweh5!jyX>tG@h2+jVgs;L@i-`WX zxb&H3X6NZ3nWW(d_o=4l7&!s&3?D+FD!|eYi;Q2sdc&XB6pO7<{LeN!GDl94N~P58 zEhF|U%b=MdR=&9OsqEhD$9YO756B5P7jA%CW>5M>hO;cY9w2u?1+}!xjG)Ns7t3x4 z$+C*uK~QG-{UQsaWV?R#3ib6|BBua1l28#DH8-Cu8wUbssjI7lT!I1zgdHy6KT|e# zntVEVWh;w|7tWW3VRiK<#pB!L7T|#J-~jTzp37zTo;`nIsKncMA83ZR=zlnv@PtC4 zYkYA+WYyB5P;70THaCB()Py=PPd0#+hVI=tv@4JkM&tzw z5&`E&nOox&S>%D<32a3>=^+o$b7X&Y3=k z{3(xm&PGF0x-{b zV`F2!9cwo^7e?@MK@|o#_V@Qkv~ke^#Dm*oMhK$N|e^O+k$jJLS3rcw3(2 zRgakw+jc-hBogt~*4E#@XXVr!^c9{K$(ybZ8H1;Wn5^i3)&d>?Juw!H^xX8?OM8Rx+uoD(3slz2bwP2)-E)0 z)#?siFbNtG0)>P?7kqioI72HP<~`_4PXatLciuVg-uv!-Pa#P(8jV=L-(MaK23bW> zGW^ko!(j~1c--W%!M`W`{3sj_@5kfurp&yG1hLlZ^>RGp-=c>L_sx$CcxR*$3R6I>R@-G|tD=|6X?lK8btDl-Hmmxp5euBmTrS9GfTP`R=k5O^0f+~;$0a{SU~X`v)9L0V0f6O}6+c8+ zbFSE~x8Zm(K~i!^4srh@kI1p|adN=18NrY*n7S8rUi=@N#N-BhO-M2F*dE^XiGDGME&#{ixMy?O#kZH~a{)Z~LIS z_$dsHipl`Zl#pprM9a>lrUD^mgDDm6bnbvhp0kLgsFYZlZ&ue3!lPSvS}1~*k(}iE z{?0Grpv8?d!L|!{=XwH-60_38Ota;U2<@1Y6Q1zo!G7M@$7R89MiRe0Z7Ddbg0Y3n zzLtW;VrJibn=0m!a9DbEi0B?c1xerr9HKz3 z+QO#N0pAGFBJu~hMo3|k%#VKlvA^B4IU8c>cQ#>VZHH`41p|R^GOW%_SZI3?1phOF z;0h#ut~s(L_Ta8z59o*x*pLiRi`d=bKy1})*4>p2T2QhCkHg2%;yn(~gE@>FKeX)k@(Obc493V|DU z#*Jt6L<$_(>JYV1Ww(6VD2lrKrPH!f$8u#~BQJ~1&(cG`wNJHAb#&FPY?N$rAIM;x zcY`bW>u4{y71f*P&3q(I?qy(tTZO_~y2CDCD(NrlP{UHyZyqZB6lTSg zvxsSf%~LOY3o0ArvVd*ipUWx_Ujp4a^J!Ig{O~@#Z5q2JqgHI=^8CuamNMcy>m zmE60Y7p*=%R@8f*_ZZfMdj)U2&faAoygahOH= z>`=;id+rs<81A_YcFw%^NEG?nF|0N#x2`!2ys9mqMcj0uwgjjm>@ga=XkmG6g}k=3 zTT}CNglVp*9CHd&HERws3ieC4WEC4yWuR-W5;F`H$usaDta*9Nkz*drOtr3-@AwTl zk>r4aa#f)YVRtg|I1&faC2_z|<0>Lw+wDFPJxC^%bAA3Mx=-PTz9CBJ`lX1kXZ$(3 z|IWSxn_LP!cnr1>2T&AnRQV2ec6d1Wr%nd~^; z%@21O30OY}D;cS_7)8X_?8l5f7B8_;hP(D2#5PYJK{(>PVz!@W*2yHohhVo^^Yt;c zq_pcP?Rr(d72E8qA+X1VF*_$VmCnTs?nv^Isa1$K=D+!m;rC0dBNbxVgv>3hT WLahs%>IZc+1j2rbK-UJvpZo{grEx?6 diff --git a/public/icons/dicord_2.png b/public/icons/dicord_2.png deleted file mode 100644 index bb064565ea8a03e59d665130bb7afb5d40bb354a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1538 zcmbu9`9Bj30LEvLV===bX6PO1b){S*H!D}My=v&qmHTcTm`#py6SmYyOpI5=$LMui z3o-XCcVUiDxzqCM^ZoLcW zD&$+)QSb*}cEAAuAkkmq0#I@!jz%sV`jQEtya%>?RJgs2ZHxhc$~4dqR~`U>C(FXj z_>WMowHzmJn7u^%;f4C~@z0+XCNvu58y(y**>pGcokwGi{`qQ&RJX>Fk33=|gLnbA z628RQg44riN~lytD!9h6)U7%JT7b5|l==8d4o+%yOy8%?JTviT_Vg%t^#sD3oUvoi zkzStcEF?Q(2Fdi@*ix-(>5`C67E`trIjP7GBE|Apz6Hv=MrBkOSzz-J|8?$?jFRNo z(6;;;X#$Y1Y77x~rA4XHf~LQq%x9T<;Y$e_E9*JC@DpW11>=J{**}gSTXt4A*-$ce z;JLQCjXhN-Y85@_1i@jU|+5f<4BrV_TGPK~~u1H9#0GVfNtFwGbE6Ir55<_qKPeN1mb!$(iP1d@&| z0L?Ksq{2iN7VF;M)ZK*@qiS1Hst-DE%VA2WdLcG@cEzX?BlUrsEAvWB%KJ~}2%vnC zLZJ3g6Q4~f_9Zt&tvu=6;g8W^kk$QhdUZd`v=O(<^ zj|u-v9z11iBNeYGyVQ*e&OsP_?> z8=9{v>XXJg9{^A2J62p7p|s>P^p~tS8npqbk@EM3nv)&}aV1s`jXmnAd^4+=tL*2%iFLFG>Xz4_Jq$uz~F3BX0wm zRQ*y0UvKpa~2U~GbMeDh`36Kx$bJ+N%SGGGJX$}&!CUv8**ZH-)tYNW9{Lvk= z2{n4&pxX$uy~eYv`7jVyTc+KM*YSakHw}>cl4PRPt7R`OFGV5#PcW%$b&dL|kA>V< z=57>b%q+(Yk6u16d+|H}fNwI?48qBNnH>NCa2guu!p;)K+brx(-4XgtI z01(d~F#u3#{vgC7_kmAwUtV7yu?5g^8@AIq0Pl!5RLwB zUs)*Ts^gD`+}TPI-{=WU#TCGO*UqwsQdIAOqJUDx?fNORY+WlE9hlAC)R_Rq<8=UA z9P4Z6_#c)r>7SXc_GjvGvsih!s>yaKYxE-3j}Rfybo=m`Fe-5>6{7m=diTpDAu7qQ z$lWueqZ8zW40PkY$a`QhA3*ni;iZW=SS1=oVEs{YEL@vSGGzV6>Q*F{;;bOGgHEk- z8R!yZ=0bWf_cP(EI!A1|P=k9qT!Y!rbpP0vh4RD|4u!l_A4jq&sM110#vU_>lB~uOIPr~FVMk07JV+lFiq1w7^kQ2JX(klT+t0|A1|RvYm$^35 z-xivI<+Zj3xu2WU(-v&?N^!e*6WBrvT*2pcP^L-(-zTRBdeTy|ETuIkzGvOkXcVbH%j!~$&wZi1a`<$e3i!R z9*?c&Ru(369e}`kQs0htt2R+l%^e{!9{kXuLaEi#g{k~rb@Y=!;<+Oq8s^n)UpCeT+bb=7%fW&xEWe`CXEl|xv`*Xc z3iaMq`ig=l~ePWfPU7$R4+S^7MAKzK4v zhuY>_94>CVkG18qvs2p;A>l4hOeCxeD{W55(t|*gH#{MyH#cH`rC;(3rXtK60&*bx z{FbKU>*`W^)D(H}@Z=hAF;)D_L{(;8R{8X&Ev1_6I50BM>SXNtMlr;g|1^1V_G9tS zK1NS!+U~WjM^Z)69fnSj-~DKPF%8TXVbR_~+A~B3%w6A`i2v4BgV~FQ}2kGGNbvq!d6NZXfg7nw^_dPJwclHlmDrZq}d^6 zuQW;1JlEoFK6n!R8Ab6n@w=P5y4=Jg%&D3WlgmqVim4sfz@+9^j?l};fm$t!r3;(& tvJ&BQr=u&21>{VYTYnF&|Fe$5{V_J`Ydx47aYkf-p`Mv;wYKB){{Vvw)>;4n diff --git a/public/icons/iPhoneHomeIndicator.png b/public/icons/iPhoneHomeIndicator.png deleted file mode 100644 index 57597475a2318cad8cebf363fe5ad68c785c951a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 283 zcmeAS@N?(olHy`uVBq!ia0y~yU@Ql+l{nadfk$L90|U1(2s1Lwnj--e zWGoJHcVbv~PUa<$!;&U>cv7h@-A}f%;!PCVtq=ND7bwklZ4gxF}_G&8_O=w`5 zAi$|{*h@x$^TdgzmV9!&B_<-%|IZFI*qpBn)Px5-VAp(c&w!^r>h;!Z7OXC3ESDEX zPOHA4`* + + + + + + + + diff --git a/public/images/Instagram.svg b/public/images/Instagram.svg new file mode 100644 index 0000000..3037155 --- /dev/null +++ b/public/images/Instagram.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/MobileSignal.svg b/public/images/MobileSignal.svg new file mode 100644 index 0000000..a3c8f10 --- /dev/null +++ b/public/images/MobileSignal.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/images/Wifi.svg b/public/images/Wifi.svg new file mode 100644 index 0000000..02ad206 --- /dev/null +++ b/public/images/Wifi.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/_StatusBar-battery.svg b/public/images/_StatusBar-battery.svg new file mode 100644 index 0000000..2a2b77b --- /dev/null +++ b/public/images/_StatusBar-battery.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/call.svg b/public/images/call.svg new file mode 100644 index 0000000..38318cc --- /dev/null +++ b/public/images/call.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/circleCall.svg b/public/images/circleCall.svg new file mode 100644 index 0000000..5350d60 --- /dev/null +++ b/public/images/circleCall.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/circleMessage.svg b/public/images/circleMessage.svg new file mode 100644 index 0000000..8fd3598 --- /dev/null +++ b/public/images/circleMessage.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/circlePlus.svg b/public/images/circlePlus.svg new file mode 100644 index 0000000..071006c --- /dev/null +++ b/public/images/circlePlus.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/clearSend.svg b/public/images/clearSend.svg new file mode 100644 index 0000000..25424c2 --- /dev/null +++ b/public/images/clearSend.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/dicord.svg b/public/images/dicord.svg new file mode 100644 index 0000000..67ed1ff --- /dev/null +++ b/public/images/dicord.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/dicord_2.svg b/public/images/dicord_2.svg new file mode 100644 index 0000000..a6f5aa7 --- /dev/null +++ b/public/images/dicord_2.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/dicord_3.svg b/public/images/dicord_3.svg new file mode 100644 index 0000000..2e7a9d9 --- /dev/null +++ b/public/images/dicord_3.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/discord24.svg b/public/images/discord24.svg new file mode 100644 index 0000000..6b1a3a7 --- /dev/null +++ b/public/images/discord24.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/discord32.svg b/public/images/discord32.svg new file mode 100644 index 0000000..10b126e --- /dev/null +++ b/public/images/discord32.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/discord40.svg b/public/images/discord40.svg new file mode 100644 index 0000000..ee522d5 --- /dev/null +++ b/public/images/discord40.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/discord48.svg b/public/images/discord48.svg new file mode 100644 index 0000000..f881a2b --- /dev/null +++ b/public/images/discord48.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/discord80.svg b/public/images/discord80.svg new file mode 100644 index 0000000..6616917 --- /dev/null +++ b/public/images/discord80.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/edit.svg b/public/images/edit.svg new file mode 100644 index 0000000..56139d5 --- /dev/null +++ b/public/images/edit.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/friend.svg b/public/images/friend.svg new file mode 100644 index 0000000..b6fd9c7 --- /dev/null +++ b/public/images/friend.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/iPhoneHomeIndicator.svg b/public/images/iPhoneHomeIndicator.svg new file mode 100644 index 0000000..16584bb --- /dev/null +++ b/public/images/iPhoneHomeIndicator.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/leftArrow.svg b/public/images/leftArrow.svg new file mode 100644 index 0000000..7abfda2 --- /dev/null +++ b/public/images/leftArrow.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/magnifyingGlass.svg b/public/images/magnifyingGlass.svg new file mode 100644 index 0000000..2e92865 --- /dev/null +++ b/public/images/magnifyingGlass.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/messageLarge.svg b/public/images/messageLarge.svg new file mode 100644 index 0000000..1e17a7f --- /dev/null +++ b/public/images/messageLarge.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/messageSmall.svg b/public/images/messageSmall.svg new file mode 100644 index 0000000..63622e1 --- /dev/null +++ b/public/images/messageSmall.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/newFriend.svg b/public/images/newFriend.svg new file mode 100644 index 0000000..e830ab5 --- /dev/null +++ b/public/images/newFriend.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/newMessage.svg b/public/images/newMessage.svg new file mode 100644 index 0000000..08e46cd --- /dev/null +++ b/public/images/newMessage.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/plus.svg b/public/images/plus.svg new file mode 100644 index 0000000..808eaa5 --- /dev/null +++ b/public/images/plus.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/rightarrow.svg b/public/images/rightarrow.svg new file mode 100644 index 0000000..c25f14f --- /dev/null +++ b/public/images/rightarrow.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/smileEmoji.svg b/public/images/smileEmoji.svg new file mode 100644 index 0000000..f43c401 --- /dev/null +++ b/public/images/smileEmoji.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/staleSend.svg b/public/images/staleSend.svg new file mode 100644 index 0000000..ce4b00d --- /dev/null +++ b/public/images/staleSend.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/images/video.svg b/public/images/video.svg new file mode 100644 index 0000000..9a8d227 --- /dev/null +++ b/public/images/video.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/whiteSend.svg b/public/images/whiteSend.svg new file mode 100644 index 0000000..ce9496d --- /dev/null +++ b/public/images/whiteSend.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/AppMain.tsx b/src/components/AppMain.tsx index bf11eed..b06f425 100644 --- a/src/components/AppMain.tsx +++ b/src/components/AppMain.tsx @@ -1,5 +1,6 @@ import styled from 'styled-components'; import ChatHead from '@components/fixed/ChatHead/ChatHead'; +import ChatInput from '@components/non-fixed/ChatInput/ChatInput'; import HomeIndicator from '@components/fixed/HomeIndicator/HomeIndicator'; const StyledAppMain = styled.main` @@ -13,6 +14,7 @@ export default function AppMain() { return ( + ); diff --git a/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavLeft.tsx b/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavLeft.tsx index 7f55f89..694c783 100644 --- a/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavLeft.tsx +++ b/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavLeft.tsx @@ -37,14 +37,8 @@ const StyledUserNameSpan = styled.span` export default function ChatHeadNavLeft() { return ( - - + + 김정민 ); diff --git a/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavRight.tsx b/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavRight.tsx index 692c956..971d3d8 100644 --- a/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavRight.tsx +++ b/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavRight.tsx @@ -1,4 +1,5 @@ import styled from 'styled-components'; +import * as ST from '@styles/styledComponents'; const StyledChatHeadNavRight = styled.div` height: 32px; @@ -12,21 +13,20 @@ const StyledChatHeadNavRight = styled.div` const StyledChatHeadNavRightCallImg = styled.img` width: 32px; height: 32px; + ${ST.hoverCursor} `; const StyledChatHeadNavRightBalloonImg = styled.img` width: 32px; height: 32px; + ${ST.hoverCursor} `; export default function ChatHeadNavRight() { return ( - - + + ); } diff --git a/src/components/fixed/ChatHead/IphoneStatusBar/IphoneStatusRightDiv.tsx b/src/components/fixed/ChatHead/IphoneStatusBar/IphoneStatusRightDiv.tsx index 960c01c..2b51282 100644 --- a/src/components/fixed/ChatHead/IphoneStatusBar/IphoneStatusRightDiv.tsx +++ b/src/components/fixed/ChatHead/IphoneStatusBar/IphoneStatusRightDiv.tsx @@ -15,9 +15,12 @@ const StyledIphoneStatusRightDiv = styled.div` export default function IphoneStatusRightDiv() { return ( - - - + + + ); } diff --git a/src/components/fixed/HomeIndicator/HomeIndicator.tsx b/src/components/fixed/HomeIndicator/HomeIndicator.tsx index 59d95aa..ecf5802 100644 --- a/src/components/fixed/HomeIndicator/HomeIndicator.tsx +++ b/src/components/fixed/HomeIndicator/HomeIndicator.tsx @@ -2,11 +2,14 @@ import styled from 'styled-components'; const StyledHomeIndicator = styled.div` width: 100%; - height: auto; + height: 34px; position: absolute; bottom: 0; display: flex; align-items: flex-start; + z-index: 10; + background-color: transparent; + /* opacity: 0; */ `; const StyledHomeIndicatorImage = styled.img``; @@ -14,7 +17,7 @@ const StyledHomeIndicatorImage = styled.img``; export default function HomeIndicator() { return ( - + ); } diff --git a/src/components/non-fixed/ChatInput/ChatInput.tsx b/src/components/non-fixed/ChatInput/ChatInput.tsx new file mode 100644 index 0000000..e02f1b0 --- /dev/null +++ b/src/components/non-fixed/ChatInput/ChatInput.tsx @@ -0,0 +1,22 @@ +import styled from 'styled-components'; +import ChatInputForm from '@components/non-fixed/ChatInput/ChatInputForm'; +// input 박스와 종이 비행기 UI 간의 간격 차이는 8px이고 종이비행기와 전체 박스는 16px로 어느 상황에서나 same +// 아이템 간의 간격은 8px이고 양 끝단 item과 박스 간의 간격은 16px임. 따라서 전체 상자의 padding을 그냥 16px로 고정하면 됨 + +const StyledChatInputContainer = styled.div` + width: 375px; + height: auto; + min-height: 90px; + position: absolute; + bottom: 0; + padding-bottom: 34px; + background-color: ${(props) => props.theme.color.white}; +`; + +export default function ChatInput() { + return ( + + + + ); +} diff --git a/src/components/non-fixed/ChatInput/ChatInputForm.tsx b/src/components/non-fixed/ChatInput/ChatInputForm.tsx new file mode 100644 index 0000000..cbc8610 --- /dev/null +++ b/src/components/non-fixed/ChatInput/ChatInputForm.tsx @@ -0,0 +1,45 @@ +import styled from 'styled-components'; +import * as ST from '@styles/styledComponents'; + +const StyledChatInputForm = styled.form` + width: 100%; + height: 56px; + display: flex; + align-items: center; + column-gap: 8px; + padding: 0 16px; +`; + +const StyledPlusButton = styled.img` + ${ST.hoverCursor}; +`; + +const StyledInputBox = styled.input` + outline: none; + border: none; + height: 40px; + flex-grow: 1; + flex-shrink: 0; + border-radius: 20px; + background-color: ${(props) => props.theme.color.grayMedium}; + text-indent: 16px; + font-size: 15px; + font-weight: 400; + font-family: Pretendard; +`; + +const StyledStaleSendIcon = styled.img` + ${ST.hoverCursor}; +`; + +const StyledClearSendIcon = styled.img``; + +export default function ChatInputForm() { + return ( + + + + + + ); +} diff --git a/src/styles/theme.ts b/src/styles/theme.ts index f3ca950..db74824 100644 --- a/src/styles/theme.ts +++ b/src/styles/theme.ts @@ -16,6 +16,10 @@ const theme = { body2: '15px', label: '13px', caption: '10px', + thirtyTwoPixel: '32px', + sixteenPixel: '16px', + eightPixel: '8px', + fourPixel: '4px', }, lineHeight: { h1: '150%', From 72a15f11d1fb27fa1b13ef647c28ad7073c0ed1d Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Thu, 28 Mar 2024 12:56:39 +0900 Subject: [PATCH 11/81] Feat : chat input UI make --- .../non-fixed/ChatInput/ChatInput.tsx | 1 + .../non-fixed/ChatInput/ChatInputForm.tsx | 29 +++++++++++++++---- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/components/non-fixed/ChatInput/ChatInput.tsx b/src/components/non-fixed/ChatInput/ChatInput.tsx index e02f1b0..84bd486 100644 --- a/src/components/non-fixed/ChatInput/ChatInput.tsx +++ b/src/components/non-fixed/ChatInput/ChatInput.tsx @@ -11,6 +11,7 @@ const StyledChatInputContainer = styled.div` bottom: 0; padding-bottom: 34px; background-color: ${(props) => props.theme.color.white}; + opacity: 0.95; `; export default function ChatInput() { diff --git a/src/components/non-fixed/ChatInput/ChatInputForm.tsx b/src/components/non-fixed/ChatInput/ChatInputForm.tsx index cbc8610..97ca658 100644 --- a/src/components/non-fixed/ChatInput/ChatInputForm.tsx +++ b/src/components/non-fixed/ChatInput/ChatInputForm.tsx @@ -14,18 +14,32 @@ const StyledPlusButton = styled.img` ${ST.hoverCursor}; `; -const StyledInputBox = styled.input` - outline: none; - border: none; +const StyledInputBoxContainer = styled.div` height: 40px; flex-grow: 1; flex-shrink: 0; - border-radius: 20px; - background-color: ${(props) => props.theme.color.grayMedium}; + position: relative; +`; + +const StyledInputBox = styled.input` + outline: none; + border: none; + width: 100%; + height: 100%; text-indent: 16px; font-size: 15px; font-weight: 400; font-family: Pretendard; + background-color: ${(props) => props.theme.color.grayMedium}; + border-radius: 20px; + padding: 0; +`; + +const StyledSmilingIcon = styled.img` + position: absolute; + right: 12px; + top: 8px; + bottom: 8px; `; const StyledStaleSendIcon = styled.img` @@ -38,7 +52,10 @@ export default function ChatInputForm() { return ( - + + + + ); From 7d5af1865d2a75ec766f43af510712bf05a21617 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Thu, 28 Mar 2024 13:07:04 +0900 Subject: [PATCH 12/81] =?UTF-8?q?Chore=20:=20=EC=A0=84=EC=B2=B4=20?= =?UTF-8?q?=EA=B8=B0=EB=B3=B8=20=EC=84=9C=EC=B2=B4=EB=A5=BC=20Pretendard?= =?UTF-8?q?=20=EC=B5=9C=EC=9A=B0=EC=84=A0=EC=9C=BC=EB=A1=9C=20=EB=A7=8C?= =?UTF-8?q?=EB=93=A4=EC=96=B4=EC=A3=BC=EC=97=88=EC=9D=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/index.html | 4 ++++ src/styles/globalStyles.tsx | 1 + 2 files changed, 5 insertions(+) diff --git a/public/index.html b/public/index.html index 444f3b5..b69b001 100644 --- a/public/index.html +++ b/public/index.html @@ -11,6 +11,10 @@ /> + React App diff --git a/src/styles/globalStyles.tsx b/src/styles/globalStyles.tsx index 6e3d7c6..e5c8971 100644 --- a/src/styles/globalStyles.tsx +++ b/src/styles/globalStyles.tsx @@ -10,6 +10,7 @@ const GlobalStyles = createGlobalStyle` background-color: #E5E6EB; } body { + font-family: 'Pretendard', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background-color: #FFFFFF; opacity: 0.95; margin: 0 auto; From 8a9ead6a5868e2fbd7a9aa2565de59b82ba19267 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Thu, 28 Mar 2024 13:40:27 +0900 Subject: [PATCH 13/81] Feat : Add Recoil global state management library to project MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. context/state 디렉터리에서 원하는 js파일을 통해 상태들을 정의해주고 이를 export해준다. 2. 프로젝트의 최상위 컴포넌트(CRA에서는 App.tsx 컴포넌트)에서 RecoilRoot 컴포넌트로 wrapping 3. 원하는 파일에서 1에서 정의해준 상태를 import하여 useRecoilState() 훅으로 사용. 받는 것은 useState와 매우 흡사하다. --- make.md | 2 +- package-lock.json | 25 +++++++++++++++++++ package.json | 1 + public/images/HomeIndicator.svg | 3 +++ src/App.tsx | 9 ++++--- src/components/AppMain.tsx | 2 ++ .../IphoneStatusBar/IphoneStatusBar.tsx | 2 -- .../fixed/HomeIndicator/HomeIndicator.tsx | 13 ++++++---- .../non-fixed/ChatBody/ChatBody.tsx | 13 ++++++++++ .../non-fixed/ChatInput/ChatInput.tsx | 4 +-- src/context/state/atom.js | 6 +++++ 11 files changed, 67 insertions(+), 13 deletions(-) create mode 100644 public/images/HomeIndicator.svg create mode 100644 src/components/non-fixed/ChatBody/ChatBody.tsx create mode 100644 src/context/state/atom.js diff --git a/make.md b/make.md index 1e9b421..e89182d 100644 --- a/make.md +++ b/make.md @@ -44,4 +44,4 @@ ### 해결 포인트 -1. input 박스 옆에 웃는 아이코은 span 태그 내부에 input과 이모지를 flex item으로 구성하며 해당 길이는 따라서 조정해줘야 함 +1. input 박스 옆에 웃는 아이코은 span 태그 내부에 input과 이모지를 flex item으로 구성하며 해당 길이는 따라서 조정해줘야 함 => 아예 컨테이너를 만들어서 그 안에 input 요소를 넣고 이모지는 absolute 속성으로서 해결함 diff --git a/package-lock.json b/package-lock.json index e561ae0..a9772e0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-scripts": "5.0.1", + "recoil": "^0.7.7", "styled-components": "^6.1.8", "styled-reset": "^4.5.2", "typescript": "^4.9.5", @@ -9031,6 +9032,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/hamt_plus": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hamt_plus/-/hamt_plus-1.0.2.tgz", + "integrity": "sha512-t2JXKaehnMb9paaYA7J0BX8QQAY8lwfQ9Gjf4pg/mk4krt+cmwmU652HOoWonf+7+EQV97ARPMhhVgU1ra2GhA==" + }, "node_modules/handle-thing": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", @@ -15168,6 +15174,25 @@ "node": ">=8.10.0" } }, + "node_modules/recoil": { + "version": "0.7.7", + "resolved": "https://registry.npmjs.org/recoil/-/recoil-0.7.7.tgz", + "integrity": "sha512-8Og5KPQW9LwC577Vc7Ug2P0vQshkv1y3zG3tSSkWMqkWSwHmE+by06L8JtnGocjW6gcCvfwB3YtrJG6/tWivNQ==", + "dependencies": { + "hamt_plus": "1.0.2" + }, + "peerDependencies": { + "react": ">=16.13.1" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, "node_modules/recursive-readdir": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", diff --git a/package.json b/package.json index 949b996..5297521 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-scripts": "5.0.1", + "recoil": "^0.7.7", "styled-components": "^6.1.8", "styled-reset": "^4.5.2", "typescript": "^4.9.5", diff --git a/public/images/HomeIndicator.svg b/public/images/HomeIndicator.svg new file mode 100644 index 0000000..94776d8 --- /dev/null +++ b/public/images/HomeIndicator.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/App.tsx b/src/App.tsx index 87041cd..08cae4d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,14 +2,17 @@ import { ThemeProvider } from 'styled-components'; import theme from '@styles/theme'; import GlobalStyles from '@styles/globalStyles'; import AppMain from '@components/AppMain'; +import { RecoilRoot } from 'recoil'; function App() { return ( <> - - - + + + + + ); } diff --git a/src/components/AppMain.tsx b/src/components/AppMain.tsx index b06f425..c82fbf6 100644 --- a/src/components/AppMain.tsx +++ b/src/components/AppMain.tsx @@ -1,5 +1,6 @@ import styled from 'styled-components'; import ChatHead from '@components/fixed/ChatHead/ChatHead'; +import ChatBody from '@components/non-fixed/ChatBody/ChatBody'; import ChatInput from '@components/non-fixed/ChatInput/ChatInput'; import HomeIndicator from '@components/fixed/HomeIndicator/HomeIndicator'; @@ -14,6 +15,7 @@ export default function AppMain() { return ( + diff --git a/src/components/fixed/ChatHead/IphoneStatusBar/IphoneStatusBar.tsx b/src/components/fixed/ChatHead/IphoneStatusBar/IphoneStatusBar.tsx index 11607f8..05bb8cd 100644 --- a/src/components/fixed/ChatHead/IphoneStatusBar/IphoneStatusBar.tsx +++ b/src/components/fixed/ChatHead/IphoneStatusBar/IphoneStatusBar.tsx @@ -3,8 +3,6 @@ import IphoneStatusLeftDiv from '@components/fixed/ChatHead/IphoneStatusBar/Ipho import IphoneStatusRightDiv from '@components/fixed/ChatHead/IphoneStatusBar/IphoneStatusRightDiv'; const StyledIphoneStatusBar = styled.div` - /* position: absolute; */ - /* top: 0; */ width: 375px; height: 47px; display: flex; diff --git a/src/components/fixed/HomeIndicator/HomeIndicator.tsx b/src/components/fixed/HomeIndicator/HomeIndicator.tsx index ecf5802..e3e5247 100644 --- a/src/components/fixed/HomeIndicator/HomeIndicator.tsx +++ b/src/components/fixed/HomeIndicator/HomeIndicator.tsx @@ -5,19 +5,22 @@ const StyledHomeIndicator = styled.div` height: 34px; position: absolute; bottom: 0; - display: flex; - align-items: flex-start; z-index: 10; background-color: transparent; - /* opacity: 0; */ `; -const StyledHomeIndicatorImage = styled.img``; +const StyledHomeIndicatorImage = styled.img` + position: absolute; + top: 21px; + right: 120px; + bottom: 8px; + left: 121px; +`; export default function HomeIndicator() { return ( - + ); } diff --git a/src/components/non-fixed/ChatBody/ChatBody.tsx b/src/components/non-fixed/ChatBody/ChatBody.tsx new file mode 100644 index 0000000..54bc32a --- /dev/null +++ b/src/components/non-fixed/ChatBody/ChatBody.tsx @@ -0,0 +1,13 @@ +import styled from 'styled-components'; + +const StyledChatBodyContainer = styled.div` + flex-grow: 1; +`; + +export default function ChatBody() { + return ( + + this is chat body container + + ); +} diff --git a/src/components/non-fixed/ChatInput/ChatInput.tsx b/src/components/non-fixed/ChatInput/ChatInput.tsx index 84bd486..c6026e8 100644 --- a/src/components/non-fixed/ChatInput/ChatInput.tsx +++ b/src/components/non-fixed/ChatInput/ChatInput.tsx @@ -7,8 +7,8 @@ const StyledChatInputContainer = styled.div` width: 375px; height: auto; min-height: 90px; - position: absolute; - bottom: 0; + /* position: absolute; */ + /* bottom: 0; */ padding-bottom: 34px; background-color: ${(props) => props.theme.color.white}; opacity: 0.95; diff --git a/src/context/state/atom.js b/src/context/state/atom.js new file mode 100644 index 0000000..14fd10d --- /dev/null +++ b/src/context/state/atom.js @@ -0,0 +1,6 @@ +import { atom } from 'recoil'; + +// export const testState = atom({ +// key: 'seungwanTest', +// default: 'hi seungwan', +// }); From 654f930b1439e670c3fee25df63b60be63ba92f9 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Thu, 28 Mar 2024 14:13:59 +0900 Subject: [PATCH 14/81] Chore : make dummy data file in public directory --- src/utils/makeTimeString.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/utils/makeTimeString.js diff --git a/src/utils/makeTimeString.js b/src/utils/makeTimeString.js new file mode 100644 index 0000000..09910be --- /dev/null +++ b/src/utils/makeTimeString.js @@ -0,0 +1,16 @@ +function toISOString(year, month, day, hour, minute) { + // JavaScript의 Date 객체는 월을 0부터 시작으로 카운트하기 때문에, 월에서 1을 빼줍니다. + // Date 객체를 생성할 때 UTC 시간으로 설정하기 위해 Date.UTC 메서드를 사용합니다. + const date = new Date(Date.UTC(year, month - 1, day, hour, minute)); + + // ISO 8601 형식의 문자열로 변환 + return date.toISOString(); +} + +// 함수 사용 예시 +console.log(toISOString(2024, 3, 19, 20, 45)); +console.log(toISOString(2024, 3, 19, 20, 48)); +console.log(toISOString(2024, 3, 19, 20, 49)); +console.log(toISOString(2024, 3, 19, 20, 51)); +console.log(toISOString(2024, 3, 19, 20, 52)); +console.log(toISOString(2024, 3, 20, 1, 32)); From 3f32de03d01b47f7ca6f8c41f2af3e5c4228276b Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Thu, 28 Mar 2024 14:14:54 +0900 Subject: [PATCH 15/81] Chore : recommit of dummy data --- public/Dummy/Dummy.json | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 public/Dummy/Dummy.json diff --git a/public/Dummy/Dummy.json b/public/Dummy/Dummy.json new file mode 100644 index 0000000..033adf2 --- /dev/null +++ b/public/Dummy/Dummy.json @@ -0,0 +1,38 @@ +[ + { + "from": 2, + "like": false, + "content": "동해물과 백두산이 마르고 닳도록 하느님이 보우하사 우리나라 만세", + "createdAt": "2024-03-19T20:45:00.000Z" + }, + { + "from": 1, + "like": false, + "content": "무궁화 삼천리 화려강산", + "createdAt": "2024-03-19T20:48:00.000Z" + }, + { + "from": 2, + "like": false, + "content": "대한사람 대한으로 길이 보전하세", + "createdAt": "2024-03-19T20:49:00.000Z" + }, + { + "from": 1, + "like": false, + "content": "남산 위에 저 소나무 철갑을 두른 듯 바람서리 불변함은 우리 기상일세 무궁화 삼천리 화려강산", + "createdAt": "2024-03-19T20:51:00.000Z" + }, + { + "from": 2, + "like": false, + "content": "대한사람 대한으로 길이 보전하세", + "createdAt": "2024-03-19T20:52:00.000Z" + }, + { + "from": 1, + "like": false, + "content": "가을 하늘 공활한데 높고 구름 없이밝은 달은 우리 가슴 일편단심일세무궁화 삼천리 화려 강산대한 사람 대한으로 길이 보전하세", + "createdAt": "2024-03-20T01:32:00.000Z" + } +] From 2d9645d002cb0c39fc059f7a5cf810e00e16add5 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Thu, 28 Mar 2024 14:47:23 +0900 Subject: [PATCH 16/81] Feat : make date divider UI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. before와 after psuedo element를 이용하여 만듦. 주의할 점은 왼쪽으로 위치하려면 -8px, 오른쪽은 -8px 느낌으로 해야 겹치지 않고 보여줄 수 있음 --- .../non-fixed/ChatBody/ChatBody.tsx | 6 ++- .../ChatBody/DateDivider/DateDivider.tsx | 37 +++++++++++++++++++ src/styles/styledComponents.ts | 9 +++++ src/types/type.tsx | 3 ++ 4 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 src/components/non-fixed/ChatBody/DateDivider/DateDivider.tsx create mode 100644 src/types/type.tsx diff --git a/src/components/non-fixed/ChatBody/ChatBody.tsx b/src/components/non-fixed/ChatBody/ChatBody.tsx index 54bc32a..7cdd721 100644 --- a/src/components/non-fixed/ChatBody/ChatBody.tsx +++ b/src/components/non-fixed/ChatBody/ChatBody.tsx @@ -1,13 +1,17 @@ import styled from 'styled-components'; +import DateDivider from '@components/non-fixed/ChatBody/DateDivider/DateDivider'; const StyledChatBodyContainer = styled.div` flex-grow: 1; + margin-top: 16px; + margin-right: 16px; + margin-left: 16px; `; export default function ChatBody() { return ( - this is chat body container + ); } diff --git a/src/components/non-fixed/ChatBody/DateDivider/DateDivider.tsx b/src/components/non-fixed/ChatBody/DateDivider/DateDivider.tsx new file mode 100644 index 0000000..3db4b1e --- /dev/null +++ b/src/components/non-fixed/ChatBody/DateDivider/DateDivider.tsx @@ -0,0 +1,37 @@ +import styled from 'styled-components'; +import { dateStringProps } from 'types/type'; +import { dateBeforeAfter } from '@styles/styledComponents'; + +const StyledDateDividerContainer = styled.div` + width: 100%; + height: 20px; + display: flex; + justify-content: center; + align-items: center; + position: relative; +`; + +const StyledDateDivider = styled.div` + &::before { + ${dateBeforeAfter} + left: -8px; + } + + &::after { + ${dateBeforeAfter} + right: -8px; + } + font-size: 13px; + font-weight: 500; + line-height: 19.5px; +`; + +export default function DateDivider({ dateString }: dateStringProps) { + const [year, month, day] = dateString.slice(0, 10).split('-'); + + return ( + + {`${year}년 ${month}월 ${day}일`} + + ); +} diff --git a/src/styles/styledComponents.ts b/src/styles/styledComponents.ts index 46b0e06..aa60478 100644 --- a/src/styles/styledComponents.ts +++ b/src/styles/styledComponents.ts @@ -5,3 +5,12 @@ export const hoverCursor = css` cursor: pointer; } `; + +export const dateBeforeAfter = css` + content: ''; + position: absolute; + top: 50%; + width: 117px; + height: 1px; + background-color: ${(props) => props.theme.color.grayMedium}; +`; diff --git a/src/types/type.tsx b/src/types/type.tsx new file mode 100644 index 0000000..401d26d --- /dev/null +++ b/src/types/type.tsx @@ -0,0 +1,3 @@ +export type dateStringProps = { + dateString: string; +}; From 9cc166aa79f69058a77ebf8918cd854c98ee2573 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Thu, 28 Mar 2024 15:54:43 +0900 Subject: [PATCH 17/81] Feat : make message div UI --- .../non-fixed/ChatBody/ChatBody.tsx | 4 ++ .../non-fixed/ChatBody/ChatLog/ChatLog.tsx | 19 +++++++ .../ChatLog/ChatLogLeft/ChatLogLeft.tsx | 26 +++++++++ .../ChatLog/ChatLogRight/ChatLogRight.tsx | 55 +++++++++++++++++++ .../ChatBody/DateDivider/DateDivider.tsx | 2 + src/styles/styledComponents.ts | 4 ++ src/types/type.tsx | 4 ++ 7 files changed, 114 insertions(+) create mode 100644 src/components/non-fixed/ChatBody/ChatLog/ChatLog.tsx create mode 100644 src/components/non-fixed/ChatBody/ChatLog/ChatLogLeft/ChatLogLeft.tsx create mode 100644 src/components/non-fixed/ChatBody/ChatLog/ChatLogRight/ChatLogRight.tsx diff --git a/src/components/non-fixed/ChatBody/ChatBody.tsx b/src/components/non-fixed/ChatBody/ChatBody.tsx index 7cdd721..80935df 100644 --- a/src/components/non-fixed/ChatBody/ChatBody.tsx +++ b/src/components/non-fixed/ChatBody/ChatBody.tsx @@ -1,5 +1,7 @@ +import { useEffect } from 'react'; import styled from 'styled-components'; import DateDivider from '@components/non-fixed/ChatBody/DateDivider/DateDivider'; +import ChatLog from '@components/non-fixed/ChatBody/ChatLog/ChatLog'; const StyledChatBodyContainer = styled.div` flex-grow: 1; @@ -9,9 +11,11 @@ const StyledChatBodyContainer = styled.div` `; export default function ChatBody() { + // 여기에서 json 데이터를 불러와서 날짜별로 쪼갠다. 그리고 구분선, 메시지,메시지, 다시 구분선 메시지 메시지 느낌으로 나눠준다 return ( + ); } diff --git a/src/components/non-fixed/ChatBody/ChatLog/ChatLog.tsx b/src/components/non-fixed/ChatBody/ChatLog/ChatLog.tsx new file mode 100644 index 0000000..6bb2828 --- /dev/null +++ b/src/components/non-fixed/ChatBody/ChatLog/ChatLog.tsx @@ -0,0 +1,19 @@ +import styled from 'styled-components'; +import ChatLogLeft from '@components/non-fixed/ChatBody/ChatLog/ChatLogLeft/ChatLogLeft'; +import ChatLogRight from '@components/non-fixed/ChatBody/ChatLog/ChatLogRight/ChatLogRight'; + +const StyledChatLogContainer = styled.div` + width: 100%; + height: fit-content; + display: flex; + column-gap: 8px; +`; + +export default function ChatLog() { + return ( + + + + + ); +} diff --git a/src/components/non-fixed/ChatBody/ChatLog/ChatLogLeft/ChatLogLeft.tsx b/src/components/non-fixed/ChatBody/ChatLog/ChatLogLeft/ChatLogLeft.tsx new file mode 100644 index 0000000..ee0364e --- /dev/null +++ b/src/components/non-fixed/ChatBody/ChatLog/ChatLogLeft/ChatLogLeft.tsx @@ -0,0 +1,26 @@ +import styled from 'styled-components'; +import { isMessageOwnerEqualsWithModeProps } from 'types/type'; + +const StyledChatLogLeft = styled.div` + width: auto; + height: 100%; +`; + +const StyledChatDiscordLogo = styled.img``; + +export default function ChatLogLeft({ + isEqual, +}: isMessageOwnerEqualsWithModeProps) { + return ( + + {isEqual === true ? ( + + ) : ( + + )} + + ); +} diff --git a/src/components/non-fixed/ChatBody/ChatLog/ChatLogRight/ChatLogRight.tsx b/src/components/non-fixed/ChatBody/ChatLog/ChatLogRight/ChatLogRight.tsx new file mode 100644 index 0000000..eba1693 --- /dev/null +++ b/src/components/non-fixed/ChatBody/ChatLog/ChatLogRight/ChatLogRight.tsx @@ -0,0 +1,55 @@ +import styled from 'styled-components'; + +const StyledChatLogRightContainer = styled.div` + display: flex; + flex-direction: column; +`; + +const StyledNameAndMessageContainer = styled.div` + display: flex; + height: 20px; + justify-content: flex-start; + column-gap: 4px; +`; + +const StyledNameSpan = styled.div` + padding-top: 4px; + padding-bottom: 1px; + font-size: 13px; + font-weight: 500; + height: 100%; + color: ${(props) => props.theme.color.grayDark}; +`; + +const StyledTimeSpan = styled.div` + padding-top: 4px; + padding-bottom: 1px; + display: flex; + align-items: center; + flex-grow: 1; + font-size: 10px; + font-weight: 500; + color: ${(props) => props.theme.color.grayDark}; +`; + +const StyledMessageContentContainer = styled.div` + height: fit-content; + flex-grow: 1; + font-size: ${(props) => props.theme.textStyle.fontSize.body2}; + font-weight: 400; + color: ${(props) => props.theme.color.black}; + line-height: 22.5px; +`; +export default function ChatLogRight() { + return ( + + + 김정민 + 20:45 + + + 동해물과 백두산이 마르고 닳도록 하느님이 보우하사 우리나라 만세 + + + ); +} diff --git a/src/components/non-fixed/ChatBody/DateDivider/DateDivider.tsx b/src/components/non-fixed/ChatBody/DateDivider/DateDivider.tsx index 3db4b1e..c0f40ea 100644 --- a/src/components/non-fixed/ChatBody/DateDivider/DateDivider.tsx +++ b/src/components/non-fixed/ChatBody/DateDivider/DateDivider.tsx @@ -1,6 +1,7 @@ import styled from 'styled-components'; import { dateStringProps } from 'types/type'; import { dateBeforeAfter } from '@styles/styledComponents'; +import { chatBodyDivElementGap } from '@styles/styledComponents'; const StyledDateDividerContainer = styled.div` width: 100%; @@ -9,6 +10,7 @@ const StyledDateDividerContainer = styled.div` justify-content: center; align-items: center; position: relative; + ${chatBodyDivElementGap} `; const StyledDateDivider = styled.div` diff --git a/src/styles/styledComponents.ts b/src/styles/styledComponents.ts index aa60478..4c6c489 100644 --- a/src/styles/styledComponents.ts +++ b/src/styles/styledComponents.ts @@ -14,3 +14,7 @@ export const dateBeforeAfter = css` height: 1px; background-color: ${(props) => props.theme.color.grayMedium}; `; + +export const chatBodyDivElementGap = css` + margin-bottom: 24px; +`; diff --git a/src/types/type.tsx b/src/types/type.tsx index 401d26d..fb33133 100644 --- a/src/types/type.tsx +++ b/src/types/type.tsx @@ -1,3 +1,7 @@ export type dateStringProps = { dateString: string; }; + +export type isMessageOwnerEqualsWithModeProps = { + isEqual: boolean; +}; From 9453e49158f56ffb2b8b8cb20596a4b2b462dcdc Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Thu, 28 Mar 2024 18:01:15 +0900 Subject: [PATCH 18/81] =?UTF-8?q?Build=20:=20npm=20build=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20package.json=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 5297521..7748e77 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "build": "craco build", "test": "craco test", "eject": "craco eject", + "serve": "serve -s build", "push": "git push origin Programming-Seungwan:Programming-Seungwan" }, "eslintConfig": { From 2926f21873d587d6cac956d14c6c4d6fe0899d49 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Fri, 29 Mar 2024 01:23:59 +0900 Subject: [PATCH 19/81] Feat : make entire ChatBody UI --- public/images/discordGreen40.svg | 9 +++ .../non-fixed/ChatBody/ChatBody.tsx | 60 +++++++++++++++++-- .../non-fixed/ChatBody/ChatLog/ChatLog.tsx | 18 +++++- .../ChatLog/ChatLogLeft/ChatLogLeft.tsx | 7 +-- .../ChatLog/ChatLogRight/ChatLogRight.tsx | 19 ++++-- .../ChatBody/DateDivider/DateDivider.tsx | 4 +- .../OneDateContainer/OneDateContainer.tsx | 38 ++++++++++++ .../non-fixed/ChatInput/ChatInput.tsx | 2 - src/context/state/atom.js | 6 -- src/context/state/atom.ts | 18 ++++++ src/type/type.tsx | 21 +++++++ src/types/type.tsx | 7 --- src/utils/sortArrayByDate.js | 8 +++ tsconfig.paths.json | 3 +- 14 files changed, 184 insertions(+), 36 deletions(-) create mode 100644 public/images/discordGreen40.svg create mode 100644 src/components/non-fixed/ChatBody/OneDateContainer/OneDateContainer.tsx delete mode 100644 src/context/state/atom.js create mode 100644 src/context/state/atom.ts create mode 100644 src/type/type.tsx delete mode 100644 src/types/type.tsx create mode 100644 src/utils/sortArrayByDate.js diff --git a/public/images/discordGreen40.svg b/public/images/discordGreen40.svg new file mode 100644 index 0000000..7a6dcbe --- /dev/null +++ b/public/images/discordGreen40.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/components/non-fixed/ChatBody/ChatBody.tsx b/src/components/non-fixed/ChatBody/ChatBody.tsx index 80935df..398a9b7 100644 --- a/src/components/non-fixed/ChatBody/ChatBody.tsx +++ b/src/components/non-fixed/ChatBody/ChatBody.tsx @@ -1,7 +1,14 @@ +import { useRecoilState } from 'recoil'; +import { + messageDataState, + messageDateArrayState, + userNumberState, +} from '@context/state/atom'; import { useEffect } from 'react'; import styled from 'styled-components'; -import DateDivider from '@components/non-fixed/ChatBody/DateDivider/DateDivider'; -import ChatLog from '@components/non-fixed/ChatBody/ChatLog/ChatLog'; +import OneDateContainer from '@components/non-fixed/ChatBody/OneDateContainer/OneDateContainer'; +import { messageDataObject } from '@_type/type'; +import sortByDate from '@utils/sortArrayByDate'; const StyledChatBodyContainer = styled.div` flex-grow: 1; @@ -12,10 +19,55 @@ const StyledChatBodyContainer = styled.div` export default function ChatBody() { // 여기에서 json 데이터를 불러와서 날짜별로 쪼갠다. 그리고 구분선, 메시지,메시지, 다시 구분선 메시지 메시지 느낌으로 나눠준다 + const [messageData, setMessageData] = useRecoilState(messageDataState); + const [messageDateArray, setMessageDateArray] = useRecoilState( + messageDateArrayState + ); + + // 처음 chatBody 컴포넌트가 DOM에 마운트 되면 json 파일로부터 정보를 가져온다. + useEffect(() => { + async function loadMessageData() { + try { + const tmpDateArray: string[] = []; + const tmpMessageDataObject: messageDataObject = {}; + const response = await fetch('/Dummy/Dummy.json'); + const messageJsonData = await response.json(); + for (const messageData of messageJsonData) { + const { content, createdAt, from, like } = messageData; + const slicedCreatedDate: string = createdAt.slice(0, 10); + if (tmpMessageDataObject[slicedCreatedDate] === undefined) { + tmpMessageDataObject[slicedCreatedDate] = []; + } + + tmpMessageDataObject[slicedCreatedDate].push({ + content: content, + createdAt: createdAt, + createdDate: slicedCreatedDate, + from: from, + like: like, + }); + + if (!tmpDateArray.includes(slicedCreatedDate)) { + tmpDateArray.push(slicedCreatedDate); + } + } + // 기존의 날짜 배열을 오름차순으로 정렬 + tmpDateArray.sort(sortByDate); + setMessageData(tmpMessageDataObject); + setMessageDateArray(tmpDateArray); + } catch (error) { + console.log('error is : ', error); + } + } + + loadMessageData(); + }, []); + return ( - - + {messageDateArray.map((messageDate) => { + return ; + })} ); } diff --git a/src/components/non-fixed/ChatBody/ChatLog/ChatLog.tsx b/src/components/non-fixed/ChatBody/ChatLog/ChatLog.tsx index 6bb2828..7937528 100644 --- a/src/components/non-fixed/ChatBody/ChatLog/ChatLog.tsx +++ b/src/components/non-fixed/ChatBody/ChatLog/ChatLog.tsx @@ -1,19 +1,31 @@ import styled from 'styled-components'; import ChatLogLeft from '@components/non-fixed/ChatBody/ChatLog/ChatLogLeft/ChatLogLeft'; import ChatLogRight from '@components/non-fixed/ChatBody/ChatLog/ChatLogRight/ChatLogRight'; +import { chatBodyDivElementGap } from '@styles/styledComponents'; const StyledChatLogContainer = styled.div` width: 100%; height: fit-content; display: flex; column-gap: 8px; + ${chatBodyDivElementGap} `; -export default function ChatLog() { +export default function ChatLog({ + isEqual, + from, + createdAt, + content, +}: { + isEqual: boolean; + from: number; + createdAt: string; + content: string; +}) { return ( - - + + ); } diff --git a/src/components/non-fixed/ChatBody/ChatLog/ChatLogLeft/ChatLogLeft.tsx b/src/components/non-fixed/ChatBody/ChatLog/ChatLogLeft/ChatLogLeft.tsx index ee0364e..0a6cf1c 100644 --- a/src/components/non-fixed/ChatBody/ChatLog/ChatLogLeft/ChatLogLeft.tsx +++ b/src/components/non-fixed/ChatBody/ChatLog/ChatLogLeft/ChatLogLeft.tsx @@ -1,5 +1,5 @@ import styled from 'styled-components'; -import { isMessageOwnerEqualsWithModeProps } from 'types/type'; +import { isMessageOwnerEqualsWithModeProps } from 'type/type'; const StyledChatLogLeft = styled.div` width: auto; @@ -16,10 +16,7 @@ export default function ChatLogLeft({ {isEqual === true ? ( ) : ( - + )} ); diff --git a/src/components/non-fixed/ChatBody/ChatLog/ChatLogRight/ChatLogRight.tsx b/src/components/non-fixed/ChatBody/ChatLog/ChatLogRight/ChatLogRight.tsx index eba1693..0d6ebd8 100644 --- a/src/components/non-fixed/ChatBody/ChatLog/ChatLogRight/ChatLogRight.tsx +++ b/src/components/non-fixed/ChatBody/ChatLog/ChatLogRight/ChatLogRight.tsx @@ -40,16 +40,23 @@ const StyledMessageContentContainer = styled.div` color: ${(props) => props.theme.color.black}; line-height: 22.5px; `; -export default function ChatLogRight() { +export default function ChatLogRight({ + from, + createdAt, + content, +}: { + from: number; + createdAt: string; + content: string; +}) { + const createdHourMinute = createdAt.slice(11, 16); return ( - 김정민 - 20:45 + {from === 2 ? '김정민' : '김승완'} + {createdHourMinute} - - 동해물과 백두산이 마르고 닳도록 하느님이 보우하사 우리나라 만세 - + {content} ); } diff --git a/src/components/non-fixed/ChatBody/DateDivider/DateDivider.tsx b/src/components/non-fixed/ChatBody/DateDivider/DateDivider.tsx index c0f40ea..7c7dc09 100644 --- a/src/components/non-fixed/ChatBody/DateDivider/DateDivider.tsx +++ b/src/components/non-fixed/ChatBody/DateDivider/DateDivider.tsx @@ -1,5 +1,5 @@ import styled from 'styled-components'; -import { dateStringProps } from 'types/type'; +import { dateStringProps } from 'type/type'; import { dateBeforeAfter } from '@styles/styledComponents'; import { chatBodyDivElementGap } from '@styles/styledComponents'; @@ -29,7 +29,7 @@ const StyledDateDivider = styled.div` `; export default function DateDivider({ dateString }: dateStringProps) { - const [year, month, day] = dateString.slice(0, 10).split('-'); + const [year, month, day] = dateString.split('-'); return ( diff --git a/src/components/non-fixed/ChatBody/OneDateContainer/OneDateContainer.tsx b/src/components/non-fixed/ChatBody/OneDateContainer/OneDateContainer.tsx new file mode 100644 index 0000000..6a9283d --- /dev/null +++ b/src/components/non-fixed/ChatBody/OneDateContainer/OneDateContainer.tsx @@ -0,0 +1,38 @@ +import styled from 'styled-components'; +import { useRecoilState } from 'recoil'; +import DateDivider from '@components/non-fixed/ChatBody/DateDivider/DateDivider'; +import { userNumberState, messageDataState } from '@context/state/atom'; +import ChatLog from '@components/non-fixed/ChatBody/ChatLog/ChatLog'; + +const StyledOneDateContainer = styled.div` + width: 100%; + height: auto; +`; + +export default function OneDateContainer({ + messageDate, +}: { + messageDate: string; +}) { + const [userNumber, setUserNumber] = useRecoilState(userNumberState); + const [messageData, setMessageData] = useRecoilState(messageDataState); + + const oneDateMessagData = messageData[messageDate]; + + return ( + + + {oneDateMessagData.map((data) => { + return ( + + ); + })} + + ); +} diff --git a/src/components/non-fixed/ChatInput/ChatInput.tsx b/src/components/non-fixed/ChatInput/ChatInput.tsx index c6026e8..0789a30 100644 --- a/src/components/non-fixed/ChatInput/ChatInput.tsx +++ b/src/components/non-fixed/ChatInput/ChatInput.tsx @@ -7,8 +7,6 @@ const StyledChatInputContainer = styled.div` width: 375px; height: auto; min-height: 90px; - /* position: absolute; */ - /* bottom: 0; */ padding-bottom: 34px; background-color: ${(props) => props.theme.color.white}; opacity: 0.95; diff --git a/src/context/state/atom.js b/src/context/state/atom.js deleted file mode 100644 index 14fd10d..0000000 --- a/src/context/state/atom.js +++ /dev/null @@ -1,6 +0,0 @@ -import { atom } from 'recoil'; - -// export const testState = atom({ -// key: 'seungwanTest', -// default: 'hi seungwan', -// }); diff --git a/src/context/state/atom.ts b/src/context/state/atom.ts new file mode 100644 index 0000000..3b9a2e2 --- /dev/null +++ b/src/context/state/atom.ts @@ -0,0 +1,18 @@ +import { atom } from 'recoil'; +import { messageDataObject } from '@_type/type'; + +export const messageDataState = atom({ + key: 'messageData', + default: {} as messageDataObject, +}); + +export const messageDateArrayState = atom({ + key: 'messageDateArray', + default: [] as string[], +}); + +// 추후에 유저들을 identifier 숫자로 구분할 것이기 때문에 확장성을 고려함 +export const userNumberState = atom({ + key: 'userNumber', + default: 1, +}); diff --git a/src/type/type.tsx b/src/type/type.tsx new file mode 100644 index 0000000..d7e415a --- /dev/null +++ b/src/type/type.tsx @@ -0,0 +1,21 @@ +export type dateStringProps = { + dateString: string; +}; + +export type isMessageOwnerEqualsWithModeProps = { + isEqual: boolean; +}; + +export type processedMessageData = { + content: string; + createdAt: string; + createdDate: string; + from: number; + like: boolean; +}; + +export type processedMessageDataArray = processedMessageData[]; + +export interface messageDataObject { + [key: string]: processedMessageDataArray; +} diff --git a/src/types/type.tsx b/src/types/type.tsx deleted file mode 100644 index fb33133..0000000 --- a/src/types/type.tsx +++ /dev/null @@ -1,7 +0,0 @@ -export type dateStringProps = { - dateString: string; -}; - -export type isMessageOwnerEqualsWithModeProps = { - isEqual: boolean; -}; diff --git a/src/utils/sortArrayByDate.js b/src/utils/sortArrayByDate.js new file mode 100644 index 0000000..9076e3d --- /dev/null +++ b/src/utils/sortArrayByDate.js @@ -0,0 +1,8 @@ +export default function sortByDate(a, b) { + // 'yyyy-mm-dd' 형식의 문자열을 날짜로 변환하여 비교 + const dateA = new Date(a); + const dateB = new Date(b); + + // 오름차순 정렬 + return dateA - dateB; +} diff --git a/tsconfig.paths.json b/tsconfig.paths.json index 16ae388..46cbf16 100644 --- a/tsconfig.paths.json +++ b/tsconfig.paths.json @@ -8,7 +8,8 @@ "@context/*": ["./context/*"], "@hooks/*": ["./hooks/*"], "@utils/*": ["./utils/*"], - "@pages/*": ["./pages/*"] + "@pages/*": ["./pages/*"], + "@_type/*": ["./type/*"] } } } From 721534934c8037359c783b70950acf9a60be002e Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Fri, 29 Mar 2024 01:32:03 +0900 Subject: [PATCH 20/81] Feat : make toggle user change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 위의 디스코드 버튼이나 이름을 누르면 사용자가 전환되고 관련 UI도 함께 바뀜 --- .../ChatHead/ChatHeadNav/ChatHeadNavLeft.tsx | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavLeft.tsx b/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavLeft.tsx index 694c783..65e4516 100644 --- a/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavLeft.tsx +++ b/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavLeft.tsx @@ -1,5 +1,7 @@ import styled from 'styled-components'; import * as ST from '@styles/styledComponents'; +import { useRecoilState } from 'recoil'; +import { userNumberState } from '@context/state/atom'; const StyledChatHeadNavLeft = styled.div` width: 122px; @@ -35,11 +37,31 @@ const StyledUserNameSpan = styled.span` `; export default function ChatHeadNavLeft() { + const [userNumber, setUserNumber] = useRecoilState(userNumberState); + + function handleClickChangeUserMode() { + if (userNumber === 1) { + setUserNumber(2); + } else if (userNumber === 2) { + setUserNumber(1); + } + } return ( - - 김정민 + + {userNumber === 1 ? ( + + 김정민 + + ) : ( + + 김승완 + + )} ); } From a5a2bb0008a27cb29577a63a411d2d01e9003c1f Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Fri, 29 Mar 2024 01:45:27 +0900 Subject: [PATCH 21/81] =?UTF-8?q?Fix=20:=20input=20=EB=B0=95=EC=8A=A4?= =?UTF-8?q?=EC=97=90=20=ED=85=8D=EC=8A=A4=ED=8A=B8=EA=B0=80=20=EB=A7=8E?= =?UTF-8?q?=EC=95=84=EC=A7=80=EB=A9=B4=20=EC=9D=B4=EB=AA=A8=EC=A7=80=20?= =?UTF-8?q?=EC=95=84=EB=9E=98=EB=A1=9C=20=EB=82=B4=EB=A0=A4=EA=B0=80?= =?UTF-8?q?=EB=8A=94=20=EA=B2=83=EC=9D=84=20padding-right=20=EC=86=8D?= =?UTF-8?q?=EC=84=B1=EC=9D=84=20=EC=9D=B4=EC=9A=A9=ED=95=B4=20content-box?= =?UTF-8?q?=EC=9D=98=20=ED=81=AC=EA=B8=B0=EB=A5=BC=20=EC=A0=9C=ED=95=9C?= =?UTF-8?q?=ED=95=B4=EC=A3=BC=EC=97=88=EC=9D=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/non-fixed/ChatInput/ChatInputForm.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/non-fixed/ChatInput/ChatInputForm.tsx b/src/components/non-fixed/ChatInput/ChatInputForm.tsx index 97ca658..54d9772 100644 --- a/src/components/non-fixed/ChatInput/ChatInputForm.tsx +++ b/src/components/non-fixed/ChatInput/ChatInputForm.tsx @@ -1,5 +1,6 @@ import styled from 'styled-components'; import * as ST from '@styles/styledComponents'; +import { useRef, forwardRef } from 'react'; const StyledChatInputForm = styled.form` width: 100%; @@ -33,6 +34,7 @@ const StyledInputBox = styled.input` background-color: ${(props) => props.theme.color.grayMedium}; border-radius: 20px; padding: 0; + padding-right: 40px; // 텍스트가 스마일 표시를 넘어가지 않게 `; const StyledSmilingIcon = styled.img` @@ -43,12 +45,14 @@ const StyledSmilingIcon = styled.img` `; const StyledStaleSendIcon = styled.img` - ${ST.hoverCursor}; + /* ${ST.hoverCursor}; */ `; const StyledClearSendIcon = styled.img``; export default function ChatInputForm() { + const inputRef = useRef(); + return ( From c9ea7da20b6b2f8c0001720419ec6926f61bdfb1 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Fri, 29 Mar 2024 02:12:14 +0900 Subject: [PATCH 22/81] =?UTF-8?q?Feat=20:=20input=20box=20focus=20?= =?UTF-8?q?=EC=97=AC=EB=B6=80=EC=97=90=20=EB=94=B0=EB=9D=BC=20chatInput=20?= =?UTF-8?q?=EC=98=81=EC=97=AD=EC=9D=98=20UI=EB=A5=BC=20=EC=A1=B0=EA=B1=B4?= =?UTF-8?q?=EB=B6=80=20=EB=A0=8C=EB=8D=94=EB=A7=81=EC=9D=84=20=ED=86=B5?= =?UTF-8?q?=ED=95=B4=20=EB=B3=80=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../non-fixed/ChatInput/ChatInputForm.tsx | 27 +++++++++++++++---- src/context/state/atom.ts | 5 ++++ 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/components/non-fixed/ChatInput/ChatInputForm.tsx b/src/components/non-fixed/ChatInput/ChatInputForm.tsx index 54d9772..e97b795 100644 --- a/src/components/non-fixed/ChatInput/ChatInputForm.tsx +++ b/src/components/non-fixed/ChatInput/ChatInputForm.tsx @@ -1,6 +1,8 @@ import styled from 'styled-components'; import * as ST from '@styles/styledComponents'; -import { useRef, forwardRef } from 'react'; + +import { useRecoilState } from 'recoil'; +import { isInputBoxFocusedState } from '@context/state/atom'; const StyledChatInputForm = styled.form` width: 100%; @@ -51,16 +53,31 @@ const StyledStaleSendIcon = styled.img` const StyledClearSendIcon = styled.img``; export default function ChatInputForm() { - const inputRef = useRef(); + const [isInputBoxFocused, setIsInputBoxFocused] = useRecoilState( + isInputBoxFocusedState + ); + + function handleToggleIsInputBoxFocused() { + setIsInputBoxFocused((prev) => !prev); + } return ( - + {isInputBoxFocused === false && ( + + )} - + - + {isInputBoxFocused === false ? ( + + ) : ( + + )} ); } diff --git a/src/context/state/atom.ts b/src/context/state/atom.ts index 3b9a2e2..35706d2 100644 --- a/src/context/state/atom.ts +++ b/src/context/state/atom.ts @@ -16,3 +16,8 @@ export const userNumberState = atom({ key: 'userNumber', default: 1, }); + +export const isInputBoxFocusedState = atom({ + key: 'isInputBoxFocused', + default: false, +}); From 57eee5922f1b183d8cb821c3b426324e40b96e8a Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Fri, 29 Mar 2024 11:21:21 +0900 Subject: [PATCH 23/81] =?UTF-8?q?Fix=20:=20clear=20send=20=EB=B2=84?= =?UTF-8?q?=ED=8A=BC=EC=9D=84=20=EB=88=8C=EB=A0=80=EC=9D=84=20=EB=95=8C=20?= =?UTF-8?q?onSubmit=20=EC=86=8D=EC=84=B1=EC=9D=B4=20=EC=95=84=EB=8B=88?= =?UTF-8?q?=EB=9D=BC=20onBlur=20=ED=95=B8=EB=93=A4=EB=9F=AC=ED=95=A8?= =?UTF-8?q?=EC=88=98=EA=B0=80=20=EB=B0=9C=EB=8F=99=EB=90=98=EB=8A=94=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=EC=9D=84=20onBlur=20=ED=95=B8=EB=93=A4?= =?UTF-8?q?=EB=9F=AC=20=ED=95=A8=EC=88=98=20=EB=82=B4=EB=B6=80=EC=9D=98=20?= =?UTF-8?q?=EC=A1=B0=EA=B1=B4=EB=AC=B8=20=EB=B6=84=EA=B8=B0=EB=A1=9C=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- make.md | 3 +- .../non-fixed/ChatInput/ChatInputForm.tsx | 44 +++++++++++++++---- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/make.md b/make.md index e89182d..c32610f 100644 --- a/make.md +++ b/make.md @@ -42,6 +42,7 @@ 2. 친구와의 메시지 탭을 누르면 바로 메시지 url로 넘어감 : `message/{친구명}`의 주소로 처리됨 3. <- arrow를 통해서 이전의 `/message` 주소로 리디렉션됨. -### 해결 포인트 +### 해결 포인트(trouble shooting) 1. input 박스 옆에 웃는 아이코은 span 태그 내부에 input과 이모지를 flex item으로 구성하며 해당 길이는 따라서 조정해줘야 함 => 아예 컨테이너를 만들어서 그 안에 input 요소를 넣고 이모지는 absolute 속성으로서 해결함 +2. focused가 된 상태에서 제출 버튼을 누르면 폼이 제출 되는 것이 아니라 그냥 입력해제가 되어버림. onBlur가 먼저 트리거 됨 => `handleToggleIsInputBoxFocused` 함수의 내부 조건부 로직으로 바꿔줌 diff --git a/src/components/non-fixed/ChatInput/ChatInputForm.tsx b/src/components/non-fixed/ChatInput/ChatInputForm.tsx index e97b795..89dba00 100644 --- a/src/components/non-fixed/ChatInput/ChatInputForm.tsx +++ b/src/components/non-fixed/ChatInput/ChatInputForm.tsx @@ -1,7 +1,7 @@ import styled from 'styled-components'; import * as ST from '@styles/styledComponents'; - import { useRecoilState } from 'recoil'; +import { useRef } from 'react'; import { isInputBoxFocusedState } from '@context/state/atom'; const StyledChatInputForm = styled.form` @@ -50,19 +50,43 @@ const StyledStaleSendIcon = styled.img` /* ${ST.hoverCursor}; */ `; -const StyledClearSendIcon = styled.img``; +const StyledSubmitButton = styled.button` + border: none; + background: none; + padding: 0; + margin: 0; +`; + +const StyledClearSendIcon = styled.img` + ${ST.hoverCursor}; +`; export default function ChatInputForm() { + const inputRef = useRef(null); const [isInputBoxFocused, setIsInputBoxFocused] = useRecoilState( isInputBoxFocusedState ); function handleToggleIsInputBoxFocused() { + // 포커스가 되었는데 값이 있는 상태에서 블러가 풀리는 것으로 가면 상태를 변경시키지 않음 => 내용이 없는 상태에서 메시지 전송도 막아줌 + if (isInputBoxFocused && inputRef.current?.value !== '') { + return; + } setIsInputBoxFocused((prev) => !prev); } + function handleSubmitForm(ev: any) { + if (isInputBoxFocused === false) { + ev.preventDefault(); + console.log('hi'); + } else if (isInputBoxFocused === true) { + ev.preventDefault(); + console.log('good'); + } + } + return ( - + {isInputBoxFocused === false && ( )} @@ -70,14 +94,18 @@ export default function ChatInputForm() { - {isInputBoxFocused === false ? ( - - ) : ( - - )} + + {isInputBoxFocused === false ? ( + + ) : ( + + )} + ); } From d14dc0de7e08936b0ec333e3ba1f64ae3ef6d390 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Fri, 29 Mar 2024 12:02:56 +0900 Subject: [PATCH 24/81] Fix : make user adjusted time generating function in makeTimeString.js --- make.md | 1 + package-lock.json | 6 +++ package.json | 1 + public/Dummy/Dummy.json | 2 +- src/App.tsx | 2 + .../non-fixed/ChatBody/ChatBody.tsx | 37 ++++++++++++++++--- src/utils/makeTimeString.js | 27 +++++++------- 7 files changed, 55 insertions(+), 21 deletions(-) diff --git a/make.md b/make.md index c32610f..7484886 100644 --- a/make.md +++ b/make.md @@ -46,3 +46,4 @@ 1. input 박스 옆에 웃는 아이코은 span 태그 내부에 input과 이모지를 flex item으로 구성하며 해당 길이는 따라서 조정해줘야 함 => 아예 컨테이너를 만들어서 그 안에 input 요소를 넣고 이모지는 absolute 속성으로서 해결함 2. focused가 된 상태에서 제출 버튼을 누르면 폼이 제출 되는 것이 아니라 그냥 입력해제가 되어버림. onBlur가 먼저 트리거 됨 => `handleToggleIsInputBoxFocused` 함수의 내부 조건부 로직으로 바꿔줌 +3. 처음에 로컬스토리지에 접근했는데 없다면 상태를 로컬 스토리지에 넣어줘야하고 이미 있다면 상태를 그걸로 적용해줘야 됨 diff --git a/package-lock.json b/package-lock.json index a9772e0..29ccf3c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@types/node": "^16.18.91", "@types/react": "^18.2.69", "@types/react-dom": "^18.2.22", + "dayjs": "^1.11.10", "react": "^18.2.0", "react-dom": "^18.2.0", "react-scripts": "5.0.1", @@ -6790,6 +6791,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/dayjs": { + "version": "1.11.10", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", + "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==" + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", diff --git a/package.json b/package.json index 7748e77..0e5b2a0 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "@types/node": "^16.18.91", "@types/react": "^18.2.69", "@types/react-dom": "^18.2.22", + "dayjs": "^1.11.10", "react": "^18.2.0", "react-dom": "^18.2.0", "react-scripts": "5.0.1", diff --git a/public/Dummy/Dummy.json b/public/Dummy/Dummy.json index 033adf2..0cf806e 100644 --- a/public/Dummy/Dummy.json +++ b/public/Dummy/Dummy.json @@ -32,7 +32,7 @@ { "from": 1, "like": false, - "content": "가을 하늘 공활한데 높고 구름 없이밝은 달은 우리 가슴 일편단심일세무궁화 삼천리 화려 강산대한 사람 대한으로 길이 보전하세", + "content": "가을 하늘 공활한데 높고 구름 없이 밝은 달은 우리 가슴 일편단심일세 무궁화 삼천리 화려 강산 대한 사람 대한으로 길이 보전하세", "createdAt": "2024-03-20T01:32:00.000Z" } ] diff --git a/src/App.tsx b/src/App.tsx index 08cae4d..5644922 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3,8 +3,10 @@ import theme from '@styles/theme'; import GlobalStyles from '@styles/globalStyles'; import AppMain from '@components/AppMain'; import { RecoilRoot } from 'recoil'; +import { adjustTimeForUserLocation } from '@utils/makeTimeString'; function App() { + console.log(adjustTimeForUserLocation()); return ( <> diff --git a/src/components/non-fixed/ChatBody/ChatBody.tsx b/src/components/non-fixed/ChatBody/ChatBody.tsx index 398a9b7..3211aa7 100644 --- a/src/components/non-fixed/ChatBody/ChatBody.tsx +++ b/src/components/non-fixed/ChatBody/ChatBody.tsx @@ -1,9 +1,5 @@ import { useRecoilState } from 'recoil'; -import { - messageDataState, - messageDateArrayState, - userNumberState, -} from '@context/state/atom'; +import { messageDataState, messageDateArrayState } from '@context/state/atom'; import { useEffect } from 'react'; import styled from 'styled-components'; import OneDateContainer from '@components/non-fixed/ChatBody/OneDateContainer/OneDateContainer'; @@ -53,6 +49,14 @@ export default function ChatBody() { } // 기존의 날짜 배열을 오름차순으로 정렬 tmpDateArray.sort(sortByDate); + localStorage.setItem( + 'chatMessageData', + JSON.stringify(tmpMessageDataObject) + ); + localStorage.setItem( + 'chatMessageDateArray', + JSON.stringify(tmpDateArray) + ); setMessageData(tmpMessageDataObject); setMessageDateArray(tmpDateArray); } catch (error) { @@ -60,7 +64,28 @@ export default function ChatBody() { } } - loadMessageData(); + // 기존의 로컬 스토리지에 아무 정보도 없다면 json 파일의 내용을 상태로 만들고 로컬 스토리지에도 반영 + // 하지만 이미 있다면 해당 내용을 가져와서 상태로 만든다 + if ( + localStorage.getItem('chatMessageData') === null && + localStorage.getItem('chatMessageDateArray') === null + ) { + console.log('yes!'); + loadMessageData(); + } else if ( + localStorage.getItem('chatMessageData') !== null && + localStorage.getItem('chatMessageDateArray') !== null + ) { + const lstrgChatMessageData = JSON.parse( + localStorage.getItem('chatMessageData') as string + ); + const lstrgChatMessageDateArray = JSON.parse( + localStorage.getItem('chatMessageDateArray') as string + ); + + setMessageData(lstrgChatMessageData); + setMessageDateArray(lstrgChatMessageDateArray); + } }, []); return ( diff --git a/src/utils/makeTimeString.js b/src/utils/makeTimeString.js index 09910be..f9ad9b9 100644 --- a/src/utils/makeTimeString.js +++ b/src/utils/makeTimeString.js @@ -1,16 +1,15 @@ -function toISOString(year, month, day, hour, minute) { - // JavaScript의 Date 객체는 월을 0부터 시작으로 카운트하기 때문에, 월에서 1을 빼줍니다. - // Date 객체를 생성할 때 UTC 시간으로 설정하기 위해 Date.UTC 메서드를 사용합니다. - const date = new Date(Date.UTC(year, month - 1, day, hour, minute)); - - // ISO 8601 형식의 문자열로 변환 - return date.toISOString(); +// 사용자의 시간대 오프셋(시간대에 따라 분 단위로 설정) +function getUserTimezoneOffset() { + // 예시: 한국 시간대의 경우 UTC+9 + return 9 * 60; // 분 단위로 설정 } -// 함수 사용 예시 -console.log(toISOString(2024, 3, 19, 20, 45)); -console.log(toISOString(2024, 3, 19, 20, 48)); -console.log(toISOString(2024, 3, 19, 20, 49)); -console.log(toISOString(2024, 3, 19, 20, 51)); -console.log(toISOString(2024, 3, 19, 20, 52)); -console.log(toISOString(2024, 3, 20, 1, 32)); +// ISO 문자열로 시간을 보정하는 함수 +export function adjustTimeForUserLocation() { + const currentTime = new Date(); + const userTimezoneOffset = getUserTimezoneOffset(); + const adjustedTime = new Date( + currentTime.getTime() + userTimezoneOffset * 60000 + ); // 시간대 오프셋을 밀리초 단위로 변환하여 시간 보정 + return adjustedTime.toISOString(); +} From bce5f6f8b4c9ab3a718ad8533b6b1d42bbd98d24 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Fri, 29 Mar 2024 12:44:09 +0900 Subject: [PATCH 25/81] =?UTF-8?q?Feat=20:=20make=20chat=20append=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 사용자가 채팅을 입력하면 상태도 바꿔주고 로컬 스토리지도 동기화시켜줌 --- make.md | 4 +- src/App.tsx | 2 - .../non-fixed/ChatInput/ChatInputForm.tsx | 53 ++++++++++++++++++- 3 files changed, 55 insertions(+), 4 deletions(-) diff --git a/make.md b/make.md index 7484886..ed688de 100644 --- a/make.md +++ b/make.md @@ -46,4 +46,6 @@ 1. input 박스 옆에 웃는 아이코은 span 태그 내부에 input과 이모지를 flex item으로 구성하며 해당 길이는 따라서 조정해줘야 함 => 아예 컨테이너를 만들어서 그 안에 input 요소를 넣고 이모지는 absolute 속성으로서 해결함 2. focused가 된 상태에서 제출 버튼을 누르면 폼이 제출 되는 것이 아니라 그냥 입력해제가 되어버림. onBlur가 먼저 트리거 됨 => `handleToggleIsInputBoxFocused` 함수의 내부 조건부 로직으로 바꿔줌 -3. 처음에 로컬스토리지에 접근했는데 없다면 상태를 로컬 스토리지에 넣어줘야하고 이미 있다면 상태를 그걸로 적용해줘야 됨 +3. 처음에 로컬스토리지에 접근했는데 없다면 상태를 로컬 스토리지에 넣어줘야하고 이미 있다면 상태를 그걸로 적용해줘야 됨 => 해결 `useEffect` 훅 내에서 분기로 처리함 +4. useNumber는 일단 처음은 1로 설정되고 내 메시지는 노란색으로, 상대방은 초록색으로 보인다. +5. 사용자가 메시지를 입력하면 기존 상태 2개(data와 date 2개)를 바꾸고 이를 로컬 스토리지에도 반영해야한다 diff --git a/src/App.tsx b/src/App.tsx index 5644922..08cae4d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3,10 +3,8 @@ import theme from '@styles/theme'; import GlobalStyles from '@styles/globalStyles'; import AppMain from '@components/AppMain'; import { RecoilRoot } from 'recoil'; -import { adjustTimeForUserLocation } from '@utils/makeTimeString'; function App() { - console.log(adjustTimeForUserLocation()); return ( <> diff --git a/src/components/non-fixed/ChatInput/ChatInputForm.tsx b/src/components/non-fixed/ChatInput/ChatInputForm.tsx index 89dba00..0d17cbd 100644 --- a/src/components/non-fixed/ChatInput/ChatInputForm.tsx +++ b/src/components/non-fixed/ChatInput/ChatInputForm.tsx @@ -2,7 +2,15 @@ import styled from 'styled-components'; import * as ST from '@styles/styledComponents'; import { useRecoilState } from 'recoil'; import { useRef } from 'react'; -import { isInputBoxFocusedState } from '@context/state/atom'; +import { + isInputBoxFocusedState, + messageDataState, + messageDateArrayState, + userNumberState, +} from '@context/state/atom'; +import { adjustTimeForUserLocation } from '@utils/makeTimeString'; +import sortByDate from '@utils/sortArrayByDate'; +import { processedMessageData } from '@_type/type'; const StyledChatInputForm = styled.form` width: 100%; @@ -66,6 +74,11 @@ export default function ChatInputForm() { const [isInputBoxFocused, setIsInputBoxFocused] = useRecoilState( isInputBoxFocusedState ); + const [messageData, setMessageData] = useRecoilState(messageDataState); + const [messageDateArray, setMessageDateArray] = useRecoilState( + messageDateArrayState + ); + const [userNumber, setUserNumber] = useRecoilState(userNumberState); function handleToggleIsInputBoxFocused() { // 포커스가 되었는데 값이 있는 상태에서 블러가 풀리는 것으로 가면 상태를 변경시키지 않음 => 내용이 없는 상태에서 메시지 전송도 막아줌 @@ -82,6 +95,44 @@ export default function ChatInputForm() { } else if (isInputBoxFocused === true) { ev.preventDefault(); console.log('good'); + const createdAt = adjustTimeForUserLocation(); + const createdDate = createdAt.slice(0, 10); + const content = inputRef.current?.value as string; + const from = userNumber; + const like = false; + + const newMessageData = { + createdAt: createdAt, + createdDate: createdDate, + content: content, + from: from, + like: like, + }; + + const tmpMessageData = { ...messageData }; + const tmpMessageDateArray = [...messageDateArray]; + + if (tmpMessageData[createdDate] === undefined) { + tmpMessageData[createdDate] = []; + tmpMessageData[createdDate].push(newMessageData); + } else { + tmpMessageData[createdDate].push(newMessageData); + } + + if (!tmpMessageDateArray.includes(createdDate)) { + tmpMessageDateArray.push(createdDate); + tmpMessageDateArray.sort(sortByDate); + } + setMessageData(tmpMessageData); + setMessageDateArray(tmpMessageDateArray); + localStorage.setItem('chatMessageData', JSON.stringify(tmpMessageData)); + localStorage.setItem( + 'chatMessageDateArray', + JSON.stringify(tmpMessageDateArray) + ); + if (inputRef.current !== null) { + inputRef.current.value = ''; + } } } From 4b1123add34c7724cf66e38c3cc7f0a125849d3b Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Fri, 29 Mar 2024 13:11:01 +0900 Subject: [PATCH 26/81] =?UTF-8?q?Fix=20:=20=EC=9D=B4=EB=AF=B8=20=EC=A1=B4?= =?UTF-8?q?=EC=9E=AC=ED=95=98=EB=8A=94=20=EB=82=A0=EC=A7=9C=20=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=EC=97=90=20=EC=83=88=EB=A1=9C=EC=9A=B4=20?= =?UTF-8?q?=EB=A9=94=EC=8B=9C=EC=A7=80=20=EC=B6=94=EA=B0=80=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 기존의 chatMessageData의 특정 날짜에 해당하는 배열과 주소값이 달라야함 2. 따라서 prevDateArray라는 변수를 기존의 것을 배열 구조 분해하여 받고 그다음 새로운 데이터를 push함 3. 이렇게 바뀌었는데 메모리 주소값이 다른 데이터를 새로운 상태로 설 --- src/components/non-fixed/ChatBody/ChatBody.tsx | 7 ++++++- src/components/non-fixed/ChatInput/ChatInputForm.tsx | 10 +++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/components/non-fixed/ChatBody/ChatBody.tsx b/src/components/non-fixed/ChatBody/ChatBody.tsx index 3211aa7..77c68fa 100644 --- a/src/components/non-fixed/ChatBody/ChatBody.tsx +++ b/src/components/non-fixed/ChatBody/ChatBody.tsx @@ -8,9 +8,14 @@ import sortByDate from '@utils/sortArrayByDate'; const StyledChatBodyContainer = styled.div` flex-grow: 1; + flex-shrink: 0; margin-top: 16px; margin-right: 16px; margin-left: 16px; + /* overflow-y: scroll; */ + width: 343px; + height: 595px; + overflow-y: scroll; `; export default function ChatBody() { @@ -89,7 +94,7 @@ export default function ChatBody() { }, []); return ( - + {messageDateArray.map((messageDate) => { return ; })} diff --git a/src/components/non-fixed/ChatInput/ChatInputForm.tsx b/src/components/non-fixed/ChatInput/ChatInputForm.tsx index 0d17cbd..c56afaf 100644 --- a/src/components/non-fixed/ChatInput/ChatInputForm.tsx +++ b/src/components/non-fixed/ChatInput/ChatInputForm.tsx @@ -102,21 +102,25 @@ export default function ChatInputForm() { const like = false; const newMessageData = { + content: content, createdAt: createdAt, createdDate: createdDate, - content: content, from: from, like: like, }; - const tmpMessageData = { ...messageData }; + const tmpMessageData = Object.assign({}, messageData); + const tmpMessageDateArray = [...messageDateArray]; if (tmpMessageData[createdDate] === undefined) { tmpMessageData[createdDate] = []; tmpMessageData[createdDate].push(newMessageData); } else { - tmpMessageData[createdDate].push(newMessageData); + console.log(tmpMessageData[createdDate]); + const prevDateArray = [...tmpMessageData[createdDate]]; + prevDateArray.push(newMessageData); + tmpMessageData[createdDate] = prevDateArray; } if (!tmpMessageDateArray.includes(createdDate)) { From 665f56359b73941c42cfb0544621d88ce57022dc Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Fri, 29 Mar 2024 13:57:30 +0900 Subject: [PATCH 27/81] Feat : make useScrollToBottom custom hook MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 스크롤을 아래로 내일 chatBody와 chatInput은 부모-자식 관계가 아니므로 prop으로 콜백을 전달할 수 없음 2. 따라서 전역 상태 관리로 이를 구현해야하는데 커스텀 훅을 통해서 진행 --- make.md | 5 +++-- src/components/non-fixed/ChatBody/ChatBody.tsx | 16 +++++++++++----- .../non-fixed/ChatInput/ChatInputForm.tsx | 2 +- src/context/state/atom.ts | 5 +++++ src/hooks/useScrollToBottom.tsx | 14 ++++++++++++++ src/type/type.tsx | 2 ++ 6 files changed, 36 insertions(+), 8 deletions(-) create mode 100644 src/hooks/useScrollToBottom.tsx diff --git a/make.md b/make.md index ed688de..4efc42b 100644 --- a/make.md +++ b/make.md @@ -47,5 +47,6 @@ 1. input 박스 옆에 웃는 아이코은 span 태그 내부에 input과 이모지를 flex item으로 구성하며 해당 길이는 따라서 조정해줘야 함 => 아예 컨테이너를 만들어서 그 안에 input 요소를 넣고 이모지는 absolute 속성으로서 해결함 2. focused가 된 상태에서 제출 버튼을 누르면 폼이 제출 되는 것이 아니라 그냥 입력해제가 되어버림. onBlur가 먼저 트리거 됨 => `handleToggleIsInputBoxFocused` 함수의 내부 조건부 로직으로 바꿔줌 3. 처음에 로컬스토리지에 접근했는데 없다면 상태를 로컬 스토리지에 넣어줘야하고 이미 있다면 상태를 그걸로 적용해줘야 됨 => 해결 `useEffect` 훅 내에서 분기로 처리함 -4. useNumber는 일단 처음은 1로 설정되고 내 메시지는 노란색으로, 상대방은 초록색으로 보인다. -5. 사용자가 메시지를 입력하면 기존 상태 2개(data와 date 2개)를 바꾸고 이를 로컬 스토리지에도 반영해야한다 +4. useNumber는 일단 처음은 1로 설정되고 내 메시지는 노란색으로, 상대방은 초록색으로 보인다. => 해결 +5. 사용자가 메시지를 입력하면 기존 상태 2개(data와 date 2개)를 바꾸고 이를 로컬 스토리지에도 반영해야한다 => 해결 +6. 메시지를 보내면 바로 스크롤되어 내려옴. useRef를 이용해서 scrollTop 속성을 scrollHeight로 바꿔서 아래로 내리면 되는데, chatBody와 chatInput은 형제 속성이라서 함께 건드리기 어려움 => 전역 상태로 관리 diff --git a/src/components/non-fixed/ChatBody/ChatBody.tsx b/src/components/non-fixed/ChatBody/ChatBody.tsx index 77c68fa..4b3f136 100644 --- a/src/components/non-fixed/ChatBody/ChatBody.tsx +++ b/src/components/non-fixed/ChatBody/ChatBody.tsx @@ -1,9 +1,9 @@ import { useRecoilState } from 'recoil'; import { messageDataState, messageDateArrayState } from '@context/state/atom'; -import { useEffect } from 'react'; +import { useEffect, useRef } from 'react'; import styled from 'styled-components'; import OneDateContainer from '@components/non-fixed/ChatBody/OneDateContainer/OneDateContainer'; -import { messageDataObject } from '@_type/type'; +import { messageDataObject, voidFunction } from '@_type/type'; import sortByDate from '@utils/sortArrayByDate'; const StyledChatBodyContainer = styled.div` @@ -12,7 +12,6 @@ const StyledChatBodyContainer = styled.div` margin-top: 16px; margin-right: 16px; margin-left: 16px; - /* overflow-y: scroll; */ width: 343px; height: 595px; overflow-y: scroll; @@ -20,11 +19,19 @@ const StyledChatBodyContainer = styled.div` export default function ChatBody() { // 여기에서 json 데이터를 불러와서 날짜별로 쪼갠다. 그리고 구분선, 메시지,메시지, 다시 구분선 메시지 메시지 느낌으로 나눠준다 + const chatBodyContainerRef = useRef(null); const [messageData, setMessageData] = useRecoilState(messageDataState); const [messageDateArray, setMessageDateArray] = useRecoilState( messageDateArrayState ); + const scrollToBottom: voidFunction = function () { + if (chatBodyContainerRef.current) { + chatBodyContainerRef.current.scrollTop = + chatBodyContainerRef.current.scrollHeight; + } + }; + // 처음 chatBody 컴포넌트가 DOM에 마운트 되면 json 파일로부터 정보를 가져온다. useEffect(() => { async function loadMessageData() { @@ -75,7 +82,6 @@ export default function ChatBody() { localStorage.getItem('chatMessageData') === null && localStorage.getItem('chatMessageDateArray') === null ) { - console.log('yes!'); loadMessageData(); } else if ( localStorage.getItem('chatMessageData') !== null && @@ -94,7 +100,7 @@ export default function ChatBody() { }, []); return ( - + {messageDateArray.map((messageDate) => { return ; })} diff --git a/src/components/non-fixed/ChatInput/ChatInputForm.tsx b/src/components/non-fixed/ChatInput/ChatInputForm.tsx index c56afaf..b6b2bfd 100644 --- a/src/components/non-fixed/ChatInput/ChatInputForm.tsx +++ b/src/components/non-fixed/ChatInput/ChatInputForm.tsx @@ -10,7 +10,6 @@ import { } from '@context/state/atom'; import { adjustTimeForUserLocation } from '@utils/makeTimeString'; import sortByDate from '@utils/sortArrayByDate'; -import { processedMessageData } from '@_type/type'; const StyledChatInputForm = styled.form` width: 100%; @@ -118,6 +117,7 @@ export default function ChatInputForm() { tmpMessageData[createdDate].push(newMessageData); } else { console.log(tmpMessageData[createdDate]); + // 기존의 배열을 구조분해하여 다른 메모리 값을 가진 배열로 받음 const prevDateArray = [...tmpMessageData[createdDate]]; prevDateArray.push(newMessageData); tmpMessageData[createdDate] = prevDateArray; diff --git a/src/context/state/atom.ts b/src/context/state/atom.ts index 35706d2..a688c92 100644 --- a/src/context/state/atom.ts +++ b/src/context/state/atom.ts @@ -21,3 +21,8 @@ export const isInputBoxFocusedState = atom({ key: 'isInputBoxFocused', default: false, }); + +export const scrollToBottomState = atom({ + key: 'scrollToBottomState', + default: () => {}, +}); diff --git a/src/hooks/useScrollToBottom.tsx b/src/hooks/useScrollToBottom.tsx new file mode 100644 index 0000000..1d4eb0c --- /dev/null +++ b/src/hooks/useScrollToBottom.tsx @@ -0,0 +1,14 @@ +import { useRecoilState } from 'recoil'; +import { scrollToBottomState } from '@context/state/atom'; +import { voidFunction } from '@_type/type'; + +export default function useScrollToBottom() { + const [scrollToBottom, setScrollToBottom] = + useRecoilState(scrollToBottomState); + + function setScrollFunction(scrollFunction: voidFunction) { + setScrollToBottom(() => scrollFunction); + } + + return [scrollToBottom, setScrollFunction]; +} diff --git a/src/type/type.tsx b/src/type/type.tsx index d7e415a..1508ff6 100644 --- a/src/type/type.tsx +++ b/src/type/type.tsx @@ -19,3 +19,5 @@ export type processedMessageDataArray = processedMessageData[]; export interface messageDataObject { [key: string]: processedMessageDataArray; } + +export type voidFunction = () => void; From b45208050d66943195a4be85c8f4ae2f78935f70 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Fri, 29 Mar 2024 14:36:13 +0900 Subject: [PATCH 28/81] =?UTF-8?q?Fix=20:=20dom=EC=9D=B4=20=EC=97=85?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=8A=B8=EB=90=9C=20=EC=9D=B4=ED=9B=84?= =?UTF-8?q?=EC=97=90=20=EC=8A=A4=ED=81=AC=EB=A1=A4=EC=9D=84=20=EB=81=9D?= =?UTF-8?q?=EA=B9=8C=EC=A7=80=20=EB=82=B4=EB=A6=AC=EB=A0=A4=EB=A9=B4=20mes?= =?UTF-8?q?sageData=EA=B0=80=20=EB=B0=94=EB=80=8C=EA=B3=A0=20=EA=B7=B8=20?= =?UTF-8?q?=EB=8B=A4=EC=9D=8C=EC=97=90=20=EC=9D=B4=EB=A5=BC=20=EC=9D=98?= =?UTF-8?q?=EC=A1=B4=EC=84=B1=20=EB=B0=B0=EC=97=B4=EB=A1=9C=20=ED=95=98?= =?UTF-8?q?=EB=8A=94=20useEffect()=20=ED=9B=85=EC=97=90=EC=84=9C,=20?= =?UTF-8?q?=EC=BB=A4=EC=8A=A4=ED=85=80=20=ED=9B=85=EC=9D=98=20scrollToBott?= =?UTF-8?q?om=EC=9D=84=20=ED=95=B4=EC=A3=BC=EC=96=B4=EC=95=BC=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/non-fixed/ChatBody/ChatBody.tsx | 14 +++++++++++++- .../non-fixed/ChatInput/ChatInputForm.tsx | 7 +++++-- src/hooks/useScrollToBottom.tsx | 2 +- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/components/non-fixed/ChatBody/ChatBody.tsx b/src/components/non-fixed/ChatBody/ChatBody.tsx index 4b3f136..ff9bb3a 100644 --- a/src/components/non-fixed/ChatBody/ChatBody.tsx +++ b/src/components/non-fixed/ChatBody/ChatBody.tsx @@ -5,6 +5,7 @@ import styled from 'styled-components'; import OneDateContainer from '@components/non-fixed/ChatBody/OneDateContainer/OneDateContainer'; import { messageDataObject, voidFunction } from '@_type/type'; import sortByDate from '@utils/sortArrayByDate'; +import useScrollToBottom from '@hooks/useScrollToBottom'; const StyledChatBodyContainer = styled.div` flex-grow: 1; @@ -25,13 +26,24 @@ export default function ChatBody() { messageDateArrayState ); - const scrollToBottom: voidFunction = function () { + const [scrollToBottom, setScrollFunction] = useScrollToBottom(); + + const scrollToBottomFunction: voidFunction = function () { if (chatBodyContainerRef.current) { chatBodyContainerRef.current.scrollTop = chatBodyContainerRef.current.scrollHeight; } }; + useEffect(() => { + setScrollFunction(scrollToBottomFunction); + }, []); + + // messageData가 변경된 이후에 dom에 반영되고 그 다음에 scroll이 내려가야 새로 생긴 요소까지 반영 + useEffect(() => { + scrollToBottom(); + }, [messageData]); + // 처음 chatBody 컴포넌트가 DOM에 마운트 되면 json 파일로부터 정보를 가져온다. useEffect(() => { async function loadMessageData() { diff --git a/src/components/non-fixed/ChatInput/ChatInputForm.tsx b/src/components/non-fixed/ChatInput/ChatInputForm.tsx index b6b2bfd..7ccd23c 100644 --- a/src/components/non-fixed/ChatInput/ChatInputForm.tsx +++ b/src/components/non-fixed/ChatInput/ChatInputForm.tsx @@ -10,6 +10,7 @@ import { } from '@context/state/atom'; import { adjustTimeForUserLocation } from '@utils/makeTimeString'; import sortByDate from '@utils/sortArrayByDate'; +import useScrollToBottom from '@hooks/useScrollToBottom'; const StyledChatInputForm = styled.form` width: 100%; @@ -78,6 +79,7 @@ export default function ChatInputForm() { messageDateArrayState ); const [userNumber, setUserNumber] = useRecoilState(userNumberState); + const [scrollToBottom, setScrollFunction] = useScrollToBottom(); function handleToggleIsInputBoxFocused() { // 포커스가 되었는데 값이 있는 상태에서 블러가 풀리는 것으로 가면 상태를 변경시키지 않음 => 내용이 없는 상태에서 메시지 전송도 막아줌 @@ -90,10 +92,10 @@ export default function ChatInputForm() { function handleSubmitForm(ev: any) { if (isInputBoxFocused === false) { ev.preventDefault(); - console.log('hi'); + // console.log('hi'); } else if (isInputBoxFocused === true) { ev.preventDefault(); - console.log('good'); + // console.log('good'); const createdAt = adjustTimeForUserLocation(); const createdDate = createdAt.slice(0, 10); const content = inputRef.current?.value as string; @@ -137,6 +139,7 @@ export default function ChatInputForm() { if (inputRef.current !== null) { inputRef.current.value = ''; } + // scrollToBottom(); } } diff --git a/src/hooks/useScrollToBottom.tsx b/src/hooks/useScrollToBottom.tsx index 1d4eb0c..fe44902 100644 --- a/src/hooks/useScrollToBottom.tsx +++ b/src/hooks/useScrollToBottom.tsx @@ -6,7 +6,7 @@ export default function useScrollToBottom() { const [scrollToBottom, setScrollToBottom] = useRecoilState(scrollToBottomState); - function setScrollFunction(scrollFunction: voidFunction) { + function setScrollFunction(scrollFunction?: voidFunction) { setScrollToBottom(() => scrollFunction); } From 4382061b24a4839eb923bb801bc1be75f86cfaa3 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Fri, 29 Mar 2024 14:38:58 +0900 Subject: [PATCH 29/81] =?UTF-8?q?Fix=20:=20=EA=B0=80=EB=A1=9C=20=EB=B0=A9?= =?UTF-8?q?=ED=96=A5=EC=9C=BC=EB=A1=9C=20=EB=AA=A8=EB=B0=94=EC=9D=BC=20?= =?UTF-8?q?=ED=99=98=EA=B2=BD=EC=97=90=EC=84=9C=20=EC=9B=80=EC=A7=81?= =?UTF-8?q?=EC=9D=B4=EB=8A=94=20=EA=B2=83=EC=9D=84=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/styles/globalStyles.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/styles/globalStyles.tsx b/src/styles/globalStyles.tsx index e5c8971..682c044 100644 --- a/src/styles/globalStyles.tsx +++ b/src/styles/globalStyles.tsx @@ -24,6 +24,7 @@ const GlobalStyles = createGlobalStyle` /* 스크롤 바 관련 UI를 제거하는 속성 원하는 요소에 scroll-box 클래스를 부여하면 됨 */ .scroll-box { + overflow-x: hidden; -ms-overflow-style: none; } From 30a4d4c9a87d5bb901f7bc1c603d14173cbb10b6 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Fri, 29 Mar 2024 15:50:07 +0900 Subject: [PATCH 30/81] Feat : make like heartUI and conditional rendering --- public/images/likeHeart.svg | 4 ++++ .../non-fixed/ChatBody/ChatLog/ChatLog.tsx | 10 +++++++++- .../ChatLog/ChatLogRight/ChatLogRight.tsx | 19 +++++++++++++++++++ .../OneDateContainer/OneDateContainer.tsx | 2 ++ src/styles/globalStyles.tsx | 2 ++ 5 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 public/images/likeHeart.svg diff --git a/public/images/likeHeart.svg b/public/images/likeHeart.svg new file mode 100644 index 0000000..8bdbf81 --- /dev/null +++ b/public/images/likeHeart.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/components/non-fixed/ChatBody/ChatLog/ChatLog.tsx b/src/components/non-fixed/ChatBody/ChatLog/ChatLog.tsx index 7937528..813456a 100644 --- a/src/components/non-fixed/ChatBody/ChatLog/ChatLog.tsx +++ b/src/components/non-fixed/ChatBody/ChatLog/ChatLog.tsx @@ -16,16 +16,24 @@ export default function ChatLog({ from, createdAt, content, + like, }: { isEqual: boolean; from: number; createdAt: string; content: string; + like: boolean; }) { return ( - + ); } diff --git a/src/components/non-fixed/ChatBody/ChatLog/ChatLogRight/ChatLogRight.tsx b/src/components/non-fixed/ChatBody/ChatLog/ChatLogRight/ChatLogRight.tsx index 0d6ebd8..4577d09 100644 --- a/src/components/non-fixed/ChatBody/ChatLog/ChatLogRight/ChatLogRight.tsx +++ b/src/components/non-fixed/ChatBody/ChatLog/ChatLogRight/ChatLogRight.tsx @@ -40,14 +40,28 @@ const StyledMessageContentContainer = styled.div` color: ${(props) => props.theme.color.black}; line-height: 22.5px; `; + +const StyledLikeHeartDiv = styled.div` + width: 100%; + height: 22px; + display: flex; + justify-content: flex-start; + padding-top: 2px; +`; + +const StyledLikeHeartImage = styled.img``; export default function ChatLogRight({ + isEqual, from, createdAt, content, + like, }: { + isEqual: boolean; from: number; createdAt: string; content: string; + like: boolean; }) { const createdHourMinute = createdAt.slice(11, 16); return ( @@ -57,6 +71,11 @@ export default function ChatLogRight({ {createdHourMinute} {content} + {isEqual === false && like === true && ( + + + + )} ); } diff --git a/src/components/non-fixed/ChatBody/OneDateContainer/OneDateContainer.tsx b/src/components/non-fixed/ChatBody/OneDateContainer/OneDateContainer.tsx index 6a9283d..ab366c1 100644 --- a/src/components/non-fixed/ChatBody/OneDateContainer/OneDateContainer.tsx +++ b/src/components/non-fixed/ChatBody/OneDateContainer/OneDateContainer.tsx @@ -23,6 +23,7 @@ export default function OneDateContainer({ {oneDateMessagData.map((data) => { + console.log(data); return ( ); })} diff --git a/src/styles/globalStyles.tsx b/src/styles/globalStyles.tsx index 682c044..e08dfc8 100644 --- a/src/styles/globalStyles.tsx +++ b/src/styles/globalStyles.tsx @@ -8,6 +8,7 @@ const GlobalStyles = createGlobalStyle` } html { background-color: #E5E6EB; + height: 100%; } body { font-family: 'Pretendard', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; @@ -15,6 +16,7 @@ const GlobalStyles = createGlobalStyle` opacity: 0.95; margin: 0 auto; height: 812px; + height: 100%; width: 375px; } #root { From 7a6ecb41ec4c5d7b217792b540c33128f427b069 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Fri, 29 Mar 2024 17:11:25 +0900 Subject: [PATCH 31/81] =?UTF-8?q?Feat=20:=20add=20like=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EC=82=AD=EC=A0=9C=20=EA=B8=B0=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 기존의 상태값을 JSON.parse(JSON.stringify())를 통해서 깊은 복사를 한다. 다른 스프레드 연산자 방식은 중첩된 대상들까지는 안될 수도 있다 --- make.md | 1 + public/Dummy/Dummy.json | 12 +++---- .../ChatLog/ChatLogRight/ChatLogRight.tsx | 31 ++++++++++++++++++- .../OneDateContainer/OneDateContainer.tsx | 1 - src/hooks/useLocalStorage.tsx | 28 +++++++++++++++++ 5 files changed, 65 insertions(+), 8 deletions(-) create mode 100644 src/hooks/useLocalStorage.tsx diff --git a/make.md b/make.md index 4efc42b..881c414 100644 --- a/make.md +++ b/make.md @@ -50,3 +50,4 @@ 4. useNumber는 일단 처음은 1로 설정되고 내 메시지는 노란색으로, 상대방은 초록색으로 보인다. => 해결 5. 사용자가 메시지를 입력하면 기존 상태 2개(data와 date 2개)를 바꾸고 이를 로컬 스토리지에도 반영해야한다 => 해결 6. 메시지를 보내면 바로 스크롤되어 내려옴. useRef를 이용해서 scrollTop 속성을 scrollHeight로 바꿔서 아래로 내리면 되는데, chatBody와 chatInput은 형제 속성이라서 함께 건드리기 어려움 => 전역 상태로 관리 +7. 더블 클릭하면 하트가 생겨야 하는데, 남의 것에만 할 수 있어야 한다 diff --git a/public/Dummy/Dummy.json b/public/Dummy/Dummy.json index 0cf806e..3a95ad7 100644 --- a/public/Dummy/Dummy.json +++ b/public/Dummy/Dummy.json @@ -3,36 +3,36 @@ "from": 2, "like": false, "content": "동해물과 백두산이 마르고 닳도록 하느님이 보우하사 우리나라 만세", - "createdAt": "2024-03-19T20:45:00.000Z" + "createdAt": "2024-03-19T20:45:01.000Z" }, { "from": 1, "like": false, "content": "무궁화 삼천리 화려강산", - "createdAt": "2024-03-19T20:48:00.000Z" + "createdAt": "2024-03-19T20:48:02.000Z" }, { "from": 2, "like": false, "content": "대한사람 대한으로 길이 보전하세", - "createdAt": "2024-03-19T20:49:00.000Z" + "createdAt": "2024-03-19T20:49:03.000Z" }, { "from": 1, "like": false, "content": "남산 위에 저 소나무 철갑을 두른 듯 바람서리 불변함은 우리 기상일세 무궁화 삼천리 화려강산", - "createdAt": "2024-03-19T20:51:00.000Z" + "createdAt": "2024-03-19T20:51:04.000Z" }, { "from": 2, "like": false, "content": "대한사람 대한으로 길이 보전하세", - "createdAt": "2024-03-19T20:52:00.000Z" + "createdAt": "2024-03-19T20:52:05.000Z" }, { "from": 1, "like": false, "content": "가을 하늘 공활한데 높고 구름 없이 밝은 달은 우리 가슴 일편단심일세 무궁화 삼천리 화려 강산 대한 사람 대한으로 길이 보전하세", - "createdAt": "2024-03-20T01:32:00.000Z" + "createdAt": "2024-03-20T01:32:06.000Z" } ] diff --git a/src/components/non-fixed/ChatBody/ChatLog/ChatLogRight/ChatLogRight.tsx b/src/components/non-fixed/ChatBody/ChatLog/ChatLogRight/ChatLogRight.tsx index 4577d09..4572061 100644 --- a/src/components/non-fixed/ChatBody/ChatLog/ChatLogRight/ChatLogRight.tsx +++ b/src/components/non-fixed/ChatBody/ChatLog/ChatLogRight/ChatLogRight.tsx @@ -1,4 +1,8 @@ import styled from 'styled-components'; +import { useRecoilState } from 'recoil'; +import { messageDataState, userNumberState } from '@context/state/atom'; +import useLocalStorage from '@hooks/useLocalStorage'; +import { processedMessageData } from '@_type/type'; const StyledChatLogRightContainer = styled.div` display: flex; @@ -63,10 +67,35 @@ export default function ChatLogRight({ content: string; like: boolean; }) { + const [messageData, setMessageData] = useRecoilState(messageDataState); + const [userNumber, setUserNumber] = useRecoilState(userNumberState); + const [localStorageChatMessageData, localStorageChatMessageDateArray] = + useLocalStorage(); const createdHourMinute = createdAt.slice(11, 16); + const createdDate = createdAt.slice(0, 10); + const deepCopiedMessageData = JSON.parse(JSON.stringify(messageData)); + console.log(deepCopiedMessageData); + + function handleDoubleClickMessage() { + // 자신과 달라야 한다. + if (from === userNumber) return; + + const prevSpecifiedDateArray = deepCopiedMessageData[createdDate]; + for (const data of prevSpecifiedDateArray) { + if (data['createdAt'] === createdAt) { + data['like'] = !data['like']; + } + } + setMessageData(deepCopiedMessageData); + localStorage.setItem( + 'chatMessageData', + JSON.stringify(deepCopiedMessageData) + ); + } + return ( - + {from === 2 ? '김정민' : '김승완'} {createdHourMinute} diff --git a/src/components/non-fixed/ChatBody/OneDateContainer/OneDateContainer.tsx b/src/components/non-fixed/ChatBody/OneDateContainer/OneDateContainer.tsx index ab366c1..8d3cdf8 100644 --- a/src/components/non-fixed/ChatBody/OneDateContainer/OneDateContainer.tsx +++ b/src/components/non-fixed/ChatBody/OneDateContainer/OneDateContainer.tsx @@ -23,7 +23,6 @@ export default function OneDateContainer({ {oneDateMessagData.map((data) => { - console.log(data); return ( { + const storedChatMessageData = localStorage.getItem('chatMessageData'); + const storedChatMessageDateArray = localStorage.getItem( + 'chatMessageDateArray' + ); + if (storedChatMessageData) { + setLocalStorageChatMessageData(JSON.parse(storedChatMessageData)); + } + + if (storedChatMessageDateArray) { + setLocalStorageChatMessageDateArray( + JSON.parse(storedChatMessageDateArray) + ); + } + }, []); + + return [localStorageChatMessageData, localStorageChatMessageDateArray]; +} From 205ea15265589e0b19b575e4eed588d7630b29e4 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Fri, 29 Mar 2024 17:17:39 +0900 Subject: [PATCH 32/81] =?UTF-8?q?Refactor=20:=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20=EC=A3=BC=EC=84=9D=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ChatBody/ChatLog/ChatLogRight/ChatLogRight.tsx | 5 +---- src/components/non-fixed/ChatInput/ChatInput.tsx | 2 -- src/components/non-fixed/ChatInput/ChatInputForm.tsx | 6 +----- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/src/components/non-fixed/ChatBody/ChatLog/ChatLogRight/ChatLogRight.tsx b/src/components/non-fixed/ChatBody/ChatLog/ChatLogRight/ChatLogRight.tsx index 4572061..2cc0e38 100644 --- a/src/components/non-fixed/ChatBody/ChatLog/ChatLogRight/ChatLogRight.tsx +++ b/src/components/non-fixed/ChatBody/ChatLog/ChatLogRight/ChatLogRight.tsx @@ -1,8 +1,6 @@ import styled from 'styled-components'; import { useRecoilState } from 'recoil'; import { messageDataState, userNumberState } from '@context/state/atom'; -import useLocalStorage from '@hooks/useLocalStorage'; -import { processedMessageData } from '@_type/type'; const StyledChatLogRightContainer = styled.div` display: flex; @@ -69,8 +67,7 @@ export default function ChatLogRight({ }) { const [messageData, setMessageData] = useRecoilState(messageDataState); const [userNumber, setUserNumber] = useRecoilState(userNumberState); - const [localStorageChatMessageData, localStorageChatMessageDateArray] = - useLocalStorage(); + const createdHourMinute = createdAt.slice(11, 16); const createdDate = createdAt.slice(0, 10); const deepCopiedMessageData = JSON.parse(JSON.stringify(messageData)); diff --git a/src/components/non-fixed/ChatInput/ChatInput.tsx b/src/components/non-fixed/ChatInput/ChatInput.tsx index 0789a30..f3f2553 100644 --- a/src/components/non-fixed/ChatInput/ChatInput.tsx +++ b/src/components/non-fixed/ChatInput/ChatInput.tsx @@ -1,7 +1,5 @@ import styled from 'styled-components'; import ChatInputForm from '@components/non-fixed/ChatInput/ChatInputForm'; -// input 박스와 종이 비행기 UI 간의 간격 차이는 8px이고 종이비행기와 전체 박스는 16px로 어느 상황에서나 same -// 아이템 간의 간격은 8px이고 양 끝단 item과 박스 간의 간격은 16px임. 따라서 전체 상자의 padding을 그냥 16px로 고정하면 됨 const StyledChatInputContainer = styled.div` width: 375px; diff --git a/src/components/non-fixed/ChatInput/ChatInputForm.tsx b/src/components/non-fixed/ChatInput/ChatInputForm.tsx index 7ccd23c..1611c1a 100644 --- a/src/components/non-fixed/ChatInput/ChatInputForm.tsx +++ b/src/components/non-fixed/ChatInput/ChatInputForm.tsx @@ -10,7 +10,6 @@ import { } from '@context/state/atom'; import { adjustTimeForUserLocation } from '@utils/makeTimeString'; import sortByDate from '@utils/sortArrayByDate'; -import useScrollToBottom from '@hooks/useScrollToBottom'; const StyledChatInputForm = styled.form` width: 100%; @@ -79,7 +78,6 @@ export default function ChatInputForm() { messageDateArrayState ); const [userNumber, setUserNumber] = useRecoilState(userNumberState); - const [scrollToBottom, setScrollFunction] = useScrollToBottom(); function handleToggleIsInputBoxFocused() { // 포커스가 되었는데 값이 있는 상태에서 블러가 풀리는 것으로 가면 상태를 변경시키지 않음 => 내용이 없는 상태에서 메시지 전송도 막아줌 @@ -92,10 +90,9 @@ export default function ChatInputForm() { function handleSubmitForm(ev: any) { if (isInputBoxFocused === false) { ev.preventDefault(); - // console.log('hi'); } else if (isInputBoxFocused === true) { ev.preventDefault(); - // console.log('good'); + const createdAt = adjustTimeForUserLocation(); const createdDate = createdAt.slice(0, 10); const content = inputRef.current?.value as string; @@ -139,7 +136,6 @@ export default function ChatInputForm() { if (inputRef.current !== null) { inputRef.current.value = ''; } - // scrollToBottom(); } } From 0a1df882fa1b7f6d55d8bc343ff6e3995f8f97e7 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Fri, 29 Mar 2024 17:30:43 +0900 Subject: [PATCH 33/81] =?UTF-8?q?Fix=20:=20=EB=B0=B0=ED=8F=AC=20=ED=99=98?= =?UTF-8?q?=EA=B2=BD=EC=97=90=EC=84=9C=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=EB=A0=8C=EB=8D=94=EB=A7=81=20=EC=8B=A4=ED=8C=A8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20&=20=EC=BD=98=EC=86=94=20=EC=B6=9C=EB=A0=A5?= =?UTF-8?q?=EB=AC=B8=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../non-fixed/ChatBody/ChatLog/ChatLogRight/ChatLogRight.tsx | 1 - src/components/non-fixed/ChatInput/ChatInputForm.tsx | 1 - 2 files changed, 2 deletions(-) diff --git a/src/components/non-fixed/ChatBody/ChatLog/ChatLogRight/ChatLogRight.tsx b/src/components/non-fixed/ChatBody/ChatLog/ChatLogRight/ChatLogRight.tsx index 2cc0e38..1b0bd2c 100644 --- a/src/components/non-fixed/ChatBody/ChatLog/ChatLogRight/ChatLogRight.tsx +++ b/src/components/non-fixed/ChatBody/ChatLog/ChatLogRight/ChatLogRight.tsx @@ -71,7 +71,6 @@ export default function ChatLogRight({ const createdHourMinute = createdAt.slice(11, 16); const createdDate = createdAt.slice(0, 10); const deepCopiedMessageData = JSON.parse(JSON.stringify(messageData)); - console.log(deepCopiedMessageData); function handleDoubleClickMessage() { // 자신과 달라야 한다. diff --git a/src/components/non-fixed/ChatInput/ChatInputForm.tsx b/src/components/non-fixed/ChatInput/ChatInputForm.tsx index 1611c1a..774888b 100644 --- a/src/components/non-fixed/ChatInput/ChatInputForm.tsx +++ b/src/components/non-fixed/ChatInput/ChatInputForm.tsx @@ -115,7 +115,6 @@ export default function ChatInputForm() { tmpMessageData[createdDate] = []; tmpMessageData[createdDate].push(newMessageData); } else { - console.log(tmpMessageData[createdDate]); // 기존의 배열을 구조분해하여 다른 메모리 값을 가진 배열로 받음 const prevDateArray = [...tmpMessageData[createdDate]]; prevDateArray.push(newMessageData); From 78a2ab47c678dc8d5933f71e346dd8fb26931bd9 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Fri, 29 Mar 2024 17:43:03 +0900 Subject: [PATCH 34/81] Fix : replace mobile signal image --- .../fixed/ChatHead/IphoneStatusBar/IphoneStatusRightDiv.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/components/fixed/ChatHead/IphoneStatusBar/IphoneStatusRightDiv.tsx b/src/components/fixed/ChatHead/IphoneStatusBar/IphoneStatusRightDiv.tsx index 2b51282..7e8ace1 100644 --- a/src/components/fixed/ChatHead/IphoneStatusBar/IphoneStatusRightDiv.tsx +++ b/src/components/fixed/ChatHead/IphoneStatusBar/IphoneStatusRightDiv.tsx @@ -15,10 +15,7 @@ const StyledIphoneStatusRightDiv = styled.div` export default function IphoneStatusRightDiv() { return ( - + From dce5e0aa6309f792cee64de4f6974433a7adc59f Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Fri, 29 Mar 2024 17:50:38 +0900 Subject: [PATCH 35/81] Fix : fix signal image and apply now exact time --- public/images/{MobileSignal.svg => internetSignal.svg} | 0 .../fixed/ChatHead/IphoneStatusBar/IphoneStatusLeftDiv.tsx | 7 ++++++- .../ChatHead/IphoneStatusBar/IphoneStatusRightDiv.tsx | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) rename public/images/{MobileSignal.svg => internetSignal.svg} (100%) diff --git a/public/images/MobileSignal.svg b/public/images/internetSignal.svg similarity index 100% rename from public/images/MobileSignal.svg rename to public/images/internetSignal.svg diff --git a/src/components/fixed/ChatHead/IphoneStatusBar/IphoneStatusLeftDiv.tsx b/src/components/fixed/ChatHead/IphoneStatusBar/IphoneStatusLeftDiv.tsx index 86f6f17..23e91ad 100644 --- a/src/components/fixed/ChatHead/IphoneStatusBar/IphoneStatusLeftDiv.tsx +++ b/src/components/fixed/ChatHead/IphoneStatusBar/IphoneStatusLeftDiv.tsx @@ -1,4 +1,5 @@ import styled from 'styled-components'; +import { adjustTimeForUserLocation } from '@utils/makeTimeString'; const StyledIphoneStatusLeftDiv = styled.div` width: 54px; @@ -12,5 +13,9 @@ const StyledIphoneStatusLeftDiv = styled.div` `; export default function IphoneStatusLeftDiv() { - return 9:41; + const time = adjustTimeForUserLocation(); + + return ( + {time.slice(11, 16)} + ); } diff --git a/src/components/fixed/ChatHead/IphoneStatusBar/IphoneStatusRightDiv.tsx b/src/components/fixed/ChatHead/IphoneStatusBar/IphoneStatusRightDiv.tsx index 7e8ace1..7c1d7e4 100644 --- a/src/components/fixed/ChatHead/IphoneStatusBar/IphoneStatusRightDiv.tsx +++ b/src/components/fixed/ChatHead/IphoneStatusBar/IphoneStatusRightDiv.tsx @@ -15,7 +15,7 @@ const StyledIphoneStatusRightDiv = styled.div` export default function IphoneStatusRightDiv() { return ( - + From 913a86e55d387fdaf333afe592b0b615335e6946 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Fri, 29 Mar 2024 18:09:50 +0900 Subject: [PATCH 36/81] Fix : try first to fix mobile device UI --- pr.md | 17 +++++++++++++++++ src/styles/globalStyles.tsx | 3 ++- 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 pr.md diff --git a/pr.md b/pr.md new file mode 100644 index 0000000..135c11b --- /dev/null +++ b/pr.md @@ -0,0 +1,17 @@ +> # 배포 링크 +> +> [배포 링크](https://react-messenger-19th-seungwan-wg21.vercel.app/) + +## 📌 구현 기능 + +## 🧠 느낀 점 및 시간 투자 부분 + +## ❓ Key Questions + +- Virtual-DOM은 무엇이고, 이를 사용함으로서 얻는 이점은 무엇인가요?
+ +- + +- + +- diff --git a/src/styles/globalStyles.tsx b/src/styles/globalStyles.tsx index e08dfc8..97fbcd6 100644 --- a/src/styles/globalStyles.tsx +++ b/src/styles/globalStyles.tsx @@ -8,7 +8,8 @@ const GlobalStyles = createGlobalStyle` } html { background-color: #E5E6EB; - height: 100%; + /* height: 100%; */ + height: 100dvh; } body { font-family: 'Pretendard', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; From 789e62f502255e460f73d9903c1fe12730ba36a0 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Fri, 29 Mar 2024 18:16:15 +0900 Subject: [PATCH 37/81] Fix : change home indicator position --- src/components/fixed/HomeIndicator/HomeIndicator.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/fixed/HomeIndicator/HomeIndicator.tsx b/src/components/fixed/HomeIndicator/HomeIndicator.tsx index e3e5247..e4b3f56 100644 --- a/src/components/fixed/HomeIndicator/HomeIndicator.tsx +++ b/src/components/fixed/HomeIndicator/HomeIndicator.tsx @@ -5,7 +5,7 @@ const StyledHomeIndicator = styled.div` height: 34px; position: absolute; bottom: 0; - z-index: 10; + /* z-index: 10; */ background-color: transparent; `; From d53e7d542e9239ef0df6d477ebd9b3aa38a3665b Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Fri, 29 Mar 2024 18:26:52 +0900 Subject: [PATCH 38/81] Fix : fix for mobile --- src/components/AppMain.tsx | 1 + src/components/fixed/HomeIndicator/HomeIndicator.tsx | 4 ++-- src/components/non-fixed/ChatInput/ChatInput.tsx | 6 +++--- src/styles/globalStyles.tsx | 6 +++--- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/components/AppMain.tsx b/src/components/AppMain.tsx index c82fbf6..c678442 100644 --- a/src/components/AppMain.tsx +++ b/src/components/AppMain.tsx @@ -9,6 +9,7 @@ const StyledAppMain = styled.main` display: flex; flex-direction: column; height: 100%; + padding-bottom: 34px; `; export default function AppMain() { diff --git a/src/components/fixed/HomeIndicator/HomeIndicator.tsx b/src/components/fixed/HomeIndicator/HomeIndicator.tsx index e4b3f56..e7d01d5 100644 --- a/src/components/fixed/HomeIndicator/HomeIndicator.tsx +++ b/src/components/fixed/HomeIndicator/HomeIndicator.tsx @@ -1,11 +1,11 @@ import styled from 'styled-components'; const StyledHomeIndicator = styled.div` - width: 100%; + width: inherit; height: 34px; position: absolute; bottom: 0; - /* z-index: 10; */ + z-index: 10; background-color: transparent; `; diff --git a/src/components/non-fixed/ChatInput/ChatInput.tsx b/src/components/non-fixed/ChatInput/ChatInput.tsx index f3f2553..88f0a7d 100644 --- a/src/components/non-fixed/ChatInput/ChatInput.tsx +++ b/src/components/non-fixed/ChatInput/ChatInput.tsx @@ -3,9 +3,9 @@ import ChatInputForm from '@components/non-fixed/ChatInput/ChatInputForm'; const StyledChatInputContainer = styled.div` width: 375px; - height: auto; - min-height: 90px; - padding-bottom: 34px; + /* height: auto; */ + min-height: 56px; + /* padding-bottom: 34px; */ background-color: ${(props) => props.theme.color.white}; opacity: 0.95; `; diff --git a/src/styles/globalStyles.tsx b/src/styles/globalStyles.tsx index 97fbcd6..b8380f8 100644 --- a/src/styles/globalStyles.tsx +++ b/src/styles/globalStyles.tsx @@ -8,7 +8,7 @@ const GlobalStyles = createGlobalStyle` } html { background-color: #E5E6EB; - /* height: 100%; */ + height: 100dvh; } body { @@ -17,11 +17,11 @@ const GlobalStyles = createGlobalStyle` opacity: 0.95; margin: 0 auto; height: 812px; - height: 100%; + height: 100dvh; width: 375px; } #root { - height: 100%; + height: 100dvh; width: 100%; } From 17cf03bc30fe087c377896f42f328e215174bec1 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Fri, 29 Mar 2024 18:29:34 +0900 Subject: [PATCH 39/81] =?UTF-8?q?Fix=20:=20doubleClick=20trigger=20functio?= =?UTF-8?q?n=20=EC=9D=84=20right=20container=EB=A1=9C=20=EC=98=AE=EA=B2=A8?= =?UTF-8?q?=EC=A3=BC=EA=B3=A0=20=ED=95=B4=EB=8B=B9=20=EC=B9=B8=EC=9D=84=20?= =?UTF-8?q?100%=EB=A1=9C=20=EB=8A=98=EB=A0=A4=20=EC=96=B4=EB=94=94?= =?UTF-8?q?=EB=A5=BC=20=EB=8D=94=EB=B8=94=20=ED=81=B4=EB=A6=AD=ED=95=98?= =?UTF-8?q?=EB=8D=98=20=EC=A0=9C=EB=8C=80=EB=A1=9C=20=EB=8F=99=EC=9E=91?= =?UTF-8?q?=ED=95=98=EA=B2=8C=20=EB=A7=8C=EB=93=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../non-fixed/ChatBody/ChatLog/ChatLogRight/ChatLogRight.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/non-fixed/ChatBody/ChatLog/ChatLogRight/ChatLogRight.tsx b/src/components/non-fixed/ChatBody/ChatLog/ChatLogRight/ChatLogRight.tsx index 1b0bd2c..faa3d6c 100644 --- a/src/components/non-fixed/ChatBody/ChatLog/ChatLogRight/ChatLogRight.tsx +++ b/src/components/non-fixed/ChatBody/ChatLog/ChatLogRight/ChatLogRight.tsx @@ -5,6 +5,7 @@ import { messageDataState, userNumberState } from '@context/state/atom'; const StyledChatLogRightContainer = styled.div` display: flex; flex-direction: column; + width: 100%; `; const StyledNameAndMessageContainer = styled.div` @@ -90,8 +91,8 @@ export default function ChatLogRight({ } return ( - - + + {from === 2 ? '김정민' : '김승완'} {createdHourMinute} From 770713ad7b03d85a783e7cdb6e1d23e7176f7d1a Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Fri, 29 Mar 2024 18:34:58 +0900 Subject: [PATCH 40/81] Fix : delete 812px height from body --- src/styles/globalStyles.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/styles/globalStyles.tsx b/src/styles/globalStyles.tsx index b8380f8..6a4c24d 100644 --- a/src/styles/globalStyles.tsx +++ b/src/styles/globalStyles.tsx @@ -16,8 +16,8 @@ const GlobalStyles = createGlobalStyle` background-color: #FFFFFF; opacity: 0.95; margin: 0 auto; - height: 812px; - height: 100dvh; + /* height: 812px; */ + height: 100%; width: 375px; } #root { From adb45b548f2f95df15837e0b4917aad732d7e65f Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Fri, 29 Mar 2024 18:47:51 +0900 Subject: [PATCH 41/81] Fix : reset start MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. z-index 같은거 없앰 --- src/components/AppMain.tsx | 1 - src/components/fixed/HomeIndicator/HomeIndicator.tsx | 4 ++-- src/components/non-fixed/ChatInput/ChatInput.tsx | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/components/AppMain.tsx b/src/components/AppMain.tsx index c678442..c82fbf6 100644 --- a/src/components/AppMain.tsx +++ b/src/components/AppMain.tsx @@ -9,7 +9,6 @@ const StyledAppMain = styled.main` display: flex; flex-direction: column; height: 100%; - padding-bottom: 34px; `; export default function AppMain() { diff --git a/src/components/fixed/HomeIndicator/HomeIndicator.tsx b/src/components/fixed/HomeIndicator/HomeIndicator.tsx index e7d01d5..e4b3f56 100644 --- a/src/components/fixed/HomeIndicator/HomeIndicator.tsx +++ b/src/components/fixed/HomeIndicator/HomeIndicator.tsx @@ -1,11 +1,11 @@ import styled from 'styled-components'; const StyledHomeIndicator = styled.div` - width: inherit; + width: 100%; height: 34px; position: absolute; bottom: 0; - z-index: 10; + /* z-index: 10; */ background-color: transparent; `; diff --git a/src/components/non-fixed/ChatInput/ChatInput.tsx b/src/components/non-fixed/ChatInput/ChatInput.tsx index 88f0a7d..55adfc1 100644 --- a/src/components/non-fixed/ChatInput/ChatInput.tsx +++ b/src/components/non-fixed/ChatInput/ChatInput.tsx @@ -4,8 +4,8 @@ import ChatInputForm from '@components/non-fixed/ChatInput/ChatInputForm'; const StyledChatInputContainer = styled.div` width: 375px; /* height: auto; */ - min-height: 56px; - /* padding-bottom: 34px; */ + min-height: 90px; + padding-bottom: 34px; background-color: ${(props) => props.theme.color.white}; opacity: 0.95; `; From c375615b686ba32b7d6443a623e12e8ed2197a4c Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Fri, 29 Mar 2024 18:56:31 +0900 Subject: [PATCH 42/81] =?UTF-8?q?Fix=20:=20chatBody=20height=20=EC=86=8D?= =?UTF-8?q?=EC=84=B1=20get=20rid=20of?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/non-fixed/ChatBody/ChatBody.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/non-fixed/ChatBody/ChatBody.tsx b/src/components/non-fixed/ChatBody/ChatBody.tsx index ff9bb3a..ec36594 100644 --- a/src/components/non-fixed/ChatBody/ChatBody.tsx +++ b/src/components/non-fixed/ChatBody/ChatBody.tsx @@ -9,12 +9,12 @@ import useScrollToBottom from '@hooks/useScrollToBottom'; const StyledChatBodyContainer = styled.div` flex-grow: 1; - flex-shrink: 0; + /* flex-shrink: 0; */ margin-top: 16px; margin-right: 16px; margin-left: 16px; width: 343px; - height: 595px; + /* height: 595px; */ overflow-y: scroll; `; From 2a1a00742e924820323c29e9ee49ffdbf73809c7 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Fri, 29 Mar 2024 19:31:02 +0900 Subject: [PATCH 43/81] =?UTF-8?q?Fix=20:=20=EC=A0=9C=EC=B6=9C=20=EC=8B=9C?= =?UTF-8?q?=EC=9D=98=20isFocused=20=EC=83=81=ED=83=9C=20=EA=B0=90=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 일단 폼이 제출되면 안에서 false로 바꿔버린다. 2. 제출을 엔터키로 하면 상관 없는데 제출 버튼으로 하면 isFocused가 풀릴 수도 있으니 false로 만드는 함수에서는 이미 isFocused가 true인지와 내용이 있는지를 검사한다 --- src/components/non-fixed/ChatBody/ChatBody.tsx | 2 -- .../non-fixed/ChatInput/ChatInputForm.tsx | 16 +++++++++++----- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/components/non-fixed/ChatBody/ChatBody.tsx b/src/components/non-fixed/ChatBody/ChatBody.tsx index ec36594..71ddccf 100644 --- a/src/components/non-fixed/ChatBody/ChatBody.tsx +++ b/src/components/non-fixed/ChatBody/ChatBody.tsx @@ -9,12 +9,10 @@ import useScrollToBottom from '@hooks/useScrollToBottom'; const StyledChatBodyContainer = styled.div` flex-grow: 1; - /* flex-shrink: 0; */ margin-top: 16px; margin-right: 16px; margin-left: 16px; width: 343px; - /* height: 595px; */ overflow-y: scroll; `; diff --git a/src/components/non-fixed/ChatInput/ChatInputForm.tsx b/src/components/non-fixed/ChatInput/ChatInputForm.tsx index 774888b..8bcbf23 100644 --- a/src/components/non-fixed/ChatInput/ChatInputForm.tsx +++ b/src/components/non-fixed/ChatInput/ChatInputForm.tsx @@ -8,6 +8,7 @@ import { messageDateArrayState, userNumberState, } from '@context/state/atom'; +import { flushSync } from 'react-dom'; import { adjustTimeForUserLocation } from '@utils/makeTimeString'; import sortByDate from '@utils/sortArrayByDate'; @@ -79,12 +80,15 @@ export default function ChatInputForm() { ); const [userNumber, setUserNumber] = useRecoilState(userNumberState); - function handleToggleIsInputBoxFocused() { - // 포커스가 되었는데 값이 있는 상태에서 블러가 풀리는 것으로 가면 상태를 변경시키지 않음 => 내용이 없는 상태에서 메시지 전송도 막아줌 + function handleMakeIsInputfocusedFalse() { if (isInputBoxFocused && inputRef.current?.value !== '') { return; } - setIsInputBoxFocused((prev) => !prev); + setIsInputBoxFocused(false); + } + + function handleMakeIsInputfocusedTrue() { + setIsInputBoxFocused(true); } function handleSubmitForm(ev: any) { @@ -135,6 +139,8 @@ export default function ChatInputForm() { if (inputRef.current !== null) { inputRef.current.value = ''; } + setIsInputBoxFocused(false); + inputRef.current?.blur(); } } @@ -145,8 +151,8 @@ export default function ChatInputForm() { )} From 60bb765b8a9ef174686cb8905fce6ba92f20f882 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Fri, 29 Mar 2024 19:34:10 +0900 Subject: [PATCH 44/81] =?UTF-8?q?Fix=20:=20=EC=95=84=EB=AC=B4=EA=B2=83?= =?UTF-8?q?=EB=8F=84=20=EC=9E=85=EB=A0=A5=ED=95=98=EC=A7=80=20=EC=95=8A?= =?UTF-8?q?=EC=95=98=EC=9D=84=20=EA=B2=BD=EC=9A=B0=EC=97=90=EB=8A=94=20?= =?UTF-8?q?=EB=94=B0=EB=A1=9C=20=EB=A9=94=EC=8B=9C=EC=A7=80=EA=B0=80=20?= =?UTF-8?q?=EB=82=A0=EB=9D=BC=EA=B0=80=EC=A7=80=20=EC=95=8A=EA=B2=8C=20?= =?UTF-8?q?=EC=A1=B0=EC=A0=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/non-fixed/ChatInput/ChatInputForm.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/non-fixed/ChatInput/ChatInputForm.tsx b/src/components/non-fixed/ChatInput/ChatInputForm.tsx index 8bcbf23..07692b0 100644 --- a/src/components/non-fixed/ChatInput/ChatInputForm.tsx +++ b/src/components/non-fixed/ChatInput/ChatInputForm.tsx @@ -95,6 +95,7 @@ export default function ChatInputForm() { if (isInputBoxFocused === false) { ev.preventDefault(); } else if (isInputBoxFocused === true) { + if (inputRef.current?.value === '') return; ev.preventDefault(); const createdAt = adjustTimeForUserLocation(); From 222de2aa954db6f5dacfdf26d28279021b086c61 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Fri, 29 Mar 2024 22:42:07 +0900 Subject: [PATCH 45/81] Docs : make pr.md file --- pr.md | 49 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/pr.md b/pr.md index 135c11b..628e830 100644 --- a/pr.md +++ b/pr.md @@ -4,14 +4,55 @@ ## 📌 구현 기능 +1. 피그마에 나타난 화면 UI 구현 => `styled-components`의 `ThemeProvider`를 통해 디자인 시스템 구축 +2. 기존의 **json** 파일에 나타난 데이터를 보여주거나 이미 데이터가 존재한다면 이를 보여주기 +3. 사용자가 채팅을 입력하고, 기존의 데이터에 뒤이어 UI로 구현 => 채팅 입력 시, 채팅창의 최하단으로 포커스 이동 +4. 사용자가 이미 보낸 상대방의 메시지를 더블 클릭하면 하트 UI가 생성되고, 다시 더블 클릭하면 사라짐 +5. 채팅방 상단의 프로필을 클릭하면 상대방으로 사용자가 바뀜 + ## 🧠 느낀 점 및 시간 투자 부분 +### 시간 투자 💪🏼 + +1. 절대 경로를 이용하기 위해 `tsconfig.json` 파일을 이용하려 했지만, CRA의 기본 동작상 이를 그냥 무시하기에 `webpack` 빌드 툴의 설정을 바꿔야 했습니다. 이를 `craco`(CRA configuration Overriding)JS의 설정을 하는데에 시간 소요가 다소 걸렸습니다.
+2. 기존의 state를 이용한 prop drilling이나 `contextAPI`를 사용해도 좋지만 recoilJS 전역 상태관리 라이브러리를 사용하니 쉬웠던 것 같습니다.
+3. 새로 고침을 해주어도 기존의 사항이 반영되도록 localStorage를 이용했습니다. 로컬 스토리지에 기존의 데이터가 없다면 json 파일의 데이터를 상태로 정하고 이를 로컬 스토리지에 반영하며, 기존의 데이터가 존재한다면 상태로 이용하는 분기적인 로직을 구현했습니다. 다만, 추후에 메시지의 추가나 더블 클릭을 통한 좋아요 하트 표시의 생성과 삭제 시에 객체가 `readOnly` 속성인 점을 유의해야 했던 것 같습니다. 이를 위해 스프레드 연산자를 사용하지 못하는 경우도 많았습니다. 스프레드 연산자를 사용하면 중첩된 속성이 다시 객체 혹은 배열이라면 `deepcopy`가 되지 않기 때문에 `JSON.parse(JSON.stringify({기존 객체}))`방식을 이용했습니다.
+4. 사용자가 input 박스에 focus 했을을 판단하기 위해 `isInputBoxFocused` 속성을 이용했습니다. 기존에 풀려있다가 focus되면 true로 만들어주는 경우, 아직 focus 안됐는데 제출 버튼을 클릭한 경우(이는 무시됨), focus 되었는데 메시지가 없는 경우(무시됨), focus 되었는데 제출 버튼을 클릭했지만 이것이 메시지를 담는 경우에 onBlur 때문에 발동되는 함수를 조기에 `return`시키고 다음의 onSubmit 트리거 함수에서 data 상태와 로컬스토리지를 업데이트 시킨 뒤 그 다음 `isInputBoxFocused` 속성을 `false`로 바꿔주는 로직을 작성했습니다. +5. 메시지를 추가하고 스크롤을 제일 아래로 내리는 것을 DOM 요소의 `scrollTop`과 `scrollHeight` 속성을 이용하여 구현하고자 했습니다. 하지만 이는 추가적인 메시지 DOM 요소가 생성되기 이전에 트리거 되므로 기존의 DOM 요소 높이만을 계산합니다. 따라서 이를 `messageData` 상태가 배뀌면 바로 트리거 되도록 `chatBody` 컴포넌트 내의 `useEffect` 훅을 이용하여 구현해주니 해결되었습니다.

+ +### 느낀 점 ❗️ + +1. 기존에 심미적으로 깔끔하고 이쁜 레이아웃을 만드는 것보다는 다른 방면으로 UX를 향상시키는 것에 관심이 많은 편이기에, 디자이너 분께서 UI 초안을 제시해주셔서 상당히 좋았던 것 같습니다. 또한 UI 컴포넌트마다 재사용될 수 있도록 분리해 놓으셔서 이를 활용해 재사용성을 높일 수 있었습니다. + +2. 어떤 상태를 설정하고 이를 어떻게 활용할지 미리 설계해두고 들어가면 참 좋겠다는 생각, 이를 위해서는 UI 컴포넌트의 단위를 잘 설정해야 한다는 것을 알 수 있었습니다. + +3. 타입스크립트를 사용하면 내가 에상하지 못하는 케이스를 시스템이 잡아줄 수 있다는 것을 알 수 있었습니다. + ## ❓ Key Questions -- Virtual-DOM은 무엇이고, 이를 사용함으로서 얻는 이점은 무엇인가요?
+- JavaScript를 사용할때에 비해 TypeScript를 사용할 때의 장점은 무엇인가요?
+ +> 타입스크립트를 사용하면 정적 타입 검사를 통해 에러를 런타임 이전에 방지할 수 있습니다.
+ +타입스크립트는 Javascript 언어의 superset으로서, tsc와 같은 컴파일러의 컴파일링을 통해 Javascript로 변환되어 실행됩니다. 타입 스크립트를 사용하면 `type alias`나 `interface`와 같은 개념을 통해 매개변수에 전달되는 인자의 타입을 강제할 수 있어 컴파일 시간에 오류를 잡아낼 수 있습니다. 이는 프로그래밍 작업 시에 vscode 단에서 오류를 미리 표시해주어 상당히 편리하다고 할 수 있습니다.
+특정 함수나 객체를 작성할 때 vscode 단에서 미리 매개변수나 속성들을 추천해주어 개발 생산성이 상당히 올라간다고 할 수 있습니다. + +- 디자이너로부터 전달받은 피그마 링크 혹은, 피그마 캡처본
+ +- 컴포넌트를 분리한 기준은 무엇인가요?
+ +크게 UI에서 고정이 되는 부분과, 유동적으로 바뀔 수 있는 부분에 대해서 생각을 많이 했던 것 같습니다. 화면에서 채팅의 개수가 늘어날 때마다 `chatBody`이 스크롤 되어야 함, 사용자가 `input box`에 focus 했을 때 왼쪽의 플러스 버튼이 사라지고, 다시 해제되었을 때에는 생성되는 것도 상태를 이용한 조건부 렌더링을 통해 UI를 구축했습니다.

+크게 `iphoneStatusBar`, `chatHeadNav`를 상단부에, 중간에 `chatBody`를, 아래에 `chatInput`과 `HomeIndicator`를 구성했고 마지막 `HomeIndicator` main 요소에 참조하여 항상 하단에 고정해주었습니다. + +> 다양한 UI 컴포넌트들은 `components`, recoil을 통한 상태 생성은 `context`, css 설정 관련해서는 `styles`, ts를 위한 공통적인 타입 정의는 `type`, 여기저기에서 쓰일 간단한 함수들은 `utils` 디렉터리에 구분하여 만들어 주었습니다. + +- 디자인 시스템을 적용하면서 느낀 점은 무엇인가요?
+ +디자인 시스템을 적용하며 느낀점은 바로 "재활용성"입니다. 디자이너 분께서 특정 컨셉을 활용하시기에, 중구 난방으로 튀는 디자인 원칙이 아닌 일관적인 UI 시스템은 사용자로 하여금 UX를 향상할 수 있습니다. 이를 `styled-components` 라이브러리의 시스템을 활용하니 손쉽게 사용할 수 있어 좋았습니다.
-- +다만, 스타일드 컴포넌트는 `css-in-js` 시스템이기 때문에 결국에는 Javascript 코드가 css 파일로 변환되어야함을 의미합니다. 이를 추후에 `nextJS`와 같은 풀스택 프레임워크와 함께 사용된다면 서버 사이드 렌더링이 진행될텐데 어떻게 활용할 수 있을지 궁금합니다. -- +- 디자이너와 소통하며 느낀점은 무엇인가요?
-- +원래 figma 툴의 사용법에 대해서 거의 무지한 수준이었습니다. 하지만 디자이너 분께서 작성해주신 피그마 툴의 요소 간의 거리 측정 기능, 이미지 파일로의 전환 기능 등에 대해서 소개 받을 수 있었고 이해가 잘 가지 않는 것에 대해서도 질문할 수 있었습니다. 또한 매번 개인 카톡으로 연락 드리기보다는 comment 기능을 활용하여 제가 원하는 질문을 남겨놓으면 디자이너 분께서 가능하실 때 보시고 답변해주셔서 유연하다고 생각해습니다. +
From 39205a203dda82782d23b83e33062b42f8c96b9d Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Fri, 29 Mar 2024 22:56:10 +0900 Subject: [PATCH 46/81] Fix : make clear send button activate when user typed at least one character MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 디자이너의 요청에 따라서 사용자가 포커스 뿐만 아니라 한 글자라도 입력했을 때 activate되고 다시 지우면 deactivate 되도록 만듦 --- src/components/non-fixed/ChatInput/ChatInputForm.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/components/non-fixed/ChatInput/ChatInputForm.tsx b/src/components/non-fixed/ChatInput/ChatInputForm.tsx index 07692b0..9af15ca 100644 --- a/src/components/non-fixed/ChatInput/ChatInputForm.tsx +++ b/src/components/non-fixed/ChatInput/ChatInputForm.tsx @@ -88,6 +88,15 @@ export default function ChatInputForm() { } function handleMakeIsInputfocusedTrue() { + if (inputRef.current?.value === '') return; + setIsInputBoxFocused(true); + } + + function handleUserTypeInput() { + if (inputRef.current?.value === '') { + setIsInputBoxFocused(false); + return; + } setIsInputBoxFocused(true); } @@ -154,6 +163,7 @@ export default function ChatInputForm() { From e2f357d95df0ac31165ab67e424deb4dc48ae75b Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Sun, 31 Mar 2024 12:51:46 +0900 Subject: [PATCH 47/81] Fix : fix name text wrapping in window env --- src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavLeft.tsx | 1 + src/components/non-fixed/ChatInput/ChatInputForm.tsx | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavLeft.tsx b/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavLeft.tsx index 65e4516..56b6973 100644 --- a/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavLeft.tsx +++ b/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavLeft.tsx @@ -34,6 +34,7 @@ const StyledUserNameSpan = styled.span` font-weight: 600; ${ST.hoverCursor} margin-left: 4px; + white-space: nowrap; `; export default function ChatHeadNavLeft() { diff --git a/src/components/non-fixed/ChatInput/ChatInputForm.tsx b/src/components/non-fixed/ChatInput/ChatInputForm.tsx index 9af15ca..5188a30 100644 --- a/src/components/non-fixed/ChatInput/ChatInputForm.tsx +++ b/src/components/non-fixed/ChatInput/ChatInputForm.tsx @@ -81,6 +81,7 @@ export default function ChatInputForm() { const [userNumber, setUserNumber] = useRecoilState(userNumberState); function handleMakeIsInputfocusedFalse() { + // 사용자가 인풋에 텍스트를 치다가 다른 화면을 눌러 나왔을때, onBlur() 트리거 함수가 작동했을 때 false가 되면 안됨 if (isInputBoxFocused && inputRef.current?.value !== '') { return; } @@ -93,6 +94,7 @@ export default function ChatInputForm() { } function handleUserTypeInput() { + // 인풋에 타이핑을 하고 있다가 다 지워버려도 해당 함수는 트리거 됨. 다 비어버리면 false로 만들고 함수 종료 if (inputRef.current?.value === '') { setIsInputBoxFocused(false); return; From 38ded233a6ca2d30d6e4a70a2595444758a56621 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Sun, 31 Mar 2024 14:42:01 +0900 Subject: [PATCH 48/81] =?UTF-8?q?Fix=20:=20=EB=8D=94=EB=B8=94=20=ED=81=B4?= =?UTF-8?q?=EB=A6=AD=EC=9D=84=20=ED=95=B4=EC=84=9C=20=ED=95=98=ED=8A=B8=20?= =?UTF-8?q?=ED=91=9C=EC=8B=9C=EB=A5=BC=20=EB=A7=8C=EB=93=A4=EC=97=88?= =?UTF-8?q?=EC=9D=84=20=EB=95=8C=EC=97=90=EB=8F=84=20messageData=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=EA=B0=80=20=EB=B0=94=EB=80=8C=EB=8A=94=20?= =?UTF-8?q?=ED=98=84=EC=83=81=EC=9D=84=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. isMessageLikeButtonClicked 상태를 만들어서 useEffect() 훅 내에서 검사함 --- src/components/non-fixed/ChatBody/ChatBody.tsx | 15 +++++++++++++-- .../ChatLog/ChatLogRight/ChatLogRight.tsx | 10 +++++++++- src/context/state/atom.ts | 5 +++++ 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/components/non-fixed/ChatBody/ChatBody.tsx b/src/components/non-fixed/ChatBody/ChatBody.tsx index 71ddccf..b524dc0 100644 --- a/src/components/non-fixed/ChatBody/ChatBody.tsx +++ b/src/components/non-fixed/ChatBody/ChatBody.tsx @@ -1,5 +1,9 @@ import { useRecoilState } from 'recoil'; -import { messageDataState, messageDateArrayState } from '@context/state/atom'; +import { + messageDataState, + messageDateArrayState, + isMessageLikeButtonClickedState, +} from '@context/state/atom'; import { useEffect, useRef } from 'react'; import styled from 'styled-components'; import OneDateContainer from '@components/non-fixed/ChatBody/OneDateContainer/OneDateContainer'; @@ -18,11 +22,13 @@ const StyledChatBodyContainer = styled.div` export default function ChatBody() { // 여기에서 json 데이터를 불러와서 날짜별로 쪼갠다. 그리고 구분선, 메시지,메시지, 다시 구분선 메시지 메시지 느낌으로 나눠준다 - const chatBodyContainerRef = useRef(null); + const chatBodyContainerRef = useRef(null); const [messageData, setMessageData] = useRecoilState(messageDataState); const [messageDateArray, setMessageDateArray] = useRecoilState( messageDateArrayState ); + const [isMessageLikeButtonClicked, setIsMessageLikeButtonClicked] = + useRecoilState(isMessageLikeButtonClickedState); const [scrollToBottom, setScrollFunction] = useScrollToBottom(); @@ -39,6 +45,11 @@ export default function ChatBody() { // messageData가 변경된 이후에 dom에 반영되고 그 다음에 scroll이 내려가야 새로 생긴 요소까지 반영 useEffect(() => { + // 좋아요 버튼이 눌린 상태면 하트 UI만 만들어주고 다시 false로 만들어주고 끝내야 다음 상태가 정상적으로 반영 + setIsMessageLikeButtonClicked(false); + if (isMessageLikeButtonClicked === true) { + return; + } scrollToBottom(); }, [messageData]); diff --git a/src/components/non-fixed/ChatBody/ChatLog/ChatLogRight/ChatLogRight.tsx b/src/components/non-fixed/ChatBody/ChatLog/ChatLogRight/ChatLogRight.tsx index faa3d6c..dce7c6a 100644 --- a/src/components/non-fixed/ChatBody/ChatLog/ChatLogRight/ChatLogRight.tsx +++ b/src/components/non-fixed/ChatBody/ChatLog/ChatLogRight/ChatLogRight.tsx @@ -1,6 +1,10 @@ import styled from 'styled-components'; import { useRecoilState } from 'recoil'; -import { messageDataState, userNumberState } from '@context/state/atom'; +import { + messageDataState, + userNumberState, + isMessageLikeButtonClickedState, +} from '@context/state/atom'; const StyledChatLogRightContainer = styled.div` display: flex; @@ -68,6 +72,8 @@ export default function ChatLogRight({ }) { const [messageData, setMessageData] = useRecoilState(messageDataState); const [userNumber, setUserNumber] = useRecoilState(userNumberState); + const [isMessageLikeButtonClicked, setIsMessageLikeButtonClicked] = + useRecoilState(isMessageLikeButtonClickedState); const createdHourMinute = createdAt.slice(11, 16); const createdDate = createdAt.slice(0, 10); @@ -88,6 +94,8 @@ export default function ChatLogRight({ 'chatMessageData', JSON.stringify(deepCopiedMessageData) ); + // 메시지 버튼이 눌렸는지에 관한 상태를 true로 만들어주고 chatBody에서 useEffect에서 조건부로 검사함 + setIsMessageLikeButtonClicked(true); } return ( diff --git a/src/context/state/atom.ts b/src/context/state/atom.ts index a688c92..fdb514d 100644 --- a/src/context/state/atom.ts +++ b/src/context/state/atom.ts @@ -26,3 +26,8 @@ export const scrollToBottomState = atom({ key: 'scrollToBottomState', default: () => {}, }); + +export const isMessageLikeButtonClickedState = atom({ + key: 'isMessageLikeButtonClicked', + default: false, +}); From f22347eaae7c141b96cbaffdf62321445e813af2 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Sun, 31 Mar 2024 14:55:29 +0900 Subject: [PATCH 49/81] Refactor : make ChatMain page component into module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. react-router-dom을 이용하여 ChatMain 컴포넌트를 일단 / url에 해당할때 렌더링될 수 있도록 만들어줌 2. 나중에 랜딩 페이지(/) 바꿔줄 수도 있음 --- package-lock.json | 39 +++++++++++++++++++ package.json | 1 + src/App.tsx | 11 ++++-- src/components/{AppMain.tsx => ChatMain.tsx} | 2 +- .../IphoneStatusBar/IphoneStatusRightDiv.tsx | 1 - 5 files changed, 48 insertions(+), 6 deletions(-) rename src/components/{AppMain.tsx => ChatMain.tsx} (93%) diff --git a/package-lock.json b/package-lock.json index 29ccf3c..c1a0e1b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "dayjs": "^1.11.10", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-router-dom": "^6.22.3", "react-scripts": "5.0.1", "recoil": "^0.7.7", "styled-components": "^6.1.8", @@ -3407,6 +3408,14 @@ } } }, + "node_modules/@remix-run/router": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz", + "integrity": "sha512-Oy8rmScVrVxWZVOpEF57ovlnhpZ8CCPlnIIumVcV9nFdiSIrus99+Lw78ekXyGvVDlIsFJbSfmSovJUhCWYV3w==", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@rollup/plugin-babel": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", @@ -15076,6 +15085,36 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "6.22.3", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.3.tgz", + "integrity": "sha512-dr2eb3Mj5zK2YISHK++foM9w4eBnO23eKnZEDs7c880P6oKbrjz/Svg9+nxqtHQK+oMW4OtjZca0RqPglXxguQ==", + "dependencies": { + "@remix-run/router": "1.15.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.22.3", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.3.tgz", + "integrity": "sha512-7ZILI7HjcE+p31oQvwbokjk6OA/bnFxrhJ19n82Ex9Ph8fNAq+Hm/7KchpMGlTgWhUxRHMMCut+vEtNpWpowKw==", + "dependencies": { + "@remix-run/router": "1.15.3", + "react-router": "6.22.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/react-scripts": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", diff --git a/package.json b/package.json index 0e5b2a0..e352eee 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "dayjs": "^1.11.10", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-router-dom": "^6.22.3", "react-scripts": "5.0.1", "recoil": "^0.7.7", "styled-components": "^6.1.8", diff --git a/src/App.tsx b/src/App.tsx index 08cae4d..914a3e2 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,19 +1,22 @@ import { ThemeProvider } from 'styled-components'; import theme from '@styles/theme'; import GlobalStyles from '@styles/globalStyles'; -import AppMain from '@components/AppMain'; +import ChatMain from '@components/ChatMain'; import { RecoilRoot } from 'recoil'; +import { BrowserRouter, Routes, Route } from 'react-router-dom'; function App() { return ( - <> + - + + } /> + - + ); } diff --git a/src/components/AppMain.tsx b/src/components/ChatMain.tsx similarity index 93% rename from src/components/AppMain.tsx rename to src/components/ChatMain.tsx index c82fbf6..0bda8cb 100644 --- a/src/components/AppMain.tsx +++ b/src/components/ChatMain.tsx @@ -11,7 +11,7 @@ const StyledAppMain = styled.main` height: 100%; `; -export default function AppMain() { +export default function ChatMain() { return ( diff --git a/src/components/fixed/ChatHead/IphoneStatusBar/IphoneStatusRightDiv.tsx b/src/components/fixed/ChatHead/IphoneStatusBar/IphoneStatusRightDiv.tsx index 7c1d7e4..ab6d33c 100644 --- a/src/components/fixed/ChatHead/IphoneStatusBar/IphoneStatusRightDiv.tsx +++ b/src/components/fixed/ChatHead/IphoneStatusBar/IphoneStatusRightDiv.tsx @@ -9,7 +9,6 @@ const StyledIphoneStatusRightDiv = styled.div` display: flex; align-items: center; margin-right: 23.6px; - display: center; `; export default function IphoneStatusRightDiv() { From 91a6f83c752413e9ea11d26aa63a7fa962d4002e Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Thu, 4 Apr 2024 15:40:53 +0900 Subject: [PATCH 50/81] =?UTF-8?q?Docs=20:=20=EC=B6=94=EA=B0=80=20=ED=99=95?= =?UTF-8?q?=EC=9E=A5=20=EA=B8=B0=EB=8A=A5=EC=97=90=20=EB=8C=80=ED=95=9C=20?= =?UTF-8?q?=EB=AA=85=EC=84=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- layout.md | 19 +++++++++++++++++++ make.md | 14 +++++++++++--- .../fixed/HomeIndicator/HomeIndicator.tsx | 1 - 3 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 layout.md diff --git a/layout.md b/layout.md new file mode 100644 index 0000000..fc4f0fa --- /dev/null +++ b/layout.md @@ -0,0 +1,19 @@ +# 문서 목적 + +> 본 문서는 전체적인 웹 페이지의 레이아웃에 대한 이해를 돕고자 작성되었습니다. + +## 상단 고정 + +1. ChatHead => 채팅방 페이지. 내부에 `IphoneStatusBar` 컴포넌트가 위치하며 고정적으로 47px의 높이를 가짐. 그 다음에 `ChatHeadNav` 컴포넌트가 위치함(이건 flex-grow를 다 먹어버림) + +## 하단 고정 + +1. 매번 나타나는 것은 `HomeIndicator`임. 이는 부모 컴포넌트인 main이 relative에 맞춰 `position: absolute` 속성을 가지며 그 위에 와야하는 것들은 기존의 문서 흐름에 고대로 있기에 `HomeIndicator` 높이 만큼의 padding-bottom 속성을 부여받는다. +2. `HomeIndicator`위에 바로 오는 것은 Input이 될 수도 있고 tab Bar가 될 수도 있다. 따라서 이때에는 유도리있게 1의 논리를 적용하여 padding-bottom을 적극 활용한다(홈 인디케이터 바는 계속 고정적으로 있으니까). + +## 중앙에 위치하는 컨텐츠 + +1. 일단 main 부모 컴포넌트의 문서 흐름에 맞추어 생각해보았을 때 `flex-grow: 1`로서 본인이 남은 칸을 모두 먹어버리고 `overflow-y: scroll` 속성을 통해 넘치면 스크롤 될 수 있게 만든다. +2. 나머지 속성들은 margin을 계산해서 넣어준다. + +## diff --git a/make.md b/make.md index 881c414..1f78ab6 100644 --- a/make.md +++ b/make.md @@ -12,7 +12,7 @@ - 사용자가 아직 아무것도 입력하지 않았을 경우 메시지 전송 버튼은 비활성화 되어 있어야 함 --> **상태**를 통해 UI를 다르게 렌더링 해주어야 함 - 하단 input에서 + 버튼을 누르면 + 가 이미지, 음성 이미지로 변환될 필요가 있음 input 박스 영역은 `flex - grow : 1` 속성을 부여하여 남는 부분을 모두 차지하도록 함 - 사용자가 제시한 날짜에 따라서 날짜 표시가 있고, 그 다음에 메시지들이 진행되어야 함. -- 카카오톡, 라인과 다르게 왼쪽 오른쪽으로 상대방과의 메시지가 엇갈려서 나타나는 것이 아니라, 왼쪽에 항상 몰려있는 구조임. 메시지의 길이에 따라서 메시지 박스 높이가 조절이 되도록 `height: 속성은 auto` 를 부여하는 것이 좋음 +- 카카오톡, 라인과 다르게 왼쪽 오른쪽으로 상대방과의 메시지가 엇갈려서 나타나는 것이 아니라, 왼쪽에 항상 몰려있는 구조임. 메시지의 길이에 따라서 메시지 박스 높이가 조절이 되도록 `height: 속성은 auto` 를 부여하는 것이 좋음. 아니면 `fit-content` 속성도 나쁘지 않음 - homeIndicator는 채팅 입력 박스가 올라오더라도 아래에 고정되어 있어야 함 ### 나누면 좋을만한 파일 @@ -28,7 +28,7 @@ - 일단 figma에서 제시한 컴포넌트들을 최대한 복사 + 붙여넣기 식으로 활용하고, 추후에 활용을 할 떄 제대로 적용이 안된 것들은 다시 `styled()` 함수를 이용하여 다시 스타일링 진행 - 그냥 단순 이미지를 렌더링해주는 컴포넌트를 imageComponent 라는 디렉터리에 한 파일로 그냥 싸악 적용해주고 끝낸다. 나중에 이를 활용하는 헤더 등의 컴포넌트에서 이들을 받아서 사용한다 -- 매 화면에 고정적으로 등장하는 iphone Status Bar(시간, 와이파이, 배터리를 가지고 있는 헤더)와 homeIndicator는 따로 fixed 컴포넌트로 만들어두고 매번 활용하는 구조. 가장 위에는 `iphoneStatusBar`, 가장 아래에는 `homeIndicator(z-index를 높게 설정)`가 위치하는 구조 +- 매 화면에 고정적으로 등장하는 iphone Status Bar(시간, 와이파이, 배터리를 가지고 있는 헤더)와 homeIndicator는 따로 fixed 컴포넌트로 만들어두고 매번 활용하는 구조. 가장 위에는 `iphoneStatusBar`, 가장 아래에는 `homeIndicator(z-index를 높게 설정)`가 위치하는 구조 => 같은 div의 자식 컴포넌트로 위치시키기 - 해당 컴포넌트가 상황에 따라서 위치나 크기 등이 가변적인지 아닌지에 따라서 `header`, `AppMain`, `footer`로 나뉘어야 함 ### 정민님 figma 기본 구조 @@ -50,4 +50,12 @@ 4. useNumber는 일단 처음은 1로 설정되고 내 메시지는 노란색으로, 상대방은 초록색으로 보인다. => 해결 5. 사용자가 메시지를 입력하면 기존 상태 2개(data와 date 2개)를 바꾸고 이를 로컬 스토리지에도 반영해야한다 => 해결 6. 메시지를 보내면 바로 스크롤되어 내려옴. useRef를 이용해서 scrollTop 속성을 scrollHeight로 바꿔서 아래로 내리면 되는데, chatBody와 chatInput은 형제 속성이라서 함께 건드리기 어려움 => 전역 상태로 관리 -7. 더블 클릭하면 하트가 생겨야 하는데, 남의 것에만 할 수 있어야 한다 +7. 더블 클릭하면 하트가 생겨야 하는데, 남의 것에만 할 수 있어야 한다 => 해결 +8. 여러 사용자들을 구분할 수 있어야 함. 일단 "나는" 김정민임. 따라서 userMode는 당연히 처음에 1로 들어가는 것이 맞긴함. 로컬 스토리지에 저장할 데이터를 `chatMessageData_{유저id숫자}`, `chatMessageDateArray_{유저 id 숫자}` 방식으로 해결하자 + +### 각 페이지 별 기능 + +1. `/friends` : 그냥 친구가 누가 있는지를 보여주기만 하면됨 일단은. (단순 UI) +2. `/message` : 채팅 목록들을 보여주고, 가장 마지막 데이터에 해당하는 사람과 메시지를 보여줄 필요가 있음 +3. `/message/{특정 user id}` : 해당 내용에 해당하는 사람꺼를 보여줌. 나중에 `useParams()` 훅을 이용하여 진행할 수 있음 +4. `/profile` : 본인의 프로필을 조회할 수 있음(단순 UI) diff --git a/src/components/fixed/HomeIndicator/HomeIndicator.tsx b/src/components/fixed/HomeIndicator/HomeIndicator.tsx index e4b3f56..1e729c3 100644 --- a/src/components/fixed/HomeIndicator/HomeIndicator.tsx +++ b/src/components/fixed/HomeIndicator/HomeIndicator.tsx @@ -5,7 +5,6 @@ const StyledHomeIndicator = styled.div` height: 34px; position: absolute; bottom: 0; - /* z-index: 10; */ background-color: transparent; `; From 683a21685515d7c9ce7a6493e5bd11258fb18e88 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Fri, 5 Apr 2024 13:13:47 +0900 Subject: [PATCH 51/81] Feat : make default friends url path layout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. outlet을 적용하여 IphoneStatus와 HomeIndicator 같이 고정적으로 박히는 레이아웃을 고정시켜놓고 그 나머지 UI만 구축하면 되도록 시스템화 2 대부분의 페이지별로 바뀌는 부분은 pages 디렉터리에 넣어놓음 --- make.md | 2 +- src/App.tsx | 9 +++- .../non-fixed/FriendsBody/FriendsBody.tsx | 14 +++++ .../non-fixed/TabBar/BlankSpace.tsx | 10 ++++ src/components/non-fixed/TabBar/TabBar.tsx | 20 ++++++++ .../non-fixed/TabBar/TabIconContainer.tsx | 51 +++++++++++++++++++ src/{components => pages}/ChatMain.tsx | 6 +-- src/pages/CommonLayout.tsx | 21 ++++++++ src/pages/Friends.tsx | 11 ++++ 9 files changed, 138 insertions(+), 6 deletions(-) create mode 100644 src/components/non-fixed/FriendsBody/FriendsBody.tsx create mode 100644 src/components/non-fixed/TabBar/BlankSpace.tsx create mode 100644 src/components/non-fixed/TabBar/TabBar.tsx create mode 100644 src/components/non-fixed/TabBar/TabIconContainer.tsx rename src/{components => pages}/ChatMain.tsx (87%) create mode 100644 src/pages/CommonLayout.tsx create mode 100644 src/pages/Friends.tsx diff --git a/make.md b/make.md index 1f78ab6..5d930a4 100644 --- a/make.md +++ b/make.md @@ -55,7 +55,7 @@ ### 각 페이지 별 기능 -1. `/friends` : 그냥 친구가 누가 있는지를 보여주기만 하면됨 일단은. (단순 UI) +1. `/friends` : 그냥 친구가 누가 있는지를 보여주기만 하면됨 일단은. (단순 UI) => 일단 애플리케이션이 켜지면 바로 들어갈 수 있게 url path를 `/`로 설정하기 2. `/message` : 채팅 목록들을 보여주고, 가장 마지막 데이터에 해당하는 사람과 메시지를 보여줄 필요가 있음 3. `/message/{특정 user id}` : 해당 내용에 해당하는 사람꺼를 보여줌. 나중에 `useParams()` 훅을 이용하여 진행할 수 있음 4. `/profile` : 본인의 프로필을 조회할 수 있음(단순 UI) diff --git a/src/App.tsx b/src/App.tsx index 914a3e2..d85fd26 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,9 +1,11 @@ import { ThemeProvider } from 'styled-components'; import theme from '@styles/theme'; import GlobalStyles from '@styles/globalStyles'; -import ChatMain from '@components/ChatMain'; +import ChatMain from '@pages/ChatMain'; import { RecoilRoot } from 'recoil'; import { BrowserRouter, Routes, Route } from 'react-router-dom'; +import CommonLayout from '@pages/CommonLayout'; +import Friends from '@pages/Friends'; function App() { return ( @@ -12,7 +14,10 @@ function App() { - } /> + }> + }> + + } /> diff --git a/src/components/non-fixed/FriendsBody/FriendsBody.tsx b/src/components/non-fixed/FriendsBody/FriendsBody.tsx new file mode 100644 index 0000000..4a257c7 --- /dev/null +++ b/src/components/non-fixed/FriendsBody/FriendsBody.tsx @@ -0,0 +1,14 @@ +import styled from 'styled-components'; + +const StyledFriendsBody = styled.section` + flex-grow: 1; + overflow-y: scroll; +`; + +export default function FriendsBody() { + return ( + + this is friends body + + ); +} diff --git a/src/components/non-fixed/TabBar/BlankSpace.tsx b/src/components/non-fixed/TabBar/BlankSpace.tsx new file mode 100644 index 0000000..5349661 --- /dev/null +++ b/src/components/non-fixed/TabBar/BlankSpace.tsx @@ -0,0 +1,10 @@ +import styled from 'styled-components'; + +const StyledBlankSpace = styled.div` + height: 34px; + width: 100%; +`; + +export default function BlankSpace() { + return ; +} diff --git a/src/components/non-fixed/TabBar/TabBar.tsx b/src/components/non-fixed/TabBar/TabBar.tsx new file mode 100644 index 0000000..0acf78e --- /dev/null +++ b/src/components/non-fixed/TabBar/TabBar.tsx @@ -0,0 +1,20 @@ +import TabIconContainer from '@components/non-fixed/TabBar/TabIconContainer'; +import BlankSpace from '@components/non-fixed/TabBar/BlankSpace'; + +import styled from 'styled-components'; + +const StyledTabBar = styled.footer` + height: 89px; + background-color: ${(props) => props.theme.color.grayLight}; + display: flex; + flex-direction: column; +`; + +export default function TabBar() { + return ( + + + + + ); +} diff --git a/src/components/non-fixed/TabBar/TabIconContainer.tsx b/src/components/non-fixed/TabBar/TabIconContainer.tsx new file mode 100644 index 0000000..ab71f70 --- /dev/null +++ b/src/components/non-fixed/TabBar/TabIconContainer.tsx @@ -0,0 +1,51 @@ +import styled from 'styled-components'; +// 이미지는 friend.svg, messageLarge.svg, discord24.svg를 사용 + +const StyledTabIconContainer = styled.div` + flex-grow: 1; + display: flex; + justify-content: center; + align-items: center; + column-gap: 32px; +`; + +const StyledIconContainer = styled.div` + width: 56px; + height: 55px; + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: center; +`; + +const StyledImage = styled.img` + width: 24px; + height: 24px; + margin-top: 6px; +`; + +const StyledTabSpan = styled.span` + font-size: 10px; + font-weight: 500; + line-height: 15px; + margin-bottom: 10px; +`; + +export default function TabIconContainer() { + return ( + + + + 친구 + + + + 메시지 + + + + + + + ); +} diff --git a/src/components/ChatMain.tsx b/src/pages/ChatMain.tsx similarity index 87% rename from src/components/ChatMain.tsx rename to src/pages/ChatMain.tsx index 0bda8cb..f395d83 100644 --- a/src/components/ChatMain.tsx +++ b/src/pages/ChatMain.tsx @@ -4,7 +4,7 @@ import ChatBody from '@components/non-fixed/ChatBody/ChatBody'; import ChatInput from '@components/non-fixed/ChatInput/ChatInput'; import HomeIndicator from '@components/fixed/HomeIndicator/HomeIndicator'; -const StyledAppMain = styled.main` +const StyledChatMain = styled.main` position: relative; display: flex; flex-direction: column; @@ -13,11 +13,11 @@ const StyledAppMain = styled.main` export default function ChatMain() { return ( - + - + ); } diff --git a/src/pages/CommonLayout.tsx b/src/pages/CommonLayout.tsx new file mode 100644 index 0000000..b20f74d --- /dev/null +++ b/src/pages/CommonLayout.tsx @@ -0,0 +1,21 @@ +import styled from 'styled-components'; +import { Outlet } from 'react-router-dom'; +import IphoneStatusBar from '@components/fixed/ChatHead/IphoneStatusBar/IphoneStatusBar'; +import HomeIndicator from '@components/fixed/HomeIndicator/HomeIndicator'; + +const StyledCommonLayout = styled.main` + position: relative; + display: flex; + flex-direction: column; + height: 100%; +`; + +export default function CommonLayout() { + return ( + + + + + + ); +} diff --git a/src/pages/Friends.tsx b/src/pages/Friends.tsx new file mode 100644 index 0000000..5e8d18f --- /dev/null +++ b/src/pages/Friends.tsx @@ -0,0 +1,11 @@ +import FriendsBody from '@components/non-fixed/FriendsBody/FriendsBody'; +import TabBar from '@components/non-fixed/TabBar/TabBar'; + +export default function Friends() { + return ( + <> + + + + ); +} From ccc0286a8ce3f3b00f86d87d1f3c431af77521a8 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Fri, 5 Apr 2024 18:05:37 +0900 Subject: [PATCH 52/81] Feat : make TabBar UI and routing --- src/components/non-fixed/TabBar/TabBar.tsx | 1 - .../non-fixed/TabBar/TabIconContainer.tsx | 64 +++++++++++++++---- src/context/state/atom.ts | 6 ++ 3 files changed, 58 insertions(+), 13 deletions(-) diff --git a/src/components/non-fixed/TabBar/TabBar.tsx b/src/components/non-fixed/TabBar/TabBar.tsx index 0acf78e..c46f9d0 100644 --- a/src/components/non-fixed/TabBar/TabBar.tsx +++ b/src/components/non-fixed/TabBar/TabBar.tsx @@ -1,6 +1,5 @@ import TabIconContainer from '@components/non-fixed/TabBar/TabIconContainer'; import BlankSpace from '@components/non-fixed/TabBar/BlankSpace'; - import styled from 'styled-components'; const StyledTabBar = styled.footer` diff --git a/src/components/non-fixed/TabBar/TabIconContainer.tsx b/src/components/non-fixed/TabBar/TabIconContainer.tsx index ab71f70..29696a6 100644 --- a/src/components/non-fixed/TabBar/TabIconContainer.tsx +++ b/src/components/non-fixed/TabBar/TabIconContainer.tsx @@ -1,5 +1,11 @@ +import { useRecoilState } from 'recoil'; +import { userPageModeState } from '@context/state/atom'; +import { useNavigate } from 'react-router-dom'; import styled from 'styled-components'; -// 이미지는 friend.svg, messageLarge.svg, discord24.svg를 사용 + +interface userPageModeAtt { + userPageMode: boolean; +} const StyledTabIconContainer = styled.div` flex-grow: 1; @@ -16,35 +22,69 @@ const StyledIconContainer = styled.div` flex-direction: column; justify-content: flex-start; align-items: center; + + &:hover { + cursor: pointer; + } `; -const StyledImage = styled.img` +const StyledImage = styled.img` width: 24px; height: 24px; margin-top: 6px; + opacity: ${(props) => (props.userPageMode === true ? 1 : 0.35)}; `; -const StyledTabSpan = styled.span` +const StyledTabSpan = styled.span` font-size: 10px; font-weight: 500; line-height: 15px; margin-bottom: 10px; + + color: ${(props) => + props.userPageMode === true + ? props.theme.color.black + : props.theme.color.grayDark}; `; export default function TabIconContainer() { + const [userPageMode, setUserPageMode] = useRecoilState(userPageModeState); + const navigate = useNavigate(); + + function handleNavigate(path: string) { + navigate(path); + } + return ( - - - 친구 + handleNavigate('/')}> + + + 친구 + - - - 메시지 + + handleNavigate('/messages')}> + + + 메시지 + - - - + + handleNavigate('/profile')}> + + + 나 + ); diff --git a/src/context/state/atom.ts b/src/context/state/atom.ts index fdb514d..7068e64 100644 --- a/src/context/state/atom.ts +++ b/src/context/state/atom.ts @@ -31,3 +31,9 @@ export const isMessageLikeButtonClickedState = atom({ key: 'isMessageLikeButtonClicked', default: false, }); + +//유저가 '/'에 있으면 friends고, '/messages'에 있으면 messages, '/chat'에 있으면 chat, '/profile에 있으면' profile임 +export const userPageModeState = atom({ + key: 'userPageMode', + default: 'friends', // 추후에 랜딩 페이지가 생기면 그냥 ''이 될 것임 +}); From e8f8d191b2e34126e1704b15fe712fdfce80626d Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Fri, 5 Apr 2024 18:14:22 +0900 Subject: [PATCH 53/81] Feat : make each page routing in app.tsx --- src/App.tsx | 4 ++++ .../non-fixed/MessageBody/MessageBody.tsx | 14 ++++++++++++++ .../non-fixed/ProfileBody/ProfileBody.tsx | 14 ++++++++++++++ src/pages/Messages.tsx | 11 +++++++++++ src/pages/Profile.tsx | 11 +++++++++++ 5 files changed, 54 insertions(+) create mode 100644 src/components/non-fixed/MessageBody/MessageBody.tsx create mode 100644 src/components/non-fixed/ProfileBody/ProfileBody.tsx create mode 100644 src/pages/Messages.tsx create mode 100644 src/pages/Profile.tsx diff --git a/src/App.tsx b/src/App.tsx index d85fd26..553b11e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -6,6 +6,8 @@ import { RecoilRoot } from 'recoil'; import { BrowserRouter, Routes, Route } from 'react-router-dom'; import CommonLayout from '@pages/CommonLayout'; import Friends from '@pages/Friends'; +import Messages from '@pages/Messages'; +import Profile from '@pages/Profile'; function App() { return ( @@ -16,6 +18,8 @@ function App() { }> }> + }> + }> } /> diff --git a/src/components/non-fixed/MessageBody/MessageBody.tsx b/src/components/non-fixed/MessageBody/MessageBody.tsx new file mode 100644 index 0000000..e67a346 --- /dev/null +++ b/src/components/non-fixed/MessageBody/MessageBody.tsx @@ -0,0 +1,14 @@ +import styled from 'styled-components'; + +const StyledMessageBody = styled.section` + flex-grow: 1; + overflow-y: scroll; +`; + +export default function MessageBody() { + return ( + + This is Messages Body + + ); +} diff --git a/src/components/non-fixed/ProfileBody/ProfileBody.tsx b/src/components/non-fixed/ProfileBody/ProfileBody.tsx new file mode 100644 index 0000000..f0037ad --- /dev/null +++ b/src/components/non-fixed/ProfileBody/ProfileBody.tsx @@ -0,0 +1,14 @@ +import styled from 'styled-components'; + +const StyledProfileBody = styled.section` + flex-grow: 1; + overflow-y: scroll; +`; + +export default function ProfileBody() { + return ( + + This is ProfileBody + + ); +} diff --git a/src/pages/Messages.tsx b/src/pages/Messages.tsx new file mode 100644 index 0000000..ed6a026 --- /dev/null +++ b/src/pages/Messages.tsx @@ -0,0 +1,11 @@ +import MessageBody from '@components/non-fixed/MessageBody/MessageBody'; +import TabBar from '@components/non-fixed/TabBar/TabBar'; + +export default function Messages() { + return ( + <> + + + + ); +} diff --git a/src/pages/Profile.tsx b/src/pages/Profile.tsx new file mode 100644 index 0000000..d5bea16 --- /dev/null +++ b/src/pages/Profile.tsx @@ -0,0 +1,11 @@ +import ProfileBody from '@components/non-fixed/ProfileBody/ProfileBody'; +import TabBar from '@components/non-fixed/TabBar/TabBar'; + +export default function Profile() { + return ( + <> + + + + ); +} From cbecabe17524d4a82a8fcdee190df1b47f71c623 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Fri, 5 Apr 2024 18:34:04 +0900 Subject: [PATCH 54/81] =?UTF-8?q?Fix=20&=20page=20routing=20:=20userPageMo?= =?UTF-8?q?de=20=EC=95=9E=EC=97=90=20$=EB=A5=BC=20=EB=B6=99=EC=97=AC=20htm?= =?UTF-8?q?l=20=EC=86=8D=EC=84=B1=EC=9D=B4=20=EC=95=84=EB=8B=98=EC=9D=84?= =?UTF-8?q?=20=EB=AA=85=EC=8B=9C=20&=20=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=EA=B0=80=20=EB=9D=BC=EC=9A=B0=ED=8C=85=20=EB=90=98=EB=A9=B4=20?= =?UTF-8?q?=ED=83=AD=EB=B0=94=EC=9D=98=20=ED=95=B4=EB=8B=B9=20=EC=95=84?= =?UTF-8?q?=EB=8B=8C=20=EB=B6=80=EB=B6=84=EC=9D=80=20opacity=EB=A5=BC=20?= =?UTF-8?q?=EB=82=AE=EA=B2=8C=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../non-fixed/TabBar/TabIconContainer.tsx | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/components/non-fixed/TabBar/TabIconContainer.tsx b/src/components/non-fixed/TabBar/TabIconContainer.tsx index 29696a6..08da2b5 100644 --- a/src/components/non-fixed/TabBar/TabIconContainer.tsx +++ b/src/components/non-fixed/TabBar/TabIconContainer.tsx @@ -1,10 +1,12 @@ +import { useEffect } from 'react'; import { useRecoilState } from 'recoil'; import { userPageModeState } from '@context/state/atom'; -import { useNavigate } from 'react-router-dom'; +import { useNavigate, useLocation } from 'react-router-dom'; import styled from 'styled-components'; interface userPageModeAtt { - userPageMode: boolean; + // 속성은 html dom에 부여하는 것이라서 일부러 $ 표시를 붙여준 것이다 + $userPageMode: boolean; } const StyledTabIconContainer = styled.div` @@ -32,7 +34,7 @@ const StyledImage = styled.img` width: 24px; height: 24px; margin-top: 6px; - opacity: ${(props) => (props.userPageMode === true ? 1 : 0.35)}; + opacity: ${(props) => (props.$userPageMode === true ? 1 : 0.35)}; `; const StyledTabSpan = styled.span` @@ -42,12 +44,13 @@ const StyledTabSpan = styled.span` margin-bottom: 10px; color: ${(props) => - props.userPageMode === true + props.$userPageMode === true ? props.theme.color.black : props.theme.color.grayDark}; `; export default function TabIconContainer() { + const location = useLocation(); const [userPageMode, setUserPageMode] = useRecoilState(userPageModeState); const navigate = useNavigate(); @@ -55,14 +58,20 @@ export default function TabIconContainer() { navigate(path); } + useEffect(() => { + if (location.pathname === '/') setUserPageMode('friends'); + else if (location.pathname === '/messages') setUserPageMode('messages'); + else if (location.pathname === '/profile') setUserPageMode('profile'); + }, [location, setUserPageMode]); // useEffect() 내부에서 상태를 변화시키는 것이 맞는가? + return ( handleNavigate('/')}> - + 친구 @@ -70,9 +79,9 @@ export default function TabIconContainer() { handleNavigate('/messages')}> - + 메시지 @@ -80,9 +89,9 @@ export default function TabIconContainer() { handleNavigate('/profile')}> - + From d97b00115113c72262c293eef97bc10c5d42ab93 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Fri, 5 Apr 2024 18:42:46 +0900 Subject: [PATCH 55/81] =?UTF-8?q?Fix=20:=20useEffect()=20=ED=9B=85=20?= =?UTF-8?q?=EB=82=B4=EC=97=90=EC=84=9C=20setUserPageMode()=20=EB=A5=BC=20?= =?UTF-8?q?=ED=86=B5=ED=95=B4=20=EC=83=81=ED=83=9C=EB=A5=BC=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=EC=8B=9C=ED=82=A4=EA=B8=B0=20=EB=B3=B4=EB=8B=A4?= =?UTF-8?q?=EB=8A=94,=20=EC=96=B4=EC=B0=A8=ED=94=BC=20=EC=83=81=ED=83=9C?= =?UTF-8?q?=20=EC=B2=98=EB=A6=AC=EB=8A=94=20=EB=B0=B0=EC=B9=98=EB=90=98?= =?UTF-8?q?=EA=B8=B0=20=EB=95=8C=EB=AC=B8=EC=97=90=20=ED=95=B8=EB=93=A4?= =?UTF-8?q?=EB=9F=AC=20=ED=95=A8=EC=88=98=20=EB=82=B4=EB=B6=80=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EB=B6=84=EA=B8=B0=ED=95=98=EC=97=AC=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=ED=95=B4=EC=A3=BC=EC=97=88=EC=9D=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../non-fixed/TabBar/TabIconContainer.tsx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/components/non-fixed/TabBar/TabIconContainer.tsx b/src/components/non-fixed/TabBar/TabIconContainer.tsx index 08da2b5..b0ed3fe 100644 --- a/src/components/non-fixed/TabBar/TabIconContainer.tsx +++ b/src/components/non-fixed/TabBar/TabIconContainer.tsx @@ -55,15 +55,16 @@ export default function TabIconContainer() { const navigate = useNavigate(); function handleNavigate(path: string) { + if (path === '/') { + setUserPageMode('friends'); + } else if (path === '/messages') { + setUserPageMode('messages'); + } else if (path === '/profile') { + setUserPageMode('profile'); + } navigate(path); } - useEffect(() => { - if (location.pathname === '/') setUserPageMode('friends'); - else if (location.pathname === '/messages') setUserPageMode('messages'); - else if (location.pathname === '/profile') setUserPageMode('profile'); - }, [location, setUserPageMode]); // useEffect() 내부에서 상태를 변화시키는 것이 맞는가? - return ( handleNavigate('/')}> From 82956e135132fbcc6ccf14035df58717e5232c35 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Fri, 5 Apr 2024 19:30:01 +0900 Subject: [PATCH 56/81] Feat : make friends search head UI --- public/index.html | 1 - .../non-fixed/FriendsBody/FriendsBody.tsx | 5 +- .../FriendsSearchHead/FriendsSearchHead.tsx | 105 ++++++++++++++++++ 3 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 src/components/non-fixed/FriendsBody/FriendsSearchHead/FriendsSearchHead.tsx diff --git a/public/index.html b/public/index.html index b69b001..5abcab9 100644 --- a/public/index.html +++ b/public/index.html @@ -19,7 +19,6 @@ React App -
diff --git a/src/components/non-fixed/FriendsBody/FriendsBody.tsx b/src/components/non-fixed/FriendsBody/FriendsBody.tsx index 4a257c7..491029d 100644 --- a/src/components/non-fixed/FriendsBody/FriendsBody.tsx +++ b/src/components/non-fixed/FriendsBody/FriendsBody.tsx @@ -1,14 +1,17 @@ import styled from 'styled-components'; +import FriendsSearchHead from '@components/non-fixed/FriendsBody/FriendsSearchHead/FriendsSearchHead'; const StyledFriendsBody = styled.section` flex-grow: 1; overflow-y: scroll; + display: flex; + flex-direction: column; `; export default function FriendsBody() { return ( - this is friends body + ); } diff --git a/src/components/non-fixed/FriendsBody/FriendsSearchHead/FriendsSearchHead.tsx b/src/components/non-fixed/FriendsBody/FriendsSearchHead/FriendsSearchHead.tsx new file mode 100644 index 0000000..1cb3df4 --- /dev/null +++ b/src/components/non-fixed/FriendsBody/FriendsSearchHead/FriendsSearchHead.tsx @@ -0,0 +1,105 @@ +import styled from 'styled-components'; + +const StyledFriendsSearchHead = styled.div` + widht: 100%; + height: 114px; + display: flex; + justify-content: center; + align-items: center; +`; + +const StyledFriendSearchInnerContainer = styled.div` + width: 343px; + height: 82px; + display: flex; + flex-direction: column; + justify-content: space-between; + row-gap: 10px; +`; + +const StyledFriendSearchUpper = styled.div` + display: flex; + justify-content: space-between; + align-items: center; +`; + +const StyledNameSpan = styled.p` + width: 35px; + height: 30px; + display: flex; + justify-content: center; + align-items: center; + font-size: 20px; + font-weight: 600; + margin-left: 8px; + color: ${(props) => props.theme.color.black}; +`; + +const StyledNewMessage = styled.div` + background-color: ${(props) => props.theme.color.grayMedium}; + width: 111px; + height: 32px; + border-radius: 20px; + display: flex; + justify-content: center; + align-items: center; + column-gap: 4px; + + &:hover { + cursor: pointer; + } +`; + +const StyledNewMessageSpan = styled.span` + font-family: Pretendard; + font-size: ${(props) => props.theme.textStyle.fontSize.body1}; + line-height: ${(props) => props.theme.textStyle.lineHeight.body1}; +`; + +const StyledFriendSearchLower = styled.div` + width: 100%; + height: 40px; + position: relative; +`; + +const StyledFriendSearchLowerInput = styled.input` + border: none; + /* text-indent: 36px; */ + width: 100%; + height: 100%; + background-color: ${(props) => props.theme.color.grayMedium}; + border-radius: 20px; + font-size: 13px; + font-weight: 500; + font-family: Pretendard; + line-height: 19.5px; + color: ${(props) => props.theme.color.grayDark}; + padding: 0 0 0 36px; + outline: none; +`; + +const StyledMagnifyingGlass = styled.img` + position: absolute; + top: 10px; + left: 12px; +`; + +export default function FriendsSearchHead() { + return ( + + + + 친구 + + + 새 메시지 + + + + + + + + + ); +} From b4c282d9e6225d91e8d8712c20da9f892265b467 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Fri, 5 Apr 2024 23:32:38 +0900 Subject: [PATCH 57/81] Feat : make profile page head UI --- .../FriendsSearchHead/FriendsSearchHead.tsx | 3 +- .../non-fixed/ProfileBody/ProfileBody.tsx | 3 +- .../ProfilePageHead/ProfilePageHead.tsx | 64 +++++++++++++++++++ src/pages/theme.ts | 39 +++++++++++ 4 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 src/components/non-fixed/ProfileBody/ProfilePageHead/ProfilePageHead.tsx create mode 100644 src/pages/theme.ts diff --git a/src/components/non-fixed/FriendsBody/FriendsSearchHead/FriendsSearchHead.tsx b/src/components/non-fixed/FriendsBody/FriendsSearchHead/FriendsSearchHead.tsx index 1cb3df4..934863b 100644 --- a/src/components/non-fixed/FriendsBody/FriendsSearchHead/FriendsSearchHead.tsx +++ b/src/components/non-fixed/FriendsBody/FriendsSearchHead/FriendsSearchHead.tsx @@ -1,7 +1,7 @@ import styled from 'styled-components'; const StyledFriendsSearchHead = styled.div` - widht: 100%; + width: 100%; height: 114px; display: flex; justify-content: center; @@ -64,7 +64,6 @@ const StyledFriendSearchLower = styled.div` const StyledFriendSearchLowerInput = styled.input` border: none; - /* text-indent: 36px; */ width: 100%; height: 100%; background-color: ${(props) => props.theme.color.grayMedium}; diff --git a/src/components/non-fixed/ProfileBody/ProfileBody.tsx b/src/components/non-fixed/ProfileBody/ProfileBody.tsx index f0037ad..b5220df 100644 --- a/src/components/non-fixed/ProfileBody/ProfileBody.tsx +++ b/src/components/non-fixed/ProfileBody/ProfileBody.tsx @@ -1,3 +1,4 @@ +import ProfilePageHead from '@components/non-fixed/ProfileBody/ProfilePageHead/ProfilePageHead'; import styled from 'styled-components'; const StyledProfileBody = styled.section` @@ -8,7 +9,7 @@ const StyledProfileBody = styled.section` export default function ProfileBody() { return ( - This is ProfileBody + ); } diff --git a/src/components/non-fixed/ProfileBody/ProfilePageHead/ProfilePageHead.tsx b/src/components/non-fixed/ProfileBody/ProfilePageHead/ProfilePageHead.tsx new file mode 100644 index 0000000..c7ec660 --- /dev/null +++ b/src/components/non-fixed/ProfileBody/ProfilePageHead/ProfilePageHead.tsx @@ -0,0 +1,64 @@ +import styled from 'styled-components'; + +const StyledProfilePageHead = styled.div` + width: 100%; + height: 64px; + display: flex; + justify-content: center; + align-items: center; +`; + +const StyldProfileHeadContainer = styled.div` + width: 343px; + height: 32px; + display: flex; + justify-content: space-between; + align-items: center; + column-gap: 10px; +`; + +const StyledProfileSpan = styled.span` + font-size: ${(props) => props.theme.textStyle.fontSize.h2}; + font-weight: 600; + line-height: ${(props) => props.theme.textStyle.lineHeight.h2}; + margin-left: 8px; +`; + +const StyledProfileEditContainer = styled.div` + width: 124px; + height: 32px; + background-color: ${(props) => props.theme.color.grayMedium}; + border-radius: 20px; + display: flex; + justify-content: center; + align-items: center; + column-gap: 4px; + + &:hover { + cursor: pointer; + } +`; + +const StyledPencilImage = styled.img``; + +const StyledEditSpan = styled.span` + font-family: pretendard; + font-size: ${(props) => props.theme.textStyle.fontSize.body1}; + line-height: ${(props) => props.theme.textStyle.lineHeight.body1}; + font-weight: 500; + color: ${(props) => props.theme.color.grayDark}; +`; + +export default function ProfilePageHead() { + return ( + + + 내 프로필 + + + 프로필 편집 + + + + ); +} diff --git a/src/pages/theme.ts b/src/pages/theme.ts new file mode 100644 index 0000000..db74824 --- /dev/null +++ b/src/pages/theme.ts @@ -0,0 +1,39 @@ +const theme = { + color: { + key: '#5865F2', + white: '#FFFFFF', + grayLight: '#F2F3F5', + grayMedium: '#EBEBEB', + grayDark: '#4E5058', + black: '#060607', + }, + textStyle: { + fontSize: { + h1: '32px', + h2: '20px', + h3: '16px', + body1: '15px', + body2: '15px', + label: '13px', + caption: '10px', + thirtyTwoPixel: '32px', + sixteenPixel: '16px', + eightPixel: '8px', + fourPixel: '4px', + }, + lineHeight: { + h1: '150%', + h2: '150%', + h3: '150%', + body1: '150%', + body2: '150%', + label: '150%', + caption: '150%', + }, + }, + gridStyle: { + display: 'grid', + }, +}; + +export default theme; From a49a2f810a130a67258d61610ab0eaaf4e1a83c3 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Fri, 5 Apr 2024 23:50:39 +0900 Subject: [PATCH 58/81] =?UTF-8?q?Fix=20:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20useEffect()=20=ED=9B=85=EC=9D=98=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=20=EC=97=86=EC=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 그냥 TabIconConatiner에서 userPageMode는 url과 곧바로 연동되면 끝이므로 useLocation() 훅의 pathname 정도만을 확인하는 로직이면 충분하다. --- make.md | 1 + .../non-fixed/TabBar/TabIconContainer.tsx | 16 ++++++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/make.md b/make.md index 5d930a4..bfce8f7 100644 --- a/make.md +++ b/make.md @@ -52,6 +52,7 @@ 6. 메시지를 보내면 바로 스크롤되어 내려옴. useRef를 이용해서 scrollTop 속성을 scrollHeight로 바꿔서 아래로 내리면 되는데, chatBody와 chatInput은 형제 속성이라서 함께 건드리기 어려움 => 전역 상태로 관리 7. 더블 클릭하면 하트가 생겨야 하는데, 남의 것에만 할 수 있어야 한다 => 해결 8. 여러 사용자들을 구분할 수 있어야 함. 일단 "나는" 김정민임. 따라서 userMode는 당연히 처음에 1로 들어가는 것이 맞긴함. 로컬 스토리지에 저장할 데이터를 `chatMessageData_{유저id숫자}`, `chatMessageDateArray_{유저 id 숫자}` 방식으로 해결하자 +9. 클릭해서 들어가는 방식 말고, url path를 직접 쳐서 들어가는 경우는 클릭해서 간 것이 아니기에 tab bar 속성이 제대로 동작하지 않는다는 문제가 발생 => onclick 속성으로 하면 안됨 ### 각 페이지 별 기능 diff --git a/src/components/non-fixed/TabBar/TabIconContainer.tsx b/src/components/non-fixed/TabBar/TabIconContainer.tsx index b0ed3fe..46f552d 100644 --- a/src/components/non-fixed/TabBar/TabIconContainer.tsx +++ b/src/components/non-fixed/TabBar/TabIconContainer.tsx @@ -1,4 +1,3 @@ -import { useEffect } from 'react'; import { useRecoilState } from 'recoil'; import { userPageModeState } from '@context/state/atom'; import { useNavigate, useLocation } from 'react-router-dom'; @@ -54,14 +53,15 @@ export default function TabIconContainer() { const [userPageMode, setUserPageMode] = useRecoilState(userPageModeState); const navigate = useNavigate(); + if (location.pathname === '/') { + setUserPageMode('friends'); + } else if (location.pathname === '/messages') { + setUserPageMode('messages'); + } else if (location.pathname === '/profile') { + setUserPageMode('profile'); + } + function handleNavigate(path: string) { - if (path === '/') { - setUserPageMode('friends'); - } else if (path === '/messages') { - setUserPageMode('messages'); - } else if (path === '/profile') { - setUserPageMode('profile'); - } navigate(path); } From aae29fa7edc36985131a1f605276b77088edf463 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Sun, 7 Apr 2024 15:46:29 +0900 Subject: [PATCH 59/81] Feat : make Friends List UI in /(friends) url path --- make.md | 2 +- .../non-fixed/FriendsBody/FriendsBody.tsx | 2 + .../FriendsBody/FriendsList/FriendsList.tsx | 31 +++++++++++ .../FriendsList/FriendsListItem.tsx | 54 +++++++++++++++++++ .../non-fixed/ProfileBody/ProfileBody.tsx | 6 ++- .../ProfileContent/ProfileContent.tsx | 10 ++++ 6 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 src/components/non-fixed/FriendsBody/FriendsList/FriendsList.tsx create mode 100644 src/components/non-fixed/FriendsBody/FriendsList/FriendsListItem.tsx create mode 100644 src/components/non-fixed/ProfileBody/ProfileContent/ProfileContent.tsx diff --git a/make.md b/make.md index bfce8f7..150c338 100644 --- a/make.md +++ b/make.md @@ -52,7 +52,7 @@ 6. 메시지를 보내면 바로 스크롤되어 내려옴. useRef를 이용해서 scrollTop 속성을 scrollHeight로 바꿔서 아래로 내리면 되는데, chatBody와 chatInput은 형제 속성이라서 함께 건드리기 어려움 => 전역 상태로 관리 7. 더블 클릭하면 하트가 생겨야 하는데, 남의 것에만 할 수 있어야 한다 => 해결 8. 여러 사용자들을 구분할 수 있어야 함. 일단 "나는" 김정민임. 따라서 userMode는 당연히 처음에 1로 들어가는 것이 맞긴함. 로컬 스토리지에 저장할 데이터를 `chatMessageData_{유저id숫자}`, `chatMessageDateArray_{유저 id 숫자}` 방식으로 해결하자 -9. 클릭해서 들어가는 방식 말고, url path를 직접 쳐서 들어가는 경우는 클릭해서 간 것이 아니기에 tab bar 속성이 제대로 동작하지 않는다는 문제가 발생 => onclick 속성으로 하면 안됨 +9. 클릭해서 들어가는 방식 말고, url path를 직접 쳐서 들어가는 경우는 클릭해서 간 것이 아니기에 tab bar 속성이 제대로 동작하지 않는다는 문제가 발생 => onclick 속성으로 하면 안됨 : `useEffect()` 훅을 따로 사용하지 않고 그냥 `useLocation()` 훅으로 받은 path를 검사해서 상태를 변경해주었음 ### 각 페이지 별 기능 diff --git a/src/components/non-fixed/FriendsBody/FriendsBody.tsx b/src/components/non-fixed/FriendsBody/FriendsBody.tsx index 491029d..48a880a 100644 --- a/src/components/non-fixed/FriendsBody/FriendsBody.tsx +++ b/src/components/non-fixed/FriendsBody/FriendsBody.tsx @@ -1,5 +1,6 @@ import styled from 'styled-components'; import FriendsSearchHead from '@components/non-fixed/FriendsBody/FriendsSearchHead/FriendsSearchHead'; +import FriendsList from '@components/non-fixed/FriendsBody/FriendsList/FriendsList'; const StyledFriendsBody = styled.section` flex-grow: 1; @@ -12,6 +13,7 @@ export default function FriendsBody() { return ( + ); } diff --git a/src/components/non-fixed/FriendsBody/FriendsList/FriendsList.tsx b/src/components/non-fixed/FriendsBody/FriendsList/FriendsList.tsx new file mode 100644 index 0000000..c048f1c --- /dev/null +++ b/src/components/non-fixed/FriendsBody/FriendsList/FriendsList.tsx @@ -0,0 +1,31 @@ +import styled from 'styled-components'; +import FriendsListItem from '@components/non-fixed/FriendsBody/FriendsList/FriendsListItem'; + +const StyledFriendsList = styled.div` + margin: 0 16px; + height: fit-content; +`; + +// 임시 UI를 위한 텍스트 집합 +const tmpFriendsList = [ + 'discord_redesign1', + '김정민', + 'discord_redesign2', + '김승완', + 'discord_redesign3', + 'CEOS', +]; + +export default function FriendsList() { + return ( + + {tmpFriendsList.map((friendsName, index) => ( + + ))} + + ); +} diff --git a/src/components/non-fixed/FriendsBody/FriendsList/FriendsListItem.tsx b/src/components/non-fixed/FriendsBody/FriendsList/FriendsListItem.tsx new file mode 100644 index 0000000..9500aa9 --- /dev/null +++ b/src/components/non-fixed/FriendsBody/FriendsList/FriendsListItem.tsx @@ -0,0 +1,54 @@ +import styled from 'styled-components'; + +const StyledFriendsListItem = styled.div` + width: 100%; + height: 64px; + display: flex; + justify-content: space-evenly; + align-items: center; + column-gap: 16px; +`; + +const StyledLogoImage = styled.img` + border-radius: 50%; + width: 32px; + height: 32px; +`; + +const StyledSpan = styled.span` + width: 199px; + height: 23px; + font-family: Pretendard; + font-size: ${(props) => props.theme.textStyle.fontSize.body1}; + line-height: ${(props) => props.theme.textStyle.lineHeight.body1}; + font-weight: 500; + color: ${(props) => props.theme.color.black}; +`; + +const StyledImage = styled.img` + &:hover { + cursor: pointer; + } +`; + +interface friendsListItemObj { + order: number; + friendName: string; +} +export default function FriendsListItem({ + order, + friendName, +}: friendsListItemObj) { + return ( + + {order % 2 === 0 ? ( + + ) : ( + + )} + {friendName} + + + + ); +} diff --git a/src/components/non-fixed/ProfileBody/ProfileBody.tsx b/src/components/non-fixed/ProfileBody/ProfileBody.tsx index b5220df..029163a 100644 --- a/src/components/non-fixed/ProfileBody/ProfileBody.tsx +++ b/src/components/non-fixed/ProfileBody/ProfileBody.tsx @@ -1,15 +1,19 @@ -import ProfilePageHead from '@components/non-fixed/ProfileBody/ProfilePageHead/ProfilePageHead'; import styled from 'styled-components'; +import ProfilePageHead from '@components/non-fixed/ProfileBody/ProfilePageHead/ProfilePageHead'; +import ProfileContent from '@components/non-fixed/ProfileBody/ProfileContent/ProfileContent'; const StyledProfileBody = styled.section` flex-grow: 1; overflow-y: scroll; + display: flex; + flex-direction: column; `; export default function ProfileBody() { return ( + ); } diff --git a/src/components/non-fixed/ProfileBody/ProfileContent/ProfileContent.tsx b/src/components/non-fixed/ProfileBody/ProfileContent/ProfileContent.tsx new file mode 100644 index 0000000..deaeee4 --- /dev/null +++ b/src/components/non-fixed/ProfileBody/ProfileContent/ProfileContent.tsx @@ -0,0 +1,10 @@ +import styled from 'styled-components'; + +const StyledProfileContent = styled.div` + flex-grow: 1; + margin-top: 16px; +`; + +export default function ProfileContent() { + return hi; +} From fcdef66110bb366239d5720c08b2e55b0cbae63e Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Sun, 7 Apr 2024 16:05:59 +0900 Subject: [PATCH 60/81] Feat : make messages search head UI --- .../fixed/SearchHead/SearchHead.tsx | 117 ++++++++++++++++++ .../FriendsSearchHead/FriendsSearchHead.tsx | 102 +-------------- .../non-fixed/MessageBody/MessageBody.tsx | 3 +- .../MessageSearchHead/MessageSearchHead.tsx | 5 + 4 files changed, 126 insertions(+), 101 deletions(-) create mode 100644 src/components/fixed/SearchHead/SearchHead.tsx create mode 100644 src/components/non-fixed/MessageBody/MessageSearchHead/MessageSearchHead.tsx diff --git a/src/components/fixed/SearchHead/SearchHead.tsx b/src/components/fixed/SearchHead/SearchHead.tsx new file mode 100644 index 0000000..ce84ed1 --- /dev/null +++ b/src/components/fixed/SearchHead/SearchHead.tsx @@ -0,0 +1,117 @@ +import styled from 'styled-components'; + +const StyledSearchHead = styled.div` + width: 100%; + height: 114px; + display: flex; + justify-content: center; + align-items: center; +`; + +const StyledSearchInnerContainer = styled.div` + width: 343px; + height: 82px; + display: flex; + flex-direction: column; + justify-content: space-between; + row-gap: 10px; +`; + +const StyledSearchUpper = styled.div` + display: flex; + justify-content: space-between; + align-items: center; +`; + +const StyledNameSpan = styled.p` + height: 30px; + display: flex; + justify-content: center; + align-items: center; + font-size: 20px; + font-weight: 600; + margin-left: 8px; + color: ${(props) => props.theme.color.black}; + white-space: nowrap; +`; + +const StyledNewMessage = styled.div` + background-color: ${(props) => props.theme.color.grayMedium}; + width: 111px; + height: 32px; + border-radius: 20px; + display: flex; + justify-content: center; + align-items: center; + column-gap: 4px; + + &:hover { + cursor: pointer; + } +`; + +const StyledNewMessageSpan = styled.span` + font-family: Pretendard; + font-size: ${(props) => props.theme.textStyle.fontSize.body1}; + line-height: ${(props) => props.theme.textStyle.lineHeight.body1}; +`; + +const StyledSearchLower = styled.div` + width: 100%; + height: 40px; + position: relative; +`; + +const StyledSearchLowerInput = styled.input` + border: none; + width: 100%; + height: 100%; + background-color: ${(props) => props.theme.color.grayMedium}; + border-radius: 20px; + font-size: 13px; + font-weight: 500; + font-family: Pretendard; + line-height: 19.5px; + color: ${(props) => props.theme.color.grayDark}; + padding: 0 0 0 36px; + outline: none; +`; + +const StyledMagnifyingGlass = styled.img` + position: absolute; + top: 10px; + left: 12px; +`; + +interface searchTypeObj { + searchType: string; +} + +export default function SearchHead({ searchType }: searchTypeObj) { + return ( + + + + + {searchType === 'friends' ? '친구' : '메시지'} + + + {searchType === 'friends' ? ( + This is new friend icon + ) : ( + This is new message icon + )} + 새 메시지 + + + + + + + + + ); +} diff --git a/src/components/non-fixed/FriendsBody/FriendsSearchHead/FriendsSearchHead.tsx b/src/components/non-fixed/FriendsBody/FriendsSearchHead/FriendsSearchHead.tsx index 934863b..b5f1d31 100644 --- a/src/components/non-fixed/FriendsBody/FriendsSearchHead/FriendsSearchHead.tsx +++ b/src/components/non-fixed/FriendsBody/FriendsSearchHead/FriendsSearchHead.tsx @@ -1,104 +1,6 @@ import styled from 'styled-components'; - -const StyledFriendsSearchHead = styled.div` - width: 100%; - height: 114px; - display: flex; - justify-content: center; - align-items: center; -`; - -const StyledFriendSearchInnerContainer = styled.div` - width: 343px; - height: 82px; - display: flex; - flex-direction: column; - justify-content: space-between; - row-gap: 10px; -`; - -const StyledFriendSearchUpper = styled.div` - display: flex; - justify-content: space-between; - align-items: center; -`; - -const StyledNameSpan = styled.p` - width: 35px; - height: 30px; - display: flex; - justify-content: center; - align-items: center; - font-size: 20px; - font-weight: 600; - margin-left: 8px; - color: ${(props) => props.theme.color.black}; -`; - -const StyledNewMessage = styled.div` - background-color: ${(props) => props.theme.color.grayMedium}; - width: 111px; - height: 32px; - border-radius: 20px; - display: flex; - justify-content: center; - align-items: center; - column-gap: 4px; - - &:hover { - cursor: pointer; - } -`; - -const StyledNewMessageSpan = styled.span` - font-family: Pretendard; - font-size: ${(props) => props.theme.textStyle.fontSize.body1}; - line-height: ${(props) => props.theme.textStyle.lineHeight.body1}; -`; - -const StyledFriendSearchLower = styled.div` - width: 100%; - height: 40px; - position: relative; -`; - -const StyledFriendSearchLowerInput = styled.input` - border: none; - width: 100%; - height: 100%; - background-color: ${(props) => props.theme.color.grayMedium}; - border-radius: 20px; - font-size: 13px; - font-weight: 500; - font-family: Pretendard; - line-height: 19.5px; - color: ${(props) => props.theme.color.grayDark}; - padding: 0 0 0 36px; - outline: none; -`; - -const StyledMagnifyingGlass = styled.img` - position: absolute; - top: 10px; - left: 12px; -`; +import SearchHead from '@components/fixed/SearchHead/SearchHead'; export default function FriendsSearchHead() { - return ( - - - - 친구 - - - 새 메시지 - - - - - - - - - ); + return ; } diff --git a/src/components/non-fixed/MessageBody/MessageBody.tsx b/src/components/non-fixed/MessageBody/MessageBody.tsx index e67a346..1f28ede 100644 --- a/src/components/non-fixed/MessageBody/MessageBody.tsx +++ b/src/components/non-fixed/MessageBody/MessageBody.tsx @@ -1,4 +1,5 @@ import styled from 'styled-components'; +import MessageSearchHead from '@components/non-fixed/MessageBody/MessageSearchHead/MessageSearchHead'; const StyledMessageBody = styled.section` flex-grow: 1; @@ -8,7 +9,7 @@ const StyledMessageBody = styled.section` export default function MessageBody() { return ( - This is Messages Body + ); } diff --git a/src/components/non-fixed/MessageBody/MessageSearchHead/MessageSearchHead.tsx b/src/components/non-fixed/MessageBody/MessageSearchHead/MessageSearchHead.tsx new file mode 100644 index 0000000..8847654 --- /dev/null +++ b/src/components/non-fixed/MessageBody/MessageSearchHead/MessageSearchHead.tsx @@ -0,0 +1,5 @@ +import SearchHead from '@components/fixed/SearchHead/SearchHead'; + +export default function MessageSearchHead() { + return ; +} From 01c3a3586492a01f9124c08427c6a8fd28de9eab Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Sun, 7 Apr 2024 16:19:57 +0900 Subject: [PATCH 61/81] Fix : fix state changing error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 렌더링 도중에 상태의 변경이 일어나면 안된다. 이를 useEffect() 훅 내부로 옮겨주었음 --- .../FriendsSearchHead/FriendsSearchHead.tsx | 1 - .../non-fixed/TabBar/TabIconContainer.tsx | 18 +++++++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/components/non-fixed/FriendsBody/FriendsSearchHead/FriendsSearchHead.tsx b/src/components/non-fixed/FriendsBody/FriendsSearchHead/FriendsSearchHead.tsx index b5f1d31..bb5c37a 100644 --- a/src/components/non-fixed/FriendsBody/FriendsSearchHead/FriendsSearchHead.tsx +++ b/src/components/non-fixed/FriendsBody/FriendsSearchHead/FriendsSearchHead.tsx @@ -1,4 +1,3 @@ -import styled from 'styled-components'; import SearchHead from '@components/fixed/SearchHead/SearchHead'; export default function FriendsSearchHead() { diff --git a/src/components/non-fixed/TabBar/TabIconContainer.tsx b/src/components/non-fixed/TabBar/TabIconContainer.tsx index 46f552d..d18546e 100644 --- a/src/components/non-fixed/TabBar/TabIconContainer.tsx +++ b/src/components/non-fixed/TabBar/TabIconContainer.tsx @@ -2,6 +2,7 @@ import { useRecoilState } from 'recoil'; import { userPageModeState } from '@context/state/atom'; import { useNavigate, useLocation } from 'react-router-dom'; import styled from 'styled-components'; +import { useEffect } from 'react'; interface userPageModeAtt { // 속성은 html dom에 부여하는 것이라서 일부러 $ 표시를 붙여준 것이다 @@ -53,13 +54,16 @@ export default function TabIconContainer() { const [userPageMode, setUserPageMode] = useRecoilState(userPageModeState); const navigate = useNavigate(); - if (location.pathname === '/') { - setUserPageMode('friends'); - } else if (location.pathname === '/messages') { - setUserPageMode('messages'); - } else if (location.pathname === '/profile') { - setUserPageMode('profile'); - } + // 렌더링 도중에 상태 변경을 진행하면 안됨 => 첫 렌더링 이후에 useEffect() 훅을 통해서 진행해줌ㄴ + useEffect(() => { + if (location.pathname === '/') { + setUserPageMode('friends'); + } else if (location.pathname === '/messages') { + setUserPageMode('messages'); + } else if (location.pathname === '/profile') { + setUserPageMode('profile'); + } + }, [location, setUserPageMode]); function handleNavigate(path: string) { navigate(path); From e3f3f9131843d6badabd9a99e29c908d44298119 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Thu, 11 Apr 2024 16:07:30 +0900 Subject: [PATCH 62/81] Feat : make /messages url path UI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 다양한 메시지 리스트들을 렌더링함 2. 추후에 특정 메시지 아이템을 클릭하면 채팅방으로 이동하게 만듬. 근데 일단 김정민을 누르면 내가 원래 구현했던 /chat으로 이동하게 할거고 나머지는 아무 것도 없는 채팅 페이지로 이동하게 할 것임 --- make.md | 2 +- public/images/blueSignal.svg | 3 + .../non-fixed/ChatBody/ChatBody.tsx | 4 +- .../non-fixed/MessageBody/MessageBody.tsx | 2 + .../MessageList/MessageListContainer.tsx | 42 ++++++++++ .../MessageListItem/MessageListItem.tsx | 40 ++++++++++ .../MessageListItem/MessageListItemLeft.tsx | 30 +++++++ .../MessageListItem/MessageListItemRight.tsx | 78 +++++++++++++++++++ 8 files changed, 199 insertions(+), 2 deletions(-) create mode 100644 public/images/blueSignal.svg create mode 100644 src/components/non-fixed/MessageBody/MessageList/MessageListContainer.tsx create mode 100644 src/components/non-fixed/MessageBody/MessageList/MessageListItem/MessageListItem.tsx create mode 100644 src/components/non-fixed/MessageBody/MessageList/MessageListItem/MessageListItemLeft.tsx create mode 100644 src/components/non-fixed/MessageBody/MessageList/MessageListItem/MessageListItemRight.tsx diff --git a/make.md b/make.md index 150c338..198ee5e 100644 --- a/make.md +++ b/make.md @@ -57,6 +57,6 @@ ### 각 페이지 별 기능 1. `/friends` : 그냥 친구가 누가 있는지를 보여주기만 하면됨 일단은. (단순 UI) => 일단 애플리케이션이 켜지면 바로 들어갈 수 있게 url path를 `/`로 설정하기 -2. `/message` : 채팅 목록들을 보여주고, 가장 마지막 데이터에 해당하는 사람과 메시지를 보여줄 필요가 있음 +2. `/messagess` : 채팅 목록들을 보여주고, 가장 마지막 데이터에 해당하는 사람과 메시지를 보여줄 필요가 있음 3. `/message/{특정 user id}` : 해당 내용에 해당하는 사람꺼를 보여줌. 나중에 `useParams()` 훅을 이용하여 진행할 수 있음 4. `/profile` : 본인의 프로필을 조회할 수 있음(단순 UI) diff --git a/public/images/blueSignal.svg b/public/images/blueSignal.svg new file mode 100644 index 0000000..c831819 --- /dev/null +++ b/public/images/blueSignal.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/non-fixed/ChatBody/ChatBody.tsx b/src/components/non-fixed/ChatBody/ChatBody.tsx index b524dc0..2d2bf59 100644 --- a/src/components/non-fixed/ChatBody/ChatBody.tsx +++ b/src/components/non-fixed/ChatBody/ChatBody.tsx @@ -58,9 +58,11 @@ export default function ChatBody() { async function loadMessageData() { try { const tmpDateArray: string[] = []; - const tmpMessageDataObject: messageDataObject = {}; + const tmpMessageDataObject: messageDataObject = {}; // 상태로도 사용하고 로컬 스토리지에도 동기화를 해줄 객체 + const response = await fetch('/Dummy/Dummy.json'); const messageJsonData = await response.json(); + for (const messageData of messageJsonData) { const { content, createdAt, from, like } = messageData; const slicedCreatedDate: string = createdAt.slice(0, 10); diff --git a/src/components/non-fixed/MessageBody/MessageBody.tsx b/src/components/non-fixed/MessageBody/MessageBody.tsx index 1f28ede..7a4f0cb 100644 --- a/src/components/non-fixed/MessageBody/MessageBody.tsx +++ b/src/components/non-fixed/MessageBody/MessageBody.tsx @@ -1,5 +1,6 @@ import styled from 'styled-components'; import MessageSearchHead from '@components/non-fixed/MessageBody/MessageSearchHead/MessageSearchHead'; +import MessageListContainer from '@components/non-fixed/MessageBody/MessageList/MessageListContainer'; const StyledMessageBody = styled.section` flex-grow: 1; @@ -10,6 +11,7 @@ export default function MessageBody() { return ( + ); } diff --git a/src/components/non-fixed/MessageBody/MessageList/MessageListContainer.tsx b/src/components/non-fixed/MessageBody/MessageList/MessageListContainer.tsx new file mode 100644 index 0000000..2ea4b2c --- /dev/null +++ b/src/components/non-fixed/MessageBody/MessageList/MessageListContainer.tsx @@ -0,0 +1,42 @@ +import styled from 'styled-components'; +import MessageListItem from '@components/non-fixed/MessageBody/MessageList/MessageListItem/MessageListItem'; + +const StyledMessageListContainer = styled.div` + margin: 0 16px; + height: fit-content; +`; + +export default function MessageListContainer() { + return ( + + + + + + + ); +} diff --git a/src/components/non-fixed/MessageBody/MessageList/MessageListItem/MessageListItem.tsx b/src/components/non-fixed/MessageBody/MessageList/MessageListItem/MessageListItem.tsx new file mode 100644 index 0000000..c0b2ca9 --- /dev/null +++ b/src/components/non-fixed/MessageBody/MessageList/MessageListItem/MessageListItem.tsx @@ -0,0 +1,40 @@ +import styled from 'styled-components'; +import MessageListItemLeft from '@components/non-fixed/MessageBody/MessageList/MessageListItem/MessageListItemLeft'; +import MessageListItemRight from '@components/non-fixed/MessageBody/MessageList/MessageListItem/MessageListItemRight'; + +const StyledMessageListItem = styled.div` + width: 100%; + height: 80px; + display: flex; + justify-content: space-between; + align-items: center; + column-gap: 8px; +`; + +interface messageListItemType { + discordLogoColor: string; + name: string; + ifBlueSignal: boolean; + content: string; + dateString: string; +} + +export default function MessageListItem({ + discordLogoColor, + name, + ifBlueSignal, + content, + dateString, +}: messageListItemType) { + return ( + + + + + ); +} diff --git a/src/components/non-fixed/MessageBody/MessageList/MessageListItem/MessageListItemLeft.tsx b/src/components/non-fixed/MessageBody/MessageList/MessageListItem/MessageListItemLeft.tsx new file mode 100644 index 0000000..502ac12 --- /dev/null +++ b/src/components/non-fixed/MessageBody/MessageList/MessageListItem/MessageListItemLeft.tsx @@ -0,0 +1,30 @@ +import styled from 'styled-components'; + +const StyledMessageListItemLeftContainer = styled.div` + width: 48px; + height: 48px; +`; + +const StyledMessagListItemLeftImage = styled.img` + width: 48px; + height: 48px; + border-radius: 50%; +`; + +export default function MessageListItemLeft({ + discordLogoColor, +}: { + discordLogoColor: string; +}) { + return ( + + {discordLogoColor === 'green' && ( + + )} + + {discordLogoColor === 'purple' && ( + + )} + + ); +} diff --git a/src/components/non-fixed/MessageBody/MessageList/MessageListItem/MessageListItemRight.tsx b/src/components/non-fixed/MessageBody/MessageList/MessageListItem/MessageListItemRight.tsx new file mode 100644 index 0000000..092d9fe --- /dev/null +++ b/src/components/non-fixed/MessageBody/MessageList/MessageListItem/MessageListItemRight.tsx @@ -0,0 +1,78 @@ +import styled from 'styled-components'; + +const StyledMessageListItemRight = styled.div` + display: flex; + flex-direction: column; + width: 287px; + height: 46px; +`; + +interface messageListItemRightInnerType { + ifBlueSignal: boolean; +} + +const StyledMessageListItemRightInner = styled.div` + flex-grow: 1; + display: flex; + justify-content: space-between; + align-items: center; +`; + +const StyledNameSpan = styled.span` + font-family: Pretendard; + font-size: ${(props) => props.theme.textStyle.fontSize.body1}; + line-height: ${(props) => props.theme.textStyle.lineHeight.body1}; + color: ${(props) => props.theme.color.grayDark}; + font-weight: 500; +`; + +const StyledContentdiv = styled.div` + width: 229px; + font-family: Pretendard; + font-weight: 400; + font-size: ${(props) => props.theme.textStyle.fontSize.body2}; + line-height: ${(props) => props.theme.textStyle.lineHeight.body2}; + color: ${(props) => props.theme.color.black}; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +`; + +const StyledDateString = styled.div` + font-family: Pretendard; + font-weight: 500; + font-size: 10px; + line-height: 15px; + color: ${(props) => props.theme.color.grayDark}; +`; + +interface messageListItemRightType { + name: string; + ifBlueSignal: boolean; + content: string; + dateString: string; +} + +const StyledBlueSignalImage = styled.img``; + +export default function MessageListItemRight({ + name, + ifBlueSignal, + content, + dateString, +}: messageListItemRightType) { + return ( + + + {name} + {ifBlueSignal === true ? ( + + ) : null} + + + {content} + {dateString} + + + ); +} From f80c9d801c6e3938f09236a3df9b4da7031466be Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Thu, 11 Apr 2024 19:52:14 +0900 Subject: [PATCH 63/81] Feat : make route betwwen message list page and chat page --- src/App.tsx | 2 +- .../MessageList/MessageListContainer.tsx | 15 +++++++++++++++ .../MessageListItem/MessageListItem.tsx | 10 +++++++++- .../MessageListItem/MessageListItemRight.tsx | 4 ++++ 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 553b11e..450fbbd 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -21,7 +21,7 @@ function App() { }> }> - } /> + }> diff --git a/src/components/non-fixed/MessageBody/MessageList/MessageListContainer.tsx b/src/components/non-fixed/MessageBody/MessageList/MessageListContainer.tsx index 2ea4b2c..94e38b9 100644 --- a/src/components/non-fixed/MessageBody/MessageList/MessageListContainer.tsx +++ b/src/components/non-fixed/MessageBody/MessageList/MessageListContainer.tsx @@ -1,5 +1,6 @@ import styled from 'styled-components'; import MessageListItem from '@components/non-fixed/MessageBody/MessageList/MessageListItem/MessageListItem'; +import { useNavigate } from 'react-router-dom'; const StyledMessageListContainer = styled.div` margin: 0 16px; @@ -7,6 +8,12 @@ const StyledMessageListContainer = styled.div` `; export default function MessageListContainer() { + const navigate = useNavigate(); + + function handleClickMessageListItem(path: string) { + navigate(`/chat/${path}`); + } + return ( ); diff --git a/src/components/non-fixed/MessageBody/MessageList/MessageListItem/MessageListItem.tsx b/src/components/non-fixed/MessageBody/MessageList/MessageListItem/MessageListItem.tsx index c0b2ca9..dc1a0c9 100644 --- a/src/components/non-fixed/MessageBody/MessageList/MessageListItem/MessageListItem.tsx +++ b/src/components/non-fixed/MessageBody/MessageList/MessageListItem/MessageListItem.tsx @@ -11,12 +11,18 @@ const StyledMessageListItem = styled.div` column-gap: 8px; `; +interface navigateFunction { + (pathname: string): void; +} + interface messageListItemType { discordLogoColor: string; name: string; ifBlueSignal: boolean; content: string; dateString: string; + navigateToChatFunc: navigateFunction; + path: string; } export default function MessageListItem({ @@ -25,9 +31,11 @@ export default function MessageListItem({ ifBlueSignal, content, dateString, + navigateToChatFunc, + path, }: messageListItemType) { return ( - + navigateToChatFunc(path)}> props.theme.textStyle.fontSize.body2}; From 50e092ddd9c45938377a95cf31f48a79b079e99d Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Thu, 11 Apr 2024 20:40:01 +0900 Subject: [PATCH 64/81] Feat : make chat detail members page UI --- src/App.tsx | 4 +++ .../ChatHead/ChatHeadNav/ChatHeadNav.tsx | 2 +- .../ChatHead/ChatHeadNav/ChatHeadNavLeft.tsx | 8 ++--- .../ChatHead/ChatHeadNav/EmptyChatHeadNav.tsx | 33 +++++++++++++++++++ .../non-fixed/EmptyChatBody/EmptyChatBody.tsx | 14 ++++++++ src/pages/EmptyChat.tsx | 16 +++++++++ 6 files changed, 72 insertions(+), 5 deletions(-) create mode 100644 src/components/fixed/ChatHead/ChatHeadNav/EmptyChatHeadNav.tsx create mode 100644 src/components/non-fixed/EmptyChatBody/EmptyChatBody.tsx create mode 100644 src/pages/EmptyChat.tsx diff --git a/src/App.tsx b/src/App.tsx index 450fbbd..0ebea75 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -8,6 +8,7 @@ import CommonLayout from '@pages/CommonLayout'; import Friends from '@pages/Friends'; import Messages from '@pages/Messages'; import Profile from '@pages/Profile'; +import EmptyChat from '@pages/EmptyChat'; function App() { return ( @@ -22,6 +23,9 @@ function App() { }> }> + }> + } /> + diff --git a/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNav.tsx b/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNav.tsx index 1b214a7..7f4cc62 100644 --- a/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNav.tsx +++ b/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNav.tsx @@ -2,7 +2,7 @@ import styled from 'styled-components'; import ChatHeadNavLeft from '@components/fixed/ChatHead/ChatHeadNav/ChatHeadNavLeft'; import ChatHeadNavRight from '@components/fixed/ChatHead/ChatHeadNav/ChatHeadNavRight'; -const StyledChatHeadNav = styled.nav` +export const StyledChatHeadNav = styled.nav` flex-grow: 1; flex-shrink: 0; display: flex; diff --git a/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavLeft.tsx b/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavLeft.tsx index 56b6973..4d2537c 100644 --- a/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavLeft.tsx +++ b/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavLeft.tsx @@ -3,7 +3,7 @@ import * as ST from '@styles/styledComponents'; import { useRecoilState } from 'recoil'; import { userNumberState } from '@context/state/atom'; -const StyledChatHeadNavLeft = styled.div` +export const StyledChatHeadNavLeft = styled.div` width: 122px; height: 32px; display: flex; @@ -12,14 +12,14 @@ const StyledChatHeadNavLeft = styled.div` margin-left: 16px; `; -const StyledChatHeavNavLeftBackImg = styled.img` +export const StyledChatHeavNavLeftBackImg = styled.img` width: 24px; height: 24px; ${ST.hoverCursor} margin-right: 8px; `; -const StyledChatHeadNavLeftDiscordImage = styled.img` +export const StyledChatHeadNavLeftDiscordImage = styled.img` width: 32px; height: 32px; border-radius: 50%; @@ -28,7 +28,7 @@ const StyledChatHeadNavLeftDiscordImage = styled.img` margin-right: 4px; `; -const StyledUserNameSpan = styled.span` +export const StyledUserNameSpan = styled.span` font-size: ${(props) => props.theme.textStyle.fontSize.h3}; line-height: ${(props) => props.theme.textStyle.lineHeight.h3}; font-weight: 600; diff --git a/src/components/fixed/ChatHead/ChatHeadNav/EmptyChatHeadNav.tsx b/src/components/fixed/ChatHead/ChatHeadNav/EmptyChatHeadNav.tsx new file mode 100644 index 0000000..aa5eb3f --- /dev/null +++ b/src/components/fixed/ChatHead/ChatHeadNav/EmptyChatHeadNav.tsx @@ -0,0 +1,33 @@ +import { StyledChatHeadNav } from './ChatHeadNav'; +import { + StyledChatHeadNavLeft, + StyledChatHeavNavLeftBackImg, + StyledChatHeadNavLeftDiscordImage, + StyledUserNameSpan, +} from './ChatHeadNavLeft'; +import ChatHeadNavRight from './ChatHeadNavRight'; +import styled from 'styled-components'; + +const StyledEmptyChatHeadNav = styled.nav` + height: 64px; + display: flex; + align-items: center; + justify-content: space-between; +`; + +export default function EmptyChatHeadNav({ + username, +}: { + username: string | undefined; // 공백이 올 수도 있다 +}) { + return ( + + + + + {username} + + + + ); +} diff --git a/src/components/non-fixed/EmptyChatBody/EmptyChatBody.tsx b/src/components/non-fixed/EmptyChatBody/EmptyChatBody.tsx new file mode 100644 index 0000000..e88b3eb --- /dev/null +++ b/src/components/non-fixed/EmptyChatBody/EmptyChatBody.tsx @@ -0,0 +1,14 @@ +import styled from 'styled-components'; + +const StyledEmptyChatBody = styled.div` + flex-grow: 1; + margin-top: 16px; + margin-right: 16px; + margin-left: 16px; + width: 343px; + overflow-y: scroll; +`; + +export default function EmptyChatBody() { + return ; +} diff --git a/src/pages/EmptyChat.tsx b/src/pages/EmptyChat.tsx new file mode 100644 index 0000000..a6c78fc --- /dev/null +++ b/src/pages/EmptyChat.tsx @@ -0,0 +1,16 @@ +import EmptyChatHeadNav from '@components/fixed/ChatHead/ChatHeadNav/EmptyChatHeadNav'; +import ChatInput from '@components/non-fixed/ChatInput/ChatInput'; +import { useParams } from 'react-router-dom'; +import EmptyChatBody from '@components/non-fixed/EmptyChatBody/EmptyChatBody'; + +export default function EmptyChat() { + const { username } = useParams(); + console.log(username); + return ( + <> + + + + + ); +} From 18ef9e1e522f57a625beee1988615d6465ab374c Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Thu, 11 Apr 2024 20:42:37 +0900 Subject: [PATCH 65/81] Refactor: absolute import path refactoring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 절대경로로 import 하도록 자잘한 코드 수정 --- .../fixed/ChatHead/ChatHeadNav/EmptyChatHeadNav.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/fixed/ChatHead/ChatHeadNav/EmptyChatHeadNav.tsx b/src/components/fixed/ChatHead/ChatHeadNav/EmptyChatHeadNav.tsx index aa5eb3f..e48ec2c 100644 --- a/src/components/fixed/ChatHead/ChatHeadNav/EmptyChatHeadNav.tsx +++ b/src/components/fixed/ChatHead/ChatHeadNav/EmptyChatHeadNav.tsx @@ -1,11 +1,10 @@ -import { StyledChatHeadNav } from './ChatHeadNav'; import { StyledChatHeadNavLeft, StyledChatHeavNavLeftBackImg, StyledChatHeadNavLeftDiscordImage, StyledUserNameSpan, -} from './ChatHeadNavLeft'; -import ChatHeadNavRight from './ChatHeadNavRight'; +} from '@components/fixed/ChatHead/ChatHeadNav/ChatHeadNavLeft'; +import ChatHeadNavRight from '@components/fixed/ChatHead/ChatHeadNav/ChatHeadNavRight'; import styled from 'styled-components'; const StyledEmptyChatHeadNav = styled.nav` From 768da6f7023afcee913997f04b361bcba538f8b5 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Thu, 25 Apr 2024 22:17:34 +0900 Subject: [PATCH 66/81] Chore : make router setting in chat page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 김정민 chat 말고도 다른 chat에서도 뒤로 가기 화살표를 누르면 /messages url로 갈 수 있도록 설정 --- .../ChatHead/ChatHeadNav/ChatHeadNavLeft.tsx | 13 ++++++++++++- .../ChatHead/ChatHeadNav/EmptyChatHeadNav.tsx | 16 +++++++++++++++- src/components/non-fixed/ChatBody/ChatBody.tsx | 2 +- .../ChatBody/ChatLog/ChatLogLeft/ChatLogLeft.tsx | 2 +- .../ChatBody/DateDivider/DateDivider.tsx | 2 +- src/context/state/atom.ts | 2 +- src/hooks/useScrollToBottom.tsx | 2 +- src/{type => types}/type.tsx | 0 8 files changed, 32 insertions(+), 7 deletions(-) rename src/{type => types}/type.tsx (100%) diff --git a/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavLeft.tsx b/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavLeft.tsx index 4d2537c..29d69ae 100644 --- a/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavLeft.tsx +++ b/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNavLeft.tsx @@ -2,6 +2,7 @@ import styled from 'styled-components'; import * as ST from '@styles/styledComponents'; import { useRecoilState } from 'recoil'; import { userNumberState } from '@context/state/atom'; +import { useNavigate } from 'react-router-dom'; export const StyledChatHeadNavLeft = styled.div` width: 122px; @@ -39,6 +40,7 @@ export const StyledUserNameSpan = styled.span` export default function ChatHeadNavLeft() { const [userNumber, setUserNumber] = useRecoilState(userNumberState); + const navigate = useNavigate(); function handleClickChangeUserMode() { if (userNumber === 1) { @@ -47,9 +49,18 @@ export default function ChatHeadNavLeft() { setUserNumber(1); } } + + function handleClickArrowBackImage() { + setUserNumber(1); // 뒤로 가면 userNumeber는 원상태로 초기화 + navigate('/messages'); + } + return ( - + - + {username} diff --git a/src/components/non-fixed/ChatBody/ChatBody.tsx b/src/components/non-fixed/ChatBody/ChatBody.tsx index 2d2bf59..d8ae2ed 100644 --- a/src/components/non-fixed/ChatBody/ChatBody.tsx +++ b/src/components/non-fixed/ChatBody/ChatBody.tsx @@ -7,7 +7,7 @@ import { import { useEffect, useRef } from 'react'; import styled from 'styled-components'; import OneDateContainer from '@components/non-fixed/ChatBody/OneDateContainer/OneDateContainer'; -import { messageDataObject, voidFunction } from '@_type/type'; +import { messageDataObject, voidFunction } from 'types/type'; import sortByDate from '@utils/sortArrayByDate'; import useScrollToBottom from '@hooks/useScrollToBottom'; diff --git a/src/components/non-fixed/ChatBody/ChatLog/ChatLogLeft/ChatLogLeft.tsx b/src/components/non-fixed/ChatBody/ChatLog/ChatLogLeft/ChatLogLeft.tsx index 0a6cf1c..1a6051f 100644 --- a/src/components/non-fixed/ChatBody/ChatLog/ChatLogLeft/ChatLogLeft.tsx +++ b/src/components/non-fixed/ChatBody/ChatLog/ChatLogLeft/ChatLogLeft.tsx @@ -1,5 +1,5 @@ import styled from 'styled-components'; -import { isMessageOwnerEqualsWithModeProps } from 'type/type'; +import { isMessageOwnerEqualsWithModeProps } from 'types/type'; const StyledChatLogLeft = styled.div` width: auto; diff --git a/src/components/non-fixed/ChatBody/DateDivider/DateDivider.tsx b/src/components/non-fixed/ChatBody/DateDivider/DateDivider.tsx index 7c7dc09..acd4170 100644 --- a/src/components/non-fixed/ChatBody/DateDivider/DateDivider.tsx +++ b/src/components/non-fixed/ChatBody/DateDivider/DateDivider.tsx @@ -1,5 +1,5 @@ import styled from 'styled-components'; -import { dateStringProps } from 'type/type'; +import { dateStringProps } from 'types/type'; import { dateBeforeAfter } from '@styles/styledComponents'; import { chatBodyDivElementGap } from '@styles/styledComponents'; diff --git a/src/context/state/atom.ts b/src/context/state/atom.ts index 7068e64..eebadbc 100644 --- a/src/context/state/atom.ts +++ b/src/context/state/atom.ts @@ -1,5 +1,5 @@ import { atom } from 'recoil'; -import { messageDataObject } from '@_type/type'; +import { messageDataObject } from 'types/type'; export const messageDataState = atom({ key: 'messageData', diff --git a/src/hooks/useScrollToBottom.tsx b/src/hooks/useScrollToBottom.tsx index fe44902..bd1d543 100644 --- a/src/hooks/useScrollToBottom.tsx +++ b/src/hooks/useScrollToBottom.tsx @@ -1,6 +1,6 @@ import { useRecoilState } from 'recoil'; import { scrollToBottomState } from '@context/state/atom'; -import { voidFunction } from '@_type/type'; +import { voidFunction } from 'types/type'; export default function useScrollToBottom() { const [scrollToBottom, setScrollToBottom] = diff --git a/src/type/type.tsx b/src/types/type.tsx similarity index 100% rename from src/type/type.tsx rename to src/types/type.tsx From 562c30c2728210595d3040f0d560c805d62cf1ba Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Thu, 25 Apr 2024 23:11:18 +0900 Subject: [PATCH 67/81] Feat : make empty chat default UI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 기존의 메시지 데이터가 있는 상황에 대해서 상태관리 설정을 추후에 해줘야 함 --- .../non-fixed/EmptyChatBody/EmptyChatBody.tsx | 29 ++++++- .../ProfileGroupUI/ProfileGroupUI.tsx | 79 +++++++++++++++++++ src/pages/EmptyChat.tsx | 4 +- 3 files changed, 108 insertions(+), 4 deletions(-) create mode 100644 src/components/non-fixed/EmptyChatBody/ProfileGroupUI/ProfileGroupUI.tsx diff --git a/src/components/non-fixed/EmptyChatBody/EmptyChatBody.tsx b/src/components/non-fixed/EmptyChatBody/EmptyChatBody.tsx index e88b3eb..4078e48 100644 --- a/src/components/non-fixed/EmptyChatBody/EmptyChatBody.tsx +++ b/src/components/non-fixed/EmptyChatBody/EmptyChatBody.tsx @@ -1,4 +1,6 @@ import styled from 'styled-components'; +import ProfileGroupUI from '@components/non-fixed/EmptyChatBody/ProfileGroupUI/ProfileGroupUI'; +import { useEffect } from 'react'; const StyledEmptyChatBody = styled.div` flex-grow: 1; @@ -7,8 +9,31 @@ const StyledEmptyChatBody = styled.div` margin-left: 16px; width: 343px; overflow-y: scroll; + display: flex; + flex-direction: column; `; -export default function EmptyChatBody() { - return ; +const StyledProfileGroupUIContainer = styled.div` + width: 100%; + height: fit-content; + /* padding: 16px 94px 0px 94px; */ + padding-top: 16px; + display: flex; + justify-content: center; +`; + +export default function EmptyChatBody({ + username, +}: { + username: string | undefined; +}) { + // 기존의 데이터가 없으면 로고를 보여주고, 아니면 이에 맞는 메시지 데이터들을 보여주면 된다 + + return ( + + + + + + ); } diff --git a/src/components/non-fixed/EmptyChatBody/ProfileGroupUI/ProfileGroupUI.tsx b/src/components/non-fixed/EmptyChatBody/ProfileGroupUI/ProfileGroupUI.tsx new file mode 100644 index 0000000..e6c1dd2 --- /dev/null +++ b/src/components/non-fixed/EmptyChatBody/ProfileGroupUI/ProfileGroupUI.tsx @@ -0,0 +1,79 @@ +// empty 메시지 창에 아직 메시지 데이터가 없을 때 보여줄 UI이다 +import styled from 'styled-components'; + +const StyledProfileGroupUI = styled.div` + /* width: 187px; */ + width: 100%; + height: 187px; + /* margin: 16px 94px 0px 94px; */ + /* background-color: black; */ + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: center; + row-gap: 8px; +`; + +const StyledEightyDiscordImage = styled.img` + widh: 80px; + height: 80px; +`; + +const StyledNameComponent = styled.div` + /* width: 116px; */ + width: 100%; + height: 68px; + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: center; + row-gap: 4px; +`; + +const StyledBigName = styled.p` + /* width: 83px; */ + width: 100%; + height: 41px; + font-size: ${(props) => props.theme.textStyle.fontSize.h1}; + font-family: Pretendard; + line-height: ${(props) => props.theme.textStyle.lineHeight.h1}; + color: ${(props) => props.theme.color.black}; + text-align: center; +`; + +const StyledSmallName = styled.div` + width: 100%; + height: 23px; + font-size: ${(props) => props.theme.textStyle.fontSize.body1}; + font-family: Pretendard; + line-height: ${(props) => props.theme.textStyle.lineHeight.body1}; + color: ${(props) => props.theme.color.grayDark}; + text-align: center; +`; + +const StyledGuideDiv = styled.div` + width: 100%; + height: 23px; + font-size: ${(props) => props.theme.textStyle.fontSize.body2}; + font-family: Pretendard; + line-height: ${(props) => props.theme.textStyle.lineHeight.body2}; + color: ${(props) => props.theme.color.grayDark}; + text-align: center; +`; + +export default function ProfileGroupUI({ + username, +}: { + username: string | undefined; +}) { + return ( + + + + {username} + {`${username}_ceos`} + + {username}과 대화를 시작해보세요 + + ); +} diff --git a/src/pages/EmptyChat.tsx b/src/pages/EmptyChat.tsx index a6c78fc..0cc73e1 100644 --- a/src/pages/EmptyChat.tsx +++ b/src/pages/EmptyChat.tsx @@ -5,11 +5,11 @@ import EmptyChatBody from '@components/non-fixed/EmptyChatBody/EmptyChatBody'; export default function EmptyChat() { const { username } = useParams(); - console.log(username); + return ( <> - + ); From fe8108134bbfc6d9aa92ab2df58b4d86e81568f0 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Fri, 26 Apr 2024 00:45:37 +0900 Subject: [PATCH 68/81] Feat : make default profile UI --- .../non-fixed/EmptyChatBody/EmptyChatBody.tsx | 2 +- .../ProfileGroupUI/ProfileGroupUI.tsx | 8 +- .../ProfileContent/ProfileContent.tsx | 111 +++++++++++++++++- 3 files changed, 113 insertions(+), 8 deletions(-) diff --git a/src/components/non-fixed/EmptyChatBody/EmptyChatBody.tsx b/src/components/non-fixed/EmptyChatBody/EmptyChatBody.tsx index 4078e48..a373fed 100644 --- a/src/components/non-fixed/EmptyChatBody/EmptyChatBody.tsx +++ b/src/components/non-fixed/EmptyChatBody/EmptyChatBody.tsx @@ -16,7 +16,7 @@ const StyledEmptyChatBody = styled.div` const StyledProfileGroupUIContainer = styled.div` width: 100%; height: fit-content; - /* padding: 16px 94px 0px 94px; */ + padding-top: 16px; display: flex; justify-content: center; diff --git a/src/components/non-fixed/EmptyChatBody/ProfileGroupUI/ProfileGroupUI.tsx b/src/components/non-fixed/EmptyChatBody/ProfileGroupUI/ProfileGroupUI.tsx index e6c1dd2..5ee7238 100644 --- a/src/components/non-fixed/EmptyChatBody/ProfileGroupUI/ProfileGroupUI.tsx +++ b/src/components/non-fixed/EmptyChatBody/ProfileGroupUI/ProfileGroupUI.tsx @@ -2,11 +2,9 @@ import styled from 'styled-components'; const StyledProfileGroupUI = styled.div` - /* width: 187px; */ width: 100%; height: 187px; - /* margin: 16px 94px 0px 94px; */ - /* background-color: black; */ + display: flex; flex-direction: column; justify-content: space-between; @@ -15,12 +13,11 @@ const StyledProfileGroupUI = styled.div` `; const StyledEightyDiscordImage = styled.img` - widh: 80px; + width: 80px; height: 80px; `; const StyledNameComponent = styled.div` - /* width: 116px; */ width: 100%; height: 68px; display: flex; @@ -31,7 +28,6 @@ const StyledNameComponent = styled.div` `; const StyledBigName = styled.p` - /* width: 83px; */ width: 100%; height: 41px; font-size: ${(props) => props.theme.textStyle.fontSize.h1}; diff --git a/src/components/non-fixed/ProfileBody/ProfileContent/ProfileContent.tsx b/src/components/non-fixed/ProfileBody/ProfileContent/ProfileContent.tsx index deaeee4..d687493 100644 --- a/src/components/non-fixed/ProfileBody/ProfileContent/ProfileContent.tsx +++ b/src/components/non-fixed/ProfileBody/ProfileContent/ProfileContent.tsx @@ -1,10 +1,119 @@ import styled from 'styled-components'; +import { Link } from 'react-router-dom'; const StyledProfileContent = styled.div` flex-grow: 1; margin-top: 16px; + padding-top: 57px; + background-color: ${(props) => props.theme.color.grayLight}; + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: center; +`; + +const StyledProfileNameContainer = styled.div` + width: 120px; + height: 68px; + margin-top: 47px; + margin-bottom: 48px; + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: center; + row-gap: 4px; +`; + +const StyledUsername = styled.p` + width: 100%; + height: 41px; + font-size: ${(props) => props.theme.textStyle.fontSize.h1}; + line-height: ${(props) => props.theme.textStyle.lineHeight.h1}; + font-weight: 600; + color: ${(props) => props.theme.color.black}; + font-family: Pretendard; + text-align: center; +`; + +const StyledStudy = styled.div` + width: 77px; + height: 23px; + font-size: ${(props) => props.theme.textStyle.fontSize.body1}; + line-height: ${(props) => props.theme.textStyle.lineHeight.body1}; + font-weight: 500; + font-family: Prtendard; + color: ${(props) => props.theme.color.grayDark}; + text-align: center; +`; + +const StyledSnsContainer = styled.div` + width: 343px; + height: 64px; + border-radius: 20px; + padding: 16px; + background-color: ${(props) => props.theme.color.white}; + display: flex; + justify-content: space-between; + column-gap: 8px; + margin-bottom: 8px; + &:hover { + cursor: pointer; + } + > a { + text-decoration: none; + color: inherit; + display: block; + } +`; + +const SnsImage = styled.img` + width: 32px; + height: 32px; +`; + +const SnsName = styled.p` + width: 231px; + height: 100%; + font-size: ${(props) => props.theme.textStyle.fontSize.body1}; + line-height: ${(props) => props.theme.textStyle.lineHeight.body1}; + font-family: Pretendard; + font-weight: 500; + color: ${(props) => props.theme.textStyle.lineHeight.body1}; + display: flex; + align-items: center; `; export default function ProfileContent() { - return hi; + return ( + + + 김정민 + ceos_study + + + + Instagram + + + + + + + Github + + + + + + ); } From 375827e0e19d0cbc2f0a1c5aa53da7cf1516f3a7 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Fri, 26 Apr 2024 00:51:12 +0900 Subject: [PATCH 69/81] Fix : github image file name fix --- .../non-fixed/ProfileBody/ProfileContent/ProfileContent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/non-fixed/ProfileBody/ProfileContent/ProfileContent.tsx b/src/components/non-fixed/ProfileBody/ProfileContent/ProfileContent.tsx index d687493..377eedf 100644 --- a/src/components/non-fixed/ProfileBody/ProfileContent/ProfileContent.tsx +++ b/src/components/non-fixed/ProfileBody/ProfileContent/ProfileContent.tsx @@ -104,7 +104,7 @@ export default function ProfileContent() { - + Github Date: Sat, 27 Apr 2024 00:34:31 +0900 Subject: [PATCH 70/81] =?UTF-8?q?Chore=20:=20messageData=EA=B0=80=20?= =?UTF-8?q?=EC=97=86=EC=9D=84=20=EB=95=8C=20ProfileGroup=20UI=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=B4=EC=9D=B4=EA=B2=8C=20=EC=A1=B0=EA=B1=B4=EB=B6=80=20?= =?UTF-8?q?=EB=A0=8C=EB=8D=94=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 8 ++++ package.json | 2 + .../non-fixed/EmptyChatBody/EmptyChatBody.tsx | 43 +++++++++++++++++-- 3 files changed, 49 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index c1a0e1b..d55ebf8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "@types/react": "^18.2.69", "@types/react-dom": "^18.2.22", "dayjs": "^1.11.10", + "lodash": "^4.17.21", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-dom": "^6.22.3", @@ -28,6 +29,7 @@ "web-vitals": "^2.1.4" }, "devDependencies": { + "@types/lodash": "^4.17.0", "react-app-alias": "^2.2.2" } }, @@ -4259,6 +4261,12 @@ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" }, + "node_modules/@types/lodash": { + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.0.tgz", + "integrity": "sha512-t7dhREVv6dbNj0q17X12j7yDG4bD/DHYX7o5/DbDxobP0HnGPgpRz2Ej77aL7TZT3DSw13fqUTj8J4mMnqa7WA==", + "dev": true + }, "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", diff --git a/package.json b/package.json index e352eee..31e978c 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "@types/react": "^18.2.69", "@types/react-dom": "^18.2.22", "dayjs": "^1.11.10", + "lodash": "^4.17.21", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-dom": "^6.22.3", @@ -49,6 +50,7 @@ ] }, "devDependencies": { + "@types/lodash": "^4.17.0", "react-app-alias": "^2.2.2" } } diff --git a/src/components/non-fixed/EmptyChatBody/EmptyChatBody.tsx b/src/components/non-fixed/EmptyChatBody/EmptyChatBody.tsx index a373fed..4af2c3f 100644 --- a/src/components/non-fixed/EmptyChatBody/EmptyChatBody.tsx +++ b/src/components/non-fixed/EmptyChatBody/EmptyChatBody.tsx @@ -1,6 +1,10 @@ import styled from 'styled-components'; import ProfileGroupUI from '@components/non-fixed/EmptyChatBody/ProfileGroupUI/ProfileGroupUI'; import { useEffect } from 'react'; +import { messageDataObject } from 'types/type'; +import { useRecoilState } from 'recoil'; +import { messageDataState, messageDateArrayState } from '@context/state/atom'; +import ldsh from 'lodash'; // 객체의 깊은 비교를 위한 라이브러리에 해당 const StyledEmptyChatBody = styled.div` flex-grow: 1; @@ -10,7 +14,7 @@ const StyledEmptyChatBody = styled.div` width: 343px; overflow-y: scroll; display: flex; - flex-direction: column; + /* flex-direction: column; */ `; const StyledProfileGroupUIContainer = styled.div` @@ -27,13 +31,44 @@ export default function EmptyChatBody({ }: { username: string | undefined; }) { + const [messageData, setMessageData] = useRecoilState(messageDataState); + const [messageDateArray, setMessageDateArray] = useRecoilState( + messageDateArrayState + ); + // 기존의 데이터가 없으면 로고를 보여주고, 아니면 이에 맞는 메시지 데이터들을 보여주면 된다 + useEffect(() => { + if ( + localStorage.getItem(`chatMessageData${username}`) === null && + localStorage.getItem(`chatMessageDateArray${username}`) === null + ) { + setMessageData({}); + setMessageDateArray([]); + } else if ( + // 데이터가 있는 경우 -> 상태를 변경시키고 이를 이용하여 리렌더링 진행 + localStorage.getItem(`chatMessageData${username}`) !== null && + localStorage.getItem(`chatMessageDateArray${username}`) !== null + ) { + const lstrgChatMessageData = JSON.parse( + localStorage.getItem(`chatMessageData${username}`) as string + ); + const lstrgChatMessageDateArray = JSON.parse( + localStorage.getItem(`chatMessageDateArray${username}`) as string + ); + + setMessageData(lstrgChatMessageData); + setMessageDateArray(lstrgChatMessageDateArray); + } + }, []); + return ( - - - + {ldsh.isEqual(messageData, {}) && messageDateArray.length === 0 && ( + + + + )} ); } From 028b77fdefe653afdc7f7a3ea91a47494808d1ab Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Sat, 27 Apr 2024 01:25:55 +0900 Subject: [PATCH 71/81] =?UTF-8?q?Feat:=20make=20chat=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=20in=20empty=20chat=20page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 사용자를 누르면 전환되는 것을 추가적으로 구현해야함 --- .../non-fixed/ChatInput/ChatInputForm.tsx | 12 +- .../non-fixed/EmptyChatBody/EmptyChatBody.tsx | 52 +++++++- .../EmptyChatLog/EmptyChatLog.tsx | 39 ++++++ .../EmptyChatLogLeft/EmptyChatLogLeft.tsx | 23 ++++ .../EmptyChatLogRight/EmptyChatLogRight.tsx | 117 ++++++++++++++++++ .../EmptyDateDIvider/EmptyDateDivider.tsx | 39 ++++++ .../EmptyOneDateContainer.tsx | 39 ++++++ src/utils/makeUserIdNumber.js | 11 ++ 8 files changed, 324 insertions(+), 8 deletions(-) create mode 100644 src/components/non-fixed/EmptyChatBody/EmptyChatLog/EmptyChatLog.tsx create mode 100644 src/components/non-fixed/EmptyChatBody/EmptyChatLog/EmptyChatLogLeft/EmptyChatLogLeft.tsx create mode 100644 src/components/non-fixed/EmptyChatBody/EmptyChatLog/EmptyChatLogRight/EmptyChatLogRight.tsx create mode 100644 src/components/non-fixed/EmptyChatBody/EmptyDateDIvider/EmptyDateDivider.tsx create mode 100644 src/components/non-fixed/EmptyChatBody/EmptyOneDateContainer/EmptyOneDateContainer.tsx create mode 100644 src/utils/makeUserIdNumber.js diff --git a/src/components/non-fixed/ChatInput/ChatInputForm.tsx b/src/components/non-fixed/ChatInput/ChatInputForm.tsx index 5188a30..ac3695f 100644 --- a/src/components/non-fixed/ChatInput/ChatInputForm.tsx +++ b/src/components/non-fixed/ChatInput/ChatInputForm.tsx @@ -8,9 +8,9 @@ import { messageDateArrayState, userNumberState, } from '@context/state/atom'; -import { flushSync } from 'react-dom'; import { adjustTimeForUserLocation } from '@utils/makeTimeString'; import sortByDate from '@utils/sortArrayByDate'; +import { useParams } from 'react-router-dom'; const StyledChatInputForm = styled.form` width: 100%; @@ -70,6 +70,8 @@ const StyledClearSendIcon = styled.img` `; export default function ChatInputForm() { + const { username } = useParams(); // input form은 chat에서도 나타나고 chat/다른사용자에서도 나타남 + const inputRef = useRef(null); const [isInputBoxFocused, setIsInputBoxFocused] = useRecoilState( isInputBoxFocusedState @@ -143,9 +145,13 @@ export default function ChatInputForm() { } setMessageData(tmpMessageData); setMessageDateArray(tmpMessageDateArray); - localStorage.setItem('chatMessageData', JSON.stringify(tmpMessageData)); + const usernameVariable = username === undefined ? '' : username; // chat/뭐시기 url에서도 inputform을 낭낭하게 써먹기 위한 설정 + localStorage.setItem( + `chatMessageData${usernameVariable}`, + JSON.stringify(tmpMessageData) + ); localStorage.setItem( - 'chatMessageDateArray', + `chatMessageDateArray${usernameVariable}`, JSON.stringify(tmpMessageDateArray) ); if (inputRef.current !== null) { diff --git a/src/components/non-fixed/EmptyChatBody/EmptyChatBody.tsx b/src/components/non-fixed/EmptyChatBody/EmptyChatBody.tsx index 4af2c3f..c2faaee 100644 --- a/src/components/non-fixed/EmptyChatBody/EmptyChatBody.tsx +++ b/src/components/non-fixed/EmptyChatBody/EmptyChatBody.tsx @@ -1,10 +1,16 @@ import styled from 'styled-components'; import ProfileGroupUI from '@components/non-fixed/EmptyChatBody/ProfileGroupUI/ProfileGroupUI'; -import { useEffect } from 'react'; -import { messageDataObject } from 'types/type'; +import EmptyOneDateContainer from './EmptyOneDateContainer/EmptyOneDateContainer'; +import { useEffect, useRef } from 'react'; +import { messageDataObject, voidFunction } from 'types/type'; import { useRecoilState } from 'recoil'; -import { messageDataState, messageDateArrayState } from '@context/state/atom'; +import { + messageDataState, + messageDateArrayState, + isMessageLikeButtonClickedState, +} from '@context/state/atom'; import ldsh from 'lodash'; // 객체의 깊은 비교를 위한 라이브러리에 해당 +import useScrollToBottom from '@hooks/useScrollToBottom'; const StyledEmptyChatBody = styled.div` flex-grow: 1; @@ -13,7 +19,7 @@ const StyledEmptyChatBody = styled.div` margin-left: 16px; width: 343px; overflow-y: scroll; - display: flex; + /* display: flex; */ /* flex-direction: column; */ `; @@ -31,11 +37,37 @@ export default function EmptyChatBody({ }: { username: string | undefined; }) { + const emptyChatBodyContainerRef = useRef(null); const [messageData, setMessageData] = useRecoilState(messageDataState); const [messageDateArray, setMessageDateArray] = useRecoilState( messageDateArrayState ); + const [isMessageLikeButtonClicked, setIsMessageLikeButtonClicked] = + useRecoilState(isMessageLikeButtonClickedState); + + const [scrollToBottom, setScrollFunction] = useScrollToBottom(); + + const scrollToBottomFunction: voidFunction = function () { + if (emptyChatBodyContainerRef.current) { + emptyChatBodyContainerRef.current.scrollTop = + emptyChatBodyContainerRef.current.scrollHeight; + } + }; + + useEffect(() => { + setScrollFunction(scrollToBottomFunction); + }, []); + + useEffect(() => { + // 좋아요 버튼이 눌린 상태면 하트 UI만 만들어주고 다시 false로 만들어주고 끝내야 다음 상태가 정상적으로 반영 + setIsMessageLikeButtonClicked(false); + if (isMessageLikeButtonClicked === true) { + return; + } + scrollToBottom(); + }, [messageData]); + // 기존의 데이터가 없으면 로고를 보여주고, 아니면 이에 맞는 메시지 데이터들을 보여주면 된다 useEffect(() => { @@ -63,12 +95,22 @@ export default function EmptyChatBody({ }, []); return ( - + {ldsh.isEqual(messageData, {}) && messageDateArray.length === 0 && ( )} + {!ldsh.isEqual(messageData, {}) && + !(messageDateArray.length === 0) && + messageDateArray.map((messageDate) => { + return ( + + ); + })} ); } diff --git a/src/components/non-fixed/EmptyChatBody/EmptyChatLog/EmptyChatLog.tsx b/src/components/non-fixed/EmptyChatBody/EmptyChatLog/EmptyChatLog.tsx new file mode 100644 index 0000000..d0f58f9 --- /dev/null +++ b/src/components/non-fixed/EmptyChatBody/EmptyChatLog/EmptyChatLog.tsx @@ -0,0 +1,39 @@ +import styled from 'styled-components'; +import EmptyChatLogLeft from '@components/non-fixed/EmptyChatBody/EmptyChatLog/EmptyChatLogLeft/EmptyChatLogLeft'; +import EmptyChatLogRight from '@components/non-fixed/EmptyChatBody/EmptyChatLog/EmptyChatLogRight/EmptyChatLogRight'; +import { chatBodyDivElementGap } from '@styles/styledComponents'; + +const StyledChatLogContainer = styled.div` + width: 100%; + height: fit-content; + display: flex; + column-gap: 8px; + ${chatBodyDivElementGap} +`; + +export default function EmptyChatLog({ + isEqual, + from, + createdAt, + content, + like, +}: { + isEqual: boolean; + from: number; + createdAt: string; + content: string; + like: boolean; +}) { + return ( + + + + + ); +} diff --git a/src/components/non-fixed/EmptyChatBody/EmptyChatLog/EmptyChatLogLeft/EmptyChatLogLeft.tsx b/src/components/non-fixed/EmptyChatBody/EmptyChatLog/EmptyChatLogLeft/EmptyChatLogLeft.tsx new file mode 100644 index 0000000..9ef1477 --- /dev/null +++ b/src/components/non-fixed/EmptyChatBody/EmptyChatLog/EmptyChatLogLeft/EmptyChatLogLeft.tsx @@ -0,0 +1,23 @@ +import styled from 'styled-components'; +import { isMessageOwnerEqualsWithModeProps } from 'types/type'; + +const StyledChatLogLeft = styled.div` + width: auto; + height: 100%; +`; + +const StyledChatDiscordLogo = styled.img``; + +export default function EmptyChatLogLeft({ + isEqual, +}: isMessageOwnerEqualsWithModeProps) { + return ( + + {isEqual === true ? ( + + ) : ( + + )} + + ); +} diff --git a/src/components/non-fixed/EmptyChatBody/EmptyChatLog/EmptyChatLogRight/EmptyChatLogRight.tsx b/src/components/non-fixed/EmptyChatBody/EmptyChatLog/EmptyChatLogRight/EmptyChatLogRight.tsx new file mode 100644 index 0000000..76fefe7 --- /dev/null +++ b/src/components/non-fixed/EmptyChatBody/EmptyChatLog/EmptyChatLogRight/EmptyChatLogRight.tsx @@ -0,0 +1,117 @@ +import styled from 'styled-components'; +import { useRecoilState } from 'recoil'; +import { + messageDataState, + userNumberState, + isMessageLikeButtonClickedState, +} from '@context/state/atom'; +import { useParams } from 'react-router-dom'; + +const StyledChatLogRightContainer = styled.div` + display: flex; + flex-direction: column; + width: 100%; +`; + +const StyledNameAndMessageContainer = styled.div` + display: flex; + height: 20px; + justify-content: flex-start; + column-gap: 4px; +`; + +const StyledNameSpan = styled.div` + padding-top: 4px; + padding-bottom: 1px; + font-size: 13px; + font-weight: 500; + height: 100%; + color: ${(props) => props.theme.color.grayDark}; +`; + +const StyledTimeSpan = styled.div` + padding-top: 4px; + padding-bottom: 1px; + display: flex; + align-items: center; + flex-grow: 1; + font-size: 10px; + font-weight: 500; + color: ${(props) => props.theme.color.grayDark}; +`; + +const StyledMessageContentContainer = styled.div` + height: fit-content; + flex-grow: 1; + font-size: ${(props) => props.theme.textStyle.fontSize.body2}; + font-weight: 400; + color: ${(props) => props.theme.color.black}; + line-height: 22.5px; +`; + +const StyledLikeHeartDiv = styled.div` + width: 100%; + height: 22px; + display: flex; + justify-content: flex-start; + padding-top: 2px; +`; + +const StyledLikeHeartImage = styled.img``; +export default function EmptytChatLogRight({ + isEqual, + from, + createdAt, + content, + like, +}: { + isEqual: boolean; + from: number; + createdAt: string; + content: string; + like: boolean; +}) { + const [messageData, setMessageData] = useRecoilState(messageDataState); + const [userNumber, setUserNumber] = useRecoilState(userNumberState); + const [isMessageLikeButtonClicked, setIsMessageLikeButtonClicked] = + useRecoilState(isMessageLikeButtonClickedState); + const { username } = useParams(); + + const createdHourMinute = createdAt.slice(11, 16); + const createdDate = createdAt.slice(0, 10); + const deepCopiedMessageData = JSON.parse(JSON.stringify(messageData)); + + function handleDoubleClickMessage() { + // 자신과 달라야 한다. + if (from === userNumber) return; + + const prevSpecifiedDateArray = deepCopiedMessageData[createdDate]; + for (const data of prevSpecifiedDateArray) { + if (data['createdAt'] === createdAt) { + data['like'] = !data['like']; + } + } + setMessageData(deepCopiedMessageData); + localStorage.setItem( + 'chatMessageData', + JSON.stringify(deepCopiedMessageData) + ); + // 메시지 버튼이 눌렸는지에 관한 상태를 true로 만들어주고 chatBody에서 useEffect에서 조건부로 검사함 + setIsMessageLikeButtonClicked(true); + } + + return ( + + + {from !== 1 ? `${username}` : '김승완'} + {createdHourMinute} + + {content} + {isEqual === false && like === true && ( + + + + )} + + ); +} diff --git a/src/components/non-fixed/EmptyChatBody/EmptyDateDIvider/EmptyDateDivider.tsx b/src/components/non-fixed/EmptyChatBody/EmptyDateDIvider/EmptyDateDivider.tsx new file mode 100644 index 0000000..787e45f --- /dev/null +++ b/src/components/non-fixed/EmptyChatBody/EmptyDateDIvider/EmptyDateDivider.tsx @@ -0,0 +1,39 @@ +import styled from 'styled-components'; +import { dateStringProps } from 'types/type'; +import { dateBeforeAfter } from '@styles/styledComponents'; +import { chatBodyDivElementGap } from '@styles/styledComponents'; + +const StyledDateDividerContainer = styled.div` + width: 100%; + height: 20px; + display: flex; + justify-content: center; + align-items: center; + position: relative; + ${chatBodyDivElementGap} +`; + +const StyledDateDivider = styled.div` + &::before { + ${dateBeforeAfter} + left: -8px; + } + + &::after { + ${dateBeforeAfter} + right: -8px; + } + font-size: 13px; + font-weight: 500; + line-height: 19.5px; +`; + +export default function EmptyDateDivider({ dateString }: dateStringProps) { + const [year, month, day] = dateString.split('-'); + + return ( + + {`${year}년 ${month}월 ${day}일`} + + ); +} diff --git a/src/components/non-fixed/EmptyChatBody/EmptyOneDateContainer/EmptyOneDateContainer.tsx b/src/components/non-fixed/EmptyChatBody/EmptyOneDateContainer/EmptyOneDateContainer.tsx new file mode 100644 index 0000000..30b5750 --- /dev/null +++ b/src/components/non-fixed/EmptyChatBody/EmptyOneDateContainer/EmptyOneDateContainer.tsx @@ -0,0 +1,39 @@ +import styled from 'styled-components'; +import { useRecoilState } from 'recoil'; +import EmptyDateDivider from '@components/non-fixed/EmptyChatBody/EmptyDateDIvider/EmptyDateDivider'; +import { userNumberState, messageDataState } from '@context/state/atom'; +import EmptyChatLog from '@components/non-fixed/EmptyChatBody/EmptyChatLog/EmptyChatLog'; + +const StyledOneDateContainer = styled.div` + width: 100%; + height: auto; +`; + +export default function EmptyOneDateContainer({ + messageDate, +}: { + messageDate: string; +}) { + const [userNumber, setUserNumber] = useRecoilState(userNumberState); + const [messageData, setMessageData] = useRecoilState(messageDataState); + + const oneDateMessagData = messageData[messageDate]; + + return ( + + + {oneDateMessagData.map((data) => { + return ( + + ); + })} + + ); +} diff --git a/src/utils/makeUserIdNumber.js b/src/utils/makeUserIdNumber.js new file mode 100644 index 0000000..775a918 --- /dev/null +++ b/src/utils/makeUserIdNumber.js @@ -0,0 +1,11 @@ +export function makeUserIdNumber(name) { + // 각 문자를 아스키코드 값으로 바꿔서 사용자 id를 바꿈. + let hash = 0; + if (name.length === 0) return hash; + for (let i = 0; i < name.length; i++) { + const char = name.charCodeAt(i); + hash = (hash << 5) - hash + char; + hash |= 0; // Convert to 32bit integer + } + return hash; +} From e400fb8ae50e4de3091151b476df1b1e72b72ca9 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Sat, 27 Apr 2024 01:55:13 +0900 Subject: [PATCH 72/81] =?UTF-8?q?Feat=20:=20empty=20chat=20room=EC=97=90?= =?UTF-8?q?=EB=8F=84=20=EC=B1=84=ED=8C=85=20=EA=B8=B0=EB=8A=A5=EC=9D=84=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ChatHead/ChatHeadNav/EmptyChatHeadNav.tsx | 20 ++++++++++++++++--- .../EmptyChatLogRight/EmptyChatLogRight.tsx | 2 +- .../FriendsBody/FriendsList/FriendsList.tsx | 3 ++- src/pages/Friends.tsx | 8 ++++++++ 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/components/fixed/ChatHead/ChatHeadNav/EmptyChatHeadNav.tsx b/src/components/fixed/ChatHead/ChatHeadNav/EmptyChatHeadNav.tsx index 9a33e31..778b466 100644 --- a/src/components/fixed/ChatHead/ChatHeadNav/EmptyChatHeadNav.tsx +++ b/src/components/fixed/ChatHead/ChatHeadNav/EmptyChatHeadNav.tsx @@ -7,8 +7,9 @@ import { import ChatHeadNavRight from '@components/fixed/ChatHead/ChatHeadNav/ChatHeadNavRight'; import styled from 'styled-components'; import { userNumberState } from '@context/state/atom'; -import { useNavigate } from 'react-router-dom'; +import { useNavigate, useParams } from 'react-router-dom'; import { useRecoilState } from 'recoil'; +import { makeUserIdNumber } from '@utils/makeUserIdNumber'; const StyledEmptyChatHeadNav = styled.nav` height: 64px; @@ -29,6 +30,14 @@ export default function EmptyChatHeadNav({ setUserNumber(1); navigate('/messages'); } + // 상단 이름 부분을 누르면 벌어져야 하는일 --> userNumber가 바뀌어야 한다. 그에 따라서 이름도 바뀌어야 한다 + function handleClickHeadName() { + if (userNumber === 1) { + setUserNumber(makeUserIdNumber(username)); + } else { + setUserNumber(1); + } + } return ( @@ -37,8 +46,13 @@ export default function EmptyChatHeadNav({ src="/images/leftArrow.svg" onClick={handleClickArrowBackImage} /> - - {username} + + + {userNumber !== 1 ? '김승완' : username} + diff --git a/src/components/non-fixed/EmptyChatBody/EmptyChatLog/EmptyChatLogRight/EmptyChatLogRight.tsx b/src/components/non-fixed/EmptyChatBody/EmptyChatLog/EmptyChatLogRight/EmptyChatLogRight.tsx index 76fefe7..954784d 100644 --- a/src/components/non-fixed/EmptyChatBody/EmptyChatLog/EmptyChatLogRight/EmptyChatLogRight.tsx +++ b/src/components/non-fixed/EmptyChatBody/EmptyChatLog/EmptyChatLogRight/EmptyChatLogRight.tsx @@ -93,7 +93,7 @@ export default function EmptytChatLogRight({ } setMessageData(deepCopiedMessageData); localStorage.setItem( - 'chatMessageData', + `chatMessageData${username}`, JSON.stringify(deepCopiedMessageData) ); // 메시지 버튼이 눌렸는지에 관한 상태를 true로 만들어주고 chatBody에서 useEffect에서 조건부로 검사함 diff --git a/src/components/non-fixed/FriendsBody/FriendsList/FriendsList.tsx b/src/components/non-fixed/FriendsBody/FriendsList/FriendsList.tsx index c048f1c..5f01a8f 100644 --- a/src/components/non-fixed/FriendsBody/FriendsList/FriendsList.tsx +++ b/src/components/non-fixed/FriendsBody/FriendsList/FriendsList.tsx @@ -6,7 +6,6 @@ const StyledFriendsList = styled.div` height: fit-content; `; -// 임시 UI를 위한 텍스트 집합 const tmpFriendsList = [ 'discord_redesign1', '김정민', @@ -17,6 +16,8 @@ const tmpFriendsList = [ ]; export default function FriendsList() { + // 임시 UI를 위한 텍스트 집합 + return ( {tmpFriendsList.map((friendsName, index) => ( diff --git a/src/pages/Friends.tsx b/src/pages/Friends.tsx index 5e8d18f..f56eeb5 100644 --- a/src/pages/Friends.tsx +++ b/src/pages/Friends.tsx @@ -1,7 +1,15 @@ import FriendsBody from '@components/non-fixed/FriendsBody/FriendsBody'; import TabBar from '@components/non-fixed/TabBar/TabBar'; +import { useRecoilState } from 'recoil'; +import { userNumberState } from '@context/state/atom'; +import { useEffect } from 'react'; export default function Friends() { + const [userNumber, setUserNumber] = useRecoilState(userNumberState); + + useEffect(() => { + setUserNumber(1); + }, []); return ( <> From c3b331e801b4fc5bd0d5b5a08e10c789fc5073ca Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Sat, 27 Apr 2024 02:15:50 +0900 Subject: [PATCH 73/81] =?UTF-8?q?Fix=20:=20html=20=EC=86=8D=EC=84=B1?= =?UTF-8?q?=EB=AA=85=EA=B3=BC=20=EA=B2=B9=EC=B9=98=EB=8A=94=20ifBlueSignal?= =?UTF-8?q?=20=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 앞에 $를 붙여주는 방식으로 해결 --- .../fixed/ChatHead/ChatHeadNav/ChatHeadNav.tsx | 1 + .../fixed/ChatHead/ChatHeadNav/EmptyChatHeadNav.tsx | 1 + .../ChatHead/IphoneStatusBar/IphoneStatusBar.tsx | 1 + .../MessageBody/MessageList/MessageListContainer.tsx | 8 ++++---- .../MessageList/MessageListItem/MessageListItem.tsx | 6 +++--- .../MessageListItem/MessageListItemRight.tsx | 12 ++++++------ 6 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNav.tsx b/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNav.tsx index 7f4cc62..d0b46fb 100644 --- a/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNav.tsx +++ b/src/components/fixed/ChatHead/ChatHeadNav/ChatHeadNav.tsx @@ -3,6 +3,7 @@ import ChatHeadNavLeft from '@components/fixed/ChatHead/ChatHeadNav/ChatHeadNavL import ChatHeadNavRight from '@components/fixed/ChatHead/ChatHeadNav/ChatHeadNavRight'; export const StyledChatHeadNav = styled.nav` + height: 64px; flex-grow: 1; flex-shrink: 0; display: flex; diff --git a/src/components/fixed/ChatHead/ChatHeadNav/EmptyChatHeadNav.tsx b/src/components/fixed/ChatHead/ChatHeadNav/EmptyChatHeadNav.tsx index 778b466..eb1dc95 100644 --- a/src/components/fixed/ChatHead/ChatHeadNav/EmptyChatHeadNav.tsx +++ b/src/components/fixed/ChatHead/ChatHeadNav/EmptyChatHeadNav.tsx @@ -16,6 +16,7 @@ const StyledEmptyChatHeadNav = styled.nav` display: flex; align-items: center; justify-content: space-between; + flex-shrink: 0; `; export default function EmptyChatHeadNav({ diff --git a/src/components/fixed/ChatHead/IphoneStatusBar/IphoneStatusBar.tsx b/src/components/fixed/ChatHead/IphoneStatusBar/IphoneStatusBar.tsx index 05bb8cd..274a37f 100644 --- a/src/components/fixed/ChatHead/IphoneStatusBar/IphoneStatusBar.tsx +++ b/src/components/fixed/ChatHead/IphoneStatusBar/IphoneStatusBar.tsx @@ -9,6 +9,7 @@ const StyledIphoneStatusBar = styled.div` justify-content: space-between; align-items: center; column-gap: 195px; + flex-shrink: 0; `; export default function IphoneStatusBar() { diff --git a/src/components/non-fixed/MessageBody/MessageList/MessageListContainer.tsx b/src/components/non-fixed/MessageBody/MessageList/MessageListContainer.tsx index 94e38b9..c691082 100644 --- a/src/components/non-fixed/MessageBody/MessageList/MessageListContainer.tsx +++ b/src/components/non-fixed/MessageBody/MessageList/MessageListContainer.tsx @@ -19,7 +19,7 @@ export default function MessageListContainer() { diff --git a/src/components/non-fixed/MessageBody/MessageList/MessageListItem/MessageListItemRight.tsx b/src/components/non-fixed/MessageBody/MessageList/MessageListItem/MessageListItemRight.tsx index 2730f3c..4de47d4 100644 --- a/src/components/non-fixed/MessageBody/MessageList/MessageListItem/MessageListItemRight.tsx +++ b/src/components/non-fixed/MessageBody/MessageList/MessageListItem/MessageListItemRight.tsx @@ -11,7 +11,7 @@ const StyledMessageListItemRight = styled.div` `; interface messageListItemRightInnerType { - ifBlueSignal: boolean; + $ifBlueSignal: boolean; } const StyledMessageListItemRightInner = styled.div` @@ -52,7 +52,7 @@ const StyledDateString = styled.div` interface messageListItemRightType { name: string; - ifBlueSignal: boolean; + $ifBlueSignal: boolean; content: string; dateString: string; } @@ -61,19 +61,19 @@ const StyledBlueSignalImage = styled.img``; export default function MessageListItemRight({ name, - ifBlueSignal, + $ifBlueSignal, content, dateString, }: messageListItemRightType) { return ( - + {name} - {ifBlueSignal === true ? ( + {$ifBlueSignal === true ? ( ) : null} - + {content} {dateString} From 1637dbcb4824f3f844c2b9d7bb4a08e067f37bf9 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Sun, 28 Apr 2024 17:58:01 +0900 Subject: [PATCH 74/81] =?UTF-8?q?Docs=20:=20=EB=91=90=20=EB=B2=88=EC=A7=B8?= =?UTF-8?q?=20pr=EC=9D=84=20=EC=9C=84=ED=95=9C=20md=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pr2.md | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 pr2.md diff --git a/pr2.md b/pr2.md new file mode 100644 index 0000000..4aa0d52 --- /dev/null +++ b/pr2.md @@ -0,0 +1,96 @@ +> # 배포 링크 +> +> [배포 링크](https://react-messenger-19th-seungwan-wg21.vercel.app/) + +## 📌 구현 기능 + +1. 피그마에 나타난 화면 UI 구현 => `styled-components`의 `ThemeProvider`를 통해 디자인 시스템 구축 +2. 기존의 **json** 파일에 나타난 데이터를 보여주거나 이미 데이터가 존재한다면 이를 보여주기 +3. 사용자가 채팅을 입력하고, 기존의 데이터에 뒤이어 UI로 구현 => 채팅 입력 시, 채팅창의 최하단으로 포커스 이동 +4. 사용자가 이미 보낸 상대방의 메시지를 더블 클릭하면 하트 UI가 생성되고, 다시 더블 클릭하면 사라짐 +5. 채팅방 상단의 프로필을 클릭하면 상대방으로 사용자가 바뀜 +6. 나머지 피그마 디자인 페이지 구현 + +## 🧠 느낀 점 및 시간 투자 부분 + +### 시간 투자 💪🏼 + +### 느낀 점 ❗️ + +## ❓ Key Questions + +- Routing 이란?

+ 라우팅이란, 프론트엔드에서 웹 애플리케이션 내의 다양한 페이지를 탐색할 수 있도록 도와주는 기술이다. 네이버, 다음, 쿠팡, 배달의 민족과 같이 우리가 자주 사용하는 웹 사이트는 랜딩 페이지 이외에도 여러 세부 페이지로 나뉘어져 있다.
+ 기존의 html, css, vanilla Javascript 만을 이용하는 방식에서는 `a` 태그를 통해 특정 페이지로의 전환을 하고 해당 페이지에 알맞은 html 파일을 통해 사용자에게 적절한 UI를 렌더링한다.
+ 요즘 많이 사용되는 AngularJS(인기가 좀 죽긴 했다), vueJS, reactJS에는 **Angular-router**, **vue-router**, **react-router** 등의 방식으로 라우팅을 지원하는데, 이를 사용하지 않았을 때와 비교해서 살펴보면 좋다. + +``` +import "./styles.css"; +import { useState } from "react"; +import Home from "./Home"; +import About from "./About"; +import NotFound from "./Not-found"; + +export default function App() { + const [comp, setComp] = useState(Home); + + return ( +
+ + + +
+
+ ); +} +``` + +위와 같은 방식은 react 코드 단에서 react-router-dom의 도움 없이 상태(state)만으로 라우팅을 진행하고 있다. 버튼을 누르면 상태가 변경되어 `main` 태그 내에서 렌더링되는 children 속성 컴포넌트가 바뀔 수 있게 한다. 하지만 이와 같은 로직은 페이지가 추가될 때마다 관련 핸들러 함수를 만드는 등 복잡도가 증가할 수 있다. 이를 위해 react는 `react-router-dom` 모듈의 Router 개념을 통해 해결한다 +

+ +``` +function App() { + return ( + + + + + + }> + }> + }> + }> + + }> + }> + } /> + + + + + + ); +} +``` + +위의 코드는 react-router-dom 모듈의 `BrowserRouter`, `Routes`, `Route` 컴포넌트 들을 이용해 app.tsx 파일 내에서 라우팅을 진행해준 것이다(ThemeProvider와 RecoilRoot 컴포넌트는 각각 디자인 시스템, 전역 상태 관리를 위해 존재하는 wrapper 컴포넌트이니 신경 쓰지 않아도 된다).
+이와 같은 구조를 이용하면 웹 사이트의 도메인 주소의 특정 `url path`로 사용자가 접근하면 Route 컴포넌트의 element 속성으로 바인딩 된 컴포넌트가 렌더링 되는 구조인 것이다.
+하지만 이와 같은 라우팅 방식은 ReactJS 같은 CSR(클라이언트 사이드 렌더링) 방식의 웹 페이지에서 보이는 특성이고, nextJS나 Remix 같은 SSR(서버 사이드 렌더링), SSG(Static Site Generation)을 지원하는 풀스택 프레임워크에서는 주로 src 디렉터리 내부의 폴더 구조를 통해 라우팅을 진행한다. 즉, 디렉터리가 하나의 url path에 대응하는 것이다. + +- SPA 란?

+ SPA는 **Single Page Application**의 약자로, 특정 웹 애플리케이션이 구동될 떄 하나의 html 파일만을 기반으로 하여 작동한다는 것이다. reactJS를 이용하여 프로젝트를 진행할 때, CRA이면 public 디렉터리 내에 index.html이, vite를 이용하면 루트 디렉터리에 index.html 파일 하나만이 존재하는 것을 볼 수 있다. 해당 파일을 들여다보면 `body` 컴포넌트 내에 **id가 root인 div 태그** 하나 만이 외롭게 있는 것일 알 수 있다.
+ 이는 위에서 말한 react의 라우팅 방식에 의해 매 페이지에 해당하는 컨텐츠들이 id 가 root인 태그 내에 채워지며 상황마다 바뀌기 때문이다. [뉴진스의 홈페이지](https://newjeans.kr/)에서 [페이지 소스를 살펴보면](view-source:https://newjeans.kr/) 클라이언트(브라우저)에 전달된 리소스는 텅 빈 html 파일만이 전달된 것을 알 수 있다.
+ SPA는 세부 페이지마다 바뀌는 내용을 AJAX(Asnycronous Javascript and Xml)라는 기술을 통해 필요한 부분의 데이터에 대한 요청을 실행하여 렌더링하기 때문에 html 문서 자체가 바뀌면서 생기는 full page refresh와 같은 현상을 막을 수 있다. 이는 페이지 간의 부드러운 전환으로 사용자에게 더 나은 UX를 선사할 수 있다. 하지만 위에서 언급한 바와 같이 처음 클라이언트에게 전달되는 html 문서에는 아무런 컨텐츠가 존재하지 않기 때문에 검색 엔진 최적화(SEO)의 측면에서는 다소 불리하다고 할 수 있다.
+ react 같은 SPA 기술이 등장 이전에는 Java의 서블릿과 같은 개념, 여러 템플릿 엔진을 통해 사용자의 동적 요청에 따른 웹 페이지 반환 **(SSR: Server Side Rendering)**와 미리 만들어둔 컨텐츠가 자주 변하지 않는 정적 컨텐츠를 생성하는 **SSG(Static Site Generation)** 개념이 주를 이루었다. 이는 복수의 html 파일을 사용자에게 전달할 수 있다는 점에서 MPA(Multi Page Application)이라고 하며, 미리 컨텐츠가 채워져 있어 검색 엔진 최적화를 할 수 있지만 페이지 전환 시 뚝뚝 끊기는 현상이 발생한다는 단점이 있다.
+ + 위의 두 방식의 단점을 보완하고 장점을 챙기는 방식으로 나온 것들이 바로 nextJS와 nuxtJS 같은 풀스택 프레임워크 개념이다. 이들은 페이지, 컴포넌트 기반의 SSG, SSR 방식의 구현을 지원함과 동시에 웹 페이지의 변경되는 부분에 대한 추가적인 네트워크 데이터 요청으로 부드러운 페이지 전환 효과를 기대할 수 있다. + +- 상태 관리란?

+ 별도의 상태 관리 라이브러리를 사용하지 않아도 react 프로젝트를 진행할 수 있다. 이 경우에는 상태가 필요한 컴포넌트에서 `useState()` 훅을 통해 상태를 선언하여 UI를 렌더링 하면 된다. 하지만 복수의 컴포넌트에서 하나의 상태를 통해 렌더링 해야하는 로직이 존재한다면 상태 끌어올리기(state lifting)이 필요하다. 이는 해당 컴포넌트들이 공통적으로 가지는 부모 컴포넌트, 혹은 그 상위의 컴포넌트로 상태의 선언을 끌어올린 후,상태를 prop으로 계속해서 전달하여 사용하는 것이다. 단순한 프로젝트에서는 큰 상관이 없겠지만 이는 여러 문제점을 내포하고 있다.
+ + 1. 계속되는 **prop drilling**으로 프로그래머가 일일히 적어줘야 하고, 오타와 같은 human error가 발생할 수 있다. + 2. 상태가 변화할 때, 여러 컴포넌트를 통해 상태가 전달된다면, 그 중간에 있는 관련 없는 컴포넌트까지 리렌더링 되는 성능 비효율이 발생한다. + 3. 추후에 상태 관련 코드를 변경할 일이 생겼을 때, 하나의 상태에 여러 컴포넌트가 묶여있는 coupling 현상은 유지 보수를 어렵게 만든다 -> 단일 책임 원칙(SRP : Single Responsibility Principle)의 위반 +
+ + 이를 위해 contextAPI 같은 reactJS 자체의 방식, flux 패턴을 적용한 redux 라이브러리, atomic 패턴의 recoilJS와 그 이외의 mobx, zustand, jotai 등을 활용하면 보다 더 편리하게 상태 관리를 진행할 수 있다. From 48e8f53fecf8adc43bcb8968abb9d3b16aded76e Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Tue, 30 Apr 2024 15:06:08 +0900 Subject: [PATCH 75/81] =?UTF-8?q?Feat=20:=20profile=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EB=A1=9C=EA=B3=A0=20UI=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. img를 담은 div를 img 보다 너비 높이를 높게 잡고 border radius를 원형으로 만든 뒤, background color를 적용함 --- .../non-fixed/ProfileBody/ProfileBody.tsx | 3 ++ .../ProfileContent/ProfileContent.tsx | 5 ++-- .../ProfileDiscordLogo/ProfileDiscordLogo.tsx | 30 +++++++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 src/components/non-fixed/ProfileBody/ProfileDiscordLogo/ProfileDiscordLogo.tsx diff --git a/src/components/non-fixed/ProfileBody/ProfileBody.tsx b/src/components/non-fixed/ProfileBody/ProfileBody.tsx index 029163a..c696c17 100644 --- a/src/components/non-fixed/ProfileBody/ProfileBody.tsx +++ b/src/components/non-fixed/ProfileBody/ProfileBody.tsx @@ -1,18 +1,21 @@ import styled from 'styled-components'; import ProfilePageHead from '@components/non-fixed/ProfileBody/ProfilePageHead/ProfilePageHead'; import ProfileContent from '@components/non-fixed/ProfileBody/ProfileContent/ProfileContent'; +import ProfileDiscordLogo from '@components/non-fixed/ProfileBody/ProfileDiscordLogo/ProfileDiscordLogo'; const StyledProfileBody = styled.section` flex-grow: 1; overflow-y: scroll; display: flex; flex-direction: column; + position: relative; `; export default function ProfileBody() { return ( + ); diff --git a/src/components/non-fixed/ProfileBody/ProfileContent/ProfileContent.tsx b/src/components/non-fixed/ProfileBody/ProfileContent/ProfileContent.tsx index 377eedf..fd2fb5f 100644 --- a/src/components/non-fixed/ProfileBody/ProfileContent/ProfileContent.tsx +++ b/src/components/non-fixed/ProfileBody/ProfileContent/ProfileContent.tsx @@ -3,8 +3,9 @@ import { Link } from 'react-router-dom'; const StyledProfileContent = styled.div` flex-grow: 1; - margin-top: 16px; - padding-top: 57px; + /* margin-top: 16px; */ + /* padding-top: 57px; */ + margin-top: 44px; background-color: ${(props) => props.theme.color.grayLight}; display: flex; flex-direction: column; diff --git a/src/components/non-fixed/ProfileBody/ProfileDiscordLogo/ProfileDiscordLogo.tsx b/src/components/non-fixed/ProfileBody/ProfileDiscordLogo/ProfileDiscordLogo.tsx new file mode 100644 index 0000000..a882380 --- /dev/null +++ b/src/components/non-fixed/ProfileBody/ProfileDiscordLogo/ProfileDiscordLogo.tsx @@ -0,0 +1,30 @@ +import styled from 'styled-components'; + +const StyledProfileDiscordLogoContainer = styled.div` + position: absolute; + width: 90px; + height: 90px; + top: 65px; + left: 147.5px; + border-radius: 50%; + background-color: white; + z-index: 100; + display: flex; + justify-content: center; + align-items: center; +`; + +const StyledProfileDiscordLogo = styled.img` + /* border-radius: 50%; */ +`; + +export default function ProfileDiscordLogo() { + return ( + + + + ); +} From 9ad56dea31d4a61f781e1de04a98b21ab7d58569 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Tue, 30 Apr 2024 16:24:41 +0900 Subject: [PATCH 76/81] =?UTF-8?q?Fix=20:=20profile=20logo=20UI=20position?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 로고의 너비 높이는 80px 이지만 이를 위치시키는 컨테이너는 90px 이므로 이를 고려해야 레이아웃 배치 --- .../ProfileBody/ProfileContent/ProfileContent.tsx | 4 +--- .../ProfileBody/ProfileDiscordLogo/ProfileDiscordLogo.tsx | 7 +++---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/components/non-fixed/ProfileBody/ProfileContent/ProfileContent.tsx b/src/components/non-fixed/ProfileBody/ProfileContent/ProfileContent.tsx index fd2fb5f..13b0b8d 100644 --- a/src/components/non-fixed/ProfileBody/ProfileContent/ProfileContent.tsx +++ b/src/components/non-fixed/ProfileBody/ProfileContent/ProfileContent.tsx @@ -1,10 +1,8 @@ import styled from 'styled-components'; -import { Link } from 'react-router-dom'; const StyledProfileContent = styled.div` flex-grow: 1; - /* margin-top: 16px; */ - /* padding-top: 57px; */ + margin-top: 44px; background-color: ${(props) => props.theme.color.grayLight}; display: flex; diff --git a/src/components/non-fixed/ProfileBody/ProfileDiscordLogo/ProfileDiscordLogo.tsx b/src/components/non-fixed/ProfileBody/ProfileDiscordLogo/ProfileDiscordLogo.tsx index a882380..b00f954 100644 --- a/src/components/non-fixed/ProfileBody/ProfileDiscordLogo/ProfileDiscordLogo.tsx +++ b/src/components/non-fixed/ProfileBody/ProfileDiscordLogo/ProfileDiscordLogo.tsx @@ -5,7 +5,8 @@ const StyledProfileDiscordLogoContainer = styled.div` width: 90px; height: 90px; top: 65px; - left: 147.5px; + left: 142.5px; + right: 142.5px; border-radius: 50%; background-color: white; z-index: 100; @@ -14,9 +15,7 @@ const StyledProfileDiscordLogoContainer = styled.div` align-items: center; `; -const StyledProfileDiscordLogo = styled.img` - /* border-radius: 50%; */ -`; +const StyledProfileDiscordLogo = styled.img``; export default function ProfileDiscordLogo() { return ( From 8341301e2c88ffc512d25bda7d675c436e863c33 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Tue, 30 Apr 2024 22:00:34 +0900 Subject: [PATCH 77/81] =?UTF-8?q?Refactor=20:=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20=EC=A3=BC=EC=84=9D=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fixed/ChatHead/ChatHeadNav/EmptyChatHeadNav.tsx | 2 +- src/components/non-fixed/ChatInput/ChatInput.tsx | 1 - src/components/non-fixed/EmptyChatBody/EmptyChatBody.tsx | 4 +--- .../non-fixed/EmptyChatBody/ProfileGroupUI/ProfileGroupUI.tsx | 1 - 4 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/components/fixed/ChatHead/ChatHeadNav/EmptyChatHeadNav.tsx b/src/components/fixed/ChatHead/ChatHeadNav/EmptyChatHeadNav.tsx index eb1dc95..91244fa 100644 --- a/src/components/fixed/ChatHead/ChatHeadNav/EmptyChatHeadNav.tsx +++ b/src/components/fixed/ChatHead/ChatHeadNav/EmptyChatHeadNav.tsx @@ -7,7 +7,7 @@ import { import ChatHeadNavRight from '@components/fixed/ChatHead/ChatHeadNav/ChatHeadNavRight'; import styled from 'styled-components'; import { userNumberState } from '@context/state/atom'; -import { useNavigate, useParams } from 'react-router-dom'; +import { useNavigate } from 'react-router-dom'; import { useRecoilState } from 'recoil'; import { makeUserIdNumber } from '@utils/makeUserIdNumber'; diff --git a/src/components/non-fixed/ChatInput/ChatInput.tsx b/src/components/non-fixed/ChatInput/ChatInput.tsx index 55adfc1..f8ebe3c 100644 --- a/src/components/non-fixed/ChatInput/ChatInput.tsx +++ b/src/components/non-fixed/ChatInput/ChatInput.tsx @@ -3,7 +3,6 @@ import ChatInputForm from '@components/non-fixed/ChatInput/ChatInputForm'; const StyledChatInputContainer = styled.div` width: 375px; - /* height: auto; */ min-height: 90px; padding-bottom: 34px; background-color: ${(props) => props.theme.color.white}; diff --git a/src/components/non-fixed/EmptyChatBody/EmptyChatBody.tsx b/src/components/non-fixed/EmptyChatBody/EmptyChatBody.tsx index c2faaee..2edb47e 100644 --- a/src/components/non-fixed/EmptyChatBody/EmptyChatBody.tsx +++ b/src/components/non-fixed/EmptyChatBody/EmptyChatBody.tsx @@ -2,7 +2,7 @@ import styled from 'styled-components'; import ProfileGroupUI from '@components/non-fixed/EmptyChatBody/ProfileGroupUI/ProfileGroupUI'; import EmptyOneDateContainer from './EmptyOneDateContainer/EmptyOneDateContainer'; import { useEffect, useRef } from 'react'; -import { messageDataObject, voidFunction } from 'types/type'; +import { voidFunction } from 'types/type'; import { useRecoilState } from 'recoil'; import { messageDataState, @@ -19,8 +19,6 @@ const StyledEmptyChatBody = styled.div` margin-left: 16px; width: 343px; overflow-y: scroll; - /* display: flex; */ - /* flex-direction: column; */ `; const StyledProfileGroupUIContainer = styled.div` diff --git a/src/components/non-fixed/EmptyChatBody/ProfileGroupUI/ProfileGroupUI.tsx b/src/components/non-fixed/EmptyChatBody/ProfileGroupUI/ProfileGroupUI.tsx index 5ee7238..a73c5d3 100644 --- a/src/components/non-fixed/EmptyChatBody/ProfileGroupUI/ProfileGroupUI.tsx +++ b/src/components/non-fixed/EmptyChatBody/ProfileGroupUI/ProfileGroupUI.tsx @@ -1,4 +1,3 @@ -// empty 메시지 창에 아직 메시지 데이터가 없을 때 보여줄 UI이다 import styled from 'styled-components'; const StyledProfileGroupUI = styled.div` From 079de1124642f4263c22d095f1b1c717ae2b8c38 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Tue, 30 Apr 2024 22:35:35 +0900 Subject: [PATCH 78/81] =?UTF-8?q?Docs=20:=20pr=EC=9D=84=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20=EB=A7=88=ED=81=AC=EB=8B=A4=EC=9A=B4=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pr2.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/pr2.md b/pr2.md index 4aa0d52..2d953c4 100644 --- a/pr2.md +++ b/pr2.md @@ -15,8 +15,17 @@ ### 시간 투자 💪🏼 +url 라우팅을 진행하는 데에 시간을 꽤 투자했던 것 같습니다. 3주차의 과제가 react로 채팅방까지를 구현하는 것이라, 이를 루트(/) 디렉터리로 설정했습니다.
+이번 과제는 친구 목록, 채팅 목록, 채팅방 입장, 개인 프로필 페이지를 모두 구현해야 함과 동시에 기존의 채팅방을 녹여내는 것까지라 프로젝트 url 설계를 먼저 진행했습니다. +공통적으로 많이 사용되는 header와 tabBar 같은 경우에는 `CommonLayout` 컴포넌트에 넣어주고, 주소에 따라서 바뀌는 부분을 `Outlet` 컴포넌트를 통해 유동적으로 바꿔주었습니다.
+또한 사용자가 입장한 채팅방에서 채팅을 입력한 후, 새로 고침을 하거나 퇴장한 후 다시 입장해도 기존의 메시지 데이터가 남아있어야 하기에 이를 `localStorage`에 넣어주는 방식을 이용했습니다. + ### 느낀 점 ❗️ +- 웹 애플리케이션의 전체적인 페이지 구조가 나와야 공통 레이아웃, 변화하는 Outlet 등을 적용하기 용이한 것 같습니다. 그 과정에서 기획자나 디자이너가 다른 페이지를 추가적으로 요구할 시를 위해 확장성 있는 설계가 필요할 것 같습니다. +- 현재 구현한 웹 애플리케이션은 사용자들의 이름이 서로 다름을 가정하고 있습니다. 하지만 실제 서비스에서는 동명이인이 존재할 가능성이 충분합니다. 이에 `localStorage`에 key 값으로 사용자명을 썼는데, 어떻게 하면 primary한 데이터로 바꿀 수 있을지 더 고민해보면 좋을 것 같습니다. +- 너무 세세한 상태까지 모두 `recoil`이나 `redux` 전역 상태 관리 라이브러리로 관리할 필요는 없을 것 같습니다. 사용자의 인터렉션에 따라 상태가 변화하고 관련 UI가 구현되는 정도라면 해당 상태는 컴포넌트에서 `useState()` 훅을 이용하는 것이 단일 책임의 원칙에도 더 맞을 것 같습니다. + ## ❓ Key Questions - Routing 이란?

@@ -81,7 +90,7 @@ function App() { SPA는 **Single Page Application**의 약자로, 특정 웹 애플리케이션이 구동될 떄 하나의 html 파일만을 기반으로 하여 작동한다는 것이다. reactJS를 이용하여 프로젝트를 진행할 때, CRA이면 public 디렉터리 내에 index.html이, vite를 이용하면 루트 디렉터리에 index.html 파일 하나만이 존재하는 것을 볼 수 있다. 해당 파일을 들여다보면 `body` 컴포넌트 내에 **id가 root인 div 태그** 하나 만이 외롭게 있는 것일 알 수 있다.
이는 위에서 말한 react의 라우팅 방식에 의해 매 페이지에 해당하는 컨텐츠들이 id 가 root인 태그 내에 채워지며 상황마다 바뀌기 때문이다. [뉴진스의 홈페이지](https://newjeans.kr/)에서 [페이지 소스를 살펴보면](view-source:https://newjeans.kr/) 클라이언트(브라우저)에 전달된 리소스는 텅 빈 html 파일만이 전달된 것을 알 수 있다.
SPA는 세부 페이지마다 바뀌는 내용을 AJAX(Asnycronous Javascript and Xml)라는 기술을 통해 필요한 부분의 데이터에 대한 요청을 실행하여 렌더링하기 때문에 html 문서 자체가 바뀌면서 생기는 full page refresh와 같은 현상을 막을 수 있다. 이는 페이지 간의 부드러운 전환으로 사용자에게 더 나은 UX를 선사할 수 있다. 하지만 위에서 언급한 바와 같이 처음 클라이언트에게 전달되는 html 문서에는 아무런 컨텐츠가 존재하지 않기 때문에 검색 엔진 최적화(SEO)의 측면에서는 다소 불리하다고 할 수 있다.
- react 같은 SPA 기술이 등장 이전에는 Java의 서블릿과 같은 개념, 여러 템플릿 엔진을 통해 사용자의 동적 요청에 따른 웹 페이지 반환 **(SSR: Server Side Rendering)**와 미리 만들어둔 컨텐츠가 자주 변하지 않는 정적 컨텐츠를 생성하는 **SSG(Static Site Generation)** 개념이 주를 이루었다. 이는 복수의 html 파일을 사용자에게 전달할 수 있다는 점에서 MPA(Multi Page Application)이라고 하며, 미리 컨텐츠가 채워져 있어 검색 엔진 최적화를 할 수 있지만 페이지 전환 시 뚝뚝 끊기는 현상이 발생한다는 단점이 있다.
+ react 같은 SPA 기술이 등장 이전에는 Java의 서블릿과 같은 개념, 여러 템플릿 엔진을 통해 사용자의 동적 요청에 따른 웹 페이지 반환 **SSR(Server Side Rendering)** 와 미리 만들어둔 컨텐츠가 자주 변하지 않는 정적 컨텐츠를 생성하는 **SSG(Static Site Generation)** 개념이 주를 이루었다. 이는 복수의 html 파일을 사용자에게 전달할 수 있다는 점에서 MPA(Multi Page Application)이라고 하며, 미리 컨텐츠가 채워져 있어 검색 엔진 최적화를 할 수 있지만 페이지 전환 시 뚝뚝 끊기는 현상이 발생한다는 단점이 있다.
위의 두 방식의 단점을 보완하고 장점을 챙기는 방식으로 나온 것들이 바로 nextJS와 nuxtJS 같은 풀스택 프레임워크 개념이다. 이들은 페이지, 컴포넌트 기반의 SSG, SSR 방식의 구현을 지원함과 동시에 웹 페이지의 변경되는 부분에 대한 추가적인 네트워크 데이터 요청으로 부드러운 페이지 전환 효과를 기대할 수 있다. From 74878208ad45455536eecad5f4cbd1ade1cade23 Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Sun, 5 May 2024 13:46:58 +0900 Subject: [PATCH 79/81] Fix : not found page for unexpected approach MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 사용자가 예상치 못한 접근을 하면 not found page로 갈 수 있도록 라우팅 설정 --- src/App.tsx | 9 ++++++++- src/pages/NotFound.tsx | 20 ++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 src/pages/NotFound.tsx diff --git a/src/App.tsx b/src/App.tsx index 0ebea75..109f651 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3,12 +3,13 @@ import theme from '@styles/theme'; import GlobalStyles from '@styles/globalStyles'; import ChatMain from '@pages/ChatMain'; import { RecoilRoot } from 'recoil'; -import { BrowserRouter, Routes, Route } from 'react-router-dom'; +import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'; import CommonLayout from '@pages/CommonLayout'; import Friends from '@pages/Friends'; import Messages from '@pages/Messages'; import Profile from '@pages/Profile'; import EmptyChat from '@pages/EmptyChat'; +import NotFound from '@pages/NotFound'; function App() { return ( @@ -21,11 +22,17 @@ function App() { }> }> }> + }>
}> }> } /> + } + > + }> diff --git a/src/pages/NotFound.tsx b/src/pages/NotFound.tsx new file mode 100644 index 0000000..08b7c8b --- /dev/null +++ b/src/pages/NotFound.tsx @@ -0,0 +1,20 @@ +import styled from 'styled-components'; +import TabBar from '@components/non-fixed/TabBar/TabBar'; +import { Link } from 'react-router-dom'; + +const StyledNotFoundContainer = styled.div` + flex-grow: 1; + overflow-y: scroll; +`; + +export default function NotFound() { + return ( + <> + + This is not found page.Go back to home page + Go to home page + + + + ); +} From edcf5993ecc94b6c6c69906ca4e6c34af11e8e3c Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Sun, 5 May 2024 14:04:23 +0900 Subject: [PATCH 80/81] Forcing an empty commit. From bada0ea9da4af69060426fde11fc63437a4621de Mon Sep 17 00:00:00 2001 From: Programming-Seungwan Date: Sun, 5 May 2024 14:10:34 +0900 Subject: [PATCH 81/81] =?UTF-8?q?Refactor=20:=20=EC=A4=91=EB=B3=B5?= =?UTF-8?q?=EB=90=98=EB=8A=94=20=EC=BD=94=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/App.tsx b/src/App.tsx index 109f651..b47d19c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -32,7 +32,6 @@ function App() { path="*" element={} > - }>