refactor: improvemets

Signed-off-by: Sebastian <krupinski01@gmail.com>
This commit is contained in:
2026-03-24 19:11:29 -04:00
parent ab508a5361
commit 18d07d84cb
11 changed files with 499 additions and 270 deletions

View File

@@ -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,
}
})