Files
mail/src/components/FolderTreeView.vue
2026-05-05 23:42:27 -04:00

221 lines
7.7 KiB
Vue

<script setup lang="ts">
import { useCollectionsStore } from '@MailManager/stores/collectionsStore'
import type { CollectionObject } from '@MailManager/models/collection'
import type { ServiceObject } from '@MailManager/models'
import FolderTreeNode from './FolderTreeNode.vue'
// Props
interface Props {
selectedFolder?: CollectionObject | null
serviceGroups: Array<{
service: ServiceObject
loading: boolean
loaded: boolean
error: string | null
}>
}
const props = defineProps<Props>()
const collectionsStore = useCollectionsStore()
// Emits
const emit = defineEmits<{
select: [folder: CollectionObject]
createFolder: [service: ServiceObject, parentFolder: CollectionObject | null]
editFolder: [service: ServiceObject, folder: CollectionObject]
deleteFolder: [service: ServiceObject, folder: CollectionObject]
}>()
const getRootFolders = (service: ServiceObject): CollectionObject[] => {
return collectionsStore.collectionsInCollection(service.provider, service.identifier, null)
}
const getServiceFolders = (service: ServiceObject): CollectionObject[] => {
return collectionsStore.collectionsForService(service.provider, service.identifier)
}
</script>
<template>
<div>
<template v-for="group in serviceGroups" :key="`${group.service.provider}-${group.service.identifier}`">
<!-- Service account group -->
<v-list-group v-if="serviceGroups.length > 1" class="no-indent">
<template v-slot:activator="{ props: activatorProps }">
<v-list-item
v-bind="activatorProps"
class="account-header-item"
:title="group.service.label || 'Mail Account'"
:subtitle="group.service.primaryAddress || undefined"
>
<template v-slot:prepend>
<v-icon icon="mdi-email-outline" />
</template>
<template v-slot:append>
<v-btn
icon="mdi-folder-plus"
variant="text"
size="small"
density="compact"
@click.stop="emit('createFolder', group.service, null)"
>
<v-icon>mdi-folder-plus</v-icon>
<v-tooltip activator="parent" location="bottom">New Folder</v-tooltip>
</v-btn>
</template>
</v-list-item>
</template>
<FolderTreeNode
v-for="folder in getRootFolders(group.service)"
:key="`${folder.provider}-${folder.service}-${folder.identifier}`"
:folder="folder"
:service="group.service"
:selected-folder="selectedFolder"
@select="emit('select', $event)"
@create-subfolder="(service, parentFolder) => emit('createFolder', service, parentFolder)"
@edit-folder="(service, folder) => emit('editFolder', service, folder)"
@delete-folder="(service, folder) => emit('deleteFolder', service, folder)"
/>
<v-list-item v-if="group.loading && getServiceFolders(group.service).length === 0" disabled class="folder-status-item">
<template v-slot:prepend>
<v-progress-circular indeterminate size="18" width="2" color="primary" />
</template>
<v-list-item-title>Loading folders</v-list-item-title>
</v-list-item>
<v-list-item
v-else-if="group.error && getServiceFolders(group.service).length === 0"
disabled
class="folder-status-item"
>
<template v-slot:prepend>
<v-icon icon="mdi-alert-circle-outline" color="error" />
</template>
<v-list-item-title>Folders unavailable</v-list-item-title>
<v-list-item-subtitle>{{ group.error }}</v-list-item-subtitle>
</v-list-item>
<v-list-item
v-else-if="group.loaded && getServiceFolders(group.service).length === 0"
disabled
class="folder-status-item"
>
<template v-slot:prepend>
<v-icon icon="mdi-folder-off-outline" />
</template>
<v-list-item-title>No folders found</v-list-item-title>
</v-list-item>
</v-list-group>
<!-- Single service - show folders directly -->
<template v-else>
<v-list-item
class="account-header-item account-header-static"
:title="group.service.label || 'Mail Account'"
:subtitle="group.service.primaryAddress || undefined"
>
<template v-slot:prepend>
<v-icon icon="mdi-email-outline" />
</template>
<template v-slot:append>
<v-btn
icon="mdi-folder-plus"
variant="text"
size="small"
density="compact"
@click.stop="emit('createFolder', group.service, null)"
>
<v-icon>mdi-folder-plus</v-icon>
<v-tooltip activator="parent" location="bottom">New Folder</v-tooltip>
</v-btn>
</template>
</v-list-item>
<!-- Header with New Folder button -->
<v-list-subheader class="d-flex align-center">
<span class="flex-grow-1">FOLDERS</span>
<v-btn
icon="mdi-folder-plus"
variant="text"
size="x-small"
@click="emit('createFolder', group.service, null)"
>
<v-icon size="small">mdi-folder-plus</v-icon>
<v-tooltip activator="parent" location="bottom">New Folder</v-tooltip>
</v-btn>
</v-list-subheader>
<FolderTreeNode
v-for="folder in getRootFolders(group.service)"
:key="`${folder.provider}-${folder.service}-${folder.identifier}`"
:folder="folder"
:service="group.service"
:selected-folder="selectedFolder"
@select="emit('select', $event)"
@create-subfolder="(service, parentFolder) => emit('createFolder', service, parentFolder)"
@edit-folder="(service, folder) => emit('editFolder', service, folder)"
@delete-folder="(service, folder) => emit('deleteFolder', service, folder)"
/>
<v-list-item v-if="group.loading && getServiceFolders(group.service).length === 0" disabled class="folder-status-item">
<template v-slot:prepend>
<v-progress-circular indeterminate size="18" width="2" color="primary" />
</template>
<v-list-item-title>Loading folders</v-list-item-title>
</v-list-item>
<v-list-item
v-else-if="group.error && getServiceFolders(group.service).length === 0"
disabled
class="folder-status-item"
>
<template v-slot:prepend>
<v-icon icon="mdi-alert-circle-outline" color="error" />
</template>
<v-list-item-title>Folders unavailable</v-list-item-title>
<v-list-item-subtitle>{{ group.error }}</v-list-item-subtitle>
</v-list-item>
<v-list-item
v-else-if="group.loaded && getServiceFolders(group.service).length === 0"
disabled
class="folder-status-item"
>
<template v-slot:prepend>
<v-icon icon="mdi-folder-off-outline" />
</template>
<v-list-item-title>No folders found</v-list-item-title>
</v-list-item>
</template>
</template>
</div>
</template>
<style scoped>
.v-list-item--active {
background-color: rgba(var(--v-theme-primary), 0.12);
}
.account-header-item {
--v-list-item-prepend-size: 22px;
background-color: rgba(var(--v-theme-primary), 0.1);
border-radius: 6px;
}
.account-header-static {
margin-bottom: 4px;
}
.account-header-item :deep(.v-list-item__prepend) {
padding-inline-start: 4px;
margin-inline-end: 2px;
}
.folder-status-item {
padding-inline-start: 16px;
}
</style>