import { useDebouncedValue, useDisclosure } from '@mantine/hooks'; import { Fragment, useCallback, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { generatePath, useNavigate } from 'react-router'; import { usePlayQueueAdd } from '/@/renderer/features/player'; import { Command, CommandPalettePages } from '/@/renderer/features/search/components/command'; import { CommandItemSelectable } from '/@/renderer/features/search/components/command-item-selectable'; import { GoToCommands } from '/@/renderer/features/search/components/go-to-commands'; import { HomeCommands } from '/@/renderer/features/search/components/home-commands'; import { LibraryCommandItem } from '/@/renderer/features/search/components/library-command-item'; import { ServerCommands } from '/@/renderer/features/search/components/server-commands'; import { useSearch } from '/@/renderer/features/search/queries/search-query'; import { AppRoute } from '/@/renderer/router/routes'; import { useCurrentServer } from '/@/renderer/store'; import { ActionIcon } from '/@/shared/components/action-icon/action-icon'; import { Box } from '/@/shared/components/box/box'; import { Button } from '/@/shared/components/button/button'; import { Group } from '/@/shared/components/group/group'; import { Icon } from '/@/shared/components/icon/icon'; import { Kbd } from '/@/shared/components/kbd/kbd'; import { Modal } from '/@/shared/components/modal/modal'; import { Spinner } from '/@/shared/components/spinner/spinner'; import { TextInput } from '/@/shared/components/text-input/text-input'; import { LibraryItem } from '/@/shared/types/domain-types'; interface CommandPaletteProps { modalProps: (typeof useDisclosure)['arguments']; } export const CommandPalette = ({ modalProps }: CommandPaletteProps) => { const navigate = useNavigate(); const server = useCurrentServer(); const [value, setValue] = useState(''); const [query, setQuery] = useState(''); const [debouncedQuery] = useDebouncedValue(query, 400); const [pages, setPages] = useState([CommandPalettePages.HOME]); const activePage = pages[pages.length - 1]; const isHome = activePage === CommandPalettePages.HOME; const searchInputRef = useRef(null); const { t } = useTranslation(); const popPage = useCallback(() => { setPages((pages) => { const x = [...pages]; x.splice(-1, 1); return x; }); }, []); const { data, isLoading } = useSearch({ options: { enabled: isHome && debouncedQuery !== '' && query !== '' }, query: { albumArtistLimit: 4, albumArtistStartIndex: 0, albumLimit: 4, albumStartIndex: 0, query: debouncedQuery, songLimit: 4, songStartIndex: 0, }, serverId: server?.id, }); const showAlbumGroup = isHome && Boolean(query && data && data?.albums?.length > 0); const showArtistGroup = isHome && Boolean(query && data && data?.albumArtists?.length > 0); const showTrackGroup = isHome && Boolean(query && data && data?.songs?.length > 0); const handlePlayQueueAdd = usePlayQueueAdd(); return ( { if (isHome) { modalProps.handlers.close(); setQuery(''); } else { popPage(); } }, toggle: () => { if (isHome) { modalProps.handlers.toggle(); setQuery(''); } else { popPage(); } }, }} size="lg" styles={{ header: { display: 'none' }, }} > {pages.map((page, index) => ( {index > 0 && ' > '} ))} { if (value.includes(search)) return 1; if (value.includes('search')) return 1; return 0; }} label="Global Command Menu" onKeyDown={(e) => { // Focus the search input when navigating with arrow keys // to prevent the focus from staying on the command-item ActionIcon if (e.key === 'ArrowDown' || e.key === 'ArrowUp') { searchInputRef.current?.focus(); } }} onValueChange={setValue} value={value} > } onChange={(e) => setQuery(e.currentTarget.value)} ref={searchInputRef} rightSection={ query && ( { setQuery(''); searchInputRef.current?.focus(); }} variant="transparent" > ) } size="sm" value={query} /> No results found. {showAlbumGroup && ( {data?.albums?.map((album) => ( { navigate( generatePath(AppRoute.LIBRARY_ALBUMS_DETAIL, { albumId: album.id, }), ); modalProps.handlers.close(); setQuery(''); }} value={`search-${album.id}`} > {({ isHighlighted }) => ( artist.name) .join(', ')} title={album.name} /> )} ))} )} {showArtistGroup && ( {data?.albumArtists.map((artist) => ( { navigate( generatePath(AppRoute.LIBRARY_ALBUM_ARTISTS_DETAIL, { albumArtistId: artist.id, }), ); modalProps.handlers.close(); setQuery(''); }} value={`search-${artist.id}`} > {({ isHighlighted }) => ( )} ))} )} {showTrackGroup && ( {data?.songs.map((song) => ( { navigate( generatePath(AppRoute.LIBRARY_ALBUMS_DETAIL, { albumId: song.albumId, }), ); modalProps.handlers.close(); setQuery(''); }} value={`search-${song.id}`} > {({ isHighlighted }) => ( artist.name) .join(', ')} title={song.name} /> )} ))} )} {activePage === CommandPalettePages.HOME && ( )} {activePage === CommandPalettePages.GO_TO && ( )} {activePage === CommandPalettePages.MANAGE_SERVERS && ( )} {isHome && isLoading && query !== '' && } ESC ); };