refactor: improvemets #11

Merged
Sebastian merged 1 commits from refactor/improvements into main 2026-03-24 23:14:21 +00:00
2 changed files with 140 additions and 21 deletions

View File

@@ -5,10 +5,11 @@
*/
import { ref, onMounted, onUnmounted } from 'vue';
import type { Ref } from 'vue';
import { useEntitiesStore } from '../stores/entitiesStore';
import { useCollectionsStore } from '../stores/collectionsStore';
interface SyncSource {
export interface SyncSource {
provider: string;
service: string | number;
collections: (string | number)[];
@@ -23,7 +24,21 @@ interface SyncOptions {
fetchDetails?: boolean;
}
export function useMailSync(options: SyncOptions = {}) {
export interface MailSyncController {
isRunning: Ref<boolean>;
lastSync: Ref<Date | null>;
error: Ref<string | null>;
sources: Ref<SyncSource[]>;
addSource: (source: SyncSource) => void;
removeSource: (source: SyncSource) => void;
clearSources: () => void;
sync: () => Promise<void>;
start: () => void;
stop: () => void;
restart: () => void;
}
export function useMailSync(options: SyncOptions = {}): MailSyncController {
const {
interval = 30000,
autoStart = true,

View File

@@ -9,8 +9,13 @@ import { CollectionObject, CollectionPropertiesObject } from '../models/collecti
import type { SourceSelector, ListFilter, ListSort } from '../types'
export const useCollectionsStore = defineStore('mailCollectionsStore', () => {
const ROOT_IDENTIFIER = '__root__'
const SERVICE_INDEX_IDENTIFIER = '__service__'
// State
const _collections = ref<Record<string, CollectionObject>>({})
const _collectionsByServiceIndex = ref<Record<string, string[]>>({})
const _collectionsByParentIndex = ref<Record<string, string[]>>({})
const transceiving = ref(false)
/**
@@ -33,13 +38,20 @@ export const useCollectionsStore = defineStore('mailCollectionsStore', () => {
*/
const collectionsByService = computed(() => {
const groups: Record<string, CollectionObject[]> = {}
Object.values(_collections.value).forEach((collection) => {
const serviceKey = `${collection.provider}:${collection.service}`
if (!groups[serviceKey]) {
groups[serviceKey] = []
Object.keys(_collectionsByServiceIndex.value).forEach(serviceIndexKey => {
const collectionKeys = _collectionsByServiceIndex.value[serviceIndexKey] ?? []
const collectionsForKey = collectionKeys
.map(collectionKey => _collections.value[collectionKey])
.filter((collection): collection is CollectionObject => collection !== undefined)
if (collectionsForKey.length === 0) {
return
}
groups[serviceKey].push(collection)
const firstCollection = collectionsForKey[0]
const serviceKey = `${firstCollection.provider}:${firstCollection.service}`
groups[serviceKey] = collectionsForKey
})
return groups
@@ -75,10 +87,9 @@ export const useCollectionsStore = defineStore('mailCollectionsStore', () => {
* @returns Array of collection objects
*/
function collectionsForService(provider: string, service: string | number, retrieve: boolean = false): CollectionObject[] {
const serviceKeyPrefix = `${provider}:${service}:`
const serviceCollections = Object.entries(_collections.value)
.filter(([key]) => key.startsWith(serviceKeyPrefix))
.map(([_, collection]) => collection)
const serviceCollections = collectionObjectsForKeys(
_collectionsByServiceIndex.value[identifierKey(provider, service, SERVICE_INDEX_IDENTIFIER)] ?? [],
)
if (retrieve === true && serviceCollections.length === 0) {
console.debug(`[Mail Manager][Store] - Force fetching collections for service "${provider}:${service}"`)
@@ -93,19 +104,26 @@ export const useCollectionsStore = defineStore('mailCollectionsStore', () => {
return serviceCollections
}
function collectionsInCollection(provider: string, service: string | number, collectionId: string | number, retrieve: boolean = false): CollectionObject[] {
const collectionKeyPrefix = `${provider}:${service}:${collectionId}:`
const nestedCollections = Object.entries(_collections.value)
.filter(([key]) => key.startsWith(collectionKeyPrefix))
.map(([_, collection]) => collection)
/**
* Get direct child collections for a parent collection, or root collections when parent is null.
*
* @param provider - provider identifier
* @param service - service identifier
* @param collectionId - parent collection identifier, or null for root-level collections
* @param retrieve - Retrieve behavior: true = fetch service collections if missing, false = cache only
*
* @returns Array of direct child collection objects
*/
function collectionsInCollection(provider: string, service: string | number, collectionId: string | number | null, retrieve: boolean = false): CollectionObject[] {
const nestedCollections = collectionObjectsForKeys(
_collectionsByParentIndex.value[identifierKey(provider, service, collectionId)] ?? [],
)
if (retrieve === true && nestedCollections.length === 0) {
console.debug(`[Mail Manager][Store] - Force fetching collections in collection "${provider}:${service}:${collectionId}"`)
const sources: SourceSelector = {
[provider]: {
[String(service)]: {
[String(collectionId)]: true
}
[String(service)]: true
}
}
list(sources)
@@ -114,11 +132,66 @@ export const useCollectionsStore = defineStore('mailCollectionsStore', () => {
return nestedCollections
}
function hasChildrenInCollection(provider: string, service: string | number, collectionId: string | number | null): boolean {
return (_collectionsByParentIndex.value[identifierKey(provider, service, collectionId)]?.length ?? 0) > 0
}
/**
* Create unique key for a collection
*/
function identifierKey(provider: string, service: string | number | null, identifier: string | number | null): string {
return `${provider}:${service ?? ''}:${identifier ?? ''}`
return `${provider}:${String(service ?? ROOT_IDENTIFIER)}:${String(identifier ?? ROOT_IDENTIFIER)}`
}
function collectionObjectsForKeys(collectionKeys: string[]): CollectionObject[] {
return collectionKeys
.map(collectionKey => _collections.value[collectionKey])
.filter((collection): collection is CollectionObject => collection !== undefined)
}
function addIndexEntry(index: Record<string, string[]>, indexKey: string, collectionKey: string) {
const existing = index[indexKey] ?? []
if (existing.includes(collectionKey)) {
return
}
index[indexKey] = [...existing, collectionKey]
}
function removeIndexEntry(index: Record<string, string[]>, indexKey: string, collectionKey: string) {
const existing = index[indexKey]
if (!existing) {
return
}
const filtered = existing.filter(existingKey => existingKey !== collectionKey)
if (filtered.length === 0) {
delete index[indexKey]
return
}
index[indexKey] = filtered
}
function indexCollection(collection: CollectionObject) {
const collectionKey = identifierKey(collection.provider, collection.service, collection.identifier)
const serviceIndexKey = identifierKey(collection.provider, collection.service, SERVICE_INDEX_IDENTIFIER)
const parentIndexKey = identifierKey(collection.provider, collection.service, collection.collection)
addIndexEntry(_collectionsByServiceIndex.value, serviceIndexKey, collectionKey)
addIndexEntry(_collectionsByParentIndex.value, parentIndexKey, collectionKey)
}
function deindexCollection(collection: CollectionObject) {
const collectionKey = identifierKey(collection.provider, collection.service, collection.identifier)
const serviceIndexKey = identifierKey(collection.provider, collection.service, SERVICE_INDEX_IDENTIFIER)
const parentIndexKey = identifierKey(collection.provider, collection.service, collection.collection)
removeIndexEntry(_collectionsByServiceIndex.value, serviceIndexKey, collectionKey)
removeIndexEntry(_collectionsByParentIndex.value, parentIndexKey, collectionKey)
}
// Actions
@@ -143,6 +216,12 @@ export const useCollectionsStore = defineStore('mailCollectionsStore', () => {
Object.entries(providerServices).forEach(([_serviceId, serviceCollections]) => {
Object.entries(serviceCollections).forEach(([_collectionId, collectionObj]) => {
const key = identifierKey(collectionObj.provider, collectionObj.service, collectionObj.identifier)
const previousCollection = _collections.value[key]
if (previousCollection) {
deindexCollection(previousCollection)
}
collections[key] = collectionObj
})
})
@@ -150,6 +229,9 @@ export const useCollectionsStore = defineStore('mailCollectionsStore', () => {
// Merge retrieved collections into state
_collections.value = { ..._collections.value, ...collections }
Object.values(collections).forEach(collectionObj => {
indexCollection(collectionObj)
})
console.debug('[Mail Manager][Store] - Successfully retrieved', Object.keys(collections).length, 'collections')
return collections
@@ -177,7 +259,14 @@ export const useCollectionsStore = defineStore('mailCollectionsStore', () => {
// Merge fetched collection into state
const key = identifierKey(response.provider, response.service, response.identifier)
const previousCollection = _collections.value[key]
if (previousCollection) {
deindexCollection(previousCollection)
}
_collections.value[key] = response
indexCollection(response)
console.debug('[Mail Manager][Store] - Successfully fetched collection:', key)
return response
@@ -234,6 +323,7 @@ export const useCollectionsStore = defineStore('mailCollectionsStore', () => {
// Merge created collection into state
const key = identifierKey(response.provider, response.service, response.identifier)
_collections.value[key] = response
indexCollection(response)
console.debug('[Mail Manager][Store] - Successfully created collection:', key)
return response
@@ -267,7 +357,14 @@ export const useCollectionsStore = defineStore('mailCollectionsStore', () => {
// Merge updated collection into state
const key = identifierKey(response.provider, response.service, response.identifier)
const previousCollection = _collections.value[key]
if (previousCollection) {
deindexCollection(previousCollection)
}
_collections.value[key] = response
indexCollection(response)
console.debug('[Mail Manager][Store] - Successfully updated collection:', key)
return response
@@ -295,6 +392,12 @@ export const useCollectionsStore = defineStore('mailCollectionsStore', () => {
// Remove deleted collection from state
const key = identifierKey(provider, service, identifier)
const previousCollection = _collections.value[key]
if (previousCollection) {
deindexCollection(previousCollection)
}
delete _collections.value[key]
console.debug('[Mail Manager][Store] - Successfully deleted collection:', key)
@@ -317,6 +420,7 @@ export const useCollectionsStore = defineStore('mailCollectionsStore', () => {
collectionsByService,
collectionsForService,
collectionsInCollection,
hasChildrenInCollection,
// Actions
collection,
list,