Improve AddLocationButton & Implement UI for indexer rules (#620)

* Improve AddLocationButton + Implement UI for indexer rules

* Minor code clean-ups
 - Remove unused variables and imports

* cleanup AddLocationDialog props

---------

Co-authored-by: Brendan Allan <brendonovich@outlook.com>
This commit is contained in:
Vítor Vasconcellos 2023-03-19 17:08:48 +00:00 committed by GitHub
parent a6a57bbd50
commit 0fd53d1287
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 168 additions and 131 deletions

View file

@ -1,48 +0,0 @@
import { useLibraryMutation, usePlausibleEvent } from '@sd/client';
import { dialogManager } from '@sd/ui';
import { usePlatform } from '~/util/Platform';
import AddLocationDialog from '../../settings/library/locations/AddDialog';
export default () => {
const platform = usePlatform();
const submitPlausibleEvent = usePlausibleEvent({ platformType: platform.platform });
const createLocation = useLibraryMutation('locations.create', {
onSuccess: () => {
submitPlausibleEvent({
event: {
type: 'locationCreate'
}
});
}
});
return (
<button
onClick={() => {
if (platform.platform === 'web') {
dialogManager.create((dp) => <AddLocationDialog {...dp} />);
} else {
if (!platform.openDirectoryPickerDialog) {
alert('Opening a dialogue is not supported on this platform!');
return;
}
platform.openDirectoryPickerDialog().then((result) => {
// TODO: Pass indexer rules ids to create location
if (result)
createLocation.mutate({
path: result as string,
indexer_rules_ids: []
});
});
}
}}
className="
border-sidebar-line hover:border-sidebar-selected cursor-normal text-ink-faint mt-1 w-full rounded
border border-dashed px-2 py-1 text-center
text-xs font-medium transition
"
>
Add Location
</button>
);
};

View file

@ -22,11 +22,11 @@ import {
useOnlineLocations
} from '@sd/client';
import { Button, ButtonLink, Folder, Loader, Popover, Tooltip } from '@sd/ui';
import { AddLocationButton } from '~/app/$libraryId/settings/library/locations/AddLocationButton';
import { SubtleButton } from '~/components/SubtleButton';
import { MacTrafficLights } from '~/components/TrafficLights';
import { useOperatingSystem } from '~/hooks/useOperatingSystem';
import { OperatingSystem, usePlatform } from '~/util/Platform';
import AddLocationButton from './AddLocationButton';
import DebugPopover from './DebugPopover';
import Icon from './Icon';
import { JobsManager } from './JobManager';
@ -118,7 +118,13 @@ const LibrarySection = () => {
</SidebarLink>
);
})}
{(locations.data?.length || 0) < 4 && <AddLocationButton />}
{(locations.data?.length || 0) < 4 && (
<AddLocationButton
className="border-sidebar-line hover:border-sidebar-selected cursor-normal
text-ink-faint mt-1 w-full rounded border border-dashed px-2 py-1
text-center text-xs font-medium transition"
/>
)}
</Section>
{!!tags.data?.length && (
<Section

View file

@ -1,42 +0,0 @@
import { useLibraryMutation } from '@sd/client';
import { Dialog, UseDialogProps, useDialog } from '@sd/ui';
import { Input, useZodForm, z } from '@sd/ui/src/forms';
const schema = z.object({ path: z.string() });
type Props = UseDialogProps;
export default function AddLocationDialog(props: Props) {
const dialog = useDialog(props);
const createLocation = useLibraryMutation('locations.create');
const form = useZodForm({
schema,
defaultValues: {
// BEFORE MERGE: Remove default value
path: '/Users/jamie/Projects/spacedrive/packages/test-files/files'
}
});
return (
<Dialog
{...{ dialog, form }}
onSubmit={form.handleSubmit(({ path }) =>
createLocation.mutateAsync({
path,
indexer_rules_ids: []
})
)}
title="Add Location URL"
description="As you are using the browser version of Spacedrive you will (for now) need to specify an absolute URL of a directory local to the remote node."
ctaLabel="Add"
>
<Input
className="mt-3 w-full grow"
placeholder="/Users/jamie/Movies"
required
{...form.register('path')}
/>
</Dialog>
);
}

View file

@ -0,0 +1,36 @@
import { Button, ButtonProps, dialogManager } from '@sd/ui';
import { showAlertDialog } from '~/components/AlertDialog';
import { usePlatform } from '~/util/Platform';
import { AddLocationDialog } from './AddLocationDialog';
export const AddLocationButton = (props: ButtonProps) => {
const platform = usePlatform();
return (
<>
<Button
{...props}
onClick={async () => {
let path = '';
if (platform.openDirectoryPickerDialog) {
const _path = await platform.openDirectoryPickerDialog();
if (!_path) return;
if (typeof _path !== 'string') {
// TODO: Should support for adding multiple locations simultaneously be added?
showAlertDialog({
title: 'Error',
value: "Can't add multiple locations"
});
return;
}
path = _path;
}
await dialogManager.create((dp) => <AddLocationDialog path={path} {...dp} />);
}}
>
Add Location
</Button>
</>
);
};

View file

@ -0,0 +1,112 @@
import { ChangeEvent } from 'react';
import { Controller } from 'react-hook-form';
import { useLibraryMutation, useLibraryQuery } from '@sd/client';
import { CheckBox, Dialog, UseDialogProps, useDialog } from '@sd/ui';
import { Input, useZodForm, z } from '@sd/ui/src/forms';
import { showAlertDialog } from '~/components/AlertDialog';
import { usePlatform } from '~/util/Platform';
const schema = z.object({ path: z.string(), indexer_rules_ids: z.array(z.number()) });
interface Props extends UseDialogProps {
path: string;
}
export const AddLocationDialog = (props: Props) => {
const dialog = useDialog(props);
const platform = usePlatform();
const createLocation = useLibraryMutation('locations.create');
const indexerRulesList = useLibraryQuery(['locations.indexer_rules.list']);
const form = useZodForm({
schema,
defaultValues: {
path: props.path,
indexer_rules_ids: []
}
});
return (
<Dialog
{...{ dialog, form }}
title="New Location"
description={
platform.platform === 'web'
? '"As you are using the browser version of Spacedrive you will (for now) need to specify an absolute URL of a directory local to the remote node."'
: ''
}
onSubmit={form.handleSubmit(async ({ path, indexer_rules_ids }) => {
try {
if (platform.platform === 'tauri') createLocation.mutate({ path, indexer_rules_ids });
else await createLocation.mutateAsync({ path, indexer_rules_ids });
} catch (err) {
console.error(err);
showAlertDialog({
title: 'Error',
value: 'Failed to add location'
});
}
})}
ctaLabel="Add"
>
<div className="relative flex flex-col">
<p className="mt-2 text-[0.9rem] font-bold">Path:</p>
<Input
type="text"
onClick={async () => {
if (!platform.openDirectoryPickerDialog) return;
const path = await platform.openDirectoryPickerDialog();
if (!path) return;
if (typeof path !== 'string') {
// TODO: Should support for adding multiple locations simultaneously be added?
showAlertDialog({
title: 'Error',
value: "Can't add multiple locations"
});
return;
}
form.setValue('path', path);
}}
readOnly={platform.platform !== 'web'}
required
className="mt-3 w-full grow cursor-pointer"
{...form.register('path')}
/>
</div>
<div className="relative flex flex-col">
<p className="mt-6 text-[0.9rem] font-bold">File indexing rules:</p>
<div className="mt-4 mb-3 grid w-full grid-cols-2 gap-4">
<Controller
name="indexer_rules_ids"
control={form.control}
render={({ field }) => (
<>
{indexerRulesList.data?.map((rule) => (
<div className="flex" key={rule.id}>
<CheckBox
value={rule.id}
onChange={(event: ChangeEvent) => {
const ref = event.target as HTMLInputElement;
if (ref.checked) {
field.onChange([...field.value, Number.parseInt(ref.value)]);
} else {
field.onChange(
field.value.filter((value) => value !== Number.parseInt(ref.value))
);
}
}}
/>
<span className="mr-3 ml-0.5 mt-0.5 text-sm font-bold">{rule.name}</span>
</div>
))}
</>
)}
/>
</div>
</div>
</Dialog>
);
};

View file

@ -1,15 +1,11 @@
import { useLibraryMutation, useLibraryQuery } from '@sd/client';
import { LocationCreateArgs } from '@sd/client';
import { Button, SearchInput, dialogManager } from '@sd/ui';
import { usePlatform } from '~/util/Platform';
import { useLibraryQuery } from '@sd/client';
import { SearchInput } from '@sd/ui';
import { Heading } from '../../Layout';
import AddDialog from './AddDialog';
import { AddLocationButton } from './AddLocationButton';
import ListItem from './ListItem';
export const Component = () => {
const platform = usePlatform();
const locations = useLibraryQuery(['locations.list']);
const createLocation = useLibraryMutation('locations.create');
return (
<>
@ -19,31 +15,7 @@ export const Component = () => {
rightArea={
<div className="flex flex-row items-center space-x-5">
<SearchInput placeholder="Search locations" />
<Button
variant="accent"
size="md"
onClick={() => {
if (platform.platform === 'web') {
dialogManager.create((dp) => <AddDialog {...dp} />);
} else {
if (!platform.openDirectoryPickerDialog) {
alert('Opening a dialogue is not supported on this platform!');
return;
}
platform.openDirectoryPickerDialog().then((result) => {
// TODO: Pass indexer rules ids to create location
if (result)
createLocation.mutate({
path: result as string,
indexer_rules_ids: []
} as LocationCreateArgs);
});
}
}}
>
Add Location
</Button>
<AddLocationButton variant="accent" size="md" />
</div>
}
/>

View file

@ -8589,7 +8589,7 @@ packages:
/@types/keyv/3.1.4:
resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==}
dependencies:
'@types/node': 18.11.18
'@types/node': 18.15.1
dev: true
/@types/loadable__component/5.13.4:
@ -8730,7 +8730,7 @@ packages:
/@types/responselike/1.0.0:
resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==}
dependencies:
'@types/node': 18.11.18
'@types/node': 18.15.1
dev: true
/@types/scheduler/0.16.2:
@ -8984,7 +8984,7 @@ packages:
'@babel/plugin-transform-react-jsx-source': 7.19.6_@babel+core@7.20.12
magic-string: 0.26.7
react-refresh: 0.14.0
vite: 4.1.4_sass@1.57.1
vite: 4.1.4_ovmyjmuuyckt3r3gpaexj2onji
transitivePeerDependencies:
- supports-color
@ -10526,7 +10526,7 @@ packages:
dependencies:
clone-response: 1.0.3
get-stream: 5.2.0
http-cache-semantics: 4.1.0
http-cache-semantics: 4.1.1
keyv: 3.1.0
lowercase-keys: 2.0.0
normalize-url: 4.5.1
@ -14065,8 +14065,8 @@ packages:
entities: 4.4.0
dev: true
/http-cache-semantics/4.1.0:
resolution: {integrity: sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==}
/http-cache-semantics/4.1.1:
resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==}
dev: true
/http-errors/2.0.0:
@ -20658,6 +20658,7 @@ packages:
/trim/0.0.1:
resolution: {integrity: sha512-YzQV+TZg4AxpKxaTHK3c3D+kRDCGVEE7LemdlQZoQXn0iennk10RsIoY6ikzAqJTc9Xjl9C1/waHom/J86ziAQ==}
deprecated: Use String.prototype.trim() instead
dev: true
/trough/1.0.5:
@ -21847,7 +21848,7 @@ packages:
dependencies:
'@rollup/pluginutils': 5.0.2
'@svgr/core': 6.5.1
vite: 4.1.4_sass@1.57.1
vite: 4.1.4_ovmyjmuuyckt3r3gpaexj2onji
transitivePeerDependencies:
- rollup
- supports-color