some UI changes

This commit is contained in:
tapframe 2025-09-01 18:17:08 +05:30
parent c1bae1d7f3
commit 9b1368e7c6
8 changed files with 163 additions and 26 deletions

Binary file not shown.

View file

@ -3,7 +3,7 @@ import { View, Text, StyleSheet, Dimensions } from 'react-native';
import LottieView from 'lottie-react-native';
import { useTheme } from '../../contexts/ThemeContext';
interface DogLoadingSpinnerProps {
interface LoadingSpinnerProps {
text?: string;
size?: 'small' | 'medium' | 'large';
style?: any;
@ -11,7 +11,7 @@ interface DogLoadingSpinnerProps {
offsetY?: number; // optional vertical offset
}
const DogLoadingSpinner: React.FC<DogLoadingSpinnerProps> = ({
const LoadingSpinner: React.FC<LoadingSpinnerProps> = ({
text,
size = 'large',
style,
@ -47,7 +47,7 @@ const DogLoadingSpinner: React.FC<DogLoadingSpinnerProps> = ({
return (
<View style={[styles.container, { transform: [{ translateY: offsetY }] }, style]}>
<LottieView
source={source || require('../../../assets/dog-running.lottie')}
source={source || require('../../../assets/Ripple loading animation.lottie')}
autoPlay
loop
style={[styles.animation, getSizeStyles()]}
@ -83,4 +83,4 @@ const styles = StyleSheet.create({
},
});
export default DogLoadingSpinner;
export default LoadingSpinner;

View file

@ -0,0 +1,86 @@
import React from 'react';
import { View, Text, StyleSheet, Dimensions } from 'react-native';
import LottieView from 'lottie-react-native';
import { useTheme } from '../../contexts/ThemeContext';
interface LoadingSpinnerProps {
text?: string;
size?: 'small' | 'medium' | 'large';
style?: any;
source?: any; // optional override for Lottie source
offsetY?: number; // optional vertical offset
}
const LoadingSpinner: React.FC<LoadingSpinnerProps> = ({
text,
size = 'large',
style,
source,
offsetY = 0,
}) => {
const { currentTheme } = useTheme();
const getSizeStyles = () => {
switch (size) {
case 'small':
return { width: 60, height: 60 };
case 'medium':
return { width: 100, height: 100 };
case 'large':
default:
return { width: 150, height: 150 };
}
};
const getTextSize = () => {
switch (size) {
case 'small':
return 12;
case 'medium':
return 14;
case 'large':
default:
return 16;
}
};
return (
<View style={[styles.container, { transform: [{ translateY: offsetY }] }, style]}>
<LottieView
source={source || require('../../../assets/Ripple loading animation.lottie')}
autoPlay
loop
style={[styles.animation, getSizeStyles()]}
resizeMode="contain"
/>
{text && (
<Text style={[
styles.text,
{
color: currentTheme.colors.textMuted,
fontSize: getTextSize()
}
]}>
{text}
</Text>
)}
</View>
);
};
const styles = StyleSheet.create({
container: {
justifyContent: 'center',
alignItems: 'center',
},
animation: {
// Size will be set by getSizeStyles()
},
text: {
marginTop: 16,
textAlign: 'center',
fontWeight: '500',
},
});
export default LoadingSpinner;

View file

@ -0,0 +1,31 @@
import React, { createContext, useContext, useState, ReactNode } from 'react';
interface LoadingContextValue {
isHomeLoading: boolean;
setHomeLoading: (loading: boolean) => void;
}
const LoadingContext = createContext<LoadingContextValue | undefined>(undefined);
export const LoadingProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
const [isHomeLoading, setIsHomeLoading] = useState(true);
const value: LoadingContextValue = {
isHomeLoading,
setHomeLoading: setIsHomeLoading,
};
return (
<LoadingContext.Provider value={value}>
{children}
</LoadingContext.Provider>
);
};
export const useLoading = (): LoadingContextValue => {
const context = useContext(LoadingContext);
if (!context) {
throw new Error('useLoading must be used within a LoadingProvider');
}
return context;
};

View file

@ -44,6 +44,7 @@ import OnboardingScreen from '../screens/OnboardingScreen';
import AuthScreen from '../screens/AuthScreen';
import AccountManageScreen from '../screens/AccountManageScreen';
import { AccountProvider, useAccount } from '../contexts/AccountContext';
import { LoadingProvider, useLoading } from '../contexts/LoadingContext';
import PluginsScreen from '../screens/PluginsScreen';
import CastMoviesScreen from '../screens/CastMoviesScreen';
@ -419,6 +420,7 @@ const WrappedScreen: React.FC<{Screen: React.ComponentType<any>}> = ({ Screen })
// Tab Navigator
const MainTabs = () => {
const { currentTheme } = useTheme();
const { isHomeLoading } = useLoading();
const isTablet = Dimensions.get('window').width >= 768;
const insets = useSafeAreaInsets();
const isIosTablet = Platform.OS === 'ios' && isTablet;
@ -438,6 +440,11 @@ const MainTabs = () => {
const fade = headerAnim.interpolate({ inputRange: [0, 1], outputRange: [1, 0] });
const renderTabBar = (props: BottomTabBarProps) => {
// Hide tab bar when home is loading
if (isHomeLoading) {
return null;
}
if (isTablet) {
// Top floating, text-only pill nav for tablets
return (
@ -840,17 +847,15 @@ const InnerNavigator = ({ initialRouteName }: { initialRouteName?: keyof RootSta
}),
}}
>
{!loading && !user && (
<Stack.Screen
name="Account"
component={AuthScreen as any}
options={{
headerShown: false,
animation: 'fade',
contentStyle: { backgroundColor: currentTheme.colors.darkBackground },
}}
/>
)}
<Stack.Screen
name="Account"
component={AuthScreen as any}
options={{
headerShown: false,
animation: 'fade',
contentStyle: { backgroundColor: currentTheme.colors.darkBackground },
}}
/>
<Stack.Screen
name="Onboarding"
component={OnboardingScreen}
@ -1203,7 +1208,9 @@ const AppNavigator = ({ initialRouteName }: { initialRouteName?: keyof RootStack
}}
>
<AccountProvider>
<InnerNavigator initialRouteName={initialRouteName} />
<LoadingProvider>
<InnerNavigator initialRouteName={initialRouteName} />
</LoadingProvider>
</AccountProvider>
</PostHogProvider>
);

View file

@ -48,10 +48,11 @@ import { useSettings, settingsEmitter } from '../hooks/useSettings';
import FeaturedContent from '../components/home/FeaturedContent';
import CatalogSection from '../components/home/CatalogSection';
import { SkeletonFeatured } from '../components/home/SkeletonLoaders';
import DogLoadingSpinner from '../components/common/DogLoadingSpinner';
import LoadingSpinner from '../components/common/LoadingSpinner';
import homeStyles, { sharedStyles } from '../styles/homeStyles';
import { useTheme } from '../contexts/ThemeContext';
import type { Theme } from '../contexts/ThemeContext';
import { useLoading } from '../contexts/LoadingContext';
import * as ScreenOrientation from 'expo-screen-orientation';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
@ -94,7 +95,7 @@ const SkeletonCatalog = React.memo(() => {
return (
<View style={styles.catalogContainer}>
<View style={styles.loadingPlaceholder}>
<DogLoadingSpinner size="small" text="" />
<LoadingSpinner size="small" text="" />
</View>
</View>
);
@ -104,6 +105,7 @@ const HomeScreen = () => {
const navigation = useNavigation<NavigationProp<RootStackParamList>>();
const isDarkMode = useColorScheme() === 'dark';
const { currentTheme } = useTheme();
const { setHomeLoading } = useLoading();
const continueWatchingRef = useRef<ContinueWatchingRef>(null);
const { settings } = useSettings();
const { lastUpdate } = useCatalogContext(); // Add catalog context to listen for addon changes
@ -293,6 +295,11 @@ const HomeScreen = () => {
return heroLoading && (catalogsLoading && loadedCatalogCount === 0);
}, [showHeroSection, featuredLoading, catalogsLoading, loadedCatalogCount]);
// Update global loading state
useEffect(() => {
setHomeLoading(isLoading);
}, [isLoading, setHomeLoading]);
// React to settings changes
useEffect(() => {
setShowHeroSection(settings.showHeroSection);
@ -539,7 +546,7 @@ const HomeScreen = () => {
translucent
/>
<View style={styles.loadingMainContainer}>
<DogLoadingSpinner size="large" offsetY={-20} />
<LoadingSpinner size="large" offsetY={-20} />
</View>
</View>
);
@ -639,7 +646,7 @@ const HomeScreen = () => {
<View style={styles.catalogPlaceholder}>
<View style={styles.placeholderHeader}>
<View style={[styles.placeholderTitle, { backgroundColor: currentTheme.colors.elevation1 }]} />
<DogLoadingSpinner size="small" text="" />
<LoadingSpinner size="small" text="" />
</View>
<ScrollView
horizontal

View file

@ -40,7 +40,7 @@ import { logger } from '../utils/logger';
import { BlurView } from 'expo-blur';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { useTheme } from '../contexts/ThemeContext';
import DogLoadingSpinner from '../components/common/DogLoadingSpinner';
import LoadingSpinner from '../components/common/LoadingSpinner';
const { width, height } = Dimensions.get('window');
const isTablet = width >= 768;
@ -585,10 +585,9 @@ const SearchScreen = () => {
<View style={[styles.contentContainer, { backgroundColor: currentTheme.colors.darkBackground }]}>
{searching ? (
<View style={styles.loadingOverlay} pointerEvents="none">
<DogLoadingSpinner
<LoadingSpinner
size="large"
offsetY={-60}
source={require('../../assets/Progress of loading hand.lottie')}
/>
</View>
) : query.trim().length === 1 ? (

View file

@ -227,7 +227,7 @@ const SettingsScreen: React.FC = () => {
const { isAuthenticated, userProfile, refreshAuthStatus } = useTraktContext();
const { currentTheme } = useTheme();
const insets = useSafeAreaInsets();
const { user, signOut } = useAccount();
const { user, signOut, loading: accountLoading } = useAccount();
// Tablet-specific state
const [selectedCategory, setSelectedCategory] = useState('account');
@ -378,7 +378,7 @@ const SettingsScreen: React.FC = () => {
case 'account':
return (
<SettingsCard title="ACCOUNT" isTablet={isTablet}>
{user ? (
{!accountLoading && user ? (
<>
<SettingItem
title={user.displayName || user.email || user.id}
@ -388,7 +388,7 @@ const SettingsScreen: React.FC = () => {
isTablet={isTablet}
/>
</>
) : (
) : !accountLoading && !user ? (
<SettingItem
title="Sign in / Create account"
description="Sync across devices"
@ -396,6 +396,13 @@ const SettingsScreen: React.FC = () => {
onPress={() => navigation.navigate('Account')}
isTablet={isTablet}
/>
) : (
<SettingItem
title="Loading account..."
description="Please wait"
icon="hourglass-empty"
isTablet={isTablet}
/>
)}
<SettingItem
title="Trakt"