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 React from 'react';
import { ActivityIndicator, View } from 'react-native';
import FastImage from 'react-native-fast-image'; import FastImage from 'react-native-fast-image';
import LinearGradient from 'react-native-linear-gradient'; import LinearGradient from 'react-native-linear-gradient';
import { albumArtAtomFamily } from '../../state/music';
import colors from '../../styles/colors'; import colors from '../../styles/colors';
import CoverArt from './CoverArt'; import CoverArt from './CoverArt';
const AlbumArt: React.FC<{ interface AlbumArtProps {
height: number, id: string;
width: number, height: number;
coverArtUri?: string width: number;
}> = ({ height, width, coverArtUri }) => { }
const AlbumArt: React.FC<AlbumArtProps> = ({ id, height, width }) => {
const albumArt = useAtomValue(albumArtAtomFamily(id));
const Placeholder = () => ( const Placeholder = () => (
<LinearGradient <LinearGradient
colors={[colors.accent, colors.accentLow]} colors={[colors.accent, colors.accentLow]}
@ -26,9 +33,25 @@ const AlbumArt: React.FC<{
PlaceholderComponent={Placeholder} PlaceholderComponent={Placeholder}
height={height} height={height}
width={width} 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 { useNavigation } from '@react-navigation/native';
import { useAtomValue } from 'jotai/utils'; import { useAtomValue } from 'jotai/utils';
import React, { useEffect, useState } from 'react'; 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 { useCurrentTrackId, useSetQueue } from '../../hooks/player';
import { albumAtomFamily } from '../../state/music'; import { albumAtomFamily } from '../../state/music';
import colors from '../../styles/colors'; import colors from '../../styles/colors';
@ -100,12 +100,7 @@ const AlbumDetails: React.FC<{
paddingTop: coverSize / 8, paddingTop: coverSize / 8,
}} }}
> >
<AlbumArt <AlbumArt id={album.id} height={coverSize} width={coverSize} />
height={coverSize}
width={coverSize}
coverArtUri={album.coverArtUri}
/>
<Text style={{ <Text style={{
...text.title, ...text.title,
marginTop: 12, marginTop: 12,

View File

@ -1,14 +1,27 @@
import { useAtom } from 'jotai';
import { useAtomValue } from 'jotai/utils';
import React from 'react'; import React from 'react';
import { View } from 'react-native'; import { ActivityIndicator, View } from 'react-native';
import FastImage from 'react-native-fast-image'; import FastImage from 'react-native-fast-image';
import LinearGradient from 'react-native-linear-gradient'; import LinearGradient from 'react-native-linear-gradient';
import { artistArtAtomFamily } from '../../state/music';
import colors from '../../styles/colors'; import colors from '../../styles/colors';
import CoverArt from './CoverArt'; import CoverArt from './CoverArt';
const PlaceholderContainer: React.FC<{ interface ArtistArtSizeProps {
height: number, height: number;
width: number, width: number;
}> = ({ height, width, children}) => ( };
interface ArtistArtXUpProps extends ArtistArtSizeProps {
coverArtUris: string[];
}
interface ArtistArtProps extends ArtistArtSizeProps {
id: string;
}
const PlaceholderContainer: React.FC<ArtistArtSizeProps> = ({ height, width, children }) => (
<LinearGradient <LinearGradient
colors={[colors.accent, colors.accentLow]} colors={[colors.accent, colors.accentLow]}
style={{ style={{
@ -21,11 +34,7 @@ const PlaceholderContainer: React.FC<{
</LinearGradient> </LinearGradient>
); );
const FourUp: React.FC<{ const FourUp: React.FC<ArtistArtXUpProps> = ({ height, width, coverArtUris }) => {
height: number,
width: number,
coverArtUris: string[];
}> = ({ height, width, coverArtUris }) => {
const halfHeight = height / 2; const halfHeight = height / 2;
const halfWidth = width / 2; const halfWidth = width / 2;
@ -59,11 +68,7 @@ const FourUp: React.FC<{
); );
}; };
const ThreeUp: React.FC<{ const ThreeUp: React.FC<ArtistArtXUpProps> = ({ height, width, coverArtUris }) => {
height: number,
width: number,
coverArtUris: string[];
}> = ({ height, width, coverArtUris }) => {
const halfHeight = height / 2; const halfHeight = height / 2;
const halfWidth = width / 2; const halfWidth = width / 2;
@ -92,11 +97,7 @@ const ThreeUp: React.FC<{
); );
}; };
const TwoUp: React.FC<{ const TwoUp: React.FC<ArtistArtXUpProps> = ({ height, width, coverArtUris }) => {
height: number,
width: number,
coverArtUris: string[];
}> = ({ height, width, coverArtUris }) => {
const halfHeight = height / 2; const halfHeight = height / 2;
return ( return (
@ -119,11 +120,7 @@ const TwoUp: React.FC<{
); );
}; };
const OneUp: React.FC<{ const OneUp: React.FC<ArtistArtXUpProps> = ({ height, width, coverArtUris }) => {
height: number,
width: number,
coverArtUris: string[];
}> = ({ height, width, coverArtUris }) => {
return ( return (
<PlaceholderContainer height={height} width={width}> <PlaceholderContainer height={height} width={width}>
<FastImage <FastImage
@ -135,10 +132,7 @@ const OneUp: React.FC<{
); );
}; };
const NoneUp: React.FC<{ const NoneUp: React.FC<ArtistArtSizeProps> = ({ height, width }) => {
height: number,
width: number,
}> = ({ height, width }) => {
return ( return (
<PlaceholderContainer height={height} width={width}> <PlaceholderContainer height={height} width={width}>
<FastImage <FastImage
@ -153,26 +147,31 @@ const NoneUp: React.FC<{
); );
}; };
const ArtistArt: React.FC<{ const ArtistArt: React.FC<ArtistArtProps> = ({ id, height, width }) => {
height: number, const artistArt = useAtomValue(artistArtAtomFamily(id));
width: number,
mediumImageUrl?: string;
coverArtUris?: string[]
}> = ({ height, width, mediumImageUrl, coverArtUris }) => {
const Placeholder = () => { 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} />; 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} />; 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} />; 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 <OneUp height={height} width={width} coverArtUris={coverArtUris} />;
} }
return <NoneUp height={height} width={width} />;
return none;
} }
return ( return (
@ -184,10 +183,26 @@ const ArtistArt: React.FC<{
PlaceholderComponent={Placeholder} PlaceholderComponent={Placeholder}
height={height} height={height}
width={width} width={width}
coverArtUri={mediumImageUrl} coverArtUri={artistArt?.uri}
/> />
</View> </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> <Text style={text.paragraph}>{artist.name}</Text>
<ArtistArt <ArtistArt id={artist.id} height={200} width={200} />
height={200}
width={200}
mediumImageUrl={artist.mediumImageUrl}
coverArtUris={artist.coverArtUris}
/>
</GradientScrollView> </GradientScrollView>
) )
} }

View File

@ -10,10 +10,9 @@ import GradientFlatList from '../common/GradientFlatList';
const AlbumItem: React.FC<{ const AlbumItem: React.FC<{
id: string; id: string;
name: string, name: string;
artist?: string, artist?: string;
coverArtUri?: string }> = ({ id, name, artist, }) => {
} > = ({ id, name, artist, coverArtUri }) => {
const navigation = useNavigation(); const navigation = useNavigation();
const size = 125; const size = 125;
@ -27,11 +26,7 @@ const AlbumItem: React.FC<{
}} }}
onPress={() => navigation.navigate('AlbumView', { id, title: name })} onPress={() => navigation.navigate('AlbumView', { id, title: name })}
> >
<AlbumArt <AlbumArt id={id} height={size} width={size} />
width={size}
height={size}
coverArtUri={coverArtUri}
/>
<View style={{ <View style={{
flex: 1, flex: 1,
width: size, width: size,
@ -58,7 +53,7 @@ const AlbumItem: React.FC<{
const MemoAlbumItem = React.memo(AlbumItem); const MemoAlbumItem = React.memo(AlbumItem);
const AlbumListRenderItem: React.FC<{ item: Album }> = ({ item }) => ( 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 = () => { const AlbumsList = () => {

View File

@ -11,7 +11,6 @@ import GradientFlatList from '../common/GradientFlatList';
const ArtistItem: React.FC<{ item: Artist }> = ({ item }) => { const ArtistItem: React.FC<{ item: Artist }> = ({ item }) => {
const navigation = useNavigation(); const navigation = useNavigation();
const artistInfo = useAtomValue(artistInfoAtomFamily(item.id));
return ( return (
<Pressable <Pressable
@ -23,19 +22,7 @@ const ArtistItem: React.FC<{ item: Artist }> = ({ item }) => {
}} }}
onPress={() => navigation.navigate('ArtistView', { id: item.id, title: item.name })} onPress={() => navigation.navigate('ArtistView', { id: item.id, title: item.name })}
> >
<ArtistArt <ArtistArt id={item.id} width={56} height={56} />
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,
}}
/> */}
<Text style={{ <Text style={{
...textStyles.paragraph, ...textStyles.paragraph,
marginLeft: 12, marginLeft: 12,

View File

@ -61,16 +61,12 @@ export const useSetQueue = () => {
const tracks1 = tracks.slice(0, playIndex); const tracks1 = tracks.slice(0, playIndex);
const tracks2 = tracks.slice(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.add(tracks2);
await TrackPlayer.play(); await TrackPlayer.play();
await TrackPlayer.add(tracks1, playId); await TrackPlayer.add(tracks1, playId);
const queue = await TrackPlayer.getQueue(); 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[]; coverArtUris: string[];
} }
export interface ArtistArt {
uri?: string;
coverArtUris: string[];
}
export interface Album { export interface Album {
id: string; id: string;
artistId?: string; artistId?: string;
@ -24,6 +29,11 @@ export interface Album {
year?: number; year?: number;
} }
export interface AlbumArt {
uri?: string;
thumbUri?: string;
}
export interface AlbumWithSongs extends Album { export interface AlbumWithSongs extends Album {
songs: Song[]; songs: Song[];
} }

View File

@ -1,6 +1,6 @@
import { atom, useAtom } from 'jotai'; import { atom, useAtom } from 'jotai';
import { atomFamily, useAtomValue, useUpdateAtom } from 'jotai/utils'; 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 { SubsonicApiClient } from '../subsonic/api';
import { AlbumID3Element, ArtistID3Element, ArtistInfo2Element, ChildElement } from '../subsonic/elements'; import { AlbumID3Element, ArtistID3Element, ArtistInfo2Element, ChildElement } from '../subsonic/elements';
import { GetArtistResponse } from '../subsonic/responses'; 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); 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) => { export const artistInfoAtomFamily = atomFamily((id: string) => atom<ArtistInfo | undefined>(async (get) => {
const server = get(activeServerAtom); const server = get(activeServerAtom);
if (!server) { if (!server) {
@ -87,6 +107,29 @@ export const artistInfoAtomFamily = atomFamily((id: string) => atom<ArtistInfo |
return mapArtistInfo(artistResponse.data, artistInfoResponse.data.artistInfo, client); 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( function mapArtistInfo(
artistResponse: GetArtistResponse, artistResponse: GetArtistResponse,
artistInfo: ArtistInfo2Element, artistInfo: ArtistInfo2Element,

View File

@ -100,7 +100,7 @@ export class SubsonicApiClient {
const response = await fetch(this.buildUrl(method, params)); const response = await fetch(this.buildUrl(method, params));
const text = await response.text(); const text = await response.text();
console.log(text); // console.log(text);
const xml = new DOMParser().parseFromString(text); const xml = new DOMParser().parseFromString(text);
if (xml.documentElement.getAttribute('status') !== 'ok') { if (xml.documentElement.getAttribute('status') !== 'ok') {