refactor: improvemets #11
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
/**
|
||||
@@ -34,12 +39,19 @@ 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,
|
||||
|
||||
Reference in New Issue
Block a user