/* eslint-disable @typescript-eslint/no-explicit-any */ import React, { useEffect, useState } from 'react'; import { LayoutChangeEvent, View } from 'react-native'; import BottomSheet from '../../helpers/BottomSheet/BottomSheet'; import { RequestRenderingEvent, UnitComponentsMessage } from '../../messages/webMessages/unitComponentsMessages'; import { getStylesObject } from './UNBottomSheetComponent.styles'; import { BottomSheetNativePlaceType, BottomSheetRenderingType, BottomSheetSlotData, NativeComponentData, ScrollState, } from '../../types/internal/bottomSheet.types'; import { BottomSheetNativeMessage, BottomSheetRenderingMessage, } from '../../messages/nativeMessages/bottomSheetMessage'; import { withReduxStore } from '../../helpers/store/helpers'; import { useEventListener } from '../../hooks/useEventListener'; import { useDispatch, useSelector } from 'react-redux'; import { BOTTOM_SHEET_OVER_FULLSCREEN_PERCENTAGE_SIZE } from './UNBottomSheetComponent.constants'; import { RootState } from '../../store'; import { resetBottomSheetSlice, setIsBottomSheetActive, setIsComponentLoading, setNativePlace, setScrollState, setShouldEnableBottomSheetScroll, setShouldShowBottomSheet, } from '../../slices/BottomSheetSlice'; import UNBottomSheetSlotComponent from './components/UNBottomSheetSlotComponent/UNBottomSheetSlotComponent'; import UNBottomSheetNativeComponent from './components/UNBottomSheetNativeComponent/UNBottomSheetNativeComponent'; import { WebComponentType } from '../../types/internal/webComponent.types'; import { getNativeComponentDataFromEvent } from './UNBottomSheetComponent.utils'; const UNBottomSheetComponent = () => { const dispatch = useDispatch(); const isBottomSheetActive = useSelector((state: RootState) => state.bottomSheet.isBottomSheetActive); const isComponentLoading = useSelector((state: RootState) => state.bottomSheet.isComponentLoading); const shouldShowBottomSheet = useSelector((state: RootState) => state.bottomSheet.shouldShowBottomSheet); const nativePlace = useSelector((state: RootState) => state.bottomSheet.nativePlace); const componentHeight = useSelector((state: RootState) => state.bottomSheet.componentHeight); const scrollState = useSelector((state: RootState) => state.bottomSheet.scrollState); const shouldEnableBottomSheetScroll = useSelector((state: RootState) => state.bottomSheet.shouldEnableBottomSheetScroll); const [renderingType, setRenderingType] = useState(); const [height, setHeight] = useState(0); const [sliderMaxHeight, setSliderMaxHeight] = useState(0); const [containerHeight, setContainerHeight] = useState(0); const [nativeComponentData, setNativeComponentData] = useState(); const [requestRenderingEventData, setRequestRenderingEventData] = useState(); const [componentParams, setComponentParams] = useState<{ componentName?: WebComponentType, componentResourceId?: string }>(); const [currentBottomSheetRenderingMessage, setCurrentBottomSheetRenderingMessage] = useState(); const styles = getStylesObject(); const handleContainerLayout = (e: LayoutChangeEvent) => { setContainerHeight(e.nativeEvent.layout.height); }; useEffect(() => { if (containerHeight === 0) return; const computedOverFullScreenHeight = containerHeight * BOTTOM_SHEET_OVER_FULLSCREEN_PERCENTAGE_SIZE; // Always set sliderMaxHeight when containerHeight is available, // so the BottomSheet can mount and render children (which may need to load before reporting height). if (nativePlace === BottomSheetNativePlaceType.modal) { setSliderMaxHeight(containerHeight); } else { setSliderMaxHeight(computedOverFullScreenHeight); } if (!shouldShowBottomSheet) { setHeight(0); } else { // For overFullScreen/modal, set the known height immediately so the WebView // has space to load (older Android WebViews won't fire events with 0 height). // For menu/contentHeight, wait until content reports its height. switch (nativePlace) { case BottomSheetNativePlaceType.overFullScreen: setHeight(computedOverFullScreenHeight); break; case BottomSheetNativePlaceType.modal: setHeight(containerHeight); break; default: if (!isComponentLoading && componentHeight) { setHeight(componentHeight < computedOverFullScreenHeight ? componentHeight : computedOverFullScreenHeight); } else { setHeight(0); } break; } } }, [componentHeight, nativePlace, isComponentLoading, shouldShowBottomSheet, containerHeight]); useEffect(() => { const resetBottomSheetManagerState = () => { setComponentParams(undefined); setRequestRenderingEventData(undefined); setNativeComponentData(undefined); setRenderingType(undefined); setHeight(0); setSliderMaxHeight(0); }; if (!isBottomSheetActive) { dispatch(resetBottomSheetSlice()); resetBottomSheetManagerState(); } }, [isBottomSheetActive, dispatch]); const requestRendering = (rendering: BottomSheetRenderingMessage) => { if (isBottomSheetActive) { dispatch(setShouldShowBottomSheet(false)); } handleRequestRendering(rendering); if (isBottomSheetActive) { setTimeout(() => dispatch(setShouldShowBottomSheet(true)), 400); } else { dispatch(setIsBottomSheetActive(true)); dispatch(setShouldShowBottomSheet(true)); } }; const handleCloseFlow = () => { dispatch(setIsBottomSheetActive(false)); }; useEventListener({ busEventKey: BottomSheetNativeMessage.REQUEST_RENDERING, action: requestRendering }); useEventListener({ busEventKey: UnitComponentsMessage.UNIT_REQUEST_CLOSE_FLOW, action: handleCloseFlow }); const handleRequestRendering = (rendering: BottomSheetRenderingMessage) => { if (rendering === currentBottomSheetRenderingMessage) { return; } else { setCurrentBottomSheetRenderingMessage(rendering); } dispatch(setIsComponentLoading(true)); switch (rendering.type) { //if slot check if we know this component and want to show it, otherwise get directly the component case BottomSheetRenderingType.Slot: determineRenderingTypeFromEvent(rendering.data as BottomSheetSlotData); break; case BottomSheetRenderingType.NativeComponent: setupDataForNativeComponent(rendering.data as NativeComponentData); break; default: break; } }; const determineRenderingTypeFromEvent = (event: BottomSheetSlotData) => { const nativeComponentDataFromEvent = getNativeComponentDataFromEvent(event); if (nativeComponentDataFromEvent) { setupDataForNativeComponent(nativeComponentDataFromEvent as NativeComponentData); return; } setupDataForSlotComponent(event); }; const setupDataForNativeComponent = (eventData: NativeComponentData) => { const nativePlace = eventData.nativePlace ? eventData.nativePlace : BottomSheetNativePlaceType.modal; setRenderingType(BottomSheetRenderingType.NativeComponent); dispatch(setNativePlace(nativePlace)); setNativeComponentData(eventData as NativeComponentData); dispatch(setIsComponentLoading(false)); //todo: work with events? }; const setupDataForSlotComponent = (eventData: BottomSheetSlotData) => { setRenderingType(BottomSheetRenderingType.Slot); const componentParamsFromEvent = { componentName: eventData.componentName ? eventData.componentName : componentParams?.componentName, componentResourceId: eventData.componentResourceId ? eventData.componentResourceId : componentParams?.componentResourceId, }; setComponentParams(componentParamsFromEvent); dispatch(setNativePlace(eventData.requestRenderingEvent.data.nativePlace)); setRequestRenderingEventData(eventData.requestRenderingEvent); }; const handleWebViewScroll = () => { if (scrollState === ScrollState.unScrollable) return; dispatch(setScrollState(ScrollState.onlyWebView)); dispatch(setShouldEnableBottomSheetScroll(false)); }; const renderBottomSheetComponent = () => { switch (renderingType) { case BottomSheetRenderingType.Slot: return requestRenderingEventData ? ( ) : null; case BottomSheetRenderingType.NativeComponent: return nativeComponentData ? ( ) : null; default: return null; } }; if (!isBottomSheetActive) return null; return ( {containerHeight > 0 && sliderMaxHeight > 0 && ( dispatch(setIsBottomSheetActive(true))} onClose={() => dispatch(setIsBottomSheetActive(false))} height={height} animationDuration={200} expandToMaxHeightEnabled={false} handleWebViewScroll={() => handleWebViewScroll()} shouldEnableBottomSheetScroll={shouldEnableBottomSheetScroll} nativePlace={nativePlace} isComponentLoading={isComponentLoading} sliderMaxHeight={sliderMaxHeight} > {renderBottomSheetComponent()} )} ); }; export default withReduxStore(UNBottomSheetComponent);