Skip to content

Commit b6b41ce

Browse files
authored
fix: prevent loosing mailcompose state (#2618)
* fix: prevent loosing mailcompose state
1 parent a1eb6ca commit b6b41ce

2 files changed

Lines changed: 83 additions & 15 deletions

File tree

packages/web-app-mail/src/components/MailWidget.vue

Lines changed: 82 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@
112112

113113
<script setup lang="ts">
114114
import { ref, computed, unref, watch, onUnmounted } from 'vue'
115+
import { useRouter } from 'vue-router'
115116
import { useGettext } from 'vue3-gettext'
116117
import { storeToRefs } from 'pinia'
117118
import { useGroupwareAccountsStore, useModals } from '@opencloud-eu/web-pkg'
@@ -132,6 +133,7 @@ type ComposeAttachment = ComposeFormState['attachments'][number]
132133
133134
const { $gettext } = useGettext()
134135
const { dispatchModal } = useModals()
136+
const router = useRouter()
135137
const appliedDraftId = ref<string | null>(null)
136138
137139
const SAVED_HINT_DURATION_MS = 2000
@@ -293,6 +295,18 @@ const { draftId, isDirty, isSaving, markDirty, resetDraft, saveAsDraft, discardD
293295
}
294296
})
295297
298+
const hasUnsavedChanges = computed(() => {
299+
return unref(hasMeaningfulChanges) && unref(isDirty)
300+
})
301+
302+
const isMailRoute = (route: { path: string }) => {
303+
return route.path.startsWith('/mail')
304+
}
305+
306+
const preventUnload = (event: BeforeUnloadEvent) => {
307+
event.preventDefault()
308+
}
309+
296310
const onSend = async () => {
297311
if (
298312
!unref(currentAccountId) ||
@@ -346,6 +360,23 @@ const doClose = () => {
346360
emit('close')
347361
}
348362
363+
const requestUnsavedComposeSave = ({
364+
onCancel,
365+
onSave
366+
}: {
367+
onCancel: () => void
368+
onSave: () => void | Promise<void>
369+
}) => {
370+
dispatchModal({
371+
title: $gettext('Unsaved changes'),
372+
message: $gettext('Your email isn’t finished yet. Save it as draft before leaving.'),
373+
confirmText: $gettext('Save as draft'),
374+
hasInput: false,
375+
onConfirm: onSave,
376+
onCancel
377+
})
378+
}
379+
349380
const requestClose = () => {
350381
if (!unref(hasMeaningfulChanges)) {
351382
doClose()
@@ -357,15 +388,11 @@ const requestClose = () => {
357388
return
358389
}
359390
360-
dispatchModal({
361-
title: $gettext('Leave this screen?'),
362-
message: $gettext(
363-
'Your email isn’t finished yet. You can save it as a draft or exit without saving.'
364-
),
365-
confirmText: $gettext('Save as draft'),
366-
hasInput: false,
367-
onConfirm: () => onSaveDraftAndClose(),
368-
onCancel: () => onDiscardAndClose()
391+
requestUnsavedComposeSave({
392+
onCancel: () => {
393+
// Stay in compose.
394+
},
395+
onSave: onSaveDraftAndClose
369396
})
370397
}
371398
@@ -377,11 +404,6 @@ const onSaveDraftAndClose = async () => {
377404
doClose()
378405
}
379406
380-
const onDiscardAndClose = async () => {
381-
await discardDraft()
382-
doClose()
383-
}
384-
385407
const canAutoSaveNow = computed(() => {
386408
return unref(canSaveDraft) && unref(hasMeaningfulChanges) && unref(isDirty) && !unref(isSaving)
387409
})
@@ -398,7 +420,53 @@ useAutoSaveDraft({
398420
}
399421
})
400422
423+
watch(hasUnsavedChanges, (dirty) => {
424+
if (dirty) {
425+
window.addEventListener('beforeunload', preventUnload)
426+
} else {
427+
window.removeEventListener('beforeunload', preventUnload)
428+
}
429+
})
430+
431+
let isNavigationConfirmationOpen = false
432+
433+
const removeNavigationGuard = router.beforeEach((to, _from, next) => {
434+
if (isMailRoute(to) || !unref(hasUnsavedChanges)) {
435+
next()
436+
return
437+
}
438+
439+
if (isNavigationConfirmationOpen) {
440+
next(false)
441+
return
442+
}
443+
444+
if (!unref(canSaveDraft)) {
445+
next(false)
446+
return
447+
}
448+
449+
isNavigationConfirmationOpen = true
450+
451+
requestUnsavedComposeSave({
452+
onCancel: () => {
453+
isNavigationConfirmationOpen = false
454+
next(false)
455+
},
456+
onSave: async () => {
457+
try {
458+
await onSaveDraftAndClose()
459+
next()
460+
} finally {
461+
isNavigationConfirmationOpen = false
462+
}
463+
}
464+
})
465+
})
466+
401467
onUnmounted(() => {
468+
removeNavigationGuard()
469+
window.removeEventListener('beforeunload', preventUnload)
402470
resetCompose()
403471
})
404472

packages/web-app-mail/src/views/Inbox.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
<MailDetails :key="currentMail?.id" />
2020
</div>
2121
</div>
22-
<MailWidget v-if="showCompose" :draft-mail="draftMail" @close="closeCompose" />
2322
</template>
23+
<MailWidget v-if="showCompose" :draft-mail="draftMail" @close="closeCompose" />
2424
</template>
2525

2626
<script setup lang="ts">

0 commit comments

Comments
 (0)