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

292 lines (247 loc) 9.17 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, useState, useRef} from 'react'; import { View, Text, Dimensions, LogBox, StyleSheet, TouchableWithoutFeedback, AppState } from 'react-native'; import Interior360 from './interior/Interior360'; import Exterior360 from './exterior/Exterior360'; import BottomToolbar from './widgets/BottomToolbar'; import {connect} from 'react-redux'; import { setCurrentOrientation, setWidgetsVisible, } from '../redux/actions/currentViewAction'; import {useDispatch} from 'react-redux'; import constants from '../config/constants'; import HotspotOverlay from './hotspot/HotspotOverlay'; import OverlayButtons from './widgets/OverlayButtons'; import {preProcess} from '../utils/propsPreprocessing'; import {Slide} from '@s20.ai/safecam-components-rn'; import MissingDataHandlerOverlay from './common/overlay/MissingDataHandlerOverlay'; import AnimationOverlay from './common/overlay/AnimationOverlay'; import { setCurrentMessageOff, setCurrentMessageOn, } from '../redux/actions/messageHintAction'; import Orientation from 'react-native-orientation-locker'; import appStyleConfig from "../components/appStyleConfig"; import { getOrientation } from '../utils/device'; import WidgetOverlay from './widgets/WidgetOverlay'; import { SafeAreaProvider } from 'react-native-safe-area-context'; LogBox.ignoreLogs(['new NativeEventEmitter']); const window = Dimensions.get('window'); const width = window.width; const height = window.height; const MainWrapper = ({ currentViewState, interiorData, exteriorData, currentHotspotState, widgetsVisible, orientationState, enableHotspots, enableGalleryButton, enableHotspotTooltip, galleryButtonPressed, enableBackButton, backButtonPressed, enableCarAutorotateOnCardSwipe, enableCardSwipeOnCarRotation, bottomToolbarButtonPressed, bottomToolbarSwitchToggled, hotspotPressed, orientationChanged, onInteraction, customInteriorUrl, messageHint, customFontFamily, exterior360ResizeMode, interior360InitialPosition, customGalleryButton, customBackButton }) => { const [ exteriorDataProcessed, interiorDataProcessed, combinedHotspotMetadata, ] = preProcess(exteriorData, interiorData); const dispatch = useDispatch(); const appState = useRef(AppState.currentState); const handleOrientationChangeAll = async (orientation) => { if(orientation && orientation !== constants.UNKNOWN && orientation !== constants.PORTRAIT_UPSIDE_DOWN && orientation !== constants.FACE_UP){ dispatch(setCurrentOrientation(orientation)) } if(orientationChanged){ //when actual orientation is FACE_UP, android returns UNKNOWN if(orientation === constants.UNKNOWN){ orientationChanged(constants.FACE_UP) } else{ orientationChanged(orientation) } } }; const handleAppStateChange = (nextAppState) => { if(appState.current === "background" && nextAppState === "active"){ //required incase someone puts the viewer in the background on landscape mode and then comes back to the viewer //OR if the component is not unmounted but the app is in the background //failure to do this would result in the orientation being portrait setTimeout(()=>{ Orientation.unlockAllOrientations() }, 200) } if ( appState.current.match(/inactive|background/) && nextAppState === "active" ) { // console.log("App has come to the foreground!"); } appState.current = nextAppState; } useEffect(() => { //Required to get initial orientation - listener won't be fired if the view loads in landscape by default Orientation.getDeviceOrientation((orientation)=>{ if(orientation && (orientation.includes(constants.PORTRAIT) || orientation.includes(constants.LANDSCAPE))){ dispatch(setCurrentOrientation(orientation)) if(orientationChanged){ orientationChanged(orientation) } } else{ //TODO - extra work may be required //Fallback incase no orientation or Orientation = UNKNOWN is returned if(Orientation.getInitialOrientation()){ dispatch(setCurrentOrientation(Orientation.getInitialOrientation())) if(orientationChanged){ orientationChanged(Orientation.getInitialOrientation()) } } else{ dispatch(setCurrentOrientation(getOrientation())) if(orientationChanged){ orientationChanged(getOrientation()) } } } }) Orientation.unlockAllOrientations(); Orientation.addDeviceOrientationListener( handleOrientationChangeAll ); const appStateSubscription = AppState.addEventListener("change", handleAppStateChange); return () => { Orientation.removeAllListeners() appStateSubscription.remove() } }, []); useEffect(() => { if (currentHotspotState) { dispatch(setWidgetsVisible(false)); } else { dispatch(setWidgetsVisible(true)); } }, [currentHotspotState]); useEffect(() => { if (currentViewState.type === constants.INTERIOR_VIEW) { if(!messageHint[constants.MESSAGES.EXTERIOR_ROTATE].never){ dispatch(setCurrentMessageOff(constants.MESSAGES.EXTERIOR_ROTATE, false)); } } if (currentViewState.type === constants.EXTERIOR_VIEW) { if(!messageHint[constants.MESSAGES.INTERIOR_ROTATE].never){ dispatch(setCurrentMessageOff(constants.MESSAGES.INTERIOR_ROTATE, false)); } } }, [currentViewState]); return ( <SafeAreaProvider> <View style={appStyleConfig.mainWrapperContainer}> { Object.keys(exteriorDataProcessed).length > 0 && ( <Exterior360 customFontFamily={customFontFamily} width={width} height={height / 2} srcset={exteriorDataProcessed.imageSource} srcsetCompressed={exteriorDataProcessed.lowResImages} highResImages={exteriorDataProcessed.highResImages} filteredHotspotMetadata={ exteriorDataProcessed.filteredHotspotMetadata } hotspots={exteriorDataProcessed.hotspots} enableHotspots={enableHotspots} enableHotspotTooltip={enableHotspotTooltip} enableCarAutorotateOnCardSwipe={enableCarAutorotateOnCardSwipe} enableCardSwipeOnCarRotation={enableCardSwipeOnCarRotation} hotspotPressed={hotspotPressed} onInteraction={onInteraction} exterior360ResizeMode={exterior360ResizeMode} /> )} { Object.keys(interiorDataProcessed).length > 0 && ( <Interior360 interiorData={interiorDataProcessed} enableHotspots={enableHotspots} hotspotPressed={hotspotPressed} onInteraction={onInteraction} customInteriorUrl={customInteriorUrl} customFontFamily={customFontFamily} interior360InitialPosition={interior360InitialPosition} /> )} {currentHotspotState && Object.keys(exteriorDataProcessed).length > 0 && Object.keys(interiorDataProcessed).length > 0 && ( <HotspotOverlay exteriorData={exteriorDataProcessed} interiorData={interiorDataProcessed} customFontFamily={customFontFamily} /> )} <WidgetOverlay galleryButtonPressed={galleryButtonPressed} enableGalleryButton={enableGalleryButton} enableBackButton={enableBackButton} backButtonPressed= {backButtonPressed} combinedHotspotMetadata={combinedHotspotMetadata} bottomToolbarButtonPressed={bottomToolbarButtonPressed} bottomToolbarSwitchToggled={bottomToolbarSwitchToggled} exteriorDataProcessed={exteriorDataProcessed} interiorDataProcessed={interiorDataProcessed} enableHotspots={enableHotspots} customFontFamily={customFontFamily} customGalleryButton={ customGalleryButton} customBackButton={customBackButton} /> <MissingDataHandlerOverlay interiorDataProcessed={interiorDataProcessed} exteriorDataProcessed={exteriorDataProcessed} currentViewState={currentViewState} customFontFamily={customFontFamily} /> <AnimationOverlay/> </View> </SafeAreaProvider> ); }; const mapStateToProps = ({currentView, currentHotspot, messageHint}) => { return { currentViewState: currentView.present, orientationState: currentView.orientation, widgetsVisible: currentView.widgetsVisible, currentHotspotState: currentHotspot, messageHint: messageHint, }; }; export default connect(mapStateToProps)(MainWrapper);