feat: use resource identifiers

Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
2026-09-05 20:12:27 -04:00
parent 26ed0c8aad
commit 4b37be3e7a
11 changed files with 313 additions and 224 deletions
+71 -27
View File
@@ -8,7 +8,7 @@ import type { Ref, ComputedRef } from 'vue'
import { useProvidersStore } from '@/stores/providersStore'
import { useServicesStore } from '@/stores/servicesStore'
import { useNodesStore, ROOT_ID } from '@/stores/nodesStore'
import type { ListFilter, ListSort, ListRange } from '@/types/common'
import type { ListFilter, ListSort, ListRange, ServiceIdentifier, CollectionIdentifier } from '@/types/common'
import type { DocumentInterface } from '@/types/document'
import { CollectionObject } from '@/models/collection'
import { EntityObject } from '@/models/entity'
@@ -16,6 +16,9 @@ import { EntityObject } from '@/models/entity'
// Base URL for file manager transfer endpoints
const TRANSFER_BASE_URL = '/m/documents_manager'
// Number of collections+entities requested per page
const DEFAULT_PAGE_TALLY = 64
export interface UseDocumentManagerOptions {
providerId: string | Ref<string> | ComputedRef<string>
serviceId: string | Ref<string> | ComputedRef<string>
@@ -32,6 +35,12 @@ export function useDocumentManager(options: UseDocumentManagerOptions) {
const currentProviderId = () => unref(providerId)
const currentServiceId = () => unref(serviceId)
const currentSources = (): (ServiceIdentifier | CollectionIdentifier)[] => [
currentLocation.value === ROOT_ID
? `${currentProviderId()}:${currentServiceId()}`
: `${currentProviderId()}:${currentServiceId()}:${currentLocation.value}`,
]
// Current location (folder being viewed)
const currentLocation: Ref<string> = ref(ROOT_ID)
@@ -39,6 +48,13 @@ export function useDocumentManager(options: UseDocumentManagerOptions) {
const isLoading = computed(() => nodesStore.transceiving)
const error = computed(() => nodesStore.error)
// Pagination state for the current location + filter + sort
const currentFilter: Ref<ListFilter | undefined> = ref(undefined)
const currentSort: Ref<ListSort | undefined> = ref(undefined)
const loadedCount = ref(0)
const hasMore = ref(false)
const isLoadingMore = ref(false)
// Provider and service
const provider = computed(() => providersStore.provider(currentProviderId()))
const service = computed(() => servicesStore.service(currentProviderId(), currentServiceId()))
@@ -71,7 +87,7 @@ export function useDocumentManager(options: UseDocumentManagerOptions) {
// Navigate to a folder
const navigateTo = async (collectionId: string | null) => {
currentLocation.value = collectionId || ROOT_ID
await refresh()
await resetAndLoad(currentFilter.value, currentSort.value)
}
// Navigate up one level
@@ -90,28 +106,55 @@ export function useDocumentManager(options: UseDocumentManagerOptions) {
await navigateTo(ROOT_ID)
}
// Refresh current location
const refresh = async (
filter?: ListFilter,
sort?: ListSort,
range?: ListRange
) => {
await nodesStore.fetchNodes(
currentProviderId(),
currentServiceId(),
currentLocation.value === ROOT_ID ? ROOT_ID : currentLocation.value,
// Reset the current location's loaded items and (re)fetch the first page,
// optionally with a new filter/sort (e.g. search query or sort field change)
const resetAndLoad = async (filter?: ListFilter, sort?: ListSort) => {
currentFilter.value = filter
currentSort.value = sort
loadedCount.value = 0
hasMore.value = false
const range: ListRange = { type: 'tally', anchor: 'absolute', position: 0, tally: DEFAULT_PAGE_TALLY }
const result = await nodesStore.fetchNodes(
currentSources(),
filter,
sort,
range
range,
'reset'
)
loadedCount.value = result.collections.length + result.entities.length
hasMore.value = !result.isLastPage
}
// Fetch the next page of the current location (infinite scroll "load more")
const loadMore = async () => {
if (!hasMore.value || isLoadingMore.value) {
return
}
isLoadingMore.value = true
try {
const range: ListRange = { type: 'tally', anchor: 'absolute', position: loadedCount.value, tally: DEFAULT_PAGE_TALLY }
const result = await nodesStore.fetchNodes(
currentSources(),
currentFilter.value,
currentSort.value,
range,
'append'
)
loadedCount.value += result.collections.length + result.entities.length
hasMore.value = !result.isLastPage
} finally {
isLoadingMore.value = false
}
}
// Create a new folder
const createFolder = async (label: string): Promise<CollectionObject> => {
return await nodesStore.createCollection(
currentProviderId(),
currentServiceId(),
currentLocation.value === ROOT_ID ? ROOT_ID : currentLocation.value,
`${currentProviderId()}:${currentServiceId()}:${currentLocation.value}`,
{ label, owner: '' }
)
}
@@ -132,9 +175,7 @@ export function useDocumentManager(options: UseDocumentManagerOptions) {
}
return await nodesStore.createEntity(
currentProviderId(),
currentServiceId(),
currentLocation.value === ROOT_ID ? ROOT_ID : currentLocation.value,
`${currentProviderId()}:${currentServiceId()}:${currentLocation.value}`,
properties
)
}
@@ -147,7 +188,7 @@ export function useDocumentManager(options: UseDocumentManagerOptions) {
}
if (node instanceof CollectionObject) {
return await nodesStore.updateCollection(currentProviderId(), currentServiceId(), nodeId, {
return await nodesStore.updateCollection(`${node.provider}:${node.service}:${nodeId}`, {
label: newLabel,
owner: node.properties.owner,
})
@@ -161,7 +202,7 @@ export function useDocumentManager(options: UseDocumentManagerOptions) {
format: node.properties.format,
encoding: node.properties.encoding,
}
return await nodesStore.updateEntity(currentProviderId(), currentServiceId(), node.collection, nodeId, properties)
return await nodesStore.updateEntity(`${node.provider}:${node.service}:${node.collection || ROOT_ID}:${nodeId}`, properties)
}
}
@@ -173,9 +214,9 @@ export function useDocumentManager(options: UseDocumentManagerOptions) {
}
if (node instanceof CollectionObject) {
return await nodesStore.deleteCollection(currentProviderId(), currentServiceId(), nodeId)
return await nodesStore.deleteCollection(`${node.provider}:${node.service}:${nodeId}`)
} else {
return await nodesStore.deleteEntity(currentProviderId(), currentServiceId(), node.collection, nodeId)
return await nodesStore.deleteEntity(`${node.provider}:${node.service}:${node.collection || ROOT_ID}:${nodeId}`)
}
}
@@ -185,7 +226,7 @@ export function useDocumentManager(options: UseDocumentManagerOptions) {
if (!node || !(node instanceof EntityObject)) {
throw new Error('Entity not found')
}
return await nodesStore.readEntity(currentProviderId(), currentServiceId(), node.collection || ROOT_ID, entityId)
return await nodesStore.readEntity(`${node.provider}:${node.service}:${node.collection || ROOT_ID}:${entityId}`)
}
// Write file content
@@ -194,7 +235,7 @@ export function useDocumentManager(options: UseDocumentManagerOptions) {
if (!node || !(node instanceof EntityObject)) {
throw new Error('Entity not found')
}
return await nodesStore.writeEntity(currentProviderId(), currentServiceId(), node.collection, entityId, content)
return await nodesStore.writeEntity(`${node.provider}:${node.service}:${node.collection || ROOT_ID}:${entityId}`, content)
}
// Get a URL suitable for inline viewing (img src / video src)
@@ -253,7 +294,7 @@ export function useDocumentManager(options: UseDocumentManagerOptions) {
await providersStore.list([currentProviderId()])
await servicesStore.list()
if (autoFetch) {
await refresh()
await resetAndLoad()
}
}
@@ -262,6 +303,8 @@ export function useDocumentManager(options: UseDocumentManagerOptions) {
currentLocation,
isLoading,
error,
hasMore,
isLoadingMore,
// Provider/Service
provider,
@@ -279,7 +322,8 @@ export function useDocumentManager(options: UseDocumentManagerOptions) {
navigateTo,
navigateUp,
navigateToRoot,
refresh,
resetAndLoad,
loadMore,
// Operations
createFolder,