persisting queue objects

This commit is contained in:
Violet Caulfield
2025-02-12 23:29:21 -06:00
parent 38a8d0db5b
commit e8d5a5f0ab
9 changed files with 67 additions and 45 deletions
+6 -5
View File
@@ -10,13 +10,14 @@ import { NativeStackNavigationProp } from "@react-navigation/native-stack";
import { StackParamList } from "../../../components/types";
import { QueuingType } from "../../../enums/queuing-type";
import BlurhashedImage from "./blurhashed-image";
import { Queue } from "../../../player/types/queue-item";
interface TrackProps {
track: BaseItemDto;
navigation: NativeStackNavigationProp<StackParamList>;
tracklist?: BaseItemDto[] | undefined;
index?: number | undefined;
queueName?: string | undefined;
queue?: Queue;
showArtwork?: boolean | undefined;
onPress?: () => void | undefined;
onLongPress?: () => void | undefined;
@@ -32,7 +33,7 @@ export default function Track({
tracklist,
navigation,
index,
queueName,
queue,
showArtwork,
onPress,
onLongPress,
@@ -45,7 +46,7 @@ export default function Track({
const theme = useTheme();
const { width } = useSafeAreaFrame();
const { nowPlaying, queue, usePlayNewQueue } = usePlayerContext();
const { nowPlaying, playQueue, usePlayNewQueue } = usePlayerContext();
const isPlaying = nowPlaying?.item.Id === track.Id;
@@ -62,8 +63,8 @@ export default function Track({
usePlayNewQueue.mutate({
track,
index,
tracklist: tracklist ?? queue.map((track) => track.item),
queueName: queueName ? queueName : track.Album ? track.Album! : "Queue",
tracklist: tracklist ?? playQueue.map((track) => track.item),
queue: queue ? queue : "Queue",
queuingType: QueuingType.FromSelection
});
}
+2 -2
View File
@@ -34,7 +34,7 @@ export default function RecentlyPlayed({
track: recentTracks[0],
index: 0,
tracklist: recentTracks,
queueName: "Recently Played",
queue: "Recently Played",
queuingType: QueuingType.FromSelection
});
}}
@@ -56,7 +56,7 @@ export default function RecentlyPlayed({
track: recentlyPlayedTrack,
index: index,
tracklist: recentTracks,
queueName: "Recently Played",
queue: "Recently Played",
queuingType: QueuingType.FromSelection
});
}}
+11 -2
View File
@@ -16,6 +16,7 @@ import { ProgressMultiplier, TextTickerConfig } from "../component.config";
import { toUpper } from "lodash";
import { trigger } from "react-native-haptic-feedback";
import { Gesture, GestureDetector } from "react-native-gesture-handler";
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
const scrubGesture = Gesture.Pan();
@@ -34,7 +35,8 @@ export default function PlayerScreen({
useSeekTo,
useSkip,
usePrevious,
queueName,
playQueue,
queue
} = usePlayerContext();
const [seeking, setSeeking] = useState<boolean>(false);
@@ -92,7 +94,14 @@ export default function PlayerScreen({
>
<Text>Playing from</Text>
<TextTicker {...TextTickerConfig}>
<Text bold>{ queueName ?? "Queue"}</Text>
<Text bold>
{
// If the Queue is a BaseItemDto, display the name of it
typeof(queue) === 'object'
? (queue as BaseItemDto).Name ?? "Untitled"
: queue
}
</Text>
</TextTicker>
</YStack>
+1 -1
View File
@@ -12,7 +12,7 @@ import { FadeIn, FadeOut, ReduceMotion, SequencedTransition } from "react-native
export default function Queue({ navigation }: { navigation: NativeStackNavigationProp<StackParamList>}): React.JSX.Element {
const { width } = useSafeAreaFrame();
const { queue, useClearQueue, useRemoveFromQueue, useReorderQueue, useSkip, nowPlaying } = usePlayerContext();
const { playQueue: queue, useClearQueue, useRemoveFromQueue, useReorderQueue, useSkip, nowPlaying } = usePlayerContext();
navigation.setOptions({
headerRight: () => {
+1 -1
View File
@@ -154,7 +154,7 @@ export default function Playlist({
track={track}
tracklist={tracks!}
index={index}
queueName={playlist.Name ?? "Untitled Playlist"}
queue={playlist}
showArtwork
onLongPress={editing ? drag : undefined}
/>
+2 -1
View File
@@ -3,5 +3,6 @@ export enum MMKVStorageKeys {
Server = "SERVER",
User = "USER",
Library = "LIBRARY",
NowPlaying = "NowPlaying"
NowPlaying = "NowPlaying",
Queue = "Queue"
}
+2 -1
View File
@@ -1,12 +1,13 @@
import { JellifyTrack } from "../types/JellifyTrack";
import { QueuingType } from "../enums/queuing-type";
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
import { Queue } from "./types/queue-item";
export interface QueueMutation {
track: BaseItemDto;
index?: number | undefined;
tracklist: BaseItemDto[];
queueName: string;
queue: Queue;
queuingType?: QueuingType | undefined;
}
+39 -32
View File
@@ -18,14 +18,15 @@ import { convertRunTimeTicksToSeconds } from "../helpers/runtimeticks";
import Client from "../api/client";
import { AddToQueueMutation, QueueMutation, QueueOrderMutation } from "./interfaces";
import { Section } from "../components/Player/types";
import { Queue } from "./types/queue-item";
interface PlayerContext {
initialized: boolean;
nowPlayingIsFavorite: boolean;
setNowPlayingIsFavorite: React.Dispatch<SetStateAction<boolean>>;
nowPlaying: JellifyTrack | undefined;
queue: JellifyTrack[];
queueName: string | undefined;
playQueue: JellifyTrack[];
queue: Queue;
getQueueSectionData: () => Section[];
useAddToQueue: UseMutationResult<void, Error, AddToQueueMutation, unknown>;
useClearQueue: UseMutationResult<void, Error, void, unknown>;
@@ -54,9 +55,9 @@ const PlayerContextInitializer = () => {
const [nowPlaying, setNowPlaying] = useState<JellifyTrack | undefined>(nowPlayingJson ? JSON.parse(nowPlayingJson) : undefined);
const [isSkipping, setIsSkipping] = useState<boolean>(false);
const [queue, setQueue] = useState<JellifyTrack[]>(queueJson ? JSON.parse(queueJson) : []);
const [playQueue, setPlayQueue] = useState<JellifyTrack[]>(queueJson ? JSON.parse(queueJson) : []);
const [queueName, setQueueName] = useState<string | undefined>(undefined);
const [queue, setQueue] = useState<Queue>("Recently Played");
//#endregion State
@@ -74,7 +75,7 @@ const PlayerContextInitializer = () => {
return Object.keys(QueuingType).map((type) => {
return {
title: type,
data: queue.filter(track => track.QueuingType === type)
data: playQueue.filter(track => track.QueuingType === type)
} as Section
});
}
@@ -82,26 +83,26 @@ const PlayerContextInitializer = () => {
const resetQueue = async (hideMiniplayer?: boolean | undefined) => {
console.debug("Clearing queue")
await TrackPlayer.reset();
setQueue([]);
setPlayQueue([]);
}
const addToQueue = async (tracks: JellifyTrack[]) => {
const insertIndex = await findPlayQueueIndexStart(queue);
const insertIndex = await findPlayQueueIndexStart(playQueue);
console.debug(`Adding ${tracks.length} to queue at index ${insertIndex}`)
await TrackPlayer.add(tracks, insertIndex);
setQueue(await getQueue() as JellifyTrack[])
setPlayQueue(await getQueue() as JellifyTrack[])
}
const addToNext = async (tracks: JellifyTrack[]) => {
const insertIndex = await findPlayNextIndexStart(queue);
const insertIndex = await findPlayNextIndexStart(playQueue);
console.debug(`Adding ${tracks.length} to queue at index ${insertIndex}`);
await TrackPlayer.add(tracks, insertIndex);
setQueue(await getQueue() as JellifyTrack[]);
setPlayQueue(await getQueue() as JellifyTrack[]);
}
//#endregion Functions
@@ -124,7 +125,7 @@ const PlayerContextInitializer = () => {
await TrackPlayer.remove([index]);
setQueue(await TrackPlayer.getQueue() as JellifyTrack[])
setPlayQueue(await TrackPlayer.getQueue() as JellifyTrack[])
}
})
@@ -134,13 +135,13 @@ const PlayerContextInitializer = () => {
await TrackPlayer.removeUpcomingTracks();
setQueue(await getQueue() as JellifyTrack[]);
setPlayQueue(await getQueue() as JellifyTrack[]);
}
});
const useReorderQueue = useMutation({
mutationFn: async (mutation : QueueOrderMutation) => {
setQueue(mutation.newOrder);
setPlayQueue(mutation.newOrder);
await TrackPlayer.move(mutation.from, mutation.to);
}
})
@@ -173,13 +174,13 @@ const PlayerContextInitializer = () => {
trigger("impactMedium")
if (!isUndefined(index)) {
setIsSkipping(true);
setNowPlaying(queue[index]);
setNowPlaying(playQueue[index]);
await skip(index);
setIsSkipping(false);
}
else {
const nowPlayingIndex = queue.findIndex((track) => track.item.Id === nowPlaying!.item.Id);
setNowPlaying(queue[nowPlayingIndex + 1])
const nowPlayingIndex = playQueue.findIndex((track) => track.item.Id === nowPlaying!.item.Id);
setNowPlaying(playQueue[nowPlayingIndex + 1])
await skipToNext();
}
}
@@ -189,10 +190,10 @@ const PlayerContextInitializer = () => {
mutationFn: async () => {
trigger("impactMedium");
const nowPlayingIndex = queue.findIndex((track) => track.item.Id === nowPlaying!.item.Id);
const nowPlayingIndex = playQueue.findIndex((track) => track.item.Id === nowPlaying!.item.Id);
if (nowPlayingIndex > 0) {
setNowPlaying(queue[nowPlayingIndex - 1])
setNowPlaying(playQueue[nowPlayingIndex - 1])
await skipToPrevious();
}
}
@@ -212,7 +213,7 @@ const PlayerContextInitializer = () => {
return mapDtoToTrack(track, QueuingType.FromSelection)
}));
setQueueName(mutation.queueName);
setQueue(mutation.queue);
},
onSuccess: async (data, mutation: QueueMutation) => {
setIsSkipping(false);
@@ -303,10 +304,16 @@ const PlayerContextInitializer = () => {
//#region useEffects
useEffect(() => {
if (initialized && queue)
storage.set(MMKVStorageKeys.PlayQueue, JSON.stringify(queue))
storage.set(MMKVStorageKeys.Queue, JSON.stringify(playQueue))
}, [
queue
playQueue
])
useEffect(() => {
if (initialized && playQueue)
storage.set(MMKVStorageKeys.PlayQueue, JSON.stringify(playQueue))
}, [
playQueue
])
useEffect(() => {
@@ -317,16 +324,16 @@ const PlayerContextInitializer = () => {
])
useEffect(() => {
if (!initialized && queue.length > 0 && nowPlaying) {
TrackPlayer.setQueue(queue)
if (!initialized && playQueue.length > 0 && nowPlaying) {
TrackPlayer.setQueue(playQueue)
.then(() => {
TrackPlayer.skip(queue.findIndex(track => track.item.Id! === nowPlaying.item.Id!));
TrackPlayer.skip(playQueue.findIndex(track => track.item.Id! === nowPlaying.item.Id!));
});
}
setInitialized(true);
}, [
queue,
playQueue,
nowPlaying
])
//#endregion useEffects
@@ -337,8 +344,8 @@ const PlayerContextInitializer = () => {
nowPlayingIsFavorite,
setNowPlayingIsFavorite,
nowPlaying,
playQueue,
queue,
queueName,
getQueueSectionData,
useAddToQueue,
useClearQueue,
@@ -361,8 +368,8 @@ export const PlayerContext = createContext<PlayerContext>({
nowPlayingIsFavorite: false,
setNowPlayingIsFavorite: () => {},
nowPlaying: undefined,
queue: [],
queueName: undefined,
playQueue: [],
queue: "Recently Played",
getQueueSectionData: () => [],
useAddToQueue: {
mutate: () => {},
@@ -537,8 +544,8 @@ export const PlayerProvider: ({ children }: { children: ReactNode }) => React.JS
nowPlayingIsFavorite,
setNowPlayingIsFavorite,
nowPlaying,
queue,
queueName,
playQueue,
queue,
getQueueSectionData,
useAddToQueue,
useClearQueue,
@@ -558,8 +565,8 @@ export const PlayerProvider: ({ children }: { children: ReactNode }) => React.JS
nowPlayingIsFavorite,
setNowPlayingIsFavorite,
nowPlaying,
playQueue,
queue,
queueName,
getQueueSectionData,
useAddToQueue,
useClearQueue,
+3
View File
@@ -0,0 +1,3 @@
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
export type Queue = BaseItemDto | "Recently Played" | "Queue";