Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WiP] Selecting multiple files for deleting on left menu #368

Closed
wants to merge 6 commits into from
Closed
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
2 changes: 1 addition & 1 deletion client/components/Application/Browser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default function Browser() {
}

function DefaultBrowser() {
const path = useStore((state) => state.path)
const path = useStore((state) => state.paths)
const files = useStore((state) => state.files)
const fileEvent = useStore((state) => state.fileEvent)
const selectFile = useStore((state) => state.selectFile)
Expand Down
6 changes: 3 additions & 3 deletions client/components/Application/Buttons/Delete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,21 @@ import { selectors, useStore } from '../store'
import { useKeyPress } from 'ahooks'

export default function DeleteButton() {
const path = useStore((state) => state.path)
const paths = useStore((state) => state.paths)
const isFolder = useStore(selectors.isFolder)
const updateState = useStore((state) => state.updateState)
const type = isFolder ? 'Folder' : 'File'
useKeyPress(['ctrl.i'], (event) => {
event.preventDefault()
if (path) updateState({ dialog: `delete${type}` })
if (paths) updateState({ dialog: `delete${type}` })
})
return (
<LightTooltip title="Delete file [Ctrl+I]">
<Box>
<IconButton
label="Delete"
Icon={DeleteIcon}
disabled={!path}
disabled={!paths}
variant="text"
color="warning"
onClick={() => updateState({ dialog: `delete${type}` })}
Expand Down
6 changes: 3 additions & 3 deletions client/components/Application/Buttons/Manage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import DropdownButton from '../../Parts/Buttons/Dropdown'
import { useStore, selectors } from '../store'

export default function ManageButton() {
const path = useStore((state) => state.path)
const path = useStore((state) => state.paths)
const notIndexedFiles = useStore(selectors.notIndexedFiles)
return (
<DropdownButton
Expand All @@ -24,7 +24,7 @@ export default function ManageButton() {
}

function CopyButton() {
const path = useStore((state) => state.path)
const path = useStore((state) => state.paths)
const updateState = useStore((state) => state.updateState)
const isFolder = useStore(selectors.isFolder)
const type = isFolder ? 'Folder' : 'File'
Expand All @@ -40,7 +40,7 @@ function CopyButton() {
}

function MoveButton() {
const path = useStore((state) => state.path)
const path = useStore((state) => state.paths)
const updateState = useStore((state) => state.updateState)
const isFolder = useStore(selectors.isFolder)
const type = isFolder ? 'Folder' : 'File'
Expand Down
2 changes: 1 addition & 1 deletion client/components/Application/Content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { useStore } from './store'
export default function Content() {
const record = useStore((state) => state.record)
const indexing = useStore((state) => state.indexing)
const path = useStore((state) => state.path)
const path = useStore((state) => state.paths)
return indexing ? (
<LoadingContent />
) : record && path ? (
Expand Down
10 changes: 5 additions & 5 deletions client/components/Application/Dialogs/CopyFile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,22 @@ import InputDialog from '../../Parts/Dialogs/Input'
import { useStore } from '../store'

export default function CopyFileDialog() {
const path = useStore((state) => state.path)
const paths = useStore((state) => state.paths)
const copyFile = useStore((state) => state.copyFile)
const updateState = useStore((state) => state.updateState)
if (!path) return null
if (!paths || paths.length > 1) return null
return (
<InputDialog
open={true}
value={path}
value={paths[0]}
title="Copy File"
label="Copy"
Icon={ContentCopyIcon}
placholder="Enter a path"
description={`You are copying "${path}". Enter destination:`}
description={`You are copying "${paths[0]}". Enter destination:`}
onCancel={() => updateState({ dialog: undefined })}
onConfirm={async (toPath) => {
await copyFile(path, toPath)
await copyFile(paths[0], toPath)
updateState({ dialog: undefined })
}}
/>
Expand Down
10 changes: 5 additions & 5 deletions client/components/Application/Dialogs/CopyFolder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,22 @@ import InputDialog from '../../Parts/Dialogs/Input'
import { useStore } from '../store'

export default function CopyFolderDialog() {
const path = useStore((state) => state.path)
const paths = useStore((state) => state.paths)
const copyFolder = useStore((state) => state.copyFolder)
const updateState = useStore((state) => state.updateState)
if (!path) return null
if (!paths || paths.length > 1) return null
return (
<InputDialog
open={true}
value={path}
value={paths[0]}
title="Copy Folder"
label="Copy"
Icon={ContentCopyIcon}
placholder="Enter a path"
description={`You are copying "${path}". Enter destination:`}
description={`You are copying "${paths[0]}". Enter destination:`}
onCancel={() => updateState({ dialog: undefined })}
onConfirm={async (toPath) => {
await copyFolder(path, toPath)
await copyFolder(paths[0], toPath)
updateState({ dialog: undefined })
}}
/>
Expand Down
6 changes: 3 additions & 3 deletions client/components/Application/Dialogs/DeleteFile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import ConfirmDialog from '../../Parts/Dialogs/Confirm'
import { useStore } from '../store'

export default function DeleteFileDialog() {
const path = useStore((state) => state.path)
const paths = useStore((state) => state.paths)
const deleteFile = useStore((state) => state.deleteFile)
const updateState = useStore((state) => state.updateState)
if (!path) return null
if (!paths) return null
return (
<ConfirmDialog
open={true}
Expand All @@ -15,7 +15,7 @@ export default function DeleteFileDialog() {
cancelLabel="No"
onCancel={() => updateState({ dialog: undefined })}
onConfirm={async () => {
await deleteFile(path)
await deleteFile(paths)
updateState({ dialog: undefined })
}}
/>
Expand Down
6 changes: 3 additions & 3 deletions client/components/Application/Dialogs/DeleteFolder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import ConfirmDialog from '../../Parts/Dialogs/Confirm'
import { useStore } from '../store'

export default function DeleteFolderDialog() {
const path = useStore((state) => state.path)
const paths = useStore((state) => state.paths)
const deleteFolder = useStore((state) => state.deleteFolder)
const updateState = useStore((state) => state.updateState)
if (!path) return null
if (!paths || paths.length > 1) return null
return (
<ConfirmDialog
open={true}
Expand All @@ -15,7 +15,7 @@ export default function DeleteFolderDialog() {
cancelLabel="No"
onCancel={() => updateState({ dialog: undefined })}
onConfirm={async () => {
await deleteFolder(path)
await deleteFolder(paths[0])
updateState({ dialog: undefined })
}}
/>
Expand Down
10 changes: 5 additions & 5 deletions client/components/Application/Dialogs/MoveFile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,22 @@ import InputDialog from '../../Parts/Dialogs/Input'
import { useStore } from '../store'

export default function MoveFileDialog() {
const path = useStore((state) => state.path)
const paths = useStore((state) => state.paths)
const moveFile = useStore((state) => state.moveFile)
const updateState = useStore((state) => state.updateState)
if (!path) return null
if (!paths || paths.length > 1) return null
return (
<InputDialog
open={true}
value={path}
value={paths[0]}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure how moving file works. This might need to be reworked as well for multiple files

title="Move File"
label="Move"
Icon={CopyAllIcon}
placholder="Enter a path"
description={`You are moving "${path}". Enter destination:`}
description={`You are moving "${paths[0]}". Enter destination:`}
onCancel={() => updateState({ dialog: undefined })}
onConfirm={async (toPath) => {
await moveFile(path, toPath)
await moveFile(paths[0], toPath)
updateState({ dialog: undefined })
}}
/>
Expand Down
10 changes: 5 additions & 5 deletions client/components/Application/Dialogs/MoveFolder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,22 @@ import InputDialog from '../../Parts/Dialogs/Input'
import { useStore } from '../store'

export default function MoveFolderDialog() {
const path = useStore((state) => state.path)
const paths = useStore((state) => state.paths)
const moveFolder = useStore((state) => state.moveFolder)
const updateState = useStore((state) => state.updateState)
if (!path) return null
if (!paths || paths.length > 1) return null
return (
<InputDialog
open={true}
value={path}
value={paths[0]}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as with moving files. This might need to be adjusted for multiSelection

title="Move Folder"
label="Move"
Icon={CopyAllIcon}
placholder="Enter a path"
description={`You are moving "${path}". Enter destination:`}
description={`You are moving "${paths[0]}". Enter destination:`}
onCancel={() => updateState({ dialog: undefined })}
onConfirm={async (toPath) => {
await moveFolder(path, toPath)
await moveFolder(paths[0], toPath)
updateState({ dialog: undefined })
}}
/>
Expand Down
68 changes: 38 additions & 30 deletions client/components/Application/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import * as helpers from '../../helpers'
import * as types from '../../types'

export interface State {
path?: string
paths?: string[]
client: Client
config?: types.IConfig
record?: types.IRecord
Expand Down Expand Up @@ -46,10 +46,10 @@ export interface State {
createFile: (path: string, prompt?: string) => Promise<void>
adjustFile: (name?: string, type?: string) => Promise<void>
copyFile: (path: string, toPath: string) => Promise<void>
deleteFile: (path: string) => Promise<void>
deleteFile: (paths: string[]) => Promise<void>
moveFile: (path: string, toPath: string) => Promise<void>
locateFile: (path: string) => Promise<void>
selectFile: (path?: string) => Promise<void>
selectFile: (paths?: string[]) => Promise<void>
openFile: (path: string) => Promise<void>
closeFile: () => void

Expand Down Expand Up @@ -111,7 +111,7 @@ export function makeStore(props: ApplicationProps) {
const { loadFiles, selectFile } = get()
await loadFiles()
set({ fileEvent: { type: 'create', paths } })
if (paths.length === 1) selectFile(paths[0])
if (paths.length === 1) selectFile(paths)
await delay(500)
set({ fileEvent: undefined })
},
Expand All @@ -126,7 +126,7 @@ export function makeStore(props: ApplicationProps) {
onFilePatch: async (path) => {
const { selectFile } = get()
set({ fileEvent: { type: 'update', paths: [path] } })
selectFile(path)
selectFile([path])
await delay(500)
set({ fileEvent: undefined })
},
Expand Down Expand Up @@ -194,43 +194,52 @@ export function makeStore(props: ApplicationProps) {
}
},
adjustFile: async (name, type) => {
const { path, client, closeFile, loadFiles, selectFile } = get()
if (!path) return
await client.filePatch({ path, name, type })
set({ path: undefined })
const { paths, client, closeFile, loadFiles, selectFile } = get()
// only adjust file if one file is selected from menu
if (!paths || paths.length !== 1) return
await client.filePatch({ path: paths[0], name, type })
set({ paths: undefined })
closeFile()
await loadFiles()
await selectFile(path)
await selectFile(paths)
},
copyFile: async (path, toPath) => {
copyFile: async (paths, toPath) => {
const { client, onFileCreate } = get()
const result = await client.fileCopy({ path, toPath, deduplicate: true })
onFileCreate([result.path])
for (const path of paths) {
const result = await client.fileCopy({ path, toPath, deduplicate: true })
onFileCreate([result.path])
}
},
deleteFile: async (path) => {
deleteFile: async (paths) => {
const { client, onFileDelete } = get()
await client.fileDelete({ path })
onFileDelete(path)
for (const path of paths) {
await client.fileDelete({ path })
onFileDelete(path)
}
},
moveFile: async (path, toPath) => {
const { client, onFileCreate } = get()
const result = await client.fileMove({ path, toPath, deduplicate: true })
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know how this works. haven't tested it yet

onFileCreate([result.path])
},
locateFile: async (path) => {
set({ path })
set({ paths: [path] })
set({ fileEvent: { type: 'locate', paths: [path] } })
await delay(500)
set({ fileEvent: undefined })
},
selectFile: async (newPath) => {
const { path, record, openFile } = get()
if (path === newPath) return
set({ path: newPath })
if (!newPath) return
if (record?.path === newPath) return
selectFile: async (newPaths) => {
const { paths, record, openFile } = get()
if (paths === newPaths) return
set({ paths: newPaths })
if (!newPaths) return
if (record?.path === newPaths[0]) return
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure what this is doing. using only the first path just in case. Is it for the history? Do we need to modify the record to add all the selected paths?

if (selectors.isFolder(get())) return
await openFile(newPath)
console.log('paths', paths)
console.log('newPaths', newPaths)
// if more than one file is selected, display the only the first one
if (!paths || paths[paths.length - 1] !== newPaths[0])
await openFile(newPaths[newPaths.length - 1])
},
openFile: async (path) => {
const { client, loadFiles, fileEvent } = get()
Expand Down Expand Up @@ -356,15 +365,14 @@ export function makeStore(props: ApplicationProps) {

export const selectors = {
isFolder: (state: State) => {
return !!state.files.find(
(file) => file.path === state.path && file.type === 'folder'
)
const path = state.paths ? state.paths[0] : null
return !!state.files.find((file) => file.path === path && file.type === 'folder')
},
folderPath: (state: State) => {
if (!state.path) return undefined
if (!state.paths) return undefined
const isFolder = selectors.isFolder(state)
if (isFolder) return state.path
return helpers.getFolderPath(state.path)
if (isFolder) return state.paths[0]
return helpers.getFolderPath(state.paths[0])
},
notIndexedFiles: (state: State) => {
return state.files.filter((file) => !file.name)
Expand Down
13 changes: 7 additions & 6 deletions client/components/Parts/Trees/File.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import * as types from '../../../types'
export interface FileTreeProps {
files: types.IFile[]
event?: types.IFileEvent
selected?: string
onSelect: (path?: string) => void
selected?: string[]
onSelect: (paths?: string[]) => void
defaultExpanded?: string[]
}

Expand All @@ -31,21 +31,22 @@ export default function FileTree(props: FileTreeProps) {
: props.defaultExpanded || []
setExpanded([...new Set([...expanded, ...defaultExpanded])])
}, [props.event, props.defaultExpanded])
const selected = props.selected || ''
const selected = props.selected || ['']
return (
<Context.Provider value={{ event: props.event }}>
<ScrollBox sx={{ padding: 2 }} height="100%">
<Stack alignItems="stretch" height="100%">
<TreeView
multiSelect
selected={selected}
expanded={expanded}
onNodeSelect={(_event, nodeId) => {
props.onSelect(nodeId as string)
onNodeSelect={(_event, nodeIds) => {
props.onSelect(nodeIds as string[])
}}
onNodeToggle={(_event: React.SyntheticEvent, nodeIds: string[]) => {
// On collapsing we don't collapse a folder if it's not yet selected
const isCollapsing = nodeIds.length < expanded.length
if (isCollapsing && !expanded.includes(props.selected || '')) return
if (isCollapsing && !expanded.every(value => selected.includes(value))) return
setExpanded(nodeIds)
}}
defaultCollapseIcon={<MinusSquare />}
Expand Down
Loading
Loading