api command test
This commit is contained in:
@@ -16,6 +16,27 @@ const RE_WINDOWS_EXTENDED_PATH = /(\/\/\?\/|\\\\\?\\)/
|
||||
const RE_WINDOWS_DRIVE_ROOT = /^:local:[a-zA-Z]:\/$/
|
||||
const RE_WINDOWS_DRIVE_LETTER = /^[a-zA-Z]:$/
|
||||
|
||||
export async function startDryRun<T>(operation: () => Promise<T>): Promise<T> {
|
||||
await rclone('/options/set', {
|
||||
params: {
|
||||
query: {
|
||||
main: { DryRun: true },
|
||||
},
|
||||
},
|
||||
})
|
||||
try {
|
||||
return await operation()
|
||||
} finally {
|
||||
await rclone('/options/set', {
|
||||
params: {
|
||||
query: {
|
||||
main: { DryRun: false },
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function serializeOptions(
|
||||
remotePath: string,
|
||||
options: {
|
||||
|
||||
+73
-2
@@ -12,7 +12,7 @@ import {
|
||||
} from '@heroui/react'
|
||||
import { useMutation } from '@tanstack/react-query'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import { message } from '@tauri-apps/plugin-dialog'
|
||||
import { ask, message } from '@tauri-apps/plugin-dialog'
|
||||
import { platform } from '@tauri-apps/plugin-os'
|
||||
import cronstrue from 'cronstrue'
|
||||
import { AnimatePresence, motion } from 'framer-motion'
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
AlertOctagonIcon,
|
||||
ClockIcon,
|
||||
CopyIcon,
|
||||
EyeIcon,
|
||||
FilterIcon,
|
||||
FoldersIcon,
|
||||
PlayIcon,
|
||||
@@ -31,7 +32,7 @@ import { useSearchParams } from 'react-router-dom'
|
||||
import { getOptionsSubtitle } from '../../lib/flags'
|
||||
import { useFlags } from '../../lib/hooks'
|
||||
import notify from '../../lib/notify'
|
||||
import { startCopy } from '../../lib/rclone/api'
|
||||
import { startCopy, startDryRun } from '../../lib/rclone/api'
|
||||
import { RCLONE_CONFIG_DEFAULTS } from '../../lib/rclone/constants'
|
||||
import { openWindow } from '../../lib/window'
|
||||
import { useHostStore } from '../../store/host'
|
||||
@@ -178,6 +179,47 @@ export default function Copy() {
|
||||
},
|
||||
})
|
||||
|
||||
const dryRunMutation = useMutation({
|
||||
mutationFn: async () => {
|
||||
if (!sources || sources.length === 0 || !dest) {
|
||||
throw new Error('Please select both a source and destination path')
|
||||
}
|
||||
return startDryRun(() =>
|
||||
startCopy({
|
||||
sources,
|
||||
destination: dest,
|
||||
options: {
|
||||
config: configOptions,
|
||||
copy: copyOptions,
|
||||
filter: filterOptions,
|
||||
remotes: remoteOptions,
|
||||
},
|
||||
})
|
||||
)
|
||||
},
|
||||
onSuccess: async () => {
|
||||
const result = await ask(
|
||||
'Dry run started, you can check the results in the Transfers screen',
|
||||
{
|
||||
title: 'Preview (Dry Run)',
|
||||
kind: 'info',
|
||||
okLabel: 'Open Transfers',
|
||||
cancelLabel: 'OK',
|
||||
}
|
||||
)
|
||||
if (result) {
|
||||
await openWindow({ name: 'Transfers', url: '/transfers' })
|
||||
}
|
||||
},
|
||||
onError: async (error) => {
|
||||
console.error('Error starting dry run:', error)
|
||||
await message(error instanceof Error ? error.message : 'Failed to start dry run', {
|
||||
title: 'Dry Run',
|
||||
kind: 'error',
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
startTransition(() => {
|
||||
setConfigOptionsJsonString(JSON.stringify(RCLONE_CONFIG_DEFAULTS.config, null, 2))
|
||||
@@ -535,6 +577,35 @@ export default function Copy() {
|
||||
)}
|
||||
</AnimatePresence>
|
||||
<ButtonGroup variant="flat">
|
||||
<Tooltip
|
||||
content="Preview (Dry Run)"
|
||||
placement="top"
|
||||
size="lg"
|
||||
color="foreground"
|
||||
>
|
||||
<Button
|
||||
size="lg"
|
||||
type="button"
|
||||
color="primary"
|
||||
isIconOnly={true}
|
||||
isLoading={dryRunMutation.isPending}
|
||||
onPress={() => {
|
||||
if (
|
||||
dryRunMutation.isPending ||
|
||||
!!jsonError ||
|
||||
!sources ||
|
||||
sources.length === 0 ||
|
||||
!dest ||
|
||||
sources[0] === dest
|
||||
) {
|
||||
return
|
||||
}
|
||||
setTimeout(() => dryRunMutation.mutate(), 100)
|
||||
}}
|
||||
>
|
||||
<EyeIcon className="size-6" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip content="Schedule task" placement="top" size="lg" color="foreground">
|
||||
<Button
|
||||
size="lg"
|
||||
|
||||
+69
-2
@@ -14,13 +14,14 @@ import {
|
||||
import * as Sentry from '@sentry/browser'
|
||||
import { useMutation, useQuery } from '@tanstack/react-query'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import { message } from '@tauri-apps/plugin-dialog'
|
||||
import { ask, message } from '@tauri-apps/plugin-dialog'
|
||||
import { platform } from '@tauri-apps/plugin-os'
|
||||
import cronstrue from 'cronstrue'
|
||||
import { AnimatePresence, motion } from 'framer-motion'
|
||||
import {
|
||||
AlertOctagonIcon,
|
||||
ClockIcon,
|
||||
EyeIcon,
|
||||
FilterIcon,
|
||||
FoldersIcon,
|
||||
PlayIcon,
|
||||
@@ -32,9 +33,10 @@ import { getOptionsSubtitle } from '../../lib/flags'
|
||||
import { getRemoteName } from '../../lib/format'
|
||||
import { useFlags } from '../../lib/hooks'
|
||||
import notify from '../../lib/notify'
|
||||
import { startDelete } from '../../lib/rclone/api'
|
||||
import { startDelete, startDryRun } from '../../lib/rclone/api'
|
||||
import rclone from '../../lib/rclone/client'
|
||||
import { RCLONE_CONFIG_DEFAULTS, SUPPORTS_PURGE } from '../../lib/rclone/constants'
|
||||
import { openWindow } from '../../lib/window'
|
||||
import { useHostStore } from '../../store/host'
|
||||
import type { FlagValue } from '../../types/rclone'
|
||||
import CommandInfoButton from '../components/CommandInfoButton'
|
||||
@@ -198,6 +200,44 @@ export default function Delete() {
|
||||
},
|
||||
})
|
||||
|
||||
const dryRunMutation = useMutation({
|
||||
mutationFn: async () => {
|
||||
if (!sourceFs) {
|
||||
throw new Error('Please select a source path to delete')
|
||||
}
|
||||
return startDryRun(() =>
|
||||
startDelete({
|
||||
sources: [sourceFs],
|
||||
options: {
|
||||
filter: filterOptions,
|
||||
config: configOptions,
|
||||
},
|
||||
})
|
||||
)
|
||||
},
|
||||
onSuccess: async () => {
|
||||
const result = await ask(
|
||||
'Dry run started, you can check the results in the Transfers screen',
|
||||
{
|
||||
title: 'Preview (Dry Run)',
|
||||
kind: 'info',
|
||||
okLabel: 'Open Transfers',
|
||||
cancelLabel: 'OK',
|
||||
}
|
||||
)
|
||||
if (result) {
|
||||
await openWindow({ name: 'Transfers', url: '/transfers' })
|
||||
}
|
||||
},
|
||||
onError: async (error) => {
|
||||
console.error('Error starting dry run:', error)
|
||||
await message(error instanceof Error ? error.message : 'Failed to start dry run', {
|
||||
title: 'Dry Run',
|
||||
kind: 'error',
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
const buttonText = useMemo(() => {
|
||||
if (startDeleteMutation.isPending) return 'STARTING...'
|
||||
if (!sourceFs || sourceFs.length === 0) return 'Please select a source path'
|
||||
@@ -423,6 +463,33 @@ export default function Delete() {
|
||||
)}
|
||||
</AnimatePresence>
|
||||
<ButtonGroup variant="flat">
|
||||
<Tooltip
|
||||
content="Preview (Dry Run)"
|
||||
placement="top"
|
||||
size="lg"
|
||||
color="foreground"
|
||||
>
|
||||
<Button
|
||||
size="lg"
|
||||
type="button"
|
||||
color="primary"
|
||||
isIconOnly={true}
|
||||
isLoading={dryRunMutation.isPending}
|
||||
onPress={() => {
|
||||
if (
|
||||
dryRunMutation.isPending ||
|
||||
!!jsonError ||
|
||||
!sourceFs ||
|
||||
sourceFs.length === 0
|
||||
) {
|
||||
return
|
||||
}
|
||||
setTimeout(() => dryRunMutation.mutate(), 100)
|
||||
}}
|
||||
>
|
||||
<EyeIcon className="size-6" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip content="Schedule task" placement="top" size="lg" color="foreground">
|
||||
<Button
|
||||
size="lg"
|
||||
|
||||
+73
-2
@@ -12,13 +12,14 @@ import {
|
||||
} from '@heroui/react'
|
||||
import { useMutation } from '@tanstack/react-query'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import { message } from '@tauri-apps/plugin-dialog'
|
||||
import { ask, message } from '@tauri-apps/plugin-dialog'
|
||||
import { platform } from '@tauri-apps/plugin-os'
|
||||
import cronstrue from 'cronstrue'
|
||||
import { AnimatePresence, motion } from 'framer-motion'
|
||||
import {
|
||||
AlertOctagonIcon,
|
||||
ClockIcon,
|
||||
EyeIcon,
|
||||
FilterIcon,
|
||||
FoldersIcon,
|
||||
MoveIcon,
|
||||
@@ -31,7 +32,7 @@ import { useSearchParams } from 'react-router-dom'
|
||||
import { getOptionsSubtitle } from '../../lib/flags'
|
||||
import { useFlags } from '../../lib/hooks'
|
||||
import notify from '../../lib/notify'
|
||||
import { startMove } from '../../lib/rclone/api'
|
||||
import { startDryRun, startMove } from '../../lib/rclone/api'
|
||||
import { RCLONE_CONFIG_DEFAULTS } from '../../lib/rclone/constants'
|
||||
import { openWindow } from '../../lib/window'
|
||||
import { useHostStore } from '../../store/host'
|
||||
@@ -205,6 +206,47 @@ export default function Move() {
|
||||
},
|
||||
})
|
||||
|
||||
const dryRunMutation = useMutation({
|
||||
mutationFn: async () => {
|
||||
if (!sources || sources.length === 0 || !dest) {
|
||||
throw new Error('Please select both a source and destination path')
|
||||
}
|
||||
return startDryRun(() =>
|
||||
startMove({
|
||||
sources,
|
||||
destination: dest,
|
||||
options: {
|
||||
config: configOptions,
|
||||
move: moveOptions,
|
||||
filter: filterOptions,
|
||||
remotes: remoteOptions,
|
||||
},
|
||||
})
|
||||
)
|
||||
},
|
||||
onSuccess: async () => {
|
||||
const result = await ask(
|
||||
'Dry run started, you can check the results in the Transfers screen',
|
||||
{
|
||||
title: 'Preview (Dry Run)',
|
||||
kind: 'info',
|
||||
okLabel: 'Open Transfers',
|
||||
cancelLabel: 'OK',
|
||||
}
|
||||
)
|
||||
if (result) {
|
||||
await openWindow({ name: 'Transfers', url: '/transfers' })
|
||||
}
|
||||
},
|
||||
onError: async (error) => {
|
||||
console.error('Error starting dry run:', error)
|
||||
await message(error instanceof Error ? error.message : 'Failed to start dry run', {
|
||||
title: 'Dry Run',
|
||||
kind: 'error',
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
startTransition(() => {
|
||||
setConfigOptionsJsonString(JSON.stringify(RCLONE_CONFIG_DEFAULTS.config, null, 2))
|
||||
@@ -557,6 +599,35 @@ export default function Move() {
|
||||
)}
|
||||
</AnimatePresence>
|
||||
<ButtonGroup variant="flat">
|
||||
<Tooltip
|
||||
content="Preview (Dry Run)"
|
||||
placement="top"
|
||||
size="lg"
|
||||
color="foreground"
|
||||
>
|
||||
<Button
|
||||
size="lg"
|
||||
type="button"
|
||||
color="primary"
|
||||
isIconOnly={true}
|
||||
isLoading={dryRunMutation.isPending}
|
||||
onPress={() => {
|
||||
if (
|
||||
dryRunMutation.isPending ||
|
||||
!!jsonError ||
|
||||
!sources ||
|
||||
sources.length === 0 ||
|
||||
!dest ||
|
||||
sources[0] === dest
|
||||
) {
|
||||
return
|
||||
}
|
||||
setTimeout(() => dryRunMutation.mutate(), 100)
|
||||
}}
|
||||
>
|
||||
<EyeIcon className="size-6" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip content="Schedule task" placement="top" size="lg" color="foreground">
|
||||
<Button
|
||||
size="lg"
|
||||
|
||||
+72
-2
@@ -13,13 +13,14 @@ import {
|
||||
import * as Sentry from '@sentry/browser'
|
||||
import { useMutation } from '@tanstack/react-query'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import { message } from '@tauri-apps/plugin-dialog'
|
||||
import { ask, message } from '@tauri-apps/plugin-dialog'
|
||||
import { platform } from '@tauri-apps/plugin-os'
|
||||
import cronstrue from 'cronstrue'
|
||||
import { AnimatePresence, motion } from 'framer-motion'
|
||||
import {
|
||||
AlertOctagonIcon,
|
||||
ClockIcon,
|
||||
EyeIcon,
|
||||
FilterIcon,
|
||||
FolderSyncIcon,
|
||||
FoldersIcon,
|
||||
@@ -32,7 +33,7 @@ import { useSearchParams } from 'react-router-dom'
|
||||
import { getOptionsSubtitle } from '../../lib/flags'
|
||||
import { useFlags } from '../../lib/hooks'
|
||||
import notify from '../../lib/notify'
|
||||
import { startSync } from '../../lib/rclone/api'
|
||||
import { startDryRun, startSync } from '../../lib/rclone/api'
|
||||
import { RCLONE_CONFIG_DEFAULTS } from '../../lib/rclone/constants'
|
||||
import { openWindow } from '../../lib/window'
|
||||
import { useHostStore } from '../../store/host'
|
||||
@@ -172,6 +173,47 @@ export default function Sync() {
|
||||
},
|
||||
})
|
||||
|
||||
const dryRunMutation = useMutation({
|
||||
mutationFn: async () => {
|
||||
if (!source || !dest) {
|
||||
throw new Error('Please select both a source and destination path')
|
||||
}
|
||||
return startDryRun(() =>
|
||||
startSync({
|
||||
source,
|
||||
destination: dest,
|
||||
options: {
|
||||
config: configOptions,
|
||||
sync: syncOptions,
|
||||
filter: filterOptions,
|
||||
remotes: remoteOptions,
|
||||
},
|
||||
})
|
||||
)
|
||||
},
|
||||
onSuccess: async () => {
|
||||
const result = await ask(
|
||||
'Dry run started, you can check the results in the Transfers screen',
|
||||
{
|
||||
title: 'Preview (Dry Run)',
|
||||
kind: 'info',
|
||||
okLabel: 'Open Transfers',
|
||||
cancelLabel: 'OK',
|
||||
}
|
||||
)
|
||||
if (result) {
|
||||
await openWindow({ name: 'Transfers', url: '/transfers' })
|
||||
}
|
||||
},
|
||||
onError: async (error) => {
|
||||
console.error('Error starting dry run:', error)
|
||||
await message(error instanceof Error ? error.message : 'Failed to start dry run', {
|
||||
title: 'Dry Run',
|
||||
kind: 'error',
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
const buttonText = useMemo(() => {
|
||||
if (startSyncMutation.isPending) return 'STARTING...'
|
||||
if (!source) return 'Please select a source path'
|
||||
@@ -539,6 +581,34 @@ export default function Sync() {
|
||||
)}
|
||||
</AnimatePresence>
|
||||
<ButtonGroup variant="flat">
|
||||
<Tooltip
|
||||
content="Preview (Dry Run)"
|
||||
placement="top"
|
||||
size="lg"
|
||||
color="foreground"
|
||||
>
|
||||
<Button
|
||||
size="lg"
|
||||
type="button"
|
||||
color="primary"
|
||||
isIconOnly={true}
|
||||
isLoading={dryRunMutation.isPending}
|
||||
onPress={() => {
|
||||
if (
|
||||
dryRunMutation.isPending ||
|
||||
!!jsonError ||
|
||||
!source ||
|
||||
!dest ||
|
||||
source === dest
|
||||
) {
|
||||
return
|
||||
}
|
||||
setTimeout(() => dryRunMutation.mutate(), 100)
|
||||
}}
|
||||
>
|
||||
<EyeIcon className="size-6" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip content="Schedule task" placement="top" size="lg" color="foreground">
|
||||
<Button
|
||||
size="lg"
|
||||
|
||||
Reference in New Issue
Block a user