import { useListCollection, Stack, Text, HStack, Span, Badge } from "@chakra-ui/react" import { useDebouncedState } from '@tanstack/react-pacer' import { useQuery } from '@tanstack/react-query'; import { tanQueries } from "../../queries/index.js"; import { type TrackSearchResult } from "../../../core/Api.js"; import React, { useCallback, useEffect, useState } from "react"; import { ArtistCreditTags } from "../ArtistCreditDisplay.js"; import { LeftSideMetadataResultContent, MetadataPickMenu, MetadataSearchCombobox, type MetadataPartials } from "./MetadataResults.js"; import { playTrackDataSchema, type Credit, type TrackData } from "../../../core/Atomic.js"; import { playImage } from "../../../core/MusicMetadata.js"; import { MusicServiceIndicators } from "../musicServices/MusicServiceIndicators.js"; import { timeToHumanTimestamp } from "../../../core/TimeUtils.js"; import { CountryFlag } from "../Country.js"; import dayjs from "dayjs"; const trackPartials: MetadataPartials = { track: { label: 'Track + duration only', pick: ({ artists, album, albumArtists, albumCount, ...rest }) => rest }, artists: { label: 'Track + duration + artists', pick: ({ album, albumArtists, albumCount, ...rest }) => rest }, album: { label: 'Track + duration + album', pick: ({ artists, ...rest }) => rest }, }; export const TrackSearchResultItem = (props: { data: TrackSearchResult, onPick?: (val: TrackSearchResult) => void }) => { const { onPick } = props; const { track, album, albumCount, duration, artists = [] } = props.data; let albumContent: React.JSX.Element | undefined = undefined; if (album !== undefined) { const { albumType, albumTypeHint, country, date, name: albumName, metadata: albumMetadata } = album; const countryElm: React.JSX.Element | undefined = country !== undefined ? `Released in ${val}`}/> : undefined; let locationDateInfo: React.JSX.Element | undefined = undefined; if(countryElm !== undefined && date === undefined) { locationDateInfo = countryElm; } else if(countryElm !== undefined && date !== undefined) { locationDateInfo = {countryElm}{dayjs(date).format('YYYY')} } const albumHints: string[] = []; if(albumType !== undefined) { albumHints.push(albumType); } if(albumTypeHint !== undefined) { albumHints.push(albumTypeHint); } const andCount = albumCount !== undefined && albumCount > 1 ? ( and {albumCount} more albums... ) : undefined; albumContent = ( {albumName} {albumHints.length > 0 ? `(${albumHints.join(' -- ')})` : ''}{locationDateInfo} {andCount} ) } let artistTags: React.JSX.Element | undefined = undefined; if (artists.length > 0) { artistTags = } return ( {track?.name} {duration !== undefined ? ({timeToHumanTimestamp(duration * 1000)}) : undefined} {artistTags} {albumContent} ) } /** Play data to change when a track is selected. Artists and album are only included if the selected track has them. */ export type TrackOnChange = Pick; export interface TrackSearchProps { initial?: Credit onChange: (val: TrackOnChange) => void } const trackSearchResultToOnChange = (val: TrackOnChange): TrackOnChange => { // drops the search-only properties of a result and of the results nested in it const { meta, ...change } = playTrackDataSchema.parse(val); // an album always replaces album artists, even when it has none, so artists from a previous album are not kept return change.album !== undefined ? { ...change, albumArtists: change.albumArtists } : change; } export const TrackSearch = (props: TrackSearchProps) => { const { initial, onChange = (val) => console.log(val, 'Selected value for prop') } = props; const [selectedItem, setSelectedItem] = useState(initial ?? {name: ''}); const [debouncedQuery, setDebouncedQuery] = useDebouncedState(initial?.name ?? '', { wait: 500 }); const query = useQuery({ enabled: debouncedQuery !== '', ...tanQueries.metadata.track(debouncedQuery) }); const { collection, set } = useListCollection({ initialItems: query.data?.data ?? [], itemToString: (item) => item.track?.name ?? '', itemToValue: (item) => item.id, }); useEffect(() => { if (query.isSuccess) { set(query.data.data); } }, [query, set]) const doChange = useCallback((val: TrackOnChange) => { const change = trackSearchResultToOnChange(val); setSelectedItem(change.track ?? {name: ''}); onChange(change); },[setSelectedItem, onChange]); const services = selectedItem.metadata ?? []; let groupContent: React.JSX.Element | undefined = undefined; if(services.length > 0) { groupContent = } return ( doChange({ track: { name } })} onQueryChange={setDebouncedQuery} renderItem={(item, onPick) => } /> ); }