refactor: improve logic
Signed-off-by: Sebastian <krupinski01@gmail.com>
This commit is contained in:
@@ -1,44 +1,41 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import type { EntityInterface } from '@MailManager/types/entity'
|
import { storeToRefs } from 'pinia'
|
||||||
import type { MessageInterface } from '@MailManager/types/message'
|
import { useMailStore } from '@/stores/mailStore'
|
||||||
import type { CollectionObject } from '@MailManager/models/collection'
|
import type { EntityObject } from '@MailManager/models'
|
||||||
|
|
||||||
// Props
|
withDefaults(defineProps<{
|
||||||
interface Props {
|
|
||||||
messages: EntityInterface<MessageInterface>[]
|
|
||||||
selectedMessage?: EntityInterface<MessageInterface> | null
|
|
||||||
selectedCollection?: CollectionObject | null
|
|
||||||
loading?: boolean
|
loading?: boolean
|
||||||
}
|
}>(), {
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
|
||||||
loading: false
|
loading: false
|
||||||
})
|
})
|
||||||
|
|
||||||
// Emits
|
// Emits
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
select: [message: EntityInterface<MessageInterface>]
|
select: [message: EntityObject]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
const mailStore = useMailStore()
|
||||||
|
const { currentMessages, selectedFolder, selectedMessage } = storeToRefs(mailStore)
|
||||||
|
|
||||||
// Check if message is selected
|
// Check if message is selected
|
||||||
const isSelected = (message: EntityInterface<MessageInterface>): boolean => {
|
const isSelected = (message: EntityObject): boolean => {
|
||||||
if (!props.selectedMessage) return false
|
if (!selectedMessage.value) return false
|
||||||
return (
|
return (
|
||||||
message.provider === props.selectedMessage.provider &&
|
message.provider === selectedMessage.value.provider &&
|
||||||
message.service === props.selectedMessage.service &&
|
message.service === selectedMessage.value.service &&
|
||||||
message.collection === props.selectedMessage.collection &&
|
message.collection === selectedMessage.value.collection &&
|
||||||
message.identifier === props.selectedMessage.identifier
|
message.identifier === selectedMessage.value.identifier
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if message is unread
|
// Check if message is unread
|
||||||
const isUnread = (message: EntityInterface<MessageInterface>): boolean => {
|
const isUnread = (message: EntityObject): boolean => {
|
||||||
return !message.properties.flags?.read
|
return !message.properties.flags?.read
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if message is flagged
|
// Check if message is flagged
|
||||||
const isFlagged = (message: EntityInterface<MessageInterface>): boolean => {
|
const isFlagged = (message: EntityObject): boolean => {
|
||||||
return message.properties.flags?.flagged || false
|
return message.properties.flags?.flagged || false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,13 +86,13 @@ const truncate = (text: string | null | undefined, length: number = 100): string
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle message click
|
// Handle message click
|
||||||
const handleMessageClick = (message: EntityInterface<MessageInterface>) => {
|
const handleMessageClick = (message: EntityObject) => {
|
||||||
emit('select', message)
|
emit('select', message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sorted messages (newest first)
|
// Sorted messages (newest first)
|
||||||
const sortedMessages = computed(() => {
|
const sortedMessages = computed(() => {
|
||||||
return [...props.messages].sort((a, b) => {
|
return [...currentMessages.value].sort((a, b) => {
|
||||||
const dateA = a.properties.date ? new Date(a.properties.date).getTime() : 0
|
const dateA = a.properties.date ? new Date(a.properties.date).getTime() : 0
|
||||||
const dateB = b.properties.date ? new Date(b.properties.date).getTime() : 0
|
const dateB = b.properties.date ? new Date(b.properties.date).getTime() : 0
|
||||||
return dateB - dateA
|
return dateB - dateA
|
||||||
@@ -104,38 +101,32 @@ const sortedMessages = computed(() => {
|
|||||||
|
|
||||||
// Read/Unread counts from collection properties
|
// Read/Unread counts from collection properties
|
||||||
const unreadCount = computed(() => {
|
const unreadCount = computed(() => {
|
||||||
return props.selectedCollection?.properties.unread ?? 0
|
return selectedFolder.value?.properties.unread ?? 0
|
||||||
})
|
|
||||||
|
|
||||||
const readCount = computed(() => {
|
|
||||||
const total = props.selectedCollection?.properties.total ?? 0
|
|
||||||
const unread = props.selectedCollection?.properties.unread ?? 0
|
|
||||||
return total - unread
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const totalCount = computed(() => {
|
const totalCount = computed(() => {
|
||||||
return props.selectedCollection?.properties.total ?? 0
|
return selectedFolder.value?.properties.total ?? 0
|
||||||
})
|
})
|
||||||
|
|
||||||
// True only when the collection explicitly provides total/unread counts
|
// True only when the collection explicitly provides total/unread counts
|
||||||
const hasCountData = computed(() => {
|
const hasCountData = computed(() => {
|
||||||
return props.selectedCollection?.properties.total != null
|
return selectedFolder.value?.properties.total != null
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="message-list">
|
<div class="message-list">
|
||||||
<!-- Header with folder name and counts -->
|
<!-- Header with folder name and counts -->
|
||||||
<div v-if="selectedCollection" class="message-list-header">
|
<div v-if="selectedFolder" class="message-list-header">
|
||||||
<h2 class="text-h6">{{ selectedCollection.properties.label || 'Folder' }}</h2>
|
<h2 class="text-h6">{{ selectedFolder.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="hasCountData">
|
||||||
<span class="unread-count">{{ unreadCount }}</span>
|
<span class="unread-count">{{ unreadCount }}</span>
|
||||||
<span class="mx-1">/</span>
|
<span class="mx-1">/</span>
|
||||||
<span>{{ totalCount }}</span>
|
<span>{{ totalCount }}</span>
|
||||||
</span>
|
</span>
|
||||||
<span v-else-if="messages.length > 0">
|
<span v-else-if="currentMessages.length > 0">
|
||||||
{{ messages.length }} loaded
|
{{ currentMessages.length }} loaded
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -151,7 +142,7 @@ const hasCountData = computed(() => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Empty state -->
|
<!-- Empty state -->
|
||||||
<div v-else-if="messages.length === 0" class="pa-8 text-center">
|
<div v-else-if="currentMessages.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">
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref, watch } from 'vue'
|
import { computed, ref, watch } from 'vue'
|
||||||
import { useUser } from '@KTXC'
|
import { useUser } from '@KTXC'
|
||||||
import type { EntityInterface } from '@MailManager/types/entity'
|
import type { EntityObject } from '@MailManager/models'
|
||||||
import type { MessageInterface } from '@MailManager/types/message'
|
|
||||||
import { MessageObject } from '@MailManager/models/message'
|
import { MessageObject } from '@MailManager/models/message'
|
||||||
import { SecurityLevel } from '@/utile/emailSanitizer'
|
import { SecurityLevel } from '@/utile/emailSanitizer'
|
||||||
import ReaderEmpty from './reader/ReaderEmpty.vue'
|
import ReaderEmpty from './reader/ReaderEmpty.vue'
|
||||||
@@ -12,7 +11,7 @@ import ReaderBody from './reader/ReaderBody.vue'
|
|||||||
|
|
||||||
// Props
|
// Props
|
||||||
interface Props {
|
interface Props {
|
||||||
message?: EntityInterface<MessageInterface> | null
|
message?: EntityObject | null
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
@@ -22,11 +21,11 @@ const { getSetting } = useUser()
|
|||||||
|
|
||||||
// Emits
|
// Emits
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
reply: [message: EntityInterface<MessageInterface>]
|
reply: [message: EntityObject]
|
||||||
forward: [message: EntityInterface<MessageInterface>]
|
forward: [message: EntityObject]
|
||||||
move: [message: EntityInterface<MessageInterface>]
|
move: [message: EntityObject]
|
||||||
delete: [message: EntityInterface<MessageInterface>]
|
delete: [message: EntityObject]
|
||||||
flag: [message: EntityInterface<MessageInterface>]
|
flag: [message: EntityObject]
|
||||||
compose: []
|
compose: []
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import type { EntityInterface } from '@MailManager/types/entity'
|
import type { EntityObject } from '@MailManager/models'
|
||||||
import type { MessageInterface } from '@MailManager/types/message'
|
|
||||||
import { SecurityLevel } from '@/utile/emailSanitizer'
|
import { SecurityLevel } from '@/utile/emailSanitizer'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
message: EntityInterface<MessageInterface>
|
message: EntityObject
|
||||||
isHtml: boolean
|
isHtml: boolean
|
||||||
allowImages: boolean
|
allowImages: boolean
|
||||||
securityLevel: SecurityLevel
|
securityLevel: SecurityLevel
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ const {
|
|||||||
selectedMessage,
|
selectedMessage,
|
||||||
composeMode,
|
composeMode,
|
||||||
composeReplyTo,
|
composeReplyTo,
|
||||||
currentMessages,
|
|
||||||
moveDialogVisible,
|
moveDialogVisible,
|
||||||
} = storeToRefs(mailStore)
|
} = storeToRefs(mailStore)
|
||||||
|
|
||||||
@@ -179,9 +178,6 @@ const handleFolderCreated = (folder: CollectionObject) => mailStore.notify(`Fold
|
|||||||
<!-- Message list panel -->
|
<!-- Message list panel -->
|
||||||
<div class="mail-list-panel">
|
<div class="mail-list-panel">
|
||||||
<MessageList
|
<MessageList
|
||||||
:messages="currentMessages"
|
|
||||||
:selected-message="selectedMessage"
|
|
||||||
:selected-collection="selectedFolder"
|
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
@select="handleMessageSelect"
|
@select="handleMessageSelect"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -6,13 +6,12 @@ import { useServicesStore } from '@MailManager/stores/servicesStore'
|
|||||||
import { useMailSync } from '@MailManager/composables/useMailSync'
|
import { useMailSync } from '@MailManager/composables/useMailSync'
|
||||||
import { useSnackbar } from '@KTXC'
|
import { useSnackbar } from '@KTXC'
|
||||||
import type { CollectionIdentifier, EntityIdentifier } from '@MailManager/types/common'
|
import type { CollectionIdentifier, EntityIdentifier } from '@MailManager/types/common'
|
||||||
import type { MessageInterface } from '@MailManager/types/message'
|
|
||||||
import type { ServiceObject, CollectionObject, EntityObject } from '@MailManager/models'
|
import type { ServiceObject, CollectionObject, EntityObject } from '@MailManager/models'
|
||||||
|
|
||||||
export const useMailStore = defineStore('mailStore', () => {
|
export const useMailStore = defineStore('mailStore', () => {
|
||||||
|
const servicesStore = useServicesStore()
|
||||||
const collectionsStore = useCollectionsStore()
|
const collectionsStore = useCollectionsStore()
|
||||||
const entitiesStore = useEntitiesStore()
|
const entitiesStore = useEntitiesStore()
|
||||||
const servicesStore = useServicesStore()
|
|
||||||
const { showSnackbar } = useSnackbar()
|
const { showSnackbar } = useSnackbar()
|
||||||
|
|
||||||
// Background mail sync
|
// Background mail sync
|
||||||
@@ -134,12 +133,20 @@ export const useMailStore = defineStore('mailStore', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Sync Helpers ──────────────────────────────────────────────────────────
|
// ── Helpers ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
function _serviceKey(provider: string, service: string | number) {
|
function _serviceKey(provider: string, service: string | number) {
|
||||||
return `${provider}:${String(service)}`
|
return `${provider}:${String(service)}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _collectionIdentifier(collection: CollectionObject): CollectionIdentifier {
|
||||||
|
return `${collection.provider}:${String(collection.service)}:${String(collection.identifier)}` as CollectionIdentifier
|
||||||
|
}
|
||||||
|
|
||||||
|
function _entityIdentifier(entity: EntityObject): EntityIdentifier {
|
||||||
|
return `${entity.provider}:${String(entity.service)}:${String(entity.collection)}:${String(entity.identifier)}` as EntityIdentifier
|
||||||
|
}
|
||||||
|
|
||||||
function _setServiceFolderLoading(provider: string, service: string | number, loadingState: boolean) {
|
function _setServiceFolderLoading(provider: string, service: string | number, loadingState: boolean) {
|
||||||
serviceFolderLoadingState.value = {
|
serviceFolderLoadingState.value = {
|
||||||
...serviceFolderLoadingState.value,
|
...serviceFolderLoadingState.value,
|
||||||
@@ -208,14 +215,6 @@ export const useMailStore = defineStore('mailStore', () => {
|
|||||||
return serviceFolderErrorState.value[_serviceKey(provider, service)] ?? null
|
return serviceFolderErrorState.value[_serviceKey(provider, service)] ?? null
|
||||||
}
|
}
|
||||||
|
|
||||||
function _entityIdentifier(entity: EntityObject): EntityIdentifier {
|
|
||||||
return `${entity.provider}:${String(entity.service)}:${String(entity.collection)}:${String(entity.identifier)}` as EntityIdentifier
|
|
||||||
}
|
|
||||||
|
|
||||||
function _collectionIdentifier(collection: CollectionObject): CollectionIdentifier {
|
|
||||||
return `${collection.provider}:${String(collection.service)}:${String(collection.identifier)}` as CollectionIdentifier
|
|
||||||
}
|
|
||||||
|
|
||||||
function _isSameMessage(left: EntityObject | null, right: EntityObject): boolean {
|
function _isSameMessage(left: EntityObject | null, right: EntityObject): boolean {
|
||||||
if (!left) {
|
if (!left) {
|
||||||
return false
|
return false
|
||||||
@@ -291,11 +290,6 @@ export const useMailStore = defineStore('mailStore', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deleteMessage(message: EntityObject) {
|
|
||||||
// TODO: implement delete via entity / collection store
|
|
||||||
console.log('[Mail] Delete message:', message.identifier)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function moveMessage(targetFolder: CollectionObject) {
|
async function moveMessage(targetFolder: CollectionObject) {
|
||||||
const message = moveMessageCandidate.value
|
const message = moveMessageCandidate.value
|
||||||
|
|
||||||
@@ -349,6 +343,11 @@ export const useMailStore = defineStore('mailStore', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function deleteMessage(message: EntityObject) {
|
||||||
|
// TODO: implement delete via entity / collection store
|
||||||
|
console.log('[Mail] Delete message:', message.identifier)
|
||||||
|
}
|
||||||
|
|
||||||
function toggleSidebar() {
|
function toggleSidebar() {
|
||||||
sidebarVisible.value = !sidebarVisible.value
|
sidebarVisible.value = !sidebarVisible.value
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user