Skip to content

Commit

Permalink
Merge pull request #74 from Goorm-Lucky7/dev
Browse files Browse the repository at this point in the history
[Deploy] version 1
  • Loading branch information
kangsinbeom authored May 20, 2024
2 parents 517c46b + 6ef5e63 commit b519ba6
Show file tree
Hide file tree
Showing 85 changed files with 1,189 additions and 533 deletions.
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link rel="icon" type="image/svg+xml" href="/src/assets/images/TT.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ThinkTank</title>
</head>
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"dev": "vite --host",
"build": "tsc && vite build",
"preview": "vite preview",
"format": "prettier --check ./src",
Expand Down
35 changes: 16 additions & 19 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,28 @@
import { Route, Routes, useNavigate } from 'react-router-dom';
import { routers } from './routes';
import Layout from './routes/Layout';
import { Suspense } from 'react';
import { ErrorBoundary } from './components/shared';
import ProtectedRoute from './routes/protectedRoute';
import { ErrorBoundary } from './components/shared';

function App() {
const navigate = useNavigate();
return (
<ErrorBoundary navigate={navigate}>
<Suspense fallback={<>loading</>}>
<Routes>
<Route path="/" element={<Layout />}>
{routers.map(({ path, element: Component, isProtected }) =>
isProtected ? (
<Route
key={path}
path={path}
element={<ProtectedRoute element={<Component />} />}
/>
) : (
<Route key={path} path={path} element={<Component />} />
),
)}
</Route>
</Routes>
</Suspense>
<Routes>
<Route path="/" element={<Layout />}>
{routers.map(({ path, element: Component, isProtected }) =>
isProtected ? (
<Route
key={path}
path={path}
element={<ProtectedRoute element={<Component />} />}
/>
) : (
<Route key={path} path={path} element={<Component />} />
),
)}
</Route>
</Routes>
</ErrorBoundary>
);
}
Expand Down
26 changes: 15 additions & 11 deletions src/apis/article.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { ArticleDetail, ArticleList } from '@/types';
import instance from './instance';

