mirror of
https://github.com/Jellify-Music/App.git
synced 2026-05-22 04:49:15 -05:00
Adding scaffold for discover
adding slider player component styling wiring progess into player provider
This commit is contained in:
@@ -1,10 +1,35 @@
|
||||
import React from "react";
|
||||
import { Slider as TamaguiSlider } from "tamagui";
|
||||
import { SliderProps as TamaguiSliderProps, SliderVerticalProps, Slider as TamaguiSlider } from "tamagui";
|
||||
|
||||
export default function Slider() : React.JSX.Element {
|
||||
interface SliderProps {
|
||||
value?: number | undefined;
|
||||
max: number;
|
||||
width?: number | undefined
|
||||
props: TamaguiSliderProps
|
||||
}
|
||||
|
||||
export function HorizontalSlider({
|
||||
value,
|
||||
max,
|
||||
width,
|
||||
props
|
||||
}: {
|
||||
value?: number | undefined,
|
||||
max: number;
|
||||
width?: number | undefined,
|
||||
props?: TamaguiSliderProps | undefined
|
||||
}) : React.JSX.Element {
|
||||
|
||||
return (
|
||||
<TamaguiSlider size="$4" width={200} defaultValue={[50]} max={100} step={1}>
|
||||
<TamaguiSlider
|
||||
size="$4"
|
||||
width={width}
|
||||
value={value ? [value] : []}
|
||||
max={max}
|
||||
step={1}
|
||||
orientation="horizontal"
|
||||
{ ...props }
|
||||
>
|
||||
<TamaguiSlider.Track>
|
||||
<TamaguiSlider.TrackActive />
|
||||
</TamaguiSlider.Track>
|
||||
@@ -12,4 +37,22 @@ export default function Slider() : React.JSX.Element {
|
||||
</TamaguiSlider>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export function VerticalSlider(props: SliderVerticalProps) : React.JSX.Element {
|
||||
|
||||
return (
|
||||
<TamaguiSlider
|
||||
size="$4"
|
||||
width={200}
|
||||
defaultValue={[0]}
|
||||
max={100}
|
||||
step={1}
|
||||
orientation="vertical"
|
||||
>
|
||||
<TamaguiSlider.Track>
|
||||
<TamaguiSlider.TrackActive />
|
||||
</TamaguiSlider.Track>
|
||||
<TamaguiSlider.Thumb circular index={0} />
|
||||
</TamaguiSlider>
|
||||
)
|
||||
}
|
||||
@@ -12,11 +12,12 @@ import { SafeAreaView } from "react-native-safe-area-context";
|
||||
import { playPauseButton } from "./helpers/buttons";
|
||||
import { BottomTabNavigationEventMap } from "@react-navigation/bottom-tabs";
|
||||
import { NavigationHelpers, ParamListBase } from "@react-navigation/native";
|
||||
import { HorizontalSlider } from "../Global/slider";
|
||||
|
||||
export default function Player({ navigation }: { navigation : NavigationHelpers<ParamListBase, BottomTabNavigationEventMap> }): React.JSX.Element {
|
||||
|
||||
const { apiClient } = useApiClientContext();
|
||||
const { queue, playbackState, nowPlaying, play, pause } = usePlayerContext();
|
||||
const { queue, playbackState, nowPlaying, play, pause, progress } = usePlayerContext();
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
@@ -42,21 +43,23 @@ export default function Player({ navigation }: { navigation : NavigationHelpers<
|
||||
/>
|
||||
</XStack>
|
||||
|
||||
<XStack>
|
||||
<XStack
|
||||
alignContent="flex-start"
|
||||
marginHorizontal={10}
|
||||
>
|
||||
|
||||
<YStack alignContent="flex-start">
|
||||
<Text>{nowPlaying?.title ?? "Untitled Track"}</Text>
|
||||
<Text
|
||||
bold
|
||||
onPress={() => {
|
||||
navigation.goBack();
|
||||
navigation.navigate("Artist", {
|
||||
artistName: nowPlaying?.artist,
|
||||
artistId: nowPlaying?.ArtistId
|
||||
artistName: nowPlaying.artist,
|
||||
artistId: nowPlaying.ArtistId
|
||||
})
|
||||
}}
|
||||
>
|
||||
{nowPlaying?.artist ?? "Unknown Artist"}</Text>
|
||||
{nowPlaying.artist ?? "Unknown Artist"}</Text>
|
||||
</YStack>
|
||||
|
||||
<XStack>
|
||||
@@ -68,6 +71,12 @@ export default function Player({ navigation }: { navigation : NavigationHelpers<
|
||||
|
||||
<XStack>
|
||||
{/* playback progress goes here */}
|
||||
<HorizontalSlider
|
||||
value={progress!.position}
|
||||
max={progress!.duration}
|
||||
width={400}
|
||||
/>
|
||||
|
||||
</XStack>
|
||||
|
||||
<XStack>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export const UPDATE_INTERVAL: number = 500
|
||||
@@ -4,6 +4,22 @@ import { useQuery } from "@tanstack/react-query";
|
||||
import { QueryKeys } from "../enums/query-keys";
|
||||
import { PlaystateApi } from "@jellyfin/sdk/lib/generated-client/api/playstate-api";
|
||||
|
||||
export const usePlaybackStopped = (sessionId: string, playStateApi: PlaystateApi, activeTrack: JellifyTrack) => useQuery({
|
||||
queryKey: [QueryKeys.ReportPlaybackStopped, sessionId, activeTrack, playStateApi],
|
||||
queryFn: async ({ queryKey }) => {
|
||||
const sessionId: string = queryKey[1] as string;
|
||||
const activeTrack : JellifyTrack = queryKey[2] as JellifyTrack;
|
||||
const playStateApi : PlaystateApi = queryKey[3] as PlaystateApi;
|
||||
|
||||
return await playStateApi.reportPlaybackStopped({
|
||||
playbackStopInfo: {
|
||||
SessionId: sessionId,
|
||||
ItemId: activeTrack.ItemId
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
export const handlePlaybackStateChange = (state: State, sessionId: string, playStateApi: PlaystateApi, activeTrack: JellifyTrack) => useQuery({
|
||||
queryKey: [QueryKeys.PlaybackStateChange, state, sessionId, activeTrack, playStateApi],
|
||||
queryFn: async ({ queryKey }) => {
|
||||
|
||||
+18
-11
@@ -3,14 +3,15 @@ import { JellifyTrack } from "../types/JellifyTrack";
|
||||
import { storage } from "../constants/storage";
|
||||
import { MMKVStorageKeys } from "../enums/mmkv-storage-keys";
|
||||
import { findPlayQueueIndexStart } from "./helpers/index";
|
||||
import TrackPlayer, { Event, State, usePlaybackState, useTrackPlayerEvents } from "react-native-track-player";
|
||||
import TrackPlayer, { Event, Progress, State, useActiveTrack, usePlaybackState, useProgress, useTrackPlayerEvents } from "react-native-track-player";
|
||||
import _ from "lodash";
|
||||
import { buildNewQueue } from "./helpers/queue";
|
||||
import { useApiClientContext } from "../components/jellyfin-api-provider";
|
||||
import { getPlaystateApi } from "@jellyfin/sdk/lib/utils/api";
|
||||
import { handlePlaybackStateChange } from "./handlers";
|
||||
import { handlePlaybackStateChange, usePlaybackStopped } from "./handlers";
|
||||
import { sleep } from "@/helpers/sleep";
|
||||
import { useSetupPlayer } from "@/components/Player/hooks";
|
||||
import { UPDATE_INTERVAL } from "./config";
|
||||
|
||||
interface PlayerContext {
|
||||
showPlayer: boolean;
|
||||
@@ -24,6 +25,7 @@ interface PlayerContext {
|
||||
resetQueue: (hideMiniplayer : boolean | undefined) => Promise<void>;
|
||||
addToQueue: (tracks: JellifyTrack[]) => Promise<void>;
|
||||
playbackState: State | undefined;
|
||||
progress: Progress | undefined;
|
||||
}
|
||||
|
||||
const PlayerContextInitializer = () => {
|
||||
@@ -97,6 +99,7 @@ const PlayerContextInitializer = () => {
|
||||
|
||||
useTrackPlayerEvents([
|
||||
Event.PlaybackState,
|
||||
Event.PlaybackProgressUpdated,
|
||||
Event.PlaybackActiveTrackChanged,
|
||||
], async (event) => {
|
||||
|
||||
@@ -111,17 +114,16 @@ const PlayerContextInitializer = () => {
|
||||
|
||||
case (Event.PlaybackActiveTrackChanged) : {
|
||||
|
||||
// Scrobble previously played track
|
||||
if (nowPlaying) {
|
||||
playStateApi.reportPlaybackStopped({
|
||||
playbackStopInfo: {
|
||||
SessionId: sessionId,
|
||||
ItemId: nowPlaying.ItemId
|
||||
}
|
||||
})
|
||||
usePlaybackStopped(sessionId, playStateApi, nowPlaying)
|
||||
}
|
||||
|
||||
console.debug("Active track changed")
|
||||
const activeTrack = await sleep(500).then(async () => {
|
||||
console.debug("Active track changed");
|
||||
|
||||
// Sleep before capturing the active track in case we are
|
||||
// skipping to an initial queue index
|
||||
const activeTrack = await sleep(250).then(async () => {
|
||||
return await TrackPlayer.getActiveTrack()
|
||||
}) as JellifyTrack;
|
||||
|
||||
@@ -131,7 +133,8 @@ const PlayerContextInitializer = () => {
|
||||
}
|
||||
})
|
||||
|
||||
const { state: playbackState } = usePlaybackState()
|
||||
const { state: playbackState } = usePlaybackState();
|
||||
const progress = useProgress(UPDATE_INTERVAL);
|
||||
|
||||
useEffect(() => {
|
||||
if (!showMiniplayer)
|
||||
@@ -163,6 +166,7 @@ const PlayerContextInitializer = () => {
|
||||
addToQueue,
|
||||
resetQueue,
|
||||
playbackState,
|
||||
progress,
|
||||
}
|
||||
//#endregion return
|
||||
}
|
||||
@@ -179,6 +183,7 @@ export const PlayerContext = createContext<PlayerContext>({
|
||||
resetQueue: async () => {},
|
||||
addToQueue: async ([]) => {},
|
||||
playbackState: undefined,
|
||||
progress: undefined,
|
||||
});
|
||||
|
||||
export const PlayerProvider: ({ children }: { children: ReactNode }) => React.JSX.Element = ({ children }: { children: ReactNode }) => {
|
||||
@@ -194,6 +199,7 @@ export const PlayerProvider: ({ children }: { children: ReactNode }) => React.JS
|
||||
resetQueue,
|
||||
addToQueue,
|
||||
playbackState,
|
||||
progress
|
||||
} = PlayerContextInitializer();
|
||||
|
||||
return <PlayerContext.Provider value={{
|
||||
@@ -208,6 +214,7 @@ export const PlayerProvider: ({ children }: { children: ReactNode }) => React.JS
|
||||
resetQueue,
|
||||
addToQueue,
|
||||
playbackState,
|
||||
progress
|
||||
}}>
|
||||
{ children }
|
||||
</PlayerContext.Provider>
|
||||
|
||||
Reference in New Issue
Block a user