2025-02-22 01:16:33 -05:00
|
|
|
// app/_layout.tsx
|
2025-03-06 14:18:10 -05:00
|
|
|
import 'expo-dev-client';
|
2025-02-09 20:38:38 -05:00
|
|
|
import '@/global.css';
|
2025-02-15 14:03:42 -05:00
|
|
|
import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native';
|
2025-02-05 20:38:39 -05:00
|
|
|
import { Stack } from 'expo-router';
|
|
|
|
import { StatusBar } from 'expo-status-bar';
|
|
|
|
import * as React from 'react';
|
2025-02-16 22:47:47 -05:00
|
|
|
import { View, Text, Platform } from 'react-native';
|
2025-03-12 19:23:28 -04:00
|
|
|
import { NAV_THEME } from '@/lib/theme/constants';
|
|
|
|
import { useColorScheme } from '@/lib/theme/useColorScheme';
|
2025-02-05 20:38:39 -05:00
|
|
|
import { PortalHost } from '@rn-primitives/portal';
|
2025-02-09 20:38:38 -05:00
|
|
|
import { setAndroidNavigationBar } from '@/lib/android-navigation-bar';
|
|
|
|
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
2025-02-16 22:47:47 -05:00
|
|
|
import { DatabaseProvider } from '@/components/DatabaseProvider';
|
|
|
|
import { ErrorBoundary } from '@/components/ErrorBoundary';
|
2025-02-22 01:16:33 -05:00
|
|
|
import { SettingsDrawerProvider } from '@/lib/contexts/SettingsDrawerContext';
|
|
|
|
import SettingsDrawer from '@/components/SettingsDrawer';
|
2025-03-09 11:15:28 -04:00
|
|
|
import RelayInitializer from '@/components/RelayInitializer';
|
2025-02-22 01:16:33 -05:00
|
|
|
import { useNDKStore } from '@/lib/stores/ndk';
|
2025-02-26 23:30:00 -05:00
|
|
|
import { useWorkoutStore } from '@/stores/workoutStore';
|
2025-03-17 20:20:16 -04:00
|
|
|
// Import splash screens with fallback mechanism
|
|
|
|
let SplashComponent: React.ComponentType<{onFinish: () => void}>;
|
|
|
|
|
|
|
|
// First try to import the video splash screen
|
|
|
|
try {
|
|
|
|
// Try to dynamically import the Video component
|
|
|
|
const Video = require('expo-av').Video;
|
|
|
|
// If successful, import the VideoSplashScreen
|
|
|
|
SplashComponent = require('@/components/VideoSplashScreen').default;
|
|
|
|
console.log('Successfully imported VideoSplashScreen');
|
|
|
|
} catch (e) {
|
|
|
|
console.warn('Failed to import VideoSplashScreen or expo-av:', e);
|
|
|
|
// If that fails, use the simple splash screen
|
|
|
|
try {
|
|
|
|
SplashComponent = require('@/components/SimpleSplashScreen').default;
|
|
|
|
console.log('Using SimpleSplashScreen as fallback');
|
|
|
|
} catch (simpleSplashError) {
|
|
|
|
console.warn('Failed to import SimpleSplashScreen:', simpleSplashError);
|
|
|
|
// Last resort fallback is an inline component
|
|
|
|
SplashComponent = ({onFinish}) => {
|
|
|
|
React.useEffect(() => {
|
|
|
|
// Call onFinish after a short delay
|
|
|
|
const timer = setTimeout(() => {
|
|
|
|
onFinish();
|
|
|
|
}, 500);
|
|
|
|
return () => clearTimeout(timer);
|
|
|
|
}, [onFinish]);
|
|
|
|
|
|
|
|
return (
|
|
|
|
<View className="flex-1 items-center justify-center bg-black">
|
|
|
|
<Text className="text-white text-xl">Loading POWR...</Text>
|
|
|
|
</View>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
2025-02-05 20:38:39 -05:00
|
|
|
|
2025-03-03 22:40:11 -05:00
|
|
|
console.log('_layout.tsx loaded');
|
|
|
|
|
2025-02-16 22:47:47 -05:00
|
|
|
const LIGHT_THEME = {
|
2025-02-05 20:38:39 -05:00
|
|
|
...DefaultTheme,
|
|
|
|
colors: NAV_THEME.light,
|
|
|
|
};
|
2025-02-15 14:03:42 -05:00
|
|
|
|
2025-02-16 22:47:47 -05:00
|
|
|
const DARK_THEME = {
|
2025-02-05 20:38:39 -05:00
|
|
|
...DarkTheme,
|
|
|
|
colors: NAV_THEME.dark,
|
|
|
|
};
|
|
|
|
|
|
|
|
export default function RootLayout() {
|
2025-02-16 22:47:47 -05:00
|
|
|
const [isInitialized, setIsInitialized] = React.useState(false);
|
2025-03-17 20:20:16 -04:00
|
|
|
const [isSplashFinished, setIsSplashFinished] = React.useState(false);
|
2025-02-05 20:38:39 -05:00
|
|
|
const { colorScheme, isDarkColorScheme } = useColorScheme();
|
2025-02-22 01:16:33 -05:00
|
|
|
const { init } = useNDKStore();
|
2025-03-17 20:20:16 -04:00
|
|
|
const initializationPromise = React.useRef<Promise<void> | null>(null);
|
2025-02-05 20:38:39 -05:00
|
|
|
|
2025-03-17 20:20:16 -04:00
|
|
|
// Start app initialization immediately
|
2025-02-09 20:38:38 -05:00
|
|
|
React.useEffect(() => {
|
2025-03-17 20:20:16 -04:00
|
|
|
if (!initializationPromise.current) {
|
|
|
|
initializationPromise.current = (async () => {
|
|
|
|
try {
|
|
|
|
console.log('Starting app initialization in background...');
|
|
|
|
if (Platform.OS === 'web') {
|
|
|
|
document.documentElement.classList.add('bg-background');
|
|
|
|
}
|
|
|
|
setAndroidNavigationBar(colorScheme);
|
|
|
|
|
|
|
|
// Initialize NDK
|
|
|
|
await init();
|
|
|
|
|
|
|
|
// Load favorites from SQLite
|
|
|
|
await useWorkoutStore.getState().loadFavorites();
|
|
|
|
|
|
|
|
console.log('App initialization completed!');
|
|
|
|
setIsInitialized(true);
|
|
|
|
} catch (error) {
|
|
|
|
console.error('Failed to initialize:', error);
|
2025-02-16 22:47:47 -05:00
|
|
|
}
|
2025-03-17 20:20:16 -04:00
|
|
|
})();
|
2025-02-05 20:38:39 -05:00
|
|
|
}
|
2025-03-17 20:20:16 -04:00
|
|
|
|
|
|
|
return () => {
|
|
|
|
// This is just for cleanup, the promise will continue executing
|
|
|
|
initializationPromise.current = null;
|
|
|
|
};
|
2025-02-05 20:38:39 -05:00
|
|
|
}, []);
|
|
|
|
|
2025-03-17 20:20:16 -04:00
|
|
|
// Function to handle splash finish - will check if initialization is also complete
|
|
|
|
const handleSplashFinish = React.useCallback(() => {
|
|
|
|
console.log('Splash video finished playing');
|
|
|
|
setIsSplashFinished(true);
|
|
|
|
|
|
|
|
// If initialization isn't done yet, we'll show a loading indicator
|
|
|
|
if (!isInitialized) {
|
|
|
|
console.log('Waiting for initialization to complete...');
|
|
|
|
}
|
|
|
|
}, [isInitialized]);
|
|
|
|
|
|
|
|
// Show splash screen if not finished
|
|
|
|
if (!isSplashFinished) {
|
|
|
|
try {
|
|
|
|
return <SplashComponent onFinish={handleSplashFinish} />;
|
|
|
|
} catch (e) {
|
|
|
|
console.error('Error rendering splash screen:', e);
|
|
|
|
// Skip splash screen if there's an error
|
|
|
|
if (!isInitialized) {
|
|
|
|
return (
|
|
|
|
<View className="flex-1 items-center justify-center bg-background">
|
|
|
|
<Text className="text-foreground">Loading...</Text>
|
|
|
|
</View>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
// Force continue to main app
|
|
|
|
setIsSplashFinished(true);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If splash is done but initialization isn't, show loading
|
2025-02-16 22:47:47 -05:00
|
|
|
if (!isInitialized) {
|
|
|
|
return (
|
|
|
|
<View className="flex-1 items-center justify-center bg-background">
|
2025-03-17 20:20:16 -04:00
|
|
|
<Text className="text-foreground">Finalizing setup...</Text>
|
2025-02-16 22:47:47 -05:00
|
|
|
</View>
|
|
|
|
);
|
2025-02-05 20:38:39 -05:00
|
|
|
}
|
|
|
|
|
2025-03-17 20:20:16 -04:00
|
|
|
// Main app UI wrapped in error boundary
|
2025-02-05 20:38:39 -05:00
|
|
|
return (
|
2025-02-16 22:47:47 -05:00
|
|
|
<ErrorBoundary>
|
|
|
|
<GestureHandlerRootView style={{ flex: 1 }}>
|
|
|
|
<DatabaseProvider>
|
|
|
|
<ThemeProvider value={isDarkColorScheme ? DARK_THEME : LIGHT_THEME}>
|
2025-03-17 20:20:16 -04:00
|
|
|
{/* Ensure SettingsDrawerProvider wraps everything */}
|
2025-02-22 01:16:33 -05:00
|
|
|
<SettingsDrawerProvider>
|
2025-03-09 11:15:28 -04:00
|
|
|
{/* Add RelayInitializer here - it loads relay data once NDK is available */}
|
|
|
|
<RelayInitializer />
|
|
|
|
|
2025-02-22 01:16:33 -05:00
|
|
|
<StatusBar style={isDarkColorScheme ? 'light' : 'dark'} />
|
|
|
|
<Stack screenOptions={{ headerShown: false }}>
|
|
|
|
<Stack.Screen
|
|
|
|
name="(tabs)"
|
|
|
|
options={{
|
|
|
|
headerShown: false,
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
</Stack>
|
|
|
|
|
|
|
|
{/* Settings drawer needs to be outside the navigation stack */}
|
|
|
|
<SettingsDrawer />
|
|
|
|
|
|
|
|
<PortalHost />
|
|
|
|
</SettingsDrawerProvider>
|
2025-02-16 22:47:47 -05:00
|
|
|
</ThemeProvider>
|
|
|
|
</DatabaseProvider>
|
2025-02-09 20:38:38 -05:00
|
|
|
</GestureHandlerRootView>
|
2025-02-16 22:47:47 -05:00
|
|
|
</ErrorBoundary>
|
2025-02-05 20:38:39 -05:00
|
|
|
);
|
2025-02-09 20:38:38 -05:00
|
|
|
}
|