JIT
update live camera screen to manage full screen state without re-rendering
import React, {useState, useEffect, useRef} from 'react'; import { TouchableOpacity, Text, View, FlatList, Alert, ActivityIndicator, Image, Dimensions, StyleSheet, BackHandler, } from 'react-native'; import {useUser} from '../../context/UserContext'; import ApiManager from '../../services/ApiManager'; import ZoneScreenStyle from '../../styles/ZoneScreenStyle'; import {COLORS} from '../../styles/colors'; import {VLCPlayer} from 'react-native-vlc-media-player'; import {FULLSCREEN, HOME, NORMALSCREEN, PAUSE, PLAY} from '../../assets'; import Orientation from 'react-native-orientation-locker'; import {border_radius} from '../../styles/fontstyle'; import {useNavigation} from '@react-navigation/native'; const LiveCameraScreen = ({onFullScreenChange}) => { const [items, setItems] = useState([]); const [isFetching, setIsFetching] = useState(false); const [errorIndex, setErrorIndex] = useState(null); const [playingIndex, setPlayingIndex] = useState(null); const [isFullScreen, setIsFullScreen] = useState(false); const [currentFullScreenUrl, setCurrentFullScreenUrl] = useState(''); const navigation = useNavigation(); const vlcPlayers = useRef({}); const {userDetails} = useUser(); useEffect(() => { if (userDetails?.profileDetails.customer_id) { getCCTVDetails(); } else { setItems([]); } }, [userDetails?.profileDetails.customer_id]); // Re-run only if customer_id changes useEffect(() => { const unsubscribe = navigation.addListener('beforeRemove', () => { if (isFullScreen) { setIsFullScreen(false); Orientation.lockToPortrait(); setCurrentFullScreenUrl(''); onFullScreenChange(false); } }); return unsubscribe; }, [navigation, isFullScreen, onFullScreenChange]); function handleBackButtonClick() { Orientation.getOrientation(orientation => { console.log('Current Orientation:', orientation); if (orientation == 'LANDSCAPE-LEFT' || 'LANDSCAPE-RIGHT') { setIsFullScreen(false); Orientation.lockToPortrait(); setCurrentFullScreenUrl(''); onFullScreenChange(false); return true; } }); } useEffect(() => { BackHandler.addEventListener('hardwareBackPress', handleBackButtonClick); return () => { BackHandler.removeEventListener( 'hardwareBackPress', handleBackButtonClick, ); }; }, []); const getCCTVDetails = async (isManualRefresh = false) => { if (!userDetails?.profileDetails.customer_id) { console.log('User details not available yet or missing customer_id.'); setIsFetching(false); return; } console.log(`Fetching list... Manual Refresh: ${isManualRefresh}`); setIsFetching(true); const customer_id = userDetails.profileDetails.customer_id; try { const listResponse = await ApiManager.getcctvDetails(customer_id); console.log('camera listResponse:', listResponse); if (listResponse) { setItems(listResponse.Data.camerasList); } else { console.error( 'API did not return expected data structure:', listResponse, ); setItems([]); if (isManualRefresh) { Alert.alert( 'Error', 'Failed to fetch items. Invalid data format received.', ); } } } catch (error) { console.error('Fetch List Error in CommunityScreen:', error); if (isManualRefresh) { Alert.alert( 'Fetch Failed', error.message || 'An unexpected error occurred while fetching items.', ); } setItems([]); } finally { setIsFetching(false); } }; const handleRefresh = () => { getCCTVDetails(true); }; const handlePlayPress = (index, url) => { if (playingIndex === index) { setPlayingIndex(null); } else { setPlayingIndex(index); } setErrorIndex(null); // Reset error when trying to play }; const handleHomePress = url => { if (!isFullScreen) { setCurrentFullScreenUrl(url); setIsFullScreen(true); Orientation.lockToLandscape(); onFullScreenChange(true); } else { setIsFullScreen(false); Orientation.lockToPortrait(); setCurrentFullScreenUrl(''); onFullScreenChange(false); } }; const handleVideoError = index => { if (playingIndex === index) { setPlayingIndex(null); } setErrorIndex(index); console.log(`Error loading video at index: ${index}`); // Optionally, you can show a specific error message to the user for this item. }; const renderItem = ({item, index}) => { const isPlaying = playingIndex === index; const hasError = errorIndex === index; return ( <TouchableOpacity activeOpacity={1} style={{ backgroundColor: COLORS.background_color, borderColor: COLORS.background_color, flex: 1, marginVertical: !isFullScreen ? 8 : '', marginHorizontal: !isFullScreen ? 10 : '', borderWidth: 1, borderRadius: 6, }}> {item.rtsp_stream_url != null && ( <View style={{ borderTopLeftRadius: 6, borderTopRightRadius: 6, backgroundColor: COLORS.black_color, borderColor: COLORS.background_color, }}> {!hasError ? ( <View> <VLCPlayer ref={ref => (vlcPlayers.current[index] = ref)} source={{ initType: 2, hwDecoderEnabled: 1, hwDecoderForced: 1, uri: 'rtsp://admin:advaint101@192.168.1.90:554/ISAPI/Streaming/Channels/401', }} autoplay={false} autoAspectRatio={true} resizeMode="cover" videoAspectRatio={'16:9'} isLive={true} autoReloadLive={true} style={{ width: '100%', height: isFullScreen ? Dimensions.get('window').height / 1.97 : 200, borderRadius: border_radius.small, }} paused={!isPlaying} onError={() => handleVideoError(index)} /> <View style={{ position: 'absolute', top: '50%', left: '50%', zIndex: 10, transform: [{translateX: -25}, {translateY: -25}], }}> {isPlaying == false && ( <TouchableOpacity onPress={() => handlePlayPress(index, item.rtsp_stream_url) }> <View style={styles.playButton}> <Image source={PLAY} style={styles.playIcon} /> </View> </TouchableOpacity> )} {isPlaying && ( <TouchableOpacity onPress={() => setPlayingIndex(null)}> <View style={styles.homeButton}> <Image source={PAUSE} style={styles.homeIcon} /> </View> </TouchableOpacity> )} </View> {!isFullScreen && ( <View style={styles.videoFooter}> <Text style={styles.cameraName}>{item.name}</Text> <TouchableOpacity onPress={() => { setCurrentFullScreenUrl(item.rtsp_stream_url); setIsFullScreen(true); Orientation.lockToLandscape(); onFullScreenChange(true); }}> <Image style={styles.homeIconSmall} source={NORMALSCREEN} /> </TouchableOpacity> </View> )} </View> ) : ( <Text style={styles.errorText}> CCTV video unavailable for {item.name} </Text> )} </View> )} {isFullScreen && currentFullScreenUrl === item.rtsp_stream_url && ( <TouchableOpacity style={styles.fullScreenCloseButton} onPress={() => { setIsFullScreen(false); Orientation.lockToPortrait(); setCurrentFullScreenUrl(''); onFullScreenChange(false); }}> <Image style={styles.homeIcon} source={FULLSCREEN} /> </TouchableOpacity> )} </TouchableOpacity> ); }; const renderContent = () => { if (isFetching && items.length === 0) { return ( <View style={ZoneScreenStyle.centeredMessageContainer}> <ActivityIndicator size="large" color="#007bff" /> <Text style={ZoneScreenStyle.messageText}>Loading items...</Text> </View> ); } if (!isFetching && items.length === 0) { return ( <View style={ZoneScreenStyle.centeredMessageContainer}> <Text style={ZoneScreenStyle.messageText}> No community items found. </Text> </View> ); } return ( <FlatList data={items} renderItem={renderItem} keyExtractor={item => item.id} contentContainerStyle={ZoneScreenStyle.listContentContainer} onRefresh={handleRefresh} refreshing={isFetching} /> ); }; const handleFullScreenonPress = () => { if (isFullScreen) { setIsFullScreen(false); Orientation.lockToPortrait(); setCurrentFullScreenUrl(''); onFullScreenChange(false); } }; return ( <View style={ZoneScreenStyle.safeArea}> {isFullScreen == false && renderContent()} {isFullScreen && currentFullScreenUrl !== '' && ( <View style={styles.fullScreenContainer}> <VLCPlayer source={{ initType: 2, hwDecoderEnabled: 1, hwDecoderForced: 1, uri: 'rtsp://admin:advaint101@192.168.1.90:554/ISAPI/Streaming/Channels/401', }} autoplay={true} autoAspectRatio={true} resizeMode="cover" videoAspectRatio={'16:9'} isLive={true} autoReloadLive={true} style={styles.fullScreenVideo} onError={() => { Alert.alert('Error', 'Failed to play video in full screen.'); setIsFullScreen(false); Orientation.lockToPortrait(); setCurrentFullScreenUrl(''); }} /> <TouchableOpacity style={styles.fullScreenCloseButton} onPress={handleFullScreenonPress}> <Image style={styles.homeIcon} source={FULLSCREEN} /> </TouchableOpacity> </View> )} </View> ); }; const styles = StyleSheet.create({ playButton: { width: 50, height: 50, backgroundColor: COLORS.white_color, borderRadius: 25, justifyContent: 'center', alignItems: 'center', }, playIcon: { height: 20, width: 20, }, homeButton: { width: 50, height: 50, backgroundColor: COLORS.white_color, borderRadius: 25, justifyContent: 'center', alignItems: 'center', }, homeIcon: { height: 20, width: 20, tintColor: COLORS.black_color, }, homeIconSmall: { height: 20, width: 20, tintColor: COLORS.white_color, }, videoFooter: { flexDirection: 'row', justifyContent: 'space-between', padding: 10, position: 'absolute', bottom: 2, width: '100%', }, cameraName: { color: COLORS.white_color, }, errorText: { paddingVertical: '25%', textAlign: 'center', color: COLORS.error_color, }, fullScreenContainer: { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, backgroundColor: COLORS.black_color, zIndex: 100, justifyContent: 'center', alignItems: 'center', }, fullScreenVideo: { width: '100%', height: '100%', }, fullScreenCloseButton: { position: 'absolute', top: 20, right: 20, zIndex: 101, backgroundColor: 'rgba(0,0,0,0.5)', borderRadius: 15, width: 30, height: 30, justifyContent: 'center', alignItems: 'center', }, }); export default LiveCameraScreen; whn I press NORMALSCREEN and set setIsFullScreen(true); renderContent() should not render again & only {isFullScreen && currentFullScreenUrl !== '' && ( <View style={styles.fullScreenContainer}> <VLCPlayer source={{ initType: 2, hwDecoderEnabled: 1, hwDecoderForced: 1, uri: 'rtsp://admin:advaint101@192.168.1.90:554/ISAPI/Streaming/Channels/401', }} autoplay={true} autoAspectRatio={true} resizeMode="cover" videoAspectRatio={'16:9'} isLive={true} autoReloadLive={true} style={styles.fullScreenVideo} onError={() => { Alert.alert('Error', 'Failed to play video in full screen.'); setIsFullScreen(false); Orientation.lockToPortrait(); setCurrentFullScreenUrl(''); }} /> <TouchableOpacity style={styles.fullScreenCloseButton} onPress={handleFullScreenonPress}> <Image style={styles.homeIcon} source={FULLSCREEN} /> </TouchableOpacity> </View> )} should be visible with only single currentFullScreenUrl make chnages and provide updated src folder
Updated files
v1
import React, {useState, useEffect, useRef} from 'react'; import { TouchableOpacity, Text, View, FlatList, Alert, ActivityIndicator, Image, Dimensions, StyleSheet, BackHandler, } from 'react-native'; import {useUser} from '../../context/UserContext'; import ApiManager from '../../services/ApiManager'; import ZoneScreenStyle from '../../styles/ZoneScreenStyle'; import {COLORS} from '../../styles/colors'; import {VLCPlayer} from 'react-native-vlc-media-player'; import {FULLSCREEN, HOME, NORMALSCREEN, PAUSE, PLAY} from '../../assets'; import Orientation from 'react-native-orientation-locker'; import {border_radius} from '../../styles/fontstyle'; import {useNavigation} from '@react-navigation/native'; const LiveCameraScreen = ({onFullScreenChange}) => { const [items, setItems] = useState([]); const [isFetching, setIsFetching] = useState(false); const [errorIndex, setErrorIndex] = useState(null); const [playingIndex, setPlayingIndex] = useState(null); const [isFullScreen, setIsFullScreen] = useState(false); const [currentFullScreenUrl, setCurrentFullScreenUrl] = useState(''); const navigation = useNavigation(); const vlcPlayers = useRef({}); const {userDetails} = useUser(); useEffect(() => { if (userDetails?.profileDetails.customer_id) { getCCTVDetails(); } else { setItems([]); } }, [userDetails?.profileDetails.customer_id]); // Re-run only if customer_id changes useEffect(() => { const unsubscribe = navigation.addListener('beforeRemove', () => { if (isFullScreen) { setIsFullScreen(false); Orientation.lockToPortrait(); setCurrentFullScreenUrl(''); onFullScreenChange(false); } }); return unsubscribe; }, [navigation, isFullScreen, onFullScreenChange]); function handleBackButtonClick() { Orientation.getOrientation(orientation => { console.log('Current Orientation:', orientation); if (orientation == 'LANDSCAPE-LEFT' || 'LANDSCAPE-RIGHT') { setIsFullScreen(false); Orientation.lockToPortrait(); setCurrentFullScreenUrl(''); onFullScreenChange(false); return true; } }); } useEffect(() => { BackHandler.addEventListener('hardwareBackPress', handleBackButtonClick); return () => { BackHandler.removeEventListener( 'hardwareBackPress', handleBackButtonClick, ); }; }, []); const getCCTVDetails = async (isManualRefresh = false) => { if (!userDetails?.profileDetails.customer_id) { console.log('User details not available yet or missing customer_id.'); setIsFetching(false); return; } console.log(`Fetching list... Manual Refresh: ${isManualRefresh}`); setIsFetching(true); const customer_id = userDetails.profileDetails.customer_id; try { const listResponse = await ApiManager.getcctvDetails(customer_id); console.log('camera listResponse:', listResponse); if (listResponse) { setItems(listResponse.Data.camerasList); } else { console.error( 'API did not return expected data structure:', listResponse, ); setItems([]); if (isManualRefresh) { Alert.alert( 'Error', 'Failed to fetch items. Invalid data format received.', ); } } } catch (error) { console.error('Fetch List Error in CommunityScreen:', error); if (isManualRefresh) { Alert.alert( 'Fetch Failed', error.message || 'An unexpected error occurred while fetching items.', ); } setItems([]); } finally { setIsFetching(false); } }; const handleRefresh = () => { getCCTVDetails(true); }; const handlePlayPress = (index, url) => { if (playingIndex === index) { setPlayingIndex(null); } else { setPlayingIndex(index); } setErrorIndex(null); // Reset error when trying to play }; const handleHomePress = url => { if (!isFullScreen) { setCurrentFullScreenUrl(url); setIsFullScreen(true); Orientation.lockToLandscape(); onFullScreenChange(true); } else { setIsFullScreen(false); Orientation.lockToPortrait(); setCurrentFullScreenUrl(''); onFullScreenChange(false); } }; const handleVideoError = index => { if (playingIndex === index) { setPlayingIndex(null); } setErrorIndex(index); console.log(`Error loading video at index: ${index}`); // Optionally, you can show a specific error message to the user for this item. }; const renderItem = ({item, index}) => { const isPlaying = playingIndex === index; const hasError = errorIndex === index; return ( <TouchableOpacity activeOpacity={1} style={{ backgroundColor: COLORS.background_color, borderColor: COLORS.background_color, flex: 1, marginVertical: !isFullScreen ? 8 : '', marginHorizontal: !isFullScreen ? 10 : '', borderWidth: 1, borderRadius: 6, }}> {item.rtsp_stream_url != null && ( <View style={{ borderTopLeftRadius: 6, borderTopRightRadius: 6, backgroundColor: COLORS.black_color, borderColor: COLORS.background_color, }}> {!hasError ? ( <View> <VLCPlayer ref={ref => (vlcPlayers.current[index] = ref)} source={{ initType: 2, hwDecoderEnabled: 1, hwDecoderForced: 1, uri: 'rtsp://admin:advaint101@192.168.1.90:554/ISAPI/Streaming/Channels/401', }} autoplay={false} autoAspectRatio={true} resizeMode="cover" videoAspectRatio={'16:9'} isLive={true} autoReloadLive={true} style={{ width: '100%', height: isFullScreen ? Dimensions.get('window').height / 1.97 : 200, borderRadius: border_radius.small, }} paused={!isPlaying} onError={() => handleVideoError(index)} /> <View style={{ position: 'absolute', top: '50%', left: '50%', zIndex: 10, transform: [{translateX: -25}, {translateY: -25}], }}> {isPlaying == false && ( <TouchableOpacity onPress={() => handlePlayPress(index, item.rtsp_stream_url) }> <View style={styles.playButton}> <Image source={PLAY} style={styles.playIcon} /> </View> </TouchableOpacity> )} {isPlaying && ( <TouchableOpacity onPress={() => setPlayingIndex(null)}> <View style={styles.homeButton}> <Image source={PAUSE} style={styles.homeIcon} /> </View> </TouchableOpacity> )} </View> {!isFullScreen && ( <View style={styles.videoFooter}> <Text style={styles.cameraName}>{item.name}</Text> <TouchableOpacity onPress={() => { setCurrentFullScreenUrl(item.rtsp_stream_url); setIsFullScreen(true); Orientation.lockToLandscape(); onFullScreenChange(true); console.log(isFullScreen); }}> <Image style={styles.homeIconSmall} source={NORMALSCREEN} /> </TouchableOpacity> </View> )} </View> ) : ( <Text style={styles.errorText}> CCTV video unavailable for {item.name} </Text> )} </View> )} {isFullScreen && currentFullScreenUrl === item.rtsp_stream_url && ( <TouchableOpacity style={styles.fullScreenCloseButton} onPress={() => { setIsFullScreen(false); Orientation.lockToPortrait(); setCurrentFullScreenUrl(''); onFullScreenChange(false); }}> <Image style={styles.homeIcon} source={FULLSCREEN} /> </TouchableOpacity> )} </TouchableOpacity> ); }; // const renderContent = () => { // if (isFetching && items.length === 0) { // return ( // <View style={ZoneScreenStyle.centeredMessageContainer}> // <ActivityIndicator size="large" color="#007bff" /> // <Text style={ZoneScreenStyle.messageText}>Loading items...</Text> // </View> // ); // } // if (!isFetching && items.length === 0) { // return ( // <View style={ZoneScreenStyle.centeredMessageContainer}> // <Text style={ZoneScreenStyle.messageText}> // No community items found. // </Text> // </View> // ); // } // return ( // ); // }; const handleFullScreenonPress = () => { if (isFullScreen) { setIsFullScreen(false); Orientation.lockToPortrait(); setCurrentFullScreenUrl(''); onFullScreenChange(false); } }; return ( <View style={ZoneScreenStyle.safeArea}> {isFullScreen == false ? ( <FlatList data={items} renderItem={renderItem} keyExtractor={item => item.id} contentContainerStyle={ZoneScreenStyle.listContentContainer} onRefresh={handleRefresh} refreshing={isFetching} /> ) : ( <View style={styles.fullScreenContainer}> <VLCPlayer source={{ initType: 2, hwDecoderEnabled: 1, hwDecoderForced: 1, uri: currentFullScreenUrl, }} autoplay={true} autoAspectRatio={true} resizeMode="cover" videoAspectRatio={'16:9'} isLive={true} autoReloadLive={true} style={styles.fullScreenVideo} onError={() => { Alert.alert('Error', 'Failed to play video in full screen.'); setIsFullScreen(false); Orientation.lockToPortrait(); setCurrentFullScreenUrl(''); }} /> <TouchableOpacity style={styles.fullScreenCloseButton} onPress={handleFullScreenonPress}> <Image style={styles.homeIcon} source={FULLSCREEN} /> </TouchableOpacity> </View> )} </View> ); }; const styles = StyleSheet.create({ playButton: { width: 50, height: 50, backgroundColor: COLORS.white_color, borderRadius: 25, justifyContent: 'center', alignItems: 'center', }, playIcon: { height: 20, width: 20, }, homeButton: { width: 50, height: 50, backgroundColor: COLORS.white_color, borderRadius: 25, justifyContent: 'center', alignItems: 'center', }, homeIcon: { height: 20, width: 20, tintColor: COLORS.black_color, }, homeIconSmall: { height: 20, width: 20, tintColor: COLORS.white_color, }, videoFooter: { flexDirection: 'row', justifyContent: 'space-between', padding: 10, position: 'absolute', bottom: 2, width: '100%', }, cameraName: { color: COLORS.white_color, }, errorText: { paddingVertical: '25%', textAlign: 'center', color: COLORS.error_color, }, fullScreenContainer: { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, backgroundColor: COLORS.black_color, zIndex: 100, justifyContent: 'center', alignItems: 'center', }, fullScreenVideo: { width: '100%', height: '100%', }, fullScreenCloseButton: { position: 'absolute', top: 20, right: 20, zIndex: 101, backgroundColor: 'rgba(0,0,0,0.5)', borderRadius: 15, width: 30, height: 30, justifyContent: 'center', alignItems: 'center', }, }); export default LiveCameraScreen; here in <TouchableOpacity onPress={() => { setCurrentFullScreenUrl(item.rtsp_stream_url); setIsFullScreen(true); Orientation.lockToLandscape(); onFullScreenChange(true); console.log(isFullScreen); }}> <Image style={styles.homeIconSmall} source={NORMALSCREEN} /> </TouchableOpacity> setIsFullScreen(true); not working gives false make changes and provide updates src folder
Updated files
v2