-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' of https://github.com/epigram5-9/epigram into mer…
…ge/FE-29
- Loading branch information
Showing
32 changed files
with
386 additions
and
299 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
import Image from 'next/image'; | ||
import { Input } from '@/components/ui/input'; | ||
import { Button } from '@/components/ui/button'; | ||
import { DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'; | ||
import { useRef, useState } from 'react'; | ||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form'; | ||
import { useCreatePresignedUrl, useUpdateMe } from '@/hooks/userQueryHooks'; | ||
import { zodResolver } from '@hookform/resolvers/zod'; | ||
import { useForm } from 'react-hook-form'; | ||
import { PatchMeRequestType, PatchMeRequest } from '@/schema/user'; | ||
import fileNameChange from '../../user/utill/fileNameChange'; | ||
|
||
interface UserProfileEditProps { | ||
initialValues: { | ||
image: string; | ||
nickname: string; | ||
}; | ||
onModalClose: () => void; | ||
} | ||
|
||
export default function ProfileEdit({ initialValues, onModalClose }: UserProfileEditProps) { | ||
// 이미지 업로드 훅 | ||
const createPresignedUrl = useCreatePresignedUrl(); | ||
// 닉네임 중복 처리를 위한 변수 | ||
const [focusedField, setFocusedField] = useState<boolean>(false); | ||
|
||
const fileInputRef = useRef<HTMLInputElement | null>(null); | ||
|
||
const form = useForm<PatchMeRequestType>({ | ||
resolver: zodResolver(PatchMeRequest), | ||
mode: 'onBlur', | ||
defaultValues: initialValues, | ||
}); | ||
|
||
const { setValue, getValues, setFocus } = form; | ||
|
||
// error 반환 시 닉네임 focus를 위한 함수 | ||
const handleFieldError = () => { | ||
setFocus('nickname'); | ||
setFocusedField(true); | ||
}; | ||
|
||
// 회원정보 수정 훅 | ||
const updateMe = useUpdateMe(handleFieldError, onModalClose); | ||
|
||
// 프로필 사진 변경 클릭 | ||
const handleImageEditClick = () => { | ||
if (fileInputRef.current) { | ||
fileInputRef.current.click(); | ||
} | ||
}; | ||
|
||
// 이미지 변경 시 | ||
async function handleImageChange(e: React.ChangeEvent<HTMLInputElement>): Promise<void> { | ||
const { files } = e.currentTarget; | ||
|
||
if (files && files.length > 0) { | ||
const file = files[0]; | ||
|
||
// 중복된 파일명 및 한글파일이 저장되지 않도록 파일이름 포멧 변경 | ||
const newFileName = fileNameChange(); | ||
const newFile = new File([file], `${newFileName}.${file.name.split('.').pop()}`, { type: file.type }); | ||
|
||
createPresignedUrl.mutate( | ||
{ image: newFile }, | ||
{ | ||
onSuccess: (data) => { | ||
setValue('image', data.url); | ||
}, | ||
}, | ||
); | ||
} | ||
} | ||
|
||
return ( | ||
<Form {...form}> | ||
<form onSubmit={form.handleSubmit((values: PatchMeRequestType) => updateMe.mutate(values))}> | ||
<DialogHeader> | ||
<DialogTitle>프로필 수정</DialogTitle> | ||
<div className='flex flex-col justify-center items-center pt-8'> | ||
<div className='w-[200px] h-[200px] rounded-full overflow-hidden cursor-pointer border border-gray-300 shadow-sm'> | ||
<Image src={getValues('image') || initialValues.image} alt='유저 프로필' className='w-full h-full object-cover' width={200} height={200} priority onClick={handleImageEditClick} /> | ||
<Input type='file' accept='image/*' name='image' onChange={(e) => handleImageChange(e)} className='hidden' ref={fileInputRef} /> | ||
</div> | ||
<div className='mt-10 flex flex-col items-start gap-4'> | ||
<FormField | ||
control={form.control} | ||
name='nickname' | ||
render={({ field, fieldState }) => ( | ||
<FormItem className='flex flex-col w-full lg:max-w-[640px] md:max-w-[384px] space-y-0 md:mb-10 mb-5'> | ||
<FormLabel className={`md:mb-5 mb-4 font-pretendard lg:text-xl md:text-base sm:text-sm ${fieldState.invalid || focusedField ? 'text-state-error' : 'text-blue-900'}`}> | ||
닉네임 | ||
</FormLabel> | ||
<FormControl> | ||
<Input | ||
{...field} | ||
type='text' | ||
placeholder='닉네임' | ||
onBlur={(e) => setValue('nickname', e.target.value.trim())} | ||
className={`lg:h-16 h-11 px-4 lg:text-xl md:text-base placeholder-blue-400 rounded-xl bg-blue-200 font-pretendard ${fieldState.invalid || focusedField ? 'border-2 border-state-error' : 'focus:border-blue-500'}`} | ||
/> | ||
</FormControl> | ||
<FormMessage className='flex justify-end text-[13px] text-state-error' /> | ||
</FormItem> | ||
)} | ||
/> | ||
</div> | ||
</div> | ||
<DialogFooter> | ||
<Button type='submit' className='bg-slate-600 text-white' disabled={!form.formState.isValid}> | ||
수정하기 | ||
</Button> | ||
</DialogFooter> | ||
</DialogHeader> | ||
</form> | ||
</Form> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { createContext, useState, useContext, ReactNode, useMemo } from 'react'; | ||
|
||
interface EmotionContextType { | ||
shouldRefetch: boolean; | ||
setShouldRefetch: (value: boolean) => void; | ||
} | ||
|
||
const EmotionContext = createContext<EmotionContextType>({ | ||
shouldRefetch: false, | ||
setShouldRefetch: () => {}, | ||
}); | ||
|
||
export function EmotionProvider({ children }: { children: ReactNode }) { | ||
const [shouldRefetch, setShouldRefetch] = useState(false); | ||
|
||
const value = useMemo(() => ({ shouldRefetch, setShouldRefetch }), [shouldRefetch]); | ||
|
||
return <EmotionContext.Provider value={value}>{children}</EmotionContext.Provider>; | ||
} | ||
|
||
export function useEmotionContext() { | ||
return useContext(EmotionContext); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,12 @@ | ||
// hooks/useEpigramCommentHook.ts | ||
|
||
import { useInfiniteQuery } from '@tanstack/react-query'; | ||
import queries from '@/apis/queries'; | ||
import { InfiniteData, useInfiniteQuery } from '@tanstack/react-query'; | ||
import { CommentResponseType } from '@/schema/comment'; | ||
import queries from '@/apis/queries'; | ||
|
||
const useEpigramCommentsQuery = (epigramId: number) => { | ||
const query = queries.epigramComment.getCommentList({ id: epigramId, limit: 3 }); | ||
|
||
return useInfiniteQuery({ | ||
...query, | ||
const useEpigramCommentsQuery = (epigramId: number) => | ||
useInfiniteQuery<CommentResponseType, Error, InfiniteData<CommentResponseType>>({ | ||
...queries.epigramComment.getComments(epigramId), | ||
initialPageParam: undefined, | ||
getNextPageParam: (lastPage: CommentResponseType) => lastPage.nextCursor ?? undefined, | ||
}); | ||
}; | ||
|
||
export default useEpigramCommentsQuery; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,24 @@ | ||
import { useQuery } from '@tanstack/react-query'; | ||
import getEpigrams from '@/apis/getEpigrams'; | ||
import { useInfiniteQuery, QueryFunctionContext, QueryKey } from '@tanstack/react-query'; | ||
import { GetEpigramsResponseType } from '@/schema/epigrams'; | ||
import getEpigrams from '@/apis/getEpigrams'; | ||
|
||
type EpigramsQueryKey = [string, string]; | ||
|
||
const fetchEpigrams = async ({ pageParam = 0, queryKey }: QueryFunctionContext<EpigramsQueryKey>): Promise<GetEpigramsResponseType> => { | ||
const [, keyword] = queryKey; | ||
const cursor = typeof pageParam === 'number' ? pageParam : undefined; | ||
const response = await getEpigrams({ keyword, limit: 10, cursor }); | ||
return response; | ||
}; | ||
|
||
const useEpigrams = (query: string, page: number, limit: number = 10) => | ||
useQuery<GetEpigramsResponseType, Error>({ | ||
queryKey: ['epigrams', query, page, limit], | ||
queryFn: () => getEpigrams({ keyword: query, limit, cursor: page * limit }), | ||
enabled: !!query, | ||
staleTime: 5 * 60 * 1000, // 데이터 신선도 설정 | ||
const useEpigrams = (currentSearch: string) => | ||
useInfiniteQuery<GetEpigramsResponseType, Error>({ | ||
queryKey: ['epigrams', currentSearch] as unknown as QueryKey, | ||
queryFn: fetchEpigrams as unknown as ({ pageParam, queryKey }: QueryFunctionContext<QueryKey>) => Promise<GetEpigramsResponseType>, | ||
getNextPageParam: (lastPage) => lastPage.nextCursor, | ||
initialPageParam: 0, | ||
enabled: !!currentSearch, | ||
staleTime: 5 * 60 * 1000, | ||
}); | ||
|
||
export default useEpigrams; |
Oops, something went wrong.