Skip to content
Merged
10 changes: 5 additions & 5 deletions Mine/src/api/magazine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ export const createMoodboard = async (magazineId: number) => {
return res.data
}

export const patchMagazineCover = async (id: number, coverImageUrl: string) => {
const res = await axiosInstance.patch(`api/magazines/${id}/cover`, { coverImageUrl })
return res.data
}

export const postAddSection = async ({ magazineId, message }: RequestAddSection): Promise<ResponseAddSection> => {
const res = await axiosInstance.post(`api/magazines/${magazineId}/interact`, { message })
return res.data
Expand All @@ -110,8 +115,3 @@ export const postAddSectionInSectionPage = async ({
const res = await axiosInstance.post(`api/magazines/${magazineId}/sections/${sectionId}/interact`, { message })
return res.data
}

export const patchMagazineCover = async (id: number, coverImageUrl: string) => {
const res = await axiosInstance.patch(`api/magazines/${id}/cover`, { coverImageUrl })
return res.data
}
11 changes: 8 additions & 3 deletions Mine/src/components/hamburgerModal/ParagraphHamburgerModal.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { useRef } from 'react'
import Edit from '../../icon/edit.svg?react'
import Delete from '../../icon/delete.svg?react'
import { HamburgerSection } from './HamburgerSection'
import useDeleteParagraph from '../../hooks/useDeleteParagraph'
import useClickOutside from '../../hooks/useClickOutside'

interface ParagraphHamburgerModalProps {
top: number
Expand All @@ -11,7 +13,6 @@ interface ParagraphHamburgerModalProps {
paragraphId?: number
children?: React.ReactNode
handleClose: () => void
// onEdit: (id?: number) => void
}

export default function ParagraphHamburgerModal({
Expand All @@ -22,6 +23,9 @@ export default function ParagraphHamburgerModal({
left,
handleClose,
}: ParagraphHamburgerModalProps) {
const modalRef = useRef<HTMLDivElement>(null)
useClickOutside(modalRef, handleClose)

const deleteParagraphMutation = useDeleteParagraph()
const onDeleteClick: React.MouseEventHandler<HTMLDivElement> = () => {
deleteParagraphMutation.mutate({
Expand All @@ -34,12 +38,13 @@ export default function ParagraphHamburgerModal({

return (
<div
ref={modalRef}
key={sectionId}
className="fixed flex flex-col px-1 py-1 rounded-lg bg-gray-500-op70 shadow-[0 4px 4px 0 rgba(0, 0, 0, 0.25)] z-100"
className="fixed flex flex-col px-1 py-1 rounded-lg bg-gray-500-op70 shadow-[0 4px 4px 0 rgba(0, 0, 0, 0.25)] z-100"
style={{ top: `${top}px`, left: `${left}px` }}
>
<HamburgerSection title="이름 변경" icon={<Edit />} />
<HamburgerSection title="삭제" icon={<Delete />} onClick={onDeleteClick} />
</div>
)
}
}
7 changes: 6 additions & 1 deletion Mine/src/components/hamburgerModal/SectionHamburgerModal.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { useRef, useState } from 'react'
import Edit from '../../icon/edit.svg?react'
import Share from '../../icon/share.svg?react'
import Delete from '../../icon/delete.svg?react'
import { HamburgerSection } from './HamburgerSection'
import useDeleteSection from '../../hooks/useDeleteSection'
import { useState } from 'react'
import ShareModal from './ShareModal'
import useClickOutside from '../../hooks/useClickOutside'

interface SectionHamburgerModalProps {
top: number
Expand All @@ -15,6 +16,9 @@ interface SectionHamburgerModalProps {
}

export default function SectionHamburgerModal({ sectionId, magazineId, top, left, handleClose }: SectionHamburgerModalProps) {
const modalRef = useRef<HTMLDivElement>(null)
useClickOutside(modalRef, handleClose)

const deleteSectionMutation = useDeleteSection()
const [showShareModal, setShowShareModal] = useState(false)

Expand All @@ -27,6 +31,7 @@ export default function SectionHamburgerModal({ sectionId, magazineId, top, left
<>
{!showShareModal && (
<div
ref={modalRef}
className="fixed flex flex-col px-1 py-1 rounded-lg bg-gray-500-op70 shadow-[0_4px_4px_0_rgba(0,0,0,0.25)] z-100"
style={{ top: `${top}px`, left: `${left}px` }}
>
Expand Down
38 changes: 28 additions & 10 deletions Mine/src/components/hamburgerModal/SidebarHamburgerModal.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { useState, useRef } from 'react'
import Share from '../../icon/share.svg?react'
import Edit from '../../icon/edit.svg?react'
import Delete from '../../icon/delete.svg?react'
import useDeleteMagazine from '../../hooks/useDeleteMagazine'
import { HamburgerSection } from './HamburgerSection'
import useClickOutside from '../../hooks/useClickOutside'
import ShareModal from './ShareModal'

interface SidebarHamburgerModalProps {
top: number
Expand All @@ -13,7 +16,12 @@ interface SidebarHamburgerModalProps {
}

export default function SidebarHamburgerModal({ id, top, left, handleClose, onEdit }: SidebarHamburgerModalProps) {
const modalRef = useRef<HTMLDivElement>(null)
useClickOutside(modalRef, handleClose)

const deleteMutation = useDeleteMagazine()
const [showShareModal, setShowShareModal] = useState(false)

const onDeleteClick: React.MouseEventHandler<HTMLDivElement> = () => {
deleteMutation.mutate({ id: id })
handleClose()
Expand All @@ -22,15 +30,25 @@ export default function SidebarHamburgerModal({ id, top, left, handleClose, onEd
onEdit(id)
handleClose()
}

return (
<div
key={id}
className="fixed flex flex-col px-1 py-2 rounded-lg bg-gray-500-op70 shadow-[0 4px 4px 0 rgba(0, 0, 0, 0.25)] z-100"
style={{ top: `${top}px`, left: `${left}px` }}
>
<HamburgerSection title="공유" icon={<Share />} />
<HamburgerSection title="이름 변경" icon={<Edit />} onClick={onEditClick} />
<HamburgerSection title="삭제" icon={<Delete />} onClick={onDeleteClick} />
</div>
<>
{!showShareModal && (
<div
ref={modalRef}
key={id}
className="fixed flex flex-col px-1 py-2 rounded-lg bg-gray-500-op70 shadow-[0_4px_4px_0_rgba(0,0,0,0.25)] z-100"
style={{ top: `${top}px`, left: `${left}px` }}
>
<HamburgerSection title="공유" icon={<Share />} onClick={() => setShowShareModal(true)} />
<HamburgerSection title="이름 변경" icon={<Edit />} onClick={onEditClick} />
<HamburgerSection title="삭제" icon={<Delete />} onClick={onDeleteClick} />
</div>
)}

{showShareModal && (
<ShareModal onClose={handleClose} />
)}
</>
)
}
}
2 changes: 1 addition & 1 deletion Mine/src/components/settings/ScreenSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,4 @@ export default function ScreenSettings() {
)}
</>
)
}
}
17 changes: 17 additions & 0 deletions Mine/src/hooks/useClickOutside.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useEffect, type RefObject } from 'react'

export default function useClickOutside<T extends HTMLElement>(
ref: RefObject<T>,
handler: () => void
) {
useEffect(() => {
const handleClick = (e: MouseEvent) => {
if (ref.current && !ref.current.contains(e.target as Node)) {
handler()
}
}

document.addEventListener('mousedown', handleClick)
return () => document.removeEventListener('mousedown', handleClick)
}, [ref, handler])
}
96 changes: 86 additions & 10 deletions Mine/src/pages/magazine/components/MagazineInfo.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import Hamburger from '../../../icon/hamburger.svg?react'
import ProfileBox from './ProfileBox'
import { useState } from 'react'
import { useRef, useState } from 'react'
import SectionHamburgerModal from '../../../components/hamburgerModal/SectionHamburgerModal'
import SidebarHamburgerModal from '../../../components/hamburgerModal/SidebarHamburgerModal'
import HeartCount from './HeartCount'
import { useMagazine } from '../MagazineProvider'
import { createPortal } from 'react-dom'
import useUpdateMagazineTitle from '../../../hooks/useUpdateMagazineTitle'

interface MagazineInfoProps {
nickname?: string
Expand All @@ -13,12 +16,19 @@ interface MagazineInfoProps {
mode: 'section' | 'magazine'
onClick?: (magazineId: number) => void
}

export default function MagazineInfo({ nickname, profileImage, sectionId, mode, onClick }: MagazineInfoProps) {
const magazinedata = useMagazine()
const [modalPos, setModalPos] = useState({ top: 0, left: 0 })
const [isHamburgerOpen, setIsHamburgerOpen] = useState(false)
const [isEditing, setIsEditing] = useState(false)
const [draftTitle, setDraftTitle] = useState('')
const inputRef = useRef<HTMLInputElement>(null)
const updateTitleMutation = useUpdateMagazineTitle()

const openHamburger = () => setIsHamburgerOpen(true)
const closeHamburger = () => setIsHamburgerOpen(false)

const handleHamburger = (e: React.MouseEvent) => {
const rect = e.currentTarget.getBoundingClientRect()
setModalPos({
Expand All @@ -27,33 +37,99 @@ export default function MagazineInfo({ nickname, profileImage, sectionId, mode,
})
openHamburger()
}

const beginEdit = () => {
setDraftTitle(magazinedata?.title ?? '')
setIsEditing(true)
requestAnimationFrame(() => {
inputRef.current?.focus()
})
}

const cancelEdit = () => {
setIsEditing(false)
setDraftTitle('')
}

const commitEdit = () => {
const next = draftTitle.trim()
if (!next || next === magazinedata?.title) {
cancelEdit()
return
}
updateTitleMutation.mutate(
{ id: magazinedata?.magazineId ?? 0, title: next, introduction: '' },
{ onSuccess: () => cancelEdit() }
)
}

const onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Escape') {
e.preventDefault()
cancelEdit()
}
if (e.key === 'Enter') {
e.preventDefault()
commitEdit()
}
}

return (
<div className="flex w-full justify-between items-center self-stretch">
<div className="flex items-center gap-2 basis-100">
<div
className={`cursor-pointer ${mode === 'section' ? 'font-regular16 font-notoserif text-gray-600' : 'font-semibold20 font-pretendard text-gray-100'}`}
onClick={() => magazinedata?.magazineId && onClick?.(magazinedata.magazineId)}
>
{magazinedata?.title}
</div>
{!isEditing ? (
<div
className={`cursor-pointer ${mode === 'section' ? 'font-regular16 font-notoserif text-gray-600' : 'font-semibold20 font-pretendard text-gray-100'}`}
onClick={() => magazinedata?.magazineId && onClick?.(magazinedata.magazineId)}
>
{magazinedata?.title}
</div>
) : (
<input
ref={inputRef}
value={draftTitle}
onChange={(e) => setDraftTitle(e.target.value)}
onKeyDown={onKeyDown}
onBlur={commitEdit}
className={`bg-transparent outline-none ${mode === 'section' ? 'font-regular16 font-notoserif text-gray-600' : 'font-semibold20 font-pretendard text-gray-100'}`}
/>
)}

<Hamburger className="rotate-90 text-black-icon cursor-pointer" onClick={handleHamburger} />
{isHamburgerOpen && magazinedata?.magazineId !== undefined && sectionId !== undefined && (

{isHamburgerOpen && mode === 'section' && magazinedata?.magazineId !== undefined && sectionId !== undefined && (
<SectionHamburgerModal
handleClose={closeHamburger}
magazineId={magazinedata?.magazineId}
sectionId={sectionId}
top={modalPos.top}
left={modalPos.left}
// onEdit={}
/>
)}
</div>

<HeartCount
hearts={magazinedata?.likeCount}
magazineId={magazinedata?.magazineId}
classname="basis-1 justify-center"
/>
<ProfileBox nickname={nickname} profileImage={profileImage} mode={mode} classname="basis-100 justify-end" />

{isHamburgerOpen && mode === 'magazine' && magazinedata?.magazineId !== undefined &&
createPortal(
<SidebarHamburgerModal
handleClose={closeHamburger}
id={magazinedata.magazineId}
top={modalPos.top}
left={modalPos.left}
onEdit={() => {
closeHamburger()
beginEdit()
}}
/>,
document.body
)
}
</div>
)
}
}
1 change: 0 additions & 1 deletion Mine/src/pages/magazine/components/SectionContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ export default function SectionContent({ sectionId, magazineId }: SectionContent
<IconWandStars className="w-6 h-6" />
</button>
</div>

<ScreenSettingsModal isOpen={isScreenSettingsOpen} onClose={() => setIsScreenSettingsOpen(false)} />
</div>
)
Expand Down
Loading