feat(releases-widget): added auto icon search when editing a repository (#3087)
This commit is contained in:
@@ -2,5 +2,6 @@ export * from "./new-app/_app-new-form";
|
|||||||
export * from "./new-app/_form";
|
export * from "./new-app/_form";
|
||||||
|
|
||||||
export * from "./icon-picker/icon-picker";
|
export * from "./icon-picker/icon-picker";
|
||||||
|
export * from "./new-app/icon-matcher";
|
||||||
|
|
||||||
export * from "./upload-media/upload-media";
|
export * from "./upload-media/upload-media";
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React, { useCallback, useMemo, useState } from "react";
|
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import { ActionIcon, Button, Divider, Fieldset, Group, Select, Stack, Text, TextInput } from "@mantine/core";
|
import { ActionIcon, Button, Divider, Fieldset, Group, Select, Stack, Text, TextInput } from "@mantine/core";
|
||||||
import type { FormErrors } from "@mantine/form";
|
import type { FormErrors } from "@mantine/form";
|
||||||
|
import { useDebouncedValue } from "@mantine/hooks";
|
||||||
import { IconEdit, IconTrash, IconTriangleFilled } from "@tabler/icons-react";
|
import { IconEdit, IconTrash, IconTriangleFilled } from "@tabler/icons-react";
|
||||||
import { escapeForRegEx } from "@tiptap/react";
|
import { escapeForRegEx } from "@tiptap/react";
|
||||||
|
|
||||||
import { IconPicker } from "@homarr/forms-collection";
|
import { clientApi } from "@homarr/api/client";
|
||||||
|
import { findBestIconMatch, IconPicker } from "@homarr/forms-collection";
|
||||||
import { createModal, useModalAction } from "@homarr/modals";
|
import { createModal, useModalAction } from "@homarr/modals";
|
||||||
import { useScopedI18n } from "@homarr/translation/client";
|
import { useScopedI18n } from "@homarr/translation/client";
|
||||||
import { MaskedOrNormalImage } from "@homarr/ui";
|
import { MaskedOrNormalImage } from "@homarr/ui";
|
||||||
@@ -197,6 +199,13 @@ const ReleaseEditModal = createModal<ReleaseEditProps>(({ innerProps, actions })
|
|||||||
const [tempRepository, setTempRepository] = useState(() => ({ ...innerProps.repository }));
|
const [tempRepository, setTempRepository] = useState(() => ({ ...innerProps.repository }));
|
||||||
const [formErrors, setFormErrors] = useState<FormErrors>({});
|
const [formErrors, setFormErrors] = useState<FormErrors>({});
|
||||||
|
|
||||||
|
// Allows user to not select an icon by removing the url from the input,
|
||||||
|
// will only try and get an icon if the name or identifier changes
|
||||||
|
const [autoSetIcon, setAutoSetIcon] = useState(false);
|
||||||
|
|
||||||
|
// Debounce the name value with 200ms delay
|
||||||
|
const [debouncedName] = useDebouncedValue(tempRepository.name, 800);
|
||||||
|
|
||||||
const handleConfirm = useCallback(() => {
|
const handleConfirm = useCallback(() => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
@@ -221,6 +230,25 @@ const ReleaseEditModal = createModal<ReleaseEditProps>(({ innerProps, actions })
|
|||||||
setTempRepository((prev) => ({ ...prev, ...changedValue }));
|
setTempRepository((prev) => ({ ...prev, ...changedValue }));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// Auto-select icon based on identifier formatted name with debounced search
|
||||||
|
const { data: iconsData } = clientApi.icon.findIcons.useQuery(
|
||||||
|
{
|
||||||
|
searchText: debouncedName,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
enabled: autoSetIcon && (debouncedName?.length ?? 0) > 3,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (autoSetIcon && debouncedName && !tempRepository.iconUrl && iconsData?.icons) {
|
||||||
|
const bestMatch = findBestIconMatch(debouncedName, iconsData.icons);
|
||||||
|
if (bestMatch) {
|
||||||
|
handleChange({ iconUrl: bestMatch });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [debouncedName, iconsData, tempRepository, handleChange, autoSetIcon]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack>
|
<Stack>
|
||||||
<Group align="center" wrap="nowrap">
|
<Group align="center" wrap="nowrap">
|
||||||
@@ -256,6 +284,8 @@ const ReleaseEditModal = createModal<ReleaseEditProps>(({ innerProps, actions })
|
|||||||
identifier: event.currentTarget.value,
|
identifier: event.currentTarget.value,
|
||||||
name,
|
name,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (event.currentTarget.value) setAutoSetIcon(true);
|
||||||
}}
|
}}
|
||||||
error={formErrors[`${innerProps.fieldPath}.identifier`]}
|
error={formErrors[`${innerProps.fieldPath}.identifier`]}
|
||||||
w="100%"
|
w="100%"
|
||||||
@@ -268,6 +298,8 @@ const ReleaseEditModal = createModal<ReleaseEditProps>(({ innerProps, actions })
|
|||||||
value={tempRepository.name ?? ""}
|
value={tempRepository.name ?? ""}
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
handleChange({ name: event.currentTarget.value });
|
handleChange({ name: event.currentTarget.value });
|
||||||
|
|
||||||
|
if (event.currentTarget.value) setAutoSetIcon(true);
|
||||||
}}
|
}}
|
||||||
error={formErrors[`${innerProps.fieldPath}.name`]}
|
error={formErrors[`${innerProps.fieldPath}.name`]}
|
||||||
style={{ flex: 1, flexBasis: "40%" }}
|
style={{ flex: 1, flexBasis: "40%" }}
|
||||||
@@ -276,7 +308,14 @@ const ReleaseEditModal = createModal<ReleaseEditProps>(({ innerProps, actions })
|
|||||||
<IconPicker
|
<IconPicker
|
||||||
withAsterisk={false}
|
withAsterisk={false}
|
||||||
value={tempRepository.iconUrl ?? ""}
|
value={tempRepository.iconUrl ?? ""}
|
||||||
onChange={(url) => handleChange({ iconUrl: url === "" ? undefined : url })}
|
onChange={(url) => {
|
||||||
|
if (url === "") {
|
||||||
|
setAutoSetIcon(false);
|
||||||
|
handleChange({ iconUrl: undefined });
|
||||||
|
} else {
|
||||||
|
handleChange({ iconUrl: url });
|
||||||
|
}
|
||||||
|
}}
|
||||||
error={formErrors[`${innerProps.fieldPath}.iconUrl`] as string}
|
error={formErrors[`${innerProps.fieldPath}.iconUrl`] as string}
|
||||||
/>
|
/>
|
||||||
</Group>
|
</Group>
|
||||||
|
|||||||
Reference in New Issue
Block a user