generated from ttipo/project-base
feat(frontend): 읽기 화면 이동 · 끌어다 놓기 · 메일함 옮기기 · 차단할 때 받은편지함 정리 · 예약 메일 지금 보내기
- 읽기 화면 [이동 ▾](옮긴 뒤 다음 메일 · [되돌리기]), 도구 줄은 읽기 칸 폭(컨테이너 쿼리)으로 아이콘만 - [이동 ▾] 메뉴 들여쓰기 · 그 메일이 든 메일함에 하위가 있으면 흐린 줄 · 넘치면 좌우 바꾸기(EditorPopup 메뉴 모두), 같은 이름이 다른 곳에도 있을 때만 알림에 "상위 › 이름" - 목록 → 메일함 패널 끌어다 놓기(고른 것 · 대화 줄, 휴지통은 삭제), 메일 화면에 놓은 파일은 무시 - 스팸메일함으로 옮기는 세 길 같게 — 메뉴 · 끌기도 스팸 표시를 붙이고, 꺼내면 스팸 아님 - 메일함 ⋯ 옮기기 창(같은 이름이 있는 곳은 처음부터 막음) · 하위까지 지우기 확인 창 - 차단 창에 받은편지함 정리 칸(같은 탭의 필터 적용 · 스팸메일함 비우기와 함께 돌지 않음), 진행 알림 하나 - 예약 안내 줄 [지금 보내기] — 결과를 모르는 실패는 그 메일을 다시 받아 가르고 확인하지 못한 일은 단정하지 않는다 - 상단 바 좁은 폭에서 [로그아웃]이 꺾이지 않게, 같은 대화 [모두 펼치기] · 펼친 줄 머리 [답장] Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -11,10 +11,13 @@
|
||||
// (usePendingSendRefresh). 들어올 때 보내기 · 읽기 설정도 받아 둔다 — 쓰기 화면의 [보내기 취소] 알림이 설정한 초로 마감을 센다(못 받으면 알리지 않는다).
|
||||
// 탭이 다시 보일 때 마지막으로 받은 지 60초가 지났으면 설정을 조용히 다시 받는다 — 다른 탭에서 바꾼 묶기가 남지 않게 (useMailSettingsRefresh, 14단계)
|
||||
// 회사가 2단계 인증을 필수로 정한 유예 기간이면 상단 바 바로 아래에 노란 줄 (13단계 — TwoFactorGraceNotice)
|
||||
// 메일 화면에 파일을 잘못 놓아도 아무 일 없게 한다(15단계 — 브라우저가 파일을 열어 웹메일을 떠나지 않게). 쓰기 화면은 지금처럼 어디에 놓아도 첨부로 받는다
|
||||
// (useIgnoredWindowFileDrop — 받는 화면이 떠 있으면 그쪽에 맡긴다)
|
||||
import { provide } from 'vue'
|
||||
|
||||
import AppShell from '@/components/shell/AppShell.vue'
|
||||
import MailContactDialogHost from '@/components/mail/MailContactDialogHost.vue'
|
||||
import MailDragChip from '@/components/mail/MailDragChip.vue'
|
||||
import MailSearchBox from '@/components/mail/MailSearchBox.vue'
|
||||
import MailboxPanel from '@/components/mail/MailboxPanel.vue'
|
||||
import TwoFactorGraceNotice from '@/components/mail/TwoFactorGraceNotice.vue'
|
||||
@@ -24,6 +27,7 @@ import { useMailSettingsRefresh } from '@/composables/useMailSettingsRefresh'
|
||||
import { useMailStatePolling } from '@/composables/useMailStatePolling'
|
||||
import { useMailTabTitle } from '@/composables/useMailTabTitle'
|
||||
import { usePendingSendRefresh } from '@/composables/usePendingSendRefresh'
|
||||
import { useIgnoredWindowFileDrop } from '@/composables/useWindowFileDrop'
|
||||
import { useMailBlockStore } from '@/stores/mail-block.store'
|
||||
import { useMailSettingsStore } from '@/stores/mail-settings.store'
|
||||
import { useMailSignatureStore } from '@/stores/mail-signature.store'
|
||||
@@ -68,6 +72,8 @@ useMailStatePolling({
|
||||
useMailTabTitle()
|
||||
// 보낼 시각이 지난 붙잡은 발송 — 보이는 목록 · 열린 메일 · 예약됨 수를 다시 본다
|
||||
usePendingSendRefresh({ clock })
|
||||
// 목록 · 읽기 화면에 놓은 파일은 받지 않는다 (쓰기 화면이 떠 있으면 그 화면이 첨부로 받는다)
|
||||
useIgnoredWindowFileDrop()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -82,4 +88,6 @@ usePendingSendRefresh({ clock })
|
||||
<RouterView />
|
||||
</AppShell>
|
||||
<MailContactDialogHost />
|
||||
<!-- 목록 줄을 메일함 패널로 끄는 동안 마우스를 따라오는 표 (15단계 — 화면 밖에 그려 둔다) -->
|
||||
<MailDragChip />
|
||||
</template>
|
||||
|
||||
@@ -17,6 +17,7 @@ import type {
|
||||
MessageDetail,
|
||||
} from '@/shared/types/mail'
|
||||
import { useAuthStore } from '@/stores/auth.store'
|
||||
import { useMailBlockRunStore } from '@/stores/mail-block-run.store'
|
||||
import { useMailStore } from '@/stores/mail.store'
|
||||
import { useToastStore } from '@/stores/toast.store'
|
||||
import {
|
||||
@@ -953,7 +954,8 @@ describe('MailFilterPage', () => {
|
||||
|
||||
await openRule(wrapper, 'r2')
|
||||
expect(applyBox(wrapper).attributes('disabled')).toBeDefined()
|
||||
expect(applyHelp(wrapper)).toBe('다른 필터로 받은편지함을 정리하는 중입니다. 끝난 뒤에 고를 수 있습니다.')
|
||||
// 차단 창의 새 칸과 같은 글 (15단계에 "다른 필터로 …"에서 바꿨다)
|
||||
expect(applyHelp(wrapper)).toBe('받은편지함을 정리하는 중이라 지금은 고를 수 없습니다.')
|
||||
await click(wrapper.get('#mail-filter-cancel'))
|
||||
|
||||
// 다른 화면으로 가도 이어 한다
|
||||
@@ -969,6 +971,21 @@ describe('MailFilterPage', () => {
|
||||
expect(applyBox(again).attributes('disabled')).toBeUndefined()
|
||||
})
|
||||
|
||||
it('이 탭에서 차단할 때 받은 메일을 옮기는 동안(15단계)도 칸을 끄고 같은 까닭 — 끝나면 다시 고를 수 있다', async () => {
|
||||
const { wrapper } = await mountPage()
|
||||
const blockRun = useMailBlockRunStore()
|
||||
blockRun.running = true
|
||||
|
||||
await openRule(wrapper, 'r2')
|
||||
expect(applyBox(wrapper).attributes('disabled')).toBeDefined()
|
||||
expect(applyHelp(wrapper)).toBe('받은편지함을 정리하는 중이라 지금은 고를 수 없습니다.')
|
||||
|
||||
blockRun.running = false
|
||||
await flushPromises()
|
||||
expect(applyBox(wrapper).attributes('disabled')).toBeUndefined()
|
||||
expect(applyHelp(wrapper)).toBe(MAIL_FILTER_APPLY_TEXT.help)
|
||||
})
|
||||
|
||||
it('「적용」을 고르고 저장하는 중에 창을 닫거나 화면을 떠나도 — 저장이 끝나면 적용한다', async () => {
|
||||
const reply = deferred()
|
||||
const { wrapper } = await mountPage({ save: () => reply.promise, run: () => step(8412, 37, 0, null) })
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
// 주소에서 지운다(replace — 새로 고침에 또 열리지 않게)
|
||||
// - 누른 단추가 사라져 초점을 잃으면(지운 행 · 사라진 줄 · 빈 상태의 [새 필터]) 가까운 행이나 머리의 [새 필터]로 옮긴다
|
||||
// - 창의 「받은편지함에 이미 있는 메일도 정리하기」(7단계)를 고른 채 저장하면 저장한 뒤 받은편지함에 적용한다(useMailFilterActions → useMailFilterRun).
|
||||
// 이 탭에서 적용하는 중이면 창의 그 칸을 끈다(mail-filter-run.store 의 running — 화면을 떠나도 적용은 이어진다)
|
||||
// 이 탭에서 받은편지함을 정리하는 중이면(필터 적용 · 차단할 때 받은 메일 옮기기 — 15단계) 창의 그 칸을 끈다
|
||||
// (mail-block-run.store 의 inboxBusy — 차단 창의 새 칸과 같은 규칙, 화면을 떠나도 정리는 이어진다)
|
||||
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
||||
@@ -32,12 +33,13 @@ import { MAIL_FILTER_FROM_QUERY, MAIL_FILTER_TEXT } from '@/shared/constants/mai
|
||||
import type { MailFilterRule } from '@/shared/types/mail'
|
||||
import { formatCount } from '@/shared/utils/format'
|
||||
import { useAuthStore } from '@/stores/auth.store'
|
||||
import { useMailFilterRunStore } from '@/stores/mail-filter-run.store'
|
||||
import { useMailBlockRunStore } from '@/stores/mail-block-run.store'
|
||||
import { useMailFilterStore } from '@/stores/mail-filter.store'
|
||||
import { useMailStore } from '@/stores/mail.store'
|
||||
|
||||
const filterStore = useMailFilterStore()
|
||||
const filterRunStore = useMailFilterRunStore()
|
||||
/** 이 탭에서 받은편지함을 정리하는 중인지 — 필터 적용 · 차단 정리 (inboxBusy) */
|
||||
const inboxCleanup = useMailBlockRunStore()
|
||||
const mailStore = useMailStore()
|
||||
const authStore = useAuthStore()
|
||||
const route = useRoute()
|
||||
@@ -282,7 +284,7 @@ watch(
|
||||
:busy="filterStore.saving"
|
||||
:error="dialogError"
|
||||
:conflict="dialogConflict"
|
||||
:apply-busy="filterRunStore.running"
|
||||
:apply-busy="inboxCleanup.inboxBusy"
|
||||
@save="(rule, apply) => settleDialog(saveDialog(rule, { apply }))"
|
||||
@remove="settleDialog(removeDialog())"
|
||||
@cancel="cancelDialog"
|
||||
|
||||
Reference in New Issue
Block a user