mirror of
https://github.com/austinried/subtracks.git
synced 2025-12-27 09:09:29 +01:00
image optimizations
This commit is contained in:
parent
b1944f7791
commit
c8ed5bf5cb
2441
package-lock.json
generated
2441
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -18,6 +18,7 @@
|
|||||||
"md5": "^2.3.0",
|
"md5": "^2.3.0",
|
||||||
"react": "17.0.1",
|
"react": "17.0.1",
|
||||||
"react-native": "0.64.1",
|
"react-native": "0.64.1",
|
||||||
|
"react-native-fast-image": "^8.3.4",
|
||||||
"react-native-fs": "^2.18.0",
|
"react-native-fs": "^2.18.0",
|
||||||
"react-native-gesture-handler": "^1.10.3",
|
"react-native-gesture-handler": "^1.10.3",
|
||||||
"react-native-get-random-values": "^1.7.0",
|
"react-native-get-random-values": "^1.7.0",
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { Text, View, Image, Pressable } from 'react-native';
|
|||||||
import { BottomTabBarProps } from '@react-navigation/bottom-tabs';
|
import { BottomTabBarProps } from '@react-navigation/bottom-tabs';
|
||||||
import textStyles from '../../styles/text';
|
import textStyles from '../../styles/text';
|
||||||
import colors from '../../styles/colors';
|
import colors from '../../styles/colors';
|
||||||
|
import FastImage from 'react-native-fast-image';
|
||||||
|
|
||||||
const icons: {[key: string]: any} = {
|
const icons: {[key: string]: any} = {
|
||||||
home: {
|
home: {
|
||||||
@ -66,13 +67,13 @@ const BottomTabBar: React.FC<BottomTabBarProps> = ({ state, descriptors, navigat
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Image
|
<FastImage
|
||||||
source={isFocused ? img.fill : img.regular}
|
source={isFocused ? img.fill : img.regular}
|
||||||
style={{
|
style={{
|
||||||
height: 26,
|
height: 26,
|
||||||
width: 26,
|
width: 26,
|
||||||
tintColor: isFocused ? colors.text.primary : colors.text.secondary,
|
|
||||||
}}
|
}}
|
||||||
|
tintColor={isFocused ? colors.text.primary : colors.text.secondary}
|
||||||
/>
|
/>
|
||||||
<Text style={{
|
<Text style={{
|
||||||
...textStyles.xsmall,
|
...textStyles.xsmall,
|
||||||
|
|||||||
@ -1,62 +1,45 @@
|
|||||||
import React, { memo, useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { View, Image, Text, FlatList, Button, ListRenderItem } from 'react-native';
|
import { FlatList, Text, View } from 'react-native';
|
||||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
import FastImage from 'react-native-fast-image';
|
||||||
import { Album } from '../../models/music';
|
|
||||||
import { albumsState, albumState, useUpdateAlbums, albumIdsState, useCoverArtUri } from '../../state/albums';
|
|
||||||
import TopTabContainer from '../common/TopTabContainer';
|
|
||||||
import textStyles from '../../styles/text';
|
|
||||||
import { ScrollView } from 'react-native-gesture-handler';
|
|
||||||
import colors from '../../styles/colors';
|
|
||||||
import LinearGradient from 'react-native-linear-gradient';
|
import LinearGradient from 'react-native-linear-gradient';
|
||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
import { Album } from '../../models/music';
|
||||||
|
import { albumsState, useCoverArtUri, useUpdateAlbums } from '../../state/albums';
|
||||||
|
import colors from '../../styles/colors';
|
||||||
|
import textStyles from '../../styles/text';
|
||||||
|
import TopTabContainer from '../common/TopTabContainer';
|
||||||
|
|
||||||
const AlbumArt: React.FC<{ height: number, width: number, id?: string }> = ({ height, width, id }) => {
|
const AlbumArt: React.FC<{ height: number, width: number, id?: string }> = ({ height, width, id }) => {
|
||||||
const coverArtSource = useCoverArtUri(id);
|
const coverArtUri = useCoverArtUri(id);
|
||||||
|
|
||||||
// useEffect(() => {
|
|
||||||
// console.log(id);
|
|
||||||
// });
|
|
||||||
|
|
||||||
const Placeholder = (
|
const Placeholder = (
|
||||||
<LinearGradient
|
<LinearGradient
|
||||||
colors={[colors.accent, colors.accentLow]}
|
colors={[colors.accent, colors.accentLow]}
|
||||||
style={{
|
style={{ height, width }}
|
||||||
height, width,
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Image
|
<FastImage
|
||||||
source={require('../../../res/record-m.png')}
|
source={require('../../../res/record-m.png')}
|
||||||
style={{
|
style={{ height, width }}
|
||||||
height, width,
|
resizeMode={FastImage.resizeMode.contain}
|
||||||
resizeMode: 'contain',
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</LinearGradient>
|
</LinearGradient>
|
||||||
);
|
);
|
||||||
|
|
||||||
const CoverArt = (
|
const CoverArt = (
|
||||||
<View style={{
|
<View style={{ height, width }}>
|
||||||
height, width,
|
<FastImage
|
||||||
}}>
|
source={{ uri: coverArtUri }}
|
||||||
<Image
|
style={{ height, width }}
|
||||||
source={{ uri: coverArtSource }}
|
resizeMode={FastImage.resizeMode.contain}
|
||||||
style={{
|
|
||||||
height, width,
|
|
||||||
resizeMode: 'contain',
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
||||||
return coverArtSource ? CoverArt : Placeholder;
|
return coverArtUri ? CoverArt : Placeholder;
|
||||||
}
|
}
|
||||||
|
const MemoAlbumArt = React.memo(AlbumArt);
|
||||||
|
|
||||||
const AlbumItem: React.FC<{ id: string } > = ({ id }) => {
|
const AlbumItem: React.FC<{ name: string, coverArt?: string } > = ({ name, coverArt }) => {
|
||||||
const album = useRecoilValue(albumState(id));
|
|
||||||
|
|
||||||
// useEffect(() => {
|
|
||||||
// console.log(album.name);
|
|
||||||
// });
|
|
||||||
|
|
||||||
const size = 125;
|
const size = 125;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -68,10 +51,10 @@ const AlbumItem: React.FC<{ id: string } > = ({ id }) => {
|
|||||||
// width: size,
|
// width: size,
|
||||||
flex: 1/3,
|
flex: 1/3,
|
||||||
}}>
|
}}>
|
||||||
<AlbumArt
|
<MemoAlbumArt
|
||||||
width={size}
|
width={size}
|
||||||
height={size}
|
height={size}
|
||||||
id={album.coverArt}
|
id={coverArt}
|
||||||
/>
|
/>
|
||||||
<View style={{
|
<View style={{
|
||||||
flex: 1,
|
flex: 1,
|
||||||
@ -85,7 +68,7 @@ const AlbumItem: React.FC<{ id: string } > = ({ id }) => {
|
|||||||
}}
|
}}
|
||||||
numberOfLines={2}
|
numberOfLines={2}
|
||||||
>
|
>
|
||||||
{album.name}
|
{name}
|
||||||
</Text>
|
</Text>
|
||||||
<Text
|
<Text
|
||||||
style={{
|
style={{
|
||||||
@ -94,28 +77,24 @@ const AlbumItem: React.FC<{ id: string } > = ({ id }) => {
|
|||||||
}}
|
}}
|
||||||
numberOfLines={1}
|
numberOfLines={1}
|
||||||
>
|
>
|
||||||
{album.name}
|
{name}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
const MemoAlbumItem = React.memo(AlbumItem);
|
||||||
|
|
||||||
const MemoAlbumItem = memo(AlbumItem, (prev, next) => {
|
const AlbumListRenderItem: React.FC<{ item: Album }> = ({ item }) => (
|
||||||
// console.log('prev: ' + JSON.stringify(prev) + ' next: ' + JSON.stringify(next))
|
<MemoAlbumItem name={item.name} coverArt={item.coverArt} />
|
||||||
return prev.id == next.id;
|
);
|
||||||
});
|
|
||||||
|
|
||||||
const AlbumsList = () => {
|
const AlbumsList = () => {
|
||||||
const albumIds = useRecoilValue(albumIdsState);
|
const albums = useRecoilValue(albumsState);
|
||||||
const updateAlbums = useUpdateAlbums();
|
const updateAlbums = useUpdateAlbums();
|
||||||
|
|
||||||
const [refreshing, setRefreshing] = useState(false);
|
const [refreshing, setRefreshing] = useState(false);
|
||||||
|
|
||||||
const renderItem: React.FC<{ item: string }> = ({ item }) => (
|
|
||||||
<MemoAlbumItem id={item} />
|
|
||||||
);
|
|
||||||
|
|
||||||
const refresh = async () => {
|
const refresh = async () => {
|
||||||
setRefreshing(true);
|
setRefreshing(true);
|
||||||
await updateAlbums();
|
await updateAlbums();
|
||||||
@ -123,7 +102,7 @@ const AlbumsList = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!refreshing && albumIds.length === 0) {
|
if (!refreshing && Object.keys(albums).length === 0) {
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -131,13 +110,13 @@ const AlbumsList = () => {
|
|||||||
return (
|
return (
|
||||||
<View style={{ flex: 1 }}>
|
<View style={{ flex: 1 }}>
|
||||||
<FlatList
|
<FlatList
|
||||||
data={albumIds}
|
data={Object.values(albums)}
|
||||||
renderItem={renderItem}
|
renderItem={AlbumListRenderItem}
|
||||||
keyExtractor={item => item}
|
keyExtractor={item => item.id}
|
||||||
onRefresh={refresh}
|
onRefresh={refresh}
|
||||||
refreshing={refreshing}
|
refreshing={refreshing}
|
||||||
numColumns={3}
|
numColumns={3}
|
||||||
removeClippedSubviews={false}
|
removeClippedSubviews={true}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
@ -151,4 +130,4 @@ const AlbumsTab = () => (
|
|||||||
</TopTabContainer>
|
</TopTabContainer>
|
||||||
);
|
);
|
||||||
|
|
||||||
export default AlbumsTab;
|
export default React.memo(AlbumsTab);
|
||||||
|
|||||||
@ -10,6 +10,4 @@ export interface Album {
|
|||||||
name: string;
|
name: string;
|
||||||
starred?: Date;
|
starred?: Date;
|
||||||
coverArt?: string;
|
coverArt?: string;
|
||||||
coverArtPath?: string;
|
|
||||||
coverArtModified?: Date;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { atom, DefaultValue, selector, selectorFamily, useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
|
import { atom, DefaultValue, selector, selectorFamily, useRecoilValue, useSetRecoilState } from 'recoil';
|
||||||
import { SubsonicApiClient } from '../subsonic/api';
|
import { SubsonicApiClient } from '../subsonic/api';
|
||||||
import { activeServer } from './settings'
|
import { activeServer } from './settings'
|
||||||
import { Album } from '../models/music';
|
import { Album } from '../models/music';
|
||||||
@ -23,21 +23,11 @@ export const albumsState = atom<{ [id: string]: Album }>({
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
export const albumIdsState = selector<string[]>({
|
|
||||||
key: 'albumIdsState',
|
|
||||||
get: ({get}) => Object.keys(get(albumsState)),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const albumState = selectorFamily<Album, string>({
|
export const albumState = selectorFamily<Album, string>({
|
||||||
key: 'albumState',
|
key: 'albumState',
|
||||||
get: id => ({ get }) => {
|
get: id => ({ get }) => {
|
||||||
return get(albumsState)[id];
|
return get(albumsState)[id];
|
||||||
},
|
},
|
||||||
// set: id => ({ set, get }, newValue) => {
|
|
||||||
// if (!(newValue instanceof DefaultValue)) {
|
|
||||||
// set(albumsState, prevState => ({ ...prevState, [id]: newValue }));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const useUpdateAlbums = () => {
|
export const useUpdateAlbums = () => {
|
||||||
@ -49,7 +39,7 @@ export const useUpdateAlbums = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const client = new SubsonicApiClient(server.address, server.username, server.token, server.salt);
|
const client = new SubsonicApiClient(server);
|
||||||
const response = await client.getAlbumList2({ type: 'alphabeticalByArtist', size: 50 });
|
const response = await client.getAlbumList2({ type: 'alphabeticalByArtist', size: 50 });
|
||||||
|
|
||||||
const albums = response.data.albums.reduce((acc, x) => {
|
const albums = response.data.albums.reduce((acc, x) => {
|
||||||
@ -65,7 +55,7 @@ export const useUpdateAlbums = () => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export function useCoverArtUri(id: string | undefined): string | undefined {
|
export function useCoverArtUri(id?: string): string | undefined {
|
||||||
if (!id) {
|
if (!id) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@ -100,7 +90,7 @@ export function useCoverArtUri(id: string | undefined): string | undefined {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const client = new SubsonicApiClient(server.address, server.username, server.token, server.salt);
|
const client = new SubsonicApiClient(server);
|
||||||
await client.getCoverArt({ id });
|
await client.getCoverArt({ id });
|
||||||
|
|
||||||
setCoverArtSource(fileUri);
|
setCoverArtSource(fileUri);
|
||||||
|
|||||||
@ -30,7 +30,7 @@ export const useUpdateArtists = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const client = new SubsonicApiClient(server.address, server.username, server.token, server.salt);
|
const client = new SubsonicApiClient(server);
|
||||||
const response = await client.getArtists();
|
const response = await client.getArtists();
|
||||||
|
|
||||||
setArtists(response.data.artists.map(x => ({
|
setArtists(response.data.artists.map(x => ({
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { DOMParser } from 'xmldom';
|
|||||||
import RNFS from 'react-native-fs';
|
import RNFS from 'react-native-fs';
|
||||||
import { GetAlbumList2Params, GetAlbumListParams, GetArtistInfo2Params, GetArtistInfoParams, GetCoverArtParams, GetIndexesParams } from './params';
|
import { GetAlbumList2Params, GetAlbumListParams, GetArtistInfo2Params, GetArtistInfoParams, GetCoverArtParams, GetIndexesParams } from './params';
|
||||||
import { GetAlbumList2Response, GetAlbumListResponse, GetArtistInfo2Response, GetArtistInfoResponse, GetArtistsResponse, GetIndexesResponse, SubsonicResponse } from './responses';
|
import { GetAlbumList2Response, GetAlbumListResponse, GetArtistInfo2Response, GetArtistInfoResponse, GetArtistsResponse, GetIndexesResponse, SubsonicResponse } from './responses';
|
||||||
|
import { ServerSettings } from '../models/settings';
|
||||||
|
|
||||||
export class SubsonicApiError extends Error {
|
export class SubsonicApiError extends Error {
|
||||||
method: string;
|
method: string;
|
||||||
@ -56,14 +57,14 @@ export class SubsonicApiClient {
|
|||||||
|
|
||||||
private params: URLSearchParams
|
private params: URLSearchParams
|
||||||
|
|
||||||
constructor(address: string, username: string, token: string, salt: string) {
|
constructor(server: ServerSettings) {
|
||||||
this.address = address;
|
this.address = server.address;
|
||||||
this.username = username;
|
this.username = server.username;
|
||||||
|
|
||||||
this.params = new URLSearchParams();
|
this.params = new URLSearchParams();
|
||||||
this.params.append('u', username);
|
this.params.append('u', server.username);
|
||||||
this.params.append('t', token);
|
this.params.append('t', server.token);
|
||||||
this.params.append('s', salt);
|
this.params.append('s', server.salt);
|
||||||
this.params.append('v', '1.15.0');
|
this.params.append('v', '1.15.0');
|
||||||
this.params.append('c', 'subsonify-cool-unique-app-string')
|
this.params.append('c', 'subsonify-cool-unique-app-string')
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user