UNPKG

@s20.ai/safecam-360-rn

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

229 lines (189 loc) 6.74 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.snapToItem(currentHotspotState.address); } }, [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.90} inactiveSlideOpacity={1} sliderWidth={viewportWidth} lockScrollWhileSnapping={true} ref={ref => { carouselRef.current = ref; }} 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);