better tabs and more layout

This commit is contained in:
austinried 2021-06-18 16:18:32 +09:00
parent 0f1e10d50f
commit d41fb5b9e7
20 changed files with 196 additions and 72 deletions

70
App.tsx
View File

@ -1,78 +1,12 @@
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { Image } from 'react-native';
import { RecoilRoot } from 'recoil';
import SettingsView from './src/components/Settings';
import ArtistsList from './src/components/ArtistsList';
import NowPlayingLayout from './src/components/NowPlayingLayout';
const SettingsIcon: React.FC<{ focused: boolean }> = ({ focused }) => (
<Image
style={{
height: 32,
width: 32,
}}
source={focused ? require('./res/settings-fill.png') : require('./res/settings.png')} />
);
const NowPlayingIcon: React.FC<{ focused: boolean }> = ({ focused }) => (
<Image
style={{
height: 32,
width: 32,
}}
source={focused ? require('./res/music_notes-fill.png') : require('./res/music_notes.png')} />
);
const ArtistsIcon: React.FC<{ focused: boolean }> = ({ focused }) => (
<Image
style={{
height: 32,
width: 32,
}}
source={focused ? require('./res/mic_on-fill.png') : require('./res/mic_on.png')} />
);
const Tab = createBottomTabNavigator();
import RootNavigator from './src/components/RootNavigator';
const App = () => (
<RecoilRoot>
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen
name='Settings'
component={SettingsView}
options={{
tabBarIcon: SettingsIcon,
}}
/>
<Tab.Screen
name='Artists'
component={ArtistsList}
options={{
tabBarIcon: ArtistsIcon,
tabBarVisibilityAnimationConfig: {
show: {
animation: 'spring',
},
hide: {
animation: 'spring',
}
}
}}
/>
<Tab.Screen
name='Now Playing'
component={NowPlayingLayout}
options={{
tabBarIcon: NowPlayingIcon,
}}
/>
</Tab.Navigator>
<RootNavigator />
</NavigationContainer>
</RecoilRoot>
);

Binary file not shown.

View File

