Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion src/components/FolderPicker/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import { IOCozyFile, NextcloudFile } from 'cozy-client/types/types'

export type File = IOCozyFile | NextcloudFile
export type File = (IOCozyFile | NextcloudFile) & {
cozyMetadata?: {
createdOn?: string
}
attributes?: {
cozyMetadata?: {
createdOn?: string
}
}
}

export interface FolderPickerEntry {
_id?: string
Expand All @@ -11,4 +20,5 @@ export interface FolderPickerEntry {
dir_id?: string
class?: string
path?: string
driveId?: string
}
46 changes: 37 additions & 9 deletions src/contexts/ClipboardProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,21 @@ interface ClipboardState {
cutItemIds: Set<string>
sourceFolderIds: Set<string> | null
moveValidationModal: MoveValidationModal
sourceDirectory: IOCozyFile
}

interface ClipboardContextValue {
clipboardData: ClipboardState
copyFiles: (files: IOCozyFile[], sourceFolderIds?: Set<string>) => void
cutFiles: (files: IOCozyFile[], sourceFolderIds?: Set<string>) => void
copyFiles: (
files: IOCozyFile[],
sourceFolderIds?: Set<string>,
sourceDirectory?: IOCozyFile
) => void
cutFiles: (
files: IOCozyFile[],
sourceFolderIds?: Set<string>,
sourceDirectory?: IOCozyFile
) => void
clearClipboard: () => void
hasClipboardData: boolean
isItemCut: (itemId: string) => boolean
Expand All @@ -56,13 +65,18 @@ const HIDE_SHARING_MODAL = 'HIDE_SHARING_MODAL'
type ClipboardAction =
| {
type: typeof COPY_FILES
payload: { files: IOCozyFile[]; sourceFolderIds?: Set<string> }
payload: {
files: IOCozyFile[]
sourceFolderIds?: Set<string>
sourceDirectory?: IOCozyFile
}
}
| {
type: typeof CUT_FILES
payload: {
files: IOCozyFile[]
sourceFolderIds?: Set<string>
sourceDirectory?: IOCozyFile
}
}
| { type: typeof CLEAR_CLIPBOARD }
Expand All @@ -84,6 +98,7 @@ const initialState: ClipboardState = {
timestamp: null,
cutItemIds: new Set(),
sourceFolderIds: new Set(),
sourceDirectory: {} as IOCozyFile,
moveValidationModal: {
isVisible: false,
type: null,
Expand All @@ -106,7 +121,8 @@ const clipboardReducer = (
operation: OPERATION_COPY,
timestamp: Date.now(),
cutItemIds: new Set(),
sourceFolderIds: action.payload.sourceFolderIds ?? null
sourceFolderIds: action.payload.sourceFolderIds ?? null,
sourceDirectory: action.payload.sourceDirectory ?? ({} as IOCozyFile)
}
case CUT_FILES:
return {
Expand All @@ -115,7 +131,8 @@ const clipboardReducer = (
operation: OPERATION_CUT,
timestamp: Date.now(),
cutItemIds: new Set(action.payload.files.map(file => file._id)),
sourceFolderIds: action.payload.sourceFolderIds ?? null
sourceFolderIds: action.payload.sourceFolderIds ?? null,
sourceDirectory: action.payload.sourceDirectory ?? ({} as IOCozyFile)
}
case CLEAR_CLIPBOARD:
return {
Expand Down Expand Up @@ -157,17 +174,28 @@ const ClipboardProvider: React.FC<ClipboardProviderProps> = ({ children }) => {
const [state, dispatch] = useReducer(clipboardReducer, initialState)

const copyFiles = useCallback(
(files: IOCozyFile[], sourceFolderIds?: Set<string>) => {
dispatch({ type: COPY_FILES, payload: { files, sourceFolderIds } })
(
files: IOCozyFile[],
sourceFolderIds?: Set<string>,
sourceDirectory?: IOCozyFile
) => {
dispatch({
type: COPY_FILES,
payload: { files, sourceFolderIds, sourceDirectory }
})
},
[]
)

const cutFiles = useCallback(
(files: IOCozyFile[], sourceFolderIds?: Set<string>) => {
(
files: IOCozyFile[],
sourceFolderIds?: Set<string>,
sourceDirectory?: IOCozyFile
) => {
dispatch({
type: CUT_FILES,
payload: { files, sourceFolderIds }
payload: { files, sourceFolderIds, sourceDirectory }
})
},
[]
Expand Down
15 changes: 15 additions & 0 deletions src/declarations.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,21 @@ declare module 'cozy-client/dist/models/file' {
filename: string,
driveId: string
) => Promise<string>
export const moveRelateToSharedDrive: (
client: import('cozy-client/types/CozyClient').CozyClient,
source: {
instance?: string
sharing_id?: string
file_id?: string
dir_id?: string
},
dest: {
instance?: string
sharing_id?: string
dir_id: string
},
isCopy?: boolean
) => Promise<void>
}

declare module 'cozy-client/dist/models/note' {
Expand Down
49 changes: 42 additions & 7 deletions src/hooks/useKeyboardShortcuts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import { useAlert } from 'cozy-ui/transpiled/react/providers/Alert'
import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n'

import { isEditableTarget, normalizeKey } from './helpers'
import { isEditableButNotCheckbox, normalizeKey } from './helpers'

import { isMacOS } from '@/components/pushClient'
import { SHARED_DRIVES_DIR_ID } from '@/constants/config'
Expand All @@ -17,8 +17,8 @@
OPERATION_CUT
} from '@/contexts/ClipboardProvider'
import { useDisplayedFolder } from '@/hooks'
import { startRenamingAsync } from '@/modules/drive/rename'
import DeleteConfirm from '@/modules/drive/DeleteConfirm'
import { startRenamingAsync } from '@/modules/drive/rename'
import { useNextcloudCurrentFolder } from '@/modules/nextcloud/hooks/useNextcloudCurrentFolder'
import { handlePasteOperation } from '@/modules/paste'
import { useSelectionContext } from '@/modules/selection/SelectionProvider'
Expand All @@ -30,6 +30,7 @@
items?: IOCozyFile[]
sharingContext?: unknown
allowCopy?: boolean
allowCut?: boolean
isNextCloudFolder?: boolean
pushModal?: (modal: React.ReactElement) => void
popModal?: () => void
Expand All @@ -43,6 +44,7 @@
items = [],
sharingContext = null,
allowCopy = true,
allowCut = true,
isNextCloudFolder = false,
pushModal,
popModal,
Expand Down Expand Up @@ -104,18 +106,38 @@
return
}

copyFiles(filesToCopy, new Set(parentFolderIds))
copyFiles(
filesToCopy,
new Set(parentFolderIds),
currentFolder as IOCozyFile
)
const message =
filesToCopy.length === 1
? t('alert.item_copied')
: t('alert.items_copied', { count: filesToCopy.length })
showAlert({ message, severity: 'success' })
clearSelection()
}, [allowCopy, selectedItems, copyFiles, showAlert, t, clearSelection])
}, [
allowCopy,
selectedItems,
currentFolder,
copyFiles,
showAlert,
t,
clearSelection
])

const handleCut = useCallback(() => {
if (!selectedItems.length) return

if (!allowCut) {
showAlert({
message: t('alert.cut_not_allowed'),
severity: 'secondary'
})
return
}

const parentFolderIds = selectedItems.map(item => item.dir_id)

if (parentFolderIds.includes(SHARED_DRIVES_DIR_ID)) {
Expand All @@ -126,14 +148,26 @@
return
}

cutFiles(selectedItems, new Set(parentFolderIds))
cutFiles(
selectedItems,
new Set(parentFolderIds),
currentFolder as IOCozyFile
)
const message =
selectedItems.length === 1
? t('alert.item_cut')
: t('alert.items_cut', { count: selectedItems.length })
showAlert({ message, severity: 'success' })
clearSelection()
}, [selectedItems, cutFiles, showAlert, t, clearSelection])
}, [
selectedItems,
allowCut,
currentFolder,
cutFiles,
t,
showAlert,
clearSelection
])

const handlePaste = useCallback(async () => {
if (!hasClipboardData || !client || !currentFolder) return
Expand Down Expand Up @@ -163,6 +197,7 @@
client,
clipboardData.files,
clipboardData.operation,
clipboardData.sourceDirectory,
currentFolder,
{
showAlert,
Expand Down Expand Up @@ -257,7 +292,7 @@
}

const handleKeyDown = (event: KeyboardEvent): void => {
if (!event.target || isEditableTarget(event.target)) return
if (!event.target || isEditableButNotCheckbox(event.target)) return

Check failure on line 295 in src/hooks/useKeyboardShortcuts.tsx

View workflow job for this annotation

GitHub Actions / Build and publish

Unsafe call of an `any` typed value

const combo = normalizeKey(event, isApple)
const handler = shortcuts[combo]
Expand Down
12 changes: 9 additions & 3 deletions src/modules/duplicate/components/DuplicateModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React, { FC, useState } from 'react'
import { useNavigate } from 'react-router-dom'

import { useClient } from 'cozy-client'
import { copy } from 'cozy-client/dist/models/file'
import { useAlert } from 'cozy-ui/transpiled/react/providers/Alert'
import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n'

Expand All @@ -12,19 +11,23 @@ import { File, FolderPickerEntry } from '@/components/FolderPicker/types'
import { ROOT_DIR_ID } from '@/constants/config'
import { useCancelable } from '@/modules/move/hooks/useCancelable'
import { computeNextcloudFolderQueryId } from '@/modules/nextcloud/helpers'
import { buildCopyApi } from '@/modules/paste'

interface DuplicateModalProps {
entries: FolderPickerEntry[]
currentFolder: File
onClose: () => void | Promise<void>
showNextcloudFolder?: boolean
showSharedDriveFolder?: boolean
driveId?: string
}

const DuplicateModal: FC<DuplicateModalProps> = ({
entries,
currentFolder,
onClose,
showNextcloudFolder
showNextcloudFolder,
showSharedDriveFolder
}) => {
const { t } = useI18n()
const { showAlert } = useAlert()
Expand All @@ -39,7 +42,9 @@ const DuplicateModal: FC<DuplicateModalProps> = ({
setBusy(true)
await Promise.all(
entries.map(async entry => {
await registerCancelable(copy(client, entry as Partial<File>, folder))
await registerCancelable(
buildCopyApi(client, entry, currentFolder, folder)
)
})
)

Expand Down Expand Up @@ -88,6 +93,7 @@ const DuplicateModal: FC<DuplicateModalProps> = ({
return (
<FolderPicker
showNextcloudFolder={showNextcloudFolder}
showSharedDriveFolder={showSharedDriveFolder}
currentFolder={currentFolder}
entries={entries}
// eslint-disable-next-line @typescript-eslint/no-misused-promises
Expand Down
1 change: 1 addition & 0 deletions src/modules/move/MoveModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
try {
setMoveInProgress(true)
const trashedFiles = []
const force = !sharedPaths.includes(folder.path)

Check failure on line 97 in src/modules/move/MoveModal.jsx

View workflow job for this annotation

GitHub Actions / Build and publish

'force' is assigned a value but never used
await Promise.all(
entries.map(async entry => {
const force = !sharedPaths.includes(folder.path)
Expand Down
2 changes: 2 additions & 0 deletions src/modules/navigation/AppRoute.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import FileOpenerExternal from '@/modules/viewer/FileOpenerExternal'
import { KonnectorRoutes } from '@/modules/views/Drive/KonnectorRoutes'
import { FavoritesView } from '@/modules/views/Favorites/FavoritesView'
import { FolderDuplicateView } from '@/modules/views/Folder/FolderDuplicateView'
import { SharedDriveDuplicateView } from '@/modules/views/Folder/SharedDriveDuplicateView'
import { MoveFilesView } from '@/modules/views/Modal/MoveFilesView'
import { QualifyFileView } from '@/modules/views/Modal/QualifyFileView'
import { ShareDisplayedFolderView } from '@/modules/views/Modal/ShareDisplayedFolderView'
Expand Down Expand Up @@ -146,6 +147,7 @@ const AppRoute = () => (
<Route path="file/:fileId/revision" element={<FileHistory />} />
<Route path="file/:fileId/v/revision" element={<FileHistory />} />
<Route path="share" element={<ShareDisplayedFolderView />} />
<Route path="duplicate" element={<SharedDriveDuplicateView />} />
</Route>
</>
) : null}
Expand Down
Loading
Loading