refactor: clean up event methods
Signed-off-by: Sebastian <krupinski01@gmail.com>
This commit is contained in:
@@ -23,72 +23,30 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
// Emits
|
||||
const emit = defineEmits<{
|
||||
open: [message: EntityObject]
|
||||
toggleSelection: [message: EntityObject]
|
||||
activateSelectionMode: [message: EntityObject]
|
||||
toggleSelectAll: [value: boolean]
|
||||
clearSelection: []
|
||||
moveSelection: []
|
||||
deleteSelection: []
|
||||
selectionMode: [message: EntityObject]
|
||||
selectionToggleOne: [message: EntityObject]
|
||||
selectionToggleAll: [value: boolean]
|
||||
selectionClear: []
|
||||
selectionMove: []
|
||||
selectionDelete: []
|
||||
}>()
|
||||
|
||||
const longPressTimer = ref<number | null>(null)
|
||||
const longPressActivated = ref(false)
|
||||
const suppressNextClick = ref(false)
|
||||
const LONG_PRESS_MS = 450
|
||||
|
||||
const LONG_PRESS_MS = 400
|
||||
const selectedIdSet = computed(() => new Set(props.selectionList))
|
||||
|
||||
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))
|
||||
})
|
||||
const selectionCount = computed(() => props.selectionList.length ?? 0)
|
||||
|
||||
// Sorted messages (newest first)
|
||||
const sortedMessages = computed(() => {
|
||||
return [...currentMessages.value].sort((a, b) => {
|
||||
const dateA = getMessageTimeValue(a)
|
||||
const dateB = getMessageTimeValue(b)
|
||||
return [...props.messages].sort((a, b) => {
|
||||
const dateA = timeStamp(a) ?? 0
|
||||
const dateB = timeStamp(b) ?? 0
|
||||
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 => {
|
||||
if (!props.selectedMessage) return false
|
||||
return (message.identifier === props.selectedMessage.identifier)
|
||||
@@ -98,8 +56,23 @@ const isSelected = (message: EntityObject): boolean => {
|
||||
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
|
||||
const formatDate = (date: Date | string | null | undefined): string => {
|
||||
const formatDate = (date: Date | string | number | null | undefined): string => {
|
||||
if (!date) return ''
|
||||
|
||||
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 => {
|
||||
return event.target instanceof Element && event.target.closest('.message-selection-checkbox') !== null
|
||||
}
|
||||
|
||||
const handleSelectionToggle = (message: EntityObject) => {
|
||||
emit('toggleSelection', message)
|
||||
const handleSelectionToggleOne = (message: EntityObject) => {
|
||||
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)) {
|
||||
return
|
||||
}
|
||||
@@ -168,7 +139,7 @@ const handleMessageMouseClick = (event: MouseEvent | KeyboardEvent, message: Ent
|
||||
if (event.shiftKey && !props.selectionMode) {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
emit('activateSelectionMode', message)
|
||||
emit('selectionMode', message)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -178,14 +149,14 @@ const handleMessageMouseClick = (event: MouseEvent | KeyboardEvent, message: Ent
|
||||
}
|
||||
|
||||
if (props.selectionMode) {
|
||||
emit('toggleSelection', message)
|
||||
emit('selectionToggleOne', message)
|
||||
return
|
||||
}
|
||||
|
||||
emit('open', message)
|
||||
}
|
||||
|
||||
const handleMessageMouseDown = (event: MouseEvent, message: EntityObject) => {
|
||||
const handleMouseDown = (event: MouseEvent, message: EntityObject) => {
|
||||
if (isSelectionControlClick(event)) {
|
||||
return
|
||||
}
|
||||
@@ -197,14 +168,7 @@ const handleMessageMouseDown = (event: MouseEvent, message: EntityObject) => {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
suppressNextClick.value = true
|
||||
emit('activateSelectionMode', message)
|
||||
}
|
||||
|
||||
const clearLongPressTimer = () => {
|
||||
if (longPressTimer.value !== null) {
|
||||
window.clearTimeout(longPressTimer.value)
|
||||
longPressTimer.value = null
|
||||
}
|
||||
emit('selectionMode', message)
|
||||
}
|
||||
|
||||
const handleTouchStart = (message: EntityObject) => {
|
||||
@@ -213,9 +177,9 @@ const handleTouchStart = (message: EntityObject) => {
|
||||
|
||||
longPressTimer.value = window.setTimeout(() => {
|
||||
if (!props.selectionMode) {
|
||||
emit('activateSelectionMode', message)
|
||||
emit('selectionMode', message)
|
||||
} else {
|
||||
emit('toggleSelection', message)
|
||||
emit('selectionToggleOne', message)
|
||||
}
|
||||
|
||||
longPressActivated.value = true
|
||||
@@ -231,13 +195,16 @@ const handleTouchMove = () => {
|
||||
clearLongPressTimer()
|
||||
}
|
||||
|
||||
const clearLongPressTimer = () => {
|
||||
if (longPressTimer.value !== null) {
|
||||
window.clearTimeout(longPressTimer.value)
|
||||
longPressTimer.value = null
|
||||
}
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
clearLongPressTimer()
|
||||
})
|
||||
|
||||
const handleSelectAllToggle = (value: boolean | null) => {
|
||||
emit('toggleSelectAll', value === true)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -245,12 +212,12 @@ const handleSelectAllToggle = (value: boolean | null) => {
|
||||
<!-- Header with folder name and counts -->
|
||||
<div v-if="selectedCollection" class="message-list-header">
|
||||
<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">
|
||||
<span v-if="hasCountData">
|
||||
<span class="unread-count">{{ unreadCount }}</span>
|
||||
<span v-if="selectedCollection?.properties.total != null">
|
||||
<span class="unread-count">{{ selectedCollection?.properties.unread ?? 0 }}</span>
|
||||
<span class="mx-1">/</span>
|
||||
<span>{{ totalCount }}</span>
|
||||
<span>{{ selectedCollection?.properties.total ?? 0 }}</span>
|
||||
</span>
|
||||
<span v-else-if="messages.length > 0">
|
||||
{{ messages.length }} loaded
|
||||
@@ -261,11 +228,11 @@ const handleSelectAllToggle = (value: boolean | null) => {
|
||||
<div v-if="selectionMode && messages.length > 0" class="selection-summary">
|
||||
<div class="selection-controls">
|
||||
<v-checkbox-btn
|
||||
:model-value="allCurrentMessagesSelected"
|
||||
:indeterminate="hasSelection && !allCurrentMessagesSelected"
|
||||
:model-value="selectionCount !== 0"
|
||||
:indeterminate="selectionCount > 0 && selectionCount !== messages.length"
|
||||
density="compact"
|
||||
hide-details
|
||||
@update:model-value="handleSelectAllToggle"
|
||||
@update:model-value="handleSelectionToggleAll"
|
||||
/>
|
||||
<span class="text-caption text-medium-emphasis">
|
||||
{{ selectionCount > 0 ? `${selectionCount} selected` : 'Select all loaded' }}
|
||||
@@ -277,8 +244,8 @@ const handleSelectAllToggle = (value: boolean | null) => {
|
||||
size="small"
|
||||
icon="mdi-folder-move-outline"
|
||||
variant="text"
|
||||
:disabled="!hasSelection"
|
||||
@click="emit('moveSelection')"
|
||||
:disabled="selectionCount === 0"
|
||||
@click="emit('selectionMove')"
|
||||
>
|
||||
<v-icon>mdi-folder-move-outline</v-icon>
|
||||
<v-tooltip activator="parent" location="bottom">Move Selected</v-tooltip>
|
||||
@@ -287,8 +254,8 @@ const handleSelectAllToggle = (value: boolean | null) => {
|
||||
size="small"
|
||||
icon="mdi-delete-outline"
|
||||
variant="text"
|
||||
:disabled="!hasSelection"
|
||||
@click="emit('deleteSelection')"
|
||||
:disabled="selectionCount === 0"
|
||||
@click="emit('selectionDelete')"
|
||||
>
|
||||
<v-icon>mdi-delete-outline</v-icon>
|
||||
<v-tooltip activator="parent" location="bottom">Delete Selected</v-tooltip>
|
||||
@@ -297,7 +264,7 @@ const handleSelectAllToggle = (value: boolean | null) => {
|
||||
size="small"
|
||||
icon="mdi-close"
|
||||
variant="text"
|
||||
@click="emit('clearSelection')"
|
||||
@click="emit('selectionClear')"
|
||||
>
|
||||
<v-icon>mdi-close</v-icon>
|
||||
<v-tooltip activator="parent" location="bottom">Clear Selected</v-tooltip>
|
||||
@@ -317,7 +284,7 @@ const handleSelectAllToggle = (value: boolean | null) => {
|
||||
</div>
|
||||
|
||||
<!-- 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>
|
||||
<div class="text-h6 mt-4 text-medium-emphasis">No messages</div>
|
||||
<div class="text-body-2 text-medium-emphasis">
|
||||
@@ -342,8 +309,8 @@ const handleSelectAllToggle = (value: boolean | null) => {
|
||||
'selection-mode': selectionMode,
|
||||
'unread': !message.properties.isRead
|
||||
}"
|
||||
@mousedown="handleMessageMouseDown($event, message)"
|
||||
@click="handleMessageMouseClick($event, message)"
|
||||
@mousedown="handleMouseDown($event, message)"
|
||||
@click="handleMouseClick($event, message)"
|
||||
@touchstart.passive="handleTouchStart(message)"
|
||||
@touchend="handleTouchEnd"
|
||||
@touchcancel="handleTouchEnd"
|
||||
@@ -358,7 +325,7 @@ const handleSelectAllToggle = (value: boolean | null) => {
|
||||
density="compact"
|
||||
hide-details
|
||||
@click.stop
|
||||
@update:model-value="handleSelectionToggle(message)"
|
||||
@update:model-value="handleSelectionToggleOne(message)"
|
||||
/>
|
||||
|
||||
<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' }}
|
||||
</span>
|
||||
<span class="text-caption text-medium-emphasis ml-2">
|
||||
{{ formatDate(getMessageTimestamp(message)) }}
|
||||
{{ formatDate(timeStamp(message)) }}
|
||||
</span>
|
||||
</v-list-item-title>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user