Merge branch 'main' of github.com:Jellify-Music/App into maintenance/artist-screen

This commit is contained in:
Violet Caulfield
2025-11-24 18:18:12 -06:00
10 changed files with 68 additions and 42 deletions

View File

@@ -186,7 +186,7 @@ function AlbumTrackListHeader(): React.JSX.Element {
<XStack justify='center' marginVertical={'$2'}>
<YStack flex={1}>
{album.ProductionYear ? (
<Text display='block' textAlign='right'>
<Text textAlign='right'>
{album.ProductionYear?.toString() ?? 'Unknown Year'}
</Text>
) : null}

View File

@@ -137,7 +137,6 @@ function Image({
return (
<ZStack style={imageViewStyle.view} justifyContent='center' alignContent='center'>
{!isLoaded && <ItemBlurhash item={item} />}
<AnimatedTamaguiImage
objectFit='cover'
// recyclingKey={imageUrl}
@@ -151,6 +150,7 @@ function Image({
exiting={FadeOut}
style={Styles.blurhash}
/>
{!isLoaded && <ItemBlurhash item={item} />}
</ZStack>
)
}

View File

@@ -13,13 +13,7 @@ export function RunTimeSeconds({
alignment?: 'center' | 'left' | 'right'
}): React.JSX.Element {
return (
<Text
bold
color={color}
display='block'
textAlign={alignment}
fontVariant={['tabular-nums']}
>
<Text bold color={color} textAlign={alignment} fontVariant={['tabular-nums']}>
{calculateRunTimeFromSeconds(children)}
</Text>
)
@@ -37,7 +31,7 @@ export function RunTimeTicks({
const time = calculateRunTimeFromTicks(children)
return (
<Text {...props} display='block' color='$borderColor' fontVariant={['tabular-nums']}>
<Text {...props} color='$borderColor' fontVariant={['tabular-nums']}>
{time}
</Text>
)

View File

@@ -1,14 +1,14 @@
import { XStack, YStack, Spacer, useTheme } from 'tamagui'
import { Text } from '../../Global/helpers/text'
import React, { useCallback, useMemo, useRef } from 'react'
import React, { useCallback, useMemo } from 'react'
import ItemImage from '../../Global/components/image'
import Animated, {
FadeIn,
FadeOut,
useAnimatedStyle,
useSharedValue,
withSpring,
withTiming,
} from 'react-native-reanimated'
import { LayoutChangeEvent, Platform, View } from 'react-native'
import { LayoutChangeEvent, Platform } from 'react-native'
import MaterialDesignIcons from '@react-native-vector-icons/material-design-icons'
import navigationRef from '../../../../navigation'
import { useCurrentTrack, useQueueRef } from '../../../stores/player/queue'
@@ -62,37 +62,37 @@ export default function PlayerHeader(): React.JSX.Element {
function PlayerArtwork(): React.JSX.Element {
const nowPlaying = useCurrentTrack()
const artworkMaxHeight = '65%'
const artworkMaxWidth = useSharedValue<number>(300)
const artworkContainerRef = useRef<View>(null)
const artworkMaxHeight = useSharedValue<number>(200)
const artworkMaxWidth = useSharedValue<number>(200)
const animatedStyle = useAnimatedStyle(() => ({
width: artworkMaxWidth.get(),
width: withSpring(artworkMaxWidth.get()),
height: withSpring(artworkMaxWidth.get()),
opacity: withTiming(nowPlaying ? 1 : 0),
}))
const handleLayout = useCallback((event: LayoutChangeEvent) => {
artworkMaxHeight.set(event.nativeEvent.layout.height)
artworkMaxWidth.set(event.nativeEvent.layout.height)
}, [])
return (
<YStack
ref={artworkContainerRef}
flex={1}
alignItems='center'
justifyContent='center'
paddingHorizontal={'$2'}
maxHeight={artworkMaxHeight}
maxHeight={'65%'}
marginHorizontal={'$4'}
marginVertical={'auto'}
onLayout={handleLayout}
>
{nowPlaying && (
<Animated.View
entering={FadeIn}
exiting={FadeOut}
key={`${nowPlaying!.item.AlbumId}-item-image`}
style={{ flex: 1, ...animatedStyle }}
style={{
...animatedStyle,
}}
>
<ItemImage item={nowPlaying!.item} testID='player-image-test-id' />
</Animated.View>

View File

@@ -1,4 +1,4 @@
import { Square } from 'tamagui'
import { Spacer, Square } from 'tamagui'
import { Text } from '../../Global/helpers/text'
import navigationRef from '../../../../navigation'
import { parseBitrateFromTranscodingUrl } from '../../../utils/url-parsers'
@@ -25,11 +25,13 @@ export default function QualityBadge({
return bitrate && container ? (
<Square
enterStyle={{ opacity: 1 }}
exitStyle={{ opacity: 0 }}
animation={'bouncy'}
justifyContent='center'
borderWidth={'$1'}
backgroundColor={'$primary'}
paddingHorizontal={'$1'}
paddingVertical={'$0.5'}
paddingHorizontal={'$2'}
borderRadius={'$2'}
pressStyle={{ scale: 0.875 }}
onPress={() => {

View File

@@ -1,7 +1,7 @@
import React, { useCallback, useEffect, useMemo, useState, useRef } from 'react'
import { HorizontalSlider } from '../../../components/Global/helpers/slider'
import { Gesture, GestureDetector } from 'react-native-gesture-handler'
import { XStack, YStack } from 'tamagui'
import { Spacer, XStack, YStack } from 'tamagui'
import { useSafeAreaFrame } from 'react-native-safe-area-context'
import { useSeekTo } from '../../../providers/Player/hooks/mutations'
import { RunTimeSeconds } from '../../../components/Global/helpers/time-codes'
@@ -157,21 +157,33 @@ export default function Scrubber(): React.JSX.Element {
/>
<XStack alignItems='center' paddingTop={'$2'}>
<YStack alignItems='flex-start' flexShrink={1}>
<YStack
alignItems='flex-start'
justifyContent='center'
flexShrink={1}
height={'$2'}
>
<RunTimeSeconds alignment='left'>{currentSeconds}</RunTimeSeconds>
</YStack>
<YStack alignItems='center' flexGrow={1}>
{nowPlaying?.mediaSourceInfo && displayAudioQualityBadge && (
<YStack alignItems='center' justifyContent='center' flexGrow={1} height={'$2'}>
{nowPlaying?.mediaSourceInfo && displayAudioQualityBadge ? (
<QualityBadge
item={nowPlaying.item}
sourceType={nowPlaying.sourceType}
mediaSourceInfo={nowPlaying.mediaSourceInfo}
/>
) : (
<Spacer />
)}
</YStack>
<YStack alignItems='flex-end' flexShrink={1}>
<YStack
alignItems='flex-end'
justifyContent='center'
flexShrink={1}
height={'$2'}
>
<RunTimeSeconds alignment='right'>{totalSeconds}</RunTimeSeconds>
</YStack>
</XStack>

View File

@@ -1,5 +1,5 @@
import React, { useMemo, useCallback } from 'react'
import { getToken, Progress, View, XStack, YStack } from 'tamagui'
import { getToken, Progress, XStack, YStack } from 'tamagui'
import { useNavigation } from '@react-navigation/native'
import { Text } from '../Global/helpers/text'
import TextTicker from 'react-native-text-ticker'
@@ -11,7 +11,14 @@ import { Progress as TrackPlayerProgress } from 'react-native-track-player'
import { useProgress } from '../../providers/Player/hooks/queries'
import { Gesture, GestureDetector } from 'react-native-gesture-handler'
import Animated, { FadeIn, FadeOut, useSharedValue, withSpring } from 'react-native-reanimated'
import Animated, {
FadeIn,
FadeInDown,
FadeOut,
FadeOutDown,
useSharedValue,
withSpring,
} from 'react-native-reanimated'
import { runOnJS } from 'react-native-worklets'
import { RootStackParamList } from '../../screens/types'
import { NativeStackNavigationProp } from '@react-navigation/native-stack'
@@ -79,10 +86,22 @@ export const Miniplayer = React.memo(function Miniplayer(): React.JSX.Element {
return (
<GestureDetector gesture={gesture}>
<Animated.View testID='miniplayer-test-id' entering={FadeIn} exiting={FadeOut}>
<Animated.View
testID='miniplayer-test-id'
entering={FadeInDown.springify()}
exiting={FadeOutDown.springify()}
>
<YStack>
<MiniPlayerProgress />
<XStack paddingBottom={'$1'} alignItems='center' onPress={openPlayer}>
<XStack
alignItems='center'
pressStyle={{
opacity: 0.6,
}}
animation={'quick'}
onPress={openPlayer}
paddingBottom={'$1'}
>
<YStack justify='center' alignItems='center' marginLeft={'$2'}>
<Animated.View
entering={FadeIn}

View File

@@ -79,7 +79,7 @@ export default function PlaybackTab(): React.JSX.Element {
onCheckedChange={setDisplayAudioQualityBadge}
size={'$2'}
checked={displayAudioQualityBadge}
label={displayAudioQualityBadge ? 'Enabled' : 'Disabled'}
label={displayAudioQualityBadge ? 'Displaying' : 'Disabled'}
/>
),
},

View File

@@ -32,14 +32,14 @@ export default function StorageTab(): React.JSX.Element {
onPress: () => navigation.navigate('StorageManagement'),
},
{
title: 'Automatically Cache Tracks',
title: 'Auto-Download Tracks',
subTitle: 'Download tracks as they are played',
iconName: autoDownload ? 'cloud-download' : 'cloud-off-outline',
iconColor: autoDownload ? '$success' : '$borderColor',
children: (
<SwitchWithLabel
size={'$2'}
label={autoDownload ? 'Enabled' : 'Disabled'}
label={autoDownload ? 'Downloading' : 'Disabled'}
checked={autoDownload}
onCheckedChange={() => setAutoDownload(!autoDownload)}
/>

View File

@@ -1,6 +1,5 @@
import { create } from 'zustand'
import { createJSONStorage, devtools, persist } from 'zustand/middleware'
import { Platform } from 'react-native'
import { mmkvStateStorage } from '../../constants/storage'
import StreamingQuality from '../../enums/audio-quality'
@@ -21,7 +20,7 @@ export const useUsageSettingsStore = create<UsageSettingsStore>()(
downloadQuality: StreamingQuality.Original,
setDownloadQuality: (downloadQuality: DownloadQuality) => set({ downloadQuality }),
autoDownload: Platform.OS === 'android' || Platform.OS === 'ios',
autoDownload: false,
setAutoDownload: (autoDownload) => set({ autoDownload }),
}),
{