mirror of
https://github.com/Jellify-Music/App.git
synced 2026-02-16 07:28:34 -06:00
Merge pull request #123 from anultravioletaurora/9-again-implement-playlist-crud
9 again implement playlist crud
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { BaseItemDto, MediaType } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import Client from "../../../api/client";
|
||||
import { getPlaylistsApi } from "@jellyfin/sdk/lib/utils/api";
|
||||
import { getLibraryApi, getPlaylistsApi } from "@jellyfin/sdk/lib/utils/api";
|
||||
|
||||
export async function addToPlaylist(track: BaseItemDto, playlist: BaseItemDto) {
|
||||
|
||||
@@ -40,7 +40,7 @@ export async function reorderPlaylist(playlistId: string, itemId: string, to: nu
|
||||
}
|
||||
|
||||
export async function createPlaylist(name: string) {
|
||||
console.debug("Creating new playlist");
|
||||
console.debug("Creating new playlist...");
|
||||
|
||||
return getPlaylistsApi(Client.api!)
|
||||
.createPlaylist({
|
||||
@@ -52,6 +52,15 @@ export async function createPlaylist(name: string) {
|
||||
});
|
||||
}
|
||||
|
||||
export async function deletePlaylist(playlistId: string) {
|
||||
console.debug("Deleting playlist...");
|
||||
|
||||
return getLibraryApi(Client.api!)
|
||||
.deleteItem({
|
||||
itemId: playlistId
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a Jellyfin playlist with the provided options.
|
||||
*
|
||||
|
||||
@@ -20,7 +20,7 @@ export function fetchUserPlaylists(
|
||||
fields: [
|
||||
"Path"
|
||||
],
|
||||
sortBy: defaultSorting.concat(sortBy),
|
||||
sortBy: sortBy.concat(defaultSorting),
|
||||
sortOrder: [
|
||||
SortOrder.Ascending
|
||||
]
|
||||
|
||||
@@ -18,7 +18,7 @@ interface LabelProps {
|
||||
|
||||
export function Label(props: LabelProps): React.JSX.Element {
|
||||
return (
|
||||
<TamaguiLabel htmlFor={props.htmlFor} justifyContent="flex-end">{ props.children }</TamaguiLabel>
|
||||
<TamaguiLabel fontWeight={600} htmlFor={props.htmlFor} justifyContent="flex-end">{ props.children }</TamaguiLabel>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import { StackParamList } from "../types";
|
||||
import { ScrollView, RefreshControl } from "react-native";
|
||||
import { SafeAreaView } from "react-native-safe-area-context";
|
||||
import { YStack, XStack, Separator } from "tamagui";
|
||||
import Playlists from "./helpers/playlists";
|
||||
import RecentArtists from "./helpers/recent-artists";
|
||||
import RecentlyPlayed from "./helpers/recently-played";
|
||||
import { useHomeContext } from "./provider";
|
||||
import { H3 } from "../Global/helpers/text";
|
||||
import Avatar from "../Global/components/avatar";
|
||||
import Client from "../../api/client";
|
||||
import { usePlayerContext } from "../../player/provider";
|
||||
import { useEffect } from "react";
|
||||
@@ -30,7 +28,6 @@ export function ProvidedHome({
|
||||
])
|
||||
|
||||
return (
|
||||
<SafeAreaView edges={["top", "right", "left"]}>
|
||||
<ScrollView
|
||||
contentInsetAdjustmentBehavior="automatic"
|
||||
refreshControl={
|
||||
@@ -57,6 +54,5 @@ export function ProvidedHome({
|
||||
<Playlists navigation={navigation}/>
|
||||
</YStack>
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
@@ -26,10 +26,10 @@ export default function Home(): React.JSX.Element {
|
||||
name="Home"
|
||||
component={ProvidedHome}
|
||||
options={{
|
||||
headerLargeTitle: true,
|
||||
headerLargeTitleStyle: {
|
||||
fontFamily: 'Aileron-Bold'
|
||||
}
|
||||
// headerLargeTitle: true,
|
||||
// headerLargeTitleStyle: {
|
||||
// fontFamily: 'Aileron-Bold'
|
||||
// }
|
||||
}}
|
||||
/>
|
||||
|
||||
|
||||
@@ -35,9 +35,9 @@ export default function AddPlaylist({
|
||||
|
||||
navigation.goBack();
|
||||
|
||||
// Refresh user playlists component on home screen
|
||||
// Refresh user playlists component in library
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [QueryKeys.UserPlaylists]
|
||||
queryKey: [QueryKeys.FavoritePlaylists]
|
||||
});
|
||||
},
|
||||
onError: () => {
|
||||
|
||||
57
components/Library/components/delete-playlist.tsx
Normal file
57
components/Library/components/delete-playlist.tsx
Normal file
@@ -0,0 +1,57 @@
|
||||
import { View, XStack } from "tamagui";
|
||||
import { DeletePlaylistProps } from "../../../components/types";
|
||||
import Button from "../../../components/Global/helpers/button";
|
||||
import { Text } from "../../../components/Global/helpers/text";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { deletePlaylist } from "../../../api/mutations/functions/playlists";
|
||||
import { trigger } from "react-native-haptic-feedback";
|
||||
import { queryClient } from "../../../constants/query-client";
|
||||
import { QueryKeys } from "../../../enums/query-keys";
|
||||
|
||||
import * as Burnt from "burnt";
|
||||
|
||||
export default function DeletePlaylist(
|
||||
{
|
||||
navigation,
|
||||
route
|
||||
}: DeletePlaylistProps) : React.JSX.Element {
|
||||
|
||||
|
||||
const useDeletePlaylist = useMutation({
|
||||
mutationFn: (playlist: BaseItemDto) => deletePlaylist(playlist.Id!),
|
||||
onSuccess: (data, playlist) => {
|
||||
trigger("notificationSuccess");
|
||||
|
||||
navigation.goBack();
|
||||
navigation.goBack();
|
||||
Burnt.alert({
|
||||
title: `Playlist deleted`,
|
||||
message: `Deleted ${playlist.Name ?? "Untitled Playlist"}`,
|
||||
duration: 1,
|
||||
preset: 'done'
|
||||
});
|
||||
|
||||
// Refresh user playlists component in library
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [QueryKeys.FavoritePlaylists]
|
||||
});
|
||||
|
||||
|
||||
},
|
||||
onError: () => {
|
||||
trigger("notificationError");
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<View marginHorizontal={"$2"}>
|
||||
<Text bold textAlign="center">{`Delete playlist ${route.params.playlist.Name ?? "Untitled Playlist"}?`}</Text>
|
||||
<XStack justifyContent="space-evenly">
|
||||
<Button onPress={() => navigation.goBack()}>Cancel</Button>
|
||||
<Button danger onPress={() => useDeletePlaylist.mutate(route.params.playlist)}>Delete</Button>
|
||||
</XStack>
|
||||
</View>
|
||||
|
||||
)
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import TracksScreen from "../Tracks/screen";
|
||||
import DetailsScreen from "../ItemDetail/screen";
|
||||
import PlaylistsScreen from "../Playlists/screen";
|
||||
import AddPlaylist from "./components/add-playlist";
|
||||
import DeletePlaylist from "./components/delete-playlist";
|
||||
|
||||
const Stack = createNativeStackNavigator<StackParamList>();
|
||||
|
||||
@@ -111,6 +112,14 @@ export default function LibraryStack(): React.JSX.Element {
|
||||
title: "Add Playlist",
|
||||
}}
|
||||
/>
|
||||
|
||||
<Stack.Screen
|
||||
name="DeletePlaylist"
|
||||
component={DeletePlaylist}
|
||||
options={{
|
||||
title: "Delete Playlist"
|
||||
}}
|
||||
/>
|
||||
</Stack.Group>
|
||||
|
||||
</Stack.Navigator>
|
||||
|
||||
@@ -73,7 +73,7 @@ export function Miniplayer({ navigation }: { navigation : NavigationHelpers<Para
|
||||
color={theme.borderColor.val}
|
||||
name="skip-next"
|
||||
onPress={() => useSkip.mutate(undefined)}
|
||||
/>
|
||||
/>
|
||||
</XStack>
|
||||
</XStack>
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { NativeStackNavigationProp } from "@react-navigation/native-stack";
|
||||
import { StackParamList } from "../types";
|
||||
import { getTokens, Separator, XStack, YStack } from "tamagui";
|
||||
import { getToken, Separator, Spacer, XStack, YStack } from "tamagui";
|
||||
import { useItemTracks } from "../../api/queries/tracks";
|
||||
import { RunTimeTicks } from "../Global/helpers/time-codes";
|
||||
import { H4, H5, Text } from "../Global/helpers/text";
|
||||
@@ -45,14 +45,26 @@ export default function Playlist({
|
||||
navigation.setOptions({
|
||||
headerRight: () => {
|
||||
return (
|
||||
<Icon
|
||||
color={editing
|
||||
? getTokens().color.telemagenta.val
|
||||
: getTokens().color.white.val
|
||||
}
|
||||
name={editing ? 'check' : 'pencil'}
|
||||
onPress={() => setEditing(!editing)}
|
||||
/>
|
||||
|
||||
<XStack justifyContent="space-between">
|
||||
|
||||
{ editing && (
|
||||
<Icon
|
||||
color={getToken("$color.danger")}
|
||||
name="delete-sweep-outline" // otherwise use "delete-circle"
|
||||
onPress={() => navigation.navigate("DeletePlaylist", { playlist })}
|
||||
/>
|
||||
|
||||
)}
|
||||
|
||||
<Spacer />
|
||||
|
||||
<Icon
|
||||
color={getToken("$color.amethyst")}
|
||||
name={editing ? 'content-save-outline' : 'pencil'}
|
||||
onPress={() => setEditing(!editing)}
|
||||
/>
|
||||
</XStack>
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
4
components/types.d.ts
vendored
4
components/types.d.ts
vendored
@@ -26,6 +26,9 @@ export type StackParamList = {
|
||||
Tracks: undefined;
|
||||
Genres: undefined;
|
||||
Playlists: undefined;
|
||||
DeletePlaylist: {
|
||||
playlist: BaseItemDto
|
||||
}
|
||||
|
||||
Search: undefined;
|
||||
|
||||
@@ -84,6 +87,7 @@ export type ArtistsProps = NativeStackScreenProps<StackParamList, "Artists">;
|
||||
export type AlbumsProps = NativeStackScreenProps<StackParamList, "Albums">;
|
||||
|
||||
export type FavoritePlaylistsProps = NativeStackScreenProps<StackParamList, "Playlists">;
|
||||
export type DeletePlaylistProps = NativeStackScreenProps<StackParamList, "DeletePlaylist">;
|
||||
|
||||
export type FavoriteTracksProps = NativeStackScreenProps<StackParamList, "Tracks">;
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import { headingFont, bodyFont } from './fonts.config'
|
||||
const tokens = createTokens({
|
||||
...TamaguiTokens,
|
||||
color: {
|
||||
danger: "#ff0000",
|
||||
purpleDark: "#0C0622",
|
||||
purple: "#100538",
|
||||
purpleGray: "#66617B",
|
||||
|
||||
Reference in New Issue
Block a user