diff --git a/api/queries/favorites.ts b/api/queries/favorites.ts index 933550d9..34bc52f8 100644 --- a/api/queries/favorites.ts +++ b/api/queries/favorites.ts @@ -1,21 +1,20 @@ import { QueryKeys } from "../../enums/query-keys"; import { useQuery } from "@tanstack/react-query"; -import { fetchFavoriteAlbums, fetchFavoriteArtists, fetchFavoriteTracks, fetchUserData } from "./functions/favorites"; +import { fetchFavoriteAlbums, fetchFavoriteArtists, fetchFavoritePlaylists, fetchFavoriteTracks, fetchUserData } from "./functions/favorites"; export const useFavoriteArtists = () => useQuery({ queryKey: [QueryKeys.FavoriteArtists], - queryFn: () => { - - return fetchFavoriteArtists() - } + queryFn: () => fetchFavoriteArtists() }); export const useFavoriteAlbums = () => useQuery({ queryKey: [QueryKeys.FavoriteAlbums], - queryFn: () => { + queryFn: () => fetchFavoriteAlbums() +}); - return fetchFavoriteAlbums() - } +export const useFavoritePlaylists = () => useQuery({ + queryKey: [QueryKeys.FavoritePlaylists], + queryFn: () => fetchFavoritePlaylists() }); export const useFavoriteTracks = () => useQuery({ diff --git a/api/queries/functions/favorites.ts b/api/queries/functions/favorites.ts index 39afe52b..54eaf516 100644 --- a/api/queries/functions/favorites.ts +++ b/api/queries/functions/favorites.ts @@ -70,6 +70,38 @@ export function fetchFavoriteAlbums(): Promise { }) } +export function fetchFavoritePlaylists(): Promise { + console.debug(`Fetching user's favorite playlists`); + + return new Promise(async (resolver, reject) => { + getItemsApi(Client.api!) + .getItems({ + parentId: Client.library!.playlistLibraryId, + includeItemTypes: [ + BaseItemKind.Playlist + ], + isFavorite: true, + sortBy: [ + ItemSortBy.SortName + ], + sortOrder: [ + SortOrder.Ascending + ] + }) + .then((response) => { + if (response.data.Items) + resolver(response.data.Items) + + else + resolver([]) + }) + .catch((error) => { + console.error(error); + reject(error); + }); + }); +} + export function fetchFavoriteTracks(): Promise { console.debug(`Fetching user's favorite tracks`); diff --git a/components/Albums/screen.tsx b/components/Albums/screen.tsx index 47b452e2..79f74603 100644 --- a/components/Albums/screen.tsx +++ b/components/Albums/screen.tsx @@ -1,17 +1,12 @@ import React from "react" import { StackParamList } from "../types" -import { RouteProp } from "@react-navigation/native" -import { NativeStackNavigationProp } from "@react-navigation/native-stack" +import { NativeStackScreenProps } from "@react-navigation/native-stack" import Albums from "./component" -export default function AlbumsScreen({ - route, - navigation -} : { - route: RouteProp, - navigation: NativeStackNavigationProp -}) : React.JSX.Element { +export default function AlbumsScreen( + props: NativeStackScreenProps +) : React.JSX.Element { return ( - + ) } \ No newline at end of file diff --git a/components/Favorites/component.tsx b/components/Favorites/component.tsx index 417d75fa..5e53526e 100644 --- a/components/Favorites/component.tsx +++ b/components/Favorites/component.tsx @@ -9,6 +9,7 @@ import ArtistsScreen from "../Artists/screen"; import AlbumsScreen from "../Albums/screen"; import TracksScreen from "../Tracks/screen"; import DetailsScreen from "../ItemDetail/screen"; +import PlaylistsScreen from "../Playlists/screen"; const LibraryStack = createNativeStackNavigator(); @@ -82,6 +83,17 @@ export default function Library(): React.JSX.Element { }} /> + + void; name: string; + title?: string | undefined; circular?: boolean | undefined; size?: number; } @@ -13,6 +15,7 @@ interface IconButtonProps { export default function IconButton({ name, onPress, + title, circular, size } : IconButtonProps) : React.JSX.Element { @@ -37,7 +40,11 @@ export default function IconButton({ large name={name} color={"$color"} - /> + /> + + { title && ( + { title } + )} diff --git a/components/ItemDetail/component.tsx b/components/ItemDetail/component.tsx index 883df8fc..6e8ef9cf 100644 --- a/components/ItemDetail/component.tsx +++ b/components/ItemDetail/component.tsx @@ -58,7 +58,7 @@ export default function ItemDetail({ return ( - + - - - { item.Name ?? "Untitled Track" } - + {/* Item Name, Artist, Album, and Favorite Button */} + + + + { item.Name ?? "Untitled Track" } + - { - if (item.ArtistItems) { + { + if (item.ArtistItems) { + + if (isNested) + navigation.getParent()!.goBack(); + + navigation.goBack(); + navigation.push("Artist", { + artist: item.ArtistItems[0] + }); + } + }}> + { item.Artists?.join(", ") ?? "Unknown Artist"} + + + + { item.Album ?? "" } + + - if (isNested) - navigation.getParent()!.goBack(); - - navigation.goBack(); - navigation.push("Artist", { - artist: item.ArtistItems[0] - }); - } - }}> - { item.Artists?.join(", ") ?? "Unknown Artist"} - - - - { item.Album ?? "" } - - - - - + + + + - + - { options ?? } - + { options ?? } diff --git a/components/ItemDetail/helpers/TrackOptions.tsx b/components/ItemDetail/helpers/TrackOptions.tsx index 171230a0..7467065a 100644 --- a/components/ItemDetail/helpers/TrackOptions.tsx +++ b/components/ItemDetail/helpers/TrackOptions.tsx @@ -4,8 +4,10 @@ import Icon from "../../../components/Global/helpers/icon"; import { StackParamList } from "../../../components/types"; import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models"; import { NativeStackNavigationProp } from "@react-navigation/native-stack"; -import { XStack } from "tamagui"; +import { Spacer, XStack, YStack } from "tamagui"; import { QueuingType } from "../../../enums/queuing-type"; +import { useSafeAreaFrame } from "react-native-safe-area-context"; +import IconButton from "@/components/Global/helpers/icon-button"; export default function TrackOptions({ item, @@ -20,43 +22,53 @@ export default function TrackOptions({ const { data: album, isSuccess } = useItem(item.AlbumId ?? ""); const { useAddToQueue } = usePlayerContext(); + + const { width } = useSafeAreaFrame(); return ( - - { isSuccess && ( - + + + { isSuccess ? ( + { + + if (isNested) + navigation.getParent()!.goBack(); + + navigation.goBack(); + navigation.push("Album", { + album + }); + }} + /> + ) : ( + + )} + + { - - if (isNested) - navigation.getParent()!.goBack(); - - navigation.goBack(); - navigation.push("Album", { - album - }); + useAddToQueue.mutate({ + track: item, + queuingType: QueuingType.PlayingNext + }) }} /> - )} - { - useAddToQueue.mutate({ - track: item, - queuingType: QueuingType.PlayingNext - }) - }} - /> - - { - useAddToQueue.mutate({ - track: item - }) - }} - /> - + { + useAddToQueue.mutate({ + track: item + }) + }} + /> + + ) } \ No newline at end of file diff --git a/components/Playlists/component.tsx b/components/Playlists/component.tsx new file mode 100644 index 00000000..15a1ae6e --- /dev/null +++ b/components/Playlists/component.tsx @@ -0,0 +1,38 @@ +import { useFavoritePlaylists } from "@/api/queries/favorites"; +import { FlatList, RefreshControl } from "react-native-gesture-handler"; +import { useSafeAreaFrame } from "react-native-safe-area-context"; +import { ItemCard } from "../Global/components/item-card"; +import { PlaylistsProps } from "../types"; + +export default function Playlists({ navigation }: PlaylistsProps) : React.JSX.Element { + + const { data: playlists, isPending, refetch } = useFavoritePlaylists(); + + const { width } = useSafeAreaFrame(); + + return ( + + } + renderItem={({ index, item: playlist }) => { + return ( + { + navigation.push("Playlist", { playlist }) + }} + width={width / 2.1} + /> + ) + }} + /> + ) +} \ No newline at end of file diff --git a/components/Playlists/screen.tsx b/components/Playlists/screen.tsx new file mode 100644 index 00000000..3d42de05 --- /dev/null +++ b/components/Playlists/screen.tsx @@ -0,0 +1,12 @@ +import { NativeStackScreenProps } from "@react-navigation/native-stack"; +import { StackParamList } from "../types"; +import Playlists from "./component"; +import React from "react"; + +export default function PlaylistsScreen( + props: NativeStackScreenProps +) : React.JSX.Element { + return ( + + ) +} \ No newline at end of file diff --git a/components/types.tsx b/components/types.tsx index ad16d86d..f381ec48 100644 --- a/components/types.tsx +++ b/components/types.tsx @@ -62,6 +62,8 @@ export type ArtistsProps = NativeStackScreenProps; export type AlbumsProps = NativeStackScreenProps; +export type PlaylistsProps = NativeStackScreenProps; + export type TracksProps = NativeStackScreenProps; export type GenresProps = NativeStackScreenProps; diff --git a/enums/query-keys.ts b/enums/query-keys.ts index b90fa46c..982cb908 100644 --- a/enums/query-keys.ts +++ b/enums/query-keys.ts @@ -38,4 +38,5 @@ export enum QueryKeys { Item = "Item", Search = "Search", SearchSuggestions = "SearchSuggestions", + FavoritePlaylists = "FavoritePlaylists", } \ No newline at end of file