From d02a983d57f6576aa312ab26660d77cf6be5cb6e Mon Sep 17 00:00:00 2001 From: ttipo Date: Mon, 9 Nov 2026 18:37:55 +0900 Subject: [PATCH] =?UTF-8?q?feat(frontend):=20=EC=9D=BD=EA=B8=B0=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20=EC=9D=B4=EB=8F=99=20=C2=B7=20=EB=81=8C?= =?UTF-8?q?=EC=96=B4=EB=8B=A4=20=EB=86=93=EA=B8=B0=20=C2=B7=20=EB=A9=94?= =?UTF-8?q?=EC=9D=BC=ED=95=A8=20=EC=98=AE=EA=B8=B0=EA=B8=B0=20=C2=B7=20?= =?UTF-8?q?=EC=B0=A8=EB=8B=A8=ED=95=A0=20=EB=95=8C=20=EB=B0=9B=EC=9D=80?= =?UTF-8?q?=ED=8E=B8=EC=A7=80=ED=95=A8=20=EC=A0=95=EB=A6=AC=20=C2=B7=20?= =?UTF-8?q?=EC=98=88=EC=95=BD=20=EB=A9=94=EC=9D=BC=20=EC=A7=80=EA=B8=88=20?= =?UTF-8?q?=EB=B3=B4=EB=82=B4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 읽기 화면 [이동 ▾](옮긴 뒤 다음 메일 · [되돌리기]), 도구 줄은 읽기 칸 폭(컨테이너 쿼리)으로 아이콘만 - [이동 ▾] 메뉴 들여쓰기 · 그 메일이 든 메일함에 하위가 있으면 흐린 줄 · 넘치면 좌우 바꾸기(EditorPopup 메뉴 모두), 같은 이름이 다른 곳에도 있을 때만 알림에 "상위 › 이름" - 목록 → 메일함 패널 끌어다 놓기(고른 것 · 대화 줄, 휴지통은 삭제), 메일 화면에 놓은 파일은 무시 - 스팸메일함으로 옮기는 세 길 같게 — 메뉴 · 끌기도 스팸 표시를 붙이고, 꺼내면 스팸 아님 - 메일함 ⋯ 옮기기 창(같은 이름이 있는 곳은 처음부터 막음) · 하위까지 지우기 확인 창 - 차단 창에 받은편지함 정리 칸(같은 탭의 필터 적용 · 스팸메일함 비우기와 함께 돌지 않음), 진행 알림 하나 - 예약 안내 줄 [지금 보내기] — 결과를 모르는 실패는 그 메일을 다시 받아 가르고 확인하지 못한 일은 단정하지 않는다 - 상단 바 좁은 폭에서 [로그아웃]이 꺾이지 않게, 같은 대화 [모두 펼치기] · 펼친 줄 머리 [답장] Co-Authored-By: Claude Opus 5.5 --- .../components/mail/ConversationListItem.vue | 8 + .../mail/ConversationMessagePanel.vue | 3 + .../mail/ConversationMessageRow.vue | 123 +++- .../components/mail/ConversationTableRow.vue | 8 + .../components/mail/MailBulkMoveButton.vue | 27 +- frontend/src/components/mail/MailDragChip.vue | 82 +++ .../mail/MailListDrag.stage15.spec.ts | 422 ++++++++++++ .../src/components/mail/MailToolButton.vue | 55 +- .../components/mail/MailboxDeleteDialog.vue | 59 +- .../src/components/mail/MailboxDialogs.vue | 113 +++- .../src/components/mail/MailboxMoveDialog.vue | 371 +++++++++++ .../components/mail/MailboxPanel.move.spec.ts | 437 +++++++++++++ .../src/components/mail/MailboxPanel.spec.ts | 15 +- frontend/src/components/mail/MailboxPanel.vue | 3 +- .../src/components/mail/MailboxPanelRow.vue | 75 ++- frontend/src/components/mail/MailboxTree.vue | 13 + .../mail/MessageBlockDialog.spec.ts | 148 +++++ .../components/mail/MessageBlockDialog.vue | 160 ++++- .../mail/MessageConversation.stage15.spec.ts | 354 ++++++++++ .../components/mail/MessageConversation.vue | 74 ++- .../components/mail/MessageDeleteButton.vue | 2 + .../src/components/mail/MessageListItem.vue | 8 + .../src/components/mail/MessageMoreMenu.vue | 15 +- .../src/components/mail/MessageMoveButton.vue | 138 ++++ .../mail/MessageReader.sendnow-block.spec.ts | 606 ++++++++++++++++++ .../src/components/mail/MessageReader.spec.ts | 3 +- .../mail/MessageReader.stage15.spec.ts | 309 +++++++++ .../mail/MessageReader.stage9.spec.ts | 4 +- .../src/components/mail/MessageReader.vue | 119 +++- .../components/mail/MessageReaderNotices.vue | 89 ++- .../src/components/mail/MessageSpamButton.vue | 2 + .../src/components/mail/MessageTableRow.vue | 8 + .../src/components/mail/editor/EditorMenu.vue | 29 +- .../mail/editor/EditorPopup.spec.ts | 87 +++ .../components/mail/editor/EditorPopup.vue | 17 +- .../src/components/mail/editor/editor-menu.ts | 6 + .../src/components/mail/mail-move-menu.ts | 29 + .../src/components/mail/mailbox-dialog.ts | 2 + .../src/components/shell/AppTopBar.spec.ts | 51 ++ frontend/src/components/shell/AppTopBar.vue | 36 +- .../src/composables/useBulkMailActions.ts | 75 ++- .../useConversationPanelVisibility.ts | 71 ++ frontend/src/composables/useMail.spec.ts | 69 ++ frontend/src/composables/useMail.ts | 75 ++- frontend/src/composables/useMailDrag.ts | 169 +++++ .../src/composables/useMailFilterRun.spec.ts | 29 +- frontend/src/composables/useMailFilterRun.ts | 18 +- .../src/composables/useMailMoveFeedback.ts | 22 +- frontend/src/composables/useMailboxActions.ts | 88 ++- frontend/src/composables/useMessageRemoval.ts | 23 +- frontend/src/composables/usePopupPlacement.ts | 59 +- .../composables/useScheduledMessageActions.ts | 99 ++- frontend/src/composables/useSenderBlock.ts | 20 +- .../src/composables/useSenderBlockRun.spec.ts | 285 ++++++++ frontend/src/composables/useSenderBlockRun.ts | 125 ++++ .../src/composables/useWindowFileDrop.spec.ts | 87 +++ frontend/src/composables/useWindowFileDrop.ts | 49 +- frontend/src/pages/mail/MailLayout.vue | 8 + .../src/pages/settings/MailFilterPage.spec.ts | 19 +- .../src/pages/settings/MailFilterPage.vue | 10 +- frontend/src/shared/constants/mail-block.ts | 57 ++ .../src/shared/constants/mail-conversation.ts | 6 + frontend/src/shared/constants/mail-filter.ts | 32 +- .../src/shared/constants/mail-send-later.ts | 25 + frontend/src/shared/constants/mail.ts | 9 + .../src/shared/constants/mailbox-manage.ts | 56 +- frontend/src/shared/types/mail.ts | 42 +- frontend/src/shared/utils/mail-block.spec.ts | 38 ++ frontend/src/shared/utils/mail-block.ts | 56 +- .../shared/utils/mail-move-targets.spec.ts | 138 ++++ .../src/shared/utils/mail-move-targets.ts | 97 +++ .../shared/utils/mailbox-operation.spec.ts | 26 +- .../src/shared/utils/mailbox-operation.ts | 30 +- frontend/src/shared/utils/mailbox.spec.ts | 188 +++++- frontend/src/shared/utils/mailbox.ts | 151 ++++- .../src/shared/utils/pending-send.spec.ts | 27 + frontend/src/shared/utils/pending-send.ts | 29 + .../src/shared/utils/popup-placement.spec.ts | 41 +- frontend/src/shared/utils/popup-placement.ts | 36 +- frontend/src/stores/inbox-cleanup-run.ts | 85 +++ .../src/stores/mail-block-run.store.spec.ts | 177 +++++ frontend/src/stores/mail-block-run.store.ts | 96 +++ frontend/src/stores/mail-filter-run.store.ts | 56 +- frontend/src/stores/mail.store.ts | 28 +- frontend/src/stores/mail/conversation.ts | 130 +++- frontend/src/stores/mail/mailbox-admin.ts | 86 ++- frontend/src/stores/mail/scheduled.ts | 163 ++++- frontend/src/styles/tokens.css | 2 + frontend/src/test-utils/drag.ts | 59 ++ 89 files changed, 7105 insertions(+), 371 deletions(-) create mode 100644 frontend/src/components/mail/MailDragChip.vue create mode 100644 frontend/src/components/mail/MailListDrag.stage15.spec.ts create mode 100644 frontend/src/components/mail/MailboxMoveDialog.vue create mode 100644 frontend/src/components/mail/MailboxPanel.move.spec.ts create mode 100644 frontend/src/components/mail/MessageBlockDialog.spec.ts create mode 100644 frontend/src/components/mail/MessageConversation.stage15.spec.ts create mode 100644 frontend/src/components/mail/MessageMoveButton.vue create mode 100644 frontend/src/components/mail/MessageReader.sendnow-block.spec.ts create mode 100644 frontend/src/components/mail/MessageReader.stage15.spec.ts create mode 100644 frontend/src/components/mail/editor/EditorPopup.spec.ts create mode 100644 frontend/src/components/mail/mail-move-menu.ts create mode 100644 frontend/src/components/shell/AppTopBar.spec.ts create mode 100644 frontend/src/composables/useConversationPanelVisibility.ts create mode 100644 frontend/src/composables/useMailDrag.ts create mode 100644 frontend/src/composables/useSenderBlockRun.spec.ts create mode 100644 frontend/src/composables/useSenderBlockRun.ts create mode 100644 frontend/src/composables/useWindowFileDrop.spec.ts create mode 100644 frontend/src/shared/utils/mail-move-targets.spec.ts create mode 100644 frontend/src/shared/utils/mail-move-targets.ts create mode 100644 frontend/src/stores/inbox-cleanup-run.ts create mode 100644 frontend/src/stores/mail-block-run.store.spec.ts create mode 100644 frontend/src/stores/mail-block-run.store.ts create mode 100644 frontend/src/test-utils/drag.ts diff --git a/frontend/src/components/mail/ConversationListItem.vue b/frontend/src/components/mail/ConversationListItem.vue index 8dcdaed..d7d3901 100644 --- a/frontend/src/components/mail/ConversationListItem.vue +++ b/frontend/src/components/mail/ConversationListItem.vue @@ -5,11 +5,14 @@ // - 줄의 메일이 모두 보내기를 기다리는 메일이면 고르기 · 별을 쓸 수 없다(aria-disabled + 까닭은 title — 메일 줄의 고르기 칸과 같은 결) // - 마지막 메일이 붙잡힌 메일이면 시각 자리가 "보내는 중" · "예약 · …"(청록 굵게 — 메일 목록과 같다) // - 임시보관함의 제목 없는 저장본은 메일 줄처럼 흐린 "(제목 없음)" (리뷰 L5) +// 끌어다 놓기(15단계 — 디자인 원본 ListDrag15): 줄을 메일함 패널로 끌어 옮긴다 — 고른 줄이면 고른 것 모두, 아니면 이 줄만. +// 그 대화에서 이 메일함에 든 메일을 옮기고(붙잡힌 메일은 빼고), 그런 메일뿐인 줄은 끌 수 없다. 줄의 링크가 끌어 가던 메일 주소 대신 우리 끌기 자료만 싣는다 (useMailRowDrag) import { computed } from 'vue' import type { RouteLocationRaw } from 'vue-router' import AppIcon from '@/components/AppIcon.vue' import MailCheckbox from '@/components/mail/MailCheckbox.vue' +import { useMailRowDrag } from '@/composables/useMailDrag' import { MAIL_BULK_TEXT, MAIL_LIST_ROW_TEXT, MAIL_NO_SUBJECT } from '@/shared/constants/mail' import { MAIL_CONVERSATION_LIST_TEXT } from '@/shared/constants/mail-conversation' import { SEND_LATER_TEXT } from '@/shared/constants/mail-send-later' @@ -55,6 +58,8 @@ const props = withDefaults(defineProps(), { const emit = defineEmits() +const drag = useMailRowDrag() + const subject = computed(() => props.row.latest.subject || MAIL_NO_SUBJECT) const countText = computed(() => conversationCountText(props.row)) /** 마지막 메일이 붙잡아 둔 발송이면 "예약 · …" · "보내는 중" (9단계) */ @@ -85,7 +90,10 @@ function onStar() { { 'is-selected': selected, 'is-unread': unread, 'is-checked': checked, 'is-checking': checking }, ]" :data-row-id="row.id" + :draggable="!held" @mousedown="onMouseDown" + @dragstart="(event: DragEvent) => drag.start(event, row.id)" + @dragend="drag.end" >

() /** 펼친 안의 id — 메일 id 는 서버의 불투명한 글이라 그대로 붙인다 */ const panelId = computed(() => `conversation-panel-${props.row.message.id}`) +/** 펼친 줄 머리의 [답장] — 답장할 수 있는 메일이고(row.replyLocation) 그사이 지워지지 않았을 때 (펼친 안 아래 [답장]과 같은 조건) */ +const headReply = computed(() => { + if (!props.expanded || !props.row.replyLocation) return null + const detail = props.detail + if (detail?.status === 'error' && detail.error.code === 'RES_001') return null + return props.row.replyLocation +})