refactor: front end
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
+228
-574
@@ -1,646 +1,322 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref, computed } from 'vue'
|
||||
import type { Ref, ComputedRef } from 'vue'
|
||||
import type { FileNode, FileCollection, FileEntity } from '../types/node'
|
||||
import type { FilterCondition, SortCondition, RangeCondition } from '../types/common'
|
||||
import { isFileCollection } from '../types/node'
|
||||
import { collectionService } from '../services/collectionService'
|
||||
import { entityService } from '../services/entityService'
|
||||
import { nodeService } from '../services/nodeService'
|
||||
import { FileCollectionObject } from '../models/collection'
|
||||
import { FileEntityObject } from '../models/entity'
|
||||
/**
|
||||
* Nodes Store (thin wrapper over collections/entities stores)
|
||||
*/
|
||||
|
||||
import { computed, ref, readonly } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
import type {
|
||||
SourceSelector,
|
||||
ListFilter,
|
||||
ListSort,
|
||||
ListRange,
|
||||
CollectionMutableProperties,
|
||||
DocumentInterface,
|
||||
} from '../types'
|
||||
import { CollectionObject, EntityObject } from '../models'
|
||||
import { useCollectionsStore } from './collectionsStore'
|
||||
import { useEntitiesStore } from './entitiesStore'
|
||||
|
||||
// Root collection constant
|
||||
export const ROOT_ID = '00000000-0000-0000-0000-000000000000'
|
||||
|
||||
// Store structure: provider -> service -> nodeId -> node (either collection or entity object)
|
||||
type NodeRecord = FileCollectionObject | FileEntityObject
|
||||
type ServiceNodeStore = Record<string, NodeRecord>
|
||||
type ProviderNodeStore = Record<string, ServiceNodeStore>
|
||||
type NodeStore = Record<string, ProviderNodeStore>
|
||||
type NodeRecord = CollectionObject | EntityObject
|
||||
|
||||
export const useNodesStore = defineStore('fileNodes', () => {
|
||||
const nodes: Ref<NodeStore> = ref({})
|
||||
const syncTokens: Ref<Record<string, Record<string, string>>> = ref({}) // provider -> service -> token
|
||||
const loading = ref(false)
|
||||
const error: Ref<string | null> = ref(null)
|
||||
export const useNodesStore = defineStore('documentsNodesStore', () => {
|
||||
const collectionsStore = useCollectionsStore()
|
||||
const entitiesStore = useEntitiesStore()
|
||||
|
||||
// Computed: flat list of all nodes
|
||||
const nodeList: ComputedRef<NodeRecord[]> = computed(() => {
|
||||
const result: NodeRecord[] = []
|
||||
Object.values(nodes.value).forEach(providerNodes => {
|
||||
Object.values(providerNodes).forEach(serviceNodes => {
|
||||
result.push(...Object.values(serviceNodes))
|
||||
const error = ref<string | null>(null)
|
||||
|
||||
const transceiving = computed(() => collectionsStore.transceiving || entitiesStore.transceiving)
|
||||
|
||||
const nodeList = computed<NodeRecord[]>(() => {
|
||||
return [...collectionsStore.collections, ...entitiesStore.entities]
|
||||
})
|
||||
|
||||
const collectionList = computed<CollectionObject[]>(() => collectionsStore.collections)
|
||||
const entityList = computed<EntityObject[]>(() => entitiesStore.entities)
|
||||
|
||||
function toId(value: string | number | null | undefined): string | null {
|
||||
if (value === null || value === undefined || value === '') {
|
||||
return null
|
||||
}
|
||||
|
||||
return String(value)
|
||||
}
|
||||
|
||||
function getServiceNodes(providerId: string, serviceId: string | number): NodeRecord[] {
|
||||
return [
|
||||
...collectionsStore.collectionsForService(providerId, serviceId),
|
||||
...entitiesStore.entitiesForService(providerId, serviceId),
|
||||
]
|
||||
}
|
||||
|
||||
function getNode(providerId: string, serviceId: string | number, nodeId: string | number): NodeRecord | undefined {
|
||||
const targetId = String(nodeId)
|
||||
return getServiceNodes(providerId, serviceId)
|
||||
.find((node) => String(node.identifier) === targetId)
|
||||
}
|
||||
|
||||
function isRoot(nodeId: string | number | null | undefined): boolean {
|
||||
const normalized = toId(nodeId)
|
||||
return normalized === null || normalized === ROOT_ID
|
||||
}
|
||||
|
||||
function getChildren(providerId: string, serviceId: string | number, parentId: string | number | null): NodeRecord[] {
|
||||
const serviceNodes = getServiceNodes(providerId, serviceId)
|
||||
|
||||
if (isRoot(parentId)) {
|
||||
return serviceNodes.filter((node) => {
|
||||
const parent = toId(node.collection)
|
||||
return parent === null || parent === ROOT_ID
|
||||
})
|
||||
})
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
// Computed: all collections (folders)
|
||||
const collectionList: ComputedRef<FileCollectionObject[]> = computed(() => {
|
||||
return nodeList.value.filter(
|
||||
(node): node is FileCollectionObject => node['@type'] === 'files.collection'
|
||||
)
|
||||
})
|
||||
|
||||
// Computed: all entities (files)
|
||||
const entityList: ComputedRef<FileEntityObject[]> = computed(() => {
|
||||
return nodeList.value.filter(
|
||||
(node): node is FileEntityObject => node['@type'] === 'files.entity'
|
||||
)
|
||||
})
|
||||
|
||||
// Get a specific node
|
||||
const getNode = (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
nodeId: string
|
||||
): NodeRecord | undefined => {
|
||||
return nodes.value[providerId]?.[serviceId]?.[nodeId]
|
||||
const targetParent = String(parentId)
|
||||
return serviceNodes.filter((node) => toId(node.collection) === targetParent)
|
||||
}
|
||||
|
||||
// Get all nodes for a service
|
||||
const getServiceNodes = (
|
||||
providerId: string,
|
||||
serviceId: string
|
||||
): NodeRecord[] => {
|
||||
return Object.values(nodes.value[providerId]?.[serviceId] || {})
|
||||
function getChildCollections(providerId: string, serviceId: string | number, parentId: string | number | null): CollectionObject[] {
|
||||
return getChildren(providerId, serviceId, parentId)
|
||||
.filter((node): node is CollectionObject => node instanceof CollectionObject)
|
||||
}
|
||||
|
||||
// Get children of a parent node (or root nodes if parentId is null/ROOT_ID)
|
||||
const getChildren = (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
parentId: string | null
|
||||
): NodeRecord[] => {
|
||||
const serviceNodes = nodes.value[providerId]?.[serviceId] || {}
|
||||
const targetParent = parentId === ROOT_ID ? ROOT_ID : parentId
|
||||
return Object.values(serviceNodes).filter(node => node.in === targetParent)
|
||||
function getChildEntities(providerId: string, serviceId: string | number, parentId: string | number | null): EntityObject[] {
|
||||
return getChildren(providerId, serviceId, parentId)
|
||||
.filter((node): node is EntityObject => node instanceof EntityObject)
|
||||
}
|
||||
|
||||
// Get child collections (folders)
|
||||
const getChildCollections = (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
parentId: string | null
|
||||
): FileCollectionObject[] => {
|
||||
return getChildren(providerId, serviceId, parentId).filter(
|
||||
(node): node is FileCollectionObject => node['@type'] === 'files.collection'
|
||||
)
|
||||
}
|
||||
|
||||
// Get child entities (files)
|
||||
const getChildEntities = (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
parentId: string | null
|
||||
): FileEntityObject[] => {
|
||||
return getChildren(providerId, serviceId, parentId).filter(
|
||||
(node): node is FileEntityObject => node['@type'] === 'files.entity'
|
||||
)
|
||||
}
|
||||
|
||||
// Get path to root (ancestors)
|
||||
const getPath = (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
nodeId: string
|
||||
): NodeRecord[] => {
|
||||
function getPath(providerId: string, serviceId: string | number, nodeId: string | number): NodeRecord[] {
|
||||
const path: NodeRecord[] = []
|
||||
let currentNode = getNode(providerId, serviceId, nodeId)
|
||||
|
||||
while (currentNode) {
|
||||
path.unshift(currentNode)
|
||||
if (currentNode.in === null || currentNode.in === ROOT_ID || currentNode.id === ROOT_ID) {
|
||||
let current = getNode(providerId, serviceId, nodeId)
|
||||
|
||||
while (current) {
|
||||
path.unshift(current)
|
||||
const parentId = toId(current.collection)
|
||||
|
||||
if (parentId === null || parentId === ROOT_ID) {
|
||||
break
|
||||
}
|
||||
currentNode = getNode(providerId, serviceId, currentNode.in)
|
||||
|
||||
current = getNode(providerId, serviceId, parentId)
|
||||
}
|
||||
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
// Check if a node is the root
|
||||
const isRoot = (nodeId: string): boolean => {
|
||||
return nodeId === ROOT_ID
|
||||
}
|
||||
|
||||
// Helper to hydrate a node based on its type
|
||||
const hydrateNode = (data: FileNode): NodeRecord => {
|
||||
if (isFileCollection(data)) {
|
||||
return new FileCollectionObject().fromJson(data)
|
||||
} else {
|
||||
return new FileEntityObject().fromJson(data as FileEntity)
|
||||
}
|
||||
}
|
||||
|
||||
// Set all nodes for a provider/service
|
||||
const setNodes = (
|
||||
async function fetchCollections(
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
data: FileNode[]
|
||||
) => {
|
||||
if (!nodes.value[providerId]) {
|
||||
nodes.value[providerId] = {}
|
||||
}
|
||||
const hydrated: ServiceNodeStore = {}
|
||||
for (const nodeData of data) {
|
||||
hydrated[nodeData.id] = hydrateNode(nodeData)
|
||||
}
|
||||
nodes.value[providerId][serviceId] = hydrated
|
||||
}
|
||||
|
||||
// Add/update a single node
|
||||
const addNode = (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
node: FileNode
|
||||
) => {
|
||||
if (!nodes.value[providerId]) {
|
||||
nodes.value[providerId] = {}
|
||||
}
|
||||
if (!nodes.value[providerId][serviceId]) {
|
||||
nodes.value[providerId][serviceId] = {}
|
||||
}
|
||||
nodes.value[providerId][serviceId][node.id] = hydrateNode(node)
|
||||
}
|
||||
|
||||
// Add multiple nodes (handles both array and object formats from API)
|
||||
const addNodes = (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
data: FileNode[] | Record<string, FileNode>
|
||||
) => {
|
||||
if (!nodes.value[providerId]) {
|
||||
nodes.value[providerId] = {}
|
||||
}
|
||||
if (!nodes.value[providerId][serviceId]) {
|
||||
nodes.value[providerId][serviceId] = {}
|
||||
}
|
||||
// Handle both array and object (keyed by ID) formats
|
||||
const nodeArray = Array.isArray(data) ? data : Object.values(data)
|
||||
for (const nodeData of nodeArray) {
|
||||
nodes.value[providerId][serviceId][nodeData.id] = hydrateNode(nodeData)
|
||||
}
|
||||
}
|
||||
|
||||
// Remove a node
|
||||
const removeNode = (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
nodeId: string
|
||||
) => {
|
||||
if (nodes.value[providerId]?.[serviceId]) {
|
||||
delete nodes.value[providerId][serviceId][nodeId]
|
||||
}
|
||||
}
|
||||
|
||||
// Remove multiple nodes
|
||||
const removeNodes = (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
nodeIds: string[]
|
||||
) => {
|
||||
if (nodes.value[providerId]?.[serviceId]) {
|
||||
for (const id of nodeIds) {
|
||||
delete nodes.value[providerId][serviceId][id]
|
||||
serviceId: string | number,
|
||||
collectionId: string | number | null,
|
||||
filter?: ListFilter,
|
||||
sort?: ListSort,
|
||||
): Promise<CollectionObject[]> {
|
||||
error.value = null
|
||||
try {
|
||||
const sources: SourceSelector = {
|
||||
[providerId]: {
|
||||
[String(serviceId)]: collectionId === null
|
||||
? true
|
||||
: { [String(collectionId)]: true },
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clear all nodes for a service
|
||||
const clearServiceNodes = (
|
||||
providerId: string,
|
||||
serviceId: string
|
||||
) => {
|
||||
if (nodes.value[providerId]) {
|
||||
delete nodes.value[providerId][serviceId]
|
||||
}
|
||||
}
|
||||
|
||||
// Clear all nodes
|
||||
const clearNodes = () => {
|
||||
nodes.value = {}
|
||||
}
|
||||
|
||||
// Sync token management
|
||||
const getSyncToken = (
|
||||
providerId: string,
|
||||
serviceId: string
|
||||
): string | undefined => {
|
||||
return syncTokens.value[providerId]?.[serviceId]
|
||||
}
|
||||
|
||||
const setSyncToken = (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
token: string
|
||||
) => {
|
||||
if (!syncTokens.value[providerId]) {
|
||||
syncTokens.value[providerId] = {}
|
||||
}
|
||||
syncTokens.value[providerId][serviceId] = token
|
||||
}
|
||||
|
||||
// ==================== API Actions ====================
|
||||
|
||||
// Fetch nodes (collections and entities) for a location
|
||||
const fetchNodes = async (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
location?: string | null,
|
||||
recursive: boolean = false,
|
||||
filter?: FilterCondition[] | null,
|
||||
sort?: SortCondition[] | null,
|
||||
range?: RangeCondition | null
|
||||
): Promise<NodeRecord[]> => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const data = await nodeService.list(
|
||||
providerId,
|
||||
serviceId,
|
||||
location,
|
||||
recursive,
|
||||
filter,
|
||||
sort,
|
||||
range
|
||||
)
|
||||
// API returns object keyed by ID, convert to array
|
||||
const nodeArray = Array.isArray(data) ? data : Object.values(data)
|
||||
addNodes(providerId, serviceId, nodeArray)
|
||||
return nodeArray.map(hydrateNode)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to fetch nodes'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch collections (folders) for a location
|
||||
const fetchCollections = async (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
location?: string | null,
|
||||
filter?: FilterCondition[] | null,
|
||||
sort?: SortCondition[] | null
|
||||
): Promise<FileCollectionObject[]> => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const data = await collectionService.list(providerId, serviceId, location, filter, sort)
|
||||
// API returns object keyed by ID, convert to array
|
||||
const collectionArray = Array.isArray(data) ? data : Object.values(data)
|
||||
addNodes(providerId, serviceId, collectionArray)
|
||||
return collectionArray.map(c => new FileCollectionObject().fromJson(c))
|
||||
await collectionsStore.list(sources, filter, sort)
|
||||
return collectionsStore.collectionsForService(providerId, serviceId)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to fetch collections'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch entities (files) for a collection
|
||||
const fetchEntities = async (
|
||||
async function fetchEntities(
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
collection: string,
|
||||
filter?: FilterCondition[] | null,
|
||||
sort?: SortCondition[] | null,
|
||||
range?: RangeCondition | null
|
||||
): Promise<FileEntityObject[]> => {
|
||||
loading.value = true
|
||||
serviceId: string | number,
|
||||
collectionId: string | number | null,
|
||||
filter?: ListFilter,
|
||||
sort?: ListSort,
|
||||
range?: ListRange,
|
||||
): Promise<EntityObject[]> {
|
||||
error.value = null
|
||||
try {
|
||||
const data = await entityService.list(providerId, serviceId, collection, filter, sort, range)
|
||||
// API returns object keyed by ID, convert to array
|
||||
const entityArray = Array.isArray(data) ? data : Object.values(data)
|
||||
addNodes(providerId, serviceId, entityArray)
|
||||
return entityArray.map(e => new FileEntityObject().fromJson(e))
|
||||
const sources: SourceSelector = {
|
||||
[providerId]: {
|
||||
[String(serviceId)]: collectionId === null
|
||||
? true
|
||||
: { [String(collectionId)]: true },
|
||||
},
|
||||
}
|
||||
|
||||
await entitiesStore.list(sources, filter, sort, range)
|
||||
return entitiesStore.entitiesForCollection(providerId, serviceId, collectionId)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to fetch entities'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Create a collection (folder)
|
||||
const createCollection = async (
|
||||
async function fetchNodes(
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
location: string | null,
|
||||
data: Partial<FileCollection>,
|
||||
options?: Record<string, unknown>
|
||||
): Promise<FileCollectionObject> => {
|
||||
loading.value = true
|
||||
serviceId: string | number,
|
||||
parentId: string | number | null = ROOT_ID,
|
||||
filter?: ListFilter,
|
||||
sort?: ListSort,
|
||||
range?: ListRange,
|
||||
): Promise<NodeRecord[]> {
|
||||
error.value = null
|
||||
try {
|
||||
const created = await collectionService.create(providerId, serviceId, location, data, options)
|
||||
addNode(providerId, serviceId, created)
|
||||
return new FileCollectionObject().fromJson(created)
|
||||
await Promise.all([
|
||||
fetchCollections(providerId, serviceId, parentId, filter, sort),
|
||||
fetchEntities(providerId, serviceId, parentId, filter, sort, range),
|
||||
])
|
||||
|
||||
return getChildren(providerId, serviceId, parentId)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to fetch nodes'
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
async function createCollection(
|
||||
providerId: string,
|
||||
serviceId: string | number,
|
||||
parentCollectionId: string | number | null,
|
||||
properties: CollectionMutableProperties,
|
||||
): Promise<CollectionObject> {
|
||||
error.value = null
|
||||
try {
|
||||
return await collectionsStore.create(providerId, serviceId, parentCollectionId, properties)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to create collection'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Create an entity (file)
|
||||
const createEntity = async (
|
||||
async function updateCollection(
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
collection: string | null,
|
||||
data: Partial<FileEntity>,
|
||||
options?: Record<string, unknown>
|
||||
): Promise<FileEntityObject> => {
|
||||
loading.value = true
|
||||
serviceId: string | number,
|
||||
identifier: string | number,
|
||||
properties: CollectionMutableProperties,
|
||||
): Promise<CollectionObject> {
|
||||
error.value = null
|
||||
try {
|
||||
const created = await entityService.create(providerId, serviceId, collection, data, options)
|
||||
addNode(providerId, serviceId, created)
|
||||
return new FileEntityObject().fromJson(created)
|
||||
return await collectionsStore.update(providerId, serviceId, identifier, properties)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to update collection'
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteCollection(
|
||||
providerId: string,
|
||||
serviceId: string | number,
|
||||
identifier: string | number,
|
||||
): Promise<boolean> {
|
||||
error.value = null
|
||||
try {
|
||||
const response = await collectionsStore.delete(providerId, serviceId, identifier)
|
||||
return response.success
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to delete collection'
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
async function createEntity(
|
||||
providerId: string,
|
||||
serviceId: string | number,
|
||||
collectionId: string | number,
|
||||
properties: DocumentInterface,
|
||||
options?: Record<string, unknown>,
|
||||
): Promise<EntityObject> {
|
||||
error.value = null
|
||||
try {
|
||||
return await entitiesStore.create(providerId, serviceId, collectionId, properties, options)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to create entity'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Modify a collection
|
||||
const modifyCollection = async (
|
||||
async function updateEntity(
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
identifier: string,
|
||||
data: Partial<FileCollection>
|
||||
): Promise<FileCollectionObject> => {
|
||||
loading.value = true
|
||||
serviceId: string | number,
|
||||
collectionId: string | number,
|
||||
identifier: string | number,
|
||||
properties: DocumentInterface,
|
||||
): Promise<EntityObject> {
|
||||
error.value = null
|
||||
try {
|
||||
const modified = await collectionService.modify(providerId, serviceId, identifier, data)
|
||||
addNode(providerId, serviceId, modified)
|
||||
return new FileCollectionObject().fromJson(modified)
|
||||
return await entitiesStore.update(providerId, serviceId, collectionId, identifier, properties)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to modify collection'
|
||||
error.value = e instanceof Error ? e.message : 'Failed to update entity'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Modify an entity
|
||||
const modifyEntity = async (
|
||||
async function deleteEntity(
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
collection: string | null,
|
||||
identifier: string,
|
||||
data: Partial<FileEntity>
|
||||
): Promise<FileEntityObject> => {
|
||||
loading.value = true
|
||||
serviceId: string | number,
|
||||
collectionId: string | number,
|
||||
identifier: string | number,
|
||||
): Promise<boolean> {
|
||||
error.value = null
|
||||
try {
|
||||
const modified = await entityService.modify(providerId, serviceId, collection, identifier, data)
|
||||
addNode(providerId, serviceId, modified)
|
||||
return new FileEntityObject().fromJson(modified)
|
||||
const response = await entitiesStore.delete(providerId, serviceId, collectionId, identifier)
|
||||
return response.success
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to modify entity'
|
||||
error.value = e instanceof Error ? e.message : 'Failed to delete entity'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Destroy a collection
|
||||
const destroyCollection = async (
|
||||
async function readEntity(
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
identifier: string
|
||||
): Promise<boolean> => {
|
||||
loading.value = true
|
||||
serviceId: string | number,
|
||||
collectionId: string | number,
|
||||
identifier: string | number,
|
||||
): Promise<string | null> {
|
||||
error.value = null
|
||||
try {
|
||||
const success = await collectionService.destroy(providerId, serviceId, identifier)
|
||||
if (success) {
|
||||
removeNode(providerId, serviceId, identifier)
|
||||
}
|
||||
return success
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to destroy collection'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Destroy an entity
|
||||
const destroyEntity = async (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
collection: string | null,
|
||||
identifier: string
|
||||
): Promise<boolean> => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const success = await entityService.destroy(providerId, serviceId, collection, identifier)
|
||||
if (success) {
|
||||
removeNode(providerId, serviceId, identifier)
|
||||
}
|
||||
return success
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to destroy entity'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Copy a collection
|
||||
const copyCollection = async (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
identifier: string,
|
||||
location?: string | null
|
||||
): Promise<FileCollectionObject> => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const copied = await collectionService.copy(providerId, serviceId, identifier, location)
|
||||
addNode(providerId, serviceId, copied)
|
||||
return new FileCollectionObject().fromJson(copied)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to copy collection'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Copy an entity
|
||||
const copyEntity = async (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
collection: string | null,
|
||||
identifier: string,
|
||||
destination?: string | null
|
||||
): Promise<FileEntityObject> => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const copied = await entityService.copy(providerId, serviceId, collection, identifier, destination)
|
||||
addNode(providerId, serviceId, copied)
|
||||
return new FileEntityObject().fromJson(copied)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to copy entity'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Move a collection
|
||||
const moveCollection = async (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
identifier: string,
|
||||
location?: string | null
|
||||
): Promise<FileCollectionObject> => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const moved = await collectionService.move(providerId, serviceId, identifier, location)
|
||||
addNode(providerId, serviceId, moved)
|
||||
return new FileCollectionObject().fromJson(moved)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to move collection'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Move an entity
|
||||
const moveEntity = async (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
collection: string | null,
|
||||
identifier: string,
|
||||
destination?: string | null
|
||||
): Promise<FileEntityObject> => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const moved = await entityService.move(providerId, serviceId, collection, identifier, destination)
|
||||
addNode(providerId, serviceId, moved)
|
||||
return new FileEntityObject().fromJson(moved)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to move entity'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Read entity content
|
||||
const readEntity = async (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
collection: string,
|
||||
identifier: string
|
||||
): Promise<string | null> => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const result = await entityService.read(providerId, serviceId, collection, identifier)
|
||||
return result.content
|
||||
return await entitiesStore.read(providerId, serviceId, collectionId, identifier)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to read entity'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Write entity content
|
||||
const writeEntity = async (
|
||||
async function writeEntity(
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
collection: string | null,
|
||||
identifier: string,
|
||||
content: string
|
||||
): Promise<number> => {
|
||||
loading.value = true
|
||||
serviceId: string | number,
|
||||
collectionId: string | number,
|
||||
identifier: string | number,
|
||||
content: string,
|
||||
): Promise<number> {
|
||||
error.value = null
|
||||
try {
|
||||
return await entityService.write(providerId, serviceId, collection, identifier, content)
|
||||
return await entitiesStore.write(providerId, serviceId, collectionId, identifier, content)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to write entity'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Sync delta changes
|
||||
const syncDelta = async (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
location: string | null,
|
||||
signature: string,
|
||||
recursive: boolean = false,
|
||||
detail: 'ids' | 'full' = 'full'
|
||||
): Promise<void> => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const delta = await nodeService.delta(providerId, serviceId, location, signature, recursive, detail)
|
||||
|
||||
// Handle removed nodes
|
||||
if (delta.removed.length > 0) {
|
||||
removeNodes(providerId, serviceId, delta.removed)
|
||||
}
|
||||
|
||||
// Handle added/modified nodes
|
||||
if (detail === 'full') {
|
||||
const addedNodes = delta.added as FileNode[]
|
||||
const modifiedNodes = delta.modified as FileNode[]
|
||||
if (addedNodes.length > 0) {
|
||||
addNodes(providerId, serviceId, addedNodes)
|
||||
}
|
||||
if (modifiedNodes.length > 0) {
|
||||
addNodes(providerId, serviceId, modifiedNodes)
|
||||
}
|
||||
}
|
||||
|
||||
// Update sync token
|
||||
if (delta.signature) {
|
||||
setSyncToken(providerId, serviceId, delta.signature)
|
||||
}
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to sync delta'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
function clearServiceNodes(providerId: string, serviceId: string | number): void {
|
||||
collectionsStore.clearService(providerId, serviceId)
|
||||
entitiesStore.clearService(providerId, serviceId)
|
||||
}
|
||||
|
||||
function clearNodes(): void {
|
||||
collectionsStore.clearAll()
|
||||
entitiesStore.clearAll()
|
||||
}
|
||||
|
||||
return {
|
||||
// State
|
||||
nodes,
|
||||
syncTokens,
|
||||
loading,
|
||||
error,
|
||||
// Constants
|
||||
transceiving: readonly(transceiving),
|
||||
error: readonly(error),
|
||||
ROOT_ID,
|
||||
// Computed
|
||||
nodeList,
|
||||
collectionList,
|
||||
entityList,
|
||||
// Getters
|
||||
getNode,
|
||||
getServiceNodes,
|
||||
getChildren,
|
||||
@@ -648,40 +324,18 @@ export const useNodesStore = defineStore('fileNodes', () => {
|
||||
getChildEntities,
|
||||
getPath,
|
||||
isRoot,
|
||||
// Setters
|
||||
setNodes,
|
||||
addNode,
|
||||
addNodes,
|
||||
removeNode,
|
||||
removeNodes,
|
||||
clearServiceNodes,
|
||||
clearNodes,
|
||||
// Sync
|
||||
getSyncToken,
|
||||
setSyncToken,
|
||||
// API Actions - Fetch
|
||||
fetchNodes,
|
||||
fetchCollections,
|
||||
fetchEntities,
|
||||
// API Actions - Create
|
||||
createCollection,
|
||||
updateCollection,
|
||||
deleteCollection,
|
||||
createEntity,
|
||||
// API Actions - Modify
|
||||
modifyCollection,
|
||||
modifyEntity,
|
||||
// API Actions - Destroy
|
||||
destroyCollection,
|
||||
destroyEntity,
|
||||
// API Actions - Copy
|
||||
copyCollection,
|
||||
copyEntity,
|
||||
// API Actions - Move
|
||||
moveCollection,
|
||||
moveEntity,
|
||||
// API Actions - Content
|
||||
updateEntity,
|
||||
deleteEntity,
|
||||
readEntity,
|
||||
writeEntity,
|
||||
// API Actions - Sync
|
||||
syncDelta,
|
||||
clearServiceNodes,
|
||||
clearNodes,
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user