From 510f4989dc4a8040337e4e112ec80cdbf7cfce44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=A5=98=EC=9B=90?= Date: Fri, 27 Sep 2024 00:28:19 +0900 Subject: [PATCH 01/26] =?UTF-8?q?deploy:=20vercel=EB=B0=B0=ED=8F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 201 +++++++++++++++++++++++++++++-- package.json | 6 + "src/\bimages.d.ts" | 4 + src/App.tsx | 21 +++- src/assets/Back.svg | 5 + src/assets/Profile image.svg | 7 ++ src/assets/call.svg | 5 + src/assets/cancel.svg | 10 ++ src/assets/plus.svg | 6 + src/assets/search.svg | 5 + src/assets/setting.svg | 5 + src/assets/smile.svg | 8 ++ src/assets/thumb_up.svg | 5 + src/assets/video.svg | 5 + src/components/ChatRoom.tsx | 109 +++++++++++++++++ src/components/Header.tsx | 63 ++++++++++ src/components/MessageInput.tsx | 56 +++++++++ src/components/MessageList.tsx | 110 +++++++++++++++++ src/components/ProfileDetail.tsx | 62 ++++++++++ src/components/ProfileInfo.tsx | 47 ++++++++ src/index.css | 16 +-- tailwind.config.js | 8 ++ 22 files changed, 738 insertions(+), 26 deletions(-) create mode 100644 "src/\bimages.d.ts" create mode 100644 src/assets/Back.svg create mode 100644 src/assets/Profile image.svg create mode 100644 src/assets/call.svg create mode 100644 src/assets/cancel.svg create mode 100644 src/assets/plus.svg create mode 100644 src/assets/search.svg create mode 100644 src/assets/setting.svg create mode 100644 src/assets/smile.svg create mode 100644 src/assets/thumb_up.svg create mode 100644 src/assets/video.svg create mode 100644 src/components/ChatRoom.tsx create mode 100644 src/components/Header.tsx create mode 100644 src/components/MessageInput.tsx create mode 100644 src/components/MessageList.tsx create mode 100644 src/components/ProfileDetail.tsx create mode 100644 src/components/ProfileInfo.tsx create mode 100644 tailwind.config.js diff --git a/package-lock.json b/package-lock.json index c27bbe4..5420705 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,9 +17,15 @@ "@types/react-dom": "^18.2.22", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-router-dom": "^6.26.2", "react-scripts": "5.0.1", + "styled-components": "^6.1.13", "typescript": "^4.9.5", "web-vitals": "^2.1.4" + }, + "devDependencies": { + "@types/styled-components": "^5.1.34", + "tailwindcss": "^3.4.13" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -2288,6 +2294,27 @@ "postcss-selector-parser": "^6.0.10" } }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz", + "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==", + "license": "MIT", + "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==", + "license": "MIT" + }, + "node_modules/@emotion/unitless": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==", + "license": "MIT" + }, "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", @@ -3338,6 +3365,15 @@ } } }, + "node_modules/@remix-run/router": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.2.tgz", + "integrity": "sha512-baiMx18+IMuD1yyvOGaHM9QrVUPGGG0jC+z+IPHnRJWUAUvaKuWKyE8gjDj2rzv3sz9zOGoRSPgeBVHRhZnBlA==", + "license": "MIT", + "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", @@ -4103,6 +4139,17 @@ "@types/node": "*" } }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz", + "integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "node_modules/@types/html-minifier-terser": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", @@ -4290,6 +4337,24 @@ "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==" }, + "node_modules/@types/styled-components": { + "version": "5.1.34", + "resolved": "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.34.tgz", + "integrity": "sha512-mmiVvwpYklFIv9E8qfxuPyIt/OuyIrn6gMOAMOFUO3WJfSrSE+sGUoa4PiZj77Ut7bKZpaa6o1fBKS/4TOEvnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hoist-non-react-statics": "*", + "@types/react": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/stylis": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.5.tgz", + "integrity": "sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==", + "license": "MIT" + }, "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 +5815,15 @@ "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==", + "license": "MIT", + "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", @@ -5762,9 +5836,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001600", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001600.tgz", - "integrity": "sha512-+2S9/2JFhYmYaDpZvo0lKkfvuKIglrx68MwOBqMGHhQsNkLjB5xtc/TGoEPs+MxjSyN/72qer2g97nzR641mOQ==", + "version": "1.0.30001664", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001664.tgz", + "integrity": "sha512-AmE7k4dXiNKQipgn7a2xg558IRqPN3jMQY/rOsbxDhrd0tyChwbITBfiwtnqz8bi2M5mIWbxAYBvk7W7QBUS2g==", "funding": [ { "type": "opencollective", @@ -5778,7 +5852,8 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/case-sensitive-paths-webpack-plugin": { "version": "2.4.0", @@ -6182,6 +6257,15 @@ "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==", + "license": "ISC", + "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 +6456,17 @@ "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==", + "license": "MIT", + "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", @@ -8953,6 +9048,23 @@ "he": "bin/he" } }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true, + "license": "MIT" + }, "node_modules/hoopy": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", @@ -14864,6 +14976,38 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "6.26.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.26.2.tgz", + "integrity": "sha512-tvN1iuT03kHgOFnLPfLJ8V95eijteveqdOSk+srqfePtQvqCExB8eHOYnlilbOcyJyKnYkr1vJvf7YqotAJu1A==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.19.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.26.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.2.tgz", + "integrity": "sha512-z7YkaEW0Dy35T3/QKPYB1LjMK2R1fxnHO8kWpUMTBdfVzZrWOiY9a7CtN8HqdWtDUWd5FY6Dl8HFsqVwH4uOtQ==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.19.2", + "react-router": "6.26.2" + }, + "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", @@ -15719,6 +15863,12 @@ "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==", + "license": "MIT" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -16260,6 +16410,34 @@ "webpack": "^5.0.0" } }, + "node_modules/styled-components": { + "version": "6.1.13", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.13.tgz", + "integrity": "sha512-M0+N2xSnAtwcVAQeFEsGWFFxXDftHUD7XrKla06QbpUMmbmtFBMMTcKWvFXtWxuD5qQkB8iU5gk6QASlx2ZRMw==", + "license": "MIT", + "dependencies": { + "@emotion/is-prop-valid": "1.2.2", + "@emotion/unitless": "0.8.1", + "@types/stylis": "4.2.5", + "css-to-react-native": "3.2.0", + "csstype": "3.1.3", + "postcss": "8.4.38", + "shallowequal": "1.1.0", + "stylis": "4.3.2", + "tslib": "2.6.2" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/styled-components" + }, + "peerDependencies": { + "react": ">= 16.8.0", + "react-dom": ">= 16.8.0" + } + }, "node_modules/stylehacks": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", @@ -16275,6 +16453,12 @@ "postcss": "^8.2.15" } }, + "node_modules/stylis": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz", + "integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==", + "license": "MIT" + }, "node_modules/sucrase": { "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", @@ -16491,9 +16675,10 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" }, "node_modules/tailwindcss": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz", - "integrity": "sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==", + "version": "3.4.13", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.13.tgz", + "integrity": "sha512-KqjHOJKogOUt5Bs752ykCeiwvi0fKVkr5oqsFNt/8px/tA8scFPIlkygsf6jXrfCqGHz7VflA6+yytWuM+XhFw==", + "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -16503,7 +16688,7 @@ "fast-glob": "^3.3.0", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", - "jiti": "^1.19.1", + "jiti": "^1.21.0", "lilconfig": "^2.1.0", "micromatch": "^4.0.5", "normalize-path": "^3.0.0", diff --git a/package.json b/package.json index ea335d3..72704da 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,9 @@ "@types/react-dom": "^18.2.22", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-router-dom": "^6.26.2", "react-scripts": "5.0.1", + "styled-components": "^6.1.13", "typescript": "^4.9.5", "web-vitals": "^2.1.4" }, @@ -39,5 +41,9 @@ "last 1 firefox version", "last 1 safari version" ] + }, + "devDependencies": { + "@types/styled-components": "^5.1.34", + "tailwindcss": "^3.4.13" } } diff --git "a/src/\bimages.d.ts" "b/src/\bimages.d.ts" new file mode 100644 index 0000000..1a3dd3c --- /dev/null +++ "b/src/\bimages.d.ts" @@ -0,0 +1,4 @@ +declare module "*.svg" { + const content: any; + export default content; +} diff --git a/src/App.tsx b/src/App.tsx index 5381007..b00d887 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,9 +1,20 @@ -function App() { +import React from "react"; +import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; +import ChatRoom from "./components/ChatRoom"; +import ProfileDetail from "./components/ProfileDetail"; + +const App: React.FC = () => { return ( -
-

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

-
+ + + {/* 채팅방 */} + } /> + + {/* 프로필 상세 보기 */} + } /> + + ); -} +}; export default App; diff --git a/src/assets/Back.svg b/src/assets/Back.svg new file mode 100644 index 0000000..f91592a --- /dev/null +++ b/src/assets/Back.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/Profile image.svg b/src/assets/Profile image.svg new file mode 100644 index 0000000..d8b7c11 --- /dev/null +++ b/src/assets/Profile image.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/assets/call.svg b/src/assets/call.svg new file mode 100644 index 0000000..32fabac --- /dev/null +++ b/src/assets/call.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/cancel.svg b/src/assets/cancel.svg new file mode 100644 index 0000000..a80b9a9 --- /dev/null +++ b/src/assets/cancel.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/assets/plus.svg b/src/assets/plus.svg new file mode 100644 index 0000000..aed87c4 --- /dev/null +++ b/src/assets/plus.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/search.svg b/src/assets/search.svg new file mode 100644 index 0000000..5e4f8c3 --- /dev/null +++ b/src/assets/search.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/setting.svg b/src/assets/setting.svg new file mode 100644 index 0000000..854934c --- /dev/null +++ b/src/assets/setting.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/smile.svg b/src/assets/smile.svg new file mode 100644 index 0000000..78750a3 --- /dev/null +++ b/src/assets/smile.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/assets/thumb_up.svg b/src/assets/thumb_up.svg new file mode 100644 index 0000000..11ea228 --- /dev/null +++ b/src/assets/thumb_up.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/video.svg b/src/assets/video.svg new file mode 100644 index 0000000..96a987c --- /dev/null +++ b/src/assets/video.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/components/ChatRoom.tsx b/src/components/ChatRoom.tsx new file mode 100644 index 0000000..db4e747 --- /dev/null +++ b/src/components/ChatRoom.tsx @@ -0,0 +1,109 @@ +import React, { useState, useEffect, useRef } from "react"; +import Header from "./Header"; +import MessageList from "./MessageList"; +import MessageInput from "./MessageInput"; +import ProfileInfo from "./ProfileInfo"; +import { useNavigate } from "react-router-dom"; +import ceosProfilePic from "../assets/Profile image.svg"; +import userProfilePic from "../assets/Profile image.svg"; + +// 유저 타입 정의 +interface User { + id: number; + name: string; + profilePic: string; +} + +// 메시지 타입 정의 +interface Message { + id: number; + text: string; + sender: string; + receiver: string; + time: string; +} + +const ChatRoom: React.FC = () => { + const navigate = useNavigate(); + + // 유저 정보 관리 + const users: { ceos: User; ryu: User } = { + ceos: { + id: 1, + name: "CEOS", + profilePic: ceosProfilePic, + }, + ryu: { + id: 2, + name: "채린공주", + profilePic: userProfilePic, + }, + }; + + const [currentUser, setCurrentUser] = useState(users.ryu); + const [messages, setMessages] = useState([]); + const messagesEndRef = useRef(null); + + // 페이지 로딩 시 로컬 스토리지에서 메시지 불러오기 + useEffect(() => { + const storedMessages = localStorage.getItem("conversationMessages"); + if (storedMessages) { + setMessages(JSON.parse(storedMessages)); + } + }, []); + + // 새로운 메시지가 추가될 때마다 하단으로 스크롤 + useEffect(() => { + if (messagesEndRef.current) { + messagesEndRef.current.scrollIntoView({ behavior: "smooth" }); + } + }, [messages]); + + // 헤더의 프로필을 클릭할 때 사용자 전환 + const toggleUser = () => { + setCurrentUser( + currentUser.name === users.ryu.name ? users.ceos : users.ryu + ); + }; + + //프로필 보기 버튼 클릭 시 ProfileDetail로 이동 + const handleProfileDetail = () => { + navigate("/profile-detail", { state: { user: currentUser } }); + }; + + // 메시지 전송 핸들러 + const handleSendMessage = (newMessage: string) => { + const currentTime = new Date().toISOString(); // 현재 시간 + const newMessageData: Message = { + id: messages.length + 1, + text: newMessage, + sender: currentUser.name, + receiver: + currentUser.name === users.ryu.name ? users.ceos.name : users.ryu.name, + time: currentTime, + }; + + const updatedMessages = [...messages, newMessageData]; + setMessages(updatedMessages); + localStorage.setItem( + "conversationMessages", + JSON.stringify(updatedMessages) + ); + }; + + return ( +
+
+ +
+ + +
+
+ + +
+ ); +}; + +export default ChatRoom; diff --git a/src/components/Header.tsx b/src/components/Header.tsx new file mode 100644 index 0000000..6d50b8a --- /dev/null +++ b/src/components/Header.tsx @@ -0,0 +1,63 @@ +import React from "react"; +import backIcon from "../assets/Back.svg"; +import call from "../assets/call.svg"; +import video from "../assets/video.svg"; +import search from "../assets/search.svg"; + +// 유저 타입 정의 +interface User { + name: string; + profilePic: string; +} + +// Header 컴포넌트의 props 타입 정의 +interface HeaderProps { + user: User; + onProfileClick: () => void; +} + +const Header: React.FC = ({ user, onProfileClick }) => { + return ( +
+
+ {/* 뒤로가기 버튼 */} + + + {/* 프로필 이미지 */} + Profile + + {/* 사용자 이름 */} +
+

+ {user.name} +