export const getArticle = async (postId: string) => {
const response = await instance.get(`/api/posts/${postId}`);
return response.data as ArticleDetail;
Expand Down Expand Up @@ -31,15 +30,20 @@ export const postArticle = async (
return response?.data;
};

export const postCheck = async (
formData: Pick<ArticleDetail, 'language' | 'code'>,
postId: string,
) => {
const newFormData = {
code: formData.code,
language: formData.language,
postId,
};
const response = await instance.post('api/posts/submit', newFormData);
interface postCheckState {
code: string;
language: string;
postId: string;
}

export const postCheck = async (formData: postCheckState) => {
const response = await instance.post('api/posts/submit', formData);
return response.data;
};

export const deleteArticle = async (postId: number): Promise<void> => {
const response = await instance.delete('/api/posts', {
data: { postId: postId },
});
return response.data;
};
15 changes: 2 additions & 13 deletions src/apis/auth.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,8 @@


import { getAccess } from '@/hooks/auth/useLocalStorage';
import instance from './instance';

export const getNewToken = async () => {
const expiredToken = getAccess();

try {
const response = await instance.post('/api/reissue', { expiredToken: expiredToken });
const newAccessToken = response.data.accessToken;
console.log('토큰 재발급 성공', newAccessToken);
return newAccessToken;
} catch (error) {
console.error('토큰 재발급 실패', error);
throw error;
}
const response = await instance.post('/api/reissue', { expiredToken: expiredToken });
return response.data.accessToken;
};

9 changes: 8 additions & 1 deletion src/apis/comment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,17 @@ import instance from './instance';

export const getComments = async (postId: string) => {
const response = await instance.get(`api/posts/${postId}/comments`);
return response.data as CommentDate[];
return response.data as CommentDate;
};

export const postComment = async (comment: string, postId: string) => {
const response = await instance.post('/api/comments', { postId, content: comment });
return response.data;
};

export const deleteComment = async (postId: string, commentId: number) => {
const response = await instance.delete('/api/comments', {
data: { postId, commentId },
});
return response.data;
};
20 changes: 6 additions & 14 deletions src/apis/instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import axios from 'axios';
import { getNewToken } from './auth';
import { getAccess, setAccess } from '@/hooks/auth/useLocalStorage';


const instance = axios.create({
baseURL: import.meta.env.VITE_BASE_URL,
headers: {
Expand All @@ -24,23 +23,16 @@ instance.interceptors.response.use(
const originalRequest = error.config;
const access = getAccess();
if (error.response?.status === 401 && !originalRequest._alreadyRefreshed && access) {

originalRequest._alreadyRefreshed = true; // 무한 요청 방지
try {
const newAccessToken = await getNewToken();
if (newAccessToken) {
setAccess(newAccessToken);
originalRequest.headers['Authorization'] = `Bearer ${newAccessToken}`;
return instance(originalRequest);
}
} catch (refreshError) {
console.error('토큰 발급 실패', refreshError);
// window.location.href = '/login';
throw refreshError;
const newAccessToken = await getNewToken();
if (newAccessToken) {
setAccess(newAccessToken);
originalRequest.headers['Authorization'] = `Bearer ${newAccessToken}`;
return instance(originalRequest);
}
}
throw error;
},
);

export default instance;
export default instance;
4 changes: 2 additions & 2 deletions src/apis/mypage.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { MypageArticles } from '@/types/mypage';
import instance from './instance';

/** MYPAGE 게시글 조회 */
/* MYPAGE 게시글 조회 */
export const getMypageArticles = async ({ page, size, value, email }: MypageArticles) => {
try {
const response = await instance.get('/api/users/profile', {
Expand All @@ -21,7 +21,7 @@ export const getMypageArticles = async ({ page, size, value, email }: MypageArti
}
};

/** 타인 프로필 조회 */
/* 타인 프로필 조회 */
export const getOthersProfile = async (email: string | null) => {
try {
const response = await instance.get('/api/users/profile', {
Expand Down
38 changes: 28 additions & 10 deletions src/apis/user.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,32 @@
import { Login, SignUp, User } from '@/types';
import instance from './instance';
import { AxiosError } from 'axios';

/** 로그인 **/
export const postLogin = async ({ email, password }: Login) => {
const data = { email, password };
const response = await instance.post('/api/login', data);
return response.data;
};

/** 회원가입 */
/* 회원가입 */
export const postSignup = async (data: SignUp) => {
const response = await instance.post('/api/signup', data);
return response?.data;
};

/** User 정보 불러오기 */
/* 로그인 */
export const postLogin = async (data: Login) => {
try {
const response = await instance.post('/api/login', data);
return response.data;
} catch (error) {
if (error instanceof AxiosError) {
throw error;
}
}
};

/* 카카오 로그인 */
export const getKakaoLogin = async () => {
const response = await instance.get('/api/login/oauth2/kakao');
return response.data;
};

/* User 정보 불러오기 */
export const getUserInfo = async () => {
try {
const response = await instance.get('/api/mypage/users');
Expand All @@ -24,8 +36,14 @@ export const getUserInfo = async () => {
}
};

/** User 정보 수정 */
/* User 정보 수정 */
export const putUser = async (data: Omit<User, 'email'>) => {
const response = await instance.put('/api/mypage/users', data);
return response.data;
};

/* 탈퇴 */
export const deleteUser = async () => {
const response = await instance.delete('/api/mypage/users');
return response.data;
};
4 changes: 4 additions & 0 deletions src/assets/images/TT.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
59 changes: 51 additions & 8 deletions src/components/detail/commentItem/index.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,67 @@
import { Icon, Text } from '@/components/shared';
import { Text } from '@/components/shared';
import { Comment } from '@/types/comment';
import getTimeDifference from '@/utils/getTimeDifference';
import { ForwardedRef, forwardRef } from 'react';
import { ForwardedRef, forwardRef, useState } from 'react';
import UserCircle from '@/components/shared/UserCircle';

import * as S from './styles';
import UserCircle from '@/components/shared/UserCircle';
import { animationMap } from '@/styles/framerMotion';
import { AnimatePresence } from 'framer-motion';
import { deleteComment } from '@/apis/comment';
import { postIdStore } from '@/stores/post';
import { useQueryClient } from '@tanstack/react-query';

const CommentItem = forwardRef<HTMLDivElement, Comment>(
({ content, createdAt, user }: Comment, ref: ForwardedRef<HTMLDivElement>) => {
(
{ content, createdAt, user, commentId }: Comment,
ref: ForwardedRef<HTMLDivElement>,
) => {
const [isExpand, setIsExpand] = useState<boolean>(false);
const postId = postIdStore((state) => state.postId);
const queryClient = useQueryClient();
return (
<S.Container ref={ref}>
<UserCircle size={56} profileImage={user.profileImage} />
<S.UserBox>
<Text ellipsis={100}>{user.nickname}</Text>
<Text typography="t4" color="gray300">
<Text typography="t6" ellipsis={100}>
{user.nickname}
</Text>
<Text typography="t6" color="gray300">
{getTimeDifference(createdAt)}
</Text>
</S.UserBox>
<Text css={S.contentCss}>{content}</Text>
<Icon value="threedot" css={S.threedotCss} />
<Text typography="t6" css={S.contentCss}>
{content}
</Text>
<S.ThreedotIcon
size={38}
value="threedot"
onClick={() => setIsExpand((prev) => !prev)}
/>
<AnimatePresence>
{isExpand && (
<S.Test
variants={animationMap.fadeInOut}
initial="close"
animate={isExpand ? 'open' : 'close'}
exit="close"
>
<Text
typography="t6"
color="red"
clickable
onClick={() =>
deleteComment(postId, commentId).then(() => {
queryClient.invalidateQueries({ queryKey: ['comments', postId] });
setIsExpand(false);
})
}
>
삭제
</Text>
</S.Test>
)}
</AnimatePresence>
</S.Container>
);
},
Expand Down
16 changes: 14 additions & 2 deletions src/components/detail/commentItem/styles.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { Icon } from '@/components/shared';
import { layoutMap } from '@/styles/layout';
import { css } from '@emotion/react';
import styled from '@emotion/styled';
import { motion } from 'framer-motion';

export const Container = styled.div`
${layoutMap.shadowBox}
flex-direction: row;
gap: 30px;
align-items: center;
position: relative;
`;

export const UserBox = styled.div`
Expand All @@ -23,9 +27,17 @@ export const contentCss = css`
flex: 1;
`;

export const threedotCss = css`
export const ThreedotIcon = styled(Icon)`
${layoutMap.shadowBox}
margin-left: auto;
border-radius: 100%;
padding: 10px;
padding: 5px;
`;

export const Test = styled(motion.div)`
${layoutMap.shadowBox}
transform: translateX(-100%);
position: absolute;
right: 0;
padding: 20px;
`;
6 changes: 3 additions & 3 deletions src/components/detail/commentList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ const CommentList = () => {
const { data, ref } = useGetComments(postId);
return (
<S.Container>
{data?.pages.comments.map((comment, index) => {
const commentLength = data.pages.comments.length;
if (commentLength - 2 === index && !data.pages.pageInfo.isDone) {
{data?.pages.map((comment, index) => {
const commentLength = data.pages.length;
if (commentLength - 2 === index && !data.pageParams.isDone) {
return <CommentItem key={index} {...comment} ref={ref} />;
} else {
return <CommentItem key={index} {...comment} />;
Expand Down
Loading

0 comments on commit b519ba6

Please sign in to comment.