can now sync with config files that are updated externally (not imported)
Signed-off-by: FTCHD <144691102+FTCHD@users.noreply.github.com>
This commit is contained in:
+16
-5
@@ -718,7 +718,16 @@ export async function getConfigPath({ id, validate = true }: { id: string; valid
|
||||
const appLocalDataDirPath = await appLocalDataDir()
|
||||
console.log('[getConfigPath] appLocalDataDirPath', appLocalDataDirPath)
|
||||
|
||||
let configPath = `${appLocalDataDirPath}/configs/${id}/rclone.conf`
|
||||
const slashSymbol = platform() === 'windows' ? '\\' : '/'
|
||||
|
||||
let configPath =
|
||||
appLocalDataDirPath +
|
||||
slashSymbol +
|
||||
'configs' +
|
||||
slashSymbol +
|
||||
id +
|
||||
slashSymbol +
|
||||
'rclone.conf'
|
||||
|
||||
if (id == 'default') {
|
||||
const defaultPaths = await getDefaultPaths()
|
||||
@@ -731,10 +740,12 @@ export async function getConfigPath({ id, validate = true }: { id: string; valid
|
||||
configPath = defaultPaths.config
|
||||
}
|
||||
|
||||
const configExists = await exists(configPath)
|
||||
if (validate && !configExists) {
|
||||
console.error('[getConfigPath] config file does not exist')
|
||||
throw new Error('Config file does not exist')
|
||||
if (validate) {
|
||||
const configExists = await exists(configPath)
|
||||
if (!configExists) {
|
||||
console.error('[getConfigPath] config file does not exist')
|
||||
throw new Error('Config file does not exist')
|
||||
}
|
||||
}
|
||||
|
||||
return configPath
|
||||
|
||||
+25
-3
@@ -77,6 +77,7 @@ export async function initRclone(args: string[]) {
|
||||
sync: undefined,
|
||||
isEncrypted: false,
|
||||
pass: undefined,
|
||||
passCommand: undefined,
|
||||
})
|
||||
usePersistedStore.setState({ configFiles })
|
||||
|
||||
@@ -89,6 +90,29 @@ export async function initRclone(args: string[]) {
|
||||
usePersistedStore.setState({ activeConfigFile })
|
||||
}
|
||||
|
||||
let configFolderPath = activeConfigFile.sync
|
||||
? activeConfigFile.sync
|
||||
: (await getConfigPath({ id: activeConfigFile.id!, validate: true })).replace(
|
||||
/\/rclone\.conf$/,
|
||||
''
|
||||
)
|
||||
|
||||
if (activeConfigFile.sync) {
|
||||
const slashSymbol = platform() === 'windows' ? '\\' : '/'
|
||||
if (!(await exists(configFolderPath + slashSymbol + 'rclone.conf'))) {
|
||||
await message('The config file could not be found. Switching to the default config.', {
|
||||
title: 'Invalid synced config',
|
||||
kind: 'error',
|
||||
okLabel: 'OK',
|
||||
})
|
||||
configFolderPath = (await getConfigPath({ id: 'default', validate: true })).replace(
|
||||
/\/rclone\.conf$/,
|
||||
''
|
||||
)
|
||||
usePersistedStore.setState({ activeConfigFile: configFiles[0] })
|
||||
}
|
||||
}
|
||||
|
||||
const extraParams =
|
||||
activeConfigFile.id === 'default'
|
||||
? undefined
|
||||
@@ -97,9 +121,7 @@ export async function initRclone(args: string[]) {
|
||||
...(activeConfigFile.isEncrypted
|
||||
? { RCLONE_CONFIG_PASS: activeConfigFile.pass }
|
||||
: {}),
|
||||
RCLONE_CONFIG_DIR: (
|
||||
await getConfigPath({ id: activeConfigFile.id!, validate: true })
|
||||
).replace(/\/rclone\.conf$/, ''),
|
||||
RCLONE_CONFIG_DIR: configFolderPath,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -8,8 +8,9 @@ import {
|
||||
Textarea,
|
||||
} from '@heroui/react'
|
||||
import { Button } from '@heroui/react'
|
||||
import { ask, open } from '@tauri-apps/plugin-dialog'
|
||||
import { message, open } from '@tauri-apps/plugin-dialog'
|
||||
import { mkdir, readTextFile, writeTextFile } from '@tauri-apps/plugin-fs'
|
||||
import { platform } from '@tauri-apps/plugin-os'
|
||||
import { UploadIcon } from 'lucide-react'
|
||||
import { useCallback, useMemo, useState } from 'react'
|
||||
import { getConfigPath } from '../../lib/rclone/api'
|
||||
@@ -58,7 +59,11 @@ export default function ConfigCreateDrawer({
|
||||
|
||||
const configPath = await getConfigPath({ id: generatedId, validate: false })
|
||||
|
||||
await mkdir(configPath.replace('/rclone.conf', ''), { recursive: true })
|
||||
const slashSymbol = platform() === 'windows' ? '\\' : '/'
|
||||
|
||||
await mkdir(configPath.replace(slashSymbol + 'rclone.conf', ''), {
|
||||
recursive: true,
|
||||
})
|
||||
await writeTextFile(configPath, content)
|
||||
console.log('[handleCreate] saved config to', configPath)
|
||||
|
||||
@@ -68,17 +73,20 @@ export default function ConfigCreateDrawer({
|
||||
pass,
|
||||
isEncrypted: content.includes('RCLONE_ENCRYPT_V0:'),
|
||||
sync: undefined,
|
||||
passCommand: undefined,
|
||||
})
|
||||
|
||||
onClose()
|
||||
} catch (error) {
|
||||
console.error('[handleCreate] failed to save config', error)
|
||||
await ask('Failed to save config', {
|
||||
title: 'Error',
|
||||
kind: 'error',
|
||||
okLabel: 'OK',
|
||||
cancelLabel: '',
|
||||
})
|
||||
await message(
|
||||
error instanceof Error ? error.message : 'An unknown error occurred',
|
||||
{
|
||||
title: 'Failed to save config',
|
||||
kind: 'error',
|
||||
okLabel: 'OK',
|
||||
}
|
||||
)
|
||||
} finally {
|
||||
setIsSaving(false)
|
||||
}
|
||||
@@ -194,12 +202,15 @@ export default function ConfigCreateDrawer({
|
||||
}, '')
|
||||
}
|
||||
|
||||
const slashSymbol =
|
||||
platform() === 'windows' ? '\\' : '/'
|
||||
|
||||
setConfigContent(content)
|
||||
setConfig({
|
||||
...config,
|
||||
label:
|
||||
config.label ||
|
||||
selectedFile.split('/').pop() ||
|
||||
selectedFile.split(slashSymbol).pop() ||
|
||||
'New Config',
|
||||
isEncrypted:
|
||||
content.includes('RCLONE_ENCRYPT_V0:'),
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
Textarea,
|
||||
} from '@heroui/react'
|
||||
import { Button } from '@heroui/react'
|
||||
import { message } from '@tauri-apps/plugin-dialog'
|
||||
import { readTextFile, writeTextFile } from '@tauri-apps/plugin-fs'
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { getConfigPath } from '../../lib/rclone/api'
|
||||
@@ -65,7 +66,15 @@ export default function ConfigEditDrawer({
|
||||
|
||||
onClose()
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
console.error('[handleUpdate] failed to save config', error)
|
||||
await message(
|
||||
error instanceof Error ? error.message : 'An unknown error occurred',
|
||||
{
|
||||
title: 'Failed to save config',
|
||||
kind: 'error',
|
||||
okLabel: 'OK',
|
||||
}
|
||||
)
|
||||
} finally {
|
||||
setIsSaving(false)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,338 @@
|
||||
import {
|
||||
Drawer,
|
||||
DrawerBody,
|
||||
DrawerContent,
|
||||
DrawerFooter,
|
||||
DrawerHeader,
|
||||
Input,
|
||||
Switch,
|
||||
} from '@heroui/react'
|
||||
import { Button } from '@heroui/react'
|
||||
import { message, open } from '@tauri-apps/plugin-dialog'
|
||||
import { exists, readTextFile } from '@tauri-apps/plugin-fs'
|
||||
import { platform } from '@tauri-apps/plugin-os'
|
||||
import { UploadIcon } from 'lucide-react'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { usePersistedStore } from '../../lib/store'
|
||||
import type { ConfigFile } from '../../types/config'
|
||||
|
||||
export default function ConfigSyncDrawer({
|
||||
onClose,
|
||||
isOpen,
|
||||
}: {
|
||||
onClose: () => void
|
||||
isOpen: boolean
|
||||
}) {
|
||||
const [config, setConfig] = useState<Partial<ConfigFile>>({
|
||||
label: 'New Config',
|
||||
})
|
||||
|
||||
const [isPasswordCommand, setIsPasswordCommand] = useState(false)
|
||||
|
||||
const [isSaving, setIsSaving] = useState(false)
|
||||
|
||||
const handleCreate = useCallback(
|
||||
async ({
|
||||
label,
|
||||
pass,
|
||||
isEncrypted,
|
||||
passCommand,
|
||||
sync,
|
||||
}: {
|
||||
label?: string
|
||||
sync?: string
|
||||
isEncrypted?: boolean
|
||||
pass?: string
|
||||
passCommand?: string
|
||||
}) => {
|
||||
try {
|
||||
if (!label) {
|
||||
throw new Error('Label is required')
|
||||
}
|
||||
|
||||
if (!sync) {
|
||||
throw new Error('Path is required')
|
||||
}
|
||||
|
||||
if (isEncrypted) {
|
||||
if (!pass && !passCommand) {
|
||||
throw new Error('Password is required for encrypted configs')
|
||||
}
|
||||
}
|
||||
|
||||
setIsSaving(true)
|
||||
|
||||
const generatedId = crypto.randomUUID()
|
||||
|
||||
usePersistedStore.getState().addConfigFile({
|
||||
id: generatedId,
|
||||
label,
|
||||
isEncrypted: isEncrypted || false,
|
||||
pass,
|
||||
passCommand,
|
||||
sync,
|
||||
})
|
||||
|
||||
onClose()
|
||||
} catch (error) {
|
||||
console.error('[handleCreate] failed to save config', error)
|
||||
await message(
|
||||
error instanceof Error ? error.message : 'An unknown error occurred',
|
||||
{
|
||||
title: 'Failed to save config',
|
||||
kind: 'error',
|
||||
okLabel: 'OK',
|
||||
}
|
||||
)
|
||||
} finally {
|
||||
setIsSaving(false)
|
||||
}
|
||||
},
|
||||
[onClose]
|
||||
)
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
isOpen={isOpen}
|
||||
placement={'bottom'}
|
||||
size="full"
|
||||
onClose={onClose}
|
||||
hideCloseButton={true}
|
||||
>
|
||||
<DrawerContent>
|
||||
{(close) => (
|
||||
<>
|
||||
<DrawerHeader className="flex flex-col gap-1">Sync Config</DrawerHeader>
|
||||
<DrawerBody>
|
||||
<form
|
||||
id="config-form"
|
||||
className="flex flex-col gap-5"
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault()
|
||||
handleCreate({
|
||||
label: config.label,
|
||||
pass: isPasswordCommand ? undefined : config.pass,
|
||||
isEncrypted: config.isEncrypted,
|
||||
passCommand: isPasswordCommand
|
||||
? config.passCommand
|
||||
: undefined,
|
||||
sync: config.sync,
|
||||
})
|
||||
}}
|
||||
>
|
||||
<Switch
|
||||
size="lg"
|
||||
isSelected={config.isEncrypted}
|
||||
onValueChange={() =>
|
||||
setConfig({ ...config, isEncrypted: !config.isEncrypted })
|
||||
}
|
||||
color="primary"
|
||||
>
|
||||
Encrypted
|
||||
</Switch>
|
||||
|
||||
<Input
|
||||
label="Name"
|
||||
labelPlacement="outside"
|
||||
placeholder="Enter a name for your config"
|
||||
type="text"
|
||||
value={config.label}
|
||||
autoCapitalize="off"
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
spellCheck="false"
|
||||
onValueChange={(value) => {
|
||||
setConfig({ ...config, label: value })
|
||||
}}
|
||||
isClearable={true}
|
||||
onClear={() => {
|
||||
setConfig({ ...config, label: '' })
|
||||
}}
|
||||
size="lg"
|
||||
/>
|
||||
|
||||
{config.isEncrypted && (
|
||||
<Input
|
||||
// label={
|
||||
// <div className="flex items-center gap-1.5">
|
||||
// <p className="text-medium">Password</p>
|
||||
// <Switch
|
||||
// size="sm"
|
||||
// isSelected={isPasswordCommand}
|
||||
// onValueChange={() =>
|
||||
// setIsPasswordCommand(!isPasswordCommand)
|
||||
// }
|
||||
// color="primary"
|
||||
// >
|
||||
// Command
|
||||
// </Switch>
|
||||
// </div>
|
||||
// }
|
||||
label="Password"
|
||||
labelPlacement="outside"
|
||||
placeholder={
|
||||
isPasswordCommand
|
||||
? 'Enter the password command for your config file'
|
||||
: 'Enter the password for your config file'
|
||||
}
|
||||
type={isPasswordCommand ? 'text' : 'password'}
|
||||
value={isPasswordCommand ? config.passCommand : config.pass}
|
||||
autoCapitalize="off"
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
spellCheck="false"
|
||||
onValueChange={(value) => {
|
||||
setConfig({
|
||||
...config,
|
||||
...(isPasswordCommand
|
||||
? { passCommand: value }
|
||||
: { pass: value }),
|
||||
})
|
||||
}}
|
||||
isClearable={true}
|
||||
onClear={() => {
|
||||
setConfig({
|
||||
...config,
|
||||
...(isPasswordCommand
|
||||
? { passCommand: '' }
|
||||
: { pass: '' }),
|
||||
})
|
||||
}}
|
||||
size="lg"
|
||||
/>
|
||||
)}
|
||||
|
||||
<Input
|
||||
label={
|
||||
<div className="flex items-center gap-1.5">
|
||||
<p className="text-medium">Config</p>
|
||||
<Button
|
||||
isIconOnly={true}
|
||||
variant="light"
|
||||
size="sm"
|
||||
onPress={async () => {
|
||||
try {
|
||||
let selectedFolder = await open({
|
||||
directory: true,
|
||||
multiple: false,
|
||||
title: 'Select the root directory of your config file',
|
||||
})
|
||||
|
||||
if (!selectedFolder) {
|
||||
return
|
||||
}
|
||||
|
||||
if (
|
||||
selectedFolder.endsWith('/') ||
|
||||
selectedFolder.endsWith('\\')
|
||||
) {
|
||||
selectedFolder = selectedFolder.slice(
|
||||
0,
|
||||
-1
|
||||
)
|
||||
}
|
||||
|
||||
if (!(await exists(selectedFolder))) {
|
||||
throw new Error(
|
||||
'The selected path does not exist'
|
||||
)
|
||||
}
|
||||
|
||||
const slashSymbol =
|
||||
platform() === 'windows' ? '\\' : '/'
|
||||
|
||||
const configPath =
|
||||
selectedFolder +
|
||||
slashSymbol +
|
||||
'rclone.conf'
|
||||
|
||||
let content: string | null = null
|
||||
|
||||
try {
|
||||
content = await readTextFile(configPath)
|
||||
} catch {
|
||||
throw new Error(
|
||||
'Could not find an rclone.conf file in the selected folder'
|
||||
)
|
||||
}
|
||||
|
||||
if (!content) {
|
||||
throw new Error(
|
||||
'Empty rclone.conf file'
|
||||
)
|
||||
}
|
||||
|
||||
setConfig({
|
||||
...config,
|
||||
label:
|
||||
config.label ||
|
||||
configPath
|
||||
.split(slashSymbol)
|
||||
.pop() ||
|
||||
'New Config',
|
||||
sync: selectedFolder,
|
||||
})
|
||||
} catch (error) {
|
||||
await message(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: 'An unknown error occurred',
|
||||
{
|
||||
title: 'Failed to sync config',
|
||||
kind: 'error',
|
||||
okLabel: 'OK',
|
||||
}
|
||||
)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<UploadIcon className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
labelPlacement="outside"
|
||||
placeholder="Select the folder containing your rclone.conf file"
|
||||
value={config.sync || ''}
|
||||
onValueChange={(value) => {
|
||||
console.log(value)
|
||||
setConfig({ ...config, sync: value })
|
||||
}}
|
||||
autoCapitalize="off"
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
spellCheck="false"
|
||||
type="textarea"
|
||||
size="lg"
|
||||
isClearable={true}
|
||||
onClear={() => {
|
||||
setConfig({ ...config, sync: '' })
|
||||
}}
|
||||
data-focus-visible="false"
|
||||
/>
|
||||
</form>
|
||||
</DrawerBody>
|
||||
<DrawerFooter>
|
||||
<Button
|
||||
color="danger"
|
||||
variant="light"
|
||||
onPress={close}
|
||||
data-focus-visible="false"
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
<Button
|
||||
color="primary"
|
||||
type="submit"
|
||||
form="config-form"
|
||||
isDisabled={isSaving}
|
||||
data-focus-visible="false"
|
||||
>
|
||||
{isSaving ? 'Saving...' : 'Sync'}
|
||||
</Button>
|
||||
</DrawerFooter>
|
||||
</>
|
||||
)}
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
)
|
||||
}
|
||||
+69
-47
@@ -1,4 +1,17 @@
|
||||
import { Button, Card, CardBody, Checkbox, Chip, Input, Tab, Tabs } from '@heroui/react'
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
CardBody,
|
||||
Checkbox,
|
||||
Chip,
|
||||
Dropdown,
|
||||
DropdownItem,
|
||||
DropdownMenu,
|
||||
DropdownTrigger,
|
||||
Input,
|
||||
Tab,
|
||||
Tabs,
|
||||
} from '@heroui/react'
|
||||
import { getVersion as getUiVersion } from '@tauri-apps/api/app'
|
||||
import { ask, message } from '@tauri-apps/plugin-dialog'
|
||||
import { remove } from '@tauri-apps/plugin-fs'
|
||||
@@ -10,6 +23,7 @@ import {
|
||||
CodeIcon,
|
||||
CogIcon,
|
||||
EyeIcon,
|
||||
ImportIcon,
|
||||
MedalIcon,
|
||||
PencilIcon,
|
||||
PlusIcon,
|
||||
@@ -23,6 +37,7 @@ import { usePersistedStore, useStore } from '../../lib/store'
|
||||
import { triggerTrayRebuild } from '../../lib/tray'
|
||||
import ConfigCreateDrawer from '../components/ConfigCreateDrawer'
|
||||
import ConfigEditDrawer from '../components/ConfigEditDrawer'
|
||||
import ConfigSyncDrawer from '../components/ConfigSyncDrawer'
|
||||
import RemoteCreateDrawer from '../components/RemoteCreateDrawer'
|
||||
import RemoteDefaultsDrawer from '../components/RemoteDefaultsDrawer'
|
||||
import RemoteEditDrawer from '../components/RemoteEditDrawer'
|
||||
@@ -870,6 +885,7 @@ function ConfigSection() {
|
||||
const activeConfigFile = usePersistedStore((state) => state.activeConfigFile)
|
||||
|
||||
const [isCreateDrawerOpen, setIsCreateDrawerOpen] = useState(false)
|
||||
const [isSyncDrawerOpen, setIsSyncDrawerOpen] = useState(false)
|
||||
const [isEditDrawerOpen, setIsEditDrawerOpen] = useState(false)
|
||||
const [focusedConfigId, setFocusedConfigId] = useState<string | null>(null)
|
||||
|
||||
@@ -878,44 +894,40 @@ function ConfigSection() {
|
||||
<BaseHeader
|
||||
title="Config"
|
||||
endContent={
|
||||
<Button
|
||||
onPress={async () => {
|
||||
// setFocusedConfig({
|
||||
// id: undefined,
|
||||
// label: filePath.split('/').pop() || 'New Config',
|
||||
// isEncrypted: configText.includes('RCLONE_ENCRYPT_V0:'),
|
||||
// pass: undefined,
|
||||
// sync: undefined,
|
||||
// })
|
||||
setIsCreateDrawerOpen(true)
|
||||
|
||||
// await ask(
|
||||
// 'Config loaded successfully, you can now remove the file.',
|
||||
// {
|
||||
// title: 'Success',
|
||||
// kind: 'info',
|
||||
// okLabel: 'OK',
|
||||
// cancelLabel: '',
|
||||
// }
|
||||
// )
|
||||
// } catch (error) {
|
||||
// console.error(error)
|
||||
// await ask('Failed to load config file', {
|
||||
// title: 'Error',
|
||||
// kind: 'error',
|
||||
// okLabel: 'OK',
|
||||
// cancelLabel: '',
|
||||
// })
|
||||
// }
|
||||
}}
|
||||
isIconOnly={true}
|
||||
variant="faded"
|
||||
color="primary"
|
||||
data-focus-visible="false"
|
||||
size="sm"
|
||||
>
|
||||
<PlusIcon className="w-4 h-4" />
|
||||
</Button>
|
||||
<Dropdown>
|
||||
<DropdownTrigger>
|
||||
<Button variant="faded" color="primary" data-focus-visible="false">
|
||||
Add Config
|
||||
</Button>
|
||||
</DropdownTrigger>
|
||||
<DropdownMenu
|
||||
onAction={(key) => {
|
||||
setTimeout(() => {
|
||||
if (key === 'import') {
|
||||
setIsCreateDrawerOpen(true)
|
||||
} else {
|
||||
setIsSyncDrawerOpen(true)
|
||||
}
|
||||
}, 100)
|
||||
}}
|
||||
variant="faded"
|
||||
>
|
||||
<DropdownItem
|
||||
key="import"
|
||||
description="Edit using the CLI or UI"
|
||||
startContent={<PlusIcon />}
|
||||
>
|
||||
Import Config
|
||||
</DropdownItem>
|
||||
<DropdownItem
|
||||
key="sync"
|
||||
description="Update using Git or similar"
|
||||
startContent={<ImportIcon />}
|
||||
>
|
||||
Sync Config
|
||||
</DropdownItem>
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
}
|
||||
/>
|
||||
<div className="flex flex-col gap-2 p-4">
|
||||
@@ -964,7 +976,8 @@ function ConfigSection() {
|
||||
}}
|
||||
isDisabled={
|
||||
configFile.id === 'default' ||
|
||||
configFile.id === activeConfigFile?.id
|
||||
configFile.id === activeConfigFile?.id ||
|
||||
Boolean(configFile.sync)
|
||||
}
|
||||
>
|
||||
<PencilIcon className="w-4 h-4" />
|
||||
@@ -999,14 +1012,16 @@ function ConfigSection() {
|
||||
return
|
||||
}
|
||||
|
||||
const path = await getConfigPath({
|
||||
id: configFile.id!,
|
||||
validate: true,
|
||||
})
|
||||
if (!configFile.sync) {
|
||||
const path = await getConfigPath({
|
||||
id: configFile.id!,
|
||||
validate: true,
|
||||
})
|
||||
|
||||
await remove(path.replace('rclone.conf', ''), {
|
||||
recursive: true,
|
||||
})
|
||||
await remove(path.replace('rclone.conf', ''), {
|
||||
recursive: true,
|
||||
})
|
||||
}
|
||||
|
||||
if (activeConfigFile?.id === configFile.id) {
|
||||
usePersistedStore
|
||||
@@ -1043,6 +1058,13 @@ function ConfigSection() {
|
||||
}}
|
||||
id={focusedConfigId}
|
||||
/>
|
||||
|
||||
<ConfigSyncDrawer
|
||||
isOpen={isSyncDrawerOpen}
|
||||
onClose={() => {
|
||||
setIsSyncDrawerOpen(false)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Vendored
+1
@@ -4,4 +4,5 @@ export interface ConfigFile {
|
||||
sync: string | undefined
|
||||
isEncrypted: boolean
|
||||
pass: string | undefined
|
||||
passCommand: string | undefined
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user