- Enable dark navigation headers in Tab navigator (fixes status bar overlap on all 5 tab screens automatically) - Use useSafeAreaInsets for AlbumDetail/ArtistDetail back buttons - Use useSafeAreaInsets for NowPlaying header and bottom padding - Remove redundant paddingTop from Home heading and Login content - Cleaner tab bar (height 60, better label spacing) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
164 lines
6.0 KiB
JavaScript
164 lines
6.0 KiB
JavaScript
import React, { useEffect, useState, Component } from 'react';
|
|
import { View, StatusBar, Text, ScrollView } from 'react-native';
|
|
import { NavigationContainer, DarkTheme } from '@react-navigation/native';
|
|
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
|
|
import { createStackNavigator } from '@react-navigation/stack';
|
|
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
|
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
|
import { Ionicons } from '@expo/vector-icons';
|
|
import TrackPlayer, { Event, State } from 'react-native-track-player';
|
|
import { setupPlayer } from './src/audio/player';
|
|
import { useStore } from './src/store';
|
|
|
|
import HomeScreen from './src/screens/Home';
|
|
import { ArtistsScreen, AlbumsScreen } from './src/screens/Library';
|
|
import ArtistDetailScreen from './src/screens/ArtistDetail';
|
|
import AlbumDetailScreen from './src/screens/AlbumDetail';
|
|
import SearchScreen from './src/screens/Search';
|
|
import LoginScreen from './src/screens/Login';
|
|
import NowPlayingScreen from './src/screens/NowPlaying';
|
|
import PlayerBar from './src/components/PlayerBar';
|
|
|
|
// ─── ERROR BOUNDARY ───
|
|
class ErrorBoundary extends Component {
|
|
state = { error: null };
|
|
static getDerivedStateFromError(e) { return { error: e }; }
|
|
render() {
|
|
if (this.state.error) {
|
|
return (
|
|
<ScrollView style={{ flex: 1, backgroundColor: '#121212', padding: 24, paddingTop: 60 }}>
|
|
<Text style={{ color: '#e05252', fontSize: 16, fontWeight: '700', marginBottom: 12 }}>
|
|
Fehler beim Starten
|
|
</Text>
|
|
<Text style={{ color: '#fff', fontSize: 13, fontFamily: 'monospace' }}>
|
|
{this.state.error?.message || String(this.state.error)}
|
|
</Text>
|
|
<Text style={{ color: '#aaa', fontSize: 12, marginTop: 16 }}>
|
|
{this.state.error?.stack}
|
|
</Text>
|
|
</ScrollView>
|
|
);
|
|
}
|
|
return this.props.children;
|
|
}
|
|
}
|
|
|
|
const Tab = createBottomTabNavigator();
|
|
const Stack = createStackNavigator();
|
|
|
|
function HomeTabs() {
|
|
return (
|
|
<Tab.Navigator
|
|
screenOptions={({ route }) => ({
|
|
headerStyle: {
|
|
backgroundColor: '#121212',
|
|
elevation: 0,
|
|
shadowOpacity: 0,
|
|
borderBottomWidth: 0,
|
|
},
|
|
headerTintColor: '#fff',
|
|
headerTitleStyle: { fontWeight: '800', fontSize: 20 },
|
|
tabBarStyle: {
|
|
backgroundColor: '#1a1a1a',
|
|
borderTopColor: '#2a2a2a',
|
|
borderTopWidth: 1,
|
|
height: 60,
|
|
paddingBottom: 8,
|
|
paddingTop: 4,
|
|
},
|
|
tabBarActiveTintColor: '#1db954',
|
|
tabBarInactiveTintColor: '#666',
|
|
tabBarLabelStyle: { fontSize: 10, fontWeight: '600', marginTop: 2 },
|
|
tabBarIcon: ({ color, size }) => {
|
|
const icons = { Home: 'home', Artists: 'person', Albums: 'disc', Search: 'search', Settings: 'settings-outline' };
|
|
return <Ionicons name={icons[route.name] || 'ellipse'} size={22} color={color} />;
|
|
},
|
|
})}
|
|
>
|
|
<Tab.Screen name="Home" component={HomeScreen} options={{ title: 'Start', headerTitle: 'NovaSonic' }} />
|
|
<Tab.Screen name="Artists" component={ArtistsScreen} options={{ title: 'Künstler' }} />
|
|
<Tab.Screen name="Albums" component={AlbumsScreen} options={{ title: 'Alben' }} />
|
|
<Tab.Screen name="Search" component={SearchScreen} options={{ title: 'Suche' }} />
|
|
<Tab.Screen name="Settings" component={LoginScreen} options={{ title: 'Einstellungen' }} />
|
|
</Tab.Navigator>
|
|
);
|
|
}
|
|
|
|
function RootNavigator() {
|
|
const currentTrack = useStore(s => s.currentTrack);
|
|
return (
|
|
<View style={{ flex: 1 }}>
|
|
<Stack.Navigator screenOptions={{ headerShown: false }}>
|
|
<Stack.Screen name="Tabs" component={HomeTabs} />
|
|
<Stack.Screen name="NowPlaying" component={NowPlayingScreen} options={{ presentation: 'modal' }} />
|
|
<Stack.Screen name="AlbumDetail" component={AlbumDetailScreen} />
|
|
<Stack.Screen name="ArtistDetail" component={ArtistDetailScreen} />
|
|
</Stack.Navigator>
|
|
{currentTrack && <PlayerBar />}
|
|
</View>
|
|
);
|
|
}
|
|
|
|
export default function App() {
|
|
const { loadConfig } = useStore();
|
|
const [ready, setReady] = useState(false);
|
|
const [initError, setInitError] = useState(null);
|
|
|
|
useEffect(() => {
|
|
(async () => {
|
|
try {
|
|
await loadConfig();
|
|
await setupPlayer();
|
|
} catch (e) {
|
|
setInitError(e?.message || String(e));
|
|
}
|
|
setReady(true);
|
|
})();
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
const sub1 = TrackPlayer.addEventListener(Event.PlaybackActiveTrackChanged, e => {
|
|
if (e.track) useStore.getState().setCurrentTrack(e.track._song || e.track);
|
|
});
|
|
const sub2 = TrackPlayer.addEventListener(Event.PlaybackState, e => {
|
|
useStore.getState().setIsPlaying(e.state === State.Playing);
|
|
});
|
|
return () => { sub1.remove(); sub2.remove(); };
|
|
}, []);
|
|
|
|
if (!ready) return (
|
|
<View style={{ flex: 1, backgroundColor: '#121212' }} />
|
|
);
|
|
|
|
if (initError) return (
|
|
<ScrollView style={{ flex: 1, backgroundColor: '#121212', padding: 24, paddingTop: 60 }}>
|
|
<Text style={{ color: '#e05252', fontSize: 16, fontWeight: '700', marginBottom: 12 }}>Init-Fehler</Text>
|
|
<Text style={{ color: '#fff', fontSize: 13 }}>{initError}</Text>
|
|
</ScrollView>
|
|
);
|
|
|
|
return (
|
|
<ErrorBoundary>
|
|
<GestureHandlerRootView style={{ flex: 1 }}>
|
|
<SafeAreaProvider>
|
|
<StatusBar barStyle="light-content" backgroundColor="#121212" />
|
|
<NavigationContainer theme={{
|
|
...DarkTheme,
|
|
colors: {
|
|
...DarkTheme.colors,
|
|
primary: '#1db954',
|
|
background: '#121212',
|
|
card: '#1e1e1e',
|
|
text: '#ffffff',
|
|
border: '#2a2a2a',
|
|
notification: '#1db954',
|
|
},
|
|
}}>
|
|
<RootNavigator />
|
|
</NavigationContainer>
|
|
</SafeAreaProvider>
|
|
</GestureHandlerRootView>
|
|
</ErrorBoundary>
|
|
);
|
|
}
|