diff --git a/.gitignore b/.gitignore
index 3c3629e..e1cc852 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,6 @@
node_modules
+
+.env
+
+.env.development
+.idea
diff --git a/package-lock.json b/package-lock.json
index ae26def..a207bea 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,6 +11,7 @@
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.3.0",
"@testing-library/user-event": "^13.5.0",
+ "prettier": "^2.7.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.3.0",
@@ -13564,6 +13565,20 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/prettier": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
+ "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
+ "bin": {
+ "prettier": "bin-prettier.js"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
"node_modules/pretty-bytes": {
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz",
@@ -26382,6 +26397,11 @@
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="
},
+ "prettier": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
+ "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g=="
+ },
"pretty-bytes": {
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz",
diff --git a/package.json b/package.json
index 45ee750..aecd253 100644
--- a/package.json
+++ b/package.json
@@ -6,6 +6,7 @@
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.3.0",
"@testing-library/user-event": "^13.5.0",
+ "prettier": "^2.7.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.3.0",
diff --git a/src/hooks/user/useSetUser.js b/src/hooks/user/useSetUser.js
new file mode 100644
index 0000000..d3e8a5e
--- /dev/null
+++ b/src/hooks/user/useSetUser.js
@@ -0,0 +1,7 @@
+import { useSetRecoilState } from "recoil";
+import userState from "../../state/user";
+
+const useSetUser = () => {
+ return useSetRecoilState(userState);
+};
+export default useSetUser;
diff --git a/src/hooks/user/useUserLogin.js b/src/hooks/user/useUserLogin.js
new file mode 100644
index 0000000..828460d
--- /dev/null
+++ b/src/hooks/user/useUserLogin.js
@@ -0,0 +1,26 @@
+import {
+ requestAccessTokenAPI,
+ requestLoginAPI,
+} from "../../pages/solutionReportPage/utils/gitHubLogin";
+import useSetUser from "./useSetUser";
+import { useState } from "react";
+import useUserValue from "./useUserValue";
+
+const useUserLogin = () => {
+ const [isLoggedIn, setIsLoggedIn] = useState(false);
+ const userInfo = useUserValue();
+ const setUserInfo = useSetUser();
+ const requestLogin = (code) => {
+ requestAccessTokenAPI(code)
+ .then((response) => requestLoginAPI(response.access_token))
+ .then((response) => {
+ if (response.hasOwnProperty("login")) {
+ setIsLoggedIn(true);
+ setUserInfo(response);
+ }
+ if (userInfo) setIsLoggedIn(true);
+ });
+ };
+ return { isLoggedIn, requestLogin };
+};
+export default useUserLogin;
diff --git a/src/hooks/user/useUserProfile.js b/src/hooks/user/useUserProfile.js
new file mode 100644
index 0000000..d5ae9ea
--- /dev/null
+++ b/src/hooks/user/useUserProfile.js
@@ -0,0 +1,11 @@
+import useUserValue from "./useUserValue";
+
+const useUserProfile = () => {
+ const user = useUserValue();
+ return {
+ profileImg: user.avatar_url ?? "",
+ username: user.name ?? "",
+ gitHubUrl: user.html_url ?? "",
+ };
+};
+export default useUserProfile;
diff --git a/src/hooks/user/useUserValue.js b/src/hooks/user/useUserValue.js
new file mode 100644
index 0000000..7db3079
--- /dev/null
+++ b/src/hooks/user/useUserValue.js
@@ -0,0 +1,7 @@
+import { useRecoilValue } from "recoil";
+import userState from "../../state/user";
+
+const useUserValue = () => {
+ return useRecoilValue(userState);
+};
+export default useUserValue;
diff --git a/src/pages/errorReportPage/ErrorReport.js b/src/pages/errorReportPage/ErrorReport.js
index a83763c..4fba4b6 100644
--- a/src/pages/errorReportPage/ErrorReport.js
+++ b/src/pages/errorReportPage/ErrorReport.js
@@ -21,10 +21,13 @@ export default function ErrorReport() {
const [questionName, setQuestionName] = useState("");
const [detailContent, setDetailContent] = useState("");
- const isQuestionNameVisible = errorCategory !== "" && errorCategory !== "error-notCopied";
+ const isQuestionNameVisible =
+ errorCategory !== "" && errorCategory !== "error-notCopied";
const isDetailContentVisible =
- errorCategory !== "" && (errorCategory !== "error-wrongAnswer" || questionName !== "");
- const isSubmitBtnDisabled = errorCategory === "error-other" && detailContent === "";
+ errorCategory !== "" &&
+ (errorCategory !== "error-wrongAnswer" || questionName !== "");
+ const isSubmitBtnDisabled =
+ errorCategory === "error-other" && detailContent === "";
function handleOtherErrorBtnClick() {
setSubmitted(false);
@@ -56,14 +59,19 @@ export default function ErrorReport() {
{submitted ? (
<>
제보해주셔서 감사합니다.
- 다른 오류 제보
+
+ 다른 오류 제보
+
>
) : (
오류 유형
-
diff --git a/src/pages/solutionReportPage/SolutionReport.js b/src/pages/solutionReportPage/SolutionReport.js
index b919157..7cd7f9b 100644
--- a/src/pages/solutionReportPage/SolutionReport.js
+++ b/src/pages/solutionReportPage/SolutionReport.js
@@ -1,31 +1,41 @@
-import { useState } from "react";
+import { useEffect, useState } from "react";
import styled from "styled-components";
import Header from "../../components/Header";
import {
- ThanksMsg,
- OtherReportBtn,
+ InputLabel,
MainContetnWrapper,
+ OtherReportBtn,
StepByStepInputItem,
- InputLabel,
- TextInput,
- QuestionList,
- QuestionItem,
- QuestionBtn,
- TextArea,
SubmitBtn,
+ TextArea,
+ TextInput,
+ ThanksMsg,
} from "../../style/styledComponents";
import gitHubLogoSrc from "../../images/github-logo-white.png";
+import { useSearchParams } from "react-router-dom";
+import { LOGIN_URL } from "./utils/gitHubLogin";
+import useUserProfile from "../../hooks/user/useUserProfile";
+import useUserLogin from "../../hooks/user/useUserLogin";
export default function SolutionReport() {
const [submitted, setSubmitted] = useState(false);
const [questionName, setQuestionName] = useState("");
const [detailContent, setDetailContent] = useState("");
+ const [searchParams, setSearchParams] = useSearchParams();
+ const userInfo = useUserProfile();
+ const { isLoggedIn, requestLogin } = useUserLogin();
+ useEffect(() => {
+ if (searchParams.get("code")) {
+ const code = searchParams.get("code");
+ requestLogin(code);
+ }
+ }, [searchParams]);
+ const handleGitHubLogin = async () => {};
const isDetailContentVisible = questionName !== "";
const isSubmitBtnDisabled = detailContent === "";
function handleOtherSolutionBtnClick() {
- console.log("!");
setSubmitted(false);
setQuestionName("");
setDetailContent("");
@@ -50,7 +60,9 @@ export default function SolutionReport() {
{submitted ? (
<>
제보해주셔서 감사합니다.
- 다른 정답 제보
+
+ 다른 정답 제보
+
>
) : (
@@ -61,40 +73,55 @@ export default function SolutionReport() {
placeholder="문제 이름을 검색하세요."
defaultValue={questionName}
onInput={handleQuestionNameInput}
- >
-
-
- 1번문제
-
-
- 2번문제
-
-
- 3번문제
-
-
- 4번문제
-
-
- 5번문제
-
-
- 6번문제
-
-
- 7번문제
-
-
- 8번문제
-
-
+ />
+ {/**/}
+ {/* */}
+ {/* 1번문제*/}
+ {/* */}
+ {/* */}
+ {/* 2번문제*/}
+ {/* */}
+ {/* */}
+ {/* 3번문제*/}
+ {/* */}
+ {/* */}
+ {/* 4번문제*/}
+ {/* */}
+ {/* */}
+ {/* 5번문제*/}
+ {/* */}
+ {/* */}
+ {/* 6번문제*/}
+ {/* */}
+ {/* */}
+ {/* 7번문제*/}
+ {/* */}
+ {/* */}
+ {/* 8번문제*/}
+ {/* */}
+ {/**/}
- {isDetailContentVisible && (
+ {/*isDetailContentVisible &&*/}
+ {
<>
기여자 등록
- GitHub 로그인
+ {isLoggedIn ? (
+
+ 이름: {userInfo.username}
+ 이미지: {userInfo.profileImg}
+
+ ) : (
+
+
+ GitHub 로그인
+
+
+ )}
내용
@@ -104,7 +131,7 @@ export default function SolutionReport() {
cols="100"
onInput={handleDetailContentInput}
defaultValue={detailContent}
- >
+ />
@@ -117,7 +144,7 @@ export default function SolutionReport() {
>
- )}
+ }
)}
>
@@ -139,7 +166,6 @@ const GitHubLoginBtn = styled.button`
background-repeat: no-repeat;
cursor: pointer;
`;
-
// const Msg = styled.span`
// color: ${(props) => props.theme.programmersBlue};
// `;
diff --git a/src/pages/solutionReportPage/utils/gitHubLogin.js b/src/pages/solutionReportPage/utils/gitHubLogin.js
new file mode 100644
index 0000000..e2e39b6
--- /dev/null
+++ b/src/pages/solutionReportPage/utils/gitHubLogin.js
@@ -0,0 +1,35 @@
+export const LOGIN_URL =
+ "https://github.com/login/oauth/authorize?client_id=" +
+ process.env.REACT_APP_CLIENT_ID;
+
+export const requestAccessTokenAPI = async (code) => {
+ try {
+ const client_secret = process.env.REACT_APP_CLIENT_SECRET;
+ const client_id = process.env.REACT_APP_CLIENT_ID;
+ const response = await fetch(
+ `https://github.com/login/oauth/access_token?client_id=${client_id}&client_secret=${client_secret}&code=${code}`,
+ {
+ method: "POST",
+ headers: {
+ Accept: "application/json",
+ },
+ }
+ );
+ return response.json();
+ } catch (e) {
+ console.log(e);
+ }
+};
+export const requestLoginAPI = async (access_token) => {
+ try {
+ const userInfo = await fetch("https://api.github.com/user", {
+ headers: {
+ Accept: "application/vnd.github+json",
+ Authorization: `token ${access_token}`,
+ },
+ });
+ return userInfo.json();
+ } catch (e) {
+ console.log(e);
+ }
+};
diff --git a/src/state/user.js b/src/state/user.js
new file mode 100644
index 0000000..71fd64d
--- /dev/null
+++ b/src/state/user.js
@@ -0,0 +1,48 @@
+import { atom } from "recoil";
+import { sessionStorageEffect } from "./utils/sessionStorageEffect";
+
+const userState = atom({
+ key: "userState",
+ default: {},
+ effects: [sessionStorageEffect("userState")],
+});
+export default userState;
+
+const userObject = {
+ avatar_url: "https://avatars.githubusercontent.com/u/54318460?v=4",
+ bio: null,
+ blog: "",
+ company: null,
+ created_at: "2019-08-20T13:16:15Z",
+ email: "codeisneverodd@gmail.com",
+ events_url: "https://api.github.com/users/codeisneverodd/events{/privacy}",
+ followers: 73,
+ followers_url: "https://api.github.com/users/codeisneverodd/followers",
+ following: 168,
+ following_url:
+ "https://api.github.com/users/codeisneverodd/following{/other_user}",
+ gists_url: "https://api.github.com/users/codeisneverodd/gists{/gist_id}",
+ gravatar_id: "",
+ hireable: null,
+ html_url: "https://github.com/codeisneverodd",
+ id: 54318460,
+ location: null,
+ login: "codeisneverodd",
+ name: "codeisneverodd",
+ node_id: "MDQ6VXNlcjU0MzE4NDYw",
+ organizations_url: "https://api.github.com/users/codeisneverodd/orgs",
+ public_gists: 1,
+ public_repos: 9,
+ received_events_url:
+ "https://api.github.com/users/codeisneverodd/received_events",
+ repos_url: "https://api.github.com/users/codeisneverodd/repos",
+ site_admin: false,
+ starred_url:
+ "https://api.github.com/users/codeisneverodd/starred{/owner}{/repo}",
+ subscriptions_url:
+ "https://api.github.com/users/codeisneverodd/subscriptions",
+ twitter_username: null,
+ type: "User",
+ updated_at: "2022-07-15T04:07:10Z",
+ url: "https://api.github.com/users/codeisneverodd",
+};
diff --git a/src/state/utils/sessionStorageEffect.js b/src/state/utils/sessionStorageEffect.js
new file mode 100644
index 0000000..f994c05
--- /dev/null
+++ b/src/state/utils/sessionStorageEffect.js
@@ -0,0 +1,14 @@
+export const sessionStorageEffect =
+ (key) =>
+ ({ setSelf, onSet }) => {
+ const savedValue = sessionStorage.getItem(key);
+ if (savedValue != null) {
+ setSelf(JSON.parse(savedValue));
+ }
+
+ onSet((newValue, _, isReset) => {
+ isReset
+ ? sessionStorage.removeItem(key)
+ : sessionStorage.setItem(key, JSON.stringify(newValue));
+ });
+ };