mirror of
https://github.com/hcengineering/platform.git
synced 2024-11-22 03:14:40 +03:00
UBERF-7524 Move files and folders in drive (#6016)
Signed-off-by: Alexander Onnikov <Alexander.Onnikov@xored.com>
This commit is contained in:
parent
8eea8f8ccd
commit
fa82ee1939
@ -45,7 +45,7 @@ import {
|
||||
import { TDoc, TTypedSpace } from '@hcengineering/model-core'
|
||||
import print from '@hcengineering/model-print'
|
||||
import tracker from '@hcengineering/model-tracker'
|
||||
import view, { type Viewlet, createAction } from '@hcengineering/model-view'
|
||||
import view, { type Viewlet, actionTemplates, createAction } from '@hcengineering/model-view'
|
||||
import workbench from '@hcengineering/model-workbench'
|
||||
import { getEmbeddedLabel } from '@hcengineering/platform'
|
||||
|
||||
@ -410,6 +410,23 @@ function defineFolder (builder: Builder): void {
|
||||
},
|
||||
drive.action.RenameFolder
|
||||
)
|
||||
|
||||
createAction(builder, {
|
||||
...actionTemplates.move,
|
||||
action: view.actionImpl.ShowPopup,
|
||||
actionProps: {
|
||||
component: drive.component.MoveResource,
|
||||
element: 'top',
|
||||
fillProps: {
|
||||
_object: 'value'
|
||||
}
|
||||
},
|
||||
target: drive.class.Folder,
|
||||
context: {
|
||||
mode: ['browser', 'context'],
|
||||
group: 'tools'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function defineFile (builder: Builder): void {
|
||||
@ -488,6 +505,23 @@ function defineFile (builder: Builder): void {
|
||||
},
|
||||
drive.action.RenameFile
|
||||
)
|
||||
|
||||
createAction(builder, {
|
||||
...actionTemplates.move,
|
||||
action: view.actionImpl.ShowPopup,
|
||||
actionProps: {
|
||||
component: drive.component.MoveResource,
|
||||
element: 'top',
|
||||
fillProps: {
|
||||
_object: 'value'
|
||||
}
|
||||
},
|
||||
target: drive.class.File,
|
||||
context: {
|
||||
mode: ['browser', 'context'],
|
||||
group: 'tools'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function defineApplication (builder: Builder): void {
|
||||
|
@ -43,6 +43,7 @@ export default mergeIds(driveId, drive, {
|
||||
FolderPresenter: '' as AnyComponent,
|
||||
FilePresenter: '' as AnyComponent,
|
||||
FileSizePresenter: '' as AnyComponent,
|
||||
MoveResource: '' as AnyComponent,
|
||||
ResourcePresenter: '' as AnyComponent
|
||||
},
|
||||
function: {
|
||||
|
@ -113,9 +113,6 @@
|
||||
allowDeselect
|
||||
showNavigate={false}
|
||||
docProps={{ disabled: true, noUnderline: true }}
|
||||
on:object={(evt) => {
|
||||
console.log('selected', evt)
|
||||
}}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
|
||||
|
107
plugins/drive-resources/src/components/MoveResource.svelte
Normal file
107
plugins/drive-resources/src/components/MoveResource.svelte
Normal file
@ -0,0 +1,107 @@
|
||||
<!--
|
||||
// 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 { Ref } from '@hcengineering/core'
|
||||
import type { Drive, Folder, Resource } from '@hcengineering/drive'
|
||||
import presentation, { Card, getClient, SpaceSelector } from '@hcengineering/presentation'
|
||||
import view from '@hcengineering/view'
|
||||
import { ObjectBox } from '@hcengineering/view-resources'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
import drive from '../plugin'
|
||||
import { moveResources } from '../utils'
|
||||
|
||||
import DrivePresenter from './DrivePresenter.svelte'
|
||||
import ResourcePresenter from './ResourcePresenter.svelte'
|
||||
|
||||
export let value: Resource
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
let space: Ref<Drive> = value.space
|
||||
let parent: Ref<Folder> = value.parent as Ref<Folder>
|
||||
|
||||
async function save (): Promise<void> {
|
||||
await moveResources([value], space, parent ?? drive.ids.Root)
|
||||
}
|
||||
|
||||
let children: Ref<Folder>[] = []
|
||||
$: void updateChildren(value)
|
||||
|
||||
async function updateChildren (resource: Resource): Promise<void> {
|
||||
children = await findChildren(resource)
|
||||
}
|
||||
|
||||
async function findChildren (resource: Resource): Promise<Array<Ref<Folder>>> {
|
||||
if (hierarchy.isDerived(resource._class, drive.class.Folder)) {
|
||||
const children = await client.findAll(
|
||||
drive.class.Folder,
|
||||
{ space: resource.space, path: resource._id as Ref<Folder> },
|
||||
{ projection: { _id: 1 } }
|
||||
)
|
||||
|
||||
return children.map((p) => p._id)
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
||||
|
||||
$: canSave = space !== value.space || parent !== value.parent
|
||||
</script>
|
||||
|
||||
<Card
|
||||
label={view.string.Move}
|
||||
okAction={save}
|
||||
okLabel={presentation.string.Save}
|
||||
fullSize
|
||||
{canSave}
|
||||
on:close={() => dispatch('close')}
|
||||
on:changeContent
|
||||
>
|
||||
<svelte:fragment slot="header">
|
||||
<ResourcePresenter {value} shouldShowAvatar={false} noUnderline />
|
||||
</svelte:fragment>
|
||||
|
||||
<div class="flex-row-center gap-2 min-w-100">
|
||||
<SpaceSelector
|
||||
bind:space
|
||||
_class={drive.class.Drive}
|
||||
label={drive.string.Drive}
|
||||
component={DrivePresenter}
|
||||
iconWithEmoji={view.ids.IconWithEmoji}
|
||||
defaultIcon={drive.icon.Drive}
|
||||
kind={'regular'}
|
||||
size={'small'}
|
||||
on:change={() => {
|
||||
parent = drive.ids.Root
|
||||
}}
|
||||
/>
|
||||
<ObjectBox
|
||||
bind:value={parent}
|
||||
_class={drive.class.Folder}
|
||||
label={drive.string.Root}
|
||||
docQuery={{ space }}
|
||||
kind={'regular'}
|
||||
size={'small'}
|
||||
searchField={'name'}
|
||||
allowDeselect={true}
|
||||
showNavigate={false}
|
||||
docProps={{ disabled: true, noUnderline: true }}
|
||||
excluded={[value._id, ...children]}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
@ -32,6 +32,7 @@ import FileSizePresenter from './components/FileSizePresenter.svelte'
|
||||
import FolderPanel from './components/FolderPanel.svelte'
|
||||
import FolderPresenter from './components/FolderPresenter.svelte'
|
||||
import GridView from './components/GridView.svelte'
|
||||
import MoveResource from './components/MoveResource.svelte'
|
||||
import ResourcePresenter from './components/ResourcePresenter.svelte'
|
||||
|
||||
import { getDriveLink, getFileLink, getFolderLink, resolveLocation } from './navigation'
|
||||
@ -109,6 +110,7 @@ export default async (): Promise<Resources> => ({
|
||||
FolderPanel,
|
||||
FolderPresenter,
|
||||
GridView,
|
||||
MoveResource,
|
||||
ResourcePresenter
|
||||
},
|
||||
actionImpl: {
|
||||
|
@ -98,6 +98,38 @@ export async function renameResource (resource: Resource): Promise<void> {
|
||||
})
|
||||
}
|
||||
|
||||
export async function moveResources (resources: Resource[], space: Ref<Drive>, parent: Ref<Folder>): Promise<void> {
|
||||
const client = getClient()
|
||||
|
||||
const folder = parent !== drive.ids.Root ? await client.findOne(drive.class.Folder, { _id: parent }) : undefined
|
||||
|
||||
const path = folder !== undefined ? [folder._id, ...folder.path] : []
|
||||
|
||||
const folders = resources.filter((p) => p._class === drive.class.Folder).map((p) => p._id)
|
||||
const children = await client.findAll(drive.class.Resource, { path: { $in: folders } })
|
||||
const byParent = new Map<Ref<Resource>, Resource[]>()
|
||||
for (const child of children) {
|
||||
const group = byParent.get(child.parent) ?? []
|
||||
group.push(child)
|
||||
byParent.set(child.parent, group)
|
||||
}
|
||||
|
||||
const ops = client.apply(parent)
|
||||
|
||||
for (const resource of resources) {
|
||||
await ops.update(resource, { space, parent, path })
|
||||
|
||||
const children = byParent.get(resource._id) ?? []
|
||||
for (const child of children) {
|
||||
// remove old path and add new path
|
||||
const childPath = [...child.path.filter((p) => !resource.path.includes(p)), ...path]
|
||||
await ops.update(child, { space, path: childPath })
|
||||
}
|
||||
}
|
||||
|
||||
await ops.commit()
|
||||
}
|
||||
|
||||
const fileTypesMap: Record<string, AnySvelteComponent> = {
|
||||
'application/pdf': FileTypePdf,
|
||||
audio: FileTypeAudio,
|
||||
|
Loading…
Reference in New Issue
Block a user