tryin to get these arts to handle their own shit

This commit is contained in:
austinried 2021-06-30 21:02:18 +09:00
parent 23e6c0caf0
commit c068eac3e5
10 changed files with 151 additions and 92 deletions

View File

@ -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);

View File

@ -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,

View File

@ -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);

View File

@ -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>
)
}

View File

@ -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 = () => {

View File

@ -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,

View File

@ -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)));
}
}
}

View File

@ -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[];
}

View File

@ -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,

View File

@ -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') {