352 lines
9.3 KiB
Vue
352 lines
9.3 KiB
Vue
<script setup lang="ts">
|
|
import { computed, onMounted } from 'vue'
|
|
import { storeToRefs } from 'pinia'
|
|
import { useDisplay } from 'vuetify'
|
|
import { useModuleStore } from '@KTXC'
|
|
import { useMailStore } from '@/stores/mailStore'
|
|
import type { CollectionObject, EntityObject } from '@MailManager/models'
|
|
import FolderTree from '@/components/FolderTree.vue'
|
|
import MessageList from '@/components/MessageList.vue'
|
|
import MessageReader from '@/components/MessageReader.vue'
|
|
import MessageComposer from '@/components/MessageComposer.vue'
|
|
import FolderSelectionDialog from '@/components/FolderSelectionDialog.vue'
|
|
import SettingsDialog from '@/components/settings/SettingsDialog.vue'
|
|
|
|
// Vuetify display for responsive behavior
|
|
const display = useDisplay()
|
|
const isMobile = computed(() => display.mdAndDown.value)
|
|
|
|
// Check if mail manager is available
|
|
const moduleStore = useModuleStore()
|
|
const isMailManagerAvailable = computed(() => {
|
|
return moduleStore.has('mail_manager') || moduleStore.has('MailManager')
|
|
})
|
|
|
|
// Mail module store
|
|
const mailStore = useMailStore()
|
|
|
|
// storeToRefs preserves reactivity for state and computed properties
|
|
const {
|
|
sidebarVisible,
|
|
settingsDialogVisible,
|
|
loading,
|
|
selectedFolder,
|
|
selectedMessage,
|
|
selectedMessageIds,
|
|
selectionModeActive,
|
|
composeMode,
|
|
composeReplyTo,
|
|
moveDialogVisible,
|
|
selectionCount,
|
|
hasSelection,
|
|
allCurrentMessagesSelected,
|
|
} = storeToRefs(mailStore)
|
|
|
|
// Complex store/composable objects accessed directly (not simple refs)
|
|
const { mailSync, entitiesStore } = mailStore
|
|
|
|
// Initialize
|
|
onMounted(async () => {
|
|
if (!isMailManagerAvailable.value) return
|
|
await mailStore.initialize()
|
|
})
|
|
|
|
// Handlers — thin wrappers that delegate to the store
|
|
const handleFolderSelect = (folder: CollectionObject) => mailStore.selectFolder(folder)
|
|
|
|
const handleMessageOpen = (message: EntityObject) => mailStore.selectMessage(message, isMobile.value)
|
|
|
|
const handleMessageSelectionToggle = (message: EntityObject) => mailStore.toggleMessageSelection(message)
|
|
|
|
const handleSelectionModeActivate = (message: EntityObject) => mailStore.activateSelectionMode(message)
|
|
|
|
const handleSelectAllToggle = (value: boolean) => {
|
|
if (value) {
|
|
mailStore.selectAllCurrentMessages()
|
|
return
|
|
}
|
|
|
|
mailStore.clearSelection()
|
|
}
|
|
|
|
const handleSelectionClear = () => mailStore.deactivateSelectionMode()
|
|
|
|
const handleSelectionMove = () => mailStore.openMoveDialogForSelection()
|
|
|
|
const handleCompose = (replyTo?: EntityObject) => mailStore.openCompose(replyTo)
|
|
|
|
const handleComposeClose = () => mailStore.closeCompose()
|
|
|
|
const handleComposeSent = () => mailStore.afterSent()
|
|
|
|
const handleReply = (message: EntityObject) => mailStore.openCompose(message)
|
|
|
|
const handleDelete = (message: EntityObject) => mailStore.deleteMessage(message)
|
|
|
|
const handleMove = (message: EntityObject) => mailStore.openMoveDialog(message)
|
|
|
|
const handleMoveConfirm = async (folder: CollectionObject) => {
|
|
await mailStore.moveMessages(folder)
|
|
}
|
|
|
|
const handleMoveCancel = () => mailStore.closeMoveDialog()
|
|
|
|
const toggleSidebar = () => mailStore.toggleSidebar()
|
|
|
|
const handleSettingsOpen = () => mailStore.openSettings()
|
|
|
|
const handleFolderCreated = (folder: CollectionObject) => mailStore.notify(`Folder "${folder.properties.label}" created`, 'success')
|
|
</script>
|
|
|
|
<template>
|
|
<!-- Manager Unavailable -->
|
|
<div v-if="!isMailManagerAvailable" class="mail-unavailable">
|
|
<v-alert
|
|
type="warning"
|
|
variant="outlined"
|
|
class="mail-unavailable-alert"
|
|
>
|
|
<v-icon size="64" color="warning" class="mb-6">mdi-email-off-outline</v-icon>
|
|
<h2 class="text-h5 font-weight-bold mb-4">Mail Manager Not Available</h2>
|
|
<p>
|
|
The Mail Manager module is not installed or enabled.
|
|
This module requires the <strong>mail_manager</strong> module to function properly.
|
|
</p>
|
|
<p class="mb-0 mt-2">
|
|
Please contact your system administrator to install and enable the
|
|
<code>mail_manager</code> module.
|
|
</p>
|
|
</v-alert>
|
|
</div>
|
|
|
|
<!-- Normal UI -->
|
|
<div v-else class="mail-container">
|
|
<!-- Top toolbar -->
|
|
<v-app-bar class="mail-toolbar" elevation="0" density="compact">
|
|
<v-app-bar-nav-icon
|
|
v-if="isMobile"
|
|
@click="toggleSidebar"
|
|
/>
|
|
|
|
<v-app-bar-title>Mail</v-app-bar-title>
|
|
|
|
<v-spacer />
|
|
|
|
<v-btn
|
|
icon="mdi-pencil"
|
|
@click="handleCompose()"
|
|
color="primary"
|
|
variant="text"
|
|
>
|
|
<v-icon>mdi-pencil</v-icon>
|
|
<v-tooltip activator="parent" location="bottom">Compose</v-tooltip>
|
|
</v-btn>
|
|
|
|
<v-btn
|
|
icon="mdi-refresh"
|
|
@click="mailSync.sync()"
|
|
:loading="mailSync.isRunning && entitiesStore.transceiving"
|
|
variant="text"
|
|
>
|
|
<v-icon>mdi-refresh</v-icon>
|
|
<v-tooltip activator="parent" location="bottom">
|
|
Refresh {{ mailSync.lastSync ? `(Last: ${new Date(mailSync.lastSync).toLocaleTimeString()})` : '' }}
|
|
</v-tooltip>
|
|
</v-btn>
|
|
|
|
<v-icon
|
|
v-if="mailSync.isRunning"
|
|
color="success"
|
|
size="small"
|
|
class="ml-2"
|
|
>
|
|
mdi-sync
|
|
</v-icon>
|
|
</v-app-bar>
|
|
|
|
<!-- Main content area -->
|
|
<div class="mail-content">
|
|
<!-- Folder tree sidebar -->
|
|
<v-navigation-drawer
|
|
v-model="sidebarVisible"
|
|
:permanent="!isMobile"
|
|
:temporary="isMobile"
|
|
width="280"
|
|
class="mail-sidebar"
|
|
>
|
|
<FolderTree
|
|
:selected-folder="selectedFolder"
|
|
@select="handleFolderSelect"
|
|
@folder-created="handleFolderCreated"
|
|
/>
|
|
|
|
<template #append>
|
|
<div class="pa-2">
|
|
<v-btn
|
|
block
|
|
variant="text"
|
|
prepend-icon="mdi-cog"
|
|
@click="handleSettingsOpen"
|
|
>
|
|
Settings
|
|
</v-btn>
|
|
</div>
|
|
</template>
|
|
</v-navigation-drawer>
|
|
|
|
<!-- Main area with message list and reader -->
|
|
<div class="mail-main">
|
|
<div class="mail-wrapper">
|
|
<!-- Message list panel -->
|
|
<div class="mail-list-panel">
|
|
<MessageList
|
|
<<<<<<< HEAD
|
|
:messages="currentMessages"
|
|
:selected-message="selectedMessage"
|
|
:selected-message-ids="selectedMessageIds"
|
|
:selection-mode-active="selectionModeActive"
|
|
:selection-count="selectionCount"
|
|
:has-selection="hasSelection"
|
|
:all-current-messages-selected="allCurrentMessagesSelected"
|
|
:selected-collection="selectedFolder"
|
|
=======
|
|
>>>>>>> 749d922 (refactor: improve logic)
|
|
:loading="loading"
|
|
@open="handleMessageOpen"
|
|
@toggle-selection="handleMessageSelectionToggle"
|
|
@activate-selection-mode="handleSelectionModeActivate"
|
|
@toggle-select-all="handleSelectAllToggle"
|
|
@clear-selection="handleSelectionClear"
|
|
@move-selection="handleSelectionMove"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Reader/Composer panel -->
|
|
<div class="mail-reader-panel">
|
|
<MessageComposer
|
|
v-if="composeMode"
|
|
:reply-to="composeReplyTo"
|
|
:folder="selectedFolder"
|
|
@close="handleComposeClose"
|
|
@sent="handleComposeSent"
|
|
/>
|
|
|
|
<MessageReader
|
|
v-else
|
|
:message="selectedMessage"
|
|
@reply="handleReply"
|
|
@move="handleMove"
|
|
@delete="handleDelete"
|
|
@compose="handleCompose()"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Settings Dialog -->
|
|
<SettingsDialog v-model="settingsDialogVisible" />
|
|
|
|
<FolderSelectionDialog
|
|
v-model="moveDialogVisible"
|
|
:loading="loading"
|
|
title="Move To"
|
|
confirm-text="Move"
|
|
empty-text="No other folders are available in this account."
|
|
@select="handleMoveConfirm"
|
|
@cancel="handleMoveCancel"
|
|
/>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped lang="scss">
|
|
.mail-unavailable {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
height: 100vh;
|
|
padding: 48px;
|
|
text-align: center;
|
|
width: 100%;
|
|
}
|
|
|
|
.mail-unavailable-alert {
|
|
width: 100%;
|
|
text-align: left;
|
|
}
|
|
|
|
.mail-container {
|
|
display: flex;
|
|
flex-direction: column;
|
|
height: 100vh;
|
|
isolation: isolate;
|
|
}
|
|
|
|
.mail-toolbar {
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.mail-content {
|
|
display: flex;
|
|
flex: 1;
|
|
overflow: hidden;
|
|
position: relative;
|
|
}
|
|
|
|
.mail-sidebar {
|
|
border-right: 1px solid rgb(var(--v-border-color));
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.mail-main {
|
|
flex: 1;
|
|
display: flex;
|
|
overflow: hidden;
|
|
min-width: 0;
|
|
}
|
|
|
|
.mail-wrapper {
|
|
flex: 1;
|
|
display: flex;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.mail-list-panel {
|
|
width: 320px;
|
|
min-width: 280px;
|
|
max-width: 450px;
|
|
border-right: 1px solid rgb(var(--v-border-color));
|
|
overflow-y: auto;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.mail-reader-panel {
|
|
flex: 1;
|
|
overflow: hidden;
|
|
min-height: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
/* Responsive adjustments */
|
|
@media (max-width: 960px) {
|
|
.mail-wrapper {
|
|
flex-direction: column;
|
|
}
|
|
|
|
.mail-list-panel {
|
|
width: 100%;
|
|
max-width: 100%;
|
|
border-right: none;
|
|
border-bottom: 1px solid rgb(var(--v-border-color));
|
|
max-height: 50%;
|
|
}
|
|
|
|
.mail-reader-panel {
|
|
width: 100%;
|
|
}
|
|
}
|
|
</style>
|