import React, { useEffect, useRef, useState, useCallback } from 'react'; import { View, Keyboard, TextInput, TouchableOpacity, StyleSheet, Dimensions, I18nManager, SafeAreaView, Platform, Button } from 'react-native'; import { ProductForm as ProductOptions, useSession, useLanguage, useOrder, useUtils, ToastType, useToast, useConfig, useEvent } from 'ordering-components/native'; import uuid from 'react-native-uuid'; import { useTheme } from 'styled-components/native'; import { ProductIngredient } from '../ProductIngredient'; import { ProductOption } from '../ProductOption'; import Swiper from 'react-native-swiper' import FastImage from 'react-native-fast-image'; import IconAntDesign from 'react-native-vector-icons/AntDesign'; import YoutubePlayer from "react-native-youtube-iframe" import ReactNativeHapticFeedback from "react-native-haptic-feedback"; import { WrapHeader, TopHeader, ProductTitle, ProductDescription, ProductEditions, SectionTitle, WrapperIngredients, WrapperSubOption, ProductComment, ProductActions, ExtraOptionWrap, WeightUnitSwitch, WeightUnitItem, TopActions, ProductSummary } from './styles'; import { OButton, OIcon, OInput, OText } from '../shared'; import { ScrollView } from 'react-native-gesture-handler'; import { ProductOptionSubOption } from '../ProductOptionSubOption'; import { NotFoundSource } from '../NotFoundSource'; import { Placeholder, PlaceholderLine, Fade } from 'rn-placeholder'; import NavBar from '../NavBar'; import { orderTypeList } from '../../utils'; import { ActionButton } from './ActionButton' import { ExtraOptions } from './ExtraOptions' import { IOScrollView } from 'react-native-intersection-observer'; const windowWidth = Dimensions.get('window').width; export const ProductOptionsUI = (props: any) => { const { navigation, editMode, isSoldOut, productCart, increment, decrement, handleChangeProductCartQuantity, showOption, maxProductQuantity, errors, handleSave, handleChangeIngredientState, handleChangeSuboptionState, handleChangeCommentState, productObject, productAddedToCartLength, actionStatus, handleCreateGuestUser } = props; const theme = useTheme(); const [, { showToast }] = useToast() const [events] = useEvent() const commentRef = useRef() const isChewLayout = theme?.header?.components?.layout?.type?.toLowerCase() === 'chew' const hideProductDummyLogo = theme?.business_view?.components?.products?.components?.product?.components?.dummy?.hidden const styles = StyleSheet.create({ mainContainer: { }, headerItem: { overflow: 'hidden', backgroundColor: theme.colors.clear, width: 35, marginVertical: 18, }, optionContainer: { marginBottom: 20, }, comment: { borderWidth: 1, borderRadius: 8, borderColor: theme.colors.border, height: 100, alignItems: 'flex-start', }, quantityControl: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginRight: 10, }, btnBackArrow: { borderWidth: 0, backgroundColor: '#FFF', borderRadius: 24, marginRight: 15, }, productHeaderSkeleton: { flexDirection: 'row', width: '100%', position: 'relative', maxHeight: 260, height: 260, resizeMode: 'cover', minHeight: 200, zIndex: 0, }, extraItem: { paddingHorizontal: 7, paddingBottom: 10, borderBottomWidth: 1, borderBottomColor: theme.colors.border, justifyContent: 'center', }, slide1: { flex: 1, alignItems: 'center', width: '100%' }, mainSwiper: { height: 258, }, swiperButton: { marginHorizontal: 25, alignItems: 'center', justifyContent: 'center', width: 32, height: 32, borderRadius: 16, backgroundColor: 'rgba(208,208,208,0.5)' }, unitItem: { fontSize: 12 }, productTagWrapper: { flexDirection: 'row', alignItems: 'center' }, productTagImageStyle: { width: 32, height: 32, borderRadius: 8, resizeMode: 'cover' }, productTagNameStyle: { paddingHorizontal: 6, marginRight: 5 }, actionContainer: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', width: '100%', marginTop: 10 }, wrapperNavbar: { paddingHorizontal: 20, paddingTop: 0, } }); const [{ parsePrice, optimizeImage }] = useUtils(); const [, t] = useLanguage(); const [orderState] = useOrder(); const [{ auth }] = useSession(); const [{ configs }] = useConfig() const { product, loading, error } = productObject; const [gallery, setGallery] = useState([]) const [thumbsSwiper, setThumbsSwiper] = useState(0) const [indexGallery, setIndexGallery] = useState(0) const [selOpt, setSelectedOpt] = useState(-1); const [isHaveWeight, setIsHaveWeight] = useState(false) const [playing, setPlaying] = useState(false); const [qtyBy, setQtyBy] = useState({ weight_unit: false, pieces: true }) const [pricePerWeightUnit, setPricePerWeightUnit] = useState(null) const scrollViewRef = useRef(null); const swiperRef: any = useRef(null) const [optionLayout, setOptionLayout] = useState({}) const [headerRefHeight, setHeaderRefHeight] = useState(0) const [summaryRefHeight, setSummaryRefHeight] = useState(0) const [isScrollAvailable, setIsScrollAvailable] = useState(null) const [editionsLayoutY, setEditionsLayoutY] = useState(null) const [viewedProduct, setViewedProduct] = useState(null) const [showTitle, setShowTitle] = useState(false) const productOptionsMounted = useRef(false) const guestCheckoutEnabled = configs?.guest_checkout_enabled?.value === '1' const orderTypeEnabled = !orderTypeList[orderState?.options?.type - 1] || configs?.allowed_order_types_guest_checkout?.value?.includes(orderTypeList[orderState?.options?.type - 1]) const vibrateApp = (impact?: string) => { const options = { enableVibrateFallback: true, ignoreAndroidSystemSettings: false }; ReactNativeHapticFeedback.trigger(impact || "impactLight", options); } const isError = (id: number) => { let bgColor = theme.colors.white; if (errors[`id:${id}`]) { bgColor = 'rgba(255, 0, 0, 0.05)'; } if (maxProductQuantity <= 0) { bgColor = 'hsl(0, 0%, 72%)'; } if (isSoldOut) { bgColor = theme.colors.white; } return bgColor; }; const handleSaveProduct = () => { vibrateApp() if (!productCart.quantity) { showToast(ToastType.Error, t('VALIDATION_ERROR_REQUIRED', 'The quantity field is required').replace('_attribute_', t('PRODUCT_POTIONS_QUANTITY', 'Quantity'))) return } const isErrors = Object.values(errors)?.length > 0; if (!isErrors) { handleSave && handleSave(); return; } }; const hasRespected = (options: Array, respect_id: number) => { if (respect_id == null) return; const option: any = options.filter(({ id }: any) => id === selOpt); if (option == undefined) return false; if (option?.suboptions?.length == 0) return false; const sel = option[0]?.suboptions?.filter( ({ id }: any) => id === respect_id, ); return sel?.[0]?.id !== undefined; }; const handleChangeMainIndex = (index: number) => { if (index < 0 || index > gallery?.length - 1) { setThumbsSwiper(0) return } setThumbsSwiper(index) } const handleClickThumb = (index: number) => { if (index !== indexGallery) { swiperRef?.current.scrollBy(index - thumbsSwiper, true); setIndexGallery(index) } } const handleRedirectLogin = () => { navigation.navigate('Login', { store_slug: props.businessSlug }); }; const handleSwitchQtyUnit = (val: string) => { setQtyBy({ [val]: true, [!val]: false }) } const onStateChange = useCallback((state : string) => { if (state === "ended") { setPlaying(false); } }, []); const togglePlaying = useCallback(() => { setPlaying((prev : any) => !prev); }, []); const onChangeProductCartQuantity = (quantity: number) => { if (quantity > maxProductQuantity) { showToast(ToastType.Error, t('MAX_QUANTITY', 'The max quantity is _number_').replace('_number_', maxProductQuantity)) return } handleChangeProductCartQuantity(quantity) } const scrollDown = (id: any) => { const isErrors = Object.values(errors)?.length > 0 if (!isErrors) { return } const targetOptionId = Object.getOwnPropertyNames(errors).filter(item => !item.includes(id))[0] const targetY = optionLayout[targetOptionId]?.y if (targetY) { scrollViewRef.current.scrollTo({ y: targetY + headerRefHeight + summaryRefHeight, animated: true }) } } const handleUpdateGuest = () => { const guestToken = uuid.v4() if (guestToken) handleCreateGuestUser({ guest_token: guestToken }) } let _optionLayout: any = {} const handleOnLayout = (event: any, optionId: any, lastMounts: boolean) => { if (suboptionsLength >= 200) { return } _optionLayout = { ..._optionLayout } const optionKey = 'id:' + optionId _optionLayout[optionKey] = { y: event.nativeEvent.layout?.y } if (lastMounts) { setOptionLayout(_optionLayout) } } const handleScroll = ({ nativeEvent: { contentOffset } }: any) => { setShowTitle(contentOffset.y > 30) } const handleGoBack = navigation?.canGoBack() ? () => navigation.goBack() : () => navigation.navigate('Business', { store: props.businessSlug }) useEffect(() => { if (isScrollAvailable) { setIsScrollAvailable(null) scrollDown(isScrollAvailable) } }, [errors]) useEffect(() => { const imageList: any = [] const videoList: any = [] imageList.push(product?.images?.length > 0 ? product.images : theme?.images?.dummies?.product) if (product?.gallery && product?.gallery?.length > 0) { for (const img of product?.gallery) { if (img?.file) { imageList.push(img?.file) } if (img?.video) { const keys = img?.video.split('/') let _videoId = keys[keys?.length - 1] if (_videoId.includes('watch')) { const __url = _videoId.split('=')[1] _videoId = __url } else if (_videoId.includes('?')) { const __url = _videoId.split('?')[0] _videoId = __url } if (_videoId.search(/&/i) >= 0) { _videoId = _videoId.split('&')[0] } else if (_videoId.search(/\?/i) >= 0) { _videoId = _videoId.split('?')[0] } if ((_videoId?.length === 11)) { videoList.push(_videoId) } } } } const gallery = imageList.concat(videoList) setGallery(gallery) if (product?.weight && product?.weight_unit) { setIsHaveWeight(true) setPricePerWeightUnit(product?.price / product?.weight) } }, [product]) useEffect(() => { const keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', () => { scrollViewRef.current.scrollToEnd({ animated: true }) }) return () => { keyboardDidShowListener.remove() } }, []) useEffect(() => { if (!product?.id || product?.id === viewedProduct?.id) return setViewedProduct(product) events.emit('product_viewed', product) }, [product?.id, viewedProduct]) const actionButtonProps = { navigation, isHaveWeight, isSoldOut, maxProductQuantity, productCart, handleSaveProduct, editMode, product, errors, productAddedToCartLength, handleRedirectLogin, guestCheckoutEnabled, orderTypeEnabled, handleUpdateGuest, actionStatus } const extraOptionsProps = { setSelectedOpt, scrollViewRef, optionLayout, editionsLayoutY, styles, selOpt } const suboptionsLength = product?.extras?.reduce((acc : number, extras : any) => acc + extras?.options?.reduce((accSuboptions : number, options: any) => accSuboptions + options?.suboptions?.length, 0), 0) return ( <> handleGoBack()}> {showTitle && ( {product?.name} )} {!error && ( setHeaderRefHeight(event.nativeEvent.layout?.height)}> {loading && !product ? ( ) : ( <> {(product?.images || !hideProductDummyLogo) && ( <> handleChangeMainIndex(index)} prevButton={ } nextButton={ } > {gallery && gallery?.length > 0 && gallery.map((img : any, i: number) => ( {(String(img).includes('http') || typeof img === 'number') ? ( ) : ( <>