reworked placeholder

also fixed downloads finishing early and reading corrupted files
This commit is contained in:
austinried 2021-06-22 10:49:58 +09:00
parent b4fee0aff4
commit 41d78f0d2f
6 changed files with 127 additions and 33 deletions

View File

@ -4,6 +4,9 @@ import { RecoilRoot } from 'recoil';
import SplashPage from './src/components/SplashPage'; import SplashPage from './src/components/SplashPage';
import RootNavigator from './src/components/navigation/RootNavigator'; import RootNavigator from './src/components/navigation/RootNavigator';
import { enableScreens } from 'react-native-screens';
enableScreens();
const App = () => ( const App = () => (
<RecoilRoot> <RecoilRoot>
<SplashPage> <SplashPage>

View File

@ -1,6 +1,7 @@
package com.subsonify; package com.subsonify;
import com.facebook.react.ReactActivity; import com.facebook.react.ReactActivity;
import android.os.Bundle;
public class MainActivity extends ReactActivity { public class MainActivity extends ReactActivity {
@ -12,4 +13,9 @@ public class MainActivity extends ReactActivity {
protected String getMainComponentName() { protected String getMainComponentName() {
return "SubSonify"; return "SubSonify";
} }
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(null);
}
} }

View File

@ -75,7 +75,7 @@ const BottomTabBar: React.FC<BottomTabBarProps> = ({ state, descriptors, navigat
}} }}
/> />
<Text style={{ <Text style={{
...textStyles.small, ...textStyles.xsmall,
color: isFocused ? colors.text.primary : colors.text.secondary, color: isFocused ? colors.text.primary : colors.text.secondary,
}}> }}>
{label} {label}

View File

@ -12,22 +12,42 @@ import LinearGradient from 'react-native-linear-gradient';
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 coverArtSource = useCoverArtUri(id);
return ( // useEffect(() => {
// console.log(id);
// });
const Placeholder = (
<LinearGradient <LinearGradient
colors={[colors.accent, colors.accentLow]} colors={[colors.accent, colors.accentLow]}
style={{ style={{
height, width, height, width,
backgroundColor: 'white',
}} }}
> >
<Image <Image
source={coverArtSource ? { uri: coverArtSource } : require('../../../res/record-m.png')} source={require('../../../res/record-m.png')}
style={{ style={{
height, width, height, width,
resizeMode: 'contain',
}} }}
/> />
</LinearGradient> </LinearGradient>
) );
const CoverArt = (
<View style={{
height, width,
}}>
<Image
source={{ uri: coverArtSource }}
style={{
height, width,
resizeMode: 'contain',
}}
/>
</View>
);
return coverArtSource ? CoverArt : Placeholder;
} }
const AlbumItem: React.FC<{ id: string } > = ({ id }) => { const AlbumItem: React.FC<{ id: string } > = ({ id }) => {
@ -37,23 +57,46 @@ const AlbumItem: React.FC<{ id: string } > = ({ id }) => {
// console.log(album.name); // console.log(album.name);
// }); // });
const size = 180;
return ( return (
<View style={{ <View style={{
flexDirection: 'row', // flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
marginVertical: 6, marginVertical: 10,
marginLeft: 6, // marginLeft: 6,
// height: 200, // width: size,
flex: 1/2,
}}> }}>
<AlbumArt <AlbumArt
width={56} width={size}
height={56} height={size}
id={album.coverArt} id={album.coverArt}
/> />
<Text style={{ <View style={{
...textStyles.paragraph, flex: 1,
marginLeft: 12, width: size,
}}>{album.name}</Text> // alignItems: 'baseline',
}}>
<Text
style={{
...textStyles.itemTitle,
marginTop: 5,
}}
numberOfLines={2}
>
{album.name}
</Text>
<Text
style={{
...textStyles.itemSubtitle,
marginTop: 3,
}}
numberOfLines={1}
>
{album.name}
</Text>
</View>
</View> </View>
); );
} }
@ -83,26 +126,19 @@ const AlbumsList = () => {
if (!refreshing && albumIds.length === 0) { if (!refreshing && albumIds.length === 0) {
refresh(); refresh();
} }
}) });
return ( return (
<View style={{ flex: 1 }}> <View style={{ flex: 1 }}>
{/* <Button
title='Update'
onPress={updateAlbums}
/> */}
<FlatList <FlatList
data={albumIds} data={albumIds}
renderItem={renderItem} renderItem={renderItem}
keyExtractor={item => item} keyExtractor={item => item}
onRefresh={refresh} onRefresh={refresh}
refreshing={refreshing} refreshing={refreshing}
numColumns={2}
removeClippedSubviews={false}
/> />
{/* <ScrollView>
{Object.values(albums).map(item => (
<AlbumItem item={item} key={item.id} />
))}
</ScrollView> */}
</View> </View>
); );
} }

