diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 0000000..1dc7164
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,6 @@
+{
+ "trailingComma": "es5",
+ "tabWidth": 2,
+ "semi": true,
+ "singleQuote": true
+}
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 73afbd8..4b71e47 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -17,9 +17,15 @@
"@types/react-dom": "^18.0.6",
"react": "^18.2.0",
"react-dom": "^18.2.0",
+ "react-router-dom": "^6.4.2",
"react-scripts": "5.0.1",
+ "recoil": "^0.7.5",
+ "styled-components": "^5.3.6",
"typescript": "^4.8.3",
"web-vitals": "^2.1.4"
+ },
+ "devDependencies": {
+ "@types/styled-components": "^5.1.26"
}
},
"node_modules/@adobe/css-tools": {
@@ -2140,6 +2146,29 @@
"postcss-selector-parser": "^6.0.10"
}
},
+ "node_modules/@emotion/is-prop-valid": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.0.tgz",
+ "integrity": "sha512-3aDpDprjM0AwaxGE09bOPkNxHpBd+kA6jty3RnaEXdweX1DF1U3VQpPYb0g1IStAuK7SVQ1cy+bNBBKp4W3Fjg==",
+ "dependencies": {
+ "@emotion/memoize": "^0.8.0"
+ }
+ },
+ "node_modules/@emotion/memoize": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.0.tgz",
+ "integrity": "sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA=="
+ },
+ "node_modules/@emotion/stylis": {
+ "version": "0.8.5",
+ "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz",
+ "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ=="
+ },
+ "node_modules/@emotion/unitless": {
+ "version": "0.7.5",
+ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz",
+ "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg=="
+ },
"node_modules/@eslint/eslintrc": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.2.tgz",
@@ -3083,6 +3112,14 @@
}
}
},
+ "node_modules/@remix-run/router": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.0.2.tgz",
+ "integrity": "sha512-GRSOFhJzjGN+d4sKHTMSvNeUPoZiDHWmRnXfzaxrqe7dE/Nzlc8BiMSJdLDESZlndM7jIUrZ/F4yWqVYlI0rwQ==",
+ "engines": {
+ "node": ">=14"
+ }
+ },
"node_modules/@rollup/plugin-babel": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
@@ -3757,6 +3794,16 @@
"@types/node": "*"
}
},
+ "node_modules/@types/hoist-non-react-statics": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
+ "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==",
+ "dev": true,
+ "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",
@@ -3916,6 +3963,17 @@
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz",
"integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw=="
},
+ "node_modules/@types/styled-components": {
+ "version": "5.1.26",
+ "resolved": "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.26.tgz",
+ "integrity": "sha512-KuKJ9Z6xb93uJiIyxo/+ksS7yLjS1KzG6iv5i78dhVg/X3u5t1H7juRWqVmodIdz6wGVaIApo1u01kmFRdJHVw==",
+ "dev": true,
+ "dependencies": {
+ "@types/hoist-non-react-statics": "*",
+ "@types/react": "*",
+ "csstype": "^3.0.2"
+ }
+ },
"node_modules/@types/testing-library__jest-dom": {
"version": "5.14.5",
"resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.5.tgz",
@@ -4948,6 +5006,26 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/babel-plugin-styled-components": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.0.7.tgz",
+ "integrity": "sha512-i7YhvPgVqRKfoQ66toiZ06jPNA3p6ierpfUuEWxNF+fV27Uv5gxBkf8KZLHUCc1nFA9j6+80pYoIpqCeyW3/bA==",
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.16.0",
+ "@babel/helper-module-imports": "^7.16.0",
+ "babel-plugin-syntax-jsx": "^6.18.0",
+ "lodash": "^4.17.11",
+ "picomatch": "^2.3.0"
+ },
+ "peerDependencies": {
+ "styled-components": ">= 2"
+ }
+ },
+ "node_modules/babel-plugin-syntax-jsx": {
+ "version": "6.18.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz",
+ "integrity": "sha512-qrPaCSo9c8RHNRHIotaufGbuOBN8rtdC4QrrFFc43vyWCCz7Kl7GL1PGaXtMGQZUXrkCjNEgxDfmAuAabr/rlw=="
+ },
"node_modules/babel-plugin-transform-react-remove-prop-types": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz",
@@ -5261,6 +5339,11 @@
"node": ">= 6"
}
},
+ "node_modules/camelize": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz",
+ "integrity": "sha512-W2lPwkBkMZwFlPCXhIlYgxu+7gC/NUlCtdK652DAJ1JdgV0sTrvuPFshNPrFa1TY2JOkLhgdeEBplB4ezEa+xg=="
+ },
"node_modules/caniuse-api": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz",
@@ -5700,6 +5783,14 @@
"postcss": "^8.4"
}
},
+ "node_modules/css-color-keywords": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz",
+ "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/css-declaration-sorter": {
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz",
@@ -5881,6 +5972,16 @@
"resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz",
"integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w=="
},
+ "node_modules/css-to-react-native": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.0.0.tgz",
+ "integrity": "sha512-Ro1yETZA813eoyUp2GDBhG2j+YggidUmzO1/v9eYBKR2EHVEniE2MI/NqpTQ954BMpTPZFsGNPm46qFB9dpaPQ==",
+ "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",
@@ -8188,6 +8289,11 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/hamt_plus": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/hamt_plus/-/hamt_plus-1.0.2.tgz",
+ "integrity": "sha512-t2JXKaehnMb9paaYA7J0BX8QQAY8lwfQ9Gjf4pg/mk4krt+cmwmU652HOoWonf+7+EQV97ARPMhhVgU1ra2GhA=="
+ },
"node_modules/handle-thing": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz",
@@ -8269,6 +8375,19 @@
"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==",
+ "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=="
+ },
"node_modules/hoopy": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz",
@@ -13774,6 +13893,36 @@
"node": ">=0.10.0"
}
},
+ "node_modules/react-router": {
+ "version": "6.4.2",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.4.2.tgz",
+ "integrity": "sha512-Rb0BAX9KHhVzT1OKhMvCDMw776aTYM0DtkxqUBP8dNBom3mPXlfNs76JNGK8wKJ1IZEY1+WGj+cvZxHVk/GiKw==",
+ "dependencies": {
+ "@remix-run/router": "1.0.2"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "peerDependencies": {
+ "react": ">=16.8"
+ }
+ },
+ "node_modules/react-router-dom": {
+ "version": "6.4.2",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.4.2.tgz",
+ "integrity": "sha512-yM1kjoTkpfjgczPrcyWrp+OuQMyB1WleICiiGfstnQYo/S8hPEEnVjr/RdmlH6yKK4Tnj1UGXFSa7uwAtmDoLQ==",
+ "dependencies": {
+ "@remix-run/router": "1.0.2",
+ "react-router": "6.4.2"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "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",
@@ -13878,6 +14027,25 @@
"node": ">=8.10.0"
}
},
+ "node_modules/recoil": {
+ "version": "0.7.5",
+ "resolved": "https://registry.npmjs.org/recoil/-/recoil-0.7.5.tgz",
+ "integrity": "sha512-GVShsj5+M/2GULWBs5WBJGcsNis/d3YvDiaKjYh3mLKXftjtmk9kfaQ8jwjoIXySCwn8/RhgJ4Sshwgzj2UpFA==",
+ "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.2",
"resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz",
@@ -14548,6 +14716,11 @@
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
},
+ "node_modules/shallowequal": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
+ "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ=="
+ },
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@@ -14938,6 +15111,36 @@
"webpack": "^5.0.0"
}
},
+ "node_modules/styled-components": {
+ "version": "5.3.6",
+ "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.6.tgz",
+ "integrity": "sha512-hGTZquGAaTqhGWldX7hhfzjnIYBZ0IXQXkCYdvF1Sq3DsUaLx6+NTHC5Jj1ooM2F68sBiVz3lvhfwQs/S3l6qg==",
+ "hasInstallScript": true,
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.0.0",
+ "@babel/traverse": "^7.4.5",
+ "@emotion/is-prop-valid": "^1.1.0",
+ "@emotion/stylis": "^0.8.4",
+ "@emotion/unitless": "^0.7.4",
+ "babel-plugin-styled-components": ">= 1.12.0",
+ "css-to-react-native": "^3.0.0",
+ "hoist-non-react-statics": "^3.0.0",
+ "shallowequal": "^1.1.0",
+ "supports-color": "^5.5.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/styled-components"
+ },
+ "peerDependencies": {
+ "react": ">= 16.8.0",
+ "react-dom": ">= 16.8.0",
+ "react-is": ">= 16.8.0"
+ }
+ },
"node_modules/stylehacks": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.0.tgz",
@@ -17953,6 +18156,29 @@
"integrity": "sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg==",
"requires": {}
},
+ "@emotion/is-prop-valid": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.0.tgz",
+ "integrity": "sha512-3aDpDprjM0AwaxGE09bOPkNxHpBd+kA6jty3RnaEXdweX1DF1U3VQpPYb0g1IStAuK7SVQ1cy+bNBBKp4W3Fjg==",
+ "requires": {
+ "@emotion/memoize": "^0.8.0"
+ }
+ },
+ "@emotion/memoize": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.0.tgz",
+ "integrity": "sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA=="
+ },
+ "@emotion/stylis": {
+ "version": "0.8.5",
+ "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz",
+ "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ=="
+ },
+ "@emotion/unitless": {
+ "version": "0.7.5",
+ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz",
+ "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg=="
+ },
"@eslint/eslintrc": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.2.tgz",
@@ -18631,6 +18857,11 @@
"source-map": "^0.7.3"
}
},
+ "@remix-run/router": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.0.2.tgz",
+ "integrity": "sha512-GRSOFhJzjGN+d4sKHTMSvNeUPoZiDHWmRnXfzaxrqe7dE/Nzlc8BiMSJdLDESZlndM7jIUrZ/F4yWqVYlI0rwQ=="
+ },
"@rollup/plugin-babel": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
@@ -19112,6 +19343,16 @@
"@types/node": "*"
}
},
+ "@types/hoist-non-react-statics": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
+ "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==",
+ "dev": true,
+ "requires": {
+ "@types/react": "*",
+ "hoist-non-react-statics": "^3.3.0"
+ }
+ },
"@types/html-minifier-terser": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
@@ -19271,6 +19512,17 @@
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz",
"integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw=="
},
+ "@types/styled-components": {
+ "version": "5.1.26",
+ "resolved": "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.26.tgz",
+ "integrity": "sha512-KuKJ9Z6xb93uJiIyxo/+ksS7yLjS1KzG6iv5i78dhVg/X3u5t1H7juRWqVmodIdz6wGVaIApo1u01kmFRdJHVw==",
+ "dev": true,
+ "requires": {
+ "@types/hoist-non-react-statics": "*",
+ "@types/react": "*",
+ "csstype": "^3.0.2"
+ }
+ },
"@types/testing-library__jest-dom": {
"version": "5.14.5",
"resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.5.tgz",
@@ -20016,6 +20268,23 @@
"@babel/helper-define-polyfill-provider": "^0.3.3"
}
},
+ "babel-plugin-styled-components": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.0.7.tgz",
+ "integrity": "sha512-i7YhvPgVqRKfoQ66toiZ06jPNA3p6ierpfUuEWxNF+fV27Uv5gxBkf8KZLHUCc1nFA9j6+80pYoIpqCeyW3/bA==",
+ "requires": {
+ "@babel/helper-annotate-as-pure": "^7.16.0",
+ "@babel/helper-module-imports": "^7.16.0",
+ "babel-plugin-syntax-jsx": "^6.18.0",
+ "lodash": "^4.17.11",
+ "picomatch": "^2.3.0"
+ }
+ },
+ "babel-plugin-syntax-jsx": {
+ "version": "6.18.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz",
+ "integrity": "sha512-qrPaCSo9c8RHNRHIotaufGbuOBN8rtdC4QrrFFc43vyWCCz7Kl7GL1PGaXtMGQZUXrkCjNEgxDfmAuAabr/rlw=="
+ },
"babel-plugin-transform-react-remove-prop-types": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz",
@@ -20260,6 +20529,11 @@
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
"integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="
},
+ "camelize": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz",
+ "integrity": "sha512-W2lPwkBkMZwFlPCXhIlYgxu+7gC/NUlCtdK652DAJ1JdgV0sTrvuPFshNPrFa1TY2JOkLhgdeEBplB4ezEa+xg=="
+ },
"caniuse-api": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz",
@@ -20585,6 +20859,11 @@
"postcss-selector-parser": "^6.0.9"
}
},
+ "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=="
+ },
"css-declaration-sorter": {
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz",
@@ -20692,6 +20971,16 @@
"resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz",
"integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w=="
},
+ "css-to-react-native": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.0.0.tgz",
+ "integrity": "sha512-Ro1yETZA813eoyUp2GDBhG2j+YggidUmzO1/v9eYBKR2EHVEniE2MI/NqpTQ954BMpTPZFsGNPm46qFB9dpaPQ==",
+ "requires": {
+ "camelize": "^1.0.0",
+ "css-color-keywords": "^1.0.0",
+ "postcss-value-parser": "^4.0.2"
+ }
+ },
"css-tree": {
"version": "1.0.0-alpha.37",
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz",
@@ -22366,6 +22655,11 @@
"duplexer": "^0.1.2"
}
},
+ "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=="
+ },
"handle-thing": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz",
@@ -22420,6 +22714,21 @@
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
},
+ "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==",
+ "requires": {
+ "react-is": "^16.7.0"
+ },
+ "dependencies": {
+ "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=="
+ }
+ }
+ },
"hoopy": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz",
@@ -26219,6 +26528,23 @@
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz",
"integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A=="
},
+ "react-router": {
+ "version": "6.4.2",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.4.2.tgz",
+ "integrity": "sha512-Rb0BAX9KHhVzT1OKhMvCDMw776aTYM0DtkxqUBP8dNBom3mPXlfNs76JNGK8wKJ1IZEY1+WGj+cvZxHVk/GiKw==",
+ "requires": {
+ "@remix-run/router": "1.0.2"
+ }
+ },
+ "react-router-dom": {
+ "version": "6.4.2",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.4.2.tgz",
+ "integrity": "sha512-yM1kjoTkpfjgczPrcyWrp+OuQMyB1WleICiiGfstnQYo/S8hPEEnVjr/RdmlH6yKK4Tnj1UGXFSa7uwAtmDoLQ==",
+ "requires": {
+ "@remix-run/router": "1.0.2",
+ "react-router": "6.4.2"
+ }
+ },
"react-scripts": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz",
@@ -26300,6 +26626,14 @@
"picomatch": "^2.2.1"
}
},
+ "recoil": {
+ "version": "0.7.5",
+ "resolved": "https://registry.npmjs.org/recoil/-/recoil-0.7.5.tgz",
+ "integrity": "sha512-GVShsj5+M/2GULWBs5WBJGcsNis/d3YvDiaKjYh3mLKXftjtmk9kfaQ8jwjoIXySCwn8/RhgJ4Sshwgzj2UpFA==",
+ "requires": {
+ "hamt_plus": "1.0.2"
+ }
+ },
"recursive-readdir": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz",
@@ -26789,6 +27123,11 @@
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
},
+ "shallowequal": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
+ "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ=="
+ },
"shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@@ -27081,6 +27420,23 @@
"integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==",
"requires": {}
},
+ "styled-components": {
+ "version": "5.3.6",
+ "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.6.tgz",
+ "integrity": "sha512-hGTZquGAaTqhGWldX7hhfzjnIYBZ0IXQXkCYdvF1Sq3DsUaLx6+NTHC5Jj1ooM2F68sBiVz3lvhfwQs/S3l6qg==",
+ "requires": {
+ "@babel/helper-module-imports": "^7.0.0",
+ "@babel/traverse": "^7.4.5",
+ "@emotion/is-prop-valid": "^1.1.0",
+ "@emotion/stylis": "^0.8.4",
+ "@emotion/unitless": "^0.7.4",
+ "babel-plugin-styled-components": ">= 1.12.0",
+ "css-to-react-native": "^3.0.0",
+ "hoist-non-react-statics": "^3.0.0",
+ "shallowequal": "^1.1.0",
+ "supports-color": "^5.5.0"
+ }
+ },
"stylehacks": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.0.tgz",
diff --git a/package.json b/package.json
index 665ea56..d27d5ba 100644
--- a/package.json
+++ b/package.json
@@ -12,7 +12,10 @@
"@types/react-dom": "^18.0.6",
"react": "^18.2.0",
"react-dom": "^18.2.0",
+ "react-router-dom": "^6.4.2",
"react-scripts": "5.0.1",
+ "recoil": "^0.7.5",
+ "styled-components": "^5.3.6",
"typescript": "^4.8.3",
"web-vitals": "^2.1.4"
},
@@ -39,5 +42,8 @@
"last 1 firefox version",
"last 1 safari version"
]
+ },
+ "devDependencies": {
+ "@types/styled-components": "^5.1.26"
}
}
diff --git a/public/img/0.png b/public/img/0.png
new file mode 100644
index 0000000..db5cfb9
Binary files /dev/null and b/public/img/0.png differ
diff --git a/public/img/1.png b/public/img/1.png
new file mode 100644
index 0000000..c417a5c
Binary files /dev/null and b/public/img/1.png differ
diff --git a/public/img/2.png b/public/img/2.png
new file mode 100644
index 0000000..6088d7b
Binary files /dev/null and b/public/img/2.png differ
diff --git a/public/img/3.png b/public/img/3.png
new file mode 100644
index 0000000..485787a
Binary files /dev/null and b/public/img/3.png differ
diff --git a/public/img/4.png b/public/img/4.png
new file mode 100644
index 0000000..2fb9dad
Binary files /dev/null and b/public/img/4.png differ
diff --git a/public/img/5.png b/public/img/5.png
new file mode 100644
index 0000000..55524a4
Binary files /dev/null and b/public/img/5.png differ
diff --git a/public/img/chat.png b/public/img/chat.png
new file mode 100644
index 0000000..3aaff2a
Binary files /dev/null and b/public/img/chat.png differ
diff --git a/public/img/chat2.png b/public/img/chat2.png
new file mode 100644
index 0000000..de459e0
Binary files /dev/null and b/public/img/chat2.png differ
diff --git a/public/img/find.png b/public/img/find.png
new file mode 100644
index 0000000..9ac1852
Binary files /dev/null and b/public/img/find.png differ
diff --git a/public/img/heart.png b/public/img/heart.png
new file mode 100644
index 0000000..b36b97e
Binary files /dev/null and b/public/img/heart.png differ
diff --git a/public/img/out.png b/public/img/out.png
new file mode 100644
index 0000000..3a0e09e
Binary files /dev/null and b/public/img/out.png differ
diff --git a/public/img/search.png b/public/img/search.png
new file mode 100644
index 0000000..4b154a9
Binary files /dev/null and b/public/img/search.png differ
diff --git a/public/img/setting.png b/public/img/setting.png
new file mode 100644
index 0000000..3701fef
Binary files /dev/null and b/public/img/setting.png differ
diff --git a/public/img/setting2.png b/public/img/setting2.png
new file mode 100644
index 0000000..63cd4a9
Binary files /dev/null and b/public/img/setting2.png differ
diff --git a/public/img/user.png b/public/img/user.png
new file mode 100644
index 0000000..e43f22f
Binary files /dev/null and b/public/img/user.png differ
diff --git a/public/img/user2.png b/public/img/user2.png
new file mode 100644
index 0000000..5aca4a5
Binary files /dev/null and b/public/img/user2.png differ
diff --git a/public/logo192.png b/public/logo192.png
deleted file mode 100644
index fc44b0a..0000000
Binary files a/public/logo192.png and /dev/null differ
diff --git a/public/logo512.png b/public/logo512.png
deleted file mode 100644
index a4e47a6..0000000
Binary files a/public/logo512.png and /dev/null differ
diff --git a/src/App.tsx b/src/App.tsx
index 5d90092..11854b4 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,5 +1,93 @@
+import React, { useState } from 'react';
+import InputForm from './components/InputMessage';
+import styled from 'styled-components';
+
+import MessageChatForm from './components/MessageChatForm';
+
+import { useRecoilState, useRecoilValue, RecoilRoot } from 'recoil';
+
+import useChatRoom from './hooks/useChatRoom';
+import { useEffect } from 'react';
+import { chatRoomState } from './atom';
+import { Link, useNavigate } from 'react-router-dom';
+
function App() {
- return
화이팅!!
;
+ const navigate = useNavigate();
+ const { addMsg, toggleAccount } = useChatRoom();
+ const chatRoom = useRecoilValue(chatRoomState);
+ /*
+ useEffect(() => {
+ console.log(chatRoom);
+ }, [chatRoom]);
+ */
+
+ const onCLickMe = (): void => {
+ toggleAccount(0);
+ };
+ const onCLickYou = (): void => {
+ toggleAccount(1);
+ };
+ const goToMain = () => {
+ navigate('/list');
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ );
}
+const AllTemplate = styled.div`
+ width: 350px;
+ height: 660px;
+ background-color: white;
+ border-radius: 20px;
+ box-shadow: 1px 1px 30px grey;
+ margin-top: 10%;
+ margin-bottom: 10%;
+ margin-left: 38%;
+ justify-content: center;
+ align-items: center;
+`;
+
+const ImgProfAll = styled.section`
+ display: flex;
+ flex-direction: row;
+`;
+const ImgProf = styled.img`
+ width: 100px;
+ height: 100px;
+ border-radius: 25px;
+ transition: 0.5s;
+ display: block;
+ :hover {
+ cursor: grab;
+ width: 105px;
+ height: 105px;
+ }
+`;
+
export default App;
diff --git a/src/atom.tsx b/src/atom.tsx
new file mode 100644
index 0000000..7a8812b
--- /dev/null
+++ b/src/atom.tsx
@@ -0,0 +1,82 @@
+import { atom, selector } from 'recoil';
+import messages from '../src/db/messages.json';
+import users from '../src/db/user.json';
+import {
+ IChatRoomItem,
+ IChatRoomState,
+ IUserState,
+ IChatRoomId,
+} from './interface';
+
+export const messageState = atom({
+ key: 'messages',
+ default: messages,
+});
+
+export const idFilterState = selector({
+ key: 'rooms',
+ get: ({ get }) => {
+ const filter = get(chatRoomState);
+ const ids = get(messageState);
+ const valueOb = ids.filter((va) => filter.id === va.id)[0];
+ /*if (valueOb.message.length !== 0) {
+ return valueOb;
+ } else {
+ return console.log('error');
+ }
+ */
+ return valueOb;
+ },
+ set: ({ set, get }, newValue: any) => {
+ const realData = get(messageState);
+ const filter = get(chatRoomState);
+ const realMess = realData.filter((v) => v.id === newValue.id);
+ set(messageState, newValue);
+ },
+});
+
+export const userState = atom({
+ key: 'user',
+ default: {
+ users: users,
+ mainUser: users[0],
+ },
+});
+
+export const chatRoomState = atom({
+ key: 'chatRoom',
+ default: {
+ id: 0,
+ messages: [],
+ message: [],
+ currentUser: {
+ id: 0,
+ name: '이한비',
+ currentText: '배고파...',
+ },
+ },
+});
+
+export const chatRoomId = atom({
+ key: 'chatId',
+ default: {
+ id: 1,
+ },
+});
+
+/*
+export const chatState = atom({
+ key:"chats",
+ default:[
+ {id: number,
+ text: string,
+ user: IUserInfo,
+ time: string}
+ ]
+})
+export const selectCurrentUser = selector({
+ key:"selectUser",
+ get:({get})=>{
+ const original
+ }
+})*/
diff --git a/src/components/InputMessage.tsx b/src/components/InputMessage.tsx
new file mode 100644
index 0000000..1cd62ab
--- /dev/null
+++ b/src/components/InputMessage.tsx
@@ -0,0 +1,55 @@
+import useInput from '../hooks/useInput';
+import React from 'react';
+import styled from 'styled-components';
+import { IInputForm } from '../interface';
+
+const InputForm = ({ sendMsg }: IInputForm) => {
+ const { chat, handleChange, resetChat } = useInput('');
+ const addInput = (e: React.FormEvent): void => {
+ e.preventDefault();
+ if (!chat.trim()) {
+ resetChat();
+ } else {
+ sendMsg(chat);
+ resetChat();
+ }
+ };
+
+ return (
+
+
+ 전송
+
+ );
+};
+
+const StyledAddList = styled.form`
+ display: flex;
+ text-align: center;
+ font-weight: bolder;
+ font-family: 'Jua', sans-serif;
+ padding-bottom: 10px;
+`;
+
+const StyledInput = styled.input`
+ border: 1px solid lightgrey;
+ border-radius: 10px;
+ font-family: 'Jua', sans-serif;
+ padding: 15px;
+ width: 235px;
+ height: 65px;
+ margin-left: 9px;
+ margin-top: 3px;
+`;
+const StyledButton = styled.button`
+ border: 1px solid lightgrey;
+ border-radius: 10px;
+ font-family: 'Jua', sans-serif;
+ font-size: 25px;
+ color: white;
+ background-color: #337de4;
+ margin-left: 5px;
+ margin-top: 3px;
+`;
+
+export default InputForm;
diff --git a/src/components/MessageChat.tsx b/src/components/MessageChat.tsx
new file mode 100644
index 0000000..5a5f425
--- /dev/null
+++ b/src/components/MessageChat.tsx
@@ -0,0 +1,120 @@
+import { IMessageType } from '../interface';
+import { useRecoilValue } from 'recoil';
+import { chatRoomState } from '../atom';
+import styled, { css } from 'styled-components';
+import { useParams } from 'react-router-dom';
+
+const MessageChat = ({ messages }: { messages: IMessageType }) => {
+ const { currentUser } = useRecoilValue(chatRoomState);
+
+ const isUser = messages.user.id === currentUser.id;
+
+ let params = useParams();
+ let num = params.id;
+ console.log(num);
+
+ return (
+
+
+
+
+
+ {messages.text}
+
+
+
+ {messages.time}
+
+ );
+};
+
+const MessageProfAll = styled.section`
+ display: flex;
+ flex-direction: column;
+`;
+
+const Messageimg = styled.img<{
+ isUser: boolean;
+ _id: number;
+ _currentUserId: number;
+}>`
+ width: 38px;
+ height: 38px;
+ border-radius: 50%;
+ background-size: cover;
+
+ ${({ _id, _currentUserId }) =>
+ _id === _currentUserId &&
+ css`
+ align-self: flex-end;
+ `}
+`;
+
+const MessageTime = styled.p<{ isUser: boolean }>`
+ font-size: 0.5rem;
+ color: lightgrey;
+ display: flex;
+ align-items: flex-end;
+ ${({ isUser }) =>
+ isUser
+ ? css`
+ margin-left: 0.5rem;
+ `
+ : css`
+ margin-right: 0.5rem;
+ `}
+`;
+
+const MessageBox = styled.section<{ isUser: boolean }>`
+ display: flex;
+ flex-direction: ${({ isUser }) => isUser && 'row-reverse'};
+ padding-bottom: 1rem;
+`;
+
+const MessageText = styled.p<{ isUser: boolean }>`
+ padding: 0.65rem;
+
+ font-size: 0.75rem;
+ margin: 0.4rem;
+ margin-top: 0.1px;
+ ${({ isUser }) =>
+ isUser
+ ? css`
+ border-radius: 0.5rem 0 0.5rem 0.5rem;
+ background-color: #337de4;
+ color: #ffffff;
+ font-weight: bold;
+ margin-right: 40px;
+ `
+ : css`
+ border-radius: 0 0.5rem 0.5rem 0.5rem;
+ background-color: #f1f1f3;
+ font-weight: bold;
+ margin-left: 40px;
+ `};
+`;
+
+const MessageUser = styled.p<{ isUser: boolean }>`
+ font-size: 0.5rem;
+ color: white;
+ margin: 0px;
+ display: flex;
+ ${({ isUser }) =>
+ isUser
+ ? css`
+ justify-content: flex-end;
+ margin-right: 0.2rem;
+ font-weight: bold;
+ `
+ : css`
+ margin-left: 0.2rem;
+ font-weight: bold;
+ `}
+`;
+
+export default MessageChat;
diff --git a/src/components/MessageChatForm.tsx b/src/components/MessageChatForm.tsx
new file mode 100644
index 0000000..342ae5c
--- /dev/null
+++ b/src/components/MessageChatForm.tsx
@@ -0,0 +1,81 @@
+import MessageChat from './MessageChat';
+import { useEffect, useRef } from 'react';
+import { useRecoilValue } from 'recoil';
+import { chatRoomState, idFilterState, messageState, userState } from '../atom';
+import styled from 'styled-components';
+
+import { useParams } from 'react-router-dom';
+import { IMessageType } from '../interface';
+
+const MessageChatForm = () => {
+ const messageWrapperRef = useRef(null);
+ const { currentUser } = useRecoilValue(chatRoomState);
+
+ //currentRoomId를 사용해서 room번호를 구분시켜줘야함. 이 id값은 버튼 누를때마다 바뀌어야 함!그래서 그 바뀐번호로 렌더링해줘야함
+ const messageData = useRecoilValue(messageState);
+ let params = useParams();
+
+ const num = params.id;
+ const realNum = Number(num); //나와의 채팅방하려면, -1을 없애야함
+ console.log(realNum);
+
+ //room 번호대로 라우팅 페이지 만들어서 그거대로 messageData에 접근!
+ const scrollToBottom = () => {
+ if (messageWrapperRef.current) {
+ messageWrapperRef.current.scrollTop =
+ messageWrapperRef.current.scrollHeight;
+ }
+ };
+ const realMessage = useRecoilValue(idFilterState);
+ console.log(realMessage);
+
+ useEffect(() => {
+ scrollToBottom();
+ }, [messageData]);
+
+ return (
+
+ {realMessage.messages.map((msg: IMessageType) => (
+
+
+
+ ))}
+ {realMessage.message.map((msg: IMessageType) => (
+
+
+
+ ))}
+
+ );
+};
+
+const Wrapper = styled.section`
+ display: flex;
+ flex-direction: column;
+ height: 464px;
+ overflow: auto;
+ padding: 1rem 0.8rem 0 0.8rem;
+ ::-webkit-scrollbar {
+ width: 0.9rem;
+ }
+ ::-webkit-scrollbar-thumb {
+ background-color: white;
+ border-radius: 1rem;
+ background-clip: padding-box;
+ border: 0.3rem solid transparent;
+ }
+ background: black;
+`;
+
+const MessageChatContainer = styled.section<{ isUser: boolean }>`
+ display: flex;
+ justify-content: ${({ isUser }) => isUser && 'flex-end'};
+`;
+
+export default MessageChatForm;
diff --git a/src/db/messages.json b/src/db/messages.json
new file mode 100644
index 0000000..7a4fc42
--- /dev/null
+++ b/src/db/messages.json
@@ -0,0 +1,281 @@
+[
+ {
+ "id": 0,
+ "user": [
+ {
+ "id": 22,
+ "name": "이한비",
+ "currentText": "배고파..."
+ }
+ ],
+ "messages": [
+ {
+ "id": 23,
+ "user": {
+ "id": 0,
+ "name": "이한비",
+ "currentText": "배고파..."
+ },
+ "time": "04:41",
+ "text": "과제가"
+ },
+ {
+ "id": 24,
+ "user": {
+ "id": 0,
+ "name": "이한비",
+ "currentText": "배고파..."
+ },
+ "time": "04:42",
+ "text": "너무 어려워요.."
+ }
+ ],
+ "message": [],
+ "currentUser": {
+ "id": 0,
+ "name": "이한비",
+ "currentText": "배고파..."
+ }
+ },
+ {
+ "id": 1,
+ "user": [
+ {
+ "id": 0,
+ "name": "이한비",
+ "currentText": "배고파..."
+ },
+ {
+ "id": 1,
+ "name": "문상훈",
+ "currentText": "오당기 많은 관심 부탁ㅎㅎ"
+ }
+ ],
+ "messages": [
+ {
+ "id": 1,
+ "user": {
+ "id": 0,
+ "name": "이한비",
+ "currentText": "배고파..."
+ },
+ "time": "07:41",
+ "text": "과제가"
+ },
+ {
+ "id": 2,
+ "user": {
+ "id": 0,
+ "name": "이한비",
+ "currentText": "배고파..."
+ },
+ "time": "07:42",
+ "text": "너무 어려워요.."
+ },
+ {
+ "id": 3,
+ "user": {
+ "id": 1,
+ "name": "문상훈",
+ "currentText": "오당기 많은 관심 부탁ㅎㅎ"
+ },
+ "time": "07:43",
+ "text": "개쳐발렸됴"
+ }
+ ],
+ "message": [],
+ "currentUser": {
+ "id": 0,
+ "name": "이한비",
+ "currentText": "배고파..."
+ }
+ },
+ {
+ "id": 2,
+ "user": [
+ {
+ "id": 0,
+ "name": "이한비",
+ "currentText": "배고파..."
+ },
+ { "id": 2, "name": "문쌤", "currentText": "한국지리 일타강사!" }
+ ],
+ "messages": [
+ {
+ "id": 4,
+ "user": {
+ "id": 0,
+ "name": "이한비",
+ "currentText": "배고파..."
+ },
+ "time": "08:41",
+ "text": "선생님"
+ },
+ {
+ "id": 5,
+ "user": {
+ "id": 0,
+ "name": "이한비",
+ "currentText": "배고파..."
+ },
+ "time": "08:42",
+ "text": "에어드랍 확인해보세요"
+ },
+ {
+ "id": 6,
+ "user": {
+ "id": 2,
+ "name": "문쌤",
+ "currentText": "한국지리 일타강사!"
+ },
+ "time": "08:43",
+ "text": "내가 돼지야?"
+ }
+ ],
+ "message": [],
+ "currentUser": {
+ "id": 0,
+ "name": "이한비",
+ "currentText": "배고파..."
+ }
+ },
+ {
+ "id": 3,
+ "user": [
+ {
+ "id": 0,
+ "name": "이한비",
+ "currentText": "배고파..."
+ },
+ { "id": 3, "name": "문이병", "currentText": "만년 이등병ㅋㅋ" }
+ ],
+ "messages": [
+ {
+ "id": 7,
+ "user": {
+ "id": 0,
+ "name": "이한비",
+ "currentText": "배고파..."
+ },
+ "time": "09:45",
+ "text": "문이병"
+ },
+ {
+ "id": 8,
+ "user": {
+ "id": 0,
+ "name": "이한비",
+ "currentText": "배고파..."
+ },
+ "time": "09:46",
+ "text": "일로와바"
+ },
+ {
+ "id": 9,
+ "user": { "id": 3, "name": "문이병", "currentText": "만년 이등병ㅋㅋ" },
+ "time": "09:47",
+ "text": "저한테 왜그러십니까!!!"
+ }
+ ],
+ "message": [],
+ "currentUser": {
+ "id": 0,
+ "name": "이한비",
+ "currentText": "배고파..."
+ }
+ },
+ {
+ "id": 4,
+ "user": [
+ {
+ "id": 0,
+ "name": "이한비",
+ "currentText": "배고파..."
+ },
+ { "id": 4, "name": "김인영", "currentText": "터벅터벅..." }
+ ],
+ "messages": [
+ {
+ "id": 100,
+ "user": {
+ "id": 0,
+ "name": "이한비",
+ "currentText": "배고파..."
+ },
+ "time": "11:45",
+ "text": "인영아"
+ },
+ {
+ "id": 101,
+ "user": {
+ "id": 0,
+ "name": "이한비",
+ "currentText": "배고파..."
+ },
+ "time": "11:46",
+ "text": "뭐해"
+ },
+ {
+ "id": 102,
+ "user": { "id": 4, "name": "김인영", "currentText": "터벅터벅..." },
+ "time": "11:47",
+ "text": "아 나 진짜 공부 시작하려고 했엉~"
+ }
+ ],
+ "message": [],
+ "currentUser": {
+ "id": 0,
+ "name": "이한비",
+ "currentText": "배고파..."
+ }
+ },
+ {
+ "id": 5,
+ "user": [
+ {
+ "id": 0,
+ "name": "이한비",
+ "currentText": "배고파..."
+ },
+ { "id": 5, "name": "김수혁상병", "currentText": "열심히 살자!" }
+ ],
+ "messages": [
+ {
+ "id": 200,
+ "user": {
+ "id": 0,
+ "name": "이한비",
+ "currentText": "배고파..."
+ },
+ "time": "15:45",
+ "text": "수혁아"
+ },
+ {
+ "id": 201,
+ "user": {
+ "id": 0,
+ "name": "이한비",
+ "currentText": "배고파..."
+ },
+ "time": "15:46",
+ "text": "뭐해"
+ },
+ {
+ "id": 202,
+ "user": {
+ "id": 5,
+ "name": "김수혁상병",
+ "currentText": "열심히 살자!"
+ },
+ "time": "15:47",
+ "text": "상훈이 요즘 뭐하니"
+ }
+ ],
+ "message": [],
+ "currentUser": {
+ "id": 0,
+ "name": "이한비",
+ "currentText": "배고파..."
+ }
+ }
+]
diff --git a/src/db/user.json b/src/db/user.json
new file mode 100644
index 0000000..76157ac
--- /dev/null
+++ b/src/db/user.json
@@ -0,0 +1,16 @@
+[
+ {
+ "id": 0,
+ "name": "이한비",
+ "currentText": "배고파..."
+ },
+ {
+ "id": 1,
+ "name": "문상훈",
+ "currentText": "오당기 많은 관심 부탁ㅎㅎ"
+ },
+ { "id": 2, "name": "문쌤", "currentText": "한국지리 일타강사!" },
+ { "id": 3, "name": "문이병", "currentText": "만년 이등병ㅋㅋ" },
+ { "id": 4, "name": "김인영", "currentText": "터벅터벅..." },
+ { "id": 5, "name": "김수혁상병", "currentText": "열심히 살자!" }
+]
diff --git a/src/hooks/useChatRoom.tsx b/src/hooks/useChatRoom.tsx
new file mode 100644
index 0000000..37bf187
--- /dev/null
+++ b/src/hooks/useChatRoom.tsx
@@ -0,0 +1,64 @@
+import { chatRoomState, messageState, userState, idFilterState } from '../atom';
+import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil';
+import { useParams } from 'react-router-dom';
+import { useEffect } from 'react';
+
+const useChatRoom = () => {
+ const userList = useRecoilValue(userState);
+ const [chatState, setChatState] = useRecoilState(chatRoomState);
+ const allData = useRecoilValue(messageState);
+ console.log(allData);
+
+ const [newC, setNewC] = useRecoilState(chatRoomState);
+
+ const [chatId, setChatId] = useRecoilState(idFilterState);
+ console.log(chatId);
+
+ //메세지 추가
+
+ const addMsg = (text: string): void => {
+ const curTime =
+ String(new Date().getHours()).padStart(2, '0') +
+ ':' +
+ String(new Date().getMinutes()).padStart(2, '0');
+ const messageOb = {
+ id: new Date().valueOf(),
+ user: newC.currentUser,
+ time: curTime,
+ text: text,
+ };
+ console.log(chatState);
+
+ const newOb = {
+ ...chatId,
+ currentUser: newC.currentUser,
+ message: [...chatId.message, messageOb],
+ };
+ setNewC(newOb);
+
+ console.log(newC.messages);
+ //newC or chatId인데 newC를 하면...currentUser newCcureentUser이랑 chatId의 user값이 같으면 넣어주기 아니면 x이런식으로 해보기!값을 구분해줘야됨 비어있을때도 생각해줘야함
+
+ setChatId([
+ ...allData.map((data) => (data.id === newOb.id ? newOb : data)),
+ newOb,
+ ]);
+ //객체를 그냥 선언해줘서 그걸 사용하면 오류!!해결!!
+ };
+
+ const toggleAccount = (id: number): void => {
+ let toggleId = 0;
+ if (id >= 1 && chatState.currentUser.id === userList.mainUser.id) {
+ toggleId = userList.users.findIndex((user) => user.id === id);
+ }
+ setChatState({
+ ...chatState,
+ currentUser: userList.users[toggleId],
+ });
+ console.log(id);
+ };
+
+ return { addMsg, toggleAccount };
+};
+
+export default useChatRoom;
diff --git a/src/hooks/useInput.tsx b/src/hooks/useInput.tsx
new file mode 100644
index 0000000..4beb8ee
--- /dev/null
+++ b/src/hooks/useInput.tsx
@@ -0,0 +1,17 @@
+import { useState } from 'react';
+
+const useInput = (initialValue: string) => {
+ const [chat, setChat] = useState(initialValue);
+
+ const handleChange = (e: React.ChangeEvent): void => {
+ setChat(e.target.value);
+ };
+
+ const resetChat = (): void => {
+ setChat('');
+ };
+
+ return { chat, handleChange, resetChat };
+};
+
+export default useInput;
diff --git a/src/index.tsx b/src/index.tsx
index 086c86b..1f9fa37 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -1,10 +1,28 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
+import { RecoilRoot } from 'recoil';
import App from './App';
+import { BrowserRouter, Routes, Route } from 'react-router-dom';
+import List from './pages/List';
+import MainPage from './pages/MainPage';
+import Room from './pages/Room';
+import Search from './pages/Search';
+import Setting from './pages/Setting';
+import OwnChat from './pages/OwnChat';
+const rootElement = document.getElementById('root') as HTMLElement;
+
+const root = ReactDOM.createRoot(rootElement);
-const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(
-
-
-
+
+
+
+ } />
+ } />
+ } />
+ } />
+ } />
+
+
+
);
diff --git a/src/interface/chatRoom.tsx b/src/interface/chatRoom.tsx
new file mode 100644
index 0000000..df32d5c
--- /dev/null
+++ b/src/interface/chatRoom.tsx
@@ -0,0 +1,27 @@
+import { IUserInfo } from './user';
+import { IMessageType } from './message';
+
+export interface IChatRoomItem {
+ id: number;
+ user: IUserInfo[];
+ messages: IMessageType[];
+ message: IMessageType[];
+ currentUser: IUserInfo;
+}
+
+export interface IChatRoomState {
+ message: IMessageType[];
+ currentUser: IUserInfo;
+ id: number;
+ messages: IMessageType[];
+}
+
+export interface IChatRoomId {
+ id: number;
+}
+
+export interface IChatRooms {
+ message: IMessageType[];
+ currentUser: IUserInfo;
+ id: number;
+}
diff --git a/src/interface/index.tsx b/src/interface/index.tsx
new file mode 100644
index 0000000..1c1105a
--- /dev/null
+++ b/src/interface/index.tsx
@@ -0,0 +1,19 @@
+import { IUserState, IUserInfo } from './user';
+import { IInputForm, IMessageType } from './message';
+import {
+ IChatRoomItem,
+ IChatRoomState,
+ IChatRoomId,
+ IChatRooms,
+} from './chatRoom';
+
+export type {
+ IUserState,
+ IUserInfo,
+ IInputForm,
+ IMessageType,
+ IChatRoomItem,
+ IChatRoomState,
+ IChatRoomId,
+ IChatRooms,
+};
diff --git a/src/interface/message.tsx b/src/interface/message.tsx
new file mode 100644
index 0000000..b0b8f35
--- /dev/null
+++ b/src/interface/message.tsx
@@ -0,0 +1,12 @@
+import { IUserInfo } from './user';
+
+export interface IMessageType {
+ id: number;
+ text: string;
+ user: IUserInfo;
+ time: string;
+}
+
+export interface IInputForm {
+ sendMsg: (text: string) => void;
+}
diff --git a/src/interface/user.tsx b/src/interface/user.tsx
new file mode 100644
index 0000000..5b646dc
--- /dev/null
+++ b/src/interface/user.tsx
@@ -0,0 +1,10 @@
+export interface IUserInfo {
+ id: number;
+ name: string;
+ currentText: string;
+}
+
+export interface IUserState {
+ users: IUserInfo[];
+ mainUser: IUserInfo;
+}
diff --git a/src/pages/List.tsx b/src/pages/List.tsx
new file mode 100644
index 0000000..7be514e
--- /dev/null
+++ b/src/pages/List.tsx
@@ -0,0 +1,200 @@
+import React, { useEffect, useRef } from 'react';
+import { Link, useNavigate } from 'react-router-dom';
+import styled from 'styled-components';
+import { chatRoomState, messageState, userState } from '../atom';
+import { useRecoilValue, useRecoilState, useResetRecoilState } from 'recoil';
+import useChatRoom from '../hooks/useChatRoom';
+import userEvent from '@testing-library/user-event';
+
+function List() {
+ const messageWrapperRef = useRef(null);
+ const chatRoomList = useRecoilValue(messageState);
+
+ const UserList = useRecoilValue(userState);
+ const { toggleAccount } = useChatRoom();
+
+ /* const goToChat = ({ userList }: any) => {
+ navigate(`/room/${chatRoomList.id}`);
+ };
+ */
+ const scrollToBottom = () => {
+ if (messageWrapperRef.current) {
+ messageWrapperRef.current.scrollTop =
+ messageWrapperRef.current.scrollHeight;
+ }
+ };
+ useEffect(() => {}, [chatRoomList]);
+ const ResetData = useResetRecoilState(chatRoomState);
+
+ const onClickMe = (): void => {
+ toggleAccount(0);
+ ResetData();
+ };
+
+ //recoil로 전체 가져오기!! [chatRoom,setChatROom]useRecoilState, 이한비 값은 안받는걸로 해주기
+ const TempList = ({ userList, realId }: any) => {
+ const [chatRoom, setChatRoom] = useRecoilState(messageState);
+ const testRoom = useRecoilValue(chatRoomState);
+
+ const newData = chatRoom.filter((txt) => txt.id === Number(userList.id))[0];
+
+ const realNum = newData.messages.length - 1;
+ const realNum2 = newData.message.length - 1;
+
+ if (newData.message.length === 0) {
+ return (
+
+
+
+ {userList.name}
+ {newData.messages[realNum].text}
+ {newData.messages[realNum].time}
+
+
+ );
+ } else {
+ return (
+
+
+
+ {userList.name}
+ {newData.message[realNum2].text}
+ {newData.message[realNum2].time}
+
+
+ );
+ }
+ };
+ const newUserList = UserList.users.slice(0, 6);
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 채팅
+
+ {newUserList.map((userList) => (
+
+ ))}
+
+
+
+ );
+}
+const Wrapper = styled.section`
+ display: flex;
+ flex-direction: column;
+ height: 584px;
+
+ overflow: auto;
+ padding: 0.5rem 0.8rem 0 0.2rem;
+ ::-webkit-scrollbar {
+ width: 0.9rem;
+ }
+ ::-webkit-scrollbar-thumb {
+ background-color: black;
+ border-radius: 1rem;
+ background-clip: padding-box;
+ border: 0.3rem solid transparent;
+ }
+`;
+
+const CurrentTime = styled.div`
+ font-size: 12px;
+ font-weight: light;
+ margin-left: 125px;
+ color: #d8d0d0;
+`;
+const AllTemp = styled.div`
+ display: flex;
+ flex-direction: row;
+ border-bottom: 1px solid lightgray;
+ margin-bottom: 1px;
+ padding-top: 0.05px;
+`;
+const ChatLink = styled(Link)`
+ display: flex;
+ flex-direction: column;
+
+ padding: 28px 3px;
+ align-items: left;
+
+ text-decoration: none;
+ font-size: 18px;
+ color: black;
+ font-weight: bold;
+ margin-bottom: 3px;
+`;
+
+const MainImg = styled.img`
+ width: 38px;
+ height: 38px;
+ border-radius: 50%;
+ background-size: cover;
+ margin: 15px;
+ margin-top: 15px;
+`;
+
+const CurrentText = styled.div`
+ color: #1c191976;
+ font-size: small;
+ font-weight: 650;
+`;
+const BodyTemplate = styled.div`
+ box-sizing: border-box;
+ padding: 0 3px;
+ margin-top: 1px;
+`;
+
+const RealAll = styled.div`
+ width: 350px;
+ height: 680px;
+ background-color: white;
+ border-radius: 25px;
+ box-shadow: 1px 1px 30px grey;
+ margin-top: 10%;
+ margin-bottom: 10%;
+ margin-left: 38%;
+ display: grid;
+ grid-template-columns: 68px auto;
+`;
+const SideTemplate = styled.div`
+ background-color: rgb(230, 230, 230);
+ border-right: 1px solid rgb(223, 223, 223);
+ border-radius: 25px 0px 0px 25px;
+ display: block;
+`;
+const ShowImg = styled.img`
+ width: 58px;
+ height: 58px;
+ padding: 28px 15px;
+ border-radius: 50%;
+ background-size: cover;
+`;
+
+const AllTemplate = styled.div`
+ width: 350px;
+ height: 660px;
+ background-color: white;
+ border-radius: 20px;
+ box-shadow: 1px 1px 30px grey;
+ margin-top: 10%;
+ margin-bottom: 10%;
+ margin-left: 38%;
+ justify-content: center;
+ align-items: center;
+`;
+
+export default List;
diff --git a/src/pages/MainPage.tsx b/src/pages/MainPage.tsx
new file mode 100644
index 0000000..cc7769e
--- /dev/null
+++ b/src/pages/MainPage.tsx
@@ -0,0 +1,188 @@
+import React, { useState } from 'react';
+import { Link, useNavigate } from 'react-router-dom';
+import { useRecoilValue, useResetRecoilState } from 'recoil';
+import styled from 'styled-components';
+import { chatRoomState, userState } from '../atom';
+import useChatRoom from '../hooks/useChatRoom';
+
+function MainPage() {
+ const navigate = useNavigate();
+ const UserList = useRecoilValue(userState);
+
+ const goToList = () => {
+ navigate('/Search');
+ };
+
+ //css공통된 요소 많은것 props로 넘기는걸로 바꾸기!
+ const TempList = ({ userList, realId }: any) => {
+ if (userList.id !== 0) {
+ return (
+
+
+
+
+ {userList.name}
+ {userList.currentText}
+
+
+
+ );
+ } else {
+ return (
+
+
+
+
+ {userList.name}
+ {userList.currentText}
+
+
+
친구{UserList.users.length - 1}
+
+ );
+ }
+ };
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 친구
+
+
+
+
+ {UserList.users.map((userList) => (
+
+ ))}
+
+
+
+ );
+}
+const AllTemp = styled.div`
+ display: flex;
+ flex-direction: row;
+`;
+const CurrentText = styled.div`
+ font-size: 11px;
+ color: grey;
+ font-weight: bold;
+`;
+const FriendNum = styled.h6`
+ margin-top: 10px;
+ color: grey;
+ margin-bottom: 20px;
+`;
+const ShowImg2 = styled.img`
+ width: 60px;
+ height: 60px;
+ border-radius: 50%;
+ background-size: cover;
+ margin: 0 15px;
+ border-radius: 10px;
+`;
+const HeaderTemplate = styled.div`
+ display: flex;
+ flex-direction: row;
+`;
+const FindButton = styled.img`
+ width: 18px;
+ height: 18px;
+ margin-left: 200px;
+ margin-top: 18px;
+`;
+
+const MainImg = styled.img`
+ width: 38px;
+ height: 38px;
+ border-radius: 50%;
+ background-size: cover;
+ margin: 15px;
+ margin-top: 15px;
+`;
+const ShowImg = styled.img`
+ width: 48px;
+ height: 48px;
+ border-radius: 50%;
+ background-size: cover;
+ margin: 0 15px;
+ border-radius: 10px;
+`;
+const MainId = styled(Link)`
+ display: flex;
+ -webkit-box-align: center;
+ align-items: center;
+ padding: 30px 25px;
+ text-decoration: none;
+ border-bottom: 1px solid grey;
+ padding-bottom: 10px;
+ padding-left: 1px;
+ box-sizing: border-box;
+ color: black;
+ border-radius: 3px;
+ border-bottom-color: #66616190;
+
+ /*margin: 0px 10px;*/
+`;
+const OneId = styled(Link)`
+ display: flex;
+ flex-direction: column;
+ padding: 6px;
+ -webkit-box-align: center;
+ align-items: left;
+ padding-bottom: 20px;
+ text-decoration: none;
+
+ color: black;
+`;
+const BodyTemplate = styled.div`
+ box-sizing: border-box;
+ padding: 0 3px;
+`;
+const RealAll = styled.div`
+ width: 350px;
+ height: 680px;
+ background-color: white;
+ border-radius: 25px;
+ box-shadow: 1px 1px 30px grey;
+ margin-top: 10%;
+ margin-bottom: 10%;
+ margin-left: 38%;
+ display: grid;
+ grid-template-columns: 68px auto;
+`;
+const SideTemplate = styled.div`
+ background-color: rgb(230, 230, 230);
+ border-right: 1px solid rgb(223, 223, 223);
+ border-radius: 25px 0px 0px 25px;
+ display: block;
+`;
+
+const AllTemplate = styled.div`
+ width: 350px;
+ height: 660px;
+ background-color: white;
+ border-radius: 20px;
+ box-shadow: 1px 1px 30px grey;
+ margin-top: 10%;
+ margin-bottom: 10%;
+ margin-left: 38%;
+ justify-content: center;
+ align-items: center;
+`;
+
+export default MainPage;
diff --git a/src/pages/OwnChat.tsx b/src/pages/OwnChat.tsx
new file mode 100644
index 0000000..7ca2c51
--- /dev/null
+++ b/src/pages/OwnChat.tsx
@@ -0,0 +1,62 @@
+import InputForm from '../components/InputMessage';
+import styled from 'styled-components';
+import MessageChatForm from '../components/MessageChatForm';
+
+import useChatRoom from '../hooks/useChatRoom';
+
+import { useNavigate } from 'react-router-dom';
+
+function OwnChat() {
+ const navigate = useNavigate();
+ const { addMsg, toggleAccount } = useChatRoom();
+
+ const goToMain = () => {
+ toggleAccount(0);
+ navigate('/');
+ };
+ return (
+
+
+
+
+
+
+
+
+
+
+ );
+}
+const MoveButton = styled.img`
+ width: 30px;
+ height: 30px;
+ margin-left: 250px;
+`;
+const AllTemplate = styled.div`
+ width: 350px;
+ height: 660px;
+ background-color: white;
+ border-radius: 20px;
+ box-shadow: 1px 1px 30px grey;
+ margin-top: 10%;
+ margin-bottom: 10%;
+ margin-left: 38%;
+ justify-content: center;
+ align-items: center;
+`;
+
+const ImgProfAll = styled.section`
+ display: flex;
+ flex-direction: row;
+`;
+const ImgProf = styled.img`
+ width: 53px;
+ height: 53px;
+ border-radius: 25px;
+ transition: 0.5s;
+ display: block;
+`;
+
+export default OwnChat;
diff --git a/src/pages/Room.tsx b/src/pages/Room.tsx
new file mode 100644
index 0000000..cf2d9d8
--- /dev/null
+++ b/src/pages/Room.tsx
@@ -0,0 +1,100 @@
+import React, { useState } from 'react';
+import InputForm from '../components/InputMessage';
+import styled from 'styled-components';
+import MessageChatForm from '../components/MessageChatForm';
+import { useRecoilState, useRecoilValue } from 'recoil';
+
+import useChatRoom from '../hooks/useChatRoom';
+import { useEffect } from 'react';
+import { chatRoomState, messageState, userState } from '../atom';
+import { useNavigate, useParams } from 'react-router-dom';
+import { idText } from 'typescript';
+
+function Room() {
+ const navigate = useNavigate();
+ const { addMsg, toggleAccount } = useChatRoom();
+
+ const UserList = useRecoilValue(userState);
+ const [id, setId] = useRecoilState(chatRoomState);
+
+ let params = useParams();
+
+ let num = params.id;
+
+ const realNum = Number(num);
+
+ useEffect(() => {
+ setId((prevId) => {
+ const variable = { ...prevId };
+ variable.id = realNum;
+
+ return { ...variable };
+ });
+ }, []);
+
+ const onCLickYou = (): void => {
+ toggleAccount(realNum);
+ };
+
+ const goToMain = () => {
+ toggleAccount(0);
+ navigate('/list');
+ };
+
+ return (
+
+
+
+
+
+
+ {UserList.users[realNum].name}
+
+
+
+
+ );
+}
+const Text = styled.div`
+ margin-top: 1px;
+ margin-left: 10px;
+ font-size: 13px;
+`;
+const MoveButton = styled.img`
+ width: 30px;
+ height: 30px;
+ margin-left: 250px;
+`;
+const AllTemplate = styled.div`
+ width: 350px;
+ height: 660px;
+ background-color: white;
+ border-radius: 20px;
+ box-shadow: 1px 1px 30px grey;
+ margin-top: 10%;
+ margin-bottom: 10%;
+ margin-left: 38%;
+ justify-content: center;
+ align-items: center;
+`;
+
+const ImgProfAll = styled.section`
+ display: flex;
+ flex-direction: row;
+`;
+const ImgProf = styled.img`
+ width: 53px;
+ height: 53px;
+ border-radius: 25px;
+ transition: 0.5s;
+ display: block;
+ margin-left: 5px;
+ margin-top: 5px;
+ margin-bottom: 0.05px;
+`;
+
+export default Room;
diff --git a/src/pages/Search.tsx b/src/pages/Search.tsx
new file mode 100644
index 0000000..83aee94
--- /dev/null
+++ b/src/pages/Search.tsx
@@ -0,0 +1,157 @@
+import React, { useState } from 'react';
+import { Link, useNavigate } from 'react-router-dom';
+import { useRecoilValue } from 'recoil';
+import styled from 'styled-components';
+import { userState } from '../atom';
+
+function Search() {
+ const navigate = useNavigate();
+ const UserList = useRecoilValue(userState);
+
+ const [search, setSearch] = useState('');
+ const onChange = (e: React.ChangeEvent) => {
+ setSearch(e.target.value);
+ };
+ const filterName = UserList.users.filter((p) => {
+ return p.name.replace(' ', '').includes(search);
+ });
+ const goToMain = () => {
+ navigate('/');
+ };
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 친구
+
+
+
+
+ {filterName.map((m) => (
+
+
+
+ {m.name}
+ {m.currentText}
+
+
+ ))}
+
+
+
+ );
+}
+const AllTemp = styled.div`
+ display: flex;
+ flex-direction: row;
+ border-bottom: 1px solid lightgray;
+ padding-bottom: 10px;
+ padding-top: 10px;
+`;
+const CurrentText = styled.div`
+ font-size: 11px;
+ color: grey;
+ font-weight: bold;
+`;
+const InputName = styled.input`
+ background-color: rgb(230, 230, 230);
+ border: 1px solid rgb(240, 240, 240);
+ font-size: 12px;
+ padding: 3px 10px;
+ box-sizing: border-box;
+ width: calc(100% - 36px);
+ margin: 9px 18px;
+ border-radius: 20px 20px 20px 20px;
+`;
+const HeaderTemplate = styled.div`
+ display: flex;
+ flex-direction: row;
+ margin-left: 3px;
+`;
+const FindButton = styled.img`
+ width: 18px;
+ height: 18px;
+ margin-left: 200px;
+ margin-top: 18px;
+`;
+
+const MainImg = styled.img`
+ width: 38px;
+ height: 38px;
+ border-radius: 50%;
+ background-size: cover;
+ margin: 15px;
+ margin-top: 15px;
+`;
+const ShowImg = styled.img`
+ width: 48px;
+ height: 48px;
+ border-radius: 50%;
+ background-size: cover;
+ //margin: 0 15px;
+ //margin-left: -5px;
+ //argin-right: 5px;
+ border-radius: 3px;
+ padding-left: 15px;
+ padding-right: 9px;
+`;
+const MainId = styled(Link)`
+ display: flex;
+ flex-direction: column;
+ -webkit-box-align: center;
+ align-items: left;
+ //padding: 30px 25px;
+ //padding-left: 5px;
+ padding-bottom: 10px;
+ //padding-top: 15px;
+ text-decoration: none;
+
+ box-sizing: border-box;
+ color: black;
+ border-radius: 3px;
+
+ /*margin: 0px 10px;*/
+`;
+
+const BodyTemplate = styled.div`
+ box-sizing: border-box;
+ padding: 3 2px;
+`;
+const RealAll = styled.div`
+ width: 350px;
+ height: 680px;
+ background-color: white;
+ border-radius: 25px;
+ box-shadow: 1px 1px 30px grey;
+ margin-top: 10%;
+ margin-bottom: 10%;
+ margin-left: 38%;
+ display: grid;
+ grid-template-columns: 68px auto;
+`;
+const SideTemplate = styled.div`
+ background-color: rgb(230, 230, 230);
+ border-right: 1px solid rgb(223, 223, 223);
+ display: block;
+ border-radius: 25px 0px 0px 25px;
+`;
+
+export default Search;
diff --git a/src/pages/Setting.tsx b/src/pages/Setting.tsx
new file mode 100644
index 0000000..f3449a4
--- /dev/null
+++ b/src/pages/Setting.tsx
@@ -0,0 +1,93 @@
+import React, { useEffect } from 'react';
+import { Link, useNavigate } from 'react-router-dom';
+import styled from 'styled-components';
+import { chatRoomState, messageState, userState } from '../atom';
+import { useRecoilValue, useRecoilState, useResetRecoilState } from 'recoil';
+import useChatRoom from '../hooks/useChatRoom';
+
+function Setting() {
+ const goToGit = () => {};
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Setting
+
+
+ Github
+
+
+
+
+ tistory
+
+
+
+
+ );
+}
+const PageOut = styled.a`
+ text-decoration-line: none;
+ padding-right: 50px;
+ align-items: center;
+ display: flex;
+ flex-direction: column;
+ margin-bottom: 5px;
+ font-weight: bolder;
+ color: black;
+`;
+const ComImg = styled.img`
+ width: 50px;
+ height: 50px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ //margin-left: 100px;
+`;
+
+const MainImg = styled.img`
+ width: 38px;
+ height: 38px;
+ border-radius: 50%;
+ background-size: cover;
+ margin: 15px;
+ margin-top: 15px;
+`;
+
+const BodyTemplate = styled.div`
+ box-sizing: border-box;
+ padding: 0 3px;
+`;
+
+const RealAll = styled.div`
+ width: 350px;
+ height: 680px;
+ background-color: white;
+ border-radius: 25px;
+ box-shadow: 1px 1px 30px grey;
+ margin-top: 10%;
+ margin-bottom: 10%;
+ margin-left: 38%;
+ display: grid;
+ grid-template-columns: 68px auto;
+`;
+const SideTemplate = styled.div`
+ background-color: rgb(230, 230, 230);
+ border-right: 1px solid rgb(223, 223, 223);
+ border-radius: 25px 0px 0px 25px;
+ display: block;
+`;
+
+export default Setting;