started album view

switched to native material top tabs (w/style)
This commit is contained in:
austinried 2021-06-28 15:01:35 +09:00
parent 84cff58741
commit ef69e9d517
8 changed files with 167 additions and 77 deletions

View File

@ -1,5 +1,5 @@
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { DarkTheme, NavigationContainer } from '@react-navigation/native';
import SplashPage from './src/components/SplashPage';
import RootNavigator from './src/components/navigation/RootNavigator';
import { Provider } from 'jotai';
@ -7,7 +7,7 @@ import { Provider } from 'jotai';
const App = () => (
<Provider>
<SplashPage>
<NavigationContainer>
<NavigationContainer theme={DarkTheme}>
<RootNavigator />
</NavigationContainer>
</SplashPage>

BIN
res/arrow_left-fill.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@ -0,0 +1,40 @@
import { useNavigation } from '@react-navigation/native';
import { useAtomValue } from 'jotai/utils';
import React, { useEffect } from 'react';
import { View, Text } from 'react-native';
import { albumAtomFamily } from '../../state/music';
import TopTabContainer from './TopTabContainer';
const AlbumDetails: React.FC<{
id: string,
}> = ({ id }) => {
const navigation = useNavigation();
const album = useAtomValue(albumAtomFamily(id));
useEffect(() => {
if (!album) {
return;
}
navigation.setOptions({ title: album.name });
});
return (
<>
<Text>Name: {album?.name}</Text>
<Text>Artist: {album?.artist}</Text>
</>
);
}
const AlbumView: React.FC<{
id: string,
}> = ({ id }) => (
<TopTabContainer>
<Text>{id}</Text>
<React.Suspense fallback={<Text>Loading...</Text>}>
<AlbumDetails id={id} />
</React.Suspense>
</TopTabContainer>
);
export default React.memo(AlbumView);

View File

@ -1,48 +0,0 @@
import React from 'react';
import { Text, View } from 'react-native';
import { MaterialTopTabBarProps } from '@react-navigation/material-top-tabs';
import colors from '../../styles/colors';
import textStyles from '../../styles/text';
const TopTabBar: React.FC<MaterialTopTabBarProps> = ({ state, descriptors }) => {
return (
<View style={{
backgroundColor: colors.gradient.high,
flexDirection: 'row',
alignItems: 'flex-start',
justifyContent: 'flex-start',
}}>
{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>
);
})}
</View>
);
}
export default TopTabBar;

View File

