mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-04-21 00:32:04 +00:00
Update app.json icon, remove unused image, and refactor CatalogSection and StreamsScreen components for improved layout and performance
This commit updates the app.json to change the icon path, deletes an unused image file, and refactors the CatalogSection and StreamsScreen components. The CatalogSection now includes a title with a maximum line limit, while the StreamsScreen optimizes stream information handling and adjusts rendering parameters for better performance.
This commit is contained in:
parent
01d86198ba
commit
c4f5c9e374
5 changed files with 59 additions and 119 deletions
2
app.json
2
app.json
|
|
@ -4,7 +4,7 @@
|
||||||
"slug": "nuvio",
|
"slug": "nuvio",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"orientation": "default",
|
"orientation": "default",
|
||||||
"icon": "./assets/icon.png",
|
"icon": "./assets/splash-icon.png",
|
||||||
"userInterfaceStyle": "dark",
|
"userInterfaceStyle": "dark",
|
||||||
"scheme": "stremioexpo",
|
"scheme": "stremioexpo",
|
||||||
"newArchEnabled": true,
|
"newArchEnabled": true,
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 48 KiB |
|
|
@ -80,7 +80,7 @@ const CatalogSection = ({ catalog }: CatalogSectionProps) => {
|
||||||
>
|
>
|
||||||
<View style={styles.catalogHeader}>
|
<View style={styles.catalogHeader}>
|
||||||
<View style={styles.titleContainer}>
|
<View style={styles.titleContainer}>
|
||||||
<Text style={[styles.catalogTitle, { color: currentTheme.colors.text }]}>{catalog.name}</Text>
|
<Text style={[styles.catalogTitle, { color: currentTheme.colors.text }]} numberOfLines={1}>{catalog.name}</Text>
|
||||||
<View style={[styles.titleUnderline, { backgroundColor: currentTheme.colors.primary }]} />
|
<View style={[styles.titleUnderline, { backgroundColor: currentTheme.colors.primary }]} />
|
||||||
</View>
|
</View>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
|
|
@ -141,6 +141,8 @@ const styles = StyleSheet.create({
|
||||||
},
|
},
|
||||||
titleContainer: {
|
titleContainer: {
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
|
flex: 1,
|
||||||
|
marginRight: 16,
|
||||||
},
|
},
|
||||||
catalogTitle: {
|
catalogTitle: {
|
||||||
fontSize: 24,
|
fontSize: 24,
|
||||||
|
|
@ -164,7 +166,6 @@ const styles = StyleSheet.create({
|
||||||
paddingHorizontal: 10,
|
paddingHorizontal: 10,
|
||||||
borderRadius: 20,
|
borderRadius: 20,
|
||||||
backgroundColor: 'rgba(255,255,255,0.1)',
|
backgroundColor: 'rgba(255,255,255,0.1)',
|
||||||
marginRight: -10,
|
|
||||||
},
|
},
|
||||||
viewAllText: {
|
viewAllText: {
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
|
|
|
||||||
|
|
@ -393,8 +393,6 @@ const WrappedScreen: React.FC<{Screen: React.ComponentType<any>}> = ({ Screen })
|
||||||
|
|
||||||
// Tab Navigator
|
// Tab Navigator
|
||||||
const MainTabs = () => {
|
const MainTabs = () => {
|
||||||
// Always use dark mode
|
|
||||||
const isDarkMode = true;
|
|
||||||
const { currentTheme } = useTheme();
|
const { currentTheme } = useTheme();
|
||||||
|
|
||||||
const renderTabBar = (props: BottomTabBarProps) => {
|
const renderTabBar = (props: BottomTabBarProps) => {
|
||||||
|
|
@ -536,124 +534,57 @@ const MainTabs = () => {
|
||||||
|
|
||||||
<Tab.Navigator
|
<Tab.Navigator
|
||||||
tabBar={renderTabBar}
|
tabBar={renderTabBar}
|
||||||
screenOptions={({ route }) => ({
|
screenOptions={({ route, navigation, theme }) => ({
|
||||||
tabBarIcon: ({ focused, color, size }) => {
|
header: () => (route.name === 'Home' ? <NuvioHeader /> : null),
|
||||||
let iconName: IconNameType = 'home';
|
headerShown: route.name === 'Home',
|
||||||
|
tabBarShowLabel: false,
|
||||||
switch (route.name) {
|
|
||||||
case 'Home':
|
|
||||||
iconName = 'home';
|
|
||||||
break;
|
|
||||||
case 'Library':
|
|
||||||
iconName = 'play-box-multiple';
|
|
||||||
break;
|
|
||||||
case 'Search':
|
|
||||||
iconName = 'feature-search';
|
|
||||||
break;
|
|
||||||
case 'Settings':
|
|
||||||
iconName = 'cog';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <TabIcon focused={focused} color={color} iconName={iconName} />;
|
|
||||||
},
|
|
||||||
tabBarActiveTintColor: currentTheme.colors.primary,
|
|
||||||
tabBarInactiveTintColor: currentTheme.colors.white,
|
|
||||||
tabBarStyle: {
|
tabBarStyle: {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
backgroundColor: 'transparent',
|
|
||||||
borderTopWidth: 0,
|
borderTopWidth: 0,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
height: 85,
|
|
||||||
paddingBottom: 20,
|
|
||||||
paddingTop: 12,
|
|
||||||
},
|
|
||||||
tabBarLabelStyle: {
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: '600',
|
|
||||||
marginTop: 0,
|
|
||||||
},
|
|
||||||
// Completely disable animations between tabs for better performance
|
|
||||||
animationEnabled: false,
|
|
||||||
// Keep all screens mounted and active
|
|
||||||
lazy: false,
|
|
||||||
freezeOnBlur: false,
|
|
||||||
detachPreviousScreen: false,
|
|
||||||
// Configure how the screen renders
|
|
||||||
detachInactiveScreens: false,
|
|
||||||
tabBarBackground: () => (
|
|
||||||
Platform.OS === 'ios' ? (
|
|
||||||
<BlurView
|
|
||||||
tint="dark"
|
|
||||||
intensity={75}
|
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
height: '100%',
|
|
||||||
width: '100%',
|
|
||||||
borderTopColor: currentTheme.colors.border,
|
|
||||||
borderTopWidth: 0.5,
|
|
||||||
shadowColor: currentTheme.colors.black,
|
|
||||||
shadowOffset: { width: 0, height: -2 },
|
|
||||||
shadowOpacity: 0.1,
|
|
||||||
shadowRadius: 3,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<LinearGradient
|
|
||||||
colors={[
|
|
||||||
'rgba(0, 0, 0, 0)',
|
|
||||||
'rgba(0, 0, 0, 0.65)',
|
|
||||||
'rgba(0, 0, 0, 0.85)',
|
|
||||||
'rgba(0, 0, 0, 0.98)',
|
|
||||||
]}
|
|
||||||
locations={[0, 0.2, 0.4, 0.8]}
|
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
height: '100%',
|
|
||||||
width: '100%',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
),
|
|
||||||
header: () => route.name === 'Home' ? <NuvioHeader /> : null,
|
|
||||||
headerShown: route.name === 'Home',
|
|
||||||
// Add fixed screen styling to help with consistency
|
|
||||||
contentStyle: {
|
|
||||||
backgroundColor: currentTheme.colors.darkBackground,
|
backgroundColor: currentTheme.colors.darkBackground,
|
||||||
},
|
},
|
||||||
|
detachInactiveScreens: false,
|
||||||
})}
|
})}
|
||||||
// Global configuration for the tab navigator
|
|
||||||
detachInactiveScreens={false}
|
|
||||||
>
|
>
|
||||||
<Tab.Screen
|
<Tab.Screen
|
||||||
name="Home"
|
name="Home"
|
||||||
component={HomeScreen}
|
component={HomeScreen}
|
||||||
options={{
|
options={{
|
||||||
tabBarLabel: 'Home',
|
tabBarLabel: 'Home',
|
||||||
|
tabBarIcon: ({ color, size, focused }) => (
|
||||||
|
<MaterialCommunityIcons name={focused ? 'home' : 'home-outline'} size={size} color={color} />
|
||||||
|
),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Tab.Screen
|
<Tab.Screen
|
||||||
name="Library"
|
name="Library"
|
||||||
component={LibraryScreen}
|
component={LibraryScreen}
|
||||||
options={{
|
options={{
|
||||||
tabBarLabel: 'Library',
|
tabBarLabel: 'Library',
|
||||||
headerShown: false
|
tabBarIcon: ({ color, size, focused }) => (
|
||||||
|
<MaterialCommunityIcons name={focused ? 'play-box-multiple' : 'play-box-multiple-outline'} size={size} color={color} />
|
||||||
|
),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Tab.Screen
|
<Tab.Screen
|
||||||
name="Search"
|
name="Search"
|
||||||
component={SearchScreen}
|
component={SearchScreen}
|
||||||
options={{
|
options={{
|
||||||
tabBarLabel: 'Search',
|
tabBarLabel: 'Search',
|
||||||
headerShown: false
|
tabBarIcon: ({ color, size, focused }) => (
|
||||||
|
<MaterialCommunityIcons name={focused ? 'feature-search' : 'feature-search-outline'} size={size} color={color} />
|
||||||
|
),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Tab.Screen
|
<Tab.Screen
|
||||||
name="Settings"
|
name="Settings"
|
||||||
component={SettingsScreen}
|
component={SettingsScreen}
|
||||||
options={{
|
options={{
|
||||||
tabBarLabel: 'Settings',
|
tabBarLabel: 'Settings',
|
||||||
headerShown: false
|
tabBarIcon: ({ color, size, focused }) => (
|
||||||
|
<MaterialCommunityIcons name={focused ? 'cog' : 'cog-outline'} size={size} color={color} />
|
||||||
|
),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Tab.Navigator>
|
</Tab.Navigator>
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ const DOLBY_ICON = 'https://upload.wikimedia.org/wikipedia/en/thumb/3/3f/Dolby_V
|
||||||
const { width, height } = Dimensions.get('window');
|
const { width, height } = Dimensions.get('window');
|
||||||
|
|
||||||
// Extracted Components
|
// Extracted Components
|
||||||
const StreamCard = ({ stream, onPress, index, isLoading, statusMessage, theme }: {
|
const StreamCard = memo(({ stream, onPress, index, isLoading, statusMessage, theme }: {
|
||||||
stream: Stream;
|
stream: Stream;
|
||||||
onPress: () => void;
|
onPress: () => void;
|
||||||
index: number;
|
index: number;
|
||||||
|
|
@ -66,11 +66,19 @@ const StreamCard = ({ stream, onPress, index, isLoading, statusMessage, theme }:
|
||||||
}) => {
|
}) => {
|
||||||
const styles = React.useMemo(() => createStyles(theme.colors), [theme.colors]);
|
const styles = React.useMemo(() => createStyles(theme.colors), [theme.colors]);
|
||||||
|
|
||||||
const quality = stream.title?.match(/(\d+)p/)?.[1] || null;
|
const streamInfo = useMemo(() => {
|
||||||
const isHDR = stream.title?.toLowerCase().includes('hdr');
|
const title = stream.title || '';
|
||||||
const isDolby = stream.title?.toLowerCase().includes('dolby') || stream.title?.includes('DV');
|
const name = stream.name || '';
|
||||||
const size = stream.title?.match(/💾\s*([\d.]+\s*[GM]B)/)?.[1];
|
return {
|
||||||
const isDebrid = stream.behaviorHints?.cached;
|
quality: title.match(/(\d+)p/)?.[1] || null,
|
||||||
|
isHDR: title.toLowerCase().includes('hdr'),
|
||||||
|
isDolby: title.toLowerCase().includes('dolby') || title.includes('DV'),
|
||||||
|
size: title.match(/💾\s*([\d.]+\s*[GM]B)/)?.[1],
|
||||||
|
isDebrid: stream.behaviorHints?.cached,
|
||||||
|
displayName: name || title || 'Unnamed Stream',
|
||||||
|
subTitle: title && title !== name ? title : null
|
||||||
|
};
|
||||||
|
}, [stream.name, stream.title, stream.behaviorHints]);
|
||||||
|
|
||||||
// Animation delay based on index - stagger effect
|
// Animation delay based on index - stagger effect
|
||||||
const enterDelay = 100 + (index * 30);
|
const enterDelay = 100 + (index * 30);
|
||||||
|
|
@ -93,11 +101,11 @@ const StreamCard = ({ stream, onPress, index, isLoading, statusMessage, theme }:
|
||||||
<View style={styles.streamNameRow}>
|
<View style={styles.streamNameRow}>
|
||||||
<View style={styles.streamTitleContainer}>
|
<View style={styles.streamTitleContainer}>
|
||||||
<Text style={[styles.streamName, { color: theme.colors.highEmphasis }]}>
|
<Text style={[styles.streamName, { color: theme.colors.highEmphasis }]}>
|
||||||
{stream.name || stream.title || 'Unnamed Stream'}
|
{streamInfo.displayName}
|
||||||
</Text>
|
</Text>
|
||||||
{stream.title && stream.title !== stream.name && (
|
{streamInfo.subTitle && (
|
||||||
<Text style={[styles.streamAddonName, { color: theme.colors.mediumEmphasis }]}>
|
<Text style={[styles.streamAddonName, { color: theme.colors.mediumEmphasis }]}>
|
||||||
{stream.title}
|
{streamInfo.subTitle}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
|
@ -114,21 +122,21 @@ const StreamCard = ({ stream, onPress, index, isLoading, statusMessage, theme }:
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={styles.streamMetaRow}>
|
<View style={styles.streamMetaRow}>
|
||||||
{quality && quality >= "720" && (
|
{streamInfo.quality && streamInfo.quality >= "720" && (
|
||||||
<QualityBadge type="HD" />
|
<QualityBadge type="HD" />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isDolby && (
|
{streamInfo.isDolby && (
|
||||||
<QualityBadge type="VISION" />
|
<QualityBadge type="VISION" />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{size && (
|
{streamInfo.size && (
|
||||||
<View style={[styles.chip, { backgroundColor: theme.colors.darkGray }]}>
|
<View style={[styles.chip, { backgroundColor: theme.colors.darkGray }]}>
|
||||||
<Text style={[styles.chipText, { color: theme.colors.white }]}>{size}</Text>
|
<Text style={[styles.chipText, { color: theme.colors.white }]}>{streamInfo.size}</Text>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isDebrid && (
|
{streamInfo.isDebrid && (
|
||||||
<View style={[styles.chip, { backgroundColor: theme.colors.success }]}>
|
<View style={[styles.chip, { backgroundColor: theme.colors.success }]}>
|
||||||
<Text style={[styles.chipText, { color: theme.colors.white }]}>DEBRID</Text>
|
<Text style={[styles.chipText, { color: theme.colors.white }]}>DEBRID</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
@ -146,7 +154,7 @@ const StreamCard = ({ stream, onPress, index, isLoading, statusMessage, theme }:
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
const QualityTag = React.memo(({ text, color, theme }: { text: string; color: string; theme: any }) => {
|
const QualityTag = React.memo(({ text, color, theme }: { text: string; color: string; theme: any }) => {
|
||||||
const styles = React.useMemo(() => createStyles(theme.colors), [theme.colors]);
|
const styles = React.useMemo(() => createStyles(theme.colors), [theme.colors]);
|
||||||
|
|
@ -1173,9 +1181,9 @@ export const StreamsScreen = () => {
|
||||||
renderItem={renderItem}
|
renderItem={renderItem}
|
||||||
renderSectionHeader={renderSectionHeader}
|
renderSectionHeader={renderSectionHeader}
|
||||||
stickySectionHeadersEnabled={false}
|
stickySectionHeadersEnabled={false}
|
||||||
initialNumToRender={8}
|
initialNumToRender={6}
|
||||||
maxToRenderPerBatch={4}
|
maxToRenderPerBatch={3}
|
||||||
windowSize={5}
|
windowSize={4}
|
||||||
removeClippedSubviews={false}
|
removeClippedSubviews={false}
|
||||||
contentContainerStyle={styles.streamsContainer}
|
contentContainerStyle={styles.streamsContainer}
|
||||||
style={styles.streamsContent}
|
style={styles.streamsContent}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue