From f4b50f6dfb790c091ec5265652f6d2bbbcedb6b3 Mon Sep 17 00:00:00 2001 From: tapframe Date: Wed, 30 Jul 2025 01:06:44 +0530 Subject: [PATCH] ui changes --- src/screens/StreamsScreen.tsx | 91 +++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/src/screens/StreamsScreen.tsx b/src/screens/StreamsScreen.tsx index c7a7244..07ec6cf 100644 --- a/src/screens/StreamsScreen.tsx +++ b/src/screens/StreamsScreen.tsx @@ -57,6 +57,10 @@ const DOLBY_ICON = 'https://upload.wikimedia.org/wikipedia/en/thumb/3/3f/Dolby_V const { width, height } = Dimensions.get('window'); +// Cache for scraper logos to avoid repeated async calls +const scraperLogoCache = new Map(); +let scraperLogoCachePromise: Promise | null = null; + // Extracted Components const StreamCard = memo(({ stream, onPress, index, isLoading, statusMessage, theme }: { stream: Stream; @@ -101,6 +105,47 @@ const StreamCard = memo(({ stream, onPress, index, isLoading, statusMessage, the }; }, [stream.name, stream.title, stream.behaviorHints, stream.size]); + // Get scraper logo for local scrapers using cache + const [scraperLogo, setScraperLogo] = React.useState(() => { + const scraperId = stream.addonId || stream.addon; + return scraperId ? scraperLogoCache.get(scraperId) || null : null; + }); + + React.useEffect(() => { + const scraperId = stream.addonId || stream.addon; + if (!scraperId) return; + + // Check cache first + const cachedLogo = scraperLogoCache.get(scraperId); + if (cachedLogo) { + setScraperLogo(cachedLogo); + return; + } + + // If not in cache, fetch asynchronously + let isMounted = true; + + const getScraperLogo = async () => { + try { + const availableScrapers = await localScraperService.getAvailableScrapers(); + const scraper = availableScrapers.find(s => s.id === scraperId); + if (scraper && scraper.logo && isMounted) { + // Cache the logo for future use + scraperLogoCache.set(scraperId, scraper.logo); + setScraperLogo(scraper.logo); + } + } catch (error) { + // Silently fail if we can't get scraper info + } + }; + + getScraperLogo(); + + return () => { + isMounted = false; + }; + }, [stream.addonId, stream.addon]); + return ( + {/* Scraper Logo */} + {scraperLogo && ( + + + + )} + @@ -354,6 +410,28 @@ export const StreamsScreen = () => { // Add state for no sources error const [showNoSourcesError, setShowNoSourcesError] = useState(false); + + // Preload scraper logos to cache for faster display + React.useEffect(() => { + const preloadScraperLogos = async () => { + if (scraperLogoCachePromise) return; // Already loading + + scraperLogoCachePromise = (async () => { + try { + const availableScrapers = await localScraperService.getAvailableScrapers(); + availableScrapers.forEach(scraper => { + if (scraper.logo && scraper.id) { + scraperLogoCache.set(scraper.id, scraper.logo); + } + }); + } catch (error) { + // Silently fail + } + })(); + }; + + preloadScraperLogos(); + }, []); // Monitor streams loading and update available providers immediately useEffect(() => { @@ -1574,6 +1652,19 @@ const createStyles = (colors: any) => StyleSheet.create({ width: '100%', zIndex: 1, }, + scraperLogoContainer: { + width: 32, + height: 32, + marginRight: 12, + justifyContent: 'center', + alignItems: 'center', + backgroundColor: colors.elevation2, + borderRadius: 6, + }, + scraperLogo: { + width: 24, + height: 24, + }, streamCardLoading: { opacity: 0.7, },