mirror of
https://github.com/Jellify-Music/App.git
synced 2026-02-15 23:18:32 -06:00
search ui changes to prioritize artists
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -10,7 +10,7 @@ interface HorizontalCardListProps extends FlatListProps<BaseItemDto> {
|
||||
* 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 ? (
|
||||
<IconCard
|
||||
name={
|
||||
squared
|
||||
? "arrow-right-box"
|
||||
: "arrow-right-circle"
|
||||
}
|
||||
circular={!squared}
|
||||
caption="See More"
|
||||
onPress={onSeeMore}
|
||||
/>
|
||||
return props.data && onSeeMore ? (
|
||||
<IconCard
|
||||
name={
|
||||
squared
|
||||
? "arrow-right-box"
|
||||
: "arrow-right-circle"
|
||||
}
|
||||
circular={!squared}
|
||||
caption="See More"
|
||||
onPress={onSeeMore}
|
||||
/>
|
||||
) : undefined}
|
||||
}
|
||||
removeClippedSubviews
|
||||
|
||||
@@ -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 (
|
||||
<View
|
||||
alignItems="center"
|
||||
margin={5}
|
||||
>
|
||||
<TamaguiCard
|
||||
size="$4"
|
||||
size={"$12"}
|
||||
height={props.size}
|
||||
width={props.size}
|
||||
backgroundColor={getToken("$color.amethyst")}
|
||||
borderRadius={props.squared ? 5 : dimensions.width}
|
||||
circular={!props.squared}
|
||||
borderRadius={props.squared ? 5 : 'unset'}
|
||||
animation="bouncy"
|
||||
hoverStyle={props.onPress ? { scale: 0.925 } : {}}
|
||||
pressStyle={props.onPress ? { scale: 0.875 } : {}}
|
||||
width={props.width ?? 150}
|
||||
height={props.width ?? 150}
|
||||
{...props}
|
||||
>
|
||||
<TamaguiCard.Header>
|
||||
@@ -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
|
||||
}}
|
||||
/>
|
||||
</TamaguiCard.Background>
|
||||
</TamaguiCard>
|
||||
{ props.caption && (
|
||||
<View
|
||||
<YStack
|
||||
alignContent="center"
|
||||
alignItems="center"
|
||||
width={dimensions.width}
|
||||
maxWidth={props.size}
|
||||
>
|
||||
<Text
|
||||
bold
|
||||
@@ -89,7 +86,7 @@ export function ItemCard(props: CardProps) {
|
||||
{ props.subCaption }
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
</YStack>
|
||||
)}
|
||||
</View>
|
||||
)
|
||||
|
||||
@@ -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}
|
||||
>
|
||||
<Card.Header>
|
||||
<H4 color={getTokens().color.purpleDark}>{ caption ?? "" }</H4>
|
||||
<H5 color={getTokens().color.purpleDark}>{ caption ?? "" }</H5>
|
||||
<Icon
|
||||
color={getTokens().color.purpleDark.val}
|
||||
name={name}
|
||||
|
||||
@@ -26,6 +26,7 @@ export default function Playlists({ navigation }: { navigation: NativeStackNavig
|
||||
renderItem={({ item: playlist }) =>
|
||||
<ItemCard
|
||||
item={playlist}
|
||||
size={"$11"}
|
||||
squared
|
||||
caption={playlist.Name ?? "Untitled Playlist"}
|
||||
onPress={() => {
|
||||
|
||||
@@ -36,10 +36,10 @@ export default function RecentlyPlayed({
|
||||
}}
|
||||
renderItem={({ index, item: recentlyPlayedTrack }) =>
|
||||
<ItemCard
|
||||
size={"$12"}
|
||||
caption={recentlyPlayedTrack.Name}
|
||||
subCaption={`${recentlyPlayedTrack.Artists?.join(", ")}`}
|
||||
squared
|
||||
width={150}
|
||||
item={recentlyPlayedTrack}
|
||||
onPress={() => {
|
||||
usePlayNewQueue.mutate({
|
||||
|
||||
@@ -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}
|
||||
>
|
||||
<Icon
|
||||
name="chevron-down"
|
||||
onPress={() => {
|
||||
navigation.goBack();
|
||||
}}
|
||||
small
|
||||
/>
|
||||
{/**
|
||||
* Android needs a dismiss chevron here
|
||||
*/}
|
||||
{ Platform.OS === 'android' ? (
|
||||
<Icon
|
||||
name="chevron-down"
|
||||
onPress={() => {
|
||||
navigation.goBack();
|
||||
}}
|
||||
small
|
||||
/>
|
||||
|
||||
) : (
|
||||
<Spacer />
|
||||
)}
|
||||
|
||||
<Spacer />
|
||||
|
||||
|
||||
@@ -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) && (
|
||||
<YStack>
|
||||
<H3>Results</H3>
|
||||
|
||||
<HorizontalCardList
|
||||
data={items?.filter(result => result.Type === 'MusicArtist')}
|
||||
renderItem={({ item: artistResult }) => {
|
||||
return (
|
||||
<ItemCard
|
||||
item={artistResult}
|
||||
onPress={() => {
|
||||
navigation.push('Artist', {
|
||||
artist: artistResult
|
||||
})
|
||||
}}
|
||||
size={"$8"}
|
||||
caption={artistResult.Name ?? "Untitled Artist"}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</YStack>
|
||||
)}
|
||||
</YStack>
|
||||
)}
|
||||
@@ -79,7 +103,8 @@ export default function Search({
|
||||
</YStack>
|
||||
)
|
||||
}}
|
||||
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 }) =>
|
||||
<Item item={item} queueName={searchString ?? "Search"} navigation={navigation} />
|
||||
|
||||
@@ -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 (
|
||||
<FlatList
|
||||
data={props.suggestions}
|
||||
// Artists are displayed in the header, so we'll filter them out here
|
||||
data={props.suggestions?.filter(suggestion => suggestion.Type !== 'MusicArtist')}
|
||||
ListHeaderComponent={(
|
||||
<H3>Suggestions</H3>
|
||||
<YStack>
|
||||
<H3>Suggestions</H3>
|
||||
|
||||
<HorizontalCardList
|
||||
data={props.suggestions?.filter(suggestion => suggestion.Type === 'MusicArtist')}
|
||||
renderItem={({ item: suggestedArtist }) => {
|
||||
return (
|
||||
<ItemCard
|
||||
item={suggestedArtist}
|
||||
onPress={() => {
|
||||
props.navigation.push('Artist', {
|
||||
artist: suggestedArtist
|
||||
})
|
||||
}}
|
||||
size={"$8"}
|
||||
caption={suggestedArtist.Name ?? "Untitled Artist"}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</YStack>
|
||||
)}
|
||||
ListEmptyComponent={(
|
||||
<Text textAlign="center">Wake now, discover that you are the eyes of the world...</Text>
|
||||
|
||||
Reference in New Issue
Block a user