Merge pull request 'refactor: clean up event methods' (#41) from chore/clean-up-methods into main
All checks were successful
Renovate / renovate (push) Successful in 1m18s
All checks were successful
Renovate / renovate (push) Successful in 1m18s
Reviewed-on: #41
This commit was merged in pull request #41.
This commit is contained in:
@@ -26,7 +26,6 @@ const props = defineProps<Props>()
|
|||||||
// Emits
|
// Emits
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
close: []
|
close: []
|
||||||
sent: []
|
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const mailStore = useMailStore()
|
const mailStore = useMailStore()
|
||||||
@@ -222,7 +221,6 @@ const handleSend = async () => {
|
|||||||
text: editor.value?.getText() || '',
|
text: editor.value?.getText() || '',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
emit('sent')
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Mail][Composer] Failed to send message:', error)
|
console.error('[Mail][Composer] Failed to send message:', error)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,72 +23,30 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
// Emits
|
// Emits
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
open: [message: EntityObject]
|
open: [message: EntityObject]
|
||||||
toggleSelection: [message: EntityObject]
|
selectionMode: [message: EntityObject]
|
||||||
activateSelectionMode: [message: EntityObject]
|
selectionToggleOne: [message: EntityObject]
|
||||||
toggleSelectAll: [value: boolean]
|
selectionToggleAll: [value: boolean]
|
||||||
clearSelection: []
|
selectionClear: []
|
||||||
moveSelection: []
|
selectionMove: []
|
||||||
deleteSelection: []
|
selectionDelete: []
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const longPressTimer = ref<number | null>(null)
|
const longPressTimer = ref<number | null>(null)
|
||||||
const longPressActivated = ref(false)
|
const longPressActivated = ref(false)
|
||||||
const suppressNextClick = ref(false)
|
const suppressNextClick = ref(false)
|
||||||
const LONG_PRESS_MS = 450
|
const LONG_PRESS_MS = 400
|
||||||
|
|
||||||
const selectedIdSet = computed(() => new Set(props.selectionList))
|
const selectedIdSet = computed(() => new Set(props.selectionList))
|
||||||
|
const selectionCount = computed(() => props.selectionList.length ?? 0)
|
||||||
const currentMessages = computed(() => props.messages ?? [])
|
|
||||||
|
|
||||||
const getMessageTimestamp = (message: EntityObject): string | null => {
|
|
||||||
return message.properties.received
|
|
||||||
|| message.properties.sent
|
|
||||||
|| message.modified
|
|
||||||
|| message.created
|
|
||||||
|| null
|
|
||||||
}
|
|
||||||
|
|
||||||
const getMessageTimeValue = (message: EntityObject): number => {
|
|
||||||
const timestamp = getMessageTimestamp(message)
|
|
||||||
if (!timestamp) {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
const timeValue = new Date(timestamp).getTime()
|
|
||||||
return Number.isNaN(timeValue) ? 0 : timeValue
|
|
||||||
}
|
|
||||||
|
|
||||||
const selectionCount = computed(() => props.selectionList.length)
|
|
||||||
|
|
||||||
const hasSelection = computed(() => selectionCount.value > 0)
|
|
||||||
|
|
||||||
const allCurrentMessagesSelected = computed(() => {
|
|
||||||
return currentMessages.value.length > 0 && currentMessages.value.every(message => isSelected(message))
|
|
||||||
})
|
|
||||||
|
|
||||||
// Sorted messages (newest first)
|
// Sorted messages (newest first)
|
||||||
const sortedMessages = computed(() => {
|
const sortedMessages = computed(() => {
|
||||||
return [...currentMessages.value].sort((a, b) => {
|
return [...props.messages].sort((a, b) => {
|
||||||
const dateA = getMessageTimeValue(a)
|
const dateA = timeStamp(a) ?? 0
|
||||||
const dateB = getMessageTimeValue(b)
|
const dateB = timeStamp(b) ?? 0
|
||||||
return dateB - dateA
|
return dateB - dateA
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// Read/Unread counts from collection properties
|
|
||||||
const unreadCount = computed(() => {
|
|
||||||
return props.selectedCollection?.properties.unread ?? 0
|
|
||||||
})
|
|
||||||
|
|
||||||
const totalCount = computed(() => {
|
|
||||||
return props.selectedCollection?.properties.total ?? 0
|
|
||||||
})
|
|
||||||
|
|
||||||
// True only when the collection explicitly provides total/unread counts
|
|
||||||
const hasCountData = computed(() => {
|
|
||||||
return props.selectedCollection?.properties.total != null
|
|
||||||
})
|
|
||||||
|
|
||||||
const isOpened = (message: EntityObject): boolean => {
|
const isOpened = (message: EntityObject): boolean => {
|
||||||
if (!props.selectedMessage) return false
|
if (!props.selectedMessage) return false
|
||||||
return (message.identifier === props.selectedMessage.identifier)
|
return (message.identifier === props.selectedMessage.identifier)
|
||||||
@@ -98,8 +56,23 @@ const isSelected = (message: EntityObject): boolean => {
|
|||||||
return selectedIdSet.value.has(message.identifier)
|
return selectedIdSet.value.has(message.identifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const timeStamp = (message: EntityObject): number | null => {
|
||||||
|
const timestamp = message.properties.received
|
||||||
|
|| message.properties.sent
|
||||||
|
|| message.modified
|
||||||
|
|| message.created
|
||||||
|
|| null
|
||||||
|
|
||||||
|
if (!timestamp) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeValue = new Date(timestamp).getTime()
|
||||||
|
return Number.isNaN(timeValue) ? null : timeValue
|
||||||
|
}
|
||||||
|
|
||||||
// Format date for display
|
// Format date for display
|
||||||
const formatDate = (date: Date | string | null | undefined): string => {
|
const formatDate = (date: Date | string | number | null | undefined): string => {
|
||||||
if (!date) return ''
|
if (!date) return ''
|
||||||
|
|
||||||
const messageDate = new Date(date)
|
const messageDate = new Date(date)
|
||||||
@@ -141,21 +114,19 @@ const formatDate = (date: Date | string | null | undefined): string => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Truncate text
|
|
||||||
const truncate = (text: string | null | undefined, length: number = 100): string => {
|
|
||||||
if (!text) return ''
|
|
||||||
return text.length > length ? text.substring(0, length) + '...' : text
|
|
||||||
}
|
|
||||||
|
|
||||||
const isSelectionControlClick = (event: MouseEvent | KeyboardEvent): boolean => {
|
const isSelectionControlClick = (event: MouseEvent | KeyboardEvent): boolean => {
|
||||||
return event.target instanceof Element && event.target.closest('.message-selection-checkbox') !== null
|
return event.target instanceof Element && event.target.closest('.message-selection-checkbox') !== null
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSelectionToggle = (message: EntityObject) => {
|
const handleSelectionToggleOne = (message: EntityObject) => {
|
||||||
emit('toggleSelection', message)
|
emit('selectionToggleOne', message)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleMessageMouseClick = (event: MouseEvent | KeyboardEvent, message: EntityObject) => {
|
const handleSelectionToggleAll = (value: boolean | null) => {
|
||||||
|
emit('selectionToggleAll', value === true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleMouseClick = (event: MouseEvent | KeyboardEvent, message: EntityObject) => {
|
||||||
if (isSelectionControlClick(event)) {
|
if (isSelectionControlClick(event)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -168,7 +139,7 @@ const handleMessageMouseClick = (event: MouseEvent | KeyboardEvent, message: Ent
|
|||||||
if (event.shiftKey && !props.selectionMode) {
|
if (event.shiftKey && !props.selectionMode) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
emit('activateSelectionMode', message)
|
emit('selectionMode', message)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,14 +149,14 @@ const handleMessageMouseClick = (event: MouseEvent | KeyboardEvent, message: Ent
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (props.selectionMode) {
|
if (props.selectionMode) {
|
||||||
emit('toggleSelection', message)
|
emit('selectionToggleOne', message)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
emit('open', message)
|
emit('open', message)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleMessageMouseDown = (event: MouseEvent, message: EntityObject) => {
|
const handleMouseDown = (event: MouseEvent, message: EntityObject) => {
|
||||||
if (isSelectionControlClick(event)) {
|
if (isSelectionControlClick(event)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -197,14 +168,7 @@ const handleMessageMouseDown = (event: MouseEvent, message: EntityObject) => {
|
|||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
suppressNextClick.value = true
|
suppressNextClick.value = true
|
||||||
emit('activateSelectionMode', message)
|
emit('selectionMode', message)
|
||||||
}
|
|
||||||
|
|
||||||
const clearLongPressTimer = () => {
|
|
||||||
if (longPressTimer.value !== null) {
|
|
||||||
window.clearTimeout(longPressTimer.value)
|
|
||||||
longPressTimer.value = null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleTouchStart = (message: EntityObject) => {
|
const handleTouchStart = (message: EntityObject) => {
|
||||||
@@ -213,9 +177,9 @@ const handleTouchStart = (message: EntityObject) => {
|
|||||||
|
|
||||||
longPressTimer.value = window.setTimeout(() => {
|
longPressTimer.value = window.setTimeout(() => {
|
||||||
if (!props.selectionMode) {
|
if (!props.selectionMode) {
|
||||||
emit('activateSelectionMode', message)
|
emit('selectionMode', message)
|
||||||
} else {
|
} else {
|
||||||
emit('toggleSelection', message)
|
emit('selectionToggleOne', message)
|
||||||
}
|
}
|
||||||
|
|
||||||
longPressActivated.value = true
|
longPressActivated.value = true
|
||||||
@@ -231,13 +195,16 @@ const handleTouchMove = () => {
|
|||||||
clearLongPressTimer()
|
clearLongPressTimer()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const clearLongPressTimer = () => {
|
||||||
|
if (longPressTimer.value !== null) {
|
||||||
|
window.clearTimeout(longPressTimer.value)
|
||||||
|
longPressTimer.value = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
clearLongPressTimer()
|
clearLongPressTimer()
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleSelectAllToggle = (value: boolean | null) => {
|
|
||||||
emit('toggleSelectAll', value === true)
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -245,12 +212,12 @@ const handleSelectAllToggle = (value: boolean | null) => {
|
|||||||
<!-- Header with folder name and counts -->
|
<!-- Header with folder name and counts -->
|
||||||
<div v-if="selectedCollection" class="message-list-header">
|
<div v-if="selectedCollection" class="message-list-header">
|
||||||
<div class="message-list-heading">
|
<div class="message-list-heading">
|
||||||
<h2 class="text-h6">{{ selectedCollection.properties.label || 'Folder' }}</h2>
|
<h2 class="text-h6">{{ selectedCollection?.properties.label || 'Folder' }}</h2>
|
||||||
<div class="folder-counts text-caption text-medium-emphasis">
|
<div class="folder-counts text-caption text-medium-emphasis">
|
||||||
<span v-if="hasCountData">
|
<span v-if="selectedCollection?.properties.total != null">
|
||||||
<span class="unread-count">{{ unreadCount }}</span>
|
<span class="unread-count">{{ selectedCollection?.properties.unread ?? 0 }}</span>
|
||||||
<span class="mx-1">/</span>
|
<span class="mx-1">/</span>
|
||||||
<span>{{ totalCount }}</span>
|
<span>{{ selectedCollection?.properties.total ?? 0 }}</span>
|
||||||
</span>
|
</span>
|
||||||
<span v-else-if="messages.length > 0">
|
<span v-else-if="messages.length > 0">
|
||||||
{{ messages.length }} loaded
|
{{ messages.length }} loaded
|
||||||
@@ -261,11 +228,11 @@ const handleSelectAllToggle = (value: boolean | null) => {
|
|||||||
<div v-if="selectionMode && messages.length > 0" class="selection-summary">
|
<div v-if="selectionMode && messages.length > 0" class="selection-summary">
|
||||||
<div class="selection-controls">
|
<div class="selection-controls">
|
||||||
<v-checkbox-btn
|
<v-checkbox-btn
|
||||||
:model-value="allCurrentMessagesSelected"
|
:model-value="selectionCount !== 0"
|
||||||
:indeterminate="hasSelection && !allCurrentMessagesSelected"
|
:indeterminate="selectionCount > 0 && selectionCount !== messages.length"
|
||||||
density="compact"
|
density="compact"
|
||||||
hide-details
|
hide-details
|
||||||
@update:model-value="handleSelectAllToggle"
|
@update:model-value="handleSelectionToggleAll"
|
||||||
/>
|
/>
|
||||||
<span class="text-caption text-medium-emphasis">
|
<span class="text-caption text-medium-emphasis">
|
||||||
{{ selectionCount > 0 ? `${selectionCount} selected` : 'Select all loaded' }}
|
{{ selectionCount > 0 ? `${selectionCount} selected` : 'Select all loaded' }}
|
||||||
@@ -277,8 +244,8 @@ const handleSelectAllToggle = (value: boolean | null) => {
|
|||||||
size="small"
|
size="small"
|
||||||
icon="mdi-folder-move-outline"
|
icon="mdi-folder-move-outline"
|
||||||
variant="text"
|
variant="text"
|
||||||
:disabled="!hasSelection"
|
:disabled="selectionCount === 0"
|
||||||
@click="emit('moveSelection')"
|
@click="emit('selectionMove')"
|
||||||
>
|
>
|
||||||
<v-icon>mdi-folder-move-outline</v-icon>
|
<v-icon>mdi-folder-move-outline</v-icon>
|
||||||
<v-tooltip activator="parent" location="bottom">Move Selected</v-tooltip>
|
<v-tooltip activator="parent" location="bottom">Move Selected</v-tooltip>
|
||||||
@@ -287,8 +254,8 @@ const handleSelectAllToggle = (value: boolean | null) => {
|
|||||||
size="small"
|
size="small"
|
||||||
icon="mdi-delete-outline"
|
icon="mdi-delete-outline"
|
||||||
variant="text"
|
variant="text"
|
||||||
:disabled="!hasSelection"
|
:disabled="selectionCount === 0"
|
||||||
@click="emit('deleteSelection')"
|
@click="emit('selectionDelete')"
|
||||||
>
|
>
|
||||||
<v-icon>mdi-delete-outline</v-icon>
|
<v-icon>mdi-delete-outline</v-icon>
|
||||||
<v-tooltip activator="parent" location="bottom">Delete Selected</v-tooltip>
|
<v-tooltip activator="parent" location="bottom">Delete Selected</v-tooltip>
|
||||||
@@ -297,7 +264,7 @@ const handleSelectAllToggle = (value: boolean | null) => {
|
|||||||
size="small"
|
size="small"
|
||||||
icon="mdi-close"
|
icon="mdi-close"
|
||||||
variant="text"
|
variant="text"
|
||||||
@click="emit('clearSelection')"
|
@click="emit('selectionClear')"
|
||||||
>
|
>
|
||||||
<v-icon>mdi-close</v-icon>
|
<v-icon>mdi-close</v-icon>
|
||||||
<v-tooltip activator="parent" location="bottom">Clear Selected</v-tooltip>
|
<v-tooltip activator="parent" location="bottom">Clear Selected</v-tooltip>
|
||||||
@@ -317,7 +284,7 @@ const handleSelectAllToggle = (value: boolean | null) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Empty state -->
|
<!-- Empty state -->
|
||||||
<div v-else-if="currentMessages.length === 0" class="pa-8 text-center">
|
<div v-else-if="messages.length === 0" class="pa-8 text-center">
|
||||||
<v-icon size="64" color="grey-lighten-1">mdi-email-outline</v-icon>
|
<v-icon size="64" color="grey-lighten-1">mdi-email-outline</v-icon>
|
||||||
<div class="text-h6 mt-4 text-medium-emphasis">No messages</div>
|
<div class="text-h6 mt-4 text-medium-emphasis">No messages</div>
|
||||||
<div class="text-body-2 text-medium-emphasis">
|
<div class="text-body-2 text-medium-emphasis">
|
||||||
@@ -342,8 +309,8 @@ const handleSelectAllToggle = (value: boolean | null) => {
|
|||||||
'selection-mode': selectionMode,
|
'selection-mode': selectionMode,
|
||||||
'unread': !message.properties.isRead
|
'unread': !message.properties.isRead
|
||||||
}"
|
}"
|
||||||
@mousedown="handleMessageMouseDown($event, message)"
|
@mousedown="handleMouseDown($event, message)"
|
||||||
@click="handleMessageMouseClick($event, message)"
|
@click="handleMouseClick($event, message)"
|
||||||
@touchstart.passive="handleTouchStart(message)"
|
@touchstart.passive="handleTouchStart(message)"
|
||||||
@touchend="handleTouchEnd"
|
@touchend="handleTouchEnd"
|
||||||
@touchcancel="handleTouchEnd"
|
@touchcancel="handleTouchEnd"
|
||||||
@@ -358,7 +325,7 @@ const handleSelectAllToggle = (value: boolean | null) => {
|
|||||||
density="compact"
|
density="compact"
|
||||||
hide-details
|
hide-details
|
||||||
@click.stop
|
@click.stop
|
||||||
@update:model-value="handleSelectionToggle(message)"
|
@update:model-value="handleSelectionToggleOne(message)"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<v-avatar size="40" color="primary">
|
<v-avatar size="40" color="primary">
|
||||||
@@ -374,7 +341,7 @@ const handleSelectAllToggle = (value: boolean | null) => {
|
|||||||
{{ message.properties.from?.label || message.properties.from?.address || 'Unknown Sender' }}
|
{{ message.properties.from?.label || message.properties.from?.address || 'Unknown Sender' }}
|
||||||
</span>
|
</span>
|
||||||
<span class="text-caption text-medium-emphasis ml-2">
|
<span class="text-caption text-medium-emphasis ml-2">
|
||||||
{{ formatDate(getMessageTimestamp(message)) }}
|
{{ formatDate(timeStamp(message)) }}
|
||||||
</span>
|
</span>
|
||||||
</v-list-item-title>
|
</v-list-item-title>
|
||||||
|
|
||||||
|
|||||||
@@ -94,53 +94,6 @@ const {
|
|||||||
|
|
||||||
const handleFolderSelect = (folder: CollectionObject) => mailStore.selectFolder(folder)
|
const handleFolderSelect = (folder: CollectionObject) => mailStore.selectFolder(folder)
|
||||||
|
|
||||||
const handleMessageOpen = (message: EntityObject) => {
|
|
||||||
mailStore.selectMessage(message)
|
|
||||||
|
|
||||||
if (isMobile.value) {
|
|
||||||
mailUiStore.closeSidebar()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleMessageSelectionToggle = (message: EntityObject) => mailUiStore.toggleMessageSelection(message)
|
|
||||||
|
|
||||||
const handleSelectionModeActivate = (message: EntityObject) => mailUiStore.activateSelectionMode(message)
|
|
||||||
|
|
||||||
const handleSelectAllToggle = (value: boolean) => {
|
|
||||||
if (value) {
|
|
||||||
mailUiStore.selectAllCurrentMessages()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
mailUiStore.clearSelection()
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleSelectionClear = () => mailUiStore.deactivateSelectionMode()
|
|
||||||
|
|
||||||
const handleSelectionMove = () => mailUiStore.openMoveMessagesDialog()
|
|
||||||
|
|
||||||
const handleSelectionDelete = () => mailUiStore.deleteSelectedMessages()
|
|
||||||
|
|
||||||
const handleCompose = () => mailUiStore.openCompose()
|
|
||||||
|
|
||||||
const handleComposeReply = (message: EntityObject) => mailUiStore.openCompose(message, 'reply')
|
|
||||||
|
|
||||||
const handleComposeForward = (message: EntityObject) => mailUiStore.openCompose(message, 'forward')
|
|
||||||
|
|
||||||
const handleComposeClose = () => mailUiStore.closeCompose()
|
|
||||||
|
|
||||||
const handleComposeSent = () => mailUiStore.afterSent()
|
|
||||||
|
|
||||||
const handleDelete = (message: EntityObject) => {
|
|
||||||
mailStore.deleteMessages([message.identifier])
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleMove = (message: EntityObject) => mailUiStore.openMoveMessagesDialog(message)
|
|
||||||
|
|
||||||
const handleMoveConfirm = async (target: CollectionObject) => { await mailUiStore.confirmMoveMessages(target) }
|
|
||||||
|
|
||||||
const handleMoveCancel = () => mailUiStore.closeMoveMessagesDialog()
|
|
||||||
|
|
||||||
const handleFolderCreateConfirm = async (folderName: string) => {
|
const handleFolderCreateConfirm = async (folderName: string) => {
|
||||||
try {
|
try {
|
||||||
const mutatedFolder = await mailUiStore.confirmCreateFolder(folderName)
|
const mutatedFolder = await mailUiStore.confirmCreateFolder(folderName)
|
||||||
@@ -153,7 +106,7 @@ const handleFolderCreateConfirm = async (folderName: string) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleFolderRenameConfirm = async (folderName: string) => {
|
const handleFolderEditConfirm = async (folderName: string) => {
|
||||||
try {
|
try {
|
||||||
const mutatedFolder = await mailUiStore.confirmRenameFolder(folderName)
|
const mutatedFolder = await mailUiStore.confirmRenameFolder(folderName)
|
||||||
|
|
||||||
@@ -165,6 +118,14 @@ const handleFolderRenameConfirm = async (folderName: string) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleFolderDeleteConfirm = async () => {
|
||||||
|
try {
|
||||||
|
await mailUiStore.confirmDeleteFolder()
|
||||||
|
} catch (error: unknown) {
|
||||||
|
console.error('[MailPage] Failed to delete folder:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const handleFolderMoveConfirm = async (targetFolder: CollectionObject) => {
|
const handleFolderMoveConfirm = async (targetFolder: CollectionObject) => {
|
||||||
try {
|
try {
|
||||||
await mailUiStore.confirmMoveFolder(targetFolder)
|
await mailUiStore.confirmMoveFolder(targetFolder)
|
||||||
@@ -175,14 +136,47 @@ const handleFolderMoveConfirm = async (targetFolder: CollectionObject) => {
|
|||||||
|
|
||||||
const handleFolderMoveCancel = () => mailUiStore.closeMoveFolderDialog()
|
const handleFolderMoveCancel = () => mailUiStore.closeMoveFolderDialog()
|
||||||
|
|
||||||
const handleFolderDeleteConfirm = async () => {
|
|
||||||
try {
|
const handleMessageOpen = (message: EntityObject) => {
|
||||||
await mailUiStore.confirmDeleteFolder()
|
mailStore.selectMessage(message)
|
||||||
} catch (error: unknown) {
|
|
||||||
console.error('[MailPage] Failed to delete folder:', error)
|
if (isMobile.value) {
|
||||||
|
mailUiStore.closeSidebar()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleMessageComposeFresh = () => mailUiStore.openCompose()
|
||||||
|
|
||||||
|
const handleMessageComposeReply = (message: EntityObject) => mailUiStore.openCompose(message, 'reply')
|
||||||
|
|
||||||
|
const handleMessageComposeForward = (message: EntityObject) => mailUiStore.openCompose(message, 'forward')
|
||||||
|
|
||||||
|
const handleMessageComposeClose = () => mailUiStore.closeCompose()
|
||||||
|
|
||||||
|
const handleMessageDelete = (message: EntityObject) => {
|
||||||
|
mailStore.deleteMessages([message.identifier])
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleMessageMove = (message: EntityObject) => mailUiStore.openMoveMessagesDialog(message)
|
||||||
|
|
||||||
|
const handleMessageMoveConfirm = async (target: CollectionObject) => { await mailUiStore.confirmMoveMessages(target) }
|
||||||
|
|
||||||
|
const handleMessageMoveCancel = () => mailUiStore.closeMoveMessagesDialog()
|
||||||
|
|
||||||
|
const handleMessageSelectionMode = (message: EntityObject) => mailUiStore.messageSelectionModeActivate(message)
|
||||||
|
|
||||||
|
const handleMessageSelectionToggleOne = (message: EntityObject) => mailUiStore.messageSelectionToggleOne(message)
|
||||||
|
|
||||||
|
const handleMessageSelectionToggleAll = (value: boolean) => {
|
||||||
|
mailUiStore.messageSelectionToggleAll(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleMessageSelectionClear = () => mailUiStore.messageSelectionModeDeactivate()
|
||||||
|
|
||||||
|
const handleMessageSelectionMove = () => mailUiStore.openMoveMessagesDialog()
|
||||||
|
|
||||||
|
const handleMessageSelectionDelete = () => mailUiStore.deleteSelectedMessages()
|
||||||
|
|
||||||
const toggleSidebar = () => mailUiStore.toggleSidebar()
|
const toggleSidebar = () => mailUiStore.toggleSidebar()
|
||||||
|
|
||||||
const handleSettingsOpen = () => mailUiStore.openSettings()
|
const handleSettingsOpen = () => mailUiStore.openSettings()
|
||||||
@@ -225,7 +219,7 @@ const handleSettingsOpen = () => mailUiStore.openSettings()
|
|||||||
|
|
||||||
<v-btn
|
<v-btn
|
||||||
icon="mdi-pencil"
|
icon="mdi-pencil"
|
||||||
@click="handleCompose()"
|
@click="handleMessageComposeFresh()"
|
||||||
color="primary"
|
color="primary"
|
||||||
variant="text"
|
variant="text"
|
||||||
>
|
>
|
||||||
@@ -298,12 +292,12 @@ const handleSettingsOpen = () => mailUiStore.openSettings()
|
|||||||
:selection-mode="selectionMode"
|
:selection-mode="selectionMode"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
@open="handleMessageOpen"
|
@open="handleMessageOpen"
|
||||||
@toggle-selection="handleMessageSelectionToggle"
|
@selection-mode="handleMessageSelectionMode"
|
||||||
@activate-selection-mode="handleSelectionModeActivate"
|
@selection-toggle-one="handleMessageSelectionToggleOne"
|
||||||
@toggle-select-all="handleSelectAllToggle"
|
@selection-toggle-all="handleMessageSelectionToggleAll"
|
||||||
@clear-selection="handleSelectionClear"
|
@selection-clear="handleMessageSelectionClear"
|
||||||
@move-selection="handleSelectionMove"
|
@selection-move="handleMessageSelectionMove"
|
||||||
@delete-selection="handleSelectionDelete"
|
@selection-delete="handleMessageSelectionDelete"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -314,18 +308,17 @@ const handleSettingsOpen = () => mailUiStore.openSettings()
|
|||||||
:mode="composeMode"
|
:mode="composeMode"
|
||||||
:source="composeSource"
|
:source="composeSource"
|
||||||
:folder="selectedFolder"
|
:folder="selectedFolder"
|
||||||
@close="handleComposeClose"
|
@close="handleMessageComposeClose"
|
||||||
@sent="handleComposeSent"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<MessageReader
|
<MessageReader
|
||||||
v-else
|
v-else
|
||||||
:entity="selectedMessage"
|
:entity="selectedMessage"
|
||||||
@compose="handleCompose"
|
@compose="handleMessageComposeFresh"
|
||||||
@reply="handleComposeReply"
|
@reply="handleMessageComposeReply"
|
||||||
@forward="handleComposeForward"
|
@forward="handleMessageComposeForward"
|
||||||
@move="handleMove"
|
@move="handleMessageMove"
|
||||||
@delete="handleDelete"
|
@delete="handleMessageDelete"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -336,19 +329,19 @@ const handleSettingsOpen = () => mailUiStore.openSettings()
|
|||||||
<SettingsDialog v-model="settingsDialogVisible" />
|
<SettingsDialog v-model="settingsDialogVisible" />
|
||||||
|
|
||||||
<FolderSelectionDialog
|
<FolderSelectionDialog
|
||||||
v-if="moveMessagesDialogService && moveFolderDialogSource"
|
v-if="moveMessagesDialogService"
|
||||||
v-model="moveMessagesDialogVisible"
|
v-model="moveMessagesDialogVisible"
|
||||||
:service="moveMessagesDialogService"
|
:service="moveMessagesDialogService"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
title="Move Messages To"
|
title="Move Messages To"
|
||||||
confirm-text="Move"
|
confirm-text="Move"
|
||||||
empty-text="No other folders are available in this account."
|
empty-text="No other folders are available in this account."
|
||||||
@select="handleMoveConfirm"
|
@select="handleMessageMoveConfirm"
|
||||||
@cancel="handleMoveCancel"
|
@cancel="handleMessageMoveCancel"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FolderSelectionDialog
|
<FolderSelectionDialog
|
||||||
v-if="moveFolderDialogService && moveFolderDialogSource"
|
v-if="moveFolderDialogService"
|
||||||
v-model="moveFolderDialogVisible"
|
v-model="moveFolderDialogVisible"
|
||||||
:service="moveFolderDialogService"
|
:service="moveFolderDialogService"
|
||||||
:loading="collectionsStore.transceiving"
|
:loading="collectionsStore.transceiving"
|
||||||
@@ -380,7 +373,7 @@ const handleSettingsOpen = () => mailUiStore.openSettings()
|
|||||||
:validate-name="validateRenameFolderName"
|
:validate-name="validateRenameFolderName"
|
||||||
:loading="renameFolderDialogLoading"
|
:loading="renameFolderDialogLoading"
|
||||||
:error-message="renameFolderDialogError"
|
:error-message="renameFolderDialogError"
|
||||||
@confirm="handleFolderRenameConfirm"
|
@confirm="handleFolderEditConfirm"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<DeleteFolderDialog
|
<DeleteFolderDialog
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ export const useMailStore = defineStore('mailStore', () => {
|
|||||||
void loadFoldersForService(service,{ selectInbox: true })
|
void loadFoldersForService(service,{ selectInbox: true })
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Mail] Failed to initialize:', error)
|
console.error('[Mail][Operations] Failed to initialize:', error)
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
@@ -124,10 +124,7 @@ export const useMailStore = defineStore('mailStore', () => {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = error instanceof Error ? error.message : 'Failed to load folders'
|
const message = error instanceof Error ? error.message : 'Failed to load folders'
|
||||||
_setServiceFolderError(service.provider, service.identifier, message)
|
_setServiceFolderError(service.provider, service.identifier, message)
|
||||||
console.error(
|
console.error(`[Mail][Operations] Failed to load folders for ${service.provider}:${String(service.identifier)}:`, error)
|
||||||
`[Mail] Failed to load folders for ${service.provider}:${String(service.identifier)}:`,
|
|
||||||
error,
|
|
||||||
)
|
|
||||||
_updateSyncSources()
|
_updateSyncSources()
|
||||||
return {}
|
return {}
|
||||||
} finally {
|
} finally {
|
||||||
@@ -313,13 +310,13 @@ export const useMailStore = defineStore('mailStore', () => {
|
|||||||
try {
|
try {
|
||||||
service = await servicesStore.serviceByIdentifier(identifier, true)
|
service = await servicesStore.serviceByIdentifier(identifier, true)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`[Mail] Failed to retrieve service ${identifier}:`, error)
|
console.error(`[Mail][Operations] Failed to retrieve service ${identifier}:`, error)
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!service) {
|
if (!service) {
|
||||||
const message = `Service ${identifier} not found`
|
const message = `Service ${identifier} not found`
|
||||||
console.error(`[Mail] ${message}`)
|
console.error(`[Mail][Operations] ${message}`)
|
||||||
throw new Error(message)
|
throw new Error(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -333,7 +330,7 @@ export const useMailStore = defineStore('mailStore', () => {
|
|||||||
try {
|
try {
|
||||||
await entitiesStore.list([folder.identifier])
|
await entitiesStore.list([folder.identifier])
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Mail] Failed to load messages:', error)
|
console.error('[Mail][Operations] Failed to load messages:', error)
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateSyncSources()
|
_updateSyncSources()
|
||||||
@@ -376,7 +373,7 @@ export const useMailStore = defineStore('mailStore', () => {
|
|||||||
|
|
||||||
return draft
|
return draft
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Mail] Failed to save draft:', error)
|
console.error('[Mail][Operations] Failed to save draft:', error)
|
||||||
throw error
|
throw error
|
||||||
} finally {
|
} finally {
|
||||||
composerSaving.value = false
|
composerSaving.value = false
|
||||||
@@ -414,7 +411,7 @@ export const useMailStore = defineStore('mailStore', () => {
|
|||||||
try {
|
try {
|
||||||
await entitiesStore.delete([composerDraftIdentifier.value])
|
await entitiesStore.delete([composerDraftIdentifier.value])
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Mail] Failed to delete draft after send:', error)
|
console.error('[Mail][Operations] Failed to delete draft after send:', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -423,7 +420,7 @@ export const useMailStore = defineStore('mailStore', () => {
|
|||||||
return response
|
return response
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const messageText = error instanceof Error ? error.message : 'Failed to send message'
|
const messageText = error instanceof Error ? error.message : 'Failed to send message'
|
||||||
console.error('[Mail] Failed to send message:', error)
|
console.error('[Mail][Operations] Failed to send message:', error)
|
||||||
notify(messageText, 'error')
|
notify(messageText, 'error')
|
||||||
throw error
|
throw error
|
||||||
} finally {
|
} finally {
|
||||||
@@ -431,11 +428,7 @@ export const useMailStore = defineStore('mailStore', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createFolder(
|
async function createFolder(service: ServiceObject, label: string, parentFolder: CollectionObject | null = null): Promise<CollectionObject> {
|
||||||
service: ServiceObject,
|
|
||||||
label: string,
|
|
||||||
parentFolder: CollectionObject | null = null,
|
|
||||||
): Promise<CollectionObject> {
|
|
||||||
if (service.identifier === null) {
|
if (service.identifier === null) {
|
||||||
throw new Error('Cannot create folder for a service without an identifier')
|
throw new Error('Cannot create folder for a service without an identifier')
|
||||||
}
|
}
|
||||||
@@ -574,7 +567,7 @@ export const useMailStore = defineStore('mailStore', () => {
|
|||||||
await collectionsStore.fetch(sourceCollections)
|
await collectionsStore.fetch(sourceCollections)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const messageText = error instanceof Error ? error.message : 'Failed to move messages'
|
const messageText = error instanceof Error ? error.message : 'Failed to move messages'
|
||||||
console.error('[Mail] Failed to move messages:', error)
|
console.error('[Mail][Operations] Failed to move messages:', error)
|
||||||
notify(messageText, 'error')
|
notify(messageText, 'error')
|
||||||
throw error
|
throw error
|
||||||
} finally {
|
} finally {
|
||||||
@@ -609,7 +602,7 @@ export const useMailStore = defineStore('mailStore', () => {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const messageText = error instanceof Error ? error.message : 'Failed to delete messages'
|
const messageText = error instanceof Error ? error.message : 'Failed to delete messages'
|
||||||
console.error('[Mail] Failed to delete messages:', error)
|
console.error('[Mail][Operations] Failed to delete messages:', error)
|
||||||
notify(messageText, 'error')
|
notify(messageText, 'error')
|
||||||
throw error
|
throw error
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ export const useMailUiStore = defineStore('mailUiStore', () => {
|
|||||||
watch(
|
watch(
|
||||||
() => mailStore.currentMessages,
|
() => mailStore.currentMessages,
|
||||||
() => {
|
() => {
|
||||||
reconcileSelection()
|
messageSelectionReconcile()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -208,16 +208,11 @@ export const useMailUiStore = defineStore('mailUiStore', () => {
|
|||||||
composeVisible.value = false
|
composeVisible.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
async function afterSent() {
|
function messageSelectionClear() {
|
||||||
closeCompose()
|
|
||||||
await mailStore.reloadSelectedFolder()
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearSelection() {
|
|
||||||
setSelectionList([])
|
setSelectionList([])
|
||||||
}
|
}
|
||||||
|
|
||||||
function activateSelectionMode(message?: EntityObject) {
|
function messageSelectionModeActivate(message?: EntityObject) {
|
||||||
selectionMode.value = true
|
selectionMode.value = true
|
||||||
|
|
||||||
if (!message) {
|
if (!message) {
|
||||||
@@ -231,12 +226,12 @@ export const useMailUiStore = defineStore('mailUiStore', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function deactivateSelectionMode() {
|
function messageSelectionModeDeactivate() {
|
||||||
selectionMode.value = false
|
selectionMode.value = false
|
||||||
clearSelection()
|
messageSelectionClear()
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleMessageSelection(message: EntityObject) {
|
function messageSelectionToggleOne(message: EntityObject) {
|
||||||
const identifier = message.identifier
|
const identifier = message.identifier
|
||||||
|
|
||||||
selectionMode.value = true
|
selectionMode.value = true
|
||||||
@@ -249,14 +244,18 @@ export const useMailUiStore = defineStore('mailUiStore', () => {
|
|||||||
setSelectionList([...selectionList.value, identifier])
|
setSelectionList([...selectionList.value, identifier])
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectAllCurrentMessages() {
|
function messageSelectionToggleAll(value: boolean) {
|
||||||
selectionMode.value = true
|
selectionMode.value = true
|
||||||
setSelectionList(mailStore.currentMessages.map(message => message.identifier))
|
if (value) {
|
||||||
|
setSelectionList(mailStore.currentMessages.map(message => message.identifier))
|
||||||
|
} else {
|
||||||
|
setSelectionList([])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function reconcileSelection() {
|
function messageSelectionReconcile() {
|
||||||
if (!mailStore.selectedFolder) {
|
if (!mailStore.selectedFolder) {
|
||||||
clearSelection()
|
messageSelectionClear()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -318,7 +317,7 @@ export const useMailUiStore = defineStore('mailUiStore', () => {
|
|||||||
|
|
||||||
async function confirmMoveMessages(targetIdentifier: Parameters<typeof mailStore.moveMessages>[0]) {
|
async function confirmMoveMessages(targetIdentifier: Parameters<typeof mailStore.moveMessages>[0]) {
|
||||||
await mailStore.moveMessages(targetIdentifier, moveMessagesDialogCandidates.value ?? [])
|
await mailStore.moveMessages(targetIdentifier, moveMessagesDialogCandidates.value ?? [])
|
||||||
deactivateSelectionMode()
|
messageSelectionModeDeactivate()
|
||||||
closeMoveMessagesDialog()
|
closeMoveMessagesDialog()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -507,11 +506,11 @@ export const useMailUiStore = defineStore('mailUiStore', () => {
|
|||||||
openCompose,
|
openCompose,
|
||||||
closeCompose,
|
closeCompose,
|
||||||
afterSent,
|
afterSent,
|
||||||
activateSelectionMode,
|
messageSelectionModeActivate,
|
||||||
deactivateSelectionMode,
|
messageSelectionModeDeactivate,
|
||||||
toggleMessageSelection,
|
messageSelectionToggleOne,
|
||||||
selectAllCurrentMessages,
|
messageSelectionToggleAll,
|
||||||
clearSelection,
|
messageSelectionClear,
|
||||||
validateCreateFolderName,
|
validateCreateFolderName,
|
||||||
validateRenameFolderName,
|
validateRenameFolderName,
|
||||||
openMoveMessagesDialog,
|
openMoveMessagesDialog,
|
||||||
|
|||||||
Reference in New Issue
Block a user