@ -12,6 +12,7 @@
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
E40802F2F4B64B50BC3D4A71 /* Rubik-VariableFont_wght.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 6882041C5ED04E249BB58448 /* Rubik-VariableFont_wght.ttf */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -36,6 +37,7 @@
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = SubSonify/main.m; sourceTree = "<group>"; };
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = SubSonify/LaunchScreen.storyboard; sourceTree = "<group>"; };
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
6882041C5ED04E249BB58448 /* Rubik-VariableFont_wght.ttf */ = {isa = PBXFileReference; name = "Rubik-VariableFont_wght.ttf"; path = "../assets/fonts/Rubik-VariableFont_wght.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -109,6 +111,7 @@
00E356EF1AD99517003FC87E /* SubSonifyTests */,
83CBBA001A601CBA00E9B192 /* Products */,
2D16E6871FA4F8E400B85C8A /* Frameworks */,
4E728597FECE42D1A816E9C1 /* Resources */,
);
indentWidth = 2;
sourceTree = "<group>";
@ -124,6 +127,15 @@
name = Products;
sourceTree = "<group>";
};
4E728597FECE42D1A816E9C1 /* Resources */ = {
isa = "PBXGroup";
children = (
6882041C5ED04E249BB58448 /* Rubik-VariableFont_wght.ttf */,
);
name = Resources;
sourceTree = "<group>";
path = "";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -214,6 +226,7 @@
files = (
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */,
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
E40802F2F4B64B50BC3D4A71 /* Rubik-VariableFont_wght.ttf in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -36,7 +36,7 @@
</dict>
</dict>
<key>NSLocationWhenInUseUsageDescription</key>
<string></string>
<string/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
@ -51,5 +51,9 @@
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>UIAppFonts</key>
<array>
<string>Rubik-VariableFont_wght.ttf</string>
</array>
</dict>
</plist>

7
react-native.config.js Normal file
View File

@ -0,0 +1,7 @@
module.exports = {
project: {
ios: {},
android: {},
},
assets: ['./assets/fonts']
};

BIN
res/chevron_right-fill.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
res/chevron_right.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
res/home-fill.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
res/home.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

BIN
res/library-fill.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

BIN
res/library.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

BIN
res/search-fill.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

BIN
res/search.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

@ -29,7 +29,7 @@ const List = () => {
);
}
const ArtistsList = () => {
const ListPlusControls = () => {
const resetArtists = useResetRecoilState(artistsState);
const updateArtists = useUpdateArtists();
@ -43,11 +43,17 @@ const ArtistsList = () => {
title='Update from API'
onPress={updateArtists}
/>
<React.Suspense fallback={<Text>Loading...</Text>}>
<List />
</React.Suspense>
</View>
);
}
const ArtistsList = () => (
<View>
<React.Suspense fallback={<Text>Loading...</Text>}>
<ListPlusControls />
</React.Suspense>
</View>
)
export default ArtistsList;

View File

@ -0,0 +1,25 @@
import React from 'react';
import { Image, ImageSourcePropType } from 'react-native';
export type FocusableIconProps = {
focused: boolean,
source: ImageSourcePropType;
focusedSource?: ImageSourcePropType;
width?: number;
height?: number;
};
const FocusableIcon: React.FC<FocusableIconProps> = (props) => {
props.focusedSource = props.focusedSource || props.source;
props.width = props.width || 32;
props.height = props.height || 32;
return (
<Image
style={{ height: props.height, width: props.width }}
source={props.focused ? props.focusedSource : props.source}
/>
);
}
export default FocusableIcon;

View File

@ -0,0 +1,48 @@
import React from 'react';
import { Text, View, Image } from 'react-native';
const SectionHeader: React.FC<{ title: string }> = ({ title }) => {
return (
<View style={{
height: 60,
flexDirection: 'row',
justifyContent: 'space-between',
// backgroundColor: 'green',
alignItems: 'center',
paddingLeft: 15,
paddingRight: 15,
}}>
<Text style={{
color: 'white',
fontSize: 34,
fontWeight: 'normal',
fontFamily: 'Rubik-VariableFont_wght',
}}>{title}</Text>
<Image
style={{
width: 32,
height: 32,
tintColor: 'white',
}}
source={require('../../res/chevron_right.png')}
/>
</View>
);
}
const AlbumCoverList = () => {
}
const Library = () => (
<View style={{
flex: 1,
backgroundColor: '#3b3b3b',
}}>
<SectionHeader title='Albums' />
<SectionHeader title='Artists' />
<SectionHeader title='Playlists' />
</View>
);
export default Library;

View File

@ -0,0 +1,23 @@
import React from 'react';
import { createStackNavigator } from '@react-navigation/stack';
import TabNavigator from './TabNavigator';
import NowPlayingLayout from './NowPlayingLayout';
const RootStack = createStackNavigator();
const RootNavigator = () => (
<RootStack.Navigator>
<RootStack.Screen
name='Main'
component={TabNavigator}
options={{ headerShown: false }}
/>
<RootStack.Screen
name='Now Playing'
component={NowPlayingLayout}
options={{ headerShown: false }}
/>
</RootStack.Navigator>
);
export default RootNavigator;

View File

@ -7,6 +7,7 @@ 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';
const RecreateDbButton: React.FC<{ db: DbStorage, title: string }> = ({ db, title }) => {
const [inProgress, setInProgress] = useState(false);
@ -31,10 +32,15 @@ const RecreateDbButton: React.FC<{ db: DbStorage, title: string }> = ({ db, titl
}
const DbControls = () => {
const navigation = useNavigation();
return (
<View>
<RecreateDbButton db={musicDb} title='Music' />
<RecreateDbButton db={settingsDb} title='Settings' />
<Button
title='Now Playing'
onPress={() => navigation.navigate('Now Playing')}
/>
</View>
);
}

View File

@ -0,0 +1,58 @@
import React from 'react';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import SettingsView from './Settings';
import NowPlayingLayout from './NowPlayingLayout';
import ArtistsList from './ArtistsList';
import FocusableIcon from './FocusableIcon';
import Library from './Library';
const Tab = createBottomTabNavigator();
const TabNavigator = () => {
return (
<Tab.Navigator>
<Tab.Screen
name='Home'
component={ArtistsList}
options={{
tabBarIcon: ({ focused }) => FocusableIcon({ focused,
source: require('../../res/home.png'),
focusedSource: require('../../res/home-fill.png'),
}),
}}
/>
<Tab.Screen
name='Library'
component={Library}
options={{
tabBarIcon: ({ focused }) => FocusableIcon({ focused,
source: require('../../res/library.png'),
focusedSource: require('../../res/library-fill.png'),
}),
}}
/>
<Tab.Screen
name='Search'
component={NowPlayingLayout}
options={{
tabBarIcon: ({ focused }) => FocusableIcon({ focused,
source: require('../../res/search.png'),
focusedSource: require('../../res/search-fill.png'),
}),
}}
/>
<Tab.Screen
name='Settings'
component={SettingsView}
options={{
tabBarIcon: ({ focused }) => FocusableIcon({ focused,
source: require('../../res/settings.png'),
focusedSource: require('../../res/settings-fill.png'),
}),
}}
/>
</Tab.Navigator>
);
}
export default TabNavigator;