mirror of
https://github.com/austinried/subtracks.git
synced 2025-12-27 00:59:28 +01:00
tryin to get these arts to handle their own shit
This commit is contained in:
parent
23e6c0caf0
commit
c068eac3e5
@ -1,14 +1,21 @@
|
||||
import { useAtomValue } from 'jotai/utils';
|
||||
import React from 'react';
|
||||
import { ActivityIndicator, View } from 'react-native';
|
||||
import FastImage from 'react-native-fast-image';
|
||||
import LinearGradient from 'react-native-linear-gradient';
|
||||
import { albumArtAtomFamily } from '../../state/music';
|
||||
import colors from '../../styles/colors';
|
||||
import CoverArt from './CoverArt';
|
||||
|
||||
const AlbumArt: React.FC<{
|
||||
height: number,
|
||||
width: number,
|
||||
coverArtUri?: string
|
||||
}> = ({ height, width, coverArtUri }) => {
|
||||
interface AlbumArtProps {
|
||||
id: string;
|
||||
height: number;
|
||||
width: number;
|
||||
}
|
||||
|
||||
const AlbumArt: React.FC<AlbumArtProps> = ({ id, height, width }) => {
|
||||
const albumArt = useAtomValue(albumArtAtomFamily(id));
|
||||
|
||||
const Placeholder = () => (
|
||||
<LinearGradient
|
||||
colors={[colors.accent, colors.accentLow]}
|
||||
@ -26,9 +33,25 @@ const AlbumArt: React.FC<{
|
||||
PlaceholderComponent={Placeholder}
|
||||
height={height}
|
||||
width={width}
|
||||
coverArtUri={coverArtUri}
|
||||
coverArtUri={width > 128 ? albumArt?.uri : albumArt?.thumbUri}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(AlbumArt);
|
||||
const AlbumArtFallback: React.FC<AlbumArtProps> = ({ height, width }) => (
|
||||
<View style={{
|
||||
height, width,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}>
|
||||
<ActivityIndicator size='small' color={colors.accent} />
|
||||
</View>
|
||||
);
|
||||
|
||||
const AlbumArtLoader: React.FC<AlbumArtProps> = (props) => (
|
||||
<React.Suspense fallback={<AlbumArtFallback { ...props } />}>
|
||||
<AlbumArt { ...props } />
|
||||
</React.Suspense>
|
||||
);
|
||||
|
||||
export default React.memo(AlbumArtLoader);
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { useAtomValue } from 'jotai/utils';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { GestureResponderEvent, Image, Pressable, ScrollView, Text, useWindowDimensions, View } from 'react-native';
|
||||
import { GestureResponderEvent, Image, Pressable, Text, useWindowDimensions, View } from 'react-native';
|
||||
import { useCurrentTrackId, useSetQueue } from '../../hooks/player';
|
||||
import { albumAtomFamily } from '../../state/music';
|
||||
import colors from '../../styles/colors';
|
||||
@ -100,12 +100,7 @@ const AlbumDetails: React.FC<{
|
||||
paddingTop: coverSize / 8,
|
||||
}}
|
||||
>
|
||||
<AlbumArt
|
||||
height={coverSize}
|
||||
width={coverSize}
|
||||
coverArtUri={album.coverArtUri}
|
||||
/>
|
||||
|
||||
<AlbumArt id={album.id} height={coverSize} width={coverSize} />
|
||||
<Text style={{
|
||||
...text.title,
|
||||
marginTop: 12,
|
||||
|
||||
@ -1,14 +1,27 @@
|
||||
import { useAtom } from 'jotai';
|
||||
import { useAtomValue } from 'jotai/utils';
|
||||
import React from 'react';
|
||||
import { View } from 'react-native';
|
||||
import { ActivityIndicator, View } from 'react-native';
|
||||
import FastImage from 'react-native-fast-image';
|
||||
import LinearGradient from 'react-native-linear-gradient';
|
||||
import { artistArtAtomFamily } from '../../state/music';
|
||||
import colors from '../../styles/colors';
|
||||
import CoverArt from './CoverArt';
|
||||
|
||||
const PlaceholderContainer: React.FC<{
|
||||
height: number,
|
||||
width: number,
|
||||
}> = ({ height, width, children}) => (
|
||||
interface ArtistArtSizeProps {
|
||||
height: number;
|
||||
width: number;
|
||||
};
|
||||
|
||||
interface ArtistArtXUpProps extends ArtistArtSizeProps {
|
||||
coverArtUris: string[];
|
||||
}
|
||||
|
||||
interface ArtistArtProps extends ArtistArtSizeProps {
|
||||
id: string;
|
||||
}
|
||||
|
||||
const PlaceholderContainer: React.FC<ArtistArtSizeProps> = ({ height, width, children }) => (
|
||||
<LinearGradient
|
||||
colors={[colors.accent, colors.accentLow]}
|
||||
style={{
|
||||
@ -21,11 +34,7 @@ const PlaceholderContainer: React.FC<{
|
||||
</LinearGradient>
|
||||
);
|
||||
|
||||
const FourUp: React.FC<{
|
||||
height: number,
|
||||
width: number,
|
||||
coverArtUris: string[];
|
||||
}> = ({ height, width, coverArtUris }) => {
|
||||
const FourUp: React.FC<ArtistArtXUpProps> = ({ height, width, coverArtUris }) => {
|
||||
const halfHeight = height / 2;
|
||||
const halfWidth = width / 2;
|
||||
|
||||
@ -59,11 +68,7 @@ const FourUp: React.FC<{
|
||||
);
|
||||
};
|
||||
|
||||
const ThreeUp: React.FC<{
|
||||
height: number,
|
||||
width: number,
|
||||
coverArtUris: string[];
|
||||
}> = ({ height, width, coverArtUris }) => {
|
||||
const ThreeUp: React.FC<ArtistArtXUpProps> = ({ height, width, coverArtUris }) => {
|
||||
const halfHeight = height / 2;
|
||||
const halfWidth = width / 2;
|
||||
|
||||
@ -92,11 +97,7 @@ const ThreeUp: React.FC<{
|
||||
);
|
||||
};
|
||||
|
||||
const TwoUp: React.FC<{
|
||||
height: number,
|
||||
width: number,
|
||||
coverArtUris: string[];
|
||||
}> = ({ height, width, coverArtUris }) => {
|
||||
const TwoUp: React.FC<ArtistArtXUpProps> = ({ height, width, coverArtUris }) => {
|
||||
const halfHeight = height / 2;
|
||||
|
||||
return (
|
||||
@ -119,11 +120,7 @@ const TwoUp: React.FC<{
|
||||
);
|
||||
};
|
||||
|
||||
const OneUp: React.FC<{
|
||||
height: number,
|
||||
width: number,
|
||||
coverArtUris: string[];
|
||||
}> = ({ height, width, coverArtUris }) => {
|
||||
const OneUp: React.FC<ArtistArtXUpProps> = ({ height, width, coverArtUris }) => {
|
||||
return (
|
||||
<PlaceholderContainer height={height} width={width}>
|
||||
<FastImage
|
||||
@ -135,10 +132,7 @@ const OneUp: React.FC<{
|
||||
);
|
||||
};
|
||||
|
||||
const NoneUp: React.FC<{
|
||||
height: number,
|
||||
width: number,
|
||||
}> = ({ height, width }) => {
|
||||
const NoneUp: React.FC<ArtistArtSizeProps> = ({ height, width }) => {
|
||||
return (
|
||||
<PlaceholderContainer height={height} width={width}>
|
||||
<FastImage
|
||||
@ -153,26 +147,31 @@ const NoneUp: React.FC<{
|
||||
);
|
||||
};
|
||||
|
||||
const ArtistArt: React.FC<{
|
||||
height: number,
|
||||
width: number,
|
||||
mediumImageUrl?: string;
|
||||
coverArtUris?: string[]
|
||||
}> = ({ height, width, mediumImageUrl, coverArtUris }) => {
|
||||
const ArtistArt: React.FC<ArtistArtProps> = ({ id, height, width }) => {
|
||||
const artistArt = useAtomValue(artistArtAtomFamily(id));
|
||||
|
||||
const Placeholder = () => {
|
||||
if (coverArtUris && coverArtUris.length >= 4) {
|
||||
const none = <NoneUp height={height} width={width} />;
|
||||
|
||||
if (!artistArt || !artistArt.coverArtUris) {
|
||||
return none;
|
||||
}
|
||||
const { coverArtUris } = artistArt;
|
||||
|
||||
if (coverArtUris.length >= 4) {
|
||||
return <FourUp height={height} width={width} coverArtUris={coverArtUris} />;
|
||||
}
|
||||
if (coverArtUris && coverArtUris.length === 3) {
|
||||
if (coverArtUris.length === 3) {
|
||||
return <ThreeUp height={height} width={width} coverArtUris={coverArtUris} />;
|
||||
}
|
||||
if (coverArtUris && coverArtUris.length === 2) {
|
||||
if (coverArtUris.length === 2) {
|
||||
return <TwoUp height={height} width={width} coverArtUris={coverArtUris} />;
|
||||
}
|
||||
if (coverArtUris && coverArtUris.length === 1) {
|
||||
if (coverArtUris.length === 1) {
|
||||
return <OneUp height={height} width={width} coverArtUris={coverArtUris} />;
|
||||
}
|
||||
return <NoneUp height={height} width={width} />;
|
||||
|
||||
return none;
|
||||
}
|
||||
|
||||
return (
|
||||
@ -184,10 +183,26 @@ const ArtistArt: React.FC<{
|
||||
PlaceholderComponent={Placeholder}
|
||||
height={height}
|
||||
width={width}
|
||||
coverArtUri={mediumImageUrl}
|
||||
coverArtUri={artistArt?.uri}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(ArtistArt);
|
||||
const ArtistArtFallback: React.FC<ArtistArtProps> = ({ height, width }) => (
|
||||
<View style={{
|
||||
height, width,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}>
|
||||
<ActivityIndicator size='small' color={colors.accent} />
|
||||
</View>
|
||||
);
|
||||
|
||||
const ArtistArtLoader: React.FC<ArtistArtProps> = (props) => (
|
||||
<React.Suspense fallback={<ArtistArtFallback { ...props } />}>
|
||||
<ArtistArt { ...props } />
|
||||
</React.Suspense>
|
||||
);
|
||||
|
||||
export default React.memo(ArtistArtLoader);
|
||||
|
||||
@ -25,12 +25,7 @@ const ArtistDetails: React.FC<{ id: string }> = ({ id }) => {
|
||||
}}
|
||||
>
|
||||
<Text style={text.paragraph}>{artist.name}</Text>
|
||||
<ArtistArt
|
||||
height={200}
|
||||
width={200}
|
||||
mediumImageUrl={artist.mediumImageUrl}
|
||||
coverArtUris={artist.coverArtUris}
|
||||
/>
|
||||
<ArtistArt id={artist.id} height={200} width={200} />
|
||||
</GradientScrollView>
|
||||
)
|
||||
}
|
||||
|
||||
@ -10,10 +10,9 @@ import GradientFlatList from '../common/GradientFlatList';
|
||||
|
||||
const AlbumItem: React.FC<{
|
||||
id: string;
|
||||
name: string,
|
||||
artist?: string,
|
||||
coverArtUri?: string
|
||||
} > = ({ id, name, artist, coverArtUri }) => {
|
||||
name: string;
|
||||
artist?: string;
|
||||
}> = ({ id, name, artist, }) => {
|
||||
const navigation = useNavigation();
|
||||
|
||||
const size = 125;
|
||||
@ -27,11 +26,7 @@ const AlbumItem: React.FC<{
|
||||
}}
|
||||
onPress={() => navigation.navigate('AlbumView', { id, title: name })}
|
||||
>
|
||||
<AlbumArt
|
||||
width={size}
|
||||
height={size}
|
||||
coverArtUri={coverArtUri}
|
||||
/>
|
||||
<AlbumArt id={id} height={size} width={size} />
|
||||
<View style={{
|
||||
flex: 1,
|
||||
width: size,
|
||||
@ -58,7 +53,7 @@ const AlbumItem: React.FC<{
|
||||
const MemoAlbumItem = React.memo(AlbumItem);
|
||||
|
||||
const AlbumListRenderItem: React.FC<{ item: Album }> = ({ item }) => (
|
||||
<MemoAlbumItem id={item.id} name={item.name} artist={item.artist} coverArtUri={item.coverArtThumbUri} />
|
||||
<MemoAlbumItem id={item.id} name={item.name} artist={item.artist} />
|
||||
);
|
||||
|
||||
const AlbumsList = () => {
|
||||
|
||||
@ -11,7 +11,6 @@ import GradientFlatList from '../common/GradientFlatList';
|
||||
|
||||
const ArtistItem: React.FC<{ item: Artist }> = ({ item }) => {
|
||||
const navigation = useNavigation();
|
||||
const artistInfo = useAtomValue(artistInfoAtomFamily(item.id));
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
@ -23,19 +22,7 @@ const ArtistItem: React.FC<{ item: Artist }> = ({ item }) => {
|
||||
}}
|
||||
onPress={() => navigation.navigate('ArtistView', { id: item.id, title: item.name })}
|
||||
>
|
||||
<ArtistArt
|
||||
width={56}
|
||||
height={56}
|
||||
mediumImageUrl={artistInfo?.mediumImageUrl}
|
||||
coverArtUris={artistInfo?.coverArtUris}
|
||||
/>
|
||||
{/* <Image
|
||||
source={{ uri: 'https://reactnative.dev/img/tiny_logo.png' }}
|
||||
style={{
|
||||
width: 56,
|
||||
height: 56,
|
||||
}}
|
||||
/> */}
|
||||
<ArtistArt id={item.id} width={56} height={56} />
|
||||
<Text style={{
|
||||
...textStyles.paragraph,
|
||||
marginLeft: 12,
|
||||
|
||||
@ -61,16 +61,12 @@ export const useSetQueue = () => {
|
||||
const tracks1 = tracks.slice(0, playIndex);
|
||||
const tracks2 = tracks.slice(playIndex);
|
||||
|
||||
console.log('tracks1: ' + JSON.stringify(tracks1.map(t => t.title)));
|
||||
console.log('tracks2: ' + JSON.stringify(tracks2.map(t => t.title)));
|
||||
|
||||
await TrackPlayer.add(tracks2);
|
||||
await TrackPlayer.play();
|
||||
|
||||
await TrackPlayer.add(tracks1, playId);
|
||||
|
||||
const queue = await TrackPlayer.getQueue();
|
||||
console.log('queue: ' + JSON.stringify(queue.map(t => t.title)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,6 +12,11 @@ export interface ArtistInfo extends Artist {
|
||||
coverArtUris: string[];
|
||||
}
|
||||
|
||||
export interface ArtistArt {
|
||||
uri?: string;
|
||||
coverArtUris: string[];
|
||||
}
|
||||
|
||||
export interface Album {
|
||||
id: string;
|
||||
artistId?: string;
|
||||
@ -24,6 +29,11 @@ export interface Album {
|
||||
year?: number;
|
||||
}
|
||||
|
||||
export interface AlbumArt {
|
||||
uri?: string;
|
||||
thumbUri?: string;
|
||||
}
|
||||
|
||||
export interface AlbumWithSongs extends Album {
|
||||
songs: Song[];
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { atom, useAtom } from 'jotai';
|
||||
import { atomFamily, useAtomValue, useUpdateAtom } from 'jotai/utils';
|
||||
import { Album as Album, AlbumWithSongs, Artist, ArtistInfo, Song } from '../models/music';
|
||||
import { Album, AlbumArt, AlbumWithSongs, Artist, ArtistArt, ArtistInfo, Song } from '../models/music';
|
||||
import { SubsonicApiClient } from '../subsonic/api';
|
||||
import { AlbumID3Element, ArtistID3Element, ArtistInfo2Element, ChildElement } from '../subsonic/elements';
|
||||
import { GetArtistResponse } from '../subsonic/responses';
|
||||
@ -73,6 +73,26 @@ export const albumAtomFamily = atomFamily((id: string) => atom<AlbumWithSongs |
|
||||
return mapAlbumID3WithSongs(response.data.album, response.data.songs, client);
|
||||
}));
|
||||
|
||||
export const albumArtAtomFamily = atomFamily((id: string) => atom<AlbumArt | undefined>(async (get) => {
|
||||
const server = get(activeServerAtom);
|
||||
if (!server) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const albums = get(albumsAtom);
|
||||
const album = albums.find(a => a.id === id);
|
||||
if (!album) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const client = new SubsonicApiClient(server);
|
||||
|
||||
return {
|
||||
uri: album.coverArt ? client.getCoverArtUri({ id: album.coverArt }) : undefined,
|
||||
thumbUri: album.coverArt ? client.getCoverArtUri({ id: album.coverArt, size: '256' }) : undefined,
|
||||
};
|
||||
}));
|
||||
|
||||
export const artistInfoAtomFamily = atomFamily((id: string) => atom<ArtistInfo | undefined>(async (get) => {
|
||||
const server = get(activeServerAtom);
|
||||
if (!server) {
|
||||
@ -87,6 +107,29 @@ export const artistInfoAtomFamily = atomFamily((id: string) => atom<ArtistInfo |
|
||||
return mapArtistInfo(artistResponse.data, artistInfoResponse.data.artistInfo, client);
|
||||
}));
|
||||
|
||||
export const artistArtAtomFamily = atomFamily((id: string) => atom<ArtistArt | undefined>(async (get) => {
|
||||
const artistInfo = get(artistInfoAtomFamily(id));
|
||||
if (!artistInfo) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const coverArtUris = artistInfo.albums
|
||||
.filter(a => a.coverArtThumbUri !== undefined)
|
||||
.sort((a, b) => {
|
||||
if (b.year && a.year) {
|
||||
return b.year - a.year;
|
||||
} else {
|
||||
return a.name.localeCompare(b.name) - 9000;
|
||||
}
|
||||
})
|
||||
.map(a => a.coverArtThumbUri) as string[];
|
||||
|
||||
return {
|
||||
coverArtUris,
|
||||
uri: artistInfo.mediumImageUrl,
|
||||
};
|
||||
}));
|
||||
|
||||
function mapArtistInfo(
|
||||
artistResponse: GetArtistResponse,
|
||||
artistInfo: ArtistInfo2Element,
|
||||
|
||||
@ -100,7 +100,7 @@ export class SubsonicApiClient {
|
||||
const response = await fetch(this.buildUrl(method, params));
|
||||
const text = await response.text();
|
||||
|
||||
console.log(text);
|
||||
// console.log(text);
|
||||
|
||||
const xml = new DOMParser().parseFromString(text);
|
||||
if (xml.documentElement.getAttribute('status') !== 'ok') {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user