Files
mail/src/components/reader/ReaderHeader.vue
2026-05-23 23:09:40 -04:00

176 lines
4.7 KiB
Vue

<script setup lang="ts">
import { computed } from 'vue'
import RecipientDetails from '@/components/common/RecipientDetails.vue'
import type { EntityObject } from '@MailManager/models';
interface Props {
entity: EntityObject | null
}
const props = defineProps<Props>()
const emit = defineEmits<{
downloadAttachment: [index: number]
}>()
const message = computed(() => {
return props.entity?.properties ?? null
})
const randomKey = computed(() => {
return Math.random().toString(36).substring(2, 15)
})
// Format date for display
const formatDate = (date: Date | string | null | undefined): string => {
if (!date) return ''
const messageDate = new Date(date)
return messageDate.toLocaleString('en-US', {
weekday: 'short',
year: 'numeric',
month: 'short',
day: 'numeric',
hour: 'numeric',
minute: '2-digit',
hour12: true
})
}
// Format file size for display
const formatFileSize = (bytes: number | undefined): string => {
if (!bytes) return ''
if (bytes < 1024) return bytes + ' B'
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB'
return (bytes / (1024 * 1024)).toFixed(1) + ' MB'
}
const download = async (index: number): Promise<void> => {
emit('downloadAttachment', index)
}
</script>
<template>
<div class="message-header pa-6">
<div class="text-h5 mb-4">{{ message?.subject || '(No subject)' }}</div>
<div class="d-flex align-center mb-3">
<v-avatar size="48" color="primary" class="mr-3">
<span class="text-white text-h6">
{{ (message?.from?.label || message?.from?.address || 'U')[0].toUpperCase() }}
</span>
</v-avatar>
<div class="flex-grow-1">
<div class="text-body-1 font-weight-medium">
<RecipientDetails :address="message?.from">
<template #default="{ label }">
<span class="contact-link">{{ label }}</span>
</template>
</RecipientDetails>
</div>
<div class="text-caption text-medium-emphasis">
{{ formatDate(message?.received || message?.sent) }}
</div>
</div>
</div>
<!-- Recipients -->
<div v-if="message?.to && message?.to.length > 0" class="text-body-2 mb-1">
<span class="text-medium-emphasis">To:</span>
<template v-for="(recipient, index) in message.to" :key="randomKey">
<RecipientDetails :address="recipient">
<template #default="{ label }">
<span class="contact-link">{{ label }}</span>
</template>
</RecipientDetails>
<span v-if="index < message.to.length - 1">, </span>
</template>
</div>
<div v-if="message?.cc && message?.cc.length > 0" class="text-body-2 mb-1">
<span class="text-medium-emphasis">Cc:</span>
<template v-for="(recipient, index) in message.cc" :key="randomKey">
<RecipientDetails :address="recipient">
<template #default="{ label }">
<span class="contact-link">{{ label }}</span>
</template>
</RecipientDetails>
<span v-if="index < message.cc.length - 1">, </span>
</template>
</div>
<!-- Attachments -->
<div v-if="message?.attachments && message?.attachments.length > 0" class="mt-4">
<div class="text-body-2 text-medium-emphasis mb-2">
Attachments ({{ message?.attachments.length }})
</div>
<div class="d-flex flex-wrap gap-2">
<div
v-for="(attachment, index) in message?.attachments"
:key="randomKey"
class="attachment-item"
>
<v-chip
prepend-icon="mdi-paperclip"
size="small"
variant="outlined"
class="attachment-chip"
@click="download(index)"
>
<span class="attachment-name">{{ attachment.name || 'Untitled' }}</span>
<span v-if="attachment.size" class="text-caption text-medium-emphasis ml-1">
({{ formatFileSize(attachment.size ?? undefined) }})
</span>
</v-chip>
</div>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
.message-header {
background-color: rgba(var(--v-theme-surface));
}
.gap-2 {
gap: 0.5rem;
}
.attachment-item {
display: flex;
flex-direction: column;
align-items: flex-start;
}
.attachment-chip {
max-width: 300px;
cursor: pointer;
.attachment-name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.attachment-error {
color: rgb(var(--v-theme-error));
margin-top: 0.25rem;
}
.contact-link {
display: inline-block;
border-radius: 4px;
padding: 1px 4px;
margin: -1px -4px;
transition: background-color 0.2s ease;
}
.contact-link:hover {
background-color: rgba(var(--v-theme-primary), 0.08);
}
</style>