diff --git a/api/queries/functions/search.ts b/api/queries/functions/search.ts index 33fb3673..01ebf1a5 100644 --- a/api/queries/functions/search.ts +++ b/api/queries/functions/search.ts @@ -23,12 +23,18 @@ export async function fetchSearchResults(searchString: string | undefined) : Pro searchTerm: trim(searchString), recursive: true, includeItemTypes: [ + 'MusicArtist', 'Audio', 'MusicAlbum', - 'MusicArtist', 'Playlist' ], limit: QueryConfig.limits.search, + sortBy: [ + 'IsFolder' + ], + sortOrder: [ + 'Descending' + ] }) .then((response) => { if (response.data.Items) diff --git a/components/Global/components/horizontal-list.tsx b/components/Global/components/horizontal-list.tsx index bf3d8d18..864362a4 100644 --- a/components/Global/components/horizontal-list.tsx +++ b/components/Global/components/horizontal-list.tsx @@ -10,7 +10,7 @@ interface HorizontalCardListProps extends FlatListProps { * we cut it off and display a "Show More" card */ cutoff?: number | undefined; - onSeeMore: () => void; + onSeeMore?: () => void | undefined; } /** @@ -31,17 +31,17 @@ export default function HorizontalCardList({ data={props.data} renderItem={props.renderItem} ListFooterComponent={() => { - return props.data ? ( - + return props.data && onSeeMore ? ( + ) : undefined} } removeClippedSubviews diff --git a/components/Global/components/item-card.tsx b/components/Global/components/item-card.tsx index ce0578ac..085a6d91 100644 --- a/components/Global/components/item-card.tsx +++ b/components/Global/components/item-card.tsx @@ -1,6 +1,6 @@ import React from "react"; import type { CardProps as TamaguiCardProps } from "tamagui" -import { getToken, Card as TamaguiCard, View } from "tamagui"; +import { getToken, Card as TamaguiCard, View, YStack } from "tamagui"; import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models"; import { Text } from "../helpers/text"; import { Image } from "expo-image"; @@ -16,24 +16,21 @@ interface CardProps extends TamaguiCardProps { export function ItemCard(props: CardProps) { - const dimensions = props.width && typeof(props.width) === "number" ? { width: props.width, height: props.width } : { width: 150, height: 150 }; - - const logoDimensions = props.width && typeof(props.width) === "number" ? { width: props.width / 2, height: props.width / 6 }: { width: 100, height: 75 }; - return ( @@ -59,18 +56,18 @@ export function ItemCard(props: CardProps) { )} placeholder={props.item.ImageBlurHashes && props.item.ImageBlurHashes["Primary"] ? props.item.ImageBlurHashes["Primary"][0] : undefined} style={{ - width: dimensions.width, - height: dimensions.height, - borderRadius: props.squared ? 5 : dimensions.width + width: '100%', + height: '100%', + borderRadius: props.squared ? 2 : 100 }} /> { props.caption && ( - )} - + )} ) diff --git a/components/Global/helpers/icon-card.tsx b/components/Global/helpers/icon-card.tsx index d4206156..9318a161 100644 --- a/components/Global/helpers/icon-card.tsx +++ b/components/Global/helpers/icon-card.tsx @@ -1,5 +1,5 @@ import { Card, getTokens, View } from "tamagui"; -import { H2, H4 } from "./text"; +import { H2, H4, H5 } from "./text"; import Icon from "./icon"; interface IconCardProps { @@ -30,12 +30,12 @@ export default function IconCard({ borderRadius={circular ? 300 : 5} hoverStyle={{ scale: 0.925 }} pressStyle={{ scale: 0.875 }} - width={width ? width : 150} - height={width ? width : 150} + width={width ? width : "$12"} + height={width ? width : "$12"} onPress={onPress} > -

{ caption ?? "" }

+
{ caption ?? "" }
{ diff --git a/components/Home/helpers/recently-played.tsx b/components/Home/helpers/recently-played.tsx index a7a7b23d..3ada45e6 100644 --- a/components/Home/helpers/recently-played.tsx +++ b/components/Home/helpers/recently-played.tsx @@ -36,10 +36,10 @@ export default function RecentlyPlayed({ }} renderItem={({ index, item: recentlyPlayedTrack }) => { usePlayNewQueue.mutate({ diff --git a/components/ItemDetail/component.tsx b/components/ItemDetail/component.tsx index e488146e..d2d80f5e 100644 --- a/components/ItemDetail/component.tsx +++ b/components/ItemDetail/component.tsx @@ -14,6 +14,7 @@ import { Image } from "expo-image"; import { getImageApi } from "@jellyfin/sdk/lib/utils/api"; import Client from "../../api/client"; import Icon from "../Global/helpers/icon"; +import { Platform } from "react-native"; export default function ItemDetail({ item, @@ -78,13 +79,21 @@ export default function ItemDetail({ minHeight={width / 1.5} minWidth={width / 1.5} > - { - navigation.goBack(); - }} - small - /> + {/** + * Android needs a dismiss chevron here + */} + { Platform.OS === 'android' ? ( + { + navigation.goBack(); + }} + small + /> + + ) : ( + + )} diff --git a/components/Search/component.tsx b/components/Search/component.tsx index 9511d76b..0ff8beda 100644 --- a/components/Search/component.tsx +++ b/components/Search/component.tsx @@ -7,11 +7,13 @@ import { QueryKeys } from "../../enums/query-keys"; import { fetchSearchResults } from "../../api/queries/functions/search"; import { useQuery } from "@tanstack/react-query"; import { FlatList } from "react-native"; -import { H3, Text } from "../Global/helpers/text"; +import { H3 } from "../Global/helpers/text"; import { fetchSearchSuggestions } from "../../api/queries/functions/suggestions"; import { Spinner, YStack } from "tamagui"; import Suggestions from "./suggestions"; -import { isEmpty, isUndefined } from "lodash"; +import { isEmpty } from "lodash"; +import HorizontalCardList from "../Global/components/horizontal-list"; +import { ItemCard } from "../Global/components/item-card"; export default function Search({ navigation @@ -37,14 +39,16 @@ export default function Search({ return () => { clearTimeout(timeout); - timeout = setTimeout(() => refetch, 1000) + timeout = setTimeout(() => { + refetch(); + refetchSuggestions(); + }, 1000) } }, []); const handleSearchStringUpdate = (value: string | undefined) => { setSearchString(value) search(); - refetchSuggestions(); } return ( @@ -60,7 +64,27 @@ export default function Search({ /> { !isEmpty(items) && ( +

Results

+ + result.Type === 'MusicArtist')} + renderItem={({ item: artistResult }) => { + return ( + { + navigation.push('Artist', { + artist: artistResult + }) + }} + size={"$8"} + caption={artistResult.Name ?? "Untitled Artist"} + /> + ) + }} + /> +
)} )} @@ -79,7 +103,8 @@ export default function Search({ ) }} - data={items} + // We're displaying artists separately so we're going to filter them out here + data={items?.filter((result) => result.Type !== 'MusicArtist')} refreshing={fetchingResults} renderItem={({ item }) => diff --git a/components/Search/suggestions.tsx b/components/Search/suggestions.tsx index c41f87ed..4f18127a 100644 --- a/components/Search/suggestions.tsx +++ b/components/Search/suggestions.tsx @@ -1,9 +1,12 @@ import { NativeStackNavigationProp } from "@react-navigation/native-stack"; -import { FlatList } from "react-native"; +import { FlatList, RefreshControl } from "react-native"; import { StackParamList } from "../types"; import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models"; import Item from "../Global/components/item"; import { H3, Text } from "../Global/helpers/text"; +import { YStack } from "tamagui"; +import { ItemCard } from "../Global/components/item-card"; +import HorizontalCardList from "../Global/components/horizontal-list"; interface SuggestionsProps { suggestions: BaseItemDto[] | undefined; @@ -16,9 +19,30 @@ export default function Suggestions( return ( suggestion.Type !== 'MusicArtist')} ListHeaderComponent={( -

Suggestions

+ +

Suggestions

+ + suggestion.Type === 'MusicArtist')} + renderItem={({ item: suggestedArtist }) => { + return ( + { + props.navigation.push('Artist', { + artist: suggestedArtist + }) + }} + size={"$8"} + caption={suggestedArtist.Name ?? "Untitled Artist"} + /> + ) + }} + /> +
)} ListEmptyComponent={( Wake now, discover that you are the eyes of the world...