-
Notifications
You must be signed in to change notification settings - Fork 4
[Refactor] Observer pattern을 통한 전역 모달 관리 #155
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from all commits
7c54bb8
95e1f35
dbb16da
9a63791
5988b12
fac343c
da52f43
509a915
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| import { useEffect, useState, type ReactNode } from "react"; | ||
| import { useLocation } from "react-router-dom"; | ||
|
|
||
| import { modalStore } from "@/shared/model/store"; | ||
|
|
||
| import { Modal } from "../../shared/ui/modal/modal"; | ||
|
|
||
| interface ModalItem { | ||
| id: string; | ||
| content: ReactNode; | ||
| autoPlay?: number; | ||
| } | ||
|
Comment on lines
+8
to
+12
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial store의 여기 타입이 🤖 Prompt for AI Agents |
||
|
|
||
| export const ModalProvider = () => { | ||
| const { pathname } = useLocation(); | ||
| const [modals, setModals] = useState<ModalItem[]>([]); | ||
|
|
||
| useEffect(() => { | ||
| const unsubscribe = modalStore.subscribe(setModals); | ||
| return unsubscribe; | ||
| }, []); | ||
|
Comment on lines
+16
to
+21
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial 외부 store 구독은 지금 패턴은 🤖 Prompt for AI Agents |
||
|
|
||
| useEffect(() => { | ||
| modalStore.reset(); | ||
| }, [pathname]); | ||
|
|
||
| return ( | ||
| <> | ||
| {modals.map((modal) => { | ||
| return ( | ||
| <Modal | ||
| key={modal.id} | ||
| isOpen={true} | ||
| autoPlay={modal.autoPlay} | ||
| onClose={() => modalStore.close(modal.id)} | ||
| > | ||
| {modal.content} | ||
| </Modal> | ||
| ); | ||
| })} | ||
| </> | ||
| ); | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,8 @@ | ||
| import { EXPERIENCE_TYPE } from "@/shared/config/experience"; | ||
| import { parseYMD } from "@/shared/lib/format-date"; | ||
| import { modalStore } from "@/shared/model/store"; | ||
| import { ModalBasic, Tooltip } from "@/shared/ui"; | ||
| import { Button } from "@/shared/ui/button/button"; | ||
| import { useModal } from "@/shared/ui/modal/use-modal"; | ||
| import { Tag } from "@/shared/ui/tag/tag"; | ||
| import { Textfield } from "@/shared/ui/textfield/textfield"; | ||
| import { HELP_TOOLTIP_CONTENT } from "@/shared/ui/tooltip/tooltip.content"; | ||
|
|
@@ -24,9 +24,6 @@ const ExperienceViewer = () => { | |
| const { showEditDelete, onClickEdit, onClickDelete, onToggleDefault } = | ||
| useExperienceHeaderActions(); | ||
|
|
||
| const { isOpen: isDeleteModalOpen, handleModal: toggleDeleteModal } = | ||
| useModal(); | ||
|
|
||
| const startDate = current?.startAt ? parseYMD(current.startAt) : null; | ||
| const endDate = current?.endAt ? parseYMD(current.endAt) : null; | ||
|
|
||
|
|
@@ -42,6 +39,22 @@ const ExperienceViewer = () => { | |
|
|
||
| const typeLabel = current.type ? EXPERIENCE_TYPE[current.type] : "미지정"; | ||
|
|
||
| const handleOpenDeleteModal = () => { | ||
| modalStore.open( | ||
| <ModalBasic | ||
| title="이 경험을 삭제하시겠습니까?" | ||
| subTitle="작성한 내용은 즉시 제거되며, 복구할 수 없습니다." | ||
| closeText="취소" | ||
| confirmText="삭제" | ||
| onClose={() => modalStore.reset()} // 취소 시 닫기 | ||
| onConfirm={() => { | ||
| onClickDelete(); // 실제 삭제 동작 | ||
| modalStore.reset(); // 모달 닫기 | ||
| }} | ||
| /> | ||
| ); | ||
|
Comment on lines
+42
to
+55
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 확인창에서
🤖 Prompt for AI Agents |
||
| }; | ||
|
|
||
| return ( | ||
| <main className={s.page}> | ||
| <StickyHeader | ||
|
|
@@ -53,7 +66,7 @@ const ExperienceViewer = () => { | |
| <Button | ||
| variant="secondary" | ||
| size="small" | ||
| onClick={toggleDeleteModal} | ||
| onClick={handleOpenDeleteModal} | ||
| > | ||
| 삭제하기 | ||
| </Button> | ||
|
|
@@ -130,19 +143,6 @@ const ExperienceViewer = () => { | |
| </div> | ||
| </div> | ||
| </section> | ||
|
|
||
| <ModalBasic | ||
| title="이 경험을 삭제하시겠습니까?" | ||
| subTitle="작성한 내용은 즉시 제거되며, 복구할 수 없습니다." | ||
| closeText="취소" | ||
| confirmText="삭제" | ||
| isOpen={isDeleteModalOpen} | ||
| onClose={toggleDeleteModal} | ||
| onConfirm={() => { | ||
| toggleDeleteModal(); | ||
| onClickDelete(); | ||
| }} | ||
| /> | ||
| </main> | ||
| ); | ||
| }; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
깊은 상대 경로 import는 alias로 통일해주세요.
이 파일은 Line 4에서 이미 alias import를 쓰고 있는데,
../../shared/ui/modal/modal만 상대 경로라 이동/리팩터링에 더 취약합니다.@/shared/ui/modal/modal로 맞추는 편이 일관성과 유지보수성에 좋습니다. As per coding guidelines, "@shared,@features,@widgets등 alias 기반 절대 경로 import 사용 권장" and "깊은 상대 경로(../../..) 사용은 가독성과 유지보수 측면에서 지양".🤖 Prompt for AI Agents