mirror of
https://github.com/austinried/subtracks.git
synced 2025-12-27 00:59:28 +01:00
switched to navigation-react-native
seems much simpler, but now i may need some other deps...
This commit is contained in:
parent
c7d65e0a58
commit
17fe1b9850
9
App.tsx
9
App.tsx
@ -1,18 +1,13 @@
|
||||
import React from 'react';
|
||||
import { NavigationContainer } from '@react-navigation/native';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
import SplashPage from './src/components/SplashPage';
|
||||
import RootNavigator from './src/components/navigation/RootNavigator';
|
||||
|
||||
import { enableScreens } from 'react-native-screens';
|
||||
enableScreens();
|
||||
import BottomTabNavigator from './src/components/navigation/BottomTabNavigator';
|
||||
|
||||
const App = () => (
|
||||
<RecoilRoot>
|
||||
<SplashPage>
|
||||
<NavigationContainer>
|
||||
<RootNavigator />
|
||||
</NavigationContainer>
|
||||
<BottomTabNavigator />
|
||||
</SplashPage>
|
||||
</RecoilRoot>
|
||||
);
|
||||
|
||||
9
android/app/proguard-rules.pro
vendored
9
android/app/proguard-rules.pro
vendored
@ -9,4 +9,11 @@
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
-keep class com.facebook.react.turbomodule.** { *; }
|
||||
-keep public class com.dylanvann.fastimage.* {*;}
|
||||
-keep public class com.dylanvann.fastimage.** {*;}
|
||||
-keep public class * implements com.bumptech.glide.module.GlideModule
|
||||
-keep public class * extends com.bumptech.glide.module.AppGlideModule
|
||||
-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
|
||||
**[] $VALUES;
|
||||
public *;
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package com.subsonify;
|
||||
|
||||
import com.facebook.react.ReactActivity;
|
||||
import android.os.Bundle;
|
||||
|
||||
public class MainActivity extends ReactActivity {
|
||||
|
||||
@ -13,9 +12,4 @@ public class MainActivity extends ReactActivity {
|
||||
protected String getMainComponentName() {
|
||||
return "SubSonify";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(null);
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,10 +12,6 @@ import com.facebook.soloader.SoLoader;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.List;
|
||||
|
||||
// for reanimated
|
||||
import com.facebook.react.bridge.JSIModulePackage;
|
||||
import com.swmansion.reanimated.ReanimatedJSIModulePackage;
|
||||
|
||||
public class MainApplication extends Application implements ReactApplication {
|
||||
|
||||
private final ReactNativeHost mReactNativeHost =
|
||||
@ -38,12 +34,6 @@ public class MainApplication extends Application implements ReactApplication {
|
||||
protected String getJSMainModuleName() {
|
||||
return "index";
|
||||
}
|
||||
|
||||
// for reanimated
|
||||
@Override
|
||||
protected JSIModulePackage getJSIModulePackage() {
|
||||
return new ReanimatedJSIModulePackage();
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
|
||||
7
android/app/src/main/res/anim/fade_in.xml
Normal file
7
android/app/src/main/res/anim/fade_in.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<alpha
|
||||
android:duration="300"
|
||||
android:fromAlpha="0"
|
||||
android:toAlpha="1" />
|
||||
</set>
|
||||
7
android/app/src/main/res/anim/fade_out.xml
Normal file
7
android/app/src/main/res/anim/fade_out.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<alpha
|
||||
android:duration="300"
|
||||
android:fromAlpha="1"
|
||||
android:toAlpha="0" />
|
||||
</set>
|
||||
7
android/app/src/main/res/anim/slide_in.xml
Normal file
7
android/app/src/main/res/anim/slide_in.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<translate
|
||||
android:duration="300"
|
||||
android:fromXDelta="100%"
|
||||
android:toXDelta="0" />
|
||||
</set>
|
||||
7
android/app/src/main/res/anim/slide_out.xml
Normal file
7
android/app/src/main/res/anim/slide_out.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<translate
|
||||
android:duration="300"
|
||||
android:fromXDelta="0"
|
||||
android:toXDelta="100%" />
|
||||
</set>
|
||||
@ -1,7 +1,3 @@
|
||||
module.exports = {
|
||||
presets: ['module:metro-react-native-babel-preset'],
|
||||
plugins: [
|
||||
// reanimated has to be listed last in plugins
|
||||
'react-native-reanimated/plugin',
|
||||
],
|
||||
};
|
||||
|
||||
1
index.js
1
index.js
@ -1,4 +1,3 @@
|
||||
import 'react-native-gesture-handler';
|
||||
import 'react-native-get-random-values';
|
||||
|
||||
import { AppRegistry, LogBox } from 'react-native';
|
||||
|
||||
3118
package-lock.json
generated
3118
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
14
package.json
14
package.json
@ -10,23 +10,17 @@
|
||||
"lint": "eslint . --ext .js,.jsx,.ts,.tsx"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-native-community/masked-view": "^0.1.11",
|
||||
"@react-navigation/bottom-tabs": "^5.11.11",
|
||||
"@react-navigation/material-top-tabs": "^5.3.15",
|
||||
"@react-navigation/native": "^5.9.4",
|
||||
"@react-navigation/stack": "^5.14.5",
|
||||
"md5": "^2.3.0",
|
||||
"navigation": "^5.5.0",
|
||||
"navigation-react": "^4.3.0",
|
||||
"navigation-react-native": "^7.4.2",
|
||||
"react": "17.0.1",
|
||||
"react-native": "0.64.1",
|
||||
"react-native-fast-image": "^8.3.4",
|
||||
"react-native-fs": "^2.18.0",
|
||||
"react-native-gesture-handler": "^1.10.3",
|
||||
"react-native-get-random-values": "^1.7.0",
|
||||
"react-native-linear-gradient": "^2.5.6",
|
||||
"react-native-reanimated": "^2.0.0",
|
||||
"react-native-safe-area-context": "^3.2.0",
|
||||
"react-native-screens": "^3.4.0",
|
||||
"react-native-sqlite-storage": "^5.0.0",
|
||||
"react-native-tab-view": "^2.16.0",
|
||||
"react-native-track-player": "^1.2.7",
|
||||
"recoil": "^0.3.1",
|
||||
"uuid": "^8.3.2",
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import { Button, FlatList, Text, View } from 'react-native';
|
||||
import { useRecoilValue, useResetRecoilState } from 'recoil';
|
||||
import { artistsState, useUpdateArtists } from '../state/artists';
|
||||
@ -21,6 +21,12 @@ const List = () => {
|
||||
<ArtistItem item={item} />
|
||||
);
|
||||
|
||||
console.log('rendering artists');
|
||||
|
||||
useEffect(() => {
|
||||
console.log('mounting artists');
|
||||
});
|
||||
|
||||
return (
|
||||
<FlatList
|
||||
data={artists}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { Button, TextInput, View, Text } from 'react-native';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
@ -6,8 +6,7 @@ import md5 from 'md5';
|
||||
import { musicDb, settingsDb } from '../clients';
|
||||
import { appSettingsState, serversState } from '../state/settings';
|
||||
import { DbStorage } from '../storage/db';
|
||||
import { StackScreenProps } from '@react-navigation/stack';
|
||||
import { useNavigation } from '@react-navigation/core';
|
||||
import { NavigationContext } from 'navigation-react';
|
||||
|
||||
const RecreateDbButton: React.FC<{ db: DbStorage, title: string }> = ({ db, title }) => {
|
||||
const [inProgress, setInProgress] = useState(false);
|
||||
@ -32,14 +31,14 @@ const RecreateDbButton: React.FC<{ db: DbStorage, title: string }> = ({ db, titl
|
||||
}
|
||||
|
||||
const DbControls = () => {
|
||||
const navigation = useNavigation();
|
||||
const { stateNavigator } = useContext(NavigationContext);
|
||||
return (
|
||||
<View>
|
||||
<RecreateDbButton db={musicDb} title='Music' />
|
||||
<RecreateDbButton db={settingsDb} title='Settings' />
|
||||
<Button
|
||||
title='Now Playing'
|
||||
onPress={() => navigation.navigate('Now Playing')}
|
||||
onPress={() => stateNavigator.navigate('nowplaying')}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
import React from 'react';
|
||||
import { Text, View, Image, Pressable } from 'react-native';
|
||||
import { BottomTabBarProps } from '@react-navigation/bottom-tabs';
|
||||
import textStyles from '../../styles/text';
|
||||
import colors from '../../styles/colors';
|
||||
import { useContext } from 'react';
|
||||
import { NavigationContext } from 'navigation-react';
|
||||
import FastImage from 'react-native-fast-image';
|
||||
|
||||
const icons: {[key: string]: any} = {
|
||||
home: {
|
||||
@ -23,7 +25,43 @@ const icons: {[key: string]: any} = {
|
||||
},
|
||||
}
|
||||
|
||||
const BottomTabBar: React.FC<BottomTabBarProps> = ({ state, descriptors, navigation }) => {
|
||||
const BottomTabButton: React.FC<{
|
||||
route: string,
|
||||
title: string,
|
||||
onPress: () => void,
|
||||
isFocused: boolean,
|
||||
}> = ({ route, title, onPress, isFocused }) => {
|
||||
const img = icons[route];
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
onPress={onPress}
|
||||
style={{
|
||||
alignItems: 'center',
|
||||
flex: 1,
|
||||
}}
|
||||
>
|
||||
<FastImage
|
||||
source={isFocused ? img.fill : img.regular}
|
||||
style={{
|
||||
height: 26,
|
||||
width: 26,
|
||||
}}
|
||||
tintColor={isFocused ? colors.text.primary : colors.text.secondary}
|
||||
/>
|
||||
<Text style={{
|
||||
...textStyles.xsmall,
|
||||
color: isFocused ? colors.text.primary : colors.text.secondary,
|
||||
}}>
|
||||
{title}
|
||||
</Text>
|
||||
</Pressable>
|
||||
);
|
||||
}
|
||||
|
||||
const BottomTabBar = () => {
|
||||
const { stateNavigator } = useContext(NavigationContext);
|
||||
|
||||
return (
|
||||
<View style={{
|
||||
height: 54,
|
||||
@ -33,56 +71,19 @@ const BottomTabBar: React.FC<BottomTabBarProps> = ({ state, descriptors, navigat
|
||||
justifyContent: 'space-around',
|
||||
paddingHorizontal: 28,
|
||||
}}>
|
||||
{state.routes.map((route, index) => {
|
||||
const { options } = descriptors[route.key] as any;
|
||||
const label =
|
||||
options.tabBarLabel !== undefined
|
||||
? options.tabBarLabel as string
|
||||
: options.title !== undefined
|
||||
? options.title
|
||||
: route.name;
|
||||
|
||||
const isFocused = state.index === index;
|
||||
const img = icons[options.icon];
|
||||
|
||||
const onPress = () => {
|
||||
const event = navigation.emit({
|
||||
type: 'tabPress',
|
||||
target: route.key,
|
||||
canPreventDefault: true,
|
||||
});
|
||||
|
||||
if (!isFocused && !event.defaultPrevented) {
|
||||
navigation.navigate(route.name);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
key={route.key}
|
||||
onPress={onPress}
|
||||
style={{
|
||||
alignItems: 'center',
|
||||
flex: 1,
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
source={isFocused ? img.fill : img.regular}
|
||||
style={{
|
||||
height: 26,
|
||||
width: 26,
|
||||
tintColor: isFocused ? colors.text.primary : colors.text.secondary,
|
||||
}}
|
||||
/>
|
||||
<Text style={{
|
||||
...textStyles.xsmall,
|
||||
color: isFocused ? colors.text.primary : colors.text.secondary,
|
||||
}}>
|
||||
{label}
|
||||
</Text>
|
||||
</Pressable>
|
||||
);
|
||||
})}
|
||||
{Object.values(stateNavigator.states).map(state => (
|
||||
<BottomTabButton
|
||||
key={state.key}
|
||||
route={state.key}
|
||||
title={state.title}
|
||||
onPress={() => {
|
||||
if (stateNavigator.stateContext.state.key !== state.key) {
|
||||
stateNavigator.navigate(state.key);
|
||||
}
|
||||
}}
|
||||
isFocused={stateNavigator.stateContext.state.key === state.key}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,46 +1,56 @@
|
||||
import React from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
import { MaterialTopTabBarProps } from '@react-navigation/material-top-tabs';
|
||||
import { NavigationContext } from 'navigation-react';
|
||||
import React, { useContext } from 'react';
|
||||
import { Pressable, Text, View } from 'react-native';
|
||||
import colors from '../../styles/colors';
|
||||
import textStyles from '../../styles/text';
|
||||
|
||||
const TopTabBar: React.FC<MaterialTopTabBarProps> = ({ state, descriptors }) => {
|
||||
const TopTabButton: React.FC<{
|
||||
title: string,
|
||||
onPress: () => void,
|
||||
isFocused: boolean,
|
||||
}> = ({ title, onPress, isFocused }) => {
|
||||
const color = isFocused ? colors.text.primary : colors.text.secondary;
|
||||
const borderBottomColor = isFocused ? colors.accent : colors.gradient.high;
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
onPress={onPress}
|
||||
style={{
|
||||
borderBottomColor,
|
||||
borderBottomWidth: 1.5,
|
||||
width: 94,
|
||||
height: 44,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}>
|
||||
<Text style={{
|
||||
...textStyles.header, color,
|
||||
}}>{title}</Text>
|
||||
</Pressable>
|
||||
);
|
||||
}
|
||||
|
||||
const TopTabBar = () => {
|
||||
const { stateNavigator } = useContext(NavigationContext);
|
||||
|
||||
return (
|
||||
<View style={{
|
||||
backgroundColor: colors.gradient.high,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'flex-start',
|
||||
justifyContent: 'flex-start',
|
||||
justifyContent: 'space-evenly',
|
||||
}}>
|
||||
{state.routes.map((route, index) => {
|
||||
const { options } = descriptors[route.key];
|
||||
const label =
|
||||
options.tabBarLabel !== undefined
|
||||
? options.tabBarLabel
|
||||
: options.title !== undefined
|
||||
? options.title
|
||||
: route.name;
|
||||
|
||||
const isFocused = state.index === index;
|
||||
const color = isFocused ? colors.text.primary : colors.text.secondary;
|
||||
const borderBottomColor = isFocused ? colors.accent : colors.gradient.high;
|
||||
|
||||
return (
|
||||
<View key={route.key} style={{
|
||||
borderBottomColor,
|
||||
borderBottomWidth: 1.5,
|
||||
// paddingVertical: 8,
|
||||
width: 94,
|
||||
height: 44,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}>
|
||||
<Text style={{
|
||||
...textStyles.header, color,
|
||||
}}>{label}</Text>
|
||||
</View>
|
||||
);
|
||||
})}
|
||||
{Object.values(stateNavigator.states).map(state => (
|
||||
<TopTabButton
|
||||
key={state.key}
|
||||
title={state.title}
|
||||
onPress={() => {
|
||||
if (stateNavigator.stateContext.state.key !== state.key) {
|
||||
stateNavigator.navigate(state.key);
|
||||
}
|
||||
}}
|
||||
isFocused={stateNavigator.stateContext.state.key === state.key}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,17 +1,16 @@
|
||||
import React, { memo, useEffect, useState } from 'react';
|
||||
import { View, Image, Text, FlatList, Button, ListRenderItem } from 'react-native';
|
||||
import { View, Image, Text, FlatList, Button, ListRenderItem, ScrollView } from 'react-native';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
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 FastImage from 'react-native-fast-image';
|
||||
import RNFS from 'react-native-fs';
|
||||
|
||||
const AlbumArt: React.FC<{ height: number, width: number, id?: string }> = ({ height, width, id }) => {
|
||||
const coverArtSource = useCoverArtUri(id);
|
||||
|
||||
// useEffect(() => {
|
||||
// console.log(id);
|
||||
// });
|
||||
@ -19,16 +18,12 @@ const AlbumArt: React.FC<{ height: number, width: number, id?: string }> = ({ he
|
||||
const Placeholder = (
|
||||
<LinearGradient
|
||||
colors={[colors.accent, colors.accentLow]}
|
||||
style={{
|
||||
height, width,
|
||||
}}
|
||||
style={{ height, width }}
|
||||
>
|
||||
<Image
|
||||
<FastImage
|
||||
source={require('../../../res/record-m.png')}
|
||||
style={{
|
||||
height, width,
|
||||
resizeMode: 'contain',
|
||||
}}
|
||||
style={{ height, width }}
|
||||
resizeMode={FastImage.resizeMode.contain}
|
||||
/>
|
||||
</LinearGradient>
|
||||
);
|
||||
@ -37,24 +32,23 @@ const AlbumArt: React.FC<{ height: number, width: number, id?: string }> = ({ he
|
||||
<View style={{
|
||||
height, width,
|
||||
}}>
|
||||
<Image
|
||||
source={{ uri: coverArtSource }}
|
||||
style={{
|
||||
height, width,
|
||||
resizeMode: 'contain',
|
||||
}}
|
||||
<FastImage
|
||||
source={{ uri: `file://${RNFS.DocumentDirectoryPath}/image_cache/${id}` }}
|
||||
style={{ height, width }}
|
||||
resizeMode={FastImage.resizeMode.contain}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
|
||||
return coverArtSource ? CoverArt : Placeholder;
|
||||
return id ? CoverArt : Placeholder;
|
||||
}
|
||||
|
||||
const AlbumItem: React.FC<{ id: string } > = ({ id }) => {
|
||||
const album = useRecoilValue(albumState(id));
|
||||
|
||||
const AlbumItem: React.FC<{
|
||||
name: string,
|
||||
coverArt?: string,
|
||||
} > = ({ name, coverArt }) => {
|
||||
// useEffect(() => {
|
||||
// console.log(album.name);
|
||||
// console.log(name);
|
||||
// });
|
||||
|
||||
const size = 125;
|
||||
@ -66,12 +60,13 @@ const AlbumItem: React.FC<{ id: string } > = ({ id }) => {
|
||||
marginVertical: 8,
|
||||
// marginLeft: 6,
|
||||
// width: size,
|
||||
height: 180,
|
||||
flex: 1/3,
|
||||
}}>
|
||||
<AlbumArt
|
||||
width={size}
|
||||
height={size}
|
||||
id={album.coverArt}
|
||||
id={coverArt}
|
||||
/>
|
||||
<View style={{
|
||||
flex: 1,
|
||||
@ -85,7 +80,7 @@ const AlbumItem: React.FC<{ id: string } > = ({ id }) => {
|
||||
}}
|
||||
numberOfLines={2}
|
||||
>
|
||||
{album.name}
|
||||
{name}
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
@ -94,36 +89,34 @@ const AlbumItem: React.FC<{ id: string } > = ({ id }) => {
|
||||
}}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{album.name}
|
||||
{name}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const MemoAlbumItem = memo(AlbumItem, (prev, next) => {
|
||||
// console.log('prev: ' + JSON.stringify(prev) + ' next: ' + JSON.stringify(next))
|
||||
return prev.id == next.id;
|
||||
});
|
||||
function renderItem(props: { item: Album }) {
|
||||
return <AlbumItem name={props.item.name} coverArt={props.item.coverArt} />;
|
||||
}
|
||||
|
||||
const AlbumsList = () => {
|
||||
const albumIds = useRecoilValue(albumIdsState);
|
||||
const albums = useRecoilValue(albumsState);
|
||||
const updateAlbums = useUpdateAlbums();
|
||||
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
|
||||
const renderItem: React.FC<{ item: string }> = ({ item }) => (
|
||||
<MemoAlbumItem id={item} />
|
||||
);
|
||||
|
||||
const refresh = async () => {
|
||||
setRefreshing(true);
|
||||
await updateAlbums();
|
||||
setRefreshing(false);
|
||||
}
|
||||
|
||||
console.log('rendering albums');
|
||||
|
||||
useEffect(() => {
|
||||
if (!refreshing && albumIds.length === 0) {
|
||||
console.log('mounting albums');
|
||||
if (!refreshing && Object.keys(albums).length === 0) {
|
||||
refresh();
|
||||
}
|
||||
});
|
||||
@ -131,22 +124,37 @@ const AlbumsList = () => {
|
||||
return (
|
||||
<View style={{ flex: 1 }}>
|
||||
<FlatList
|
||||
data={albumIds}
|
||||
data={Object.values(albums)}
|
||||
renderItem={renderItem}
|
||||
keyExtractor={item => item}
|
||||
keyExtractor={item => item.id}
|
||||
onRefresh={refresh}
|
||||
refreshing={refreshing}
|
||||
numColumns={3}
|
||||
removeClippedSubviews={false}
|
||||
removeClippedSubviews={true}
|
||||
// initialNumToRender={3}
|
||||
// maxToRenderPerBatch={2}
|
||||
// updateCellsBatchingPeriod={1000}
|
||||
getItemLayout={(data, index) => ({
|
||||
length: 180,
|
||||
offset: 180 * Math.floor(index / 3),
|
||||
index
|
||||
})}
|
||||
/>
|
||||
{/* <ScrollView>
|
||||
{Object.values(albums).map(album => (
|
||||
<AlbumItem name={album.name} coverArt={album.coverArt} key={album.id} />
|
||||
))}
|
||||
</ScrollView> */}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const MemoAlbumsList = React.memo(AlbumsList);
|
||||
|
||||
const AlbumsTab = () => (
|
||||
<TopTabContainer>
|
||||
<React.Suspense fallback={<Text>Loading...</Text>}>
|
||||
<AlbumsList />
|
||||
<MemoAlbumsList />
|
||||
</React.Suspense>
|
||||
</TopTabContainer>
|
||||
);
|
||||
|
||||
@ -1,40 +1,36 @@
|
||||
import React from 'react';
|
||||
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
|
||||
import SettingsView from '../Settings';
|
||||
import NowPlayingLayout from '../NowPlayingLayout';
|
||||
import ArtistsList from '../ArtistsList';
|
||||
import LibraryTopTabNavigator from './LibraryTopTabNavigator';
|
||||
import BottomTabBar from '../common/BottomTabBar';
|
||||
import { NavigationHandler } from 'navigation-react';
|
||||
import { NavigationStack } from 'navigation-react-native';
|
||||
import { StateNavigator } from 'navigation';
|
||||
|
||||
const Tab = createBottomTabNavigator();
|
||||
const stateNavigator = new StateNavigator([
|
||||
{ key: 'home', title: 'Home' },
|
||||
{ key: 'library', title: 'Library', trackCrumbTrail: true },
|
||||
{ key: 'search', title: 'Search', trackCrumbTrail: true },
|
||||
{ key: 'settings', title: 'Settings', trackCrumbTrail: true },
|
||||
]);
|
||||
|
||||
const BottomTabNavigator = () => {
|
||||
return (
|
||||
<Tab.Navigator
|
||||
tabBar={BottomTabBar}
|
||||
>
|
||||
<Tab.Screen
|
||||
name='Home'
|
||||
component={ArtistsList}
|
||||
options={{ icon: 'home' } as any}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name='Library'
|
||||
component={LibraryTopTabNavigator}
|
||||
options={{ icon: 'library' } as any}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name='Search'
|
||||
component={NowPlayingLayout}
|
||||
options={{ icon: 'search' } as any}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name='Settings'
|
||||
component={SettingsView}
|
||||
options={{ icon: 'settings' } as any}
|
||||
/>
|
||||
</Tab.Navigator>
|
||||
);
|
||||
}
|
||||
const { home, library, search, settings } = stateNavigator.states;
|
||||
home.renderScene = ArtistsList;
|
||||
library.renderScene = LibraryTopTabNavigator;
|
||||
search.renderScene = NowPlayingLayout;
|
||||
settings.renderScene = SettingsView;
|
||||
|
||||
const BottomTabNavigator = () => (
|
||||
<NavigationHandler stateNavigator={stateNavigator}>
|
||||
<NavigationStack
|
||||
unmountStyle={from => from ? 'fade_in' : 'fade_out'}
|
||||
crumbStyle={from => from ? 'fade_in' : 'fade_out'}
|
||||
/>
|
||||
<BottomTabBar />
|
||||
</NavigationHandler>
|
||||
);
|
||||
|
||||
stateNavigator.navigate('home');
|
||||
|
||||
export default BottomTabNavigator;
|
||||
|
||||
@ -1,32 +1,34 @@
|
||||
import React from 'react';
|
||||
import { View } from 'react-native';
|
||||
import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs';
|
||||
import AlbumsTab from '../library/AlbumsTab';
|
||||
import ArtistsTab from '../library/ArtistsTab';
|
||||
import PlaylistsTab from '../library/PlaylistsTab';
|
||||
import TopTabBar from '../common/TopTabBar';
|
||||
import { StateNavigator } from 'navigation';
|
||||
import { NavigationHandler } from 'navigation-react';
|
||||
import { NavigationStack } from 'navigation-react-native';
|
||||
|
||||
const Tab = createMaterialTopTabNavigator();
|
||||
const stateNavigator = new StateNavigator([
|
||||
{ key: 'albums', title: 'Albums' },
|
||||
{ key: 'artists', title: 'Artists', trackCrumbTrail: true, },
|
||||
{ key: 'playlists', title: 'Playlists', trackCrumbTrail: true, },
|
||||
]);
|
||||
|
||||
const { albums, artists, playlists } = stateNavigator.states;
|
||||
albums.renderScene = AlbumsTab;
|
||||
artists.renderScene = ArtistsTab;
|
||||
playlists.renderScene = PlaylistsTab;
|
||||
|
||||
const LibraryTopTabNavigator = () => (
|
||||
<View style={{
|
||||
flex: 1,
|
||||
}}>
|
||||
<Tab.Navigator tabBar={TopTabBar}>
|
||||
<Tab.Screen
|
||||
name='Albums'
|
||||
component={AlbumsTab}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name='Artists'
|
||||
component={ArtistsTab}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name='Playlists'
|
||||
component={PlaylistsTab}
|
||||
/>
|
||||
</Tab.Navigator>
|
||||
</View>
|
||||
<NavigationHandler stateNavigator={stateNavigator}>
|
||||
<TopTabBar />
|
||||
<NavigationStack
|
||||
unmountStyle={from => from ? 'slide_in' : 'slide_out'}
|
||||
crumbStyle={from => from ? 'slide_in' : 'slide_out'}
|
||||
/>
|
||||
</NavigationHandler>
|
||||
);
|
||||
|
||||
stateNavigator.navigate('albums');
|
||||
|
||||
export default LibraryTopTabNavigator;
|
||||
|
||||
@ -1,23 +1,26 @@
|
||||
import { StateNavigator } from 'navigation';
|
||||
import { NavigationHandler } from 'navigation-react';
|
||||
import { NavigationStack } from 'navigation-react-native';
|
||||
import React from 'react';
|
||||
import { createStackNavigator } from '@react-navigation/stack';
|
||||
import { View } from 'react-native';
|
||||
import NowPlayingLayout from '../NowPlayingLayout';
|
||||
import BottomTabNavigator from './BottomTabNavigator';
|
||||
|
||||
const RootStack = createStackNavigator();
|
||||
const stateNavigator = new StateNavigator([
|
||||
{ key: 'main' },
|
||||
{ key: 'nowplaying', trackCrumbTrail: true },
|
||||
]);
|
||||
|
||||
const { main, nowplaying } = stateNavigator.states;
|
||||
main.renderScene = () => <BottomTabNavigator />;
|
||||
nowplaying.renderScene = () => <NowPlayingLayout />;
|
||||
|
||||
const RootNavigator = () => (
|
||||
<RootStack.Navigator>
|
||||
<RootStack.Screen
|
||||
name='Main'
|
||||
component={BottomTabNavigator}
|
||||
options={{ headerShown: false }}
|
||||
/>
|
||||
<RootStack.Screen
|
||||
name='Now Playing'
|
||||
component={NowPlayingLayout}
|
||||
options={{ headerShown: false }}
|
||||
/>
|
||||
</RootStack.Navigator>
|
||||
<NavigationHandler stateNavigator={stateNavigator}>
|
||||
<NavigationStack />
|
||||
</NavigationHandler>
|
||||
);
|
||||
|
||||
stateNavigator.navigate('main');
|
||||
|
||||
export default RootNavigator;
|
||||
|
||||
@ -101,7 +101,7 @@ export function useCoverArtUri(id: string | undefined): string | undefined {
|
||||
}
|
||||
|
||||
const client = new SubsonicApiClient(server.address, server.username, server.token, server.salt);
|
||||
await client.getCoverArt({ id });
|
||||
await client.getCoverArt({ id, size: '32' });
|
||||
|
||||
setCoverArtSource(fileUri);
|
||||
}
|
||||
|
||||
@ -78,7 +78,7 @@ export class SubsonicApiClient {
|
||||
}
|
||||
|
||||
const url = `${this.address}/rest/${method}?${query}`;
|
||||
console.log(url);
|
||||
// console.log(url);
|
||||
return url;
|
||||
}
|
||||
|
||||
@ -98,7 +98,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