@ -1,6 +1,7 @@
import { useNavigation } from '@react-navigation/native';
import { useAtomValue } from 'jotai/utils';
import React, { useEffect } from 'react';
import { FlatList, Text, View } from 'react-native';
import { FlatList, Pressable, Text, View } from 'react-native';
import FastImage from 'react-native-fast-image';
import LinearGradient from 'react-native-linear-gradient';
import { Album } from '../../models/music';
@ -42,18 +43,24 @@ const AlbumArt: React.FC<{
const MemoAlbumArt = React.memo(AlbumArt);
const AlbumItem: React.FC<{
id: string;
name: string,
artist?: string,
coverArtUri?: string
} > = ({ name, artist, coverArtUri }) => {
} > = ({ id, name, artist, coverArtUri }) => {
const navigation = useNavigation();
const size = 125;
return (
<View style={{
<Pressable
style={{
alignItems: 'center',
marginVertical: 8,
flex: 1/3,
}}>
}}
onPress={() => navigation.navigate('AlbumView', { id })}
>
<MemoAlbumArt
width={size}
height={size}
@ -79,13 +86,13 @@ const AlbumItem: React.FC<{
{artist}
</Text>
</View>
</View>
</Pressable>
);
}
const MemoAlbumItem = React.memo(AlbumItem);
const AlbumListRenderItem: React.FC<{ item: Album }> = ({ item }) => (
<MemoAlbumItem name={item.name} artist={item.artist} coverArtUri={item.coverArtThumbUri} />
<MemoAlbumItem id={item.id} name={item.name} artist={item.artist} coverArtUri={item.coverArtThumbUri} />
);
const AlbumsList = () => {

View File

@ -4,15 +4,37 @@ import { createMaterialTopTabNavigator } from '@react-navigation/material-top-ta
import AlbumsTab from '../library/AlbumsTab';
import ArtistsTab from '../library/ArtistsTab';
import PlaylistsTab from '../library/PlaylistsTab';
import TopTabBar from '../common/TopTabBar';
import { createStackNavigator, StackNavigationProp } from '@react-navigation/stack';
import AlbumView from '../common/AlbumView';
import { RouteProp } from '@react-navigation/native';
import text from '../../styles/text';
import colors from '../../styles/colors';
import FastImage from 'react-native-fast-image';
const Tab = createMaterialTopTabNavigator();
const LibraryTopTabNavigator = () => (
<View style={{
flex: 1,
<Tab.Navigator tabBarOptions={{
// scrollEnabled: true,
tabStyle: {
// width: 100,
// paddingHorizontal: 0,
},
style: {
height: 48,
backgroundColor: colors.gradient.high,
elevation: 0,
},
labelStyle: {
...text.header,
textTransform: null as any,
marginTop: 0,
marginHorizontal: 2,
},
indicatorStyle: {
backgroundColor: colors.accent,
},
}}>
<Tab.Navigator tabBar={TopTabBar}>
<Tab.Screen
name='Albums'
component={AlbumsTab}
@ -26,7 +48,60 @@ const LibraryTopTabNavigator = () => (
component={PlaylistsTab}
/>
</Tab.Navigator>
);
type LibraryStackParamList = {
LibraryTopTabs: undefined,
AlbumView: { id: string };
}
type AlbumScreenNavigationProp = StackNavigationProp<LibraryStackParamList, 'AlbumView'>;
type AlbumScreenRouteProp = RouteProp<LibraryStackParamList, 'AlbumView'>;
type AlbumScreenProps = {
route: AlbumScreenRouteProp,
navigation: AlbumScreenNavigationProp,
};
const AlbumScreen: React.FC<AlbumScreenProps> = ({ route }) => (
<AlbumView id={route.params.id} />
);
const Stack = createStackNavigator<LibraryStackParamList>();
const LibraryStackNavigator = () => (
<View style={{ flex: 1 }}>
<Stack.Navigator>
<Stack.Screen
name='LibraryTopTabs'
component={LibraryTopTabNavigator}
options={{ headerShown: false }}
/>
<Stack.Screen
name='AlbumView'
component={AlbumScreen}
options={{ title: 'Album',
headerStyle: {
height: 50,
backgroundColor: colors.gradient.high,
},
headerTitleContainerStyle: {
marginLeft: -14,
},
headerLeftContainerStyle: {
marginLeft: 8,
},
headerTitleStyle: {
...text.header,
},
headerBackImage: () => <FastImage
source={require('../../../res/arrow_left-fill.png')}
tintColor={colors.text.primary}
style={{ height: 22, width: 22 }}
/>,
}}
/>
</Stack.Navigator>
</View>
);
export default LibraryTopTabNavigator;
export default LibraryStackNavigator;

View File

@ -1,5 +1,5 @@
import { atom, useAtom } from 'jotai';
import { useAtomValue, useUpdateAtom } from 'jotai/utils';
import { atomFamily, useAtomValue, useUpdateAtom } from 'jotai/utils';
import { Album, Artist } from '../models/music';
import { SubsonicApiClient } from '../subsonic/api';
import { activeServerAtom } from './settings';
@ -70,3 +70,19 @@ export const useUpdateAlbums = () => {
setUpdating(false);
}
}
export const albumAtomFamily = atomFamily((id: string) => atom<Album | undefined>(async (get) => {
const server = get(activeServerAtom);
if (!server) {
return undefined;
}
const client = new SubsonicApiClient(server);
const response = await client.getAlbum({ id });
return {
id,
name: response.data.album.name,
artist: response.data.album.artist,
};
}));

View File

@ -12,7 +12,7 @@ const paragraph: TextStyle = {
const header: TextStyle = {
...paragraph,
fontSize: 20,
fontSize: 19,
fontFamily: fontSemiBold,
};