+
+
+ + {/* 오른쪽에 아이콘들 */} +
+ + + +
+
+ ); +}; + +export default Header; diff --git a/src/components/MessageInput.tsx b/src/components/MessageInput.tsx new file mode 100644 index 0000000..d2a8573 --- /dev/null +++ b/src/components/MessageInput.tsx @@ -0,0 +1,56 @@ +import React, { useState } from "react"; +import plus from "../assets/plus.svg"; +import smile from "../assets/smile.svg"; +import thumb_up from "../assets/thumb_up.svg"; + +interface MessageInputProps { + onSendMessage: (message: string) => void; +} + +const MessageInput: React.FC = ({ onSendMessage }) => { + const [message, setMessage] = useState(""); + + const handleSend = (): void => { + if (message.trim() !== "") { + onSendMessage(message); + setMessage(""); + } + }; + + const handleKeyUp = (e: React.KeyboardEvent): void => { + if (e.key === "Enter") { + e.preventDefault(); + handleSend(); + } + }; + + return ( +
+ + ) => + setMessage(e.target.value) + } + onKeyUp={handleKeyUp} + className="flex-1 w-[255px] h-[34px] px-[7px] py-[16px] border focus:outline-none" + style={{ borderRadius: "60px", backgroundColor: "#F0F2F3" }} + placeholder="Aa" + > + smile Icon + +
+ ); +}; + +export default MessageInput; diff --git a/src/components/MessageList.tsx b/src/components/MessageList.tsx new file mode 100644 index 0000000..495d9bb --- /dev/null +++ b/src/components/MessageList.tsx @@ -0,0 +1,110 @@ +import React from "react"; +import ceosProfilePic from "../assets/Profile image.svg"; +import userProfilePic from "../assets/Profile image.svg"; +import styled from "styled-components"; + +interface Message { + id: string | number; + sender: string; + text: string; + time: string | number | Date; +} + +interface User { + name: string; +} + +interface MessageListProps { + messages: Message[]; + currentUser: User; +} + +const MessageList: React.FC = ({ messages, currentUser }) => { + const formatTime = (time: string | number | Date): string => { + const date = new Date(time); + return date.toLocaleTimeString([], { + hour: "2-digit", + minute: "2-digit", + }); + }; + + const getProfilePic = (sender: string): string => { + return sender === "CEOS" ? ceosProfilePic : userProfilePic; + }; + + return ( +
+ {messages.length === 0 + ? null + : messages.map((message) => ( + + {message.sender !== currentUser.name && ( + Profile + )} + + {message.sender === currentUser.name ? ( + <> + + {formatTime(message.time)} + + + {message.text} + + + ) : ( + <> + + {message.text} + + + {formatTime(message.time)} + + + )} + + + ))} +
+ ); +}; + +const MessageContainer = styled.div<{ isCurrentUser: boolean }>` + display: flex; + justify-content: ${(props) => + props.isCurrentUser ? "flex-end" : "flex-start"}; + margin-bottom: 16px; +`; + +const MessageContent = styled.div` + display: flex; + align-items: center; +`; + +const MessageText = styled.div<{ isCurrentUser: boolean }>` + background-color: ${(props) => (props.isCurrentUser ? "#0A7CFF" : "#F0F2F3")}; + color: ${(props) => (props.isCurrentUser ? "white" : "#333")}; + padding: 8px 16px; + border-radius: 20px; + font-family: Pretendard; + font-size: 15px; + font-weight: 400; + line-height: 20px; + margin-bottom: 4px; + display: inline-block; +`; + +const TimeText = styled.span<{ isCurrentUser: boolean }>` + font-size: 12px; + color: #999; + margin-left: ${(props) => (props.isCurrentUser ? "8px" : "0")}; + margin-right: ${(props) => (!props.isCurrentUser ? "8px" : "0")}; +`; + +export default MessageList; diff --git a/src/components/ProfileDetail.tsx b/src/components/ProfileDetail.tsx new file mode 100644 index 0000000..c20ad90 --- /dev/null +++ b/src/components/ProfileDetail.tsx @@ -0,0 +1,62 @@ +import React from "react"; +import { useNavigate, useLocation } from "react-router-dom"; +import cancel from "../assets/cancel.svg"; +import setting from "../assets/setting.svg"; + +interface User { + name: string; + profilePic: string; +} + +interface LocationState { + user: User; +} + +const ProfileDetail: React.FC = () => { + const navigate = useNavigate(); + const location = useLocation(); + const user = (location.state as LocationState)?.user; + + if (!user) { + return
유저 정보가 없습니다.
; + } + + return ( +
+ {/* 상단 바 */} +
+ + +
+ + {/* 프로필 이미지 */} + Profile + + {/* 사용자 이름 */} +

{user.name}

+ + {/* 소셜 아이콘 */} +
+ + + +
+
+ ); +}; + +export default ProfileDetail; diff --git a/src/components/ProfileInfo.tsx b/src/components/ProfileInfo.tsx new file mode 100644 index 0000000..8e70dbc --- /dev/null +++ b/src/components/ProfileInfo.tsx @@ -0,0 +1,47 @@ +import React from "react"; + +interface User { + name: string; + profilePic: string; +} + +interface ProfileInfoProps { + user: User | undefined; + onProfileDetail: () => void; +} + +const ProfileInfo: React.FC = ({ user, onProfileDetail }) => { + if (!user) { + return

사용자 정보가 없습니다.

; + } + + return ( +
+
+ {/* 프로필 이미지 */} + Profile + + {/* 사용자 이름 */} +

{user.name}

+ + {/* 추가 정보 */} +

Facebook 친구입니다

+

서울특별시 마포 거주

+ + {/* 프로필 보기 버튼 */} + +
+
+ ); +}; + +export default ProfileInfo; diff --git a/src/index.css b/src/index.css index ec2585e..b5c61c9 100644 --- a/src/index.css +++ b/src/index.css @@ -1,13 +1,3 @@ -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; -} +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 0000000..ecf62b5 --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,8 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], + theme: { + extend: {}, + }, + plugins: [], +}; From 43ad7e6f09222e168145b7f781c11a8f3526bc6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=A5=98=EC=9B=90?= Date: Fri, 27 Sep 2024 14:59:12 +0900 Subject: [PATCH 02/26] =?UTF-8?q?refactor:=20=ED=94=84=EB=A1=9C=ED=95=84?= =?UTF-8?q?=EB=B3=B4=EA=B8=B0=20=ED=81=B4=EB=A6=AD=EC=8B=9C=20=EC=B1=84?= =?UTF-8?q?=ED=8C=85=EC=B0=BD=20blur=EB=A1=9C=20=EB=B0=B0=EA=B2=BD=20?= =?UTF-8?q?=EB=B3=B4=EC=9D=B4=EA=B2=8C=20=EC=88=98=EC=A0=95,=20feat:=20?= =?UTF-8?q?=EC=9E=85=EB=A0=A5=20input=20=ED=81=B4=EB=A6=AD=EC=8B=9C=20thum?= =?UTF-8?q?b=5Fup=20=EC=97=90=EC=84=9C=20send=EC=9D=B4=EB=AA=A8=EC=A7=80?= =?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 --- src/App.tsx | 26 +++++++++++++---- src/assets/profile_facebook.svg | 19 +++++++++++++ src/assets/profile_insta.svg | 37 ++++++++++++++++++++++++ src/assets/profile_plus.svg | 19 +++++++++++++ src/assets/send.svg | 11 ++++++++ src/components/ChatRoom.css | 5 ++++ src/components/ChatRoom.tsx | 48 +++++++++++++++++++++----------- src/components/Header.tsx | 14 ++++------ src/components/MessageInput.tsx | 21 ++++++++++---- src/components/MessageList.tsx | 5 ++-- src/components/ProfileDetail.tsx | 28 ++++++------------- src/components/ProfileInfo.tsx | 25 +++++++++++------ src/index.css | 5 ++++ 13 files changed, 197 insertions(+), 66 deletions(-) create mode 100644 src/assets/profile_facebook.svg create mode 100644 src/assets/profile_insta.svg create mode 100644 src/assets/profile_plus.svg create mode 100644 src/assets/send.svg create mode 100644 src/components/ChatRoom.css diff --git a/src/App.tsx b/src/App.tsx index b00d887..601fb5f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,20 +1,36 @@ -import React from "react"; import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; import ChatRoom from "./components/ChatRoom"; import ProfileDetail from "./components/ProfileDetail"; +import { useState } from "react"; + +function App() { + // 상태로 관리할 유저 정보 + const [user, setUser] = useState({ + name: "채린공주", + profilePic: "/path/to/profile-pic.jpg", + }); + + // ProfileDetail에서 닫기 기능을 위한 핸들러 + const handleCloseProfileDetail = () => { + console.log("Profile detail closed"); + // 추가적인 동작이 필요할 경우 이곳에 작성 + }; -const App: React.FC = () => { return ( - {/* 채팅방 */} } /> {/* 프로필 상세 보기 */} - } /> + + } + /> ); -}; +} export default App; diff --git a/src/assets/profile_facebook.svg b/src/assets/profile_facebook.svg new file mode 100644 index 0000000..7752dde --- /dev/null +++ b/src/assets/profile_facebook.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/profile_insta.svg b/src/assets/profile_insta.svg new file mode 100644 index 0000000..5ea81cc --- /dev/null +++ b/src/assets/profile_insta.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/profile_plus.svg b/src/assets/profile_plus.svg new file mode 100644 index 0000000..fd5af28 --- /dev/null +++ b/src/assets/profile_plus.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/send.svg b/src/assets/send.svg new file mode 100644 index 0000000..73d2f19 --- /dev/null +++ b/src/assets/send.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/components/ChatRoom.css b/src/components/ChatRoom.css new file mode 100644 index 0000000..2301c8c --- /dev/null +++ b/src/components/ChatRoom.css @@ -0,0 +1,5 @@ +.fadeInSlideIn { + opacity: 1; + transform: translateY(0); + transition: opacity 0.5s ease-out, transform 0.5s ease-out; +} diff --git a/src/components/ChatRoom.tsx b/src/components/ChatRoom.tsx index db4e747..676f750 100644 --- a/src/components/ChatRoom.tsx +++ b/src/components/ChatRoom.tsx @@ -1,9 +1,9 @@ -import React, { useState, useEffect, useRef } from "react"; +import { useState, useEffect, useRef } from "react"; import Header from "./Header"; import MessageList from "./MessageList"; import MessageInput from "./MessageInput"; import ProfileInfo from "./ProfileInfo"; -import { useNavigate } from "react-router-dom"; +import ProfileDetail from "./ProfileDetail"; import ceosProfilePic from "../assets/Profile image.svg"; import userProfilePic from "../assets/Profile image.svg"; @@ -24,24 +24,23 @@ interface Message { } const ChatRoom: React.FC = () => { - const navigate = useNavigate(); - // 유저 정보 관리 - const users: { ceos: User; ryu: User } = { + const users: { ceos: User; chae: User } = { ceos: { id: 1, name: "CEOS", profilePic: ceosProfilePic, }, - ryu: { + chae: { id: 2, name: "채린공주", profilePic: userProfilePic, }, }; - const [currentUser, setCurrentUser] = useState(users.ryu); + const [currentUser, setCurrentUser] = useState(users.chae); const [messages, setMessages] = useState([]); + const [isProfileDetailOpen, setIsProfileDetailOpen] = useState(false); // 프로필 보기 버튼 클릭 상태 const messagesEndRef = useRef(null); // 페이지 로딩 시 로컬 스토리지에서 메시지 불러오기 @@ -62,24 +61,31 @@ const ChatRoom: React.FC = () => { // 헤더의 프로필을 클릭할 때 사용자 전환 const toggleUser = () => { setCurrentUser( - currentUser.name === users.ryu.name ? users.ceos : users.ryu + currentUser.name === users.chae.name ? users.ceos : users.chae ); }; - //프로필 보기 버튼 클릭 시 ProfileDetail로 이동 + // 프로필 보기 버튼 클릭 시 ProfileDetail 열기 const handleProfileDetail = () => { - navigate("/profile-detail", { state: { user: currentUser } }); + setIsProfileDetailOpen(true); + }; + + // ProfileDetail 닫기 + const closeProfileDetail = () => { + setIsProfileDetailOpen(false); }; - // 메시지 전송 핸들러 + // 메시지 전송 const handleSendMessage = (newMessage: string) => { - const currentTime = new Date().toISOString(); // 현재 시간 + const currentTime = new Date().toISOString(); const newMessageData: Message = { id: messages.length + 1, text: newMessage, sender: currentUser.name, receiver: - currentUser.name === users.ryu.name ? users.ceos.name : users.ryu.name, + currentUser.name === users.chae.name + ? users.ceos.name + : users.chae.name, time: currentTime, }; @@ -92,16 +98,26 @@ const ChatRoom: React.FC = () => { }; return ( -
+
-
+
+
- + {isProfileDetailOpen && ( +
+ +
+ )}
); }; diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 6d50b8a..186d6d5 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -1,16 +1,13 @@ -import React from "react"; import backIcon from "../assets/Back.svg"; import call from "../assets/call.svg"; import video from "../assets/video.svg"; import search from "../assets/search.svg"; -// 유저 타입 정의 interface User { name: string; profilePic: string; } -// Header 컴포넌트의 props 타입 정의 interface HeaderProps { user: User; onProfileClick: () => void; @@ -18,33 +15,32 @@ interface HeaderProps { const Header: React.FC = ({ user, onProfileClick }) => { return ( -
+
- {/* 뒤로가기 버튼 */} {/* 프로필 이미지 */} Profile {/* 사용자 이름 */}

{user.name}

