PathFinder suggestions for local path
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import { Autocomplete } from '@nextui-org/autocomplete'
|
import { Autocomplete } from '@nextui-org/autocomplete'
|
||||||
import { AutocompleteItem, Button } from '@nextui-org/react'
|
import { AutocompleteItem, Button } from '@nextui-org/react'
|
||||||
import { open } from '@tauri-apps/plugin-dialog'
|
import { open } from '@tauri-apps/plugin-dialog'
|
||||||
|
import { readDir } from '@tauri-apps/plugin-fs'
|
||||||
import { ArrowDownUp, FolderOpen } from 'lucide-react'
|
import { ArrowDownUp, FolderOpen } from 'lucide-react'
|
||||||
import { useCallback, useEffect, useState } from 'react'
|
import { useCallback, useEffect, useState } from 'react'
|
||||||
import { isRemotePath } from '../../lib/fs'
|
import { isRemotePath } from '../../lib/fs'
|
||||||
@@ -64,9 +65,7 @@ export default function PathFinder({
|
|||||||
source: [],
|
source: [],
|
||||||
dest: [],
|
dest: [],
|
||||||
})
|
})
|
||||||
const [activeSuggestionField, setActiveSuggestionField] = useState<'source' | 'dest' | null>(
|
|
||||||
null
|
|
||||||
)
|
|
||||||
const [isLoading, setIsLoading] = useState<{ source: boolean; dest: boolean }>({
|
const [isLoading, setIsLoading] = useState<{ source: boolean; dest: boolean }>({
|
||||||
source: false,
|
source: false,
|
||||||
dest: false,
|
dest: false,
|
||||||
@@ -118,7 +117,7 @@ export default function PathFinder({
|
|||||||
setIsLoading((prev) => ({ ...prev, [field]: true }))
|
setIsLoading((prev) => ({ ...prev, [field]: true }))
|
||||||
setError((prev) => ({ ...prev, [field]: null }))
|
setError((prev) => ({ ...prev, [field]: null }))
|
||||||
|
|
||||||
console.log('fetching suggestions for', path, field)
|
// console.log('fetching suggestions for', path, field)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// If path is empty, show list of remotes
|
// If path is empty, show list of remotes
|
||||||
@@ -132,9 +131,37 @@ export default function PathFinder({
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only fetch suggestions for remote paths
|
// Fetch suggestions for local paths
|
||||||
if (!isRemotePath(path)) {
|
if (!isRemotePath(path)) {
|
||||||
setSuggestions((prev) => ({ ...prev, [field]: [] }))
|
let localEntries: Awaited<ReturnType<typeof readDir>> = []
|
||||||
|
let cleanedPath = path
|
||||||
|
|
||||||
|
try {
|
||||||
|
localEntries = await readDir(cleanedPath)
|
||||||
|
} catch (err) {
|
||||||
|
// most likely due to the path being a file
|
||||||
|
console.error('Failed to fetch local suggestions:', err)
|
||||||
|
try {
|
||||||
|
// we also retry in case the last part of the path is wrong
|
||||||
|
cleanedPath = path.split('/').slice(0, -1).join('/')
|
||||||
|
localEntries = await readDir(cleanedPath)
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to fetch local suggestions (again):', err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const extraSlash = cleanedPath.endsWith('/') ? '' : '/'
|
||||||
|
|
||||||
|
const localSuggestions = localEntries
|
||||||
|
.filter((entry) => !entry.isSymlink)
|
||||||
|
.map((entry) => ({
|
||||||
|
IsDir: entry.isDirectory,
|
||||||
|
Name: entry.name,
|
||||||
|
Path: `${cleanedPath}${extraSlash}${entry.name}`,
|
||||||
|
}))
|
||||||
|
|
||||||
|
setSuggestions((prev) => ({ ...prev, [field]: localSuggestions }))
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,15 +216,12 @@ export default function PathFinder({
|
|||||||
)
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (activeSuggestionField) {
|
fetchSuggestions(sourcePath, 'source')
|
||||||
const path = activeSuggestionField === 'source' ? sourcePath : destPath
|
}, [sourcePath, fetchSuggestions])
|
||||||
const timeoutId = setTimeout(() => {
|
|
||||||
fetchSuggestions(path, activeSuggestionField)
|
|
||||||
}, 300)
|
|
||||||
|
|
||||||
return () => clearTimeout(timeoutId)
|
useEffect(() => {
|
||||||
}
|
fetchSuggestions(destPath, 'dest')
|
||||||
}, [sourcePath, destPath, activeSuggestionField, fetchSuggestions])
|
}, [destPath, fetchSuggestions])
|
||||||
|
|
||||||
//! suggestions close because component re-renders
|
//! suggestions close because component re-renders
|
||||||
const renderField = useCallback(
|
const renderField = useCallback(
|
||||||
@@ -229,14 +253,10 @@ export default function PathFinder({
|
|||||||
inputValue={value}
|
inputValue={value}
|
||||||
onInputChange={(e) => setValue(e)}
|
onInputChange={(e) => setValue(e)}
|
||||||
onFocus={() => {
|
onFocus={() => {
|
||||||
setActiveSuggestionField(field)
|
|
||||||
if (!value) {
|
if (!value) {
|
||||||
fetchSuggestions('', field)
|
fetchSuggestions('', field)
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onBlur={() => {
|
|
||||||
setActiveSuggestionField(null)
|
|
||||||
}}
|
|
||||||
shouldCloseOnBlur={false}
|
shouldCloseOnBlur={false}
|
||||||
placeholder={
|
placeholder={
|
||||||
isSource ? sourceOptions.placeholder : destOptions.placeholder
|
isSource ? sourceOptions.placeholder : destOptions.placeholder
|
||||||
|
|||||||
Reference in New Issue
Block a user