@@ -4,52 +4,56 @@ import { useCollectionsStore } from '@MailManager/stores/collectionsStore'
|
||||
import { useEntitiesStore } from '@MailManager/stores/entitiesStore'
|
||||
import { useServicesStore } from '@MailManager/stores/servicesStore'
|
||||
import { useMailSync } from '@MailManager/composables/useMailSync'
|
||||
import { useSnackbar } from '@KTXC'
|
||||
import type { CollectionObject } from '@MailManager/models/collection'
|
||||
import type { EntityInterface } from '@MailManager/types/entity'
|
||||
import type { MessageInterface } from '@MailManager/types/message'
|
||||
import type { ServiceObject } from '@MailManager/models'
|
||||
|
||||
export const useMailStore = defineStore('mailStore', () => {
|
||||
const collectionsStore = useCollectionsStore()
|
||||
const entitiesStore = useEntitiesStore()
|
||||
const servicesStore = useServicesStore()
|
||||
const { showSnackbar } = useSnackbar()
|
||||
|
||||
// Background mail sync
|
||||
const mailSync = useMailSync({
|
||||
const mailSyncController = useMailSync({
|
||||
interval: 30000,
|
||||
autoStart: false,
|
||||
fetchDetails: true,
|
||||
})
|
||||
const mailSync = {
|
||||
isRunning: mailSyncController.isRunning,
|
||||
lastSync: mailSyncController.lastSync,
|
||||
error: mailSyncController.error,
|
||||
sync: mailSyncController.sync,
|
||||
start: mailSyncController.start,
|
||||
stop: mailSyncController.stop,
|
||||
restart: mailSyncController.restart,
|
||||
}
|
||||
|
||||
// ── UI State ──────────────────────────────────────────────────────────────
|
||||
|
||||
// ── General State ─────────────────-───────────────────────────────────────
|
||||
const sidebarVisible = ref(true)
|
||||
const settingsDialogVisible = ref(false)
|
||||
const loading = ref(false)
|
||||
const serviceFolderLoadingState = ref<Record<string, boolean>>({})
|
||||
const serviceFolderLoadedState = ref<Record<string, boolean>>({})
|
||||
const serviceFolderErrorState = ref<Record<string, string | null>>({})
|
||||
|
||||
// ── Selection State ───────────────────────────────────────────────────────
|
||||
|
||||
const selectedFolder = shallowRef<CollectionObject | null>(null)
|
||||
const selectedMessage = shallowRef<EntityInterface<MessageInterface> | null>(null)
|
||||
|
||||
// ── Compose State ─────────────────────────────────────────────────────────
|
||||
|
||||
const composeMode = ref(false)
|
||||
const composeReplyTo = shallowRef<EntityInterface<MessageInterface> | null>(null)
|
||||
|
||||
// ── Notification State ────────────────────────────────────────────────────
|
||||
|
||||
const snackbarVisible = ref(false)
|
||||
const snackbarMessage = ref('')
|
||||
const snackbarColor = ref<'success' | 'error' | 'info' | 'warning'>('success')
|
||||
|
||||
// ── Computed ──────────────────────────────────────────────────────────────
|
||||
|
||||
const currentMessages = computed(() => {
|
||||
if (!selectedFolder.value) return []
|
||||
|
||||
const folder = selectedFolder.value
|
||||
|
||||
// Access entitiesStore.entities (reactive computed array) so Vue tracks it
|
||||
return entitiesStore.entities.filter(e =>
|
||||
e.provider === folder.provider &&
|
||||
String(e.service) === String(folder.service) &&
|
||||
@@ -57,14 +61,109 @@ export const useMailStore = defineStore('mailStore', () => {
|
||||
)
|
||||
})
|
||||
|
||||
// ── Initialization ────────────────────────────────────────────────────────
|
||||
|
||||
async function initialize() {
|
||||
loading.value = true
|
||||
try {
|
||||
await servicesStore.list()
|
||||
|
||||
const services = [...servicesStore.services]
|
||||
services.forEach(service => {
|
||||
void loadFoldersForService(service,{ selectInbox: true })
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('[Mail] Failed to initialize:', error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function loadFoldersForService(
|
||||
service: ServiceObject,
|
||||
options: { selectInbox?: boolean } = {},
|
||||
) {
|
||||
|
||||
if (service.identifier === null) {
|
||||
return
|
||||
}
|
||||
|
||||
_setServiceFolderLoading(service.provider, service.identifier, true)
|
||||
_setServiceFolderError(service.provider, service.identifier, null)
|
||||
|
||||
try {
|
||||
// retrieve folders for service
|
||||
const collections = await collectionsStore.list({
|
||||
[service.provider]: {
|
||||
[String(service.identifier)]: true,
|
||||
},
|
||||
})
|
||||
|
||||
_setServiceFolderLoaded(service.provider, service.identifier, true)
|
||||
|
||||
if (options.selectInbox && !selectedFolder.value) {
|
||||
const inbox = Object.values(collections).find(
|
||||
folder =>
|
||||
folder.provider === service.provider &&
|
||||
String(folder.service) === String(service.identifier) &&
|
||||
(folder.properties.role === 'inbox' ||
|
||||
String(folder.identifier).toLowerCase() === 'inbox'),
|
||||
)
|
||||
|
||||
if (inbox) {
|
||||
await selectFolder(inbox)
|
||||
}
|
||||
}
|
||||
|
||||
_updateSyncSources()
|
||||
return collections
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : 'Failed to load folders'
|
||||
_setServiceFolderError(service.provider, service.identifier, message)
|
||||
console.error(
|
||||
`[Mail] Failed to load folders for ${service.provider}:${String(service.identifier)}:`,
|
||||
error,
|
||||
)
|
||||
_updateSyncSources()
|
||||
return {}
|
||||
} finally {
|
||||
_setServiceFolderLoading(service.provider, service.identifier, false)
|
||||
}
|
||||
}
|
||||
|
||||
// ── Sync Helpers ──────────────────────────────────────────────────────────
|
||||
|
||||
function _serviceKey(provider: string, service: string | number) {
|
||||
return `${provider}:${String(service)}`
|
||||
}
|
||||
|
||||
function _setServiceFolderLoading(provider: string, service: string | number, loadingState: boolean) {
|
||||
serviceFolderLoadingState.value = {
|
||||
...serviceFolderLoadingState.value,
|
||||
[_serviceKey(provider, service)]: loadingState,
|
||||
}
|
||||
}
|
||||
|
||||
function _setServiceFolderLoaded(provider: string, service: string | number, loaded: boolean) {
|
||||
serviceFolderLoadedState.value = {
|
||||
...serviceFolderLoadedState.value,
|
||||
[_serviceKey(provider, service)]: loaded,
|
||||
}
|
||||
}
|
||||
|
||||
function _setServiceFolderError(provider: string, service: string | number, error: string | null) {
|
||||
serviceFolderErrorState.value = {
|
||||
...serviceFolderErrorState.value,
|
||||
[_serviceKey(provider, service)]: error,
|
||||
}
|
||||
}
|
||||
|
||||
function _updateSyncSources() {
|
||||
mailSync.clearSources()
|
||||
mailSyncController.clearSources()
|
||||
|
||||
// Track the currently selected folder
|
||||
if (selectedFolder.value) {
|
||||
mailSync.addSource({
|
||||
mailSyncController.addSource({
|
||||
provider: selectedFolder.value.provider,
|
||||
service: selectedFolder.value.service,
|
||||
collections: [selectedFolder.value.identifier],
|
||||
@@ -73,15 +172,15 @@ export const useMailStore = defineStore('mailStore', () => {
|
||||
|
||||
// Always track inboxes for each account (for new-mail notifications)
|
||||
servicesStore.services.forEach(service => {
|
||||
const inboxes = collectionsStore.collections.filter(
|
||||
const inboxes = collectionsStore.collectionsForService(service.provider, service.identifier).filter(
|
||||
c =>
|
||||
c.service === service.identifier &&
|
||||
String(c.service) === String(service.identifier) &&
|
||||
(c.properties.role === 'inbox' ||
|
||||
String(c.identifier).toLowerCase() === 'inbox'),
|
||||
)
|
||||
|
||||
if (inboxes.length > 0) {
|
||||
mailSync.addSource({
|
||||
mailSyncController.addSource({
|
||||
provider: service.provider,
|
||||
service: service.identifier as string | number,
|
||||
collections: inboxes.map(inbox => inbox.identifier),
|
||||
@@ -89,11 +188,23 @@ export const useMailStore = defineStore('mailStore', () => {
|
||||
}
|
||||
})
|
||||
|
||||
if (mailSync.sources.value.length > 0 && !mailSync.isRunning.value) {
|
||||
mailSync.start()
|
||||
if (mailSyncController.sources.value.length > 0 && !mailSyncController.isRunning.value) {
|
||||
mailSyncController.start()
|
||||
}
|
||||
}
|
||||
|
||||
function isServiceFolderLoading(provider: string, service: string | number) {
|
||||
return serviceFolderLoadingState.value[_serviceKey(provider, service)] === true
|
||||
}
|
||||
|
||||
function hasServiceFoldersLoaded(provider: string, service: string | number) {
|
||||
return serviceFolderLoadedState.value[_serviceKey(provider, service)] === true
|
||||
}
|
||||
|
||||
function getServiceFolderError(provider: string, service: string | number) {
|
||||
return serviceFolderErrorState.value[_serviceKey(provider, service)] ?? null
|
||||
}
|
||||
|
||||
// ── Actions ───────────────────────────────────────────────────────────────
|
||||
|
||||
async function selectFolder(folder: CollectionObject) {
|
||||
@@ -159,38 +270,8 @@ export const useMailStore = defineStore('mailStore', () => {
|
||||
settingsDialogVisible.value = true
|
||||
}
|
||||
|
||||
function notify(message: string, color: typeof snackbarColor.value = 'success') {
|
||||
snackbarMessage.value = message
|
||||
snackbarColor.value = color
|
||||
snackbarVisible.value = true
|
||||
}
|
||||
|
||||
async function onFolderCreated(folder: CollectionObject) {
|
||||
notify(`Folder "${folder.properties.label}" created successfully`)
|
||||
// Reload collections so the sidebar reflects the new folder
|
||||
await collectionsStore.list()
|
||||
}
|
||||
|
||||
// ── Initialization ────────────────────────────────────────────────────────
|
||||
|
||||
async function initialize() {
|
||||
loading.value = true
|
||||
try {
|
||||
await servicesStore.list()
|
||||
await collectionsStore.list()
|
||||
|
||||
// Select inbox by default
|
||||
const inbox = collectionsStore.collections.find(c => c.properties.role === 'inbox')
|
||||
if (inbox) {
|
||||
await selectFolder(inbox)
|
||||
}
|
||||
|
||||
mailSync.start()
|
||||
} catch (error) {
|
||||
console.error('[Mail] Failed to initialize:', error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
function notify(message: string, color: 'success' | 'error' | 'info' | 'warning' = 'success') {
|
||||
showSnackbar({ message, color })
|
||||
}
|
||||
|
||||
// ── Exports ───────────────────────────────────────────────────────────────
|
||||
@@ -210,9 +291,9 @@ export const useMailStore = defineStore('mailStore', () => {
|
||||
selectedMessage,
|
||||
composeMode,
|
||||
composeReplyTo,
|
||||
snackbarVisible,
|
||||
snackbarMessage,
|
||||
snackbarColor,
|
||||
serviceFolderLoadingState,
|
||||
serviceFolderLoadedState,
|
||||
serviceFolderErrorState,
|
||||
|
||||
// Computed
|
||||
currentMessages,
|
||||
@@ -227,7 +308,10 @@ export const useMailStore = defineStore('mailStore', () => {
|
||||
toggleSidebar,
|
||||
openSettings,
|
||||
notify,
|
||||
onFolderCreated,
|
||||
isServiceFolderLoading,
|
||||
hasServiceFoldersLoaded,
|
||||
getServiceFolderError,
|
||||
loadFoldersForService,
|
||||
initialize,
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user