This commit is contained in:
tapframe 2025-07-27 19:10:22 +05:30
parent 2180453a16
commit 49e9b213bc
2 changed files with 71 additions and 26 deletions

View file

@ -39,29 +39,39 @@ const SPACING = {
const ANDROID_STATUSBAR_HEIGHT = StatusBar.currentHeight || 0; const ANDROID_STATUSBAR_HEIGHT = StatusBar.currentHeight || 0;
// Screen dimensions and grid layout
const { width } = Dimensions.get('window');
// Dynamic column calculation based on screen width // Dynamic column calculation based on screen width
const calculateCatalogLayout = (screenWidth: number) => { const calculateCatalogLayout = (screenWidth: number) => {
const MIN_ITEM_WIDTH = 120; // Increased minimum for better readability const MIN_ITEM_WIDTH = 120;
const MAX_ITEM_WIDTH = 160; // Adjusted maximum const MAX_ITEM_WIDTH = 180; // Increased for tablets
const HORIZONTAL_PADDING = SPACING.lg * 2; // Total horizontal padding const HORIZONTAL_PADDING = SPACING.lg * 2;
const ITEM_SPACING = SPACING.sm; // Space between items const ITEM_SPACING = SPACING.sm;
// Calculate how many columns can fit // Calculate how many columns can fit
const availableWidth = screenWidth - HORIZONTAL_PADDING; const availableWidth = screenWidth - HORIZONTAL_PADDING;
const maxColumns = Math.floor(availableWidth / (MIN_ITEM_WIDTH + ITEM_SPACING)); const maxColumns = Math.floor(availableWidth / (MIN_ITEM_WIDTH + ITEM_SPACING));
// Limit to reasonable number of columns (2-4 for better UX) // More flexible column limits for different screen sizes
const numColumns = Math.min(Math.max(maxColumns, 2), 4); let numColumns;
if (screenWidth < 600) {
// Phone: 2-3 columns
numColumns = Math.min(Math.max(maxColumns, 2), 3);
} else if (screenWidth < 900) {
// Small tablet: 3-5 columns
numColumns = Math.min(Math.max(maxColumns, 3), 5);
} else if (screenWidth < 1200) {
// Large tablet: 4-6 columns
numColumns = Math.min(Math.max(maxColumns, 4), 6);
} else {
// Very large screens: 5-8 columns
numColumns = Math.min(Math.max(maxColumns, 5), 8);
}
// Calculate actual item width with proper spacing // Calculate actual item width with proper spacing
const totalSpacing = ITEM_SPACING * (numColumns - 1); const totalSpacing = ITEM_SPACING * (numColumns - 1);
const itemWidth = (availableWidth - totalSpacing) / numColumns; const itemWidth = (availableWidth - totalSpacing) / numColumns;
// For 2 columns, ensure we use the full available width // Ensure item width doesn't exceed maximum
const finalItemWidth = numColumns === 2 ? itemWidth : Math.min(itemWidth, MAX_ITEM_WIDTH); const finalItemWidth = Math.min(itemWidth, MAX_ITEM_WIDTH);
return { return {
numColumns, numColumns,
@ -69,11 +79,6 @@ const calculateCatalogLayout = (screenWidth: number) => {
}; };
}; };
const catalogLayout = calculateCatalogLayout(width);
const NUM_COLUMNS = catalogLayout.numColumns;
const ITEM_MARGIN = SPACING.sm;
const ITEM_WIDTH = catalogLayout.itemWidth;
// Create a styles creator function that accepts the theme colors // Create a styles creator function that accepts the theme colors
const createStyles = (colors: any) => StyleSheet.create({ const createStyles = (colors: any) => StyleSheet.create({
container: { container: {
@ -85,6 +90,10 @@ const createStyles = (colors: any) => StyleSheet.create({
alignItems: 'center', alignItems: 'center',
paddingHorizontal: 16, paddingHorizontal: 16,
paddingTop: Platform.OS === 'android' ? ANDROID_STATUSBAR_HEIGHT + 8 : 8, paddingTop: Platform.OS === 'android' ? ANDROID_STATUSBAR_HEIGHT + 8 : 8,
// Center header on very wide screens
alignSelf: 'center',
maxWidth: 1400,
width: '100%',
}, },
backButton: { backButton: {
flexDirection: 'row', flexDirection: 'row',
@ -103,10 +112,18 @@ const createStyles = (colors: any) => StyleSheet.create({
paddingHorizontal: 16, paddingHorizontal: 16,
paddingBottom: 16, paddingBottom: 16,
paddingTop: 8, paddingTop: 8,
// Center title on very wide screens
alignSelf: 'center',
maxWidth: 1400,
width: '100%',
}, },
list: { list: {
padding: SPACING.lg, padding: SPACING.lg,
paddingTop: SPACING.sm, paddingTop: SPACING.sm,
// Center content on very wide screens
alignSelf: 'center',
maxWidth: 1400, // Prevent content from being too wide on large screens
width: '100%',
}, },
item: { item: {
marginBottom: SPACING.lg, marginBottom: SPACING.lg,
@ -162,6 +179,10 @@ const createStyles = (colors: any) => StyleSheet.create({
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
padding: SPACING.xl, padding: SPACING.xl,
// Center content on very wide screens
alignSelf: 'center',
maxWidth: 600, // Narrower max width for centered content
width: '100%',
}, },
emptyText: { emptyText: {
color: colors.white, color: colors.white,
@ -195,12 +216,31 @@ const CatalogScreen: React.FC<CatalogScreenProps> = ({ route, navigation }) => {
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const [dataSource, setDataSource] = useState<DataSource>(DataSource.STREMIO_ADDONS); const [dataSource, setDataSource] = useState<DataSource>(DataSource.STREMIO_ADDONS);
const [actualCatalogName, setActualCatalogName] = useState<string | null>(null); const [actualCatalogName, setActualCatalogName] = useState<string | null>(null);
const [screenData, setScreenData] = useState(() => {
const { width } = Dimensions.get('window');
return {
width,
...calculateCatalogLayout(width)
};
});
const { currentTheme } = useTheme(); const { currentTheme } = useTheme();
const colors = currentTheme.colors; const colors = currentTheme.colors;
const styles = createStyles(colors); const styles = createStyles(colors);
const isDarkMode = true; const isDarkMode = true;
const isInitialRender = React.useRef(true); const isInitialRender = React.useRef(true);
// Handle screen dimension changes
useEffect(() => {
const subscription = Dimensions.addEventListener('change', ({ window }) => {
setScreenData({
width: window.width,
...calculateCatalogLayout(window.width)
});
});
return () => subscription?.remove();
}, []);
const { getCustomName, isLoadingCustomNames } = useCustomCatalogNames(); const { getCustomName, isLoadingCustomNames } = useCustomCatalogNames();
// Create display name with proper type suffix // Create display name with proper type suffix
@ -504,8 +544,8 @@ const CatalogScreen: React.FC<CatalogScreenProps> = ({ route, navigation }) => {
const renderItem = useCallback(({ item, index }: { item: Meta; index: number }) => { const renderItem = useCallback(({ item, index }: { item: Meta; index: number }) => {
// Calculate if this is the last item in a row // Calculate if this is the last item in a row
const isLastInRow = (index + 1) % NUM_COLUMNS === 0; const isLastInRow = (index + 1) % screenData.numColumns === 0;
// For 2-column layout, ensure proper spacing // For proper spacing
const rightMargin = isLastInRow ? 0 : SPACING.sm; const rightMargin = isLastInRow ? 0 : SPACING.sm;
return ( return (
@ -514,8 +554,7 @@ const CatalogScreen: React.FC<CatalogScreenProps> = ({ route, navigation }) => {
styles.item, styles.item,
{ {
marginRight: rightMargin, marginRight: rightMargin,
// For 2 columns, ensure items fill the available space properly width: screenData.itemWidth
width: NUM_COLUMNS === 2 ? ITEM_WIDTH : ITEM_WIDTH
} }
]} ]}
onPress={() => navigation.navigate('Metadata', { id: item.id, type: item.type, addonId })} onPress={() => navigation.navigate('Metadata', { id: item.id, type: item.type, addonId })}
@ -542,7 +581,7 @@ const CatalogScreen: React.FC<CatalogScreenProps> = ({ route, navigation }) => {
</View> </View>
</TouchableOpacity> </TouchableOpacity>
); );
}, [navigation, styles, NUM_COLUMNS, ITEM_WIDTH]); }, [navigation, styles, screenData.numColumns, screenData.itemWidth]);
const renderEmptyState = () => ( const renderEmptyState = () => (
<View style={styles.centered}> <View style={styles.centered}>
@ -640,8 +679,8 @@ const CatalogScreen: React.FC<CatalogScreenProps> = ({ route, navigation }) => {
data={items} data={items}
renderItem={renderItem} renderItem={renderItem}
keyExtractor={(item) => `${item.id}-${item.type}`} keyExtractor={(item) => `${item.id}-${item.type}`}
numColumns={NUM_COLUMNS} numColumns={screenData.numColumns}
key={NUM_COLUMNS} key={screenData.numColumns}
refreshControl={ refreshControl={
<RefreshControl <RefreshControl
refreshing={refreshing} refreshing={refreshing}
@ -667,4 +706,4 @@ const CatalogScreen: React.FC<CatalogScreenProps> = ({ route, navigation }) => {
); );
}; };
export default CatalogScreen; export default CatalogScreen;

View file

@ -29,6 +29,7 @@ import { useTheme } from '../contexts/ThemeContext';
import { Stream } from '../types/metadata'; import { Stream } from '../types/metadata';
import { tmdbService } from '../services/tmdbService'; import { tmdbService } from '../services/tmdbService';
import { stremioService } from '../services/stremioService'; import { stremioService } from '../services/stremioService';
import { localScraperService } from '../services/localScraperService';
import { VideoPlayerService } from '../services/videoPlayerService'; import { VideoPlayerService } from '../services/videoPlayerService';
import { useSettings } from '../hooks/useSettings'; import { useSettings } from '../hooks/useSettings';
import QualityBadge from '../components/metadata/QualityBadge'; import QualityBadge from '../components/metadata/QualityBadge';
@ -365,7 +366,12 @@ export const StreamsScreen = () => {
const checkProviders = async () => { const checkProviders = async () => {
// Check for Stremio addons // Check for Stremio addons
const hasStremioProviders = await stremioService.hasStreamProviders(); const hasStremioProviders = await stremioService.hasStreamProviders();
const hasProviders = hasStremioProviders;
// Check for local scrapers (only if enabled in settings)
const hasLocalScrapers = settings.enableLocalScrapers && await localScraperService.hasScrapers();
// We have providers if we have either Stremio addons OR enabled local scrapers
const hasProviders = hasStremioProviders || hasLocalScrapers;
if (!isMounted.current) return; if (!isMounted.current) return;
@ -1648,4 +1654,4 @@ const createStyles = (colors: any) => StyleSheet.create({
}, },
}); });
export default memo(StreamsScreen); export default memo(StreamsScreen);