diff --git a/src/abstractions/collection.service.ts b/src/abstractions/collection.service.ts index a6decc2c09..4847a816f1 100644 --- a/src/abstractions/collection.service.ts +++ b/src/abstractions/collection.service.ts @@ -1,6 +1,7 @@ import { CollectionData } from '../models/data/collectionData'; import { Collection } from '../models/domain/collection'; +import { TreeNode } from '../models/domain/treeNode'; import { CollectionView } from '../models/view/collectionView'; @@ -13,6 +14,7 @@ export abstract class CollectionService { get: (id: string) => Promise; getAll: () => Promise; getAllDecrypted: () => Promise; + getAllNested: (collections?: CollectionView[]) => Promise>>; upsert: (collection: CollectionData | CollectionData[]) => Promise; replace: (collections: { [id: string]: CollectionData; }) => Promise; clear: (userId: string) => Promise; diff --git a/src/misc/serviceUtils.ts b/src/misc/serviceUtils.ts new file mode 100644 index 0000000000..550a402925 --- /dev/null +++ b/src/misc/serviceUtils.ts @@ -0,0 +1,37 @@ +import { + ITreeNodeObject, + TreeNode, +} from '../models/domain/treeNode'; + +export class ServiceUtils { + static nestedTraverse(nodeTree: Array>, partIndex: number, parts: string[], + obj: ITreeNodeObject, delimiter: string) { + if (parts.length <= partIndex) { + return; + } + + const end = partIndex === parts.length - 1; + const partName = parts[partIndex]; + + for (let i = 0; i < nodeTree.length; i++) { + if (nodeTree[i].node.name === parts[partIndex]) { + if (end && nodeTree[i].node.id !== obj.id) { + // Another node with the same name. + nodeTree.push(new TreeNode(obj, partName)); + return; + } + this.nestedTraverse(nodeTree[i].children, partIndex + 1, parts, obj, delimiter); + return; + } + } + + if (nodeTree.filter((n) => n.node.name === partName).length === 0) { + if (end) { + nodeTree.push(new TreeNode(obj, partName)); + return; + } + const newPartName = parts[partIndex] + delimiter + parts[partIndex + 1]; + this.nestedTraverse(nodeTree, 0, [newPartName, ...parts.slice(partIndex + 2)], obj, delimiter); + } + } +} diff --git a/src/models/domain/treeNode.ts b/src/models/domain/treeNode.ts index 4463c50fb4..ee020c2980 100644 --- a/src/models/domain/treeNode.ts +++ b/src/models/domain/treeNode.ts @@ -1,8 +1,14 @@ -export class TreeNode { +export class TreeNode { node: T; children: Array> = []; - constructor(node: T) { + constructor(node: T, name: string) { this.node = node; + this.node.name = name; } } + +export interface ITreeNodeObject { + id: string; + name: string; +} diff --git a/src/models/view/collectionView.ts b/src/models/view/collectionView.ts index 1c625182d4..f667f742db 100644 --- a/src/models/view/collectionView.ts +++ b/src/models/view/collectionView.ts @@ -1,8 +1,9 @@ import { View } from './view'; import { Collection } from '../domain/collection'; +import { ITreeNodeObject } from '../domain/treeNode'; -export class CollectionView implements View { +export class CollectionView implements View, ITreeNodeObject { id: string; organizationId: string; name: string; diff --git a/src/models/view/folderView.ts b/src/models/view/folderView.ts index a0fa99b48d..a61091b2fd 100644 --- a/src/models/view/folderView.ts +++ b/src/models/view/folderView.ts @@ -1,8 +1,9 @@ import { View } from './view'; import { Folder } from '../domain/folder'; +import { ITreeNodeObject } from '../domain/treeNode'; -export class FolderView implements View { +export class FolderView implements View, ITreeNodeObject { id: string = null; name: string; revisionDate: Date; diff --git a/src/services/collection.service.ts b/src/services/collection.service.ts index 152d91713b..42a0f48361 100644 --- a/src/services/collection.service.ts +++ b/src/services/collection.service.ts @@ -1,6 +1,7 @@ import { CollectionData } from '../models/data/collectionData'; import { Collection } from '../models/domain/collection'; +import { TreeNode } from '../models/domain/treeNode'; import { CollectionView } from '../models/view/collectionView'; @@ -10,11 +11,13 @@ import { I18nService } from '../abstractions/i18n.service'; import { StorageService } from '../abstractions/storage.service'; import { UserService } from '../abstractions/user.service'; +import { ServiceUtils } from '../misc/serviceUtils'; import { Utils } from '../misc/utils'; const Keys = { collectionsPrefix: 'collections_', }; +const NestingDelimiter = '/'; export class CollectionService implements CollectionServiceAbstraction { decryptedCollectionCache: CollectionView[]; @@ -95,6 +98,19 @@ export class CollectionService implements CollectionServiceAbstraction { return this.decryptedCollectionCache; } + async getAllNested(collections: CollectionView[] = null): Promise>> { + if (collections == null) { + collections = await this.getAllDecrypted(); + } + const nodes: Array> = []; + collections.forEach((f) => { + const collectionCopy = new CollectionView(); + collectionCopy.id = f.id; + ServiceUtils.nestedTraverse(nodes, 0, f.name.split(NestingDelimiter), collectionCopy, NestingDelimiter); + }); + return nodes; + } + async upsert(collection: CollectionData | CollectionData[]): Promise { const userId = await this.userService.getUserId(); let collections = await this.storageService.get<{ [id: string]: CollectionData; }>( diff --git a/src/services/folder.service.ts b/src/services/folder.service.ts index 07dc79024a..6ca6875de0 100644 --- a/src/services/folder.service.ts +++ b/src/services/folder.service.ts @@ -2,6 +2,7 @@ import { FolderData } from '../models/data/folderData'; import { Folder } from '../models/domain/folder'; import { SymmetricCryptoKey } from '../models/domain/symmetricCryptoKey'; +import { TreeNode } from '../models/domain/treeNode'; import { FolderRequest } from '../models/request/folderRequest'; @@ -18,8 +19,8 @@ import { StorageService } from '../abstractions/storage.service'; import { UserService } from '../abstractions/user.service'; import { CipherData } from '../models/data/cipherData'; +import { ServiceUtils } from '../misc/serviceUtils'; import { Utils } from '../misc/utils'; -import { TreeNode } from '../models/domain/treeNode'; const Keys = { foldersPrefix: 'folders_', @@ -104,7 +105,7 @@ export class FolderService implements FolderServiceAbstraction { const folderCopy = new FolderView(); folderCopy.id = f.id; folderCopy.revisionDate = f.revisionDate; - this.nestedTraverse(nodes, 0, f.name.split(NestingDelimiter), folderCopy); + ServiceUtils.nestedTraverse(nodes, 0, f.name.split(NestingDelimiter), folderCopy, NestingDelimiter); }); return nodes; } @@ -199,37 +200,4 @@ export class FolderService implements FolderServiceAbstraction { await this.apiService.deleteFolder(id); await this.delete(id); } - - private nestedTraverse(nodeTree: Array>, partIndex: number, - parts: string[], folder: FolderView) { - if (parts.length <= partIndex) { - return; - } - - const end = partIndex === parts.length - 1; - const partName = parts[partIndex]; - - for (let i = 0; i < nodeTree.length; i++) { - if (nodeTree[i].node.name === parts[partIndex]) { - if (end && nodeTree[i].node.id !== folder.id) { - // Another node with the same name. - folder.name = partName; - nodeTree.push(new TreeNode(folder)); - return; - } - this.nestedTraverse(nodeTree[i].children, partIndex + 1, parts, folder); - return; - } - } - - if (nodeTree.filter((n) => n.node.name === partName).length === 0) { - if (end) { - folder.name = partName; - nodeTree.push(new TreeNode(folder)); - return; - } - const newPartName = parts[partIndex] + NestingDelimiter + parts[partIndex + 1]; - this.nestedTraverse(nodeTree, 0, [newPartName, ...parts.slice(partIndex + 2)], folder); - } - } }