refactor: improvemets
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
@@ -5,10 +5,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { ref, onMounted, onUnmounted } from 'vue';
|
import { ref, onMounted, onUnmounted } from 'vue';
|
||||||
|
import type { Ref } from 'vue';
|
||||||
import { useEntitiesStore } from '../stores/entitiesStore';
|
import { useEntitiesStore } from '../stores/entitiesStore';
|
||||||
import { useCollectionsStore } from '../stores/collectionsStore';
|
import { useCollectionsStore } from '../stores/collectionsStore';
|
||||||
|
|
||||||
interface SyncSource {
|
export interface SyncSource {
|
||||||
provider: string;
|
provider: string;
|
||||||
service: string | number;
|
service: string | number;
|
||||||
collections: (string | number)[];
|
collections: (string | number)[];
|
||||||
@@ -23,7 +24,21 @@ interface SyncOptions {
|
|||||||
fetchDetails?: boolean;
|
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 {
|
const {
|
||||||
interval = 30000,
|
interval = 30000,
|
||||||
autoStart = true,
|
autoStart = true,
|
||||||
|
|||||||
@@ -9,8 +9,13 @@ import { CollectionObject, CollectionPropertiesObject } from '../models/collecti
|
|||||||
import type { SourceSelector, ListFilter, ListSort } from '../types'
|
import type { SourceSelector, ListFilter, ListSort } from '../types'
|
||||||
|
|
||||||
export const useCollectionsStore = defineStore('mailCollectionsStore', () => {
|
export const useCollectionsStore = defineStore('mailCollectionsStore', () => {
|
||||||
|
const ROOT_IDENTIFIER = '__root__'
|
||||||
|
const SERVICE_INDEX_IDENTIFIER = '__service__'
|
||||||
|
|
||||||
// State
|
// State
|
||||||
const _collections = ref<Record<string, CollectionObject>>({})
|
const _collections = ref<Record<string, CollectionObject>>({})
|
||||||
|
const _collectionsByServiceIndex = ref<Record<string, string[]>>({})
|
||||||
|
const _collectionsByParentIndex = ref<Record<string, string[]>>({})
|
||||||
const transceiving = ref(false)
|
const transceiving = ref(false)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -33,13 +38,20 @@ export const useCollectionsStore = defineStore('mailCollectionsStore', () => {
|
|||||||
*/
|
*/
|
||||||
const collectionsByService = computed(() => {
|
const collectionsByService = computed(() => {
|
||||||
const groups: Record<string, CollectionObject[]> = {}
|
const groups: Record<string, CollectionObject[]> = {}
|
||||||
|
|
||||||
Object.values(_collections.value).forEach((collection) => {
|
Object.keys(_collectionsByServiceIndex.value).forEach(serviceIndexKey => {
|
||||||
const serviceKey = `${collection.provider}:${collection.service}`
|
const collectionKeys = _collectionsByServiceIndex.value[serviceIndexKey] ?? []
|
||||||
if (!groups[serviceKey]) {
|
const collectionsForKey = collectionKeys
|
||||||
groups[serviceKey] = []
|
.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
|
return groups
|
||||||
@@ -75,10 +87,9 @@ export const useCollectionsStore = defineStore('mailCollectionsStore', () => {
|
|||||||
* @returns Array of collection objects
|
* @returns Array of collection objects
|
||||||
*/
|
*/
|
||||||
function collectionsForService(provider: string, service: string | number, retrieve: boolean = false): CollectionObject[] {
|
function collectionsForService(provider: string, service: string | number, retrieve: boolean = false): CollectionObject[] {
|
||||||
const serviceKeyPrefix = `${provider}:${service}:`
|
const serviceCollections = collectionObjectsForKeys(
|
||||||
const serviceCollections = Object.entries(_collections.value)
|
_collectionsByServiceIndex.value[identifierKey(provider, service, SERVICE_INDEX_IDENTIFIER)] ?? [],
|
||||||
.filter(([key]) => key.startsWith(serviceKeyPrefix))
|
)
|
||||||
.map(([_, collection]) => collection)
|
|
||||||
|
|
||||||
if (retrieve === true && serviceCollections.length === 0) {
|
if (retrieve === true && serviceCollections.length === 0) {
|
||||||
console.debug(`[Mail Manager][Store] - Force fetching collections for service "${provider}:${service}"`)
|
console.debug(`[Mail Manager][Store] - Force fetching collections for service "${provider}:${service}"`)
|
||||||
@@ -93,19 +104,26 @@ export const useCollectionsStore = defineStore('mailCollectionsStore', () => {
|
|||||||
return serviceCollections
|
return serviceCollections
|
||||||
}
|
}
|
||||||
|
|
||||||
function collectionsInCollection(provider: string, service: string | number, collectionId: string | number, retrieve: boolean = false): CollectionObject[] {
|
/**
|
||||||
const collectionKeyPrefix = `${provider}:${service}:${collectionId}:`
|
* Get direct child collections for a parent collection, or root collections when parent is null.
|
||||||
const nestedCollections = Object.entries(_collections.value)
|
*
|
||||||
.filter(([key]) => key.startsWith(collectionKeyPrefix))
|
* @param provider - provider identifier
|
||||||
.map(([_, collection]) => collection)
|
* @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) {
|
if (retrieve === true && nestedCollections.length === 0) {
|
||||||
console.debug(`[Mail Manager][Store] - Force fetching collections in collection "${provider}:${service}:${collectionId}"`)
|
console.debug(`[Mail Manager][Store] - Force fetching collections in collection "${provider}:${service}:${collectionId}"`)
|
||||||
const sources: SourceSelector = {
|
const sources: SourceSelector = {
|
||||||
[provider]: {
|
[provider]: {
|
||||||
[String(service)]: {
|
[String(service)]: true
|
||||||
[String(collectionId)]: true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
list(sources)
|
list(sources)
|
||||||
@@ -114,11 +132,66 @@ export const useCollectionsStore = defineStore('mailCollectionsStore', () => {
|
|||||||
return nestedCollections
|
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
|
* Create unique key for a collection
|
||||||
*/
|
*/
|
||||||
function identifierKey(provider: string, service: string | number | null, identifier: string | number | null): string {
|
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
|
// Actions
|
||||||
@@ -143,6 +216,12 @@ export const useCollectionsStore = defineStore('mailCollectionsStore', () => {
|
|||||||
Object.entries(providerServices).forEach(([_serviceId, serviceCollections]) => {
|
Object.entries(providerServices).forEach(([_serviceId, serviceCollections]) => {
|
||||||
Object.entries(serviceCollections).forEach(([_collectionId, collectionObj]) => {
|
Object.entries(serviceCollections).forEach(([_collectionId, collectionObj]) => {
|
||||||
const key = identifierKey(collectionObj.provider, collectionObj.service, collectionObj.identifier)
|
const key = identifierKey(collectionObj.provider, collectionObj.service, collectionObj.identifier)
|
||||||
|
const previousCollection = _collections.value[key]
|
||||||
|
|
||||||
|
if (previousCollection) {
|
||||||
|
deindexCollection(previousCollection)
|
||||||
|
}
|
||||||
|
|
||||||
collections[key] = collectionObj
|
collections[key] = collectionObj
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -150,6 +229,9 @@ export const useCollectionsStore = defineStore('mailCollectionsStore', () => {
|
|||||||
|
|
||||||
// Merge retrieved collections into state
|
// Merge retrieved collections into state
|
||||||
_collections.value = { ..._collections.value, ...collections }
|
_collections.value = { ..._collections.value, ...collections }
|
||||||
|
Object.values(collections).forEach(collectionObj => {
|
||||||
|
indexCollection(collectionObj)
|
||||||
|
})
|
||||||
|
|
||||||
console.debug('[Mail Manager][Store] - Successfully retrieved', Object.keys(collections).length, 'collections')
|
console.debug('[Mail Manager][Store] - Successfully retrieved', Object.keys(collections).length, 'collections')
|
||||||
return collections
|
return collections
|
||||||
@@ -177,7 +259,14 @@ export const useCollectionsStore = defineStore('mailCollectionsStore', () => {
|
|||||||
|
|
||||||
// Merge fetched collection into state
|
// Merge fetched collection into state
|
||||||
const key = identifierKey(response.provider, response.service, response.identifier)
|
const key = identifierKey(response.provider, response.service, response.identifier)
|
||||||
|
const previousCollection = _collections.value[key]
|
||||||
|
|
||||||
|
if (previousCollection) {
|
||||||
|
deindexCollection(previousCollection)
|
||||||
|
}
|
||||||
|
|
||||||
_collections.value[key] = response
|
_collections.value[key] = response
|
||||||
|
indexCollection(response)
|
||||||
|
|
||||||
console.debug('[Mail Manager][Store] - Successfully fetched collection:', key)
|
console.debug('[Mail Manager][Store] - Successfully fetched collection:', key)
|
||||||
return response
|
return response
|
||||||
@@ -234,6 +323,7 @@ export const useCollectionsStore = defineStore('mailCollectionsStore', () => {
|
|||||||
// Merge created collection into state
|
// Merge created collection into state
|
||||||
const key = identifierKey(response.provider, response.service, response.identifier)
|
const key = identifierKey(response.provider, response.service, response.identifier)
|
||||||
_collections.value[key] = response
|
_collections.value[key] = response
|
||||||
|
indexCollection(response)
|
||||||
|
|
||||||
console.debug('[Mail Manager][Store] - Successfully created collection:', key)
|
console.debug('[Mail Manager][Store] - Successfully created collection:', key)
|
||||||
return response
|
return response
|
||||||
@@ -267,7 +357,14 @@ export const useCollectionsStore = defineStore('mailCollectionsStore', () => {
|
|||||||
|
|
||||||
// Merge updated collection into state
|
// Merge updated collection into state
|
||||||
const key = identifierKey(response.provider, response.service, response.identifier)
|
const key = identifierKey(response.provider, response.service, response.identifier)
|
||||||
|
const previousCollection = _collections.value[key]
|
||||||
|
|
||||||
|
if (previousCollection) {
|
||||||
|
deindexCollection(previousCollection)
|
||||||
|
}
|
||||||
|
|
||||||
_collections.value[key] = response
|
_collections.value[key] = response
|
||||||
|
indexCollection(response)
|
||||||
|
|
||||||
console.debug('[Mail Manager][Store] - Successfully updated collection:', key)
|
console.debug('[Mail Manager][Store] - Successfully updated collection:', key)
|
||||||
return response
|
return response
|
||||||
@@ -295,6 +392,12 @@ export const useCollectionsStore = defineStore('mailCollectionsStore', () => {
|
|||||||
|
|
||||||
// Remove deleted collection from state
|
// Remove deleted collection from state
|
||||||
const key = identifierKey(provider, service, identifier)
|
const key = identifierKey(provider, service, identifier)
|
||||||
|
const previousCollection = _collections.value[key]
|
||||||
|
|
||||||
|
if (previousCollection) {
|
||||||
|
deindexCollection(previousCollection)
|
||||||
|
}
|
||||||
|
|
||||||
delete _collections.value[key]
|
delete _collections.value[key]
|
||||||
|
|
||||||
console.debug('[Mail Manager][Store] - Successfully deleted collection:', key)
|
console.debug('[Mail Manager][Store] - Successfully deleted collection:', key)
|
||||||
@@ -317,6 +420,7 @@ export const useCollectionsStore = defineStore('mailCollectionsStore', () => {
|
|||||||
collectionsByService,
|
collectionsByService,
|
||||||
collectionsForService,
|
collectionsForService,
|
||||||
collectionsInCollection,
|
collectionsInCollection,
|
||||||
|
hasChildrenInCollection,
|
||||||
// Actions
|
// Actions
|
||||||
collection,
|
collection,
|
||||||
list,
|
list,
|
||||||
|
|||||||
Reference in New Issue
Block a user