chore: pr feedbacks
This commit is contained in:
parent
bb102b0619
commit
221fd18820
6 changed files with 42 additions and 59 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
import { useCallback } from "react";
|
import { useCallback, useMemo } from "react";
|
||||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
import { listSnapshotFilesOptions } from "~/client/api-client/@tanstack/react-query.gen";
|
import { listSnapshotFilesOptions } from "~/client/api-client/@tanstack/react-query.gen";
|
||||||
import { FileBrowser, type FileBrowserUiProps } from "~/client/components/file-browsers/file-browser";
|
import { FileBrowser, type FileBrowserUiProps } from "~/client/components/file-browsers/file-browser";
|
||||||
|
|
@ -28,6 +28,7 @@ export const SnapshotTreeBrowser = ({
|
||||||
enabled = true,
|
enabled = true,
|
||||||
...uiProps
|
...uiProps
|
||||||
}: SnapshotTreeBrowserProps) => {
|
}: SnapshotTreeBrowserProps) => {
|
||||||
|
const { selectedPaths, onSelectionChange, ...fileBrowserUiProps } = uiProps;
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const normalizedBasePath = normalizeAbsolutePath(basePath);
|
const normalizedBasePath = normalizeAbsolutePath(basePath);
|
||||||
|
|
||||||
|
|
@ -60,6 +61,31 @@ export const SnapshotTreeBrowser = ({
|
||||||
[normalizedBasePath],
|
[normalizedBasePath],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const displaySelectedPaths = useMemo(() => {
|
||||||
|
if (!selectedPaths) return undefined;
|
||||||
|
|
||||||
|
const displayPaths = new Set<string>();
|
||||||
|
for (const fullPath of selectedPaths) {
|
||||||
|
displayPaths.add(stripBasePath(fullPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
return displayPaths;
|
||||||
|
}, [selectedPaths, stripBasePath]);
|
||||||
|
|
||||||
|
const handleSelectionChange = useCallback(
|
||||||
|
(nextDisplayPaths: Set<string>) => {
|
||||||
|
if (!onSelectionChange) return;
|
||||||
|
|
||||||
|
const nextFullPaths = new Set<string>();
|
||||||
|
for (const displayPath of nextDisplayPaths) {
|
||||||
|
nextFullPaths.add(addBasePath(displayPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelectionChange(nextFullPaths);
|
||||||
|
},
|
||||||
|
[onSelectionChange, addBasePath],
|
||||||
|
);
|
||||||
|
|
||||||
const fileBrowser = useFileBrowser({
|
const fileBrowser = useFileBrowser({
|
||||||
initialData: data,
|
initialData: data,
|
||||||
isLoading,
|
isLoading,
|
||||||
|
|
@ -102,7 +128,7 @@ export const SnapshotTreeBrowser = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FileBrowser
|
<FileBrowser
|
||||||
{...uiProps}
|
{...fileBrowserUiProps}
|
||||||
fileArray={fileBrowser.fileArray}
|
fileArray={fileBrowser.fileArray}
|
||||||
expandedFolders={fileBrowser.expandedFolders}
|
expandedFolders={fileBrowser.expandedFolders}
|
||||||
loadingFolders={fileBrowser.loadingFolders}
|
loadingFolders={fileBrowser.loadingFolders}
|
||||||
|
|
@ -113,7 +139,9 @@ export const SnapshotTreeBrowser = ({
|
||||||
isLoading={fileBrowser.isLoading}
|
isLoading={fileBrowser.isLoading}
|
||||||
isEmpty={fileBrowser.isEmpty}
|
isEmpty={fileBrowser.isEmpty}
|
||||||
errorMessage={errorMessage}
|
errorMessage={errorMessage}
|
||||||
loadingMessage={uiProps.loadingMessage ?? "Loading files..."}
|
loadingMessage={fileBrowserUiProps.loadingMessage ?? "Loading files..."}
|
||||||
|
selectedPaths={displaySelectedPaths}
|
||||||
|
onSelectionChange={onSelectionChange ? handleSelectionChange : undefined}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { LocalFileBrowser } from "./file-browsers/local-file-browser";
|
import { DirectoryBrowser } from "./file-browsers/directory-browser";
|
||||||
import { Button } from "./ui/button";
|
import { Button } from "./ui/button";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
|
@ -14,21 +14,12 @@ export const PathSelector = ({ value, onChange }: Props) => {
|
||||||
if (showBrowser) {
|
if (showBrowser) {
|
||||||
return (
|
return (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<LocalFileBrowser
|
<DirectoryBrowser
|
||||||
className="border rounded-lg overflow-hidden"
|
selectedPath={value}
|
||||||
useScrollArea
|
onSelectPath={(path) => {
|
||||||
scrollAreaClassName="h-64"
|
|
||||||
foldersOnly
|
|
||||||
selectableFolders
|
|
||||||
selectedFolder={value}
|
|
||||||
onFolderSelect={(path) => {
|
|
||||||
onChange(path);
|
onChange(path);
|
||||||
setShowBrowser(false);
|
setShowBrowser(false);
|
||||||
}}
|
}}
|
||||||
showSelectedPathFooter
|
|
||||||
selectedPath={value}
|
|
||||||
loadingMessage="Loading directories..."
|
|
||||||
emptyMessage="No subdirectories found"
|
|
||||||
/>
|
/>
|
||||||
<Button type="button" variant="ghost" size="sm" onClick={() => setShowBrowser(false)}>
|
<Button type="button" variant="ghost" size="sm" onClick={() => setShowBrowser(false)}>
|
||||||
Cancel
|
Cancel
|
||||||
|
|
|
||||||
|
|
@ -88,17 +88,6 @@ export function RestoreForm({ snapshot, repository, snapshotId, returnPath }: Re
|
||||||
};
|
};
|
||||||
}, [addEventListener, repository.id, snapshotId]);
|
}, [addEventListener, repository.id, snapshotId]);
|
||||||
|
|
||||||
const addBasePath = useCallback(
|
|
||||||
(displayPath: string): string => {
|
|
||||||
const vbp = volumeBasePath === "/" ? "" : volumeBasePath;
|
|
||||||
|
|
||||||
if (!vbp) return displayPath;
|
|
||||||
if (displayPath === "/") return vbp;
|
|
||||||
return `${vbp}${displayPath}`;
|
|
||||||
},
|
|
||||||
[volumeBasePath],
|
|
||||||
);
|
|
||||||
|
|
||||||
const { mutate: restoreSnapshot, isPending: isRestoring } = useMutation({
|
const { mutate: restoreSnapshot, isPending: isRestoring } = useMutation({
|
||||||
...restoreSnapshotMutation(),
|
...restoreSnapshotMutation(),
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
|
|
@ -117,8 +106,7 @@ export function RestoreForm({ snapshot, repository, snapshotId, returnPath }: Re
|
||||||
const isCustomLocation = restoreLocation === "custom";
|
const isCustomLocation = restoreLocation === "custom";
|
||||||
const targetPath = isCustomLocation && customTargetPath.trim() ? customTargetPath.trim() : undefined;
|
const targetPath = isCustomLocation && customTargetPath.trim() ? customTargetPath.trim() : undefined;
|
||||||
|
|
||||||
const pathsArray = Array.from(selectedPaths);
|
const includePaths = Array.from(selectedPaths);
|
||||||
const includePaths = pathsArray.map((path) => addBasePath(path));
|
|
||||||
|
|
||||||
restoreCompletedRef.current = false;
|
restoreCompletedRef.current = false;
|
||||||
setIsRestoreActive(true);
|
setIsRestoreActive(true);
|
||||||
|
|
@ -142,7 +130,6 @@ export function RestoreForm({ snapshot, repository, snapshotId, returnPath }: Re
|
||||||
restoreLocation,
|
restoreLocation,
|
||||||
customTargetPath,
|
customTargetPath,
|
||||||
selectedPaths,
|
selectedPaths,
|
||||||
addBasePath,
|
|
||||||
overwriteMode,
|
overwriteMode,
|
||||||
restoreSnapshot,
|
restoreSnapshot,
|
||||||
]);
|
]);
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import type { UseFormReturn } from "react-hook-form";
|
||||||
import { Check, Pencil, X, AlertTriangle } from "lucide-react";
|
import { Check, Pencil, X, AlertTriangle } from "lucide-react";
|
||||||
import { Button } from "../../../../components/ui/button";
|
import { Button } from "../../../../components/ui/button";
|
||||||
import { FormItem, FormLabel, FormDescription, FormField, FormControl } from "../../../../components/ui/form";
|
import { FormItem, FormLabel, FormDescription, FormField, FormControl } from "../../../../components/ui/form";
|
||||||
import { LocalFileBrowser } from "../../../../components/file-browsers/local-file-browser";
|
import { DirectoryBrowser } from "../../../../components/file-browsers/directory-browser";
|
||||||
import {
|
import {
|
||||||
AlertDialog,
|
AlertDialog,
|
||||||
AlertDialogAction,
|
AlertDialogAction,
|
||||||
|
|
@ -96,20 +96,9 @@ export const LocalRepositoryForm = ({ form }: Props) => {
|
||||||
</AlertDialogDescription>
|
</AlertDialogDescription>
|
||||||
</AlertDialogHeader>
|
</AlertDialogHeader>
|
||||||
<div className="py-4">
|
<div className="py-4">
|
||||||
<LocalFileBrowser
|
<DirectoryBrowser
|
||||||
className="border rounded-lg overflow-hidden"
|
onSelectPath={field.onChange}
|
||||||
useScrollArea
|
|
||||||
scrollAreaClassName="h-64"
|
|
||||||
foldersOnly
|
|
||||||
selectableFolders
|
|
||||||
onFolderSelect={(path) => {
|
|
||||||
field.onChange(path);
|
|
||||||
}}
|
|
||||||
selectedFolder={field.value || constants.REPOSITORY_BASE}
|
|
||||||
showSelectedPathFooter
|
|
||||||
selectedPath={field.value || constants.REPOSITORY_BASE}
|
selectedPath={field.value || constants.REPOSITORY_BASE}
|
||||||
loadingMessage="Loading directories..."
|
|
||||||
emptyMessage="No subdirectories found"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<AlertDialogFooter>
|
<AlertDialogFooter>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { Pencil } from "lucide-react";
|
import { Pencil } from "lucide-react";
|
||||||
import type { UseFormReturn } from "react-hook-form";
|
import type { UseFormReturn } from "react-hook-form";
|
||||||
import type { FormValues } from "../create-volume-form";
|
import type { FormValues } from "../create-volume-form";
|
||||||
import { LocalFileBrowser } from "../../../../components/file-browsers/local-file-browser";
|
import { DirectoryBrowser } from "../../../../components/file-browsers/directory-browser";
|
||||||
import { Button } from "../../../../components/ui/button";
|
import { Button } from "../../../../components/ui/button";
|
||||||
import {
|
import {
|
||||||
FormControl,
|
FormControl,
|
||||||
|
|
@ -38,19 +38,7 @@ export const DirectoryForm = ({ form }: Props) => {
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<LocalFileBrowser
|
<DirectoryBrowser onSelectPath={field.onChange} selectedPath={field.value} />
|
||||||
className="border rounded-lg overflow-hidden"
|
|
||||||
useScrollArea
|
|
||||||
scrollAreaClassName="h-64"
|
|
||||||
foldersOnly
|
|
||||||
selectableFolders
|
|
||||||
onFolderSelect={(path) => field.onChange(path)}
|
|
||||||
selectedFolder={field.value}
|
|
||||||
showSelectedPathFooter
|
|
||||||
selectedPath={field.value}
|
|
||||||
loadingMessage="Loading directories..."
|
|
||||||
emptyMessage="No subdirectories found"
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormDescription>Browse and select a directory on the host filesystem to track.</FormDescription>
|
<FormDescription>Browse and select a directory on the host filesystem to track.</FormDescription>
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ export const findCommonAncestor = (paths: string[]): string => {
|
||||||
const splitPaths = paths.map((path) => path.split("/").filter(Boolean));
|
const splitPaths = paths.map((path) => path.split("/").filter(Boolean));
|
||||||
const minLength = Math.min(...splitPaths.map((parts) => parts.length));
|
const minLength = Math.min(...splitPaths.map((parts) => parts.length));
|
||||||
|
|
||||||
let commonParts: string[] = [];
|
const commonParts: string[] = [];
|
||||||
for (let i = 0; i < minLength; i++) {
|
for (let i = 0; i < minLength; i++) {
|
||||||
const partSet = new Set(splitPaths.map((parts) => parts[i]));
|
const partSet = new Set(splitPaths.map((parts) => parts[i]));
|
||||||
if (partSet.size === 1) {
|
if (partSet.size === 1) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue