@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
JavaScript
/*
* 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);