Skip to content

Commit

Permalink
Merge pull request #67 from DEPthes/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
jisupark123 authored Aug 20, 2023
2 parents d8131d0 + a5c3046 commit 91215d2
Show file tree
Hide file tree
Showing 14 changed files with 219 additions and 87 deletions.
32 changes: 25 additions & 7 deletions apis/getLetters.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,39 @@
import ieumAxios from './ieumAxios';
import { authToken } from '@/class/authToken';

export type GetLettersReqParams = {
type: LetterType;
page: number;
size: number;
};

export type LettersResponse = {
check: boolean;
information: {
envelopType: number;
letterId: number;
senderNickname: string;
title: string;
modifiedAt: string;
}[];
content: {
envelopType: number;
letterId: number;
senderNickname: string | null;
title: string;
modifiedAt: string;
}[];
pageable: {
pageNumber: number;
pageSize: number;
};
last: boolean;
};
};
export type LetterType = 'read' | 'unread';
export async function getLetters(type: LetterType) {
export async function getLetters({ type, page, size }: GetLettersReqParams) {
const accessToken = authToken.getToken();
const url = type === 'read' ? '/api/mailbox/read' : '/api/mailbox';
const params = {
page,
size,
};
return await ieumAxios.get<LettersResponse>(url, {
params,
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${accessToken}` },
});
}
32 changes: 19 additions & 13 deletions components/authEmail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,22 @@ import { useMutation } from 'react-query';

type Props = {
title: React.ReactNode;
screenType: 'join' | 'reset-password';
moveNextPage: () => void;
setEmail: React.Dispatch<React.SetStateAction<string>>;
};

const DEFAULT_TIMER_TIME = 1000 * 60 * 3; // 타이머 초기 값 -> 3분

export default function AuthEmail({ title, moveNextPage, setEmail }: Props) {
const router = useRouter();
export default function AuthEmail({ title, screenType, moveNextPage, setEmail }: Props) {
// 이메일 입력값
const [emailValue, setEmailValue] = useState<string>('');

// 이메일 유효성 검사 여부
// normal - 기본값 | notIsValid - 형식 오류 | positive - 통과 | duplicated - 중복
const [emailIsValid, setEmailIsValid] = useState<'normal' | 'notIsValid' | 'positive' | 'duplicated'>('normal');
// normal - 기본값 | notIsValid - 형식 오류 | positive - 통과 | duplicated - 중복(회원가입에만 해당) | notUser - 존재하지 않는 회원(비밀번호 재설정에만 해당)
const [emailIsValid, setEmailIsValid] = useState<'normal' | 'notIsValid' | 'positive' | 'duplicated' | 'notUser'>(
'normal',
);
const [time, setTime] = useState<number>(DEFAULT_TIMER_TIME);
const [timerStarted, setTimerStarted] = useState<boolean>(false);
const [AuthNumberSended, setAuthNumberSended] = useState<boolean>(false); // 인증번호 발송되었는지
Expand Down Expand Up @@ -112,13 +114,15 @@ export default function AuthEmail({ title, moveNextPage, setEmail }: Props) {
// 비밀번호 재설정 페이지는 이메일 중복 검사를 추가로 수행한다.
// 이메일이 중복되어야 기존 회원임을 인증함

if (router.pathname === '/reset-password') {
const response = await getEmailDuplicated(emailValue);
const isUser = !response.data.information.available;
if (!isUser) {
setEmailIsValid('duplicated');
return;
}
const response = await getEmailDuplicated(emailValue);
const isExistUser = !response.data.information.available; // 이미 존재하는 유저인지
if (screenType === 'join' && isExistUser) {
setEmailIsValid('duplicated');
return;
}
if (screenType === 'reset-password' && !isExistUser) {
setEmailIsValid('notUser');
return;
}

//타이머 시작 로직
Expand Down Expand Up @@ -165,7 +169,7 @@ export default function AuthEmail({ title, moveNextPage, setEmail }: Props) {
className={`w-full px-12 py-15 rounded-10 border-2 focus:border-[#707070] outline-none placeholder-text_secondary placeholder:font-paragraph--sm gap-127 font-paragraph--sm ${
emailIsValid === 'normal'
? 'bg-white border-primary/30'
: emailIsValid === 'notIsValid' || emailIsValid === 'duplicated'
: emailIsValid === 'notIsValid' || emailIsValid === 'duplicated' || emailIsValid === 'notUser'
? 'bg-negative/10 border-negative focus:border-negative'
: ''
}`}
Expand All @@ -174,8 +178,10 @@ export default function AuthEmail({ title, moveNextPage, setEmail }: Props) {
placeholder='이메일을 입력해주세요'
onChange={emailChangeHandler}
/>
{emailIsValid === 'notIsValid' || emailIsValid === 'duplicated' ? (
{emailIsValid === 'notIsValid' || emailIsValid === 'notUser' ? (
<p className='font-paragraph--sm text-negative'>이메일을 다시 확인해주세요</p>
) : emailIsValid === 'duplicated' ? (
<p className='font-paragraph--sm text-negative'>이미 가입된 이메일이에요</p>
) : (
<p className='mt-19'></p>
)}
Expand Down
113 changes: 68 additions & 45 deletions components/pages/join/join-Password.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,40 +15,39 @@ import { useMutation } from 'react-query';
import { postSignUp } from '@/apis/postSignUp';
import useApiError from '@/hooks/custom/useApiError';
import { AxiosError } from 'axios';
import { authToken } from '@/class/authToken';

import animation from '../../../styles/loading.module.css';
import { passwordRegex } from '@/libs/passwordRegex';
import { checkPasswordTest } from '@/libs/passwordTest';
import { nicknameRegex } from '@/libs/nicknameRegex';
import { postLogin } from '@/apis/postLogin';

type JoinPasswordType = {
email: string;
getNicknames: string[];
};

//초기 닉네임 설정
const initNicknames = [
'혁명적인설탕',
'고요한오리',
'긍정적인다람쥐',
'꾸준한호랑이',
'센스있는팬더',
'빛나는별',
'신비로운달',
'환상적인바람',
'푸른바다',
'자유로운새',
];

const getRandomNickname = () => {
const randomIndex = Math.floor(Math.random() * initNicknames.length);
return initNicknames[randomIndex];
};
//initNicknames[Math.floor(Math.random() * initNicknames.length)]

const JoinPassword: React.FC<JoinPasswordType> = ({ email, getNicknames }) => {
const [nicknames, setNicknames] = useState<string[]>(getNicknames);
//받아온 닉네임의 초기값을 설정하는 함수.

const JoinPassword: React.FC<JoinPasswordType> = ({ email }) => {
const router = useRouter();
const newSignUpMutation = useMutation(postSignUp);
const [nickname, setNickname] = useState<string>(getRandomNickname());
const newLoginMutation = useMutation(postLogin);

//초기 닉네임 설정
const setInitNicknames = () => {
const initNickname: string = nicknames[0];
setNicknames((prevNicknames) => prevNicknames.slice(1));
return initNickname;
};

const [nickname, setNickname] = useState<string>(setInitNicknames);

//API로부터 받아온 닉네임들 저장하는 배열
const [nicknames, setNicknames] = useState<string[]>([]);

const [showPassword, setShowPassword] = useState({
showPassword: false,
Expand All @@ -60,7 +59,9 @@ const JoinPassword: React.FC<JoinPasswordType> = ({ email }) => {
admitSquare: false,
});

const [checkNickname, setCheckNickname] = useState<'' | 'inputNickName' | 'positive' | 'duplicated' | 'error'>('');
const [checkNickname, setCheckNickname] = useState<'' | 'inputNickName' | 'positive' | 'duplicated' | 'error'>(
'positive',
);

const [passwordValue, setPasswordValue] = useState({
passwordValue: '',
Expand All @@ -73,7 +74,7 @@ const JoinPassword: React.FC<JoinPasswordType> = ({ email }) => {

const setNicknameHandler = (event: React.ChangeEvent<HTMLInputElement>) => {
setNickname(event.target.value);
setIsDuplicatedCheckAble(true);
setIsDuplicatedCheckAble(nicknameRegex.test(event.target.value));
setCheckNickname('inputNickName');
};

Expand Down Expand Up @@ -113,7 +114,8 @@ const JoinPassword: React.FC<JoinPasswordType> = ({ email }) => {

const duplicationCheckHandler = async () => {
const response = await getNicknameDuplicated(nickname!);
if (response.data.information.available) {

if (response.data.information.available && nicknameRegex.test(nickname)) {
setCheckNickname('positive');
} else {
setCheckNickname('duplicated');
Expand Down Expand Up @@ -148,12 +150,14 @@ const JoinPassword: React.FC<JoinPasswordType> = ({ email }) => {
//GPT닉네임 바꾸는 함수
async function changeNicknameHandler() {
setIsDuplicatedCheckAble(false);

try {
// 저장된 배열이 0일 때만 API 호출
if (nicknames.length === 0) {
// 저장된 배열이 5일 때만 API 호출
if (nicknames.length <= 5) {
setIsFetch(true);
const response = await getNickname();
setNicknames(response.data.information.nickname);
const updatedNicknames: string[] = nicknames.concat(response.data.information.nickname);
setNicknames(updatedNicknames);
}
setIsFetch(false);
setNickname(nicknames[0]);
Expand All @@ -164,7 +168,7 @@ const JoinPassword: React.FC<JoinPasswordType> = ({ email }) => {
} else {
setCheckNickname('');
}

setCheckNickname('positive');
setNicknames((prevNicknames) => prevNicknames.slice(1));
} catch (error) {
// 서버에서 500 오류가 발생한 경우
Expand All @@ -173,31 +177,44 @@ const JoinPassword: React.FC<JoinPasswordType> = ({ email }) => {
}
}

//signUp 유효성 검사
const signUpTest = () => {
return (
checkNickname === 'positive' &&
passwordIsValid &&
checkPasswordIsValid &&
checkSquare.admitSquare &&
checkSquare.ageSquare
);
};
//닉네임 실시간 유효성 검사.
// useEffect(() => {
// setCheckNickname(nicknameRegex.test(nickname) ? 'positive' : 'inputNickName');
// }, [nickname]);

const passwordIsValid = passwordRegex.test(passwordValue.passwordValue);

const checkPasswordIsValid = checkPasswordTest(passwordValue.checkPasswordValue, passwordValue.passwordValue);

const signUpIsValid = signUpTest();
// 간편가입 완료 버튼 활성화 여부
const signUpIsValid =
checkNickname === 'positive' &&
passwordIsValid &&
checkPasswordIsValid &&
checkSquare.admitSquare &&
checkSquare.ageSquare;

const successHandler = (check: boolean) => {
console.log(check);
const successHandler = (check: boolean, access_token: string) => {
if (check) {
//acess_token 저장
authToken.setToken(access_token);
router.push('/');
console.log('sign-up');
} else {
}
};

const loginHandler = (check: boolean, email: string, password: string) => {
newLoginMutation.mutate(
{ email, password },
{
onSuccess: (response) => {
successHandler(response.data.check, response.data.information.accessToken);
},
onError: (err) => handleError(err as AxiosError),
},
);
};

//에러처리 => 인증번호가 일치하지 않을 경우

const submitHandler = (event: React.FormEvent<HTMLFormElement>) => {
Expand All @@ -208,7 +225,7 @@ const JoinPassword: React.FC<JoinPasswordType> = ({ email }) => {
{ nickname, email, password },
{
onSuccess: (response) => {
successHandler(response.data.check);
loginHandler(response.data.check, email, password);
},
onError: (err) => handleError(err as AxiosError),
},
Expand Down Expand Up @@ -251,10 +268,14 @@ const JoinPassword: React.FC<JoinPasswordType> = ({ email }) => {
<button
className={`relative flex w-full h-50 mt-16 justify-center items-center
rounded-10 text-[16px] font-label--md text-left not-italic text-[#FFFCF7]
${isDuplicatedCheckAble && nickname !== '' ? 'bg-[#675149] hover:bg-[#2D2421]' : 'bg-[#707070]'}`}
${
isDuplicatedCheckAble && nickname !== '' && checkNickname !== 'positive'
? 'bg-[#675149] hover:bg-[#2D2421]'
: 'bg-[#707070]'
}`}
type='button'
onClick={duplicationCheckHandler}
disabled={!isDuplicatedCheckAble || nickname === ''}
disabled={!isDuplicatedCheckAble || nickname === '' || checkNickname === 'positive'}
>
중복체크
</button>
Expand Down Expand Up @@ -297,6 +318,7 @@ const JoinPassword: React.FC<JoinPasswordType> = ({ email }) => {
: 'bg-[#e11900]/10 border-[#E11900] focus:border-[#E11900]'
}`}
placeholder='비밀번호를 입력'
autoComplete='off'
/>
<button type='button' onClick={togglePasswordHandler}>
{showPassword.showPassword ? (
Expand Down Expand Up @@ -326,6 +348,7 @@ const JoinPassword: React.FC<JoinPasswordType> = ({ email }) => {
: 'bg-[#e11900]/10 border-[#E11900] focus:border-[#E11900]'
}`}
placeholder='비밀번호를 입력'
autoComplete='off'
/>
<button type='button' onClick={toggleCheckPasswordHandler}>
{showPassword.showCheckPassword ? (
Expand Down
2 changes: 1 addition & 1 deletion components/pages/letter/mailbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useRouter } from 'next/router';
import Letter from '@/components/pages/letter/receive';
type MailboxProps = {
letterId: number;
nickname: string;
nickname: string | null;
title: string;
day: string;
envelopType: number;
Expand Down
4 changes: 2 additions & 2 deletions components/pages/password/password-Password.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,8 @@ const PasswordPassword: React.FC<PasswordPasswordProps> = ({ email }) => {
</div>
),
actions: [
{ title: '', style: 'primary', handler: () => router.push('/login') },
{ title: '아니오', style: 'tertiary', handler: () => router.push('/') },
{ title: '', style: 'primary', handler: () => router.push('/login') },
{ title: '아니요', style: 'tertiary', handler: () => router.push('/') },
],
}),
onError: handleDefaultError,
Expand Down
27 changes: 18 additions & 9 deletions hooks/queries/useLettersQuery.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
import { LetterType, LettersResponse, getLetters } from '@/apis/getLetters';
import { useQuery } from 'react-query';
import { GetLettersReqParams, LetterType, LettersResponse, getLetters } from '@/apis/getLetters';
import { useInfiniteQuery, useQuery } from 'react-query';
import { withVerify } from '@/apis/withVerify';

// 우체통
export const LETTERS_QUERY_KEY = 'lettersQuery';
export default function useLettersQuery(type: LetterType) {
export default function useLettersQuery(params: GetLettersReqParams) {
const {
data: letters,
data: lettersInfiniteData,
isLoading,
isError,
} = useQuery({
queryKey: [LETTERS_QUERY_KEY, type],
queryFn: () => withVerify(() => getLetters(type)),
select: (res) => res.data.information,
fetchNextPage: getNextLetters,
isSuccess: getLettersIsSuccess,
hasNextPage,
} = useInfiniteQuery({
queryKey: [LETTERS_QUERY_KEY, params],
queryFn: ({ pageParam = 0 }) => withVerify(() => getLetters({ ...params, page: pageParam })),
getNextPageParam: (lastPage) => {
if (lastPage.data.information.last) {
return undefined;
}
return lastPage.data.information.pageable.pageNumber + 1;
},
});
return { letters, isLoading, isError };
const letters = lettersInfiniteData?.pages.map((letter) => letter.data.information.content).flat();
return { letters, isLoading, isError, getNextLetters, getLettersIsSuccess, hasNextPage };
}
Loading

0 comments on commit 91215d2

Please sign in to comment.