From de0689111175c5ed6f5fba6c0cc44036ac48b320 Mon Sep 17 00:00:00 2001 From: Alexander Onnikov Date: Mon, 12 Aug 2024 13:31:54 +0700 Subject: [PATCH] UBERF-7853 Drive search (#6318) --- models/drive/package.json | 1 + models/drive/src/index.ts | 33 ++++++++++ models/drive/src/plugin.ts | 9 +++ models/server-drive/src/index.ts | 14 ++++ plugins/drive-assets/lang/en.json | 2 + plugins/drive-assets/lang/es.json | 2 + plugins/drive-assets/lang/fr.json | 2 + plugins/drive-assets/lang/pt.json | 2 + plugins/drive-assets/lang/ru.json | 2 + plugins/drive-assets/lang/zh.json | 2 + .../src/components/FileSearchItem.svelte | 35 ++++++++++ .../src/components/FolderSearchItem.svelte | 31 +++++++++ plugins/drive-resources/src/index.ts | 64 ++++++++++++++++++- 13 files changed, 197 insertions(+), 2 deletions(-) create mode 100644 plugins/drive-resources/src/components/FileSearchItem.svelte create mode 100644 plugins/drive-resources/src/components/FolderSearchItem.svelte diff --git a/models/drive/package.json b/models/drive/package.json index 0e641bcf8d..07f780594f 100644 --- a/models/drive/package.json +++ b/models/drive/package.json @@ -32,6 +32,7 @@ "@hcengineering/model": "^0.6.11", "@hcengineering/model-core": "^0.6.0", "@hcengineering/model-preference": "^0.6.0", + "@hcengineering/model-presentation": "^0.6.0", "@hcengineering/model-print": "^0.6.0", "@hcengineering/model-tracker": "^0.6.0", "@hcengineering/model-view": "^0.6.0", diff --git a/models/drive/src/index.ts b/models/drive/src/index.ts index 2795f36780..249da898a1 100644 --- a/models/drive/src/index.ts +++ b/models/drive/src/index.ts @@ -55,6 +55,7 @@ import { UX } from '@hcengineering/model' import { TAttachedDoc, TDoc, TType, TTypedSpace } from '@hcengineering/model-core' +import presentation from '@hcengineering/model-presentation' import print from '@hcengineering/model-print' import tracker from '@hcengineering/model-tracker' import view, { type Viewlet, actionTemplates, classPresenter, createAction } from '@hcengineering/model-view' @@ -432,6 +433,22 @@ function defineFolder (builder: Builder): void { encode: drive.function.FolderLinkProvider }) + // Search + + builder.createDoc( + presentation.class.ObjectSearchCategory, + core.space.Model, + { + title: drive.string.Folders, + icon: drive.icon.Drive, + label: presentation.string.Search, + query: drive.completion.FolderQuery, + context: ['search', 'mention', 'spotlight'], + classToSearch: drive.class.Folder + }, + drive.completion.FolderCategory + ) + // Actions builder.mixin(drive.class.Folder, core.class.Class, view.mixin.IgnoreActions, { @@ -566,6 +583,22 @@ function defineFile (builder: Builder): void { components: { input: chunter.component.ChatMessageInput } }) + // Search + + builder.createDoc( + presentation.class.ObjectSearchCategory, + core.space.Model, + { + title: drive.string.Files, + icon: drive.icon.Drive, + label: presentation.string.Search, + query: drive.completion.FileQuery, + context: ['search', 'mention', 'spotlight'], + classToSearch: drive.class.File + }, + drive.completion.FileCategory + ) + // Actions builder.mixin(drive.class.File, core.class.Class, view.mixin.IgnoreActions, { diff --git a/models/drive/src/plugin.ts b/models/drive/src/plugin.ts index 2de322b187..533cb025d5 100644 --- a/models/drive/src/plugin.ts +++ b/models/drive/src/plugin.ts @@ -17,6 +17,7 @@ import type { Doc, Ref } from '@hcengineering/core' import {} from '@hcengineering/core' import { driveId } from '@hcengineering/drive' import drive from '@hcengineering/drive-resources/src/plugin' +import { type ObjectSearchCategory, type ObjectSearchFactory } from '@hcengineering/model-presentation' import { type IntlString, type Resource, mergeIds } from '@hcengineering/platform' import { type AnyComponent, type Location } from '@hcengineering/ui' import { @@ -55,6 +56,12 @@ export default mergeIds(driveId, drive, { CanRenameFile: '' as Resource, CanRenameFolder: '' as Resource }, + completion: { + FileQuery: '' as Resource, + FileCategory: '' as Ref, + FolderQuery: '' as Resource, + FolderCategory: '' as Ref + }, viewlet: { Grid: '' as Ref, DriveTable: '' as Ref, @@ -93,6 +100,8 @@ export default mergeIds(driveId, drive, { Parent: '' as IntlString, Path: '' as IntlString, Drives: '' as IntlString, + Files: '' as IntlString, + Folders: '' as IntlString, Version: '' as IntlString, Restore: '' as IntlString } diff --git a/models/server-drive/src/index.ts b/models/server-drive/src/index.ts index 817c57b30b..c040399a70 100644 --- a/models/server-drive/src/index.ts +++ b/models/server-drive/src/index.ts @@ -39,4 +39,18 @@ export function createModel (builder: Builder): void { collectDocs: serverDrive.function.FindFolderResources } ) + + builder.mixin(drive.class.File, core.class.Class, serverCore.mixin.SearchPresenter, { + searchConfig: { + icon: drive.icon.File, + title: 'name' + } + }) + + builder.mixin(drive.class.Folder, core.class.Class, serverCore.mixin.SearchPresenter, { + searchConfig: { + icon: drive.icon.Folder, + title: 'name' + } + }) } diff --git a/plugins/drive-assets/lang/en.json b/plugins/drive-assets/lang/en.json index f78383395d..917295a9c3 100644 --- a/plugins/drive-assets/lang/en.json +++ b/plugins/drive-assets/lang/en.json @@ -4,9 +4,11 @@ "Drives": "Drives", "Grid": "Grid", "File": "File", + "Files": "Files", "FileVersion": "File version", "FileVersions": "File versions", "Folder": "Folder", + "Folders": "Folders", "Resource": "Resource", "Name": "Name", "Description": "Description", diff --git a/plugins/drive-assets/lang/es.json b/plugins/drive-assets/lang/es.json index a06ae13a75..c93bd5a2f8 100644 --- a/plugins/drive-assets/lang/es.json +++ b/plugins/drive-assets/lang/es.json @@ -4,9 +4,11 @@ "Drives": "Unidades", "Grid": "Red", "File": "Archivo", + "Files": "Archivos", "FileVersion": "Versión del archivo", "FileVersions": "Versiones de archivos", "Folder": "Carpeta", + "Folders": "Carpetas", "Resource": "Recurso", "Name": "Nombre", "Description": "Descripción", diff --git a/plugins/drive-assets/lang/fr.json b/plugins/drive-assets/lang/fr.json index 7daf2d616e..4b1a26856d 100644 --- a/plugins/drive-assets/lang/fr.json +++ b/plugins/drive-assets/lang/fr.json @@ -4,9 +4,11 @@ "Drives": "Disques", "Grid": "Grille", "File": "Fichier", + "Files": "Fichiers", "FileVersion": "Version du fichier", "FileVersions": "Versions de fichiers", "Folder": "Dossier", + "Folders": "Dossiers", "Resource": "Ressource", "Name": "Nom", "Description": "Description", diff --git a/plugins/drive-assets/lang/pt.json b/plugins/drive-assets/lang/pt.json index fc063c799f..0a2039008e 100644 --- a/plugins/drive-assets/lang/pt.json +++ b/plugins/drive-assets/lang/pt.json @@ -4,9 +4,11 @@ "Drives": "Unidades", "Grid": "Grade", "File": "Ficheiro", + "Files": "Ficheiros", "FileVersion": "Versão do ficheiro", "FileVersions": "Versões de ficheiro", "Folder": "Pasta", + "Folders": "Pastas", "Resource": "Recurso", "Name": "Nome", "Description": "Descrição", diff --git a/plugins/drive-assets/lang/ru.json b/plugins/drive-assets/lang/ru.json index 825892ec0d..5faa793790 100644 --- a/plugins/drive-assets/lang/ru.json +++ b/plugins/drive-assets/lang/ru.json @@ -4,9 +4,11 @@ "Drives": "Диски", "Grid": "Сетка", "File": "Файл", + "Files": "Файлы", "FileVersion": "Версия файла", "FileVersions": "Версии файла", "Folder": "Папка", + "Folders": "Папки", "Resource": "Ресурс", "Name": "Название", "Description": "Описание", diff --git a/plugins/drive-assets/lang/zh.json b/plugins/drive-assets/lang/zh.json index 0ac452d5ac..6309c7cd7a 100644 --- a/plugins/drive-assets/lang/zh.json +++ b/plugins/drive-assets/lang/zh.json @@ -4,9 +4,11 @@ "Drives": "磁盘", "Grid": "网格", "File": "文件", + "Files": "文件", "FileVersion": "檔案版本", "FileVersions": "檔案版本", "Folder": "文件夹", + "Folders": "文件夹", "Resource": "资源", "Name": "名称", "Description": "描述", diff --git a/plugins/drive-resources/src/components/FileSearchItem.svelte b/plugins/drive-resources/src/components/FileSearchItem.svelte new file mode 100644 index 0000000000..ee09641e45 --- /dev/null +++ b/plugins/drive-resources/src/components/FileSearchItem.svelte @@ -0,0 +1,35 @@ + + + +
+
+ +
+ + {value.name} + +
diff --git a/plugins/drive-resources/src/components/FolderSearchItem.svelte b/plugins/drive-resources/src/components/FolderSearchItem.svelte new file mode 100644 index 0000000000..51bad5d29d --- /dev/null +++ b/plugins/drive-resources/src/components/FolderSearchItem.svelte @@ -0,0 +1,31 @@ + + + +
+
+ +
+ + {value.name} + +
diff --git a/plugins/drive-resources/src/index.ts b/plugins/drive-resources/src/index.ts index 9a13923579..8b8f99b472 100644 --- a/plugins/drive-resources/src/index.ts +++ b/plugins/drive-resources/src/index.ts @@ -13,9 +13,10 @@ // limitations under the License. // -import { type Doc, type Ref, type WithLookup } from '@hcengineering/core' +import type { Class, Client, Doc, DocumentQuery, Ref, RelatedDocument, WithLookup } from '@hcengineering/core' import drive, { type Drive, type File, type FileVersion, type Folder } from '@hcengineering/drive' import { type Resources } from '@hcengineering/platform' +import { type ObjectSearchResult, getFileUrl } from '@hcengineering/presentation' import { showPopup, type Location } from '@hcengineering/ui' import CreateDrive from './components/CreateDrive.svelte' @@ -27,19 +28,72 @@ import EditFile from './components/EditFile.svelte' import EditFolder from './components/EditFolder.svelte' import FilePanel from './components/FilePanel.svelte' import FilePresenter from './components/FilePresenter.svelte' +import FileSearchItem from './components/FileSearchItem.svelte' import FileSizePresenter from './components/FileSizePresenter.svelte' import FileVersionPresenter from './components/FileVersionPresenter.svelte' import FileVersionVersionPresenter from './components/FileVersionVersionPresenter.svelte' import FolderPanel from './components/FolderPanel.svelte' import FolderPresenter from './components/FolderPresenter.svelte' +import FolderSearchItem from './components/FolderSearchItem.svelte' import GridView from './components/GridView.svelte' import MoveResource from './components/MoveResource.svelte' import ResourcePresenter from './components/ResourcePresenter.svelte' -import { getFileUrl } from '@hcengineering/presentation' import { getDriveLink, getFileLink, getFolderLink, resolveLocation } from './navigation' import { restoreFileVersion, showCreateFolderPopup, showRenameResourcePopup } from './utils' +const toFileObjectSearchResult = (e: WithLookup): ObjectSearchResult => ({ + doc: e, + title: e.name, + icon: drive.icon.File, + component: FileSearchItem +}) + +const toFolderObjectSearchResult = (e: WithLookup): ObjectSearchResult => ({ + doc: e, + title: e.name, + icon: drive.icon.Folder, + component: FolderSearchItem +}) + +async function queryFile ( + _class: Ref>, + client: Client, + search: string, + filter?: { in?: RelatedDocument[], nin?: RelatedDocument[] } +): Promise { + const q: DocumentQuery = { name: { $like: `%${search}%` } } + if (filter?.in !== undefined || filter?.nin !== undefined) { + q._id = {} + if (filter.in !== undefined) { + q._id.$in = filter.in?.map((it) => it._id as Ref) + } + if (filter.nin !== undefined) { + q._id.$nin = filter.nin?.map((it) => it._id as Ref) + } + } + return (await client.findAll(_class, q, { limit: 200 })).map(toFileObjectSearchResult) +} + +async function queryFolder ( + _class: Ref>, + client: Client, + search: string, + filter?: { in?: RelatedDocument[], nin?: RelatedDocument[] } +): Promise { + const q: DocumentQuery = { name: { $like: `%${search}%` } } + if (filter?.in !== undefined || filter?.nin !== undefined) { + q._id = {} + if (filter.in !== undefined) { + q._id.$in = filter.in?.map((it) => it._id as Ref) + } + if (filter.nin !== undefined) { + q._id.$nin = filter.nin?.map((it) => it._id as Ref) + } + } + return (await client.findAll(_class, q, { limit: 200 })).map(toFolderObjectSearchResult) +} + async function CreateRootFolder (doc: Drive): Promise { await showCreateFolderPopup(doc._id, drive.ids.Root) } @@ -135,6 +189,12 @@ export default async (): Promise => ({ RenameFolder, RestoreFileVersion }, + completion: { + FileQuery: async (client: Client, query: string, filter?: { in?: RelatedDocument[], nin?: RelatedDocument[] }) => + await queryFile(drive.class.File, client, query, filter), + FolderQuery: async (client: Client, query: string, filter?: { in?: RelatedDocument[], nin?: RelatedDocument[] }) => + await queryFolder(drive.class.Folder, client, query, filter) + }, function: { DriveLinkProvider, FileLinkProvider,