ui changes

This commit is contained in:
tapframe 2025-11-26 01:09:30 +05:30
parent 6d1ba14ab4
commit 6c08b459bf
3 changed files with 383 additions and 14 deletions

74
App.tsx
View file

@ -18,7 +18,7 @@ import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { StatusBar } from 'expo-status-bar';
import { Provider as PaperProvider } from 'react-native-paper';
import { enableScreens, enableFreeze } from 'react-native-screens';
import AppNavigator, {
import AppNavigator, {
CustomNavigationDarkTheme,
CustomDarkTheme
} from './src/navigation/AppNavigator';
@ -41,6 +41,7 @@ import { aiService } from './src/services/aiService';
import { AccountProvider, useAccount } from './src/contexts/AccountContext';
import { ToastProvider } from './src/contexts/ToastContext';
import { mmkvStorage } from './src/services/mmkvStorage';
import AnnouncementOverlay from './src/components/AnnouncementOverlay';
Sentry.init({
dsn: 'https://1a58bf436454d346e5852b7bfd3c95e8@o4509536317276160.ingest.de.sentry.io/4509536317734992',
@ -82,12 +83,13 @@ const ThemedApp = () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const engine = (global as any).HermesInternal ? 'Hermes' : 'JSC';
console.log('JS Engine:', engine);
} catch {}
} catch { }
}, []);
const { currentTheme } = useTheme();
const [isAppReady, setIsAppReady] = useState(false);
const [hasCompletedOnboarding, setHasCompletedOnboarding] = useState<boolean | null>(null);
const [showAnnouncement, setShowAnnouncement] = useState(false);
// Update popup functionality
const {
showUpdatePopup,
@ -100,7 +102,17 @@ const ThemedApp = () => {
// GitHub major/minor release overlay
const githubUpdate = useGithubMajorUpdate();
// Announcement data
const announcements = [
{
icon: 'zap',
title: 'Debrid Integration',
description: 'Unlock 4K high-quality streams with lightning-fast speeds. Connect your TorBox account to access cached premium content with zero buffering.',
tag: 'NEW',
},
];
// Check onboarding status and initialize services
useEffect(() => {
const initializeApp = async () => {
@ -108,28 +120,37 @@ const ThemedApp = () => {
// Check onboarding status
const onboardingCompleted = await mmkvStorage.getItem('hasCompletedOnboarding');
setHasCompletedOnboarding(onboardingCompleted === 'true');
// Initialize update service
await UpdateService.initialize();
// Initialize memory monitoring service to prevent OutOfMemoryError
memoryMonitorService; // Just accessing it starts the monitoring
console.log('Memory monitoring service initialized');
// Initialize AI service
await aiService.initialize();
console.log('AI service initialized');
// Check if announcement should be shown (version 1.0.0)
const announcementShown = await mmkvStorage.getItem('announcement_v1.0.0_shown');
if (!announcementShown && onboardingCompleted === 'true') {
// Show announcement only after app is ready
setTimeout(() => {
setShowAnnouncement(true);
}, 1000);
}
} catch (error) {
console.error('Error initializing app:', error);
// Default to showing onboarding if we can't check
setHasCompletedOnboarding(false);
}
};
initializeApp();
}, []);
// Create custom themes based on current theme
const customDarkTheme = {
...CustomDarkTheme,
@ -138,7 +159,7 @@ const ThemedApp = () => {
primary: currentTheme.colors.primary,
}
};
const customNavigationTheme = {
...CustomNavigationDarkTheme,
colors: {
@ -153,15 +174,33 @@ const ThemedApp = () => {
const handleSplashComplete = () => {
setIsAppReady(true);
};
// Navigation reference
const navigationRef = React.useRef<any>(null);
// Handler for navigating to debrid integration
const handleNavigateToDebrid = () => {
if (navigationRef.current) {
navigationRef.current.navigate('DebridIntegration');
}
};
// Handler for announcement close
const handleAnnouncementClose = async () => {
setShowAnnouncement(false);
// Mark announcement as shown
await mmkvStorage.setItem('announcement_v1.0.0_shown', 'true');
};
// Don't render anything until we know the onboarding status
const shouldShowApp = isAppReady && hasCompletedOnboarding !== null;
const initialRouteName = hasCompletedOnboarding ? 'MainTabs' : 'Onboarding';
return (
<AccountProvider>
<PaperProvider theme={customDarkTheme}>
<NavigationContainer
<NavigationContainer
ref={navigationRef}
theme={customNavigationTheme}
linking={undefined}
>
@ -186,6 +225,13 @@ const ThemedApp = () => {
onDismiss={githubUpdate.onDismiss}
onLater={githubUpdate.onLater}
/>
<AnnouncementOverlay
visible={showAnnouncement}
announcements={announcements}
onClose={handleAnnouncementClose}
onActionPress={handleNavigateToDebrid}
actionButtonText="Connect Now"
/>
</View>
</DownloadsProvider>
</NavigationContainer>

View file

@ -0,0 +1,308 @@
import React, { useEffect, useRef } from 'react';
import {
View,
Text,
StyleSheet,
Modal,
TouchableOpacity,
Animated,
Dimensions,
ScrollView,
} from 'react-native';
import { useTheme } from '../contexts/ThemeContext';
import { Feather } from '@expo/vector-icons';
const { width, height } = Dimensions.get('window');
interface Announcement {
icon: string;
title: string;
description: string;
tag?: string;
}
interface AnnouncementOverlayProps {
visible: boolean;
onClose: () => void;
onActionPress?: () => void;
title?: string;
announcements: Announcement[];
actionButtonText?: string;
}
const AnnouncementOverlay: React.FC<AnnouncementOverlayProps> = ({
visible,
onClose,
onActionPress,
title = "What's New",
announcements,
actionButtonText = "Got it!",
}) => {
const { currentTheme } = useTheme();
const colors = currentTheme.colors;
const scaleAnim = useRef(new Animated.Value(0.8)).current;
const opacityAnim = useRef(new Animated.Value(0)).current;
useEffect(() => {
if (visible) {
Animated.parallel([
Animated.spring(scaleAnim, {
toValue: 1,
tension: 50,
friction: 7,
useNativeDriver: true,
}),
Animated.timing(opacityAnim, {
toValue: 1,
duration: 300,
useNativeDriver: true,
}),
]).start();
} else {
scaleAnim.setValue(0.8);
opacityAnim.setValue(0);
}
}, [visible]);
const handleClose = () => {
Animated.parallel([
Animated.spring(scaleAnim, {
toValue: 0.8,
tension: 50,
friction: 7,
useNativeDriver: true,
}),
Animated.timing(opacityAnim, {
toValue: 0,
duration: 200,
useNativeDriver: true,
}),
]).start(() => {
onClose();
});
};
const handleAction = () => {
if (onActionPress) {
handleClose();
// Delay navigation slightly to allow animation to complete
setTimeout(() => {
onActionPress();
}, 300);
} else {
handleClose();
}
};
return (
<Modal
visible={visible}
transparent
animationType="none"
statusBarTranslucent
onRequestClose={handleClose}
>
<View style={styles.overlay}>
<Animated.View
style={[
styles.container,
{
opacity: opacityAnim,
transform: [{ scale: scaleAnim }],
},
]}
>
<View style={styles.card}>
{/* Close Button */}
<TouchableOpacity
style={styles.closeButton}
onPress={handleClose}
>
<Feather name="x" size={20} color={colors.white} />
</TouchableOpacity>
{/* Header */}
<View style={styles.header}>
<View style={[styles.iconContainer, { backgroundColor: colors.primary + '20' }]}>
<Feather name="zap" size={32} color={colors.primary} />
</View>
<Text style={[styles.title, { color: colors.white }]}>{title}</Text>
<Text style={[styles.subtitle, { color: colors.mediumEmphasis }]}>
Exciting updates in this release
</Text>
</View>
{/* Announcements */}
<ScrollView
style={styles.scrollView}
showsVerticalScrollIndicator={false}
>
{announcements.map((announcement, index) => (
<View
key={index}
style={styles.announcementItem}
>
<View style={[styles.announcementIcon, { backgroundColor: colors.primary + '15' }]}>
<Feather name={announcement.icon as any} size={24} color={colors.primary} />
</View>
<View style={styles.announcementContent}>
<View style={styles.announcementHeader}>
<Text style={[styles.announcementTitle, { color: colors.white }]}>
{announcement.title}
</Text>
{announcement.tag && (
<View style={[styles.tag, { backgroundColor: colors.primary }]}>
<Text style={styles.tagText}>{announcement.tag}</Text>
</View>
)}
</View>
<Text style={[styles.announcementDescription, { color: colors.mediumEmphasis }]}>
{announcement.description}
</Text>
</View>
</View>
))}
</ScrollView>
{/* Action Button */}
<TouchableOpacity
style={[styles.button, { backgroundColor: colors.primary }]}
onPress={handleAction}
>
<Text style={styles.buttonText}>{actionButtonText}</Text>
</TouchableOpacity>
</View>
</Animated.View>
</View>
</Modal>
);
};
const styles = StyleSheet.create({
overlay: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'rgba(0, 0, 0, 0.85)',
},
container: {
width: width * 0.9,
maxWidth: 500,
maxHeight: height * 0.8,
},
card: {
backgroundColor: '#1a1a1a',
borderRadius: 24,
padding: 24,
shadowColor: '#000',
shadowOffset: { width: 0, height: 8 },
shadowOpacity: 0.3,
shadowRadius: 16,
},
closeButton: {
position: 'absolute',
top: 16,
right: 16,
width: 32,
height: 32,
borderRadius: 16,
backgroundColor: '#2a2a2a',
justifyContent: 'center',
alignItems: 'center',
zIndex: 10,
},
header: {
alignItems: 'center',
marginBottom: 24,
},
iconContainer: {
width: 64,
height: 64,
borderRadius: 32,
justifyContent: 'center',
alignItems: 'center',
marginBottom: 16,
},
title: {
fontSize: 28,
fontWeight: '700',
letterSpacing: 0.5,
marginBottom: 8,
},
subtitle: {
fontSize: 14,
textAlign: 'center',
opacity: 0.9,
},
scrollView: {
maxHeight: height * 0.45,
marginBottom: 20,
},
announcementItem: {
backgroundColor: '#252525',
flexDirection: 'row',
padding: 16,
borderRadius: 16,
marginBottom: 12,
},
announcementIcon: {
width: 48,
height: 48,
borderRadius: 24,
justifyContent: 'center',
alignItems: 'center',
marginRight: 16,
},
announcementContent: {
flex: 1,
},
announcementHeader: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 6,
},
announcementTitle: {
fontSize: 16,
fontWeight: '700',
letterSpacing: 0.3,
flex: 1,
},
tag: {
paddingHorizontal: 8,
paddingVertical: 4,
borderRadius: 6,
marginLeft: 8,
},
tagText: {
fontSize: 10,
fontWeight: '700',
color: '#FFFFFF',
textTransform: 'uppercase',
letterSpacing: 0.5,
},
announcementDescription: {
fontSize: 14,
lineHeight: 20,
opacity: 0.9,
},
button: {
borderRadius: 12,
paddingVertical: 16,
alignItems: 'center',
shadowColor: '#000',
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.2,
shadowRadius: 8,
elevation: 4,
},
buttonText: {
color: '#FFFFFF',
fontSize: 16,
fontWeight: '700',
letterSpacing: 0.5,
},
});
export default AnnouncementOverlay;

View file

@ -764,6 +764,21 @@ const SettingsScreen: React.FC = () => {
renderControl={ChevronRight}
isTablet={isTablet}
/>
<SettingItem
title="Test Announcement"
icon="bell"
description="Show what's new overlay"
onPress={async () => {
try {
await mmkvStorage.removeItem('announcement_v1.0.0_shown');
openAlert('Success', 'Announcement reset. Restart the app to see the announcement overlay.');
} catch (error) {
openAlert('Error', 'Failed to reset announcement.');
}
}}
renderControl={ChevronRight}
isTablet={isTablet}
/>
<SettingItem
title="Clear All Data"
icon="trash-2"