RemoteOptions handling for Sync
Signed-off-by: FTCHD <144691102+FTCHD@users.noreply.github.com>
This commit is contained in:
@@ -605,11 +605,13 @@ export async function startSync({
|
||||
dstFs,
|
||||
_config,
|
||||
_filter,
|
||||
remoteOptions,
|
||||
}: {
|
||||
srcFs: string
|
||||
dstFs: string
|
||||
_config?: Record<string, string | number | boolean | string[]>
|
||||
_filter?: Record<string, string | number | boolean | string[]>
|
||||
remoteOptions?: Record<string, string | number | boolean | string[]>
|
||||
}) {
|
||||
console.log('[startSync]', srcFs, dstFs)
|
||||
|
||||
@@ -627,6 +629,12 @@ export async function startSync({
|
||||
params.set('_filter', JSON.stringify(parseRcloneOptions(_filter)))
|
||||
}
|
||||
|
||||
if (remoteOptions && Object.keys(remoteOptions).length > 0) {
|
||||
for (const [key, value] of Object.entries(remoteOptions)) {
|
||||
params.set(key, value.toString())
|
||||
}
|
||||
}
|
||||
|
||||
const r = await fetch(`http://localhost:5572/sync/sync?${params.toString()}`, {
|
||||
method: 'POST',
|
||||
headers: getAuthHeader(),
|
||||
|
||||
+165
-79
@@ -1,17 +1,20 @@
|
||||
import { Accordion, AccordionItem, Avatar, Button } from '@heroui/react'
|
||||
import { useAutoAnimate } from '@formkit/auto-animate/react'
|
||||
import { Accordion, AccordionItem, Avatar, Button, Divider, cn } from '@heroui/react'
|
||||
import { message } from '@tauri-apps/plugin-dialog'
|
||||
import { exists } from '@tauri-apps/plugin-fs'
|
||||
import cronstrue from 'cronstrue'
|
||||
import {
|
||||
AlertOctagonIcon,
|
||||
ChevronDownIcon,
|
||||
ClockIcon,
|
||||
CopyIcon,
|
||||
FilterIcon,
|
||||
FolderSyncIcon,
|
||||
FoldersIcon,
|
||||
PlayIcon,
|
||||
WrenchIcon,
|
||||
} from 'lucide-react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { startTransition, useEffect, useState } from 'react'
|
||||
import { useSearchParams } from 'react-router-dom'
|
||||
import { getRemoteName } from '../../lib/format'
|
||||
import { isRemotePath } from '../../lib/fs'
|
||||
@@ -28,6 +31,7 @@ import { openWindow } from '../../lib/window'
|
||||
import CronEditor from '../components/CronEditor'
|
||||
import OptionsSection from '../components/OptionsSection'
|
||||
import { PathFinder } from '../components/PathFinder'
|
||||
import RemoteOptionsSection from '../components/RemoteOptionsSection'
|
||||
|
||||
export default function Sync() {
|
||||
const [searchParams] = useSearchParams()
|
||||
@@ -39,7 +43,7 @@ export default function Sync() {
|
||||
|
||||
const [isStarted, setIsStarted] = useState(false)
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [jsonError, setJsonError] = useState<'sync' | 'filter' | 'config' | null>(null)
|
||||
const [jsonError, setJsonError] = useState<'sync' | 'filter' | 'config' | 'remote' | null>(null)
|
||||
|
||||
const [syncOptionsLocked, setSyncOptionsLocked] = useState(false)
|
||||
const [syncOptions, setSyncOptions] = useState<Record<string, string>>({})
|
||||
@@ -53,10 +57,21 @@ export default function Sync() {
|
||||
const [configOptions, setConfigOptions] = useState<Record<string, string>>({})
|
||||
const [configOptionsJson, setConfigOptionsJson] = useState<string>('{}')
|
||||
|
||||
const [remoteOptionsLocked, setRemoteOptionsLocked] = useState(false)
|
||||
const [remoteOptions, setRemoteOptions] = useState<Record<string, string>>({})
|
||||
const [remoteOptionsJson, setRemoteOptionsJson] = useState<string>('{}')
|
||||
|
||||
const [cronExpression, setCronExpression] = useState<string | null>(null)
|
||||
|
||||
const [currentGlobalOptions, setCurrentGlobalOptions] = useState<any[]>([])
|
||||
|
||||
const [showMore, setShowMore] = useState(false)
|
||||
const [animationParent] = useAutoAnimate()
|
||||
|
||||
const selectedRemotes = (() => {
|
||||
return [source, dest].filter(Boolean) as string[]
|
||||
})()
|
||||
|
||||
const buttonText = (() => {
|
||||
if (isLoading) return 'STARTING...'
|
||||
if (!source) return 'Please select a source path'
|
||||
@@ -154,6 +169,7 @@ export default function Sync() {
|
||||
dstFs: dest,
|
||||
_config: mergedConfig,
|
||||
_filter: filterOptions,
|
||||
remoteOptions,
|
||||
})
|
||||
|
||||
// dummy delay to avoid waiting when opening the Jobs page
|
||||
@@ -181,7 +197,9 @@ export default function Sync() {
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
getCurrentGlobalFlags().then((flags) => setCurrentGlobalOptions(flags))
|
||||
getCurrentGlobalFlags().then((flags) =>
|
||||
startTransition(() => setCurrentGlobalOptions(flags))
|
||||
)
|
||||
}, [])
|
||||
|
||||
// biome-ignore lint/correctness/useExhaustiveDependencies: when unlocking, we don't want to re-run the effect
|
||||
@@ -241,7 +259,7 @@ export default function Sync() {
|
||||
}, [source, dest])
|
||||
|
||||
useEffect(() => {
|
||||
let step: 'sync' | 'filter' | 'config' = 'sync'
|
||||
let step: 'sync' | 'filter' | 'config' | 'remote' = 'sync'
|
||||
try {
|
||||
setSyncOptions(JSON.parse(syncOptionsJson))
|
||||
|
||||
@@ -251,12 +269,15 @@ export default function Sync() {
|
||||
step = 'config'
|
||||
setConfigOptions(JSON.parse(configOptionsJson))
|
||||
|
||||
step = 'remote'
|
||||
setRemoteOptions(JSON.parse(remoteOptionsJson))
|
||||
|
||||
setJsonError(null)
|
||||
} catch (error) {
|
||||
setJsonError(step)
|
||||
console.error(`Error parsing ${step} options:`, error)
|
||||
}
|
||||
}, [syncOptionsJson, filterOptionsJson, configOptionsJson])
|
||||
}, [syncOptionsJson, filterOptionsJson, configOptionsJson, remoteOptionsJson])
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-screen gap-10 pt-10">
|
||||
@@ -289,82 +310,147 @@ export default function Sync() {
|
||||
}}
|
||||
/>
|
||||
|
||||
<Accordion>
|
||||
<AccordionItem
|
||||
key="sync"
|
||||
startContent={
|
||||
<Avatar color="success" radius="lg" fallback={<FolderSyncIcon />} />
|
||||
}
|
||||
indicator={<FolderSyncIcon />}
|
||||
subtitle="Tap to toggle sync options for this operation"
|
||||
title="Sync"
|
||||
>
|
||||
<OptionsSection
|
||||
optionsJson={syncOptionsJson}
|
||||
setOptionsJson={setSyncOptionsJson}
|
||||
globalOptions={
|
||||
currentGlobalOptions['main' as keyof typeof currentGlobalOptions]
|
||||
<div
|
||||
className={cn('flex flex-col pb-10', showMore && 'pb-0')}
|
||||
ref={animationParent}
|
||||
>
|
||||
<Accordion>
|
||||
<AccordionItem
|
||||
key="sync"
|
||||
startContent={
|
||||
<Avatar color="success" radius="lg" fallback={<FolderSyncIcon />} />
|
||||
}
|
||||
getAvailableOptions={getSyncFlags}
|
||||
rows={20}
|
||||
isLocked={syncOptionsLocked}
|
||||
setIsLocked={setSyncOptionsLocked}
|
||||
/>
|
||||
</AccordionItem>
|
||||
<AccordionItem
|
||||
key="filters"
|
||||
startContent={
|
||||
<Avatar color="danger" radius="lg" fallback={<FilterIcon />} />
|
||||
}
|
||||
indicator={<FilterIcon />}
|
||||
subtitle="Tap to toggle filtering options for this operation"
|
||||
title="Filters"
|
||||
>
|
||||
<OptionsSection
|
||||
globalOptions={
|
||||
currentGlobalOptions['filter' as keyof typeof currentGlobalOptions]
|
||||
indicator={<FolderSyncIcon />}
|
||||
subtitle="Tap to toggle sync options for this operation"
|
||||
title="Sync"
|
||||
>
|
||||
<OptionsSection
|
||||
optionsJson={syncOptionsJson}
|
||||
setOptionsJson={setSyncOptionsJson}
|
||||
globalOptions={
|
||||
currentGlobalOptions[
|
||||
'main' as keyof typeof currentGlobalOptions
|
||||
]
|
||||
}
|
||||
getAvailableOptions={getSyncFlags}
|
||||
isLocked={syncOptionsLocked}
|
||||
setIsLocked={setSyncOptionsLocked}
|
||||
/>
|
||||
</AccordionItem>
|
||||
<AccordionItem
|
||||
key="filters"
|
||||
startContent={
|
||||
<Avatar color="danger" radius="lg" fallback={<FilterIcon />} />
|
||||
}
|
||||
optionsJson={filterOptionsJson}
|
||||
setOptionsJson={setFilterOptionsJson}
|
||||
getAvailableOptions={getFilterFlags}
|
||||
rows={4}
|
||||
isLocked={filterOptionsLocked}
|
||||
setIsLocked={setFilterOptionsLocked}
|
||||
/>
|
||||
</AccordionItem>
|
||||
<AccordionItem
|
||||
key="config"
|
||||
startContent={
|
||||
<Avatar color="default" radius="lg" fallback={<WrenchIcon />} />
|
||||
}
|
||||
indicator={<WrenchIcon />}
|
||||
subtitle="Tap to toggle config options for this operation"
|
||||
title="Config"
|
||||
>
|
||||
<OptionsSection
|
||||
globalOptions={
|
||||
currentGlobalOptions['main' as keyof typeof currentGlobalOptions]
|
||||
indicator={<FilterIcon />}
|
||||
subtitle="Tap to toggle filtering options for this operation"
|
||||
title="Filters"
|
||||
>
|
||||
<OptionsSection
|
||||
globalOptions={
|
||||
currentGlobalOptions[
|
||||
'filter' as keyof typeof currentGlobalOptions
|
||||
]
|
||||
}
|
||||
optionsJson={filterOptionsJson}
|
||||
setOptionsJson={setFilterOptionsJson}
|
||||
getAvailableOptions={getFilterFlags}
|
||||
isLocked={filterOptionsLocked}
|
||||
setIsLocked={setFilterOptionsLocked}
|
||||
/>
|
||||
</AccordionItem>
|
||||
<AccordionItem
|
||||
key="cron"
|
||||
startContent={
|
||||
<Avatar color="warning" radius="lg" fallback={<ClockIcon />} />
|
||||
}
|
||||
optionsJson={configOptionsJson}
|
||||
setOptionsJson={setConfigOptionsJson}
|
||||
getAvailableOptions={getConfigFlags}
|
||||
rows={10}
|
||||
isLocked={configOptionsLocked}
|
||||
setIsLocked={setConfigOptionsLocked}
|
||||
/>
|
||||
</AccordionItem>
|
||||
<AccordionItem
|
||||
key="cron"
|
||||
startContent={
|
||||
<Avatar color="warning" radius="lg" fallback={<ClockIcon />} />
|
||||
}
|
||||
indicator={<ClockIcon />}
|
||||
subtitle="Tap to toggle cron options for this operation"
|
||||
title="Cron"
|
||||
>
|
||||
<CronEditor expression={cronExpression} onChange={setCronExpression} />
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
indicator={<ClockIcon />}
|
||||
subtitle="Tap to toggle cron options for this operation"
|
||||
title="Cron"
|
||||
>
|
||||
<CronEditor expression={cronExpression} onChange={setCronExpression} />
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
|
||||
{showMore ? (
|
||||
<Divider />
|
||||
) : (
|
||||
<div
|
||||
key="show-more-options"
|
||||
className="absolute flex flex-col items-center justify-center w-full gap-1 -bottom-8 group "
|
||||
onClick={() => {
|
||||
startTransition(() => {
|
||||
setShowMore(true)
|
||||
})
|
||||
requestAnimationFrame(() => {
|
||||
setTimeout(() => {
|
||||
scrollTo({
|
||||
top: document.body.scrollHeight,
|
||||
behavior: 'smooth',
|
||||
})
|
||||
}, 400)
|
||||
})
|
||||
}}
|
||||
>
|
||||
<p className="text-small animate-show-more-title group-hover:text-foreground-500 text-foreground-400">
|
||||
Show more options
|
||||
</p>
|
||||
<ChevronDownIcon className="size-5 stroke-foreground-400 animate-show-more group-hover:stroke-foreground-500" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{showMore && (
|
||||
// @ts-expect-error
|
||||
<Accordion>
|
||||
<AccordionItem
|
||||
key="config"
|
||||
startContent={
|
||||
<Avatar color="default" radius="lg" fallback={<WrenchIcon />} />
|
||||
}
|
||||
indicator={<WrenchIcon />}
|
||||
subtitle="Tap to toggle config options for this operation"
|
||||
title="Config"
|
||||
>
|
||||
<OptionsSection
|
||||
globalOptions={
|
||||
currentGlobalOptions[
|
||||
'main' as keyof typeof currentGlobalOptions
|
||||
]
|
||||
}
|
||||
optionsJson={configOptionsJson}
|
||||
setOptionsJson={setConfigOptionsJson}
|
||||
getAvailableOptions={getConfigFlags}
|
||||
isLocked={configOptionsLocked}
|
||||
setIsLocked={setConfigOptionsLocked}
|
||||
/>
|
||||
</AccordionItem>
|
||||
|
||||
{selectedRemotes.length > 0 && (
|
||||
<AccordionItem
|
||||
key={'remotes'}
|
||||
startContent={
|
||||
<Avatar
|
||||
className="bg-fuchsia-500"
|
||||
radius="lg"
|
||||
fallback={<CopyIcon />}
|
||||
/>
|
||||
}
|
||||
indicator={<CopyIcon />}
|
||||
subtitle="Tap to toggle remote options for this operation"
|
||||
title={'Remotes'}
|
||||
>
|
||||
<RemoteOptionsSection
|
||||
selectedRemotes={selectedRemotes}
|
||||
remoteOptionsJson={remoteOptionsJson}
|
||||
setRemoteOptionsJson={setRemoteOptionsJson}
|
||||
setRemoteOptionsLocked={setRemoteOptionsLocked}
|
||||
remoteOptionsLocked={remoteOptionsLocked}
|
||||
/>
|
||||
</AccordionItem>
|
||||
)}
|
||||
</Accordion>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="sticky bottom-0 z-50 flex items-center justify-center flex-none gap-2 p-4 border-t border-neutral-500/20 bg-neutral-900/50 backdrop-blur-lg">
|
||||
|
||||
Reference in New Issue
Block a user