176 lines
4.0 KiB
Vue
176 lines
4.0 KiB
Vue
<script setup lang="ts">
|
|
import { computed, ref, watch } from 'vue'
|
|
import { useUser } from '@KTXC'
|
|
import type { EntityObject, MessageObject } from '@MailManager/models'
|
|
import { SecurityLevel } from '@/utile/emailSanitizer'
|
|
import { useMailStore } from '@/stores/mailStore'
|
|
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 mailStore = useMailStore()
|
|
|
|
// Per-message overrides
|
|
const allowImages = ref(false)
|
|
const overrideSecurityLevel = ref<SecurityLevel | null>(null)
|
|
|
|
// 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
|
|
})
|
|
|
|
// Reset overrides when message changes
|
|
watch(() => props.entity, () => {
|
|
allowImages.value = allowImagesDefault.value
|
|
overrideSecurityLevel.value = null
|
|
})
|
|
|
|
// 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 mailStore.downloadMessage(props.entity, partIndex)
|
|
}
|
|
|
|
const handleFlag = () => {
|
|
if (props.entity) {
|
|
emit('flag', props.entity)
|
|
}
|
|
}
|
|
|
|
const handleCompose = () => {
|
|
emit('compose')
|
|
}
|
|
</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-attachment="handleDownload"
|
|
/>
|
|
|
|
<v-divider />
|
|
|
|
<ReaderBody
|
|
:message="message!"
|
|
:security-level="effectiveSecurityLevel"
|
|
:allow-images="allowImages"
|
|
/>
|
|
</div>
|
|
</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>
|