View File

@ -12,7 +12,19 @@ const header: TextStyle = {
fontSize: 18, fontSize: 18,
}; };
const small: TextStyle = { const itemTitle: TextStyle = {
...paragraph,
fontSize: 14,
// fontFamily: 'Ubuntu-Light',
};
const itemSubtitle: TextStyle = {
...paragraph,
fontSize: 12,
color: colors.text.secondary,
};
const xsmall: TextStyle = {
...paragraph, ...paragraph,
fontSize: 11, fontSize: 11,
}; };
@ -20,5 +32,7 @@ const small: TextStyle = {
export default { export default {
paragraph, paragraph,
header, header,
small, itemTitle,
itemSubtitle,
xsmall
}; };

View File

@ -18,6 +18,38 @@ export class SubsonicApiError extends Error {
} }
} }
type QueuePromise = () => Promise<any>;
class Queue {
maxSimultaneously: number;
private active = 0;
private queue: QueuePromise[] = [];
constructor(maxSimultaneously = 1) {
this.maxSimultaneously = maxSimultaneously;
}
async enqueue(func: QueuePromise) {
if (++this.active > this.maxSimultaneously) {
await new Promise(resolve => this.queue.push(resolve as QueuePromise));
}
try {
return await func();
} catch (err) {
throw err;
} finally {
this.active--;
if (this.queue.length) {
(this.queue.shift() as QueuePromise)();
}
}
}
}
const downloadQueue = new Queue(1);
export class SubsonicApiClient { export class SubsonicApiClient {
address: string; address: string;
username: string; username: string;
@ -36,7 +68,7 @@ export class SubsonicApiClient {
this.params.append('c', 'subsonify-cool-unique-app-string') this.params.append('c', 'subsonify-cool-unique-app-string')
} }
private buildUrl(method: string, params?: {[key: string]: any}): string { private buildUrl(method: string, params?: { [key: string]: any }): string {
let query = this.params.toString(); let query = this.params.toString();
if (params) { if (params) {
const urlParams = this.obj2Params(params); const urlParams = this.obj2Params(params);
@ -50,16 +82,19 @@ export class SubsonicApiClient {
return url; return url;
} }
private async apiDownload(method: string, path: string, params?: {[key: string]: any}): Promise<string> { private async apiDownload(method: string, path: string, params?: { [key: string]: any }): Promise<string> {
await RNFS.downloadFile({ const download = RNFS.downloadFile({
fromUrl: this.buildUrl(method, params), fromUrl: this.buildUrl(method, params),
toFile: path, toFile: path,
}).promise; }).promise;
await downloadQueue.enqueue(() => download);
await downloadQueue.enqueue(() => new Promise(resolve => setTimeout(resolve, 100)));
return path; return path;
} }
private async apiGetXml(method: string, params?: {[key: string]: any}): Promise<Document> { private async apiGetXml(method: string, params?: { [key: string]: any }): Promise<Document> {
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();
@ -73,7 +108,7 @@ export class SubsonicApiClient {
return xml; return xml;
} }
private obj2Params(obj: {[key: string]: any}): URLSearchParams | undefined { private obj2Params(obj: { [key: string]: any }): URLSearchParams | undefined {
const keys = Object.keys(obj); const keys = Object.keys(obj);
if (keys.length === 0) { if (keys.length === 0) {
return undefined; return undefined;