UNPKG

@s20.ai/safecam-360-rn-lite

Version:

A React Native component to view the interior panorama and exterior 360 degree views of a product featuring suppport for hotspots. Compatible with Android and iOS

217 lines (203 loc) 6.71 kB
/* * Copyright © 2022 S20.AI India Pvt. Ltd. * Contact - origin@s20.ai * License - Licensed as per https://docs.s20.ai/license.html */ import React, {useEffect, useRef, useState} from 'react'; import {View, Dimensions, StyleSheet, LogBox, Platform} from 'react-native'; import Carousel from 'react-native-snap-carousel'; import {Card, IconButton} from '@s20.ai/safecam-components-rn'; import {connect, useDispatch} from 'react-redux'; import constants from '../../config/constants'; import HotspotDetailCard from './HotspotDetailCard'; import {carouselConfig} from '../../config/carouselConfig'; import {setCurrentHotspot} from '../../redux/actions/currentHotspotAction'; import {triggerHapticFeedback, useScreenDimensions} from '../../utils/device'; import {responsiveStyleConfig} from '../responsiveStyleConfig'; import {responsiveAppStyleConfig} from '../responsiveAppStyleConfig'; const HotspotCarousel = ({ interiorData, exteriorData, currentViewState, currentHotspotState, orientation, customFontFamily, }) => { const carouselRef = useRef(null); const dispatch = useDispatch(); const [previousItemIndex, setPreviousItemIndex] = useState(null); const {width: viewportWidth, height: viewportHeight} = useScreenDimensions(); const getCarouselLeftOffset = () => { if (currentViewState.type === constants.EXTERIOR_VIEW) { if (exteriorData.filteredHotspotMetadata.length === 1) { return 0; } else { if (orientation.includes(constants.PORTRAIT)) { if (currentHotspotState.address === 0) return 20; else return 0; } else { return 0; } } } else { if (interiorData.filteredHotspotMetadata.length === 1) { return 0; } else { if (orientation.includes(constants.PORTRAIT)) { if (currentHotspotState.address === 0) return 20; else return 0; } else { return 0; } } } }; const getActiveSlideAlignment = () => { if (currentViewState.type === constants.EXTERIOR_VIEW) { if (exteriorData.filteredHotspotMetadata.length === 1) { return 'center'; } else { if ( orientation.includes(constants.PORTRAIT) && currentHotspotState.address === 0 ) { return 'start'; } else { return 'center'; } } } else { if (interiorData.filteredHotspotMetadata.length === 1) { return 'center'; } else { if ( orientation.includes(constants.PORTRAIT) && currentHotspotState.address === 0 ) { return 'start'; } else { return 'center'; } } } }; const styles = StyleSheet.create({ containerCustomStyle: { marginTop: 0, overflow: 'visible', // for custom animations width: '100%', height: '100%', zIndex: 0, left: getCarouselLeftOffset(), }, contentContainerCustomStyle: { paddingVertical: orientation.includes(constants.LANDSCAPE) ? 0 : 10, // for custom animation paddingTop: 0, paddingBottom: 10, zIndex: 0, }, }); const _renderItem = ({item, index}) => { return ( <HotspotDetailCard item={item} exteriorData={exteriorData} interiorData={interiorData} customFontFamily={customFontFamily} /> ); }; useEffect(() => { //Change carousel slide based on current hotspot if (carouselRef.current && currentHotspotState) { carouselRef.current.triggerRenderingHack(); // carouselRef.current.snapToNext(); setTimeout( () => carouselRef.current.snapToItem( currentHotspotState.address, true, true, ), 250, ); } }, [currentHotspotState]); const screenDimensions = useScreenDimensions(); let cardStyles = responsiveStyleConfig.stylePicker( 'card', screenDimensions.width, ); return ( <View style={{ ...responsiveAppStyleConfig.stylePicker( 'carouselContainer', viewportWidth, ), }}> <Carousel {...{ ...carouselConfig, sliderHeight: orientation.includes(constants.LANDSCAPE) ? viewportHeight : NaN, vertical: orientation.includes(constants.LANDSCAPE), // inactiveSlideOpacity: orientation.includes(constants.LANDSCAPE) ? 0.7 : 0.7 }} //original dimensions // itemWidth = {300} // itemHeight = {210} itemWidth={cardStyles.width} itemHeight={cardStyles.height} swipeThreshold={10} containerCustomStyle={styles.containerCustomStyle} contentContainerCustomStyle={styles.contentContainerCustomStyle} activeSlideAlignment={getActiveSlideAlignment()} inactiveSlideScale={0.9} inactiveSlideOpacity={1} sliderWidth={viewportWidth} lockScrollWhileSnapping={true} ref={carouselRef} data={ currentViewState.type === constants.EXTERIOR_VIEW ? exteriorData.filteredHotspotMetadata : interiorData.filteredHotspotMetadata } renderItem={_renderItem} onSnapToItem={index => { //If previous item index is null and index is 0, it would mean that a hotspot other than the first //one has been pressed from the main screen //if no previous item index is maintained, the below code would execute for index = 0 and the actual index that needs to be set //the effect of this would be that the hotspot will always load on the first hotspot if (previousItemIndex !== null || index !== 0) { if (currentViewState.type === constants.EXTERIOR_VIEW) { dispatch( setCurrentHotspot({ ...exteriorData.filteredHotspotMetadata[index], address: exteriorData.filteredHotspotMetadata[index].address, }), ); } else { dispatch( setCurrentHotspot({ ...interiorData.filteredHotspotMetadata[index], address: interiorData.filteredHotspotMetadata[index].address, }), ); } setPreviousItemIndex(index - 1); } }} /> </View> ); }; const mapStateToProps = ({currentView, currentHotspot}) => { return { currentViewState: currentView.present, orientation: currentView.orientation, currentHotspotState: currentHotspot, }; }; export default connect(mapStateToProps)(HotspotCarousel);