From e5c0e40ad39e5b99cbc298a94b3af839d747029a Mon Sep 17 00:00:00 2001 From: gimdogyun Date: Tue, 4 Jun 2024 12:42:45 +0900 Subject: [PATCH 01/12] =?UTF-8?q?fix=20:=20postInviteTeam=20code=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 #239 --- src/app/api/team.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/app/api/team.ts b/src/app/api/team.ts index d522201d..74622e73 100644 --- a/src/app/api/team.ts +++ b/src/app/api/team.ts @@ -27,10 +27,9 @@ const deleteTeam = (teamId: number) => method: 'DELETE', }); -const postInviteTeam = (teamId: number, code: string) => +const postInviteTeam = (teamId: number) => teamFetcher(`/teams/${teamId}/invite-code`, { method: 'POST', - body: code, }); const postJoinTeam = (teamId: number, code: string) => From 52f76a9c8b5458419858e28dcdeb865e4d4ee354 Mon Sep 17 00:00:00 2001 From: gimdogyun Date: Tue, 4 Jun 2024 12:44:03 +0900 Subject: [PATCH 02/12] =?UTF-8?q?feat:=20join=20team=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #239 --- src/app/team/[teamId]/join/page.tsx | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/app/team/[teamId]/join/page.tsx diff --git a/src/app/team/[teamId]/join/page.tsx b/src/app/team/[teamId]/join/page.tsx new file mode 100644 index 00000000..5951a096 --- /dev/null +++ b/src/app/team/[teamId]/join/page.tsx @@ -0,0 +1,25 @@ +'use client'; + +import { useParams, useRouter } from 'next/navigation'; + +import { postJoinTeam } from '@/app/api/team'; + +const Page = ({ searchParams }: { searchParams: { code: string } }) => { + const params = useParams<{ teamId: string }>(); + const teamId = parseInt(params.teamId, 10); + const { code } = searchParams; + const router = useRouter(); + + postJoinTeam(teamId, code).then((res) => { + if (res?.ok) { + router.replace(`/team/${teamId}`); + } else { + alert('유효하지 않은 초대링크입니다.'); + router.replace('/'); + } + }); + + return
; +}; + +export default Page; From bcb99f200bb52266473f70ece70a45e570a9d69d Mon Sep 17 00:00:00 2001 From: gimdogyun Date: Tue, 4 Jun 2024 12:46:05 +0900 Subject: [PATCH 03/12] =?UTF-8?q?feat:=20=EC=B4=88=EB=8C=80=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #239 --- src/app/team/[teamId]/page.tsx | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/app/team/[teamId]/page.tsx b/src/app/team/[teamId]/page.tsx index 2fa774e7..2e9d89e2 100644 --- a/src/app/team/[teamId]/page.tsx +++ b/src/app/team/[teamId]/page.tsx @@ -3,9 +3,11 @@ 'use client'; import { Box, Button, Flex, useBreakpointValue } from '@chakra-ui/react'; +import { useParams } from 'next/navigation'; import { useEffect, useState } from 'react'; import { BsLink45Deg } from 'react-icons/bs'; +import { postInviteTeam } from '@/app/api/team'; import Garden3D from '@/components/Garden3D'; import { StudyAssetCardProps } from '@/components/StudyAssetCard/types'; import { StudyCardProps } from '@/components/StudyCard/types'; @@ -22,6 +24,9 @@ import studyAssetCardData from '@/mocks/studyAssetCard'; import studyCardData from '@/mocks/studyCard'; const Page = () => { + const params = useParams<{ teamId: string }>(); + const teamId = parseInt(params.teamId, 10); + const [category, setCategory] = useState(TEAM_CATEGORY_INFOS[0].name); const [cardIdx, setCardIdx] = useState(0); @@ -77,6 +82,19 @@ const Page = () => { setCardIdx(0); }; + const handleInviteClick = () => { + postInviteTeam(teamId).then((res) => { + if (res.success) { + navigator.clipboard.writeText( + `${process.env.NEXT_PUBLIC_DEPLOY_URL}/team/${teamId}/join?code=${res.body.code}`, + ); + alert('초대 링크가 복사되었습니다.'); + } else { + alert('초대 링크 생성에 실패했습니다.'); + } + }); + }; + return ( @@ -84,7 +102,14 @@ const Page = () => { {/* TODO 팀원 목록, 초대링크 버튼 */} - From 9987b3114d05c7723a03251b78e5467825af4ac2 Mon Sep 17 00:00:00 2001 From: gimdogyun Date: Sat, 29 Jun 2024 23:45:10 +0900 Subject: [PATCH 04/12] feat: loginBackPath atom - #239 --- src/atom.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/atom.ts b/src/atom.ts index d7dec039..191d1d7a 100644 --- a/src/atom.ts +++ b/src/atom.ts @@ -6,3 +6,5 @@ export const userAtom = atomWithStorage('user', { token: '', isLogin: false, }); + +export const loginBackPathAtom = atomWithStorage('loginBackPath', '/'); From a2d623180195e61bf6607f861ffafb1ecac0a4c3 Mon Sep 17 00:00:00 2001 From: gimdogyun Date: Sat, 29 Jun 2024 23:47:09 +0900 Subject: [PATCH 05/12] =?UTF-8?q?refactor:=20=EA=B5=AC=EA=B8=80=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EC=A3=BC=EC=86=8C=20constant?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - #239 --- src/constants/googleLoginUrl.ts | 8 ++++++++ src/containers/main/GoogleLoginButton/index.tsx | 8 +------- 2 files changed, 9 insertions(+), 7 deletions(-) create mode 100644 src/constants/googleLoginUrl.ts diff --git a/src/constants/googleLoginUrl.ts b/src/constants/googleLoginUrl.ts new file mode 100644 index 00000000..57123dc8 --- /dev/null +++ b/src/constants/googleLoginUrl.ts @@ -0,0 +1,8 @@ +const GOOGLE_LOGIN_URL = + 'https://accounts.google.com/o/oauth2/v2/auth?' + + `client_id=${process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID}&` + + `redirect_uri=${process.env.NEXT_PUBLIC_GOOGLE_REDIRECT_URL}&` + + `response_type=code&` + + `scope=${process.env.NEXT_PUBLIC_GOOGLE_SCOPE}`; + +export default GOOGLE_LOGIN_URL; diff --git a/src/containers/main/GoogleLoginButton/index.tsx b/src/containers/main/GoogleLoginButton/index.tsx index acbda595..fd33a97f 100644 --- a/src/containers/main/GoogleLoginButton/index.tsx +++ b/src/containers/main/GoogleLoginButton/index.tsx @@ -1,14 +1,8 @@ import { Image, Button, Box } from '@chakra-ui/react'; +import GOOGLE_LOGIN_URL from '@/constants/googleLoginUrl'; import useGetUser from '@/hooks/useGetUser'; -const GOOGLE_LOGIN_URL = - 'https://accounts.google.com/o/oauth2/v2/auth?' + - `client_id=${process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID}&` + - `redirect_uri=${process.env.NEXT_PUBLIC_GOOGLE_REDIRECT_URL}&` + - `response_type=code&` + - `scope=${process.env.NEXT_PUBLIC_GOOGLE_SCOPE}`; - const GoogleLoginButton = () => { const user = useGetUser(); From 5ae3077cfca2738f7e69ce86ae2a4c23131744fa Mon Sep 17 00:00:00 2001 From: gimdogyun Date: Sat, 29 Jun 2024 23:47:35 +0900 Subject: [PATCH 06/12] =?UTF-8?q?feat:=20=EB=B9=84=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=EC=8B=9C=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - #239 --- src/app/oauth2/code/google/page.tsx | 9 +++++---- src/app/team/[teamId]/join/page.tsx | 30 ++++++++++++++++++++++------- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/src/app/oauth2/code/google/page.tsx b/src/app/oauth2/code/google/page.tsx index 8289215d..346dfb0c 100644 --- a/src/app/oauth2/code/google/page.tsx +++ b/src/app/oauth2/code/google/page.tsx @@ -1,17 +1,18 @@ 'use client'; -import { useSetAtom } from 'jotai'; +import { useAtomValue, useSetAtom } from 'jotai'; import { useRouter } from 'next/navigation'; import { useEffect } from 'react'; import { postGoogleLogin } from '@/app/api/login'; -import { userAtom } from '@/atom'; +import { loginBackPathAtom, userAtom } from '@/atom'; const Page = ({ searchParams }: { searchParams: { code: string } }) => { const { code } = searchParams; const router = useRouter(); const setUser = useSetAtom(userAtom); + const loginBackPath = useAtomValue(loginBackPathAtom); useEffect(() => { if (code) { @@ -25,10 +26,10 @@ const Page = ({ searchParams }: { searchParams: { code: string } }) => { } else { alert(res?.body?.message || '알 수 없는 오류가 발생했습니다.'); } - router.replace('/'); + router.replace(loginBackPath); }); } - }, [code, router, setUser]); + }, [code, router, setUser, loginBackPath]); return
; }; diff --git a/src/app/team/[teamId]/join/page.tsx b/src/app/team/[teamId]/join/page.tsx index 5951a096..4b77a31e 100644 --- a/src/app/team/[teamId]/join/page.tsx +++ b/src/app/team/[teamId]/join/page.tsx @@ -1,23 +1,39 @@ 'use client'; +import { useSetAtom } from 'jotai'; import { useParams, useRouter } from 'next/navigation'; +import { useEffect } from 'react'; import { postJoinTeam } from '@/app/api/team'; +import { loginBackPathAtom } from '@/atom'; +import GOOGLE_LOGIN_URL from '@/constants/googleLoginUrl'; +import useGetUser from '@/hooks/useGetUser'; const Page = ({ searchParams }: { searchParams: { code: string } }) => { const params = useParams<{ teamId: string }>(); const teamId = parseInt(params.teamId, 10); const { code } = searchParams; const router = useRouter(); + const user = useGetUser(); + const setLoginBackPath = useSetAtom(loginBackPathAtom); - postJoinTeam(teamId, code).then((res) => { - if (res?.ok) { - router.replace(`/team/${teamId}`); - } else { - alert('유효하지 않은 초대링크입니다.'); - router.replace('/'); + useEffect(() => { + if (user) { + if (user.isLogin) { + postJoinTeam(teamId, code).then((res) => { + if (res?.ok) { + router.replace(`/team/${teamId}`); + } else { + alert('유효하지 않은 초대링크입니다.'); + router.replace('/'); + } + }); + } else { + setLoginBackPath(`/team/${teamId}/join?code=${code}`); + window.location.href = GOOGLE_LOGIN_URL; + } } - }); + }, [user, teamId, code, router, setLoginBackPath]); return
; }; From 94e076f3e49c7da307c88b31f71384c44c4082e6 Mon Sep 17 00:00:00 2001 From: gimdogyun Date: Sun, 30 Jun 2024 00:21:34 +0900 Subject: [PATCH 07/12] feat: FetchResult type - #239 --- src/app/api/fetcher.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/app/api/fetcher.ts b/src/app/api/fetcher.ts index 77379dcb..96014f81 100644 --- a/src/app/api/fetcher.ts +++ b/src/app/api/fetcher.ts @@ -1,7 +1,10 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ type AllowObjectBodyRequestInit = Omit & { body?: RequestInit['body'] | object | FormData }; export type FetchProps = [string, AllowObjectBodyRequestInit?]; +export type FetchResult = { ok: boolean; body: any }; + export type FetcherOptions = { baseUrl?: string; headers?: HeadersInit; @@ -30,7 +33,7 @@ const defaultOptions: FetcherOptions = { export const fetcher = (options?: FetcherOptions) => { const { baseUrl, headers, interceptors } = { ...defaultOptions, ...options }; - return async (...props: FetchProps) => { + return async (...props: FetchProps): Promise => { try { let [url, config] = props; if (interceptors?.request) { From a406b8020a1fddc20205070da048cd7a94c0dd5c Mon Sep 17 00:00:00 2001 From: gimdogyun Date: Sun, 30 Jun 2024 00:22:00 +0900 Subject: [PATCH 08/12] fix: useMutateWithToken fix - #239 --- src/hooks/useFetchWithToken.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/hooks/useFetchWithToken.ts b/src/hooks/useFetchWithToken.ts index ca0e9bb4..1024e07c 100644 --- a/src/hooks/useFetchWithToken.ts +++ b/src/hooks/useFetchWithToken.ts @@ -3,6 +3,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { useEffect, useState } from 'react'; +import { FetchResult } from '@/app/api/fetcher'; import useGetUser from '@/hooks/useGetUser'; export function useGetFetchWithToken(fetch: (token: string, ...props: any[]) => any, props: any[], originUser?: any) { @@ -25,8 +26,8 @@ export function useGetFetchWithToken(fetch: (token: string, ...props: any[]) => return result; } -export function useMutateWithToken(fetch: (token: string, ...props: any[]) => any) { +export function useMutateWithToken(fetch: (token: string, ...props: any[]) => Promise) { const user = useGetUser(); - return (props: any[]) => fetch(user?.token || '', ...props); + return (...props: any[]) => fetch(user?.token || '', ...props); } From afa951509b1facda75ede9b969e79facd347e668 Mon Sep 17 00:00:00 2001 From: gimdogyun Date: Sun, 30 Jun 2024 00:22:22 +0900 Subject: [PATCH 09/12] feat: join&invite api with token - #239 --- src/app/api/team.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/app/api/team.ts b/src/app/api/team.ts index 5e8d4db4..ef21c017 100644 --- a/src/app/api/team.ts +++ b/src/app/api/team.ts @@ -27,15 +27,21 @@ const deleteTeam = (teamId: number) => method: 'DELETE', }); -const postInviteTeam = (teamId: number) => +const postInviteTeam = (token: string, teamId: number) => teamFetcher(`/teams/${teamId}/invite-code`, { method: 'POST', + headers: { + Authorization: `Bearer ${token}`, + }, }); -const postJoinTeam = (teamId: number, code: string) => +const postJoinTeam = (token: string, teamId: number, code: string) => teamFetcher(`/teams/${teamId}/join`, { method: 'POST', body: code, + headers: { + Authorization: `Bearer ${token}`, + }, }); const getMyTeams = (memberId: number) => teamFetcher(`/teams/members/${memberId}`); From 0ca090d07e940a1ae98c1bb6b09ed0e6da2b48e5 Mon Sep 17 00:00:00 2001 From: gimdogyun Date: Sun, 30 Jun 2024 00:23:06 +0900 Subject: [PATCH 10/12] feat: join&invite api with token - #239 --- src/app/team/[teamId]/join/page.tsx | 6 ++++-- src/app/team/[teamId]/page.tsx | 5 ++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/app/team/[teamId]/join/page.tsx b/src/app/team/[teamId]/join/page.tsx index 4b77a31e..5579980b 100644 --- a/src/app/team/[teamId]/join/page.tsx +++ b/src/app/team/[teamId]/join/page.tsx @@ -7,6 +7,7 @@ import { useEffect } from 'react'; import { postJoinTeam } from '@/app/api/team'; import { loginBackPathAtom } from '@/atom'; import GOOGLE_LOGIN_URL from '@/constants/googleLoginUrl'; +import { useMutateWithToken } from '@/hooks/useFetchWithToken'; import useGetUser from '@/hooks/useGetUser'; const Page = ({ searchParams }: { searchParams: { code: string } }) => { @@ -16,11 +17,12 @@ const Page = ({ searchParams }: { searchParams: { code: string } }) => { const router = useRouter(); const user = useGetUser(); const setLoginBackPath = useSetAtom(loginBackPathAtom); + const joinTeam = useMutateWithToken(postJoinTeam); useEffect(() => { if (user) { if (user.isLogin) { - postJoinTeam(teamId, code).then((res) => { + joinTeam(teamId, code).then((res) => { if (res?.ok) { router.replace(`/team/${teamId}`); } else { @@ -33,7 +35,7 @@ const Page = ({ searchParams }: { searchParams: { code: string } }) => { window.location.href = GOOGLE_LOGIN_URL; } } - }, [user, teamId, code, router, setLoginBackPath]); + }, [user, teamId, code, router, setLoginBackPath, joinTeam]); return
; }; diff --git a/src/app/team/[teamId]/page.tsx b/src/app/team/[teamId]/page.tsx index e9c2a471..d0d6cb1e 100644 --- a/src/app/team/[teamId]/page.tsx +++ b/src/app/team/[teamId]/page.tsx @@ -20,6 +20,7 @@ import NavigationButton from '@/containers/team/NavigationButton'; import StudyGridView from '@/containers/team/StudyGridView'; import TeamControlPanel from '@/containers/team/TeamControlPanel'; import TeamMember from '@/containers/team/teamMember'; +import { useMutateWithToken } from '@/hooks/useFetchWithToken'; import documentCardData from '@/mocks/documentCard'; import { gardenInfos1 } from '@/mocks/Garden3D'; import studyCardData from '@/mocks/studyCard'; @@ -39,6 +40,8 @@ const Page = ({ params }: { params: { teamId: number } }) => { const [isCreateStudyModalOpen, setIsCreateStudyModalOpen] = useState(false); + const inviteTeam = useMutateWithToken(postInviteTeam); + const getCardData = (start: number) => { if (category === '스터디') { // TODO: 스터디 목록 조회하기. @@ -87,7 +90,7 @@ const Page = ({ params }: { params: { teamId: number } }) => { }; const handleInviteClick = () => { - postInviteTeam(params.teamId).then((res) => { + inviteTeam(params.teamId).then((res) => { if (res.ok) { navigator.clipboard.writeText( `${process.env.NEXT_PUBLIC_DEPLOY_URL}/team/${params.teamId}/join?code=${res.body.code}`, From e3f62f5ed086fcaac402fb3ca7bdc6b84c2cb69b Mon Sep 17 00:00:00 2001 From: gimdogyun Date: Sun, 30 Jun 2024 00:23:31 +0900 Subject: [PATCH 11/12] feat: loginbackpath router replace - #239 --- src/app/oauth2/code/google/page.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/oauth2/code/google/page.tsx b/src/app/oauth2/code/google/page.tsx index 346dfb0c..4343bdf8 100644 --- a/src/app/oauth2/code/google/page.tsx +++ b/src/app/oauth2/code/google/page.tsx @@ -23,10 +23,11 @@ const Page = ({ searchParams }: { searchParams: { code: string } }) => { token: res.body?.token, isLogin: true, }); + router.replace(loginBackPath); } else { alert(res?.body?.message || '알 수 없는 오류가 발생했습니다.'); + router.replace('/'); } - router.replace(loginBackPath); }); } }, [code, router, setUser, loginBackPath]); From eb8f7f7dddad80a57c512b601ae7d6ab71ee0cbc Mon Sep 17 00:00:00 2001 From: gimdogyun Date: Sun, 30 Jun 2024 01:18:43 +0900 Subject: [PATCH 12/12] fix: build fail - #239 --- src/app/team/[teamId]/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/team/[teamId]/page.tsx b/src/app/team/[teamId]/page.tsx index 11114bc2..2bb1f4f3 100644 --- a/src/app/team/[teamId]/page.tsx +++ b/src/app/team/[teamId]/page.tsx @@ -110,7 +110,7 @@ const Page = ({ params }: { params: { teamId: number } }) => { {/* TODO 팀원 목록, 초대링크 버튼 */} -