diff --git a/src/components/AppShelf/SmallServiceItem.tsx b/src/components/AppShelf/SmallServiceItem.tsx new file mode 100644 index 000000000..46cb29db6 --- /dev/null +++ b/src/components/AppShelf/SmallServiceItem.tsx @@ -0,0 +1,18 @@ +import { Anchor, Avatar, Group, Text } from '@mantine/core'; + +interface smallServiceItem { + label: string; + icon?: string; + url?: string; +} + +export default function SmallServiceItem(props: any) { + const { service }: { service: smallServiceItem } = props; + // TODO : Use Next/link + return ( + + {service.icon && } + {service.label} + + ); +} diff --git a/src/modules/search/SearchModule.tsx b/src/modules/search/SearchModule.tsx index 42a778a3c..0279ed012 100644 --- a/src/modules/search/SearchModule.tsx +++ b/src/modules/search/SearchModule.tsx @@ -1,7 +1,7 @@ import { Kbd, createStyles, Autocomplete, Popover, ScrollArea, Divider } from '@mantine/core'; import { useClickOutside, useDebouncedValue, useHotkeys } from '@mantine/hooks'; import { useForm } from '@mantine/form'; -import React, { useEffect, useRef, useState } from 'react'; +import React, { forwardRef, useEffect, useRef, useState } from 'react'; import { IconSearch as Search, IconBrandYoutube as BrandYoutube, @@ -14,6 +14,7 @@ import { useConfig } from '../../tools/state'; import { IModule } from '../ModuleTypes'; import { OverseerrModule } from '../overseerr'; import { OverseerrMediaDisplay } from '../common'; +import SmallServiceItem from '../../components/AppShelf/SmallServiceItem'; const useStyles = createStyles((theme) => ({ hide: { @@ -98,11 +99,36 @@ export default function SearchBar(props: any) { if (!isModuleEnabled) { return null; } - - const autocompleteData = results.map((result) => ({ + // Match all the services that contain the query in their name if the query is not empty + const matchingServices = config.services.filter((service) => { + if (form.values.query === '' || form.values.query === undefined) { + return false; + } + return service.name.toLowerCase().includes(form.values.query.toLowerCase()); + }); + const autocompleteData = matchingServices.map((service) => ({ + label: service.name, + value: service.name, + icon: service.icon, + url: service.openedUrl ?? service.url, + })); + // Append the matching results to the autocomplete data + const autoCompleteResults = results.map((result) => ({ label: result.phrase, value: result.phrase, + icon: result.icon, + url: result.url, })); + autocompleteData.push(...autoCompleteResults); + + const AutoCompleteItem = forwardRef( + ({ label, value, icon, url, ...others }: any, ref) => ( +
+ +
+ ) + ); + return (
{ @@ -161,6 +187,15 @@ export default function SearchBar(props: any) { onFocusCapture={() => setOpened(true)} autoFocus variant="filled" + itemComponent={AutoCompleteItem} + onItemSubmit={(item) => { + setOpened(false); + if (item.url) { + results.splice(0, autocompleteData.length); + form.reset(); + window.open(item.url); + } + }} data={autocompleteData} icon={icon} ref={textInput}