mirror of
https://github.com/spacedriveapp/spacedrive.git
synced 2026-05-24 08:22:10 -04:00
[ENG-650] - Blacklist/whitelist each rule (#872)
* Blacklist/whitelist each rule * update tooltip wording * tweaks * get rid of 'magic string' value
This commit is contained in:
@@ -207,7 +207,7 @@ export const Component = () => {
|
||||
field={field}
|
||||
label="Indexer rules"
|
||||
editable={true}
|
||||
infoText="Indexer rules allow you to specify paths to ignore using RegEx."
|
||||
infoText="Indexer rules allow you to specify paths to ignore using globs."
|
||||
className="flex flex-col rounded-md border border-app-line bg-app-overlay p-5"
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -51,13 +51,13 @@ function RuleButton<T extends IndexerRuleIdFieldType>({
|
||||
})
|
||||
}
|
||||
className={clsx(
|
||||
'hover:brightness-125',
|
||||
ruleEnabled ? '!text-green-500' : 'text-red-500'
|
||||
'px-2 hover:brightness-110',
|
||||
ruleEnabled ? '!bg-accent !text-white' : 'text-ink'
|
||||
)}
|
||||
>
|
||||
{ruleEnabled ? 'Enabled' : 'Disabled'}
|
||||
</InfoPill>
|
||||
{rule.default && <InfoPill className="text-ink-faint">System</InfoPill>}
|
||||
{rule.default && <InfoPill className="px-2 text-ink-faint">System</InfoPill>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -22,11 +22,11 @@ const ruleKindEnum = z.enum(ruleKinds);
|
||||
|
||||
const schema = z.object({
|
||||
name: z.string().min(3),
|
||||
kind: ruleKindEnum,
|
||||
rules: z.array(
|
||||
z.object({
|
||||
type: z.string(),
|
||||
value: z.string().min(1, { message: 'Value required' })
|
||||
value: z.string().min(1, { message: 'Value required' }),
|
||||
kind: ruleKindEnum
|
||||
})
|
||||
)
|
||||
});
|
||||
@@ -42,17 +42,21 @@ const RulesForm = ({ onSubmitted }: Props) => {
|
||||
const REMOTE_ERROR_FORM_FIELD = 'root.serverError';
|
||||
const createIndexerRules = useLibraryMutation(['locations.indexer_rules.create']);
|
||||
const formId = useId();
|
||||
const modeOptions: { value: RuleKind; label: string }[] = [
|
||||
{ value: 'RejectFilesByGlob', label: 'Reject files by glob' },
|
||||
{ value: 'AcceptFilesByGlob', label: 'Accept files by glob' }
|
||||
];
|
||||
const form = useZodForm({
|
||||
schema,
|
||||
mode: 'onBlur',
|
||||
reValidateMode: 'onBlur',
|
||||
defaultValues: {
|
||||
name: '',
|
||||
kind: 'RejectFilesByGlob',
|
||||
rules: [
|
||||
{
|
||||
type: selectValues[0],
|
||||
value: ''
|
||||
value: '',
|
||||
kind: modeOptions[0]?.value
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -91,15 +95,15 @@ const RulesForm = ({ onSubmitted }: Props) => {
|
||||
const formatData = {
|
||||
name: data.name,
|
||||
dry_run: false,
|
||||
rules: data.rules.map(({ type, value }) => {
|
||||
rules: data.rules.map(({ type, value, kind }) => {
|
||||
switch (type) {
|
||||
case 'Name':
|
||||
return [data.kind, [`**/${value}`]];
|
||||
return [kind, [`**/${value}`]];
|
||||
case 'Extension':
|
||||
// .tar should work for .tar.gz, .tar.bz2, etc.
|
||||
return [data.kind, [`**/*${value}`, `**/*${value}.*`]];
|
||||
return [kind, [`**/*${value}`, `**/*${value}.*`]];
|
||||
default:
|
||||
return [data.kind, [value]];
|
||||
return [kind, [value]];
|
||||
}
|
||||
})
|
||||
} as IndexerRuleCreateArgs;
|
||||
@@ -139,17 +143,23 @@ const RulesForm = ({ onSubmitted }: Props) => {
|
||||
<h3 className="mb-[15px] mt-[20px] w-full text-sm font-semibold">Rules</h3>
|
||||
<div
|
||||
className={
|
||||
'grid space-y-1 rounded-md border border-app-line/60 bg-app-input p-2 pb-0'
|
||||
'grid space-y-1 rounded-md border border-app-line/60 bg-app-input p-2'
|
||||
}
|
||||
>
|
||||
<div className="mb-4 grid grid-cols-3 px-3 pt-4 text-sm font-bold">
|
||||
<h3 className="pl-2">Type</h3>
|
||||
<h3 className="pl-2">Value</h3>
|
||||
<div className="mb-2 grid w-full grid-cols-4 items-center pt-2 text-center text-[11px] font-bold">
|
||||
<h3>Type</h3>
|
||||
<h3>Value</h3>
|
||||
<h3 className="flex items-center justify-center gap-1">
|
||||
Mode
|
||||
<Tooltip label="By default, an indexer rule functions as a Reject, resulting in the exclusion of any files that match its criteria. Enabling this option will transform it into a Allow, allowing the location to solely index files that meet its specified rules.">
|
||||
<Info />
|
||||
</Tooltip>
|
||||
</h3>
|
||||
</div>
|
||||
{fields.map((field, index) => {
|
||||
return (
|
||||
<Card
|
||||
className="grid w-full grid-cols-3 gap-3 border-app-line p-0 !px-2 hover:bg-app-box/70"
|
||||
className="grid w-full grid-cols-4 gap-3 border-app-line p-0 !px-2 hover:bg-app-box/70"
|
||||
key={field.id}
|
||||
>
|
||||
<Controller
|
||||
@@ -158,7 +168,7 @@ const RulesForm = ({ onSubmitted }: Props) => {
|
||||
render={({ field }) => (
|
||||
<Select
|
||||
{...field}
|
||||
className="w-full"
|
||||
className="!h-[30px] w-full"
|
||||
onChange={(value) => {
|
||||
field.onChange(value);
|
||||
form.resetField(`rules.${index}.value`);
|
||||
@@ -177,7 +187,7 @@ const RulesForm = ({ onSubmitted }: Props) => {
|
||||
control={form.control}
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
<div className="flex w-full flex-col">
|
||||
<RuleInput
|
||||
className={clsx(
|
||||
'!h-[30px]',
|
||||
@@ -211,11 +221,28 @@ const RulesForm = ({ onSubmitted }: Props) => {
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
||||
<Controller
|
||||
name={`rules.${index}.kind` as const}
|
||||
render={({ field }) => (
|
||||
<Select
|
||||
{...field}
|
||||
className="!h-[30px] w-full"
|
||||
onChange={(value) => {
|
||||
field.onChange(value);
|
||||
}}
|
||||
>
|
||||
{modeOptions.map(({ label, value }) => (
|
||||
<SelectOption key={value} value={value}>
|
||||
{label}
|
||||
</SelectOption>
|
||||
))}
|
||||
</Select>
|
||||
)}
|
||||
control={form.control}
|
||||
/>
|
||||
{index !== 0 && (
|
||||
<Button
|
||||
className="flex h-[32px] w-[32px] items-center
|
||||
justify-self-end"
|
||||
className="flex h-[32px] w-[32px] items-center justify-self-end"
|
||||
variant="gray"
|
||||
onClick={() => remove(index)}
|
||||
>
|
||||
@@ -230,7 +257,11 @@ const RulesForm = ({ onSubmitted }: Props) => {
|
||||
<Button
|
||||
onClick={() =>
|
||||
append(
|
||||
{ type: selectValues[0] as string, value: '' },
|
||||
{
|
||||
type: selectValues[0] as string,
|
||||
value: '',
|
||||
kind: 'RejectFilesByGlob'
|
||||
},
|
||||
{ shouldFocus: false }
|
||||
)
|
||||
}
|
||||
@@ -242,35 +273,6 @@ const RulesForm = ({ onSubmitted }: Props) => {
|
||||
</Button>
|
||||
</div>
|
||||
<Divider className="my-[25px]" />
|
||||
<div className="flex w-full justify-center">
|
||||
<div className="mb-5 flex items-center gap-2">
|
||||
<p className="text-sm text-ink-faint">Blacklist</p>
|
||||
<Controller
|
||||
name="kind"
|
||||
render={({ field }) => (
|
||||
<Switch
|
||||
onCheckedChange={(checked) => {
|
||||
// TODO: These rule kinds are broken right now in the backend and this UI doesn't make much sense for them
|
||||
// kind.AcceptIfChildrenDirectoriesArePresent
|
||||
// kind.RejectIfChildrenDirectoriesArePresent
|
||||
const kind = ruleKindEnum.enum;
|
||||
field.onChange(
|
||||
checked
|
||||
? kind.AcceptFilesByGlob
|
||||
: kind.RejectFilesByGlob
|
||||
);
|
||||
}}
|
||||
size="md"
|
||||
/>
|
||||
)}
|
||||
control={form.control}
|
||||
/>
|
||||
<p className="text-sm text-ink-faint">Whitelist</p>
|
||||
<Tooltip label="By default, an indexer rule acts as a deny list, causing a location to ignore any file that match its rules. Enabling this will make it act as an allow list, and the location will only display files that match its rules.">
|
||||
<Info />
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<Button form={formId} type="submit" variant="accent" className="mx-auto w-[90px]">
|
||||
Save
|
||||
</Button>
|
||||
|
||||
@@ -34,7 +34,7 @@ export default function IndexerRuleEditor<T extends IndexerRuleIdFieldType>({
|
||||
const [toggleNewRule, setToggleNewRule] = useState(false);
|
||||
const deleteIndexerRule = useLibraryMutation(['locations.indexer_rules.delete']);
|
||||
|
||||
const deleteRule: MouseEventHandler<HTMLButtonElement> = (e) => {
|
||||
const deleteRule: MouseEventHandler<HTMLButtonElement> = () => {
|
||||
if (!selectedRule) return;
|
||||
|
||||
showAlertDialog({
|
||||
@@ -43,7 +43,6 @@ export default function IndexerRuleEditor<T extends IndexerRuleIdFieldType>({
|
||||
label: 'Confirm',
|
||||
onSubmit: async () => {
|
||||
setIsDeleting(true);
|
||||
|
||||
try {
|
||||
await deleteIndexerRule.mutateAsync(selectedRule.id);
|
||||
} catch (error) {
|
||||
|
||||
@@ -167,6 +167,7 @@ export function Dialog<S extends FieldValues>({
|
||||
<Form
|
||||
form={form}
|
||||
onSubmit={async (e) => {
|
||||
e?.preventDefault();
|
||||
await onSubmit?.(e);
|
||||
dialog.onSubmit?.();
|
||||
setOpen(false);
|
||||
|
||||
Reference in New Issue
Block a user