UBERF-7853 Drive search (#6318)

This commit is contained in:
Alexander Onnikov 2024-08-12 13:31:54 +07:00 committed by GitHub
parent 840d26dd01
commit de06891111
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 197 additions and 2 deletions

View File

@ -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",

View File

@ -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, {

View File

@ -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<ViewActionAvailabilityFunction>,
CanRenameFolder: '' as Resource<ViewActionAvailabilityFunction>
},
completion: {
FileQuery: '' as Resource<ObjectSearchFactory>,
FileCategory: '' as Ref<ObjectSearchCategory>,
FolderQuery: '' as Resource<ObjectSearchFactory>,
FolderCategory: '' as Ref<ObjectSearchCategory>
},
viewlet: {
Grid: '' as Ref<ViewletDescriptor>,
DriveTable: '' as Ref<Viewlet>,
@ -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
}

View File

@ -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'
}
})
}

View File

@ -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",

View File

@ -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",

View File

@ -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",

View File

@ -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",

View File

@ -4,9 +4,11 @@
"Drives": "Диски",
"Grid": "Сетка",
"File": "Файл",
"Files": "Файлы",
"FileVersion": "Версия файла",
"FileVersions": "Версии файла",
"Folder": "Папка",
"Folders": "Папки",
"Resource": "Ресурс",
"Name": "Название",
"Description": "Описание",

View File

@ -4,9 +4,11 @@
"Drives": "磁盘",
"Grid": "网格",
"File": "文件",
"Files": "文件",
"FileVersion": "檔案版本",
"FileVersions": "檔案版本",
"Folder": "文件夹",
"Folders": "文件夹",
"Resource": "资源",
"Name": "名称",
"Description": "描述",

View File

@ -0,0 +1,35 @@
<!--
// Copyright © 2024 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
//
-->
<script lang="ts">
import { WithLookup } from '@hcengineering/core'
import { File } from '@hcengineering/drive'
import { Icon } from '@hcengineering/ui'
import { getFileTypeIcon } from '../utils'
export let value: WithLookup<File>
$: icon = getFileTypeIcon(value.$lookup?.file?.type ?? '')
</script>
<div class="flex-row-center">
<div class="flex-center p-1 content-dark-color flex-no-shrink mr-2-5">
<Icon {icon} size={'medium'} />
</div>
<span class="overflow-label">
{value.name}
</span>
</div>

View File

@ -0,0 +1,31 @@
<!--
// Copyright © 2024 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
//
-->
<script lang="ts">
import { WithLookup } from '@hcengineering/core'
import drive, { Folder } from '@hcengineering/drive'
import { Icon } from '@hcengineering/ui'
export let value: WithLookup<Folder>
</script>
<div class="flex-row-center">
<div class="flex-center p-1 content-dark-color flex-no-shrink mr-2-5">
<Icon icon={drive.icon.Folder} size={'medium'} />
</div>
<span class="overflow-label">
{value.name}
</span>
</div>

View File

@ -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<File>): ObjectSearchResult => ({
doc: e,
title: e.name,
icon: drive.icon.File,
component: FileSearchItem
})
const toFolderObjectSearchResult = (e: WithLookup<Folder>): ObjectSearchResult => ({
doc: e,
title: e.name,
icon: drive.icon.Folder,
component: FolderSearchItem
})
async function queryFile (
_class: Ref<Class<File>>,
client: Client,
search: string,
filter?: { in?: RelatedDocument[], nin?: RelatedDocument[] }
): Promise<ObjectSearchResult[]> {
const q: DocumentQuery<File> = { 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<File>)
}
if (filter.nin !== undefined) {
q._id.$nin = filter.nin?.map((it) => it._id as Ref<File>)
}
}
return (await client.findAll(_class, q, { limit: 200 })).map(toFileObjectSearchResult)
}
async function queryFolder (
_class: Ref<Class<Folder>>,
client: Client,
search: string,
filter?: { in?: RelatedDocument[], nin?: RelatedDocument[] }
): Promise<ObjectSearchResult[]> {
const q: DocumentQuery<Folder> = { 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<Folder>)
}
if (filter.nin !== undefined) {
q._id.$nin = filter.nin?.map((it) => it._id as Ref<Folder>)
}
}
return (await client.findAll(_class, q, { limit: 200 })).map(toFolderObjectSearchResult)
}
async function CreateRootFolder (doc: Drive): Promise<void> {
await showCreateFolderPopup(doc._id, drive.ids.Root)
}
@ -135,6 +189,12 @@ export default async (): Promise<Resources> => ({
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,