Streamscreen UI Truncation fix

This commit is contained in:
tapframe 2025-09-17 15:20:19 +05:30
parent 59c0b6ba1b
commit 502a683ba2
6 changed files with 128 additions and 67 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View file

@ -95,7 +95,14 @@ const HeroCarousel: React.FC<HeroCarouselProps> = ({ items, loading = false }) =
contentContainerStyle={{ paddingHorizontal: (width - CARD_WIDTH) / 2 }}
renderItem={() => (
<View style={{ width: CARD_WIDTH + 16 }}>
<View style={[styles.card, { backgroundColor: currentTheme.colors.elevation1 }] as StyleProp<ViewStyle>}>
<View style={[
styles.card,
{
backgroundColor: currentTheme.colors.elevation1,
borderWidth: 1,
borderColor: 'rgba(255,255,255,0.18)',
}
] as StyleProp<ViewStyle>}>
<View style={styles.bannerContainer as ViewStyle}>
<View style={styles.skeletonBannerFull as ViewStyle} />
<LinearGradient
@ -264,7 +271,14 @@ const CarouselCard: React.FC<CarouselCardProps> = memo(({ item, colors, logoFail
activeOpacity={0.9}
onPress={onPressInfo}
>
<View style={[styles.card, { backgroundColor: colors.elevation1 }] as StyleProp<ViewStyle>}>
<View style={[
styles.card,
{
backgroundColor: colors.elevation1,
borderWidth: 1,
borderColor: 'rgba(255,255,255,0.18)',
}
] as StyleProp<ViewStyle>}>
<View style={styles.bannerContainer as ViewStyle}>
<ExpoImage
source={{ uri: item.banner || item.poster }}

View file

@ -831,8 +831,33 @@ const LibraryScreen = () => {
}
if (filteredItems.length === 0) {
// Intentionally render nothing to match the minimal empty state in the design
return <View style={styles.listContainer} />;
const emptyTitle = filter === 'movies' ? 'No movies yet' : filter === 'series' ? 'No TV shows yet' : 'No content yet';
const emptySubtitle = 'Add some content to your library to see it here';
return (
<View style={styles.emptyContainer}>
<MaterialIcons
name="video-library"
size={64}
color={currentTheme.colors.lightGray}
/>
<Text style={[styles.emptyText, { color: currentTheme.colors.white }]}>
{emptyTitle}
</Text>
<Text style={[styles.emptySubtext, { color: currentTheme.colors.mediumGray }]}>
{emptySubtitle}
</Text>
<TouchableOpacity
style={[styles.exploreButton, {
backgroundColor: currentTheme.colors.primary,
shadowColor: currentTheme.colors.black
}]}
onPress={() => navigation.navigate('Search')}
activeOpacity={0.7}
>
<Text style={[styles.exploreButtonText, { color: currentTheme.colors.white }]}>Find something to watch</Text>
</TouchableOpacity>
</View>
);
}
return (

View file

@ -9,6 +9,7 @@ import {
TouchableOpacity,
InteractionManager,
BackHandler,
Platform,
} from 'react-native';
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';
import { useRoute, useNavigation, useFocusEffect } from '@react-navigation/native';
@ -612,8 +613,10 @@ const MetadataScreen: React.FC = () => {
showsVerticalScrollIndicator={false}
onScroll={animations.scrollHandler}
scrollEventThrottle={16}
bounces={false}
overScrollMode="never"
bounces={Platform.OS === 'ios'}
overScrollMode={Platform.OS === 'android' ? 'always' : 'always'}
nestedScrollEnabled
keyboardShouldPersistTaps="handled"
contentContainerStyle={styles.scrollContent}
>
{/* Hero Section - Optimized */}

View file

@ -719,32 +719,46 @@ const SettingsScreen: React.FC = () => {
{renderCategoryContent(selectedCategory)}
{selectedCategory === 'about' && (
<View style={styles.footer}>
<Text style={[styles.footerText, { color: currentTheme.colors.mediumEmphasis }]}>
Made with by the Nuvio team
</Text>
</View>
)}
{/* Discord Join Button - Show on all categories for tablet */}
<View style={styles.discordContainer}>
<TouchableOpacity
style={[styles.discordButton, { backgroundColor: currentTheme.colors.elevation1 }]}
onPress={() => Linking.openURL('https://discord.gg/6w8dr3TSDN')}
activeOpacity={0.7}
>
<View style={styles.discordButtonContent}>
<Image
source={{ uri: 'https://pngimg.com/uploads/discord/discord_PNG3.png' }}
style={styles.discordLogo}
resizeMode="contain"
/>
<Text style={[styles.discordButtonText, { color: currentTheme.colors.highEmphasis }]}>
Join Discord
<>
<View style={styles.footer}>
<Text style={[styles.footerText, { color: currentTheme.colors.mediumEmphasis }]}>
Made with by Tapframe and Friends
</Text>
</View>
</TouchableOpacity>
</View>
<View style={styles.discordContainer}>
<View style={{ flexDirection: 'row', gap: 12 }}>
<TouchableOpacity
style={[styles.discordButton, { backgroundColor: currentTheme.colors.elevation1 }]}
onPress={() => Linking.openURL('https://discord.gg/6w8dr3TSDN')}
activeOpacity={0.7}
>
<View style={styles.discordButtonContent}>
<Image
source={{ uri: 'https://pngimg.com/uploads/discord/discord_PNG3.png' }}
style={styles.discordLogo}
resizeMode="contain"
/>
<Text style={[styles.discordButtonText, { color: currentTheme.colors.highEmphasis }]}>
Join Discord
</Text>
</View>
</TouchableOpacity>
<TouchableOpacity
style={[styles.discordButton, { backgroundColor: 'transparent', paddingVertical: 0, paddingHorizontal: 0 }]}
onPress={() => Linking.openURL('https://ko-fi.com/tapframe')}
activeOpacity={0.7}
>
<Image
source={require('../../assets/support_me_on_kofi_red.png')}
style={styles.kofiImage}
resizeMode="contain"
/>
</TouchableOpacity>
</View>
</View>
</>
)}
</ScrollView>
</View>
</View>
@ -785,28 +799,42 @@ const SettingsScreen: React.FC = () => {
<View style={styles.footer}>
<Text style={[styles.footerText, { color: currentTheme.colors.mediumEmphasis }]}>
Made with by the Nuvio team
Made with by Tapframe and friends
</Text>
</View>
{/* Discord Join Button */}
{/* Support & Community Buttons */}
<View style={styles.discordContainer}>
<TouchableOpacity
style={[styles.discordButton, { backgroundColor: currentTheme.colors.elevation1 }]}
onPress={() => Linking.openURL('https://discord.gg/6w8dr3TSDN')}
activeOpacity={0.7}
>
<View style={styles.discordButtonContent}>
<View style={{ flexDirection: 'row', gap: 12 }}>
<TouchableOpacity
style={[styles.discordButton, { backgroundColor: currentTheme.colors.elevation1 }]}
onPress={() => Linking.openURL('https://discord.gg/6w8dr3TSDN')}
activeOpacity={0.7}
>
<View style={styles.discordButtonContent}>
<Image
source={{ uri: 'https://pngimg.com/uploads/discord/discord_PNG3.png' }}
style={styles.discordLogo}
resizeMode="contain"
/>
<Text style={[styles.discordButtonText, { color: currentTheme.colors.highEmphasis }]}>
Join Discord
</Text>
</View>
</TouchableOpacity>
<TouchableOpacity
style={[styles.discordButton, { backgroundColor: 'transparent', paddingVertical: 0, paddingHorizontal: 0 }]}
onPress={() => Linking.openURL('https://ko-fi.com/tapframe')}
activeOpacity={0.7}
>
<Image
source={{ uri: 'https://pngimg.com/uploads/discord/discord_PNG3.png' }}
style={styles.discordLogo}
source={require('../../assets/support_me_on_kofi_red.png')}
style={styles.kofiImage}
resizeMode="contain"
/>
<Text style={[styles.discordButtonText, { color: currentTheme.colors.highEmphasis }]}>
Join Discord
</Text>
</View>
</TouchableOpacity>
</TouchableOpacity>
</View>
</View>
</ScrollView>
</View>
@ -1045,7 +1073,7 @@ const styles = StyleSheet.create({
alignItems: 'center',
justifyContent: 'center',
marginTop: 10,
marginBottom: 20,
marginBottom: 8,
},
footerText: {
fontSize: 14,
@ -1053,7 +1081,7 @@ const styles = StyleSheet.create({
},
// New styles for Discord button
discordContainer: {
marginTop: 20,
marginTop: 8,
marginBottom: 20,
alignItems: 'center',
},
@ -1079,6 +1107,10 @@ const styles = StyleSheet.create({
fontSize: 14,
fontWeight: '500',
},
kofiImage: {
height: 32,
width: 150,
},
loadingSpinner: {
width: 16,
height: 16,

View file

@ -1197,26 +1197,15 @@ class StremioService {
const isDirectStreamingUrl = this.isDirectStreamingUrl(streamUrl);
const isMagnetStream = streamUrl?.startsWith('magnet:');
// Memory optimization: Limit title length to prevent memory bloat
// Prefer full, untruncated text to preserve complete addon details
let displayTitle = stream.title || stream.name || 'Unnamed Stream';
if (stream.description && stream.description.includes('\n') && stream.description.length > (stream.title?.length || 0)) {
// If description exists, contains newlines (likely formatted metadata),
// and is longer than the title, prefer it but truncate if too long
displayTitle = stream.description.length > 150
? stream.description.substring(0, 150) + '...'
: stream.description;
// If description exists and is likely the formatted metadata, prefer it as-is
displayTitle = stream.description;
}
// Truncate display title if still too long
if (displayTitle.length > 100) {
displayTitle = displayTitle.substring(0, 100) + '...';
}
// Use the original name field for the primary identifier if available
// Use full name for primary identifier if available
let name = stream.name || stream.title || 'Unnamed Stream';
if (name.length > 80) {
name = name.substring(0, 80) + '...';
}
// Extract size: Prefer behaviorHints.videoSize, fallback to top-level size
const sizeInBytes = stream.behaviorHints?.videoSize || stream.size || undefined;
@ -1241,10 +1230,8 @@ class StremioService {
title: displayTitle,
addonName: addon.name,
addonId: addon.id,
// Memory optimization: Only include essential fields
description: stream.description && stream.description.length <= 100
? stream.description
: undefined, // Skip long descriptions
// Include description as-is to preserve full details
description: stream.description,
infoHash: stream.infoHash || undefined,
fileIdx: stream.fileIdx,
size: sizeInBytes,