mirror of
https://github.com/ajnart/homarr.git
synced 2025-11-10 07:25:48 +01:00
🎨 Quality of life : Use debouncedValue
This commit is contained in:
@@ -14,9 +14,10 @@ import {
|
|||||||
Text,
|
Text,
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { useForm } from '@mantine/form';
|
import { useForm } from '@mantine/form';
|
||||||
import { useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { IconApps as Apps } from '@tabler/icons';
|
import { IconApps as Apps } from '@tabler/icons';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
import { useDebouncedValue } from '@mantine/hooks';
|
||||||
import { useConfig } from '../../tools/state';
|
import { useConfig } from '../../tools/state';
|
||||||
import { ServiceTypeList } from '../../tools/types';
|
import { ServiceTypeList } from '../../tools/types';
|
||||||
|
|
||||||
@@ -134,6 +135,14 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } &
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [debounced, cancel] = useDebouncedValue(form.values.name, 250);
|
||||||
|
useEffect(() => {
|
||||||
|
if (form.values.name !== debounced) return;
|
||||||
|
MatchIcon(form.values.name, form);
|
||||||
|
MatchService(form.values.name, form);
|
||||||
|
MatchPort(form.values.name, form);
|
||||||
|
}, [debounced]);
|
||||||
|
|
||||||
// Try to set const hostname to new URL(form.values.url).hostname)
|
// Try to set const hostname to new URL(form.values.url).hostname)
|
||||||
// If it fails, set it to the form.values.url
|
// If it fails, set it to the form.values.url
|
||||||
let hostname = form.values.url;
|
let hostname = form.values.url;
|
||||||
@@ -186,14 +195,7 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } &
|
|||||||
required
|
required
|
||||||
label="Service name"
|
label="Service name"
|
||||||
placeholder="Plex"
|
placeholder="Plex"
|
||||||
value={form.values.name}
|
{...form.getInputProps('name')}
|
||||||
onChange={(event) => {
|
|
||||||
form.setFieldValue('name', event.currentTarget.value);
|
|
||||||
MatchIcon(event.currentTarget.value, form);
|
|
||||||
MatchService(event.currentTarget.value, form);
|
|
||||||
MatchPort(event.currentTarget.value, form);
|
|
||||||
}}
|
|
||||||
error={form.errors.name && 'Invalid icon url'}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextInput
|
<TextInput
|
||||||
|
|||||||
@@ -1,11 +1,18 @@
|
|||||||
import { TextInput, Kbd, createStyles, Text, Popover } from '@mantine/core';
|
import {
|
||||||
import { useForm, useHotkeys } from '@mantine/hooks';
|
Kbd,
|
||||||
import { useRef, useState } from 'react';
|
createStyles,
|
||||||
|
Text,
|
||||||
|
Popover,
|
||||||
|
TextInput,
|
||||||
|
} from '@mantine/core';
|
||||||
|
import { useDebouncedValue, useForm, useHotkeys } from '@mantine/hooks';
|
||||||
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
IconSearch as Search,
|
IconSearch as Search,
|
||||||
IconBrandYoutube as BrandYoutube,
|
IconBrandYoutube as BrandYoutube,
|
||||||
IconDownload as Download,
|
IconDownload as Download,
|
||||||
} from '@tabler/icons';
|
} from '@tabler/icons';
|
||||||
|
import axios from 'axios';
|
||||||
import { useConfig } from '../../../tools/state';
|
import { useConfig } from '../../../tools/state';
|
||||||
import { IModule } from '../modules';
|
import { IModule } from '../modules';
|
||||||
|
|
||||||
@@ -29,11 +36,35 @@ export const SearchModule: IModule = {
|
|||||||
export default function SearchBar(props: any) {
|
export default function SearchBar(props: any) {
|
||||||
const { config, setConfig } = useConfig();
|
const { config, setConfig } = useConfig();
|
||||||
const [opened, setOpened] = useState(false);
|
const [opened, setOpened] = useState(false);
|
||||||
|
const [results, setOpenedResults] = useState(false);
|
||||||
const [icon, setIcon] = useState(<Search />);
|
const [icon, setIcon] = useState(<Search />);
|
||||||
const queryUrl = config.settings.searchUrl ?? 'https://www.google.com/search?q=';
|
const queryUrl = config.settings.searchUrl ?? 'https://www.google.com/search?q=';
|
||||||
const textInput = useRef<HTMLInputElement>();
|
const textInput = useRef<HTMLInputElement>();
|
||||||
useHotkeys([['ctrl+K', () => textInput.current && textInput.current.focus()]]);
|
// Find a service with the type of 'Overseerr'
|
||||||
|
const service = config.services.find((s) => s.type === 'Overseerr');
|
||||||
|
|
||||||
|
const form = useForm({
|
||||||
|
initialValues: {
|
||||||
|
query: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const [debounced, cancel] = useDebouncedValue(form.values.query, 250);
|
||||||
|
const [data, setData] = useState([]);
|
||||||
|
useEffect(() => {
|
||||||
|
if (form.values.query !== debounced || form.values.query === '') return;
|
||||||
|
setOpened(false);
|
||||||
|
setOpenedResults(true);
|
||||||
|
if (service) {
|
||||||
|
const serviceUrl = new URL(service.url);
|
||||||
|
axios
|
||||||
|
.post(`/api/modules/overseerr?query=${form.values.query}`, {
|
||||||
|
service,
|
||||||
|
})
|
||||||
|
.then((res) => setData(res.data.results ?? []));
|
||||||
|
}
|
||||||
|
}, [debounced]);
|
||||||
|
useHotkeys([['ctrl+K', () => textInput.current && textInput.current.focus()]]);
|
||||||
const { classes, cx } = useStyles();
|
const { classes, cx } = useStyles();
|
||||||
const rightSection = (
|
const rightSection = (
|
||||||
<div className={classes.hide}>
|
<div className={classes.hide}>
|
||||||
@@ -43,12 +74,6 @@ export default function SearchBar(props: any) {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
const form = useForm({
|
|
||||||
initialValues: {
|
|
||||||
query: '',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// If enabled modules doesn't contain the module, return null
|
// If enabled modules doesn't contain the module, return null
|
||||||
// If module in enabled
|
// If module in enabled
|
||||||
|
|
||||||
@@ -57,6 +82,7 @@ export default function SearchBar(props: any) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Data with label as item.name
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
onChange={() => {
|
onChange={() => {
|
||||||
@@ -88,6 +114,9 @@ export default function SearchBar(props: any) {
|
|||||||
}, 20);
|
}, 20);
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
|
<Popover
|
||||||
|
opened={results}
|
||||||
|
target={
|
||||||
<Popover
|
<Popover
|
||||||
opened={opened}
|
opened={opened}
|
||||||
position="bottom"
|
position="bottom"
|
||||||
@@ -114,6 +143,13 @@ export default function SearchBar(props: any) {
|
|||||||
{...form.getInputProps('query')}
|
{...form.getInputProps('query')}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
>
|
||||||
|
<Text>
|
||||||
|
tip: Use the prefixes <b>!yt</b> and <b>!t</b> in front of your query to search on
|
||||||
|
YouTube or for a Torrent respectively.
|
||||||
|
</Text>
|
||||||
|
</Popover>
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<Text>
|
<Text>
|
||||||
Tip: Use the prefixes <b>!yt</b> and <b>!t</b> in front of your query to search on YouTube
|
Tip: Use the prefixes <b>!yt</b> and <b>!t</b> in front of your query to search on YouTube
|
||||||
|
|||||||
Reference in New Issue
Block a user