- {/* 오른쪽에 아이콘들 */} + {/* 오른쪽 아이콘 */}
); diff --git a/src/components/MessageList.tsx b/src/components/MessageList.tsx index 495d9bb..2696cb7 100644 --- a/src/components/MessageList.tsx +++ b/src/components/MessageList.tsx @@ -1,4 +1,3 @@ -import React from "react"; import ceosProfilePic from "../assets/Profile image.svg"; import userProfilePic from "../assets/Profile image.svg"; import styled from "styled-components"; @@ -103,8 +102,8 @@ const MessageText = styled.div<{ isCurrentUser: boolean }>` const TimeText = styled.span<{ isCurrentUser: boolean }>` font-size: 12px; color: #999; - margin-left: ${(props) => (props.isCurrentUser ? "8px" : "0")}; - margin-right: ${(props) => (!props.isCurrentUser ? "8px" : "0")}; + margin-left: ${(props) => (props.isCurrentUser ? "0" : "4px")}; + margin-right: ${(props) => (props.isCurrentUser ? "4px" : "0")}; `; export default MessageList; diff --git a/src/components/ProfileDetail.tsx b/src/components/ProfileDetail.tsx index c20ad90..d24fdde 100644 --- a/src/components/ProfileDetail.tsx +++ b/src/components/ProfileDetail.tsx @@ -1,35 +1,25 @@ -import React from "react"; -import { useNavigate, useLocation } from "react-router-dom"; import cancel from "../assets/cancel.svg"; -import setting from "../assets/setting.svg"; +import settings from "../assets/setting.svg"; interface User { name: string; profilePic: string; } -interface LocationState { +interface ProfileDetailProps { user: User; + onClose: () => void; } -const ProfileDetail: React.FC = () => { - const navigate = useNavigate(); - const location = useLocation(); - const user = (location.state as LocationState)?.user; - - if (!user) { - return
유저 정보가 없습니다.
; - } - +const ProfileDetail: React.FC = ({ user, onClose }) => { return ( -
- {/* 상단 바 */} -
-
diff --git a/src/components/ProfileInfo.tsx b/src/components/ProfileInfo.tsx index 8e70dbc..8637aab 100644 --- a/src/components/ProfileInfo.tsx +++ b/src/components/ProfileInfo.tsx @@ -1,5 +1,3 @@ -import React from "react"; - interface User { name: string; profilePic: string; @@ -16,25 +14,36 @@ const ProfileInfo: React.FC = ({ user, onProfileDetail }) => { } return ( -
+
{/* 프로필 이미지 */} Profile {/* 사용자 이름 */} -

{user.name}

+

+ {user.name} +

{/* 추가 정보 */} -

Facebook 친구입니다

-

서울특별시 마포 거주

+

+ Facebook 친구입니다 +

+

+ 서울특별시 마포 거주 +

{/* 프로필 보기 버튼 */}
{/* 프로필 이미지 */} - Profile +
+ +
{/* 사용자 이름 */} -

{user.name}

+

{user.name}

{/* 소셜 아이콘 */} -
- - -
From 9e051030b2a814fb6f3ed77a6b37370672cf074f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=A5=98=EC=9B=90?= Date: Fri, 27 Sep 2024 15:44:23 +0900 Subject: [PATCH 04/26] =?UTF-8?q?feat:=20profil=20detail=20sns=20=EC=97=B0?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 2 + src/components/ChatRoom.tsx | 6 +++ src/components/MessageInput.tsx | 77 ++++++++++++++++++++++++++++++-- src/components/MessageList.tsx | 15 ++++++- src/components/ProfileDetail.tsx | 19 ++++++-- 5 files changed, 112 insertions(+), 7 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 601fb5f..345b6f5 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -8,6 +8,8 @@ function App() { const [user, setUser] = useState({ name: "채린공주", profilePic: "/path/to/profile-pic.jpg", + facebook: "https://www.facebook.com/", + instagram: "https://www.facebook.com/", }); // ProfileDetail에서 닫기 기능을 위한 핸들러 diff --git a/src/components/ChatRoom.tsx b/src/components/ChatRoom.tsx index 676f750..92f607a 100644 --- a/src/components/ChatRoom.tsx +++ b/src/components/ChatRoom.tsx @@ -12,6 +12,8 @@ interface User { id: number; name: string; profilePic: string; + facebook: string; + instagram: string; } // 메시지 타입 정의 @@ -30,11 +32,15 @@ const ChatRoom: React.FC = () => { id: 1, name: "CEOS", profilePic: ceosProfilePic, + facebook: "https://www.facebook.com/clubceos/", + instagram: "https://www.instagram.com/ceos.sinchon/", }, chae: { id: 2, name: "채린공주", profilePic: userProfilePic, + facebook: "https://www.facebook.com/", + instagram: "https://www.instagram.com/", }, }; diff --git a/src/components/MessageInput.tsx b/src/components/MessageInput.tsx index 56eec39..b43299d 100644 --- a/src/components/MessageInput.tsx +++ b/src/components/MessageInput.tsx @@ -1,4 +1,5 @@ -import { useState } from "react"; +import React, { useState, useEffect } from "react"; +import styled, { keyframes } from "styled-components"; import plus from "../assets/plus.svg"; import smile from "../assets/smile.svg"; import thumb_up from "../assets/thumb_up.svg"; @@ -8,9 +9,69 @@ interface MessageInputProps { onSendMessage: (message: string) => void; } +interface ThumbsUpProps { + x: number; + y: number; +} + +const floatUp = keyframes` + 0% { + transform: translateY(0) scale(1); + opacity: 1; + } + 100% { + transform: translateY(-100px) scale(1.5); + opacity: 0; + } +`; + +const ThumbsUpEmoji = styled.div` + position: absolute; + left: ${(props) => props.x}px; + bottom: ${(props) => props.y}px; + font-size: 24px; + animation: ${floatUp} 1s ease-out forwards; +`; + +const ThumbsUpAnimation: React.FC = () => { + const [thumbsUp, setThumbsUp] = useState([]); + + useEffect(() => { + const interval = setInterval(() => { + setThumbsUp((prev) => prev.slice(1)); + }, 100); + + return () => clearInterval(interval); + }, []); + + const createThumbsUp = () => { + const newThumbsUp = Array(5) + .fill(null) + .map(() => ({ + x: Math.random() * 300, + y: Math.random() * 50 + 50, + })); + setThumbsUp((prev) => [...prev, ...newThumbsUp]); + }; + + return ( + <> + {thumbsUp.map((thumb, index) => ( + + 👍 + + ))} + + + ); +}; + const MessageInput: React.FC = ({ onSendMessage }) => { const [message, setMessage] = useState(""); const [isInputClicked, setIsInputClicked] = useState(false); + const [showAnimation, setShowAnimation] = useState(false); const handleSend = (): void => { if (message.trim() !== "") { @@ -27,6 +88,12 @@ const MessageInput: React.FC = ({ onSendMessage }) => { } }; + const handleThumbsUp = (): void => { + onSendMessage("👍"); + setShowAnimation(true); + setTimeout(() => setShowAnimation(false), 1000); + }; + return (
+ {showAnimation && }
); }; diff --git a/src/components/MessageList.tsx b/src/components/MessageList.tsx index 2696cb7..51ed8f2 100644 --- a/src/components/MessageList.tsx +++ b/src/components/MessageList.tsx @@ -1,6 +1,6 @@ import ceosProfilePic from "../assets/Profile image.svg"; import userProfilePic from "../assets/Profile image.svg"; -import styled from "styled-components"; +import styled, { keyframes } from "styled-components"; interface Message { id: string | number; @@ -18,6 +18,18 @@ interface MessageListProps { currentUser: User; } +// 애니메이션 정의 +const fadeIn = keyframes` + from { + opacity: 0; + transform: scale(0.9); + } + to { + opacity: 1; + transform: scale(1); + } +`; + const MessageList: React.FC = ({ messages, currentUser }) => { const formatTime = (time: string | number | Date): string => { const date = new Date(time); @@ -79,6 +91,7 @@ const MessageContainer = styled.div<{ isCurrentUser: boolean }>` justify-content: ${(props) => props.isCurrentUser ? "flex-end" : "flex-start"}; margin-bottom: 16px; + animation: ${fadeIn} 0.3s ease-in-out; // 애니메이션 추가 `; const MessageContent = styled.div` diff --git a/src/components/ProfileDetail.tsx b/src/components/ProfileDetail.tsx index e08f212..659ebe5 100644 --- a/src/components/ProfileDetail.tsx +++ b/src/components/ProfileDetail.tsx @@ -8,6 +8,8 @@ import edit from "../assets/edit.svg"; interface User { name: string; profilePic: string; + facebook: string; + instagram: string; } interface ProfileDetailProps { @@ -50,13 +52,24 @@ const ProfileDetail: React.FC = ({ user, onClose }) => { {/* 소셜 아이콘 */}
+
From 78369ec66d23ec53b70a3efe609f7c4ef86359eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=A5=98=EC=9B=90?= Date: Fri, 27 Sep 2024 16:15:32 +0900 Subject: [PATCH 05/26] =?UTF-8?q?feat:=20=EB=B0=98=EC=9D=91=ED=98=95?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.css | 38 --------------------------------- src/components/ChatRoom.tsx | 4 ++-- src/components/Header.tsx | 2 +- src/components/MessageInput.tsx | 2 +- 4 files changed, 4 insertions(+), 42 deletions(-) diff --git a/src/App.css b/src/App.css index 74b5e05..e69de29 100644 --- a/src/App.css +++ b/src/App.css @@ -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/components/ChatRoom.tsx b/src/components/ChatRoom.tsx index 92f607a..0afb42c 100644 --- a/src/components/ChatRoom.tsx +++ b/src/components/ChatRoom.tsx @@ -104,7 +104,7 @@ const ChatRoom: React.FC = () => { }; return ( -
+
{
-
+ {isProfileDetailOpen && (
diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 186d6d5..dbe4580 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -15,7 +15,7 @@ interface HeaderProps { const Header: React.FC = ({ user, onProfileClick }) => { return ( -
+
From 8290467bad31302fbfb2c8776810572b057c1edb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=A5=98=EC=9B=90?= Date: Fri, 27 Sep 2024 19:01:46 +0900 Subject: [PATCH 06/26] =?UTF-8?q?chore:=20profileDetail=EC=97=90=EC=84=9C?= =?UTF-8?q?=20back=20blur=20=EC=83=81=EB=8B=A8=20=ED=95=98=EB=8B=A8=20bar?= =?UTF-8?q?=EA=B9=8C=EC=A7=80,time=20=20=ED=85=8D=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=ED=95=98=EB=8B=A8=20=EC=A0=95=EB=A0=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ChatRoom.tsx | 13 ++++++++++--- src/components/Header.tsx | 12 ++++++++++-- src/components/MessageInput.tsx | 11 +++++++++-- src/components/MessageList.tsx | 1 + src/components/ProfileDetail.tsx | 2 +- 5 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/components/ChatRoom.tsx b/src/components/ChatRoom.tsx index 0afb42c..d040ad6 100644 --- a/src/components/ChatRoom.tsx +++ b/src/components/ChatRoom.tsx @@ -105,19 +105,26 @@ const ChatRoom: React.FC = () => { return (
-
+
- + {isProfileDetailOpen && (
diff --git a/src/components/Header.tsx b/src/components/Header.tsx index dbe4580..6ad4cb5 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -11,11 +11,19 @@ interface User { interface HeaderProps { user: User; onProfileClick: () => void; + isProfileDetailOpen: boolean; } -const Header: React.FC = ({ user, onProfileClick }) => { +const Header: React.FC = ({ + user, + onProfileClick, + isProfileDetailOpen, +}) => { return ( -
+
diff --git a/src/components/MessageList.tsx b/src/components/MessageList.tsx index 51ed8f2..db5adfa 100644 --- a/src/components/MessageList.tsx +++ b/src/components/MessageList.tsx @@ -117,6 +117,7 @@ const TimeText = styled.span<{ isCurrentUser: boolean }>` color: #999; margin-left: ${(props) => (props.isCurrentUser ? "0" : "4px")}; margin-right: ${(props) => (props.isCurrentUser ? "4px" : "0")}; + align-self: flex-end; `; export default MessageList; diff --git a/src/components/ProfileDetail.tsx b/src/components/ProfileDetail.tsx index 659ebe5..247b427 100644 --- a/src/components/ProfileDetail.tsx +++ b/src/components/ProfileDetail.tsx @@ -20,7 +20,7 @@ interface ProfileDetailProps { const ProfileDetail: React.FC = ({ user, onClose }) => { return (
-
+
From 8ebfd4bac94b033425c80a9248962c67cae21cb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=A5=98=EC=9B=90?= Date: Fri, 27 Sep 2024 19:54:01 +0900 Subject: [PATCH 07/26] =?UTF-8?q?feat:=20=EB=A9=94=EC=84=B8=EC=A7=80=20?= =?UTF-8?q?=EC=95=A0=EB=8B=88=EB=A9=94=EC=9D=B4=EC=85=98=20=EC=88=98?= =?UTF-8?q?=EC=A0=95(=EC=83=81=EB=8C=80=EB=B0=A9=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=A0=84=ED=99=98=EC=8B=9C=EC=97=90=EB=8F=84=20=EC=95=A0?= =?UTF-8?q?=EB=8B=88=EB=A9=94=EC=9D=B4=EC=85=98=20=ED=9A=A8=EA=B3=BC)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 32 ++++++++++++++++ package.json | 2 + src/components/ChatRoom.css | 5 --- src/components/ChatRoom.tsx | 9 +---- src/components/MessageList.tsx | 64 +++++++++++++++++++++++++++----- src/components/ProfileDetail.tsx | 8 ++-- src/components/ProfileInfo.tsx | 2 +- 7 files changed, 95 insertions(+), 27 deletions(-) delete mode 100644 src/components/ChatRoom.css diff --git a/package-lock.json b/package-lock.json index 5420705..7493350 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "@types/react-dom": "^18.2.22", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-icons": "^5.3.0", "react-router-dom": "^6.26.2", "react-scripts": "5.0.1", "styled-components": "^6.1.13", @@ -24,6 +25,7 @@ "web-vitals": "^2.1.4" }, "devDependencies": { + "@types/react-icons": "^2.2.7", "@types/styled-components": "^5.1.34", "tailwindcss": "^3.4.13" } @@ -4274,6 +4276,27 @@ "@types/react": "*" } }, + "node_modules/@types/react-icon-base": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@types/react-icon-base/-/react-icon-base-2.1.6.tgz", + "integrity": "sha512-ebbN1JjCm6RxBd3HdI1+8VCdiOI4qMjnl9DIHWJFrB/eYLF4mzIgdL34PIqCJBLY3vlwil9v6IHQvzsa8vgMsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/react-icons": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@types/react-icons/-/react-icons-2.2.7.tgz", + "integrity": "sha512-qxc8xtwgDG5Ub/WILU9tZa7zxz2UZqOU4yXbBa+Xg+0LbP031NB9gvf1d/ALvHLGCsCf3WEVttNoW/wc30jn1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/react": "*", + "@types/react-icon-base": "*" + } + }, "node_modules/@types/resolve": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", @@ -14963,6 +14986,15 @@ "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" }, + "node_modules/react-icons": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.3.0.tgz", + "integrity": "sha512-DnUk8aFbTyQPSkCfF8dbX6kQjXA9DktMeJqfjrg6cK9vwQVMxmcA3BfP4QoiztVmEHtwlTgLFsPuH2NskKT6eg==", + "license": "MIT", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", diff --git a/package.json b/package.json index 72704da..6e1ca0d 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "@types/react-dom": "^18.2.22", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-icons": "^5.3.0", "react-router-dom": "^6.26.2", "react-scripts": "5.0.1", "styled-components": "^6.1.13", @@ -43,6 +44,7 @@ ] }, "devDependencies": { + "@types/react-icons": "^2.2.7", "@types/styled-components": "^5.1.34", "tailwindcss": "^3.4.13" } diff --git a/src/components/ChatRoom.css b/src/components/ChatRoom.css deleted file mode 100644 index 2301c8c..0000000 --- a/src/components/ChatRoom.css +++ /dev/null @@ -1,5 +0,0 @@ -.fadeInSlideIn { - opacity: 1; - transform: translateY(0); - transition: opacity 0.5s ease-out, transform 0.5s ease-out; -} diff --git a/src/components/ChatRoom.tsx b/src/components/ChatRoom.tsx index d040ad6..200b217 100644 --- a/src/components/ChatRoom.tsx +++ b/src/components/ChatRoom.tsx @@ -26,7 +26,6 @@ interface Message { } const ChatRoom: React.FC = () => { - // 유저 정보 관리 const users: { ceos: User; chae: User } = { ceos: { id: 1, @@ -46,10 +45,9 @@ const ChatRoom: React.FC = () => { const [currentUser, setCurrentUser] = useState(users.chae); const [messages, setMessages] = useState([]); - const [isProfileDetailOpen, setIsProfileDetailOpen] = useState(false); // 프로필 보기 버튼 클릭 상태 + const [isProfileDetailOpen, setIsProfileDetailOpen] = useState(false); const messagesEndRef = useRef(null); - // 페이지 로딩 시 로컬 스토리지에서 메시지 불러오기 useEffect(() => { const storedMessages = localStorage.getItem("conversationMessages"); if (storedMessages) { @@ -57,31 +55,26 @@ const ChatRoom: React.FC = () => { } }, []); - // 새로운 메시지가 추가될 때마다 하단으로 스크롤 useEffect(() => { if (messagesEndRef.current) { messagesEndRef.current.scrollIntoView({ behavior: "smooth" }); } }, [messages]); - // 헤더의 프로필을 클릭할 때 사용자 전환 const toggleUser = () => { setCurrentUser( currentUser.name === users.chae.name ? users.ceos : users.chae ); }; - // 프로필 보기 버튼 클릭 시 ProfileDetail 열기 const handleProfileDetail = () => { setIsProfileDetailOpen(true); }; - // ProfileDetail 닫기 const closeProfileDetail = () => { setIsProfileDetailOpen(false); }; - // 메시지 전송 const handleSendMessage = (newMessage: string) => { const currentTime = new Date().toISOString(); const newMessageData: Message = { diff --git a/src/components/MessageList.tsx b/src/components/MessageList.tsx index db5adfa..839edeb 100644 --- a/src/components/MessageList.tsx +++ b/src/components/MessageList.tsx @@ -1,6 +1,7 @@ import ceosProfilePic from "../assets/Profile image.svg"; import userProfilePic from "../assets/Profile image.svg"; import styled, { keyframes } from "styled-components"; +import React, { useState, useEffect } from "react"; interface Message { id: string | number; @@ -22,7 +23,7 @@ interface MessageListProps { const fadeIn = keyframes` from { opacity: 0; - transform: scale(0.9); + transform: scale(0.8); } to { opacity: 1; @@ -31,6 +32,13 @@ const fadeIn = keyframes` `; const MessageList: React.FC = ({ messages, currentUser }) => { + const [currentSender, setCurrentSender] = useState(currentUser.name); + + useEffect(() => { + // 상대방 전환 시 애니메이션이 재실행되도록 설정 + setCurrentSender(currentUser.name); + }, [currentUser.name]); + const formatTime = (time: string | number | Date): string => { const date = new Date(time); return date.toLocaleTimeString([], { @@ -39,17 +47,45 @@ const MessageList: React.FC = ({ messages, currentUser }) => { }); }; + const formatDate = (time: string | number | Date): string => { + const date = new Date(time); + return `${date.getFullYear()}년 ${ + date.getMonth() + 1 + }월 ${date.getDate()}일`; + }; + + const formatDateTime = (time: string | number | Date): string => { + const date = new Date(time); + return `${date.getFullYear()}년 ${ + date.getMonth() + 1 + }월 ${date.getDate()}일 ${date.toLocaleTimeString([], { + hour: "2-digit", + minute: "2-digit", + })}`; + }; + const getProfilePic = (sender: string): string => { return sender === "CEOS" ? ceosProfilePic : userProfilePic; }; + let previousDate: string | null = null; + return ( -
- {messages.length === 0 - ? null - : messages.map((message) => ( +
+ {messages.map((message, index) => { + const messageDate = formatDate(message.time); + const showDate = messageDate !== previousDate; + previousDate = messageDate; + + return ( +
+ {showDate && ( + + {formatDateTime(message.time)} + + )} {message.sender !== currentUser.name && ( @@ -59,6 +95,7 @@ const MessageList: React.FC = ({ messages, currentUser }) => { className="w-8 h-8 rounded-full mr-2" /> )} + {message.sender === currentUser.name ? ( <> @@ -81,17 +118,26 @@ const MessageList: React.FC = ({ messages, currentUser }) => { )} - ))} +
+ ); + })}
); }; +const DateTimeText = styled.div<{ isCurrentUser: boolean }>` + text-align: center; + font-size: 12px; + color: #737373; + margin: 20px 0; +`; + const MessageContainer = styled.div<{ isCurrentUser: boolean }>` display: flex; justify-content: ${(props) => props.isCurrentUser ? "flex-end" : "flex-start"}; margin-bottom: 16px; - animation: ${fadeIn} 0.3s ease-in-out; // 애니메이션 추가 + animation: ${fadeIn} 0.3s ease-in-out; `; const MessageContent = styled.div` @@ -114,7 +160,7 @@ const MessageText = styled.div<{ isCurrentUser: boolean }>` const TimeText = styled.span<{ isCurrentUser: boolean }>` font-size: 12px; - color: #999; + color: #b4b8bc; margin-left: ${(props) => (props.isCurrentUser ? "0" : "4px")}; margin-right: ${(props) => (props.isCurrentUser ? "4px" : "0")}; align-self: flex-end; diff --git a/src/components/ProfileDetail.tsx b/src/components/ProfileDetail.tsx index 247b427..5850fea 100644 --- a/src/components/ProfileDetail.tsx +++ b/src/components/ProfileDetail.tsx @@ -50,19 +50,19 @@ const ProfileDetail: React.FC = ({ user, onClose }) => {

{user.name}

{/* 소셜 아이콘 */} -
- - diff --git a/src/components/ProfileInfo.tsx b/src/components/ProfileInfo.tsx index 8637aab..6113e4b 100644 --- a/src/components/ProfileInfo.tsx +++ b/src/components/ProfileInfo.tsx @@ -14,7 +14,7 @@ const ProfileInfo: React.FC = ({ user, onProfileDetail }) => { } return ( -
+
{/* 프로필 이미지 */} Date: Fri, 27 Sep 2024 20:06:45 +0900 Subject: [PATCH 08/26] =?UTF-8?q?feat:=20pretendard=20font=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/index.html | 5 +++++ src/App.css | 3 +++ src/components/MessageList.tsx | 2 +- src/components/ProfileDetail.tsx | 6 +++--- src/index.css | 6 ++---- 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/public/index.html b/public/index.html index aa069f2..64044dc 100644 --- a/public/index.html +++ b/public/index.html @@ -24,6 +24,11 @@ work correctly both with client-side routing and a non-root public URL. Learn how to configure a non-root public URL by running `npm run build`. --> + + React App diff --git a/src/App.css b/src/App.css index e69de29..8c2418e 100644 --- a/src/App.css +++ b/src/App.css @@ -0,0 +1,3 @@ +body { + font-family: "Pretendard"; +} diff --git a/src/components/MessageList.tsx b/src/components/MessageList.tsx index 839edeb..39085ff 100644 --- a/src/components/MessageList.tsx +++ b/src/components/MessageList.tsx @@ -85,7 +85,7 @@ const MessageList: React.FC = ({ messages, currentUser }) => { )} {message.sender !== currentUser.name && ( diff --git a/src/components/ProfileDetail.tsx b/src/components/ProfileDetail.tsx index 5850fea..6b41228 100644 --- a/src/components/ProfileDetail.tsx +++ b/src/components/ProfileDetail.tsx @@ -51,13 +51,13 @@ const ProfileDetail: React.FC = ({ user, onClose }) => { {/* 소셜 아이콘 */}
- - -
- {/* 프로필 이미지 */}
- {/* 사용자 이름 */}

{user.name}

- {/* 소셜 아이콘 */}
+ + +
+
+
+ ); +}; + +export default BottomNav; diff --git a/src/index.tsx b/src/index.tsx index d10be77..2b16b42 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,13 +1,16 @@ -import React from 'react'; -import ReactDOM from 'react-dom/client'; -import './index.css'; -import App from './App'; +import React from "react"; +import ReactDOM from "react-dom/client"; +import "./index.css"; +import App from "./App"; +import { BrowserRouter } from "react-router-dom"; const root = ReactDOM.createRoot( - document.getElementById('root') as HTMLElement + document.getElementById("root") as HTMLElement ); root.render( - - - + + + + + ); diff --git a/src/components/ChatRoom.tsx b/src/pages/ChatRoom.tsx similarity index 95% rename from src/components/ChatRoom.tsx rename to src/pages/ChatRoom.tsx index aefcf31..23dd9de 100644 --- a/src/components/ChatRoom.tsx +++ b/src/pages/ChatRoom.tsx @@ -1,8 +1,8 @@ import React, { useState, useEffect, useRef } from "react"; -import Header from "./Header"; -import MessageList from "./MessageList"; -import MessageInput from "./MessageInput"; -import ProfileInfo from "./ProfileInfo"; +import Header from "../components/Header"; +import MessageList from "../components/MessageList"; +import MessageInput from "../components/MessageInput"; +import ProfileInfo from "../components/ProfileInfo"; import ProfileDetail from "./ProfileDetail"; import ceosProfilePic from "../assets/Profile image.svg"; import userProfilePic from "../assets/Profile image.svg"; diff --git a/src/pages/FriendsList.tsx b/src/pages/FriendsList.tsx new file mode 100644 index 0000000..d200a51 --- /dev/null +++ b/src/pages/FriendsList.tsx @@ -0,0 +1,5 @@ +const FriendsList = () => { + return
list
; +}; + +export default FriendsList; diff --git a/src/components/ProfileDetail.tsx b/src/pages/ProfileDetail.tsx similarity index 100% rename from src/components/ProfileDetail.tsx rename to src/pages/ProfileDetail.tsx From 1dcbaf4d20d1ff74dc4cf52896dbfb4b2637ce03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=A5=98=EC=9B=90?= <2145007@ewha.ac.kr> Date: Thu, 31 Oct 2024 13:14:40 +0900 Subject: [PATCH 12/26] =?UTF-8?q?feat=20:=20=EC=B9=9C=EA=B5=AC=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=EB=9D=BC=EC=9A=B0=ED=8C=85,=20bottomnav=20ui?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 9 ++++++--- src/components/BottomNav.tsx | 6 ++++-- src/components/fakedata/messagesData.json | 0 src/components/fakedata/users.json | 16 ++++++++++++++++ src/index.tsx | 4 +--- src/pages/ChatList.tsx | 5 +++++ src/pages/ChatRoom.tsx | 12 ++++++------ src/pages/FriendsList.tsx | 5 ----- src/pages/Home.tsx | 5 +++++ 9 files changed, 43 insertions(+), 19 deletions(-) create mode 100644 src/components/fakedata/messagesData.json create mode 100644 src/components/fakedata/users.json create mode 100644 src/pages/ChatList.tsx delete mode 100644 src/pages/FriendsList.tsx create mode 100644 src/pages/Home.tsx diff --git a/src/App.tsx b/src/App.tsx index 36345f7..06d7d43 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -8,14 +8,16 @@ import ChatRoom from "./pages/ChatRoom"; import ProfileDetail from "./pages/ProfileDetail"; import { useState } from "react"; import BottomNav from "./components/BottomNav"; -import FriendsList from "./pages/FriendsList"; + +import Home from "./pages/Home"; +import ChatList from "./pages/ChatList"; function App() { const chatRoomMatch = useMatch("/userID"); // 상태로 관리할 유저 정보 const [user, setUser] = useState({ - name: "채린공주", + name: "김류원", profilePic: "/path/to/profile-pic.jpg", facebook: "https://www.facebook.com/", instagram: "https://www.facebook.com/", @@ -30,8 +32,9 @@ function App() { return ( <> - } /> + } /> } /> + } /> {/* 프로필 상세 보기 */} { const [activeTab, setActiveTab] = useState("home"); + const nav = useNavigate(); return (
@@ -10,7 +12,7 @@ const BottomNav = () => { className={`flex flex-col items-center ${ activeTab === "home" ? "text-blue-500" : "text-gray-500" }`} - onClick={() => setActiveTab("home")} + onClick={() => nav("/")} > @@ -19,7 +21,7 @@ const BottomNav = () => { className={`flex flex-col items-center ${ activeTab === "chat" ? "text-blue-500" : "text-gray-500" }`} - onClick={() => setActiveTab("chat")} + onClick={() => nav("/chat-list")} > 채팅 diff --git a/src/components/fakedata/messagesData.json b/src/components/fakedata/messagesData.json new file mode 100644 index 0000000..e69de29 diff --git a/src/components/fakedata/users.json b/src/components/fakedata/users.json new file mode 100644 index 0000000..e4738dd --- /dev/null +++ b/src/components/fakedata/users.json @@ -0,0 +1,16 @@ +[ + { + "id": 1, + "name": "CEOS", + "profilePic": "../assets/Profile image.svg", + "facebook": "https://www.facebook.com/clubceos/", + "instagram": "https://www.instagram.com/ceos.sinchon/" + }, + { + "id": 2, + "name": "김류원", + "profilePic": "../assets/Profile image.svg", + "facebook": "https://www.facebook.com/", + "instagram": "https://www.instagram.com/" + } +] diff --git a/src/index.tsx b/src/index.tsx index 2b16b42..16a2d58 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -9,8 +9,6 @@ const root = ReactDOM.createRoot( ); root.render( - - - + ); diff --git a/src/pages/ChatList.tsx b/src/pages/ChatList.tsx new file mode 100644 index 0000000..29e91e4 --- /dev/null +++ b/src/pages/ChatList.tsx @@ -0,0 +1,5 @@ +const ChatList = () => { + return
chatlist
; +}; + +export default ChatList; diff --git a/src/pages/ChatRoom.tsx b/src/pages/ChatRoom.tsx index 23dd9de..c97e4cf 100644 --- a/src/pages/ChatRoom.tsx +++ b/src/pages/ChatRoom.tsx @@ -1,9 +1,9 @@ import React, { useState, useEffect, useRef } from "react"; -import Header from "../components/Header"; import MessageList from "../components/MessageList"; import MessageInput from "../components/MessageInput"; import ProfileInfo from "../components/ProfileInfo"; -import ProfileDetail from "./ProfileDetail"; +import Header from "../components/Header"; +import ProfileDetail from "../pages/ProfileDetail"; import ceosProfilePic from "../assets/Profile image.svg"; import userProfilePic from "../assets/Profile image.svg"; @@ -34,7 +34,7 @@ const ChatRoom: React.FC = () => { }, chae: { id: 2, - name: "채린공주", + name: "김류원", profilePic: userProfilePic, facebook: "https://www.facebook.com/", instagram: "https://www.instagram.com/", @@ -51,13 +51,13 @@ const ChatRoom: React.FC = () => { id: 1, text: "머하밍??", sender: "CEOS", - receiver: "채린공주", + receiver: "김류원", time: new Date().toISOString(), }, { id: 2, text: "과제중🤮🤮", - sender: "채린공주", + sender: "김류원", receiver: "CEOS", time: new Date().toISOString(), }, @@ -65,7 +65,7 @@ const ChatRoom: React.FC = () => { id: 3, text: "🤦‍♀️🤦‍♀️", sender: "CEOS", - receiver: "채린공주", + receiver: "김류원", time: new Date().toISOString(), }, ]; diff --git a/src/pages/FriendsList.tsx b/src/pages/FriendsList.tsx deleted file mode 100644 index d200a51..0000000 --- a/src/pages/FriendsList.tsx +++ /dev/null @@ -1,5 +0,0 @@ -const FriendsList = () => { - return
list
; -}; - -export default FriendsList; diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx new file mode 100644 index 0000000..e0c469a --- /dev/null +++ b/src/pages/Home.tsx @@ -0,0 +1,5 @@ +const Home = () => { + return
home
; +}; + +export default Home; From 114b0f0cbbbe95332edad5d3f8684d6a7dbdfb03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=A5=98=EC=9B=90?= <2145007@ewha.ac.kr> Date: Thu, 31 Oct 2024 15:05:29 +0900 Subject: [PATCH 13/26] =?UTF-8?q?feat=20:=20home=20=EC=B9=9C=EA=B5=AC?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 160 ++++++++++++++++++ package.json | 3 + .../fakedata/messagesData.json | 0 public/fakedata/users.json | 50 ++++++ src/App.tsx | 3 + src/api.tsx | 3 + src/assets/person_plus.svg | 4 + src/components/ChatRoomHeader.tsx | 67 ++++++++ src/components/FriendsList.tsx | 23 +++ src/components/Header.tsx | 44 +---- src/components/fakedata/users.json | 16 -- src/index.tsx | 12 +- src/pages/ChatRoom.tsx | 5 +- src/pages/Home.tsx | 51 +++++- 14 files changed, 378 insertions(+), 63 deletions(-) rename {src/components => public}/fakedata/messagesData.json (100%) create mode 100644 public/fakedata/users.json create mode 100644 src/api.tsx create mode 100644 src/assets/person_plus.svg create mode 100644 src/components/ChatRoomHeader.tsx create mode 100644 src/components/FriendsList.tsx delete mode 100644 src/components/fakedata/users.json diff --git a/package-lock.json b/package-lock.json index d0c794d..0ab094e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,8 @@ "version": "0.1.0", "dependencies": { "@heroicons/react": "^2.1.5", + "@tanstack/react-query": "^5.59.16", + "@tanstack/react-query-devtools": "^5.59.16", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", @@ -19,6 +21,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-icons": "^5.3.0", + "react-query": "^3.39.3", "react-router-dom": "^6.26.2", "react-scripts": "5.0.1", "styled-components": "^6.1.13", @@ -3704,6 +3707,59 @@ "url": "https://github.com/sponsors/gregberge" } }, + "node_modules/@tanstack/query-core": { + "version": "5.59.16", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.59.16.tgz", + "integrity": "sha512-crHn+G3ltqb5JG0oUv6q+PMz1m1YkjpASrXTU+sYWW9pLk0t2GybUHNRqYPZWhxgjPaVGC4yp92gSFEJgYEsPw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/query-devtools": { + "version": "5.58.0", + "resolved": "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.58.0.tgz", + "integrity": "sha512-iFdQEFXaYYxqgrv63ots+65FGI+tNp5ZS5PdMU1DWisxk3fez5HG3FyVlbUva+RdYS5hSLbxZ9aw3yEs97GNTw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.59.16", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.59.16.tgz", + "integrity": "sha512-MuyWheG47h6ERd4PKQ6V8gDyBu3ThNG22e1fRVwvq6ap3EqsFhyuxCAwhNP/03m/mLg+DAb0upgbPaX6VB+CkQ==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.59.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, + "node_modules/@tanstack/react-query-devtools": { + "version": "5.59.16", + "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.59.16.tgz", + "integrity": "sha512-Dejo39QBXmDqXZ3vdrk7vHDvs7TvL573/AX2NveMBmRAufAPYuE3oWSKP/gGqkDfEqyr4CmldOj+v9cKskUchQ==", + "license": "MIT", + "dependencies": { + "@tanstack/query-devtools": "5.58.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "@tanstack/react-query": "^5.59.16", + "react": "^18 || ^19" + } + }, "node_modules/@testing-library/dom": { "version": "9.3.4", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.4.tgz", @@ -5613,6 +5669,15 @@ "node": ">= 8.0.0" } }, + "node_modules/big-integer": { + "version": "1.6.52", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", + "license": "Unlicense", + "engines": { + "node": ">=0.6" + } + }, "node_modules/big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -5726,6 +5791,22 @@ "node": ">=8" } }, + "node_modules/broadcast-channel": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/broadcast-channel/-/broadcast-channel-3.7.0.tgz", + "integrity": "sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.7.2", + "detect-node": "^2.1.0", + "js-sha3": "0.8.0", + "microseconds": "0.2.0", + "nano-time": "1.0.0", + "oblivious-set": "1.0.0", + "rimraf": "3.0.2", + "unload": "2.2.0" + } + }, "node_modules/browser-process-hrtime": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", @@ -12063,6 +12144,12 @@ "jiti": "bin/jiti.js" } }, + "node_modules/js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==", + "license": "MIT" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -12455,6 +12542,16 @@ "tmpl": "1.0.5" } }, + "node_modules/match-sorter": { + "version": "6.3.4", + "resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.4.tgz", + "integrity": "sha512-jfZW7cWS5y/1xswZo8VBOdudUiSd9nifYRWphc9M5D/ee4w4AoXLgBEdRbgVaxbMuagBPeUC5y2Hi8DO6o9aDg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.8", + "remove-accents": "0.5.0" + } + }, "node_modules/mdn-data": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", @@ -12517,6 +12614,12 @@ "node": ">=8.6" } }, + "node_modules/microseconds": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/microseconds/-/microseconds-0.2.0.tgz", + "integrity": "sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA==", + "license": "MIT" + }, "node_modules/mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -12701,6 +12804,15 @@ "thenify-all": "^1.0.0" } }, + "node_modules/nano-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/nano-time/-/nano-time-1.0.0.tgz", + "integrity": "sha512-flnngywOoQ0lLQOTRNexn2gGSNuM9bKj9RZAWSzhQ+UJYaAFG9bac4DW9VHjUAzrOaIcajHybCTHe/bkvozQqA==", + "license": "ISC", + "dependencies": { + "big-integer": "^1.6.16" + } + }, "node_modules/nanoid": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", @@ -12981,6 +13093,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/oblivious-set": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/oblivious-set/-/oblivious-set-1.0.0.tgz", + "integrity": "sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw==", + "license": "MIT" + }, "node_modules/obuf": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", @@ -15010,6 +15128,32 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, + "node_modules/react-query": { + "version": "3.39.3", + "resolved": "https://registry.npmjs.org/react-query/-/react-query-3.39.3.tgz", + "integrity": "sha512-nLfLz7GiohKTJDuT4us4X3h/8unOh+00MLb2yJoGTPjxKs2bc1iDhkNx2bd5MKklXnOD3NrVZ+J2UXujA5In4g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.5.5", + "broadcast-channel": "^3.4.1", + "match-sorter": "^6.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, "node_modules/react-refresh": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", @@ -15291,6 +15435,12 @@ "node": ">= 0.10" } }, + "node_modules/remove-accents": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz", + "integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==", + "license": "MIT" + }, "node_modules/renderkid": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", @@ -17248,6 +17398,16 @@ "node": ">= 10.0.0" } }, + "node_modules/unload": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unload/-/unload-2.2.0.tgz", + "integrity": "sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==", + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime": "^7.6.2", + "detect-node": "^2.0.4" + } + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", diff --git a/package.json b/package.json index f16d2a0..cd45fff 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,8 @@ "private": true, "dependencies": { "@heroicons/react": "^2.1.5", + "@tanstack/react-query": "^5.59.16", + "@tanstack/react-query-devtools": "^5.59.16", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", @@ -14,6 +16,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-icons": "^5.3.0", + "react-query": "^3.39.3", "react-router-dom": "^6.26.2", "react-scripts": "5.0.1", "styled-components": "^6.1.13", diff --git a/src/components/fakedata/messagesData.json b/public/fakedata/messagesData.json similarity index 100% rename from src/components/fakedata/messagesData.json rename to public/fakedata/messagesData.json diff --git a/public/fakedata/users.json b/public/fakedata/users.json new file mode 100644 index 0000000..92e7d8c --- /dev/null +++ b/public/fakedata/users.json @@ -0,0 +1,50 @@ +[ + { + "id": 0, + "name": "김류원", + "profilePic": "../assets/Profile image.svg", + "facebook": "https://www.facebook.com/", + "instagram": "https://www.instagram.com/" + }, + { + "id": 1, + "name": "CEOS", + "profilePic": "../assets/Profile image.svg", + "facebook": "https://www.facebook.com/clubceos/", + "instagram": "https://www.instagram.com/ceos.sinchon/", + "description": "IT 연합 동아리" + }, + + { + "id": 2, + "name": "차은우", + "profilePic": "../assets/Profile image.svg", + "facebook": "https://www.facebook.com/", + "instagram": "https://www.instagram.com/", + "description": "차은우! 차은우! 차은우!" + }, + { + "id": 3, + "name": "송강", + "profilePic": "../assets/Profile image.svg", + "facebook": "https://www.facebook.com/", + "instagram": "https://www.instagram.com/", + "description": "스위트홈에서 잘생김" + }, + { + "id": 4, + "name": "뷔", + "profilePic": "../assets/Profile image.svg", + "facebook": "https://www.facebook.com/", + "instagram": "https://www.instagram.com/", + "description": "그냥 잘생김" + }, + { + "id": 5, + "name": "서강준", + "profilePic": "../assets/Profile image.svg", + "facebook": "https://www.facebook.com/", + "instagram": "https://www.instagram.com/", + "description": "정글의법칙에서도 잘생김" + } +] diff --git a/src/App.tsx b/src/App.tsx index 06d7d43..944f433 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -8,6 +8,7 @@ import ChatRoom from "./pages/ChatRoom"; import ProfileDetail from "./pages/ProfileDetail"; import { useState } from "react"; import BottomNav from "./components/BottomNav"; +import { ReactQueryDevtools } from "react-query/devtools"; import Home from "./pages/Home"; import ChatList from "./pages/ChatList"; @@ -44,7 +45,9 @@ function App() { } /> + {chatRoomMatch ? null : } + ); } diff --git a/src/api.tsx b/src/api.tsx new file mode 100644 index 0000000..e3289d2 --- /dev/null +++ b/src/api.tsx @@ -0,0 +1,3 @@ +export function fetchUsers() { + return fetch("/fakedata/users.json").then((response) => response.json()); +} diff --git a/src/assets/person_plus.svg b/src/assets/person_plus.svg new file mode 100644 index 0000000..8deec69 --- /dev/null +++ b/src/assets/person_plus.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/components/ChatRoomHeader.tsx b/src/components/ChatRoomHeader.tsx new file mode 100644 index 0000000..bf2b909 --- /dev/null +++ b/src/components/ChatRoomHeader.tsx @@ -0,0 +1,67 @@ +import backIcon from "../assets/Back.svg"; +import call from "../assets/call.svg"; +import video from "../assets/video.svg"; +import search from "../assets/search.svg"; + +interface User { + name: string; + profilePic: string; +} + +interface HeaderProps { + user: User; + onProfileClick: () => void; + isProfileDetailOpen: boolean; +} + +const ChatRoomHeader: React.FC = ({ + user, + onProfileClick, + isProfileDetailOpen, +}) => { + return ( +
+
+ + + {/* 프로필 이미지 */} + Profile + + {/* 사용자 이름 */} +
+

+ {user.name} +

+
+
+ + {/* 오른쪽 아이콘 */} +
+ + + +
+
+ ); +}; + +export default ChatRoomHeader; diff --git a/src/components/FriendsList.tsx b/src/components/FriendsList.tsx new file mode 100644 index 0000000..184ac29 --- /dev/null +++ b/src/components/FriendsList.tsx @@ -0,0 +1,23 @@ +import { User } from "../pages/ChatRoom"; + +const FriendsList = ({ data }: { data: User[] }) => { + //data는 query에서 불러옴 + return ( +
+ {/*최상위 JSX */} + {data.slice(1).map((user) => { + return ( +
+
+
+
{user.name}
+
{user.description}
+
+
+ ); + })} +
+ ); +}; + +export default FriendsList; diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 6ad4cb5..e622325 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -3,49 +3,11 @@ import call from "../assets/call.svg"; import video from "../assets/video.svg"; import search from "../assets/search.svg"; -interface User { - name: string; - profilePic: string; -} - -interface HeaderProps { - user: User; - onProfileClick: () => void; - isProfileDetailOpen: boolean; -} - -const Header: React.FC = ({ - user, - onProfileClick, - isProfileDetailOpen, -}) => { +const Header = () => { return ( -
+
- - - {/* 프로필 이미지 */} - Profile - - {/* 사용자 이름 */} -
-

- {user.name} -

-
+ 친구
{/* 오른쪽 아이콘 */} diff --git a/src/components/fakedata/users.json b/src/components/fakedata/users.json deleted file mode 100644 index e4738dd..0000000 --- a/src/components/fakedata/users.json +++ /dev/null @@ -1,16 +0,0 @@ -[ - { - "id": 1, - "name": "CEOS", - "profilePic": "../assets/Profile image.svg", - "facebook": "https://www.facebook.com/clubceos/", - "instagram": "https://www.instagram.com/ceos.sinchon/" - }, - { - "id": 2, - "name": "김류원", - "profilePic": "../assets/Profile image.svg", - "facebook": "https://www.facebook.com/", - "instagram": "https://www.instagram.com/" - } -] diff --git a/src/index.tsx b/src/index.tsx index 16a2d58..39dba9d 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -4,11 +4,17 @@ import "./index.css"; import App from "./App"; import { BrowserRouter } from "react-router-dom"; +import { QueryClient, QueryClientProvider } from "react-query"; + +const queryClient = new QueryClient(); + const root = ReactDOM.createRoot( document.getElementById("root") as HTMLElement ); root.render( - - - + + + + + ); diff --git a/src/pages/ChatRoom.tsx b/src/pages/ChatRoom.tsx index c97e4cf..17c4fb2 100644 --- a/src/pages/ChatRoom.tsx +++ b/src/pages/ChatRoom.tsx @@ -2,17 +2,18 @@ import React, { useState, useEffect, useRef } from "react"; import MessageList from "../components/MessageList"; import MessageInput from "../components/MessageInput"; import ProfileInfo from "../components/ProfileInfo"; -import Header from "../components/Header"; +import Header from "../components/ChatRoomHeader"; import ProfileDetail from "../pages/ProfileDetail"; import ceosProfilePic from "../assets/Profile image.svg"; import userProfilePic from "../assets/Profile image.svg"; -interface User { +export interface User { id: number; name: string; profilePic: string; facebook: string; instagram: string; + description?: string; } interface Message { diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index e0c469a..d8b11b6 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -1,5 +1,54 @@ +import Header from "../components/Header"; + +import { useQuery } from "react-query"; +import { fetchUsers } from "../api"; +import { User } from "./ChatRoom"; +import FriendsList from "../components/FriendsList"; + const Home = () => { - return
home
; + const { isLoading, data = [] } = useQuery("users", fetchUsers); + + return ( +
+
+ + {isLoading ? ( +
로딩중
+ ) : ( +
+ {/* 사용자 프로필 */} +
+
+
+

{data[0].name}

+
+
+ {/* 활동 상태 */} +
+

활동 상태

+
+ {[...Array(5)].map((_, i) => ( +
+
+ +
+

세오스

+
+ ))} +
+
+ {/* 친구 목록 */} +
+

친구 {data.length}

+ +
+
+ )} +
+ ); }; export default Home; From a6b3debca50a399562237ba6dc6f46070e01e885 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=A5=98=EC=9B=90?= <2145007@ewha.ac.kr> Date: Thu, 31 Oct 2024 16:51:25 +0900 Subject: [PATCH 14/26] =?UTF-8?q?feat=20:=20chatlist=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 27 +++++++++++++++++ package.json | 1 + public/fakedata/messagesData.json | 30 +++++++++++++++++++ src/api.tsx | 6 ++++ src/components/BottomNav.tsx | 17 ++++++++--- .../{ChatRoomHeader.tsx => CHeader.tsx} | 0 src/components/ChatItem.tsx | 20 +++++++++++++ src/index.tsx | 14 +++++---- src/pages/ChatList.tsx | 26 +++++++++++++++- src/pages/ChatRoom.tsx | 4 +-- 10 files changed, 132 insertions(+), 13 deletions(-) rename src/components/{ChatRoomHeader.tsx => CHeader.tsx} (100%) create mode 100644 src/components/ChatItem.tsx diff --git a/package-lock.json b/package-lock.json index 0ab094e..63852b3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,7 @@ "react-query": "^3.39.3", "react-router-dom": "^6.26.2", "react-scripts": "5.0.1", + "recoil": "^0.7.7", "styled-components": "^6.1.13", "typescript": "^4.9.5", "web-vitals": "^2.1.4" @@ -9070,6 +9071,12 @@ "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==", + "license": "MIT" + }, "node_modules/handle-thing": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", @@ -15298,6 +15305,26 @@ "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==", + "license": "MIT", + "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 cd45fff..9e67d05 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "react-query": "^3.39.3", "react-router-dom": "^6.26.2", "react-scripts": "5.0.1", + "recoil": "^0.7.7", "styled-components": "^6.1.13", "typescript": "^4.9.5", "web-vitals": "^2.1.4" diff --git a/public/fakedata/messagesData.json b/public/fakedata/messagesData.json index e69de29..a07776e 100644 --- a/public/fakedata/messagesData.json +++ b/public/fakedata/messagesData.json @@ -0,0 +1,30 @@ +[ + { + "id": 1, + "text": "머하밍??", + "sender": "CEOS", + "receiver": "김류원", + "time": "2024-10-31T12:34:56.000Z" + }, + { + "id": 2, + "text": "과제중🤮🤮", + "sender": "김류원", + "receiver": "CEOS", + "time": "2024-10-31T12:35:56.000Z" + }, + { + "id": 3, + "text": "🤦‍♀️🤦‍♀️", + "sender": "CEOS", + "receiver": "김류원", + "time": "2024-10-31T12:36:56.000Z" + }, + { + "id": 4, + "text": "차은우가 보낸 메세지", + "sender": "차은우", + "receiver": "김류원", + "time": "2024-10-31T12:37:56.000Z" + } +] diff --git a/src/api.tsx b/src/api.tsx index e3289d2..e3c2aaf 100644 --- a/src/api.tsx +++ b/src/api.tsx @@ -1,3 +1,9 @@ export function fetchUsers() { return fetch("/fakedata/users.json").then((response) => response.json()); } + +export function fetchMessages() { + return fetch("/fakedata/messagesData.json").then((response) => + response.json() + ); +} diff --git a/src/components/BottomNav.tsx b/src/components/BottomNav.tsx index bb83cfa..d554551 100644 --- a/src/components/BottomNav.tsx +++ b/src/components/BottomNav.tsx @@ -1,8 +1,15 @@ import { useState } from "react"; -import { useNavigate } from "react-router-dom"; +import { + useLocation, + useMatch, + useNavigate, + useParams, +} from "react-router-dom"; const BottomNav = () => { const [activeTab, setActiveTab] = useState("home"); + const location = useLocation(); + const nav = useNavigate(); return ( @@ -10,7 +17,7 @@ const BottomNav = () => {
diff --git a/src/components/ChatItem.tsx b/src/components/ChatItem.tsx index 57e6f7a..7db4387 100644 --- a/src/components/ChatItem.tsx +++ b/src/components/ChatItem.tsx @@ -1,15 +1,30 @@ import { useQuery } from "react-query"; -import { Message } from "../pages/ChatRoom"; +import { Message, User } from "../pages/ChatRoom"; +import { fetchUsers } from "../api"; +import { useNavigate } from "react-router-dom"; const ChatItem = ({ sender, text }: Message) => { - const [isLoading, data] = useQuery("messageUser"); + const { isLoading, data = [] } = useQuery("messageUser", fetchUsers); + const nav = useNavigate(); + + const findProfilePic = (): User => + data.find((userInfo: User) => userInfo.name === sender); + //filter와 map의 차이 map은 전체 배열을 수정해서 새롭게 만들고 + //filter는 그중에서 찾음 + const getProfilePic = findProfilePic()?.profilePic; + + const onClick = () => { + nav(`/chat-room/${sender}`); + }; return ( -
-
- +
+
+ {!isLoading && ( + + )}
-
+
{sender}
{text}
diff --git a/src/components/FriendsList.tsx b/src/components/FriendsList.tsx index 184ac29..514a4d6 100644 --- a/src/components/FriendsList.tsx +++ b/src/components/FriendsList.tsx @@ -8,7 +8,11 @@ const FriendsList = ({ data }: { data: User[] }) => { {data.slice(1).map((user) => { return (
-
+
{user.name}
{user.description}
diff --git a/src/components/MessageList.tsx b/src/components/MessageList.tsx index d7d8f01..5fcb20f 100644 --- a/src/components/MessageList.tsx +++ b/src/components/MessageList.tsx @@ -1,5 +1,3 @@ -import ceosProfilePic from "../assets/Profile image.svg"; -import userProfilePic from "../assets/Profile image.svg"; import styled, { keyframes } from "styled-components"; import React, { useState, useEffect } from "react"; @@ -65,7 +63,7 @@ const MessageList: React.FC = ({ messages, currentUser }) => { }; const getProfilePic = (sender: string): string => { - return sender === "CEOS" ? ceosProfilePic : userProfilePic; + return sender === "CEOS" ? "/Profile_image.svg" : "/Profile_image.svg"; }; let previousDate: string | null = null; diff --git a/src/pages/ChatList.tsx b/src/pages/ChatList.tsx index 91727df..53db0bd 100644 --- a/src/pages/ChatList.tsx +++ b/src/pages/ChatList.tsx @@ -11,7 +11,7 @@ const ChatList = () => { ); const chatItemData = data.reduceRight((p: Message[], n: Message) => { - if (!p.find((item) => item.sender === n.sender)) { + if (!p.find((item) => item.sender === n.sender) && n.sender !== "김류원") { return [...p, n]; } return p; diff --git a/src/pages/ChatRoom.tsx b/src/pages/ChatRoom.tsx index 108fef5..ca187fe 100644 --- a/src/pages/ChatRoom.tsx +++ b/src/pages/ChatRoom.tsx @@ -4,8 +4,9 @@ import MessageInput from "../components/MessageInput"; import ProfileInfo from "../components/ProfileInfo"; import Header from "../components/CHeader"; import ProfileDetail from "../pages/ProfileDetail"; -import ceosProfilePic from "../assets/Profile image.svg"; -import userProfilePic from "../assets/Profile image.svg"; +import { useQuery } from "react-query"; +import { fetchMessages, fetchUsers } from "../api"; +import { useParams } from "react-router-dom"; export interface User { id: number; @@ -19,59 +20,53 @@ export interface User { export interface Message { id: number; text: string; - sender: string; + sender: string; // receiver: string; time: string; } const ChatRoom: React.FC = () => { - const users: { ceos: User; chae: User } = { - ceos: { - id: 1, - name: "CEOS", - profilePic: ceosProfilePic, - facebook: "https://www.facebook.com/clubceos/", - instagram: "https://www.instagram.com/ceos.sinchon/", - }, - chae: { - id: 2, - name: "김류원", - profilePic: userProfilePic, - facebook: "https://www.facebook.com/", - instagram: "https://www.instagram.com/", - }, - }; + const { isLoading: messageLoading, data: messageData = [] } = useQuery< + Message[] + >("chat-message", fetchMessages); + const { isLoading: userLoading, data: userData = [] } = useQuery( + "chat-user", + fetchUsers + ); + const [messages, setMessages] = useState([]); + const params = useParams(); + const [currentUser, setCurrentUser] = useState(); + const [otherUser, setOtherUser] = useState(); + + const ryuwon = userData.find((item) => item.name === "김류원"); + const other = userData.find((item) => item.name === params.sender); + + useEffect(() => { + if (ryuwon) { + setCurrentUser(ryuwon); + } + }, [userData, ryuwon]); + + useEffect(() => { + const other = userData.find((item) => item.name === params.sender); + if (other) { + setOtherUser(other); + } + }, [params.sender, other]); + + useEffect(() => { + const messages = messageData.filter( + (item: Message) => + item.sender === params.sender || item.receiver === params.sender + ); + if (messages) { + setMessages(messages); + } + }, [params.sender, messageData]); + + //함수? useeffect -> state에 담아야함 + //이전페이지에서 데이터 받기 - const [currentUser, setCurrentUser] = useState(users.chae); - - const otherUser = - currentUser.name === users.chae.name ? users.ceos : users.chae; - - const initialMessages: Message[] = [ - { - id: 1, - text: "머하밍??", - sender: "CEOS", - receiver: "김류원", - time: new Date().toISOString(), - }, - { - id: 2, - text: "과제중🤮🤮", - sender: "김류원", - receiver: "CEOS", - time: new Date().toISOString(), - }, - { - id: 3, - text: "🤦‍♀️🤦‍♀️", - sender: "CEOS", - receiver: "김류원", - time: new Date().toISOString(), - }, - ]; - - const [messages, setMessages] = useState(initialMessages); const [isProfileDetailOpen, setIsProfileDetailOpen] = useState(false); const messagesEndRef = useRef(null); @@ -90,9 +85,8 @@ const ChatRoom: React.FC = () => { }, [messages]); const toggleUser = () => { - setCurrentUser( - currentUser.name === users.chae.name ? users.ceos : users.chae - ); + setCurrentUser(currentUser?.name === "김류원" ? other : ryuwon); + setOtherUser(currentUser?.name === "김류원" ? ryuwon : other); }; const handleProfileDetail = () => { @@ -108,11 +102,12 @@ const ChatRoom: React.FC = () => { const newMessageData: Message = { id: messages.length + 1, text: newMessage, - sender: currentUser.name, - receiver: - currentUser.name === users.chae.name - ? users.ceos.name - : users.chae.name, + sender: "김류원", + receiver: currentUser + ? currentUser?.name === "김류원" + ? otherUser?.name || "김류원" + : currentUser?.name + : "김류원", // time: currentTime, }; @@ -124,7 +119,15 @@ const ChatRoom: React.FC = () => { ); }; - return ( + const loading = + userLoading || + messageLoading || + currentUser === undefined || + otherUser === undefined; + + return loading ? ( +
로딩중
+ ) : (
{
{/* 사용자 프로필 */}
-
+

{data[0].name}

From efdd176669137c0dd43cd6096d2de140d5acd596 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=A5=98=EC=9B=90?= <2145007@ewha.ac.kr> Date: Sat, 2 Nov 2024 17:50:07 +0900 Subject: [PATCH 16/26] =?UTF-8?q?chore=20:=20=ED=94=BC=EA=B7=B8=EB=A7=88?= =?UTF-8?q?=EC=97=90=20=EB=A7=9E=EA=B2=8C=20UI=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 29 ++++------------------- src/assets/Story.svg | 4 ++++ src/assets/chat.svg | 3 +++ src/assets/home.svg | 3 +++ src/components/BottomNav.tsx | 36 +++++++++++++++++++++++------ src/components/CHeader.tsx | 2 +- src/components/ChatItem.tsx | 6 ++--- src/components/FriendsList.tsx | 18 +++++++++++---- src/components/Header.tsx | 27 ++++++++++++++-------- src/components/MessageList.tsx | 22 ++++++++++++------ src/pages/ChatList.tsx | 4 ++-- src/pages/ChatRoom.tsx | 38 ++++++++++++++++++++++++------ src/pages/Home.tsx | 42 ++++++++++++++++++++++++++-------- src/pages/ProfileDetail.tsx | 15 ++++++++++-- 14 files changed, 174 insertions(+), 75 deletions(-) create mode 100644 src/assets/Story.svg create mode 100644 src/assets/chat.svg create mode 100644 src/assets/home.svg diff --git a/src/App.tsx b/src/App.tsx index 8619ec2..fe08881 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -14,21 +14,7 @@ import Home from "./pages/Home"; import ChatList from "./pages/ChatList"; function App() { - const chatRoomMatch = useMatch("/userID"); - - // 상태로 관리할 유저 정보 - const [user, setUser] = useState({ - name: "김류원", - profilePic: "/path/to/profile-pic.jpg", - facebook: "https://www.facebook.com/", - instagram: "https://www.facebook.com/", - }); - - // ProfileDetail에서 닫기 기능을 위한 핸들러 - const handleCloseProfileDetail = () => { - console.log("Profile detail closed"); - // 추가적인 동작이 필요할 경우 이곳에 작성 - }; + const chatRoomMatch = useMatch("/chat-room/:sender"); return ( <> @@ -36,17 +22,12 @@ function App() { } /> } /> } /> - - {/* 프로필 상세 보기 */} - - } - /> +
+ {" "} + {chatRoomMatch ? null : } +
- {chatRoomMatch ? null : } ); diff --git a/src/assets/Story.svg b/src/assets/Story.svg new file mode 100644 index 0000000..a8fbb76 --- /dev/null +++ b/src/assets/Story.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/chat.svg b/src/assets/chat.svg new file mode 100644 index 0000000..9095999 --- /dev/null +++ b/src/assets/chat.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/home.svg b/src/assets/home.svg new file mode 100644 index 0000000..f995d1f --- /dev/null +++ b/src/assets/home.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/BottomNav.tsx b/src/components/BottomNav.tsx index d554551..5e7d1c8 100644 --- a/src/components/BottomNav.tsx +++ b/src/components/BottomNav.tsx @@ -5,15 +5,23 @@ import { useNavigate, useParams, } from "react-router-dom"; +import chat from "../assets/chat.svg"; +import story from "../assets/Story.svg"; +import home from "../assets/home.svg"; const BottomNav = () => { - const [activeTab, setActiveTab] = useState("home"); const location = useLocation(); - const nav = useNavigate(); + const isHomeActive = location.pathname === "/"; + const isChatActive = location.pathname === "/chat-list"; + const isStoryActive = location.pathname === "/story"; + + const activeFilter = + "invert(29%) sepia(92%) saturate(1429%) hue-rotate(188deg) brightness(95%) contrast(93%)"; + return ( -
+
diff --git a/src/components/CHeader.tsx b/src/components/CHeader.tsx index a2ff6bc..82b5ead 100644 --- a/src/components/CHeader.tsx +++ b/src/components/CHeader.tsx @@ -28,7 +28,7 @@ const ChatRoomHeader: React.FC = ({ style={{ filter: isProfileDetailOpen ? "blur(4px)" : "none" }} >
- diff --git a/src/components/ChatItem.tsx b/src/components/ChatItem.tsx index 7db4387..541c7f1 100644 --- a/src/components/ChatItem.tsx +++ b/src/components/ChatItem.tsx @@ -18,13 +18,13 @@ const ChatItem = ({ sender, text }: Message) => { }; return ( -
+
{!isLoading && ( - + )}
-
+
{sender}
{text}
diff --git a/src/components/FriendsList.tsx b/src/components/FriendsList.tsx index 514a4d6..4939c54 100644 --- a/src/components/FriendsList.tsx +++ b/src/components/FriendsList.tsx @@ -1,13 +1,23 @@ import { User } from "../pages/ChatRoom"; -const FriendsList = ({ data }: { data: User[] }) => { +const FriendsList = ({ + data, + profileOnClick, +}: { + data: User[]; + profileOnClick: (index: number) => void; +}) => { //data는 query에서 불러옴 + return ( -
+
{/*최상위 JSX */} - {data.slice(1).map((user) => { + {data.slice(1).map((user, index) => { return ( -
+
profileOnClick(index + 1)} + > { + const match = useMatch("/"); + return ( -
-
- 친구 +
+
+ {match ? "친구" : "채팅"}
{/* 오른쪽 아이콘 */} @@ -15,11 +18,17 @@ const Header = () => { + {match ? ( + + ) : null} -
diff --git a/src/components/MessageList.tsx b/src/components/MessageList.tsx index 5fcb20f..0451d98 100644 --- a/src/components/MessageList.tsx +++ b/src/components/MessageList.tsx @@ -1,12 +1,13 @@ import styled, { keyframes } from "styled-components"; import React, { useState, useEffect } from "react"; +import { Message } from "../pages/ChatRoom"; -interface Message { - id: string | number; - sender: string; - text: string; - time: string | number | Date; -} +// interface Message { +// id: string | number; +// sender: string; +// text: string; +// time: string | number | Date; +// } interface User { name: string; @@ -15,6 +16,8 @@ interface User { interface MessageListProps { messages: Message[]; currentUser: User; + messageData: Message[]; + setMessages: React.Dispatch>; } // 애니메이션 정의 @@ -29,7 +32,12 @@ const fadeIn = keyframes` } `; -const MessageList: React.FC = ({ messages, currentUser }) => { +const MessageList: React.FC = ({ + messages, + currentUser, + messageData, + setMessages, +}) => { const [currentSender, setCurrentSender] = useState(currentUser.name); useEffect(() => { diff --git a/src/pages/ChatList.tsx b/src/pages/ChatList.tsx index 53db0bd..9dac870 100644 --- a/src/pages/ChatList.tsx +++ b/src/pages/ChatList.tsx @@ -16,10 +16,10 @@ const ChatList = () => { } return p; }, []); - console.log(chatItemData); + // console.log(data); return ( -
+
{chatItemData && chatItemData.map((item) => )}
diff --git a/src/pages/ChatRoom.tsx b/src/pages/ChatRoom.tsx index ca187fe..d7eac3e 100644 --- a/src/pages/ChatRoom.tsx +++ b/src/pages/ChatRoom.tsx @@ -2,11 +2,11 @@ import React, { useState, useEffect, useRef } from "react"; import MessageList from "../components/MessageList"; import MessageInput from "../components/MessageInput"; import ProfileInfo from "../components/ProfileInfo"; -import Header from "../components/CHeader"; +import CHeader from "../components/CHeader"; import ProfileDetail from "../pages/ProfileDetail"; import { useQuery } from "react-query"; import { fetchMessages, fetchUsers } from "../api"; -import { useParams } from "react-router-dom"; +import { useLocation, useParams } from "react-router-dom"; export interface User { id: number; @@ -33,6 +33,7 @@ const ChatRoom: React.FC = () => { "chat-user", fetchUsers ); + const [messages, setMessages] = useState([]); const params = useParams(); const [currentUser, setCurrentUser] = useState(); @@ -48,7 +49,6 @@ const ChatRoom: React.FC = () => { }, [userData, ryuwon]); useEffect(() => { - const other = userData.find((item) => item.name === params.sender); if (other) { setOtherUser(other); } @@ -74,9 +74,28 @@ const ChatRoom: React.FC = () => { // LocalStorage에 저장된 메시지가 있으면 불러오기 const storedMessages = localStorage.getItem("conversationMessages"); if (storedMessages) { - setMessages(JSON.parse(storedMessages)); + const currentJson = JSON.parse(storedMessages); + setMessages((pre) => + currentJson + ? currentJson.filter( + (item: Message) => + item.sender === params.sender || item.receiver === params.sender + ) + : pre + ); } - }, []); + }, [params.name, params.sender]); + + useEffect(() => { + if (messages.length === 0 && messageData.length > 0) { + const filteredMessages = messageData.filter( + (item) => + (item.sender === params.sender && item.receiver === "김류원") || + (item.sender === "김류원" && item.receiver === params.sender) + ); + setMessages(filteredMessages); + } + }, [params.sender, messageData]); useEffect(() => { if (messagesEndRef.current) { @@ -129,7 +148,7 @@ const ChatRoom: React.FC = () => {
로딩중
) : (
-
{ style={{ filter: isProfileDetailOpen ? "blur(4px)" : "none" }} > - +
{ const { isLoading, data = [] } = useQuery("users", fetchUsers); + const [isProfileDetailOpen, setIsProfileDetailOpen] = useState(false); + const [profileUser, setProfileUser] = useState(data[0]); + const profileOnClick = (index: number) => { + setIsProfileDetailOpen(true); + setProfileUser(data[index]); + }; + + const closeProfileDetail = () => { + setIsProfileDetailOpen(false); + }; return (
@@ -18,9 +30,12 @@ const Home = () => { {isLoading ? (
로딩중
) : ( -
+
{/* 사용자 프로필 */} -
+
profileOnClick(0)} + > {
{/* 활동 상태 */}
-

활동 상태

+

활동 상태

- {[...Array(5)].map((_, i) => ( + {data.slice(1).map((item, i) => (
-
+
+
-

세오스

+

{item.name}

))}
{/* 친구 목록 */}
-

친구 {data.length}

- +

+ 친구 {data.length} +

+
)} + {/* 프로필 상세 보기 */} + {isProfileDetailOpen && ( +
+ +
+ )}
); }; diff --git a/src/pages/ProfileDetail.tsx b/src/pages/ProfileDetail.tsx index 8f20fd6..2e4f002 100644 --- a/src/pages/ProfileDetail.tsx +++ b/src/pages/ProfileDetail.tsx @@ -4,6 +4,7 @@ import facebook from "../assets/profile_facebook.svg"; import instagram from "../assets/profile_insta.svg"; import plus from "../assets/profile_plus.svg"; import edit from "../assets/edit.svg"; +import { useMatch, useNavigate } from "react-router-dom"; interface User { name: string; @@ -18,8 +19,15 @@ interface ProfileDetailProps { } const ProfileDetail: React.FC = ({ user, onClose }) => { + const match = useMatch("/"); + const nav = useNavigate(); + return ( -
+
-
-
); }; diff --git a/src/components/CHeader.tsx b/src/components/CHeader.tsx index 82b5ead..a2ff6bc 100644 --- a/src/components/CHeader.tsx +++ b/src/components/CHeader.tsx @@ -28,7 +28,7 @@ const ChatRoomHeader: React.FC = ({ style={{ filter: isProfileDetailOpen ? "blur(4px)" : "none" }} >
- diff --git a/src/pages/ChatRoom.tsx b/src/pages/ChatRoom.tsx index d7eac3e..12a607f 100644 --- a/src/pages/ChatRoom.tsx +++ b/src/pages/ChatRoom.tsx @@ -6,7 +6,7 @@ import CHeader from "../components/CHeader"; import ProfileDetail from "../pages/ProfileDetail"; import { useQuery } from "react-query"; import { fetchMessages, fetchUsers } from "../api"; -import { useLocation, useParams } from "react-router-dom"; +import { useParams } from "react-router-dom"; export interface User { id: number; @@ -20,7 +20,7 @@ export interface User { export interface Message { id: number; text: string; - sender: string; // + sender: string; receiver: string; time: string; } @@ -55,39 +55,19 @@ const ChatRoom: React.FC = () => { }, [params.sender, other]); useEffect(() => { - const messages = messageData.filter( - (item: Message) => - item.sender === params.sender || item.receiver === params.sender - ); - if (messages) { - setMessages(messages); - } - }, [params.sender, messageData]); - - //함수? useeffect -> state에 담아야함 - //이전페이지에서 데이터 받기 - - const [isProfileDetailOpen, setIsProfileDetailOpen] = useState(false); - const messagesEndRef = useRef(null); - - useEffect(() => { - // LocalStorage에 저장된 메시지가 있으면 불러오기 - const storedMessages = localStorage.getItem("conversationMessages"); - if (storedMessages) { - const currentJson = JSON.parse(storedMessages); - setMessages((pre) => - currentJson - ? currentJson.filter( - (item: Message) => - item.sender === params.sender || item.receiver === params.sender - ) - : pre - ); - } - }, [params.name, params.sender]); + if (messages.length === 0) { + const storedMessages = localStorage.getItem("conversationMessages"); + if (storedMessages) { + const parsedMessages = JSON.parse(storedMessages).filter( + (item: Message) => + item.sender === params.sender || item.receiver === params.sender + ); + if (parsedMessages.length > 0) { + setMessages((pre) => parsedMessages); + return; + } + } - useEffect(() => { - if (messages.length === 0 && messageData.length > 0) { const filteredMessages = messageData.filter( (item) => (item.sender === params.sender && item.receiver === "김류원") || @@ -95,7 +75,10 @@ const ChatRoom: React.FC = () => { ); setMessages(filteredMessages); } - }, [params.sender, messageData]); + }, [params.sender, messageData, messages]); + + const [isProfileDetailOpen, setIsProfileDetailOpen] = useState(false); + const messagesEndRef = useRef(null); useEffect(() => { if (messagesEndRef.current) { @@ -126,7 +109,7 @@ const ChatRoom: React.FC = () => { ? currentUser?.name === "김류원" ? otherUser?.name || "김류원" : currentUser?.name - : "김류원", // + : "김류원", time: currentTime, }; @@ -138,11 +121,7 @@ const ChatRoom: React.FC = () => { ); }; - const loading = - userLoading || - messageLoading || - currentUser === undefined || - otherUser === undefined; + const loading = userLoading || messageLoading || !currentUser || !otherUser; return loading ? (
로딩중
diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 5e28b94..10023d6 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -29,7 +29,7 @@ const Home = () => { {isLoading ? (
로딩중
) : ( -
+
{/* 사용자 프로필 */}
Date: Sat, 2 Nov 2024 18:21:16 +0900 Subject: [PATCH 19/26] =?UTF-8?q?fix=20:=20=EC=B1=84=ED=8C=85=EB=B0=A9?= =?UTF-8?q?=EA=B0=84=20=EB=A1=9C=EC=BB=AC=EC=8A=A4=ED=86=A0=EB=A6=AC?= =?UTF-8?q?=EC=A7=80=20=EB=8F=99=EC=9D=BC=ED=95=9C=20ID=EB=A1=9C=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5=EB=90=98=EC=96=B4=20=20=EC=B4=88=EA=B8=B0?= =?UTF-8?q?=ED=99=94=EB=90=98=EB=8A=94=20=EB=AC=B8=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/ChatRoom.tsx | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/pages/ChatRoom.tsx b/src/pages/ChatRoom.tsx index 12a607f..367947d 100644 --- a/src/pages/ChatRoom.tsx +++ b/src/pages/ChatRoom.tsx @@ -42,6 +42,8 @@ const ChatRoom: React.FC = () => { const ryuwon = userData.find((item) => item.name === "김류원"); const other = userData.find((item) => item.name === params.sender); + const chatKey = `conversationMessages_${params.sender}`; + useEffect(() => { if (ryuwon) { setCurrentUser(ryuwon); @@ -56,14 +58,14 @@ const ChatRoom: React.FC = () => { useEffect(() => { if (messages.length === 0) { - const storedMessages = localStorage.getItem("conversationMessages"); + const storedMessages = localStorage.getItem(chatKey); if (storedMessages) { const parsedMessages = JSON.parse(storedMessages).filter( (item: Message) => item.sender === params.sender || item.receiver === params.sender ); if (parsedMessages.length > 0) { - setMessages((pre) => parsedMessages); + setMessages(parsedMessages); return; } } @@ -75,7 +77,7 @@ const ChatRoom: React.FC = () => { ); setMessages(filteredMessages); } - }, [params.sender, messageData, messages]); + }, [params.sender, messageData, messages, chatKey]); const [isProfileDetailOpen, setIsProfileDetailOpen] = useState(false); const messagesEndRef = useRef(null); @@ -106,19 +108,16 @@ const ChatRoom: React.FC = () => { text: newMessage, sender: "김류원", receiver: currentUser - ? currentUser?.name === "김류원" + ? currentUser.name === "김류원" ? otherUser?.name || "김류원" - : currentUser?.name + : currentUser.name : "김류원", time: currentTime, }; const updatedMessages = [...messages, newMessageData]; setMessages(updatedMessages); - localStorage.setItem( - "conversationMessages", - JSON.stringify(updatedMessages) - ); + localStorage.setItem(chatKey, JSON.stringify(updatedMessages)); }; const loading = userLoading || messageLoading || !currentUser || !otherUser; From 9fbc1b562228526d3564f2e70a79e3379c59d942 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=A5=98=EC=9B=90?= <2145007@ewha.ac.kr> Date: Mon, 4 Nov 2024 15:24:48 +0900 Subject: [PATCH 20/26] =?UTF-8?q?fix=20:=20=EC=B5=9C=EA=B7=BC=20=EB=8C=80?= =?UTF-8?q?=ED=99=94=EB=AA=A9=EB=A1=9D=20=EC=88=9C=EC=84=9C=EB=8C=80?= =?UTF-8?q?=EB=A1=9C=20=EC=A0=95=EB=A0=AC,=20=EB=A1=9C=EC=BB=AC=EC=8A=A4?= =?UTF-8?q?=ED=86=A0=EB=A6=AC=EC=A7=80=EC=97=90=20=EC=83=88=EB=A1=9C?= =?UTF-8?q?=EC=9A=B4=20=EC=B1=84=ED=8C=85=EB=B0=A9=EC=9D=B4=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=EB=90=98=EC=96=B4=EB=8F=84=20=EC=B1=84=ED=8C=85?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=ED=8A=B8=EC=97=90=20=EB=B3=B4=EC=9D=B4?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/fakedata/messagesData.json | 15 ++++--- src/components/ChatItem.tsx | 8 ++-- src/pages/ChatList.tsx | 65 +++++++++++++++++++++++++++---- src/pages/ChatRoom.tsx | 27 ++++++++----- src/pages/Home.tsx | 4 +- src/pages/ProfileDetail.tsx | 4 +- 6 files changed, 95 insertions(+), 28 deletions(-) diff --git a/public/fakedata/messagesData.json b/public/fakedata/messagesData.json index 33818fb..9cfe904 100644 --- a/public/fakedata/messagesData.json +++ b/public/fakedata/messagesData.json @@ -4,34 +4,39 @@ "text": "머하밍??", "sender": "CEOS", "receiver": "김류원", - "time": "2024-10-31T12:34:56.000Z" + "time": "2024-10-31T12:34:56.000Z", + "chatRoomId": "CEOS" }, { "id": 2, "text": "과제중🤮🤮", "sender": "김류원", "receiver": "CEOS", - "time": "2024-10-31T12:35:56.000Z" + "time": "2024-10-31T12:35:56.000Z", + "chatRoomId": "CEOS" }, { "id": 3, "text": "🤦‍♀️🤦‍♀️", "sender": "CEOS", "receiver": "김류원", - "time": "2024-10-31T12:36:56.000Z" + "time": "2024-10-31T12:36:56.000Z", + "chatRoomId": "CEOS" }, { "id": 4, "text": "차은우가 보낸 메세지", "sender": "차은우", "receiver": "김류원", - "time": "2024-10-31T12:37:56.000Z" + "time": "2024-10-31T12:37:56.000Z", + "chatRoomId": "차은우" }, { "id": 5, "text": "뷔가 보낸 메세지", "sender": "뷔", "receiver": "김류원", - "time": "2024-10-31T12:38:56.000Z" + "time": "2024-10-31T12:38:56.000Z", + "chatRoomId": "뷔" } ] diff --git a/src/components/ChatItem.tsx b/src/components/ChatItem.tsx index 541c7f1..d184961 100644 --- a/src/components/ChatItem.tsx +++ b/src/components/ChatItem.tsx @@ -3,7 +3,7 @@ import { Message, User } from "../pages/ChatRoom"; import { fetchUsers } from "../api"; import { useNavigate } from "react-router-dom"; -const ChatItem = ({ sender, text }: Message) => { +const ChatItem = ({ sender, text, chatRoomId, receiver }: Message) => { const { isLoading, data = [] } = useQuery("messageUser", fetchUsers); const nav = useNavigate(); @@ -14,7 +14,7 @@ const ChatItem = ({ sender, text }: Message) => { const getProfilePic = findProfilePic()?.profilePic; const onClick = () => { - nav(`/chat-room/${sender}`); + nav(`/chat-room/${chatRoomId}`, { state: { chatRoomId: chatRoomId } }); }; return ( @@ -25,7 +25,9 @@ const ChatItem = ({ sender, text }: Message) => { )}
-
{sender}
+
+ {sender === "김류원" ? receiver : sender} +
{text}
diff --git a/src/pages/ChatList.tsx b/src/pages/ChatList.tsx index 9dac870..7f799bb 100644 --- a/src/pages/ChatList.tsx +++ b/src/pages/ChatList.tsx @@ -1,8 +1,10 @@ import { useQuery } from "react-query"; import ChatItem from "../components/ChatItem"; import Header from "../components/Header"; -import { fetchMessages } from "../api"; -import { Message } from "./ChatRoom"; +import { fetchMessages, fetchUsers } from "../api"; +import { Message, User } from "./ChatRoom"; +import { useEffect, useState } from "react"; +import { useParams } from "react-router-dom"; const ChatList = () => { const { isLoading, data = [] } = useQuery( @@ -10,18 +12,65 @@ const ChatList = () => { fetchMessages ); - const chatItemData = data.reduceRight((p: Message[], n: Message) => { - if (!p.find((item) => item.sender === n.sender) && n.sender !== "김류원") { - return [...p, n]; + const [messageList, setMessageList] = useState([]); + + useEffect(() => { + let parsedMessages: Message[] = []; + + // 전체 `localStorage`에서 마지막 메세지 불러오기 + for (let i = 0; i < localStorage.length; i++) { + const key = localStorage.key(i); + if (key) { + // 각 키의 메시지 데이터를 JSON.parse로 배열로 변환 + const storedMessages = JSON.parse(localStorage.getItem(key) || "[]"); + + // 배열인지 확인 후에 `reduce` 메서드 사용 + if (Array.isArray(storedMessages)) { + const latestMessage = storedMessages.reduce( + (latest: Message | null, item: Message) => { + return !latest || new Date(item.time) > new Date(latest.time) + ? item + : latest; + }, + null + ); + + if (latestMessage) { + parsedMessages = [...parsedMessages, latestMessage]; + } + } + } } - return p; - }, []); + + const JsonMessages = data.reduceRight((p: Message[], n: Message) => { + if (!p.find((item) => item.chatRoomId === n.chatRoomId)) { + return [...p, n]; + } + return p; + }, []); + + const filteredMessages = parsedMessages + .concat(JsonMessages) + .reduce((p: Message[], n: Message) => { + if ( + !p.find((item) => item.chatRoomId === n.chatRoomId) && + !p.find((item) => item === n) + ) { + return [...p, n]; + } + return p; + }, []); + filteredMessages.sort( + (a, b): number => new Date(b.time).getTime() - new Date(a.time).getTime() + ); + setMessageList(filteredMessages); + }, [data]); // console.log(data); return (
- {chatItemData && chatItemData.map((item) => )} + {messageList && messageList.map((item) => )}
); }; diff --git a/src/pages/ChatRoom.tsx b/src/pages/ChatRoom.tsx index 367947d..9c5f681 100644 --- a/src/pages/ChatRoom.tsx +++ b/src/pages/ChatRoom.tsx @@ -6,7 +6,7 @@ import CHeader from "../components/CHeader"; import ProfileDetail from "../pages/ProfileDetail"; import { useQuery } from "react-query"; import { fetchMessages, fetchUsers } from "../api"; -import { useParams } from "react-router-dom"; +import { useLocation, useParams } from "react-router-dom"; export interface User { id: number; @@ -23,6 +23,7 @@ export interface Message { sender: string; receiver: string; time: string; + chatRoomId: string; } const ChatRoom: React.FC = () => { @@ -33,16 +34,20 @@ const ChatRoom: React.FC = () => { "chat-user", fetchUsers ); + const location = useLocation(); + const { chatRoomId } = location.state; + const params = useParams(); const [messages, setMessages] = useState([]); - const params = useParams(); const [currentUser, setCurrentUser] = useState(); const [otherUser, setOtherUser] = useState(); const ryuwon = userData.find((item) => item.name === "김류원"); - const other = userData.find((item) => item.name === params.sender); + const other = userData.find( + (item) => item.name === (chatRoomId || params.sender) + ); - const chatKey = `conversationMessages_${params.sender}`; + const chatKey = `conversationMessages_${chatRoomId || params.sender}`; useEffect(() => { if (ryuwon) { @@ -54,7 +59,7 @@ const ChatRoom: React.FC = () => { if (other) { setOtherUser(other); } - }, [params.sender, other]); + }, [params.sender, chatRoomId, other]); useEffect(() => { if (messages.length === 0) { @@ -62,7 +67,8 @@ const ChatRoom: React.FC = () => { if (storedMessages) { const parsedMessages = JSON.parse(storedMessages).filter( (item: Message) => - item.sender === params.sender || item.receiver === params.sender + item.sender === (chatRoomId || params.sender) || + item.receiver === (chatRoomId || params.sender) ); if (parsedMessages.length > 0) { setMessages(parsedMessages); @@ -72,12 +78,14 @@ const ChatRoom: React.FC = () => { const filteredMessages = messageData.filter( (item) => - (item.sender === params.sender && item.receiver === "김류원") || - (item.sender === "김류원" && item.receiver === params.sender) + (item.sender === (chatRoomId || params.sender) && + item.receiver === "김류원") || + (item.sender === "김류원" && + item.receiver === (chatRoomId || params.sender)) ); setMessages(filteredMessages); } - }, [params.sender, messageData, messages, chatKey]); + }, [chatRoomId, messageData, messages, chatKey, params.sender]); const [isProfileDetailOpen, setIsProfileDetailOpen] = useState(false); const messagesEndRef = useRef(null); @@ -113,6 +121,7 @@ const ChatRoom: React.FC = () => { : currentUser.name : "김류원", time: currentTime, + chatRoomId: `${chatRoomId || params.sender}`, }; const updatedMessages = [...messages, newMessageData]; diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 10023d6..35d164a 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -21,7 +21,7 @@ const Home = () => { }; return (
@@ -29,7 +29,7 @@ const Home = () => { {isLoading ? (
로딩중
) : ( -
+
{/* 사용자 프로필 */}
= ({ user, onClose }) => {
From ce3d2393969105df463dccd854092346ee3a7a09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=A5=98=EC=9B=90?= <2145007@ewha.ac.kr> Date: Mon, 4 Nov 2024 15:48:34 +0900 Subject: [PATCH 23/26] =?UTF-8?q?design=20:=20overflow=EC=BD=94=EB=93=9C?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/CHeader.tsx | 2 +- src/pages/Home.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/CHeader.tsx b/src/components/CHeader.tsx index a2ff6bc..4ed2c96 100644 --- a/src/components/CHeader.tsx +++ b/src/components/CHeader.tsx @@ -28,7 +28,7 @@ const ChatRoomHeader: React.FC = ({ style={{ filter: isProfileDetailOpen ? "blur(4px)" : "none" }} >
- diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 35d164a..60ee4d5 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -21,7 +21,7 @@ const Home = () => { }; return (
@@ -29,7 +29,7 @@ const Home = () => { {isLoading ? (
로딩중
) : ( -
+
{/* 사용자 프로필 */}
Date: Mon, 4 Nov 2024 16:39:06 +0900 Subject: [PATCH 24/26] =?UTF-8?q?refactor=20:=20||=20params.sender=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/ChatRoom.tsx | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/pages/ChatRoom.tsx b/src/pages/ChatRoom.tsx index f2c64d2..40dbe0f 100644 --- a/src/pages/ChatRoom.tsx +++ b/src/pages/ChatRoom.tsx @@ -36,18 +36,16 @@ const ChatRoom: React.FC = () => { ); const location = useLocation(); const params = useParams(); - const chatRoomId = location.state?.chatRoomId || params.sender; + const chatRoomId = location.state?.chatRoomId; const [messages, setMessages] = useState([]); const [currentUser, setCurrentUser] = useState(); const [otherUser, setOtherUser] = useState(); const ryuwon = userData.find((item) => item.name === "김류원"); - const other = userData.find( - (item) => item.name === (chatRoomId || params.sender) - ); + const other = userData.find((item) => item.name === chatRoomId); - const chatKey = `conversationMessages_${chatRoomId || params.sender}`; + const chatKey = `conversationMessages_${chatRoomId}`; useEffect(() => { if (ryuwon) { @@ -67,8 +65,7 @@ const ChatRoom: React.FC = () => { if (storedMessages) { const parsedMessages = JSON.parse(storedMessages).filter( (item: Message) => - item.sender === (chatRoomId || params.sender) || - item.receiver === (chatRoomId || params.sender) + item.sender === chatRoomId || item.receiver === chatRoomId ); if (parsedMessages.length > 0) { setMessages(parsedMessages); @@ -78,10 +75,8 @@ const ChatRoom: React.FC = () => { const filteredMessages = messageData.filter( (item) => - (item.sender === (chatRoomId || params.sender) && - item.receiver === "김류원") || - (item.sender === "김류원" && - item.receiver === (chatRoomId || params.sender)) + (item.sender === chatRoomId && item.receiver === "김류원") || + (item.sender === "김류원" && item.receiver === chatRoomId) ); setMessages(filteredMessages); } @@ -121,7 +116,7 @@ const ChatRoom: React.FC = () => { : currentUser.name : "김류원", time: currentTime, - chatRoomId: `${chatRoomId || params.sender}`, + chatRoomId: `${chatRoomId}`, }; const updatedMessages = [...messages, newMessageData]; From 75b347f6872938d48f321ca5848fdb069686e6fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=A5=98=EC=9B=90?= <2145007@ewha.ac.kr> Date: Mon, 4 Nov 2024 16:47:11 +0900 Subject: [PATCH 25/26] =?UTF-8?q?refactor=20:=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=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/components/MessageInput.tsx | 63 --------------------------------- 1 file changed, 63 deletions(-) diff --git a/src/components/MessageInput.tsx b/src/components/MessageInput.tsx index 76a9132..f9beacc 100644 --- a/src/components/MessageInput.tsx +++ b/src/components/MessageInput.tsx @@ -10,72 +10,12 @@ interface MessageInputProps { isProfileDetailOpen: boolean; } -interface ThumbsUpProps { - x: number; - y: number; -} - -const floatUp = keyframes` - 0% { - transform: translateY(0) scale(1); - opacity: 1; - } - 100% { - transform: translateY(-100px) scale(1.5); - opacity: 0; - } -`; - -const ThumbsUpEmoji = styled.div` - position: absolute; - left: ${(props) => props.x}px; - bottom: ${(props) => props.y}px; - font-size: 24px; - animation: ${floatUp} 1s ease-out forwards; -`; - -const ThumbsUpAnimation: React.FC = () => { - const [thumbsUp, setThumbsUp] = useState([]); - - useEffect(() => { - const interval = setInterval(() => { - setThumbsUp((prev) => prev.slice(1)); - }, 100); - - return () => clearInterval(interval); - }, []); - - const createThumbsUp = () => { - const newThumbsUp = Array(5) - .fill(null) - .map(() => ({ - x: Math.random() * 300, - y: Math.random() * 50 + 50, - })); - setThumbsUp((prev) => [...prev, ...newThumbsUp]); - }; - - return ( - <> - {thumbsUp.map((thumb, index) => ( - - 👍 - - ))} - - - ); -}; - const MessageInput: React.FC = ({ onSendMessage, isProfileDetailOpen, }) => { const [message, setMessage] = useState(""); const [isInputClicked, setIsInputClicked] = useState(false); - const [showAnimation, setShowAnimation] = useState(false); const handleSend = (): void => { if (message.trim() !== "") { @@ -94,8 +34,6 @@ const MessageInput: React.FC = ({ const handleThumbsUp = (): void => { onSendMessage("👍"); - setShowAnimation(true); - setTimeout(() => setShowAnimation(false), 1000); }; return ( @@ -135,7 +73,6 @@ const MessageInput: React.FC = ({ className="w-[28px] h-[28px]" /> - {showAnimation && }
); }; From d5bb6ece6ec34e51bd235bcf136c2850eb93d00a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=A5=98=EC=9B=90?= <2145007@ewha.ac.kr> Date: Mon, 4 Nov 2024 19:57:58 +0900 Subject: [PATCH 26/26] refactor : chatRoomId = location.state?.chatRoomId || params.sender; --- src/pages/ChatRoom.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/ChatRoom.tsx b/src/pages/ChatRoom.tsx index 40dbe0f..d5f2ee1 100644 --- a/src/pages/ChatRoom.tsx +++ b/src/pages/ChatRoom.tsx @@ -36,7 +36,7 @@ const ChatRoom: React.FC = () => { ); const location = useLocation(); const params = useParams(); - const chatRoomId = location.state?.chatRoomId; + const chatRoomId = location.state?.chatRoomId || params.sender; const [messages, setMessages] = useState([]); const [currentUser, setCurrentUser] = useState();