34dc636d13
Signed-off-by: Sebastian <krupinski01@gmail.com>
235 lines
5.9 KiB
Vue
235 lines
5.9 KiB
Vue
<script setup lang="ts">
|
|
import { computed, ref, watch } from 'vue'
|
|
import { useUser } from '@KTXC'
|
|
import { useMailUiStore } from '@/stores/mailUiStore'
|
|
import type { EntityObject, MessageObject } from '@MailManager/models'
|
|
import { SecurityLevel } from '@/utile/emailSanitizer'
|
|
import { mediaViewerCanDisplay, mediaViewerComponent } from '@/services/mediaViewer'
|
|
import type { MediaViewerItem } from '@/types/mediaViewer'
|
|
import ReaderEmpty from './reader/ReaderEmpty.vue'
|
|
import ReaderToolbar from './reader/ReaderToolbar.vue'
|
|
import ReaderHeader from './reader/ReaderHeader.vue'
|
|
import ReaderBody from './reader/ReaderBody.vue'
|
|
|
|
// Props
|
|
const props = defineProps<{
|
|
entity: EntityObject | null
|
|
}>()
|
|
|
|
// Emits
|
|
const emit = defineEmits<{
|
|
reply: [entity: EntityObject]
|
|
forward: [entity: EntityObject]
|
|
move: [entity: EntityObject]
|
|
delete: [entity: EntityObject]
|
|
flag: [entity: EntityObject]
|
|
compose: []
|
|
}>()
|
|
|
|
// User settings
|
|
const { getSetting } = useUser()
|
|
const mailUiStore = useMailUiStore()
|
|
|
|
// Security overrides
|
|
const allowImages = ref(false)
|
|
const overrideSecurityLevel = ref<SecurityLevel | null>(null)
|
|
|
|
// Viewer state
|
|
const showViewer = ref(false)
|
|
const viewerIndex = ref(0)
|
|
|
|
// Computed
|
|
const message = computed<MessageObject | null>(() => {
|
|
return props.entity?.properties ?? null
|
|
})
|
|
|
|
const isHtml = computed(() => {
|
|
return message.value?.hasHtmlContent() ?? false
|
|
})
|
|
|
|
// Security preferences from user settings
|
|
const securityLevel = computed(() => {
|
|
return (getSetting('mail.security.level') as SecurityLevel) || SecurityLevel.MODERATE
|
|
})
|
|
|
|
const allowImagesDefault = computed(() => {
|
|
return getSetting('mail.security.allowImagesDefault') as boolean || false
|
|
})
|
|
|
|
// Computed effective security level (use override if set, otherwise default)
|
|
const effectiveSecurityLevel = computed(() => {
|
|
return overrideSecurityLevel.value ?? securityLevel.value
|
|
})
|
|
|
|
// Preview state
|
|
const previewDialog = computed(() => mediaViewerComponent('dialog'))
|
|
const previewItems = computed<MediaViewerItem[]>(() => {
|
|
const attachments = message.value?.attachments ?? []
|
|
const items: MediaViewerItem[] = []
|
|
attachments.forEach((attachment, index) => {
|
|
if (!mediaViewerCanDisplay(attachment.type)) return
|
|
items.push({
|
|
id: attachment.partId || attachment.blobId || attachment.cid || `att-${index}`,
|
|
title: attachment.name || 'Attachment',
|
|
mime: attachment.type || '',
|
|
size: attachment.size ?? null,
|
|
meta: { index, attachment },
|
|
})
|
|
})
|
|
return items
|
|
})
|
|
|
|
// Reset overrides when message changes
|
|
watch(() => props.entity, () => {
|
|
allowImages.value = allowImagesDefault.value
|
|
overrideSecurityLevel.value = null
|
|
})
|
|
|
|
// Close the viewer when switching messages.
|
|
watch(() => props.entity, () => {
|
|
showViewer.value = false
|
|
})
|
|
|
|
// Toggle images for current message only
|
|
const toggleImages = () => {
|
|
allowImages.value = !allowImages.value
|
|
}
|
|
|
|
// Set security level override for current message only
|
|
const setSecurityLevel = (level: SecurityLevel) => {
|
|
overrideSecurityLevel.value = level
|
|
}
|
|
|
|
// Handlers
|
|
const handleReply = () => {
|
|
if (props.entity) {
|
|
emit('reply', props.entity)
|
|
}
|
|
}
|
|
|
|
const handleForward = () => {
|
|
if (props.entity) {
|
|
emit('forward', props.entity)
|
|
}
|
|
}
|
|
|
|
const handleDelete = () => {
|
|
if (props.entity) {
|
|
emit('delete', props.entity)
|
|
}
|
|
}
|
|
|
|
const handleMove = () => {
|
|
if (props.entity) {
|
|
emit('move', props.entity)
|
|
}
|
|
}
|
|
|
|
const handleDownload = async (partIndex?: number) => {
|
|
if (!props.entity) {
|
|
return
|
|
}
|
|
|
|
await mailUiStore.downloadMessage(props.entity, partIndex)
|
|
}
|
|
|
|
const handleFlag = () => {
|
|
if (props.entity) {
|
|
emit('flag', props.entity)
|
|
}
|
|
}
|
|
|
|
const handleCompose = () => {
|
|
emit('compose')
|
|
}
|
|
|
|
const handlePreview = (index: number) => {
|
|
const pos = previewItems.value.findIndex(item => item.meta.index === index)
|
|
if (pos < 0) return
|
|
viewerIndex.value = pos
|
|
showViewer.value = true
|
|
}
|
|
|
|
const handleViewerRetrieve = (item: MediaViewerItem): Promise<Blob> => {
|
|
if (!props.entity) return Promise.reject(new Error('No message selected'))
|
|
return mailUiStore.attachmentBlob(props.entity, item.meta.attachment)
|
|
}
|
|
|
|
const handleViewerDownload = (item: MediaViewerItem) => {
|
|
if (props.entity) mailUiStore.downloadMessage(props.entity, item.meta.index)
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="message-reader">
|
|
<!-- Empty state -->
|
|
<ReaderEmpty
|
|
v-if="!message"
|
|
@compose="handleCompose"
|
|
/>
|
|
|
|
<!-- Message viewer -->
|
|
<template v-else>
|
|
<ReaderToolbar
|
|
:is-html="isHtml"
|
|
:allow-images="allowImages"
|
|
:security-level="effectiveSecurityLevel"
|
|
:is-security-overridden="overrideSecurityLevel !== null"
|
|
@reply="handleReply"
|
|
@forward="handleForward"
|
|
@move="handleMove"
|
|
@delete="handleDelete"
|
|
@flag="handleFlag"
|
|
@download="handleDownload"
|
|
@toggle-images="toggleImages"
|
|
@set-security-level="setSecurityLevel"
|
|
/>
|
|
|
|
<!-- Message content -->
|
|
<div class="message-content">
|
|
<ReaderHeader
|
|
:entity="props.entity"
|
|
@download="handleDownload"
|
|
@preview="handlePreview"
|
|
/>
|
|
|
|
<v-divider />
|
|
|
|
<ReaderBody
|
|
:message="message!"
|
|
:security-level="effectiveSecurityLevel"
|
|
:allow-images="allowImages"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Inline attachment viewer (provided by the media_viewer module) -->
|
|
<component
|
|
:is="previewDialog"
|
|
v-if="previewDialog"
|
|
v-model="showViewer"
|
|
:items="previewItems"
|
|
:index="viewerIndex"
|
|
:source="handleViewerRetrieve"
|
|
:download="handleViewerDownload"
|
|
:navigation="true"
|
|
@update:index="viewerIndex = $event"
|
|
/>
|
|
</template>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped lang="scss">
|
|
.message-reader {
|
|
height: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.message-content {
|
|
flex: 1;
|
|
overflow-y: auto;
|
|
overflow-x: hidden;
|
|
}
|
|
</style>
|