Skip to content

Commit

Permalink
Merge pull request #187 from ItRecode/develop
Browse files Browse the repository at this point in the history
[develop] ์ƒ์šฉ ์„œ๋ฒ„ ์žฌ๋ฐฐํฌ
  • Loading branch information
Seongtaek-H authored Feb 1, 2023
2 parents 30306a8 + ba0ed69 commit 72dbdc1
Show file tree
Hide file tree
Showing 11 changed files with 340 additions and 226 deletions.
4 changes: 2 additions & 2 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
></script>
<script
async
src="https://www.googletagmanager.com/gtag/js?id=G-EH36QKHSGJ"
src="https://www.googletagmanager.com/gtag/js?id=G-D2286JWGQ5"
></script>
<script>
window.dataLayer = window.dataLayer || []
Expand All @@ -38,7 +38,7 @@
}
gtag('js', new Date())

gtag('config', 'G-EH36QKHSGJ')
gtag('config', 'G-D2286JWGQ5')
</script>
</head>
<body>
Expand Down
4 changes: 2 additions & 2 deletions src/apis/reply.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ export const createReply = async (data: FormData) => {
})
}

export const getReply = async (
recordId?: string | undefined,
export const getReply = async <T>(
recordId?: T,
pageParam?: number,
parentId?: number,
size?: number
Expand Down
21 changes: 21 additions & 0 deletions src/hooks/useScrollCommentId.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { useEffect, useState } from 'react'
import { useLocation } from 'react-router-dom'

export const useScrollCommentId = () => {
const { search } = useLocation()
const query = new URLSearchParams(search)

const [scrollCommentId, setScrollCommentId] = useState<number | null>(null)

useEffect(() => {
const commentIdInQuery = query.get('commentId')

if (commentIdInQuery) {
setScrollCommentId(parseInt(commentIdInQuery, 10))
} else {
setScrollCommentId(null)
}
}, [])

return { scrollCommentId }
}
60 changes: 60 additions & 0 deletions src/pages/DetailRecord/InputAddImage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React, { Dispatch, SetStateAction } from 'react'
import { ReactComponent as Camera } from '@assets/camera.svg'
import { ReactComponent as Plus } from '@assets/plus.svg'
import { RECORD_DETAIL_INPUT_IMAGE_HEIGHT } from '@assets/constant/constant'

interface AddImageType {
image: string
setImage: Dispatch<SetStateAction<string>>
setImageFile: Dispatch<SetStateAction<File | null>>
setInputSectionHeight: Dispatch<SetStateAction<number>>
}

export default function ReplyInputAddImage({
image,
setImage,
setImageFile,
setInputSectionHeight,
}: AddImageType) {
const handleSelectImageFile = (e: React.ChangeEvent<HTMLInputElement>) => {
encodeFile((e.target.files as FileList)[0])

setImage(e.target.value)
setImageFile((e.target.files as FileList)[0])

setInputSectionHeight((prev) => prev + RECORD_DETAIL_INPUT_IMAGE_HEIGHT)
e.target.value = ''
}

const encodeFile = (fileBlob: File) => {
const reader = new FileReader()
reader.readAsDataURL(fileBlob)
return new Promise<void>((resolve, reject) => {
reader.onload = () => {
try {
setImage(reader.result as string)
resolve()
} catch (error) {
reject(error)
}
}
})
}

return (
<label htmlFor="imageFile">
<div className="relative mr-2.5 mb-3 h-9 w-9 cursor-pointer bg-transparent">
<Camera className="absolute top-[7px] right-[5px]" />
{image === '' && <Plus className="absolute right-0.5 top-[5px]" />}
</div>
<input
onChange={handleSelectImageFile}
id="imageFile"
type="file"
accept=".jpg, .jpeg, .png, .svg, image/*;capture=camera"
className="hidden"
disabled={image !== ''}
/>
</label>
)
}
41 changes: 41 additions & 0 deletions src/pages/DetailRecord/InputSnackBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { DetailPageInputMode } from '@store/atom'
import React, { Dispatch, SetStateAction } from 'react'
import { useRecoilValue, useResetRecoilState } from 'recoil'
import { ReactComponent as CloseIcon } from '@assets/detail_page_icon/Close.svg'

interface Iprops {
setText: Dispatch<SetStateAction<string>>
setImage: Dispatch<SetStateAction<string>>
setImageFile: Dispatch<SetStateAction<File | null>>
}

export default function InputSnackBar({
setText,
setImage,
setImageFile,
}: Iprops) {
const inputMode = useRecoilValue(DetailPageInputMode)
const resetInputMode = useResetRecoilState(DetailPageInputMode)
return (
<>
{(inputMode.mode === 'nestedReply' || inputMode.mode === 'update') && (
<div className="flex h-[48px] w-full items-center justify-between bg-grey-2 py-2 px-4">
<p className="text-xs text-grey-6">
{inputMode.mode === 'nestedReply' ? '๋‹ต๊ธ€ ์ž‘์„ฑ์ค‘...' : '์ˆ˜์ •์ค‘...'}
</p>
<button
onClick={() => {
resetInputMode()
setText('')
setImage('')
setImageFile(null)
}}
className="cursor-pointer p-0"
>
<CloseIcon />
</button>
</div>
)}
</>
)
}
120 changes: 120 additions & 0 deletions src/pages/DetailRecord/NestedReplyItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { deleteReply } from '@apis/reply'
import Alert from '@components/Alert'
import { useUser } from '@react-query/hooks/useUser'
import { DetailPageInputMode, modifyComment } from '@store/atom'
import { useMutation, useQueryClient } from '@tanstack/react-query'
import React, { useState } from 'react'
import { useSetRecoilState } from 'recoil'
import { CommentData } from 'types/replyData'
import { getCreatedDate } from './getCreatedDate'

interface NestedReplyType extends Omit<CommentData, 'numOfSubComment'> {
parentId: number
}
export default function NestedReplyItem({
recordwriter,
content,
createdAt,
imageUrl,
writer,
commentId,
recordId,
parentId,
}: NestedReplyType) {
const { user } = useUser()
const [deleteAlert, setDeleteAlert] = useState(false)
const setInputMode = useSetRecoilState(DetailPageInputMode)
const setModifyCommentDto = useSetRecoilState(modifyComment)

const queryClient = useQueryClient()

const { mutate: onDeleteNestedReply } = useMutation(
(commentId: number) => deleteReply(commentId, recordId),
{
onSuccess: () => {
queryClient.invalidateQueries(['getReplyData', recordId])
queryClient.invalidateQueries([
'getNestedReplyData',
recordId,
parentId,
])
setDeleteAlert(false)
},
}
)

return (
<div className="mb-4 pr-[3.5rem]">
<div className="rounded-lg bg-grey-2 p-3">
<div className="flex">
<p className="text-xs font-medium">{writer ? writer : '์ต๋ช…'}</p>
<p className="mx-1.5 text-xs font-normal text-grey-5">
{getCreatedDate(createdAt)}
</p>
</div>
{imageUrl !== null && (
<div className="relative my-2.5 aspect-square w-[130px] rounded-2xl">
<img
className="aspect-square w-full rounded-2xl object-cover"
src={imageUrl}
alt="user-selected-record-image"
/>
</div>
)}
<p className="mt-1.5 whitespace-pre-wrap break-words text-xs font-normal leading-normal text-grey-8">
{content.replaceAll(/(<br>|<br\/>|<br \/>)/g, '\r\n')}
</p>
</div>

<div className="mt-1.5 flex w-full justify-end">
<div>
{user?.data === writer && (
<button
onClick={() => {
setInputMode((prev) => {
return { ...prev, mode: 'update', parentId: parentId }
})
setModifyCommentDto({
commentId: commentId,
content: content,
imageUrl: imageUrl ? imageUrl : '',
})
}}
className="cursor-pointer bg-transparent text-xs text-grey-5"
>
์ˆ˜์ •
</button>
)}
{(recordwriter === user?.data || writer === user?.data) && (
<button
onClick={() => setDeleteAlert(true)}
className="cursor-pointer bg-transparent text-xs text-sub-1"
>
์‚ญ์ œ
</button>
)}
{user?.data !== undefined && user?.data !== writer && (
<button className="cursor-pointer bg-transparent text-xs text-grey-5">
์‹ ๊ณ 
</button>
)}
</div>
</div>
{deleteAlert && (
<Alert
visible={deleteAlert}
mainMessage={<>๋Œ“๊ธ€์„ ์‚ญ์ œํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?</>}
subMessage={<>์‚ญ์ œ ํ›„ ๋ณต๊ตฌ๋Š” ๋ถˆ๊ฐ€๋Šฅํ•ด์š”.</>}
cancelMessage="์•„๋‹ˆ์˜ค"
confirmMessage="์˜ˆ"
onClose={() => setDeleteAlert(false)}
onCancel={() => setDeleteAlert(false)}
onConfirm={() => {
onDeleteNestedReply(commentId)
}}
danger={true}
/>
)}
</div>
)
}
Loading

0 comments on commit 72dbdc1

Please sign in to comment.