From b65808205c80e329f9d639f659ef8641a8b9c948 Mon Sep 17 00:00:00 2001 From: Fernando Kenji Date: Thu, 25 Nov 2021 22:30:05 -0300 Subject: [PATCH 1/3] wip: add initial logic to warn consumer when location is distant from destination Co-authored-by: lucasYudiCassin --- consumer/v2/food/home/FoodOrderHome.tsx | 6 ++++- .../v2/food/restaurant/product/ItemDetail.tsx | 24 +++++++++++++------ .../useBusinessIsAcceptingOrders.ts | 1 + 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/consumer/v2/food/home/FoodOrderHome.tsx b/consumer/v2/food/home/FoodOrderHome.tsx index ecd40a828..508bc8171 100644 --- a/consumer/v2/food/home/FoodOrderHome.tsx +++ b/consumer/v2/food/home/FoodOrderHome.tsx @@ -11,7 +11,7 @@ import { useSearch } from '../../../../common/store/api/search/useSearch'; import { useSegmentScreen } from '../../../../common/store/api/track'; import { updateCurrentLocation, - updateCurrentPlace, + updateCurrentPlace } from '../../../../common/store/consumer/actions'; import { getConsumer, getCurrentLocation } from '../../../../common/store/consumer/selectors'; import { SearchFilter } from '../../../../common/store/consumer/types'; @@ -50,6 +50,10 @@ export const FoodOrderHome = ({ route, navigation }: Props) => { refetch, fetchNextPage, } = useSearch(true, 'restaurant', 'distance', filters, currentLocation, ''); + if (restaurants) { + restaurants[0].status = 'open'; + } + const [refreshing, setRefreshing] = React.useState(false); const mostRecentRestaurants = useLastRestaurants(consumer?.id); // side effects diff --git a/consumer/v2/food/restaurant/product/ItemDetail.tsx b/consumer/v2/food/restaurant/product/ItemDetail.tsx index 69ab2ae54..699bf57e8 100644 --- a/consumer/v2/food/restaurant/product/ItemDetail.tsx +++ b/consumer/v2/food/restaurant/product/ItemDetail.tsx @@ -3,7 +3,7 @@ import { ComplementGroup, OrderItem, OrderItemComplement, - WithId, + WithId } from '@appjusto/types'; import { Feather } from '@expo/vector-icons'; import { CompositeNavigationProp, RouteProp } from '@react-navigation/native'; @@ -29,15 +29,15 @@ import { track, useSegmentScreen } from '../../../../../common/store/api/track'; import { getConsumer, getCurrentLocation, - getCurrentPlace, + getCurrentPlace } from '../../../../../common/store/consumer/selectors'; import { useContextBusiness, - useContextBusinessId, + useContextBusinessId } from '../../../../../common/store/context/business'; import { useContextGetComplementGroup, - useContextGetProductCategory, + useContextGetProductCategory } from '../../../../../common/store/context/menu'; import { useContextActiveOrder } from '../../../../../common/store/context/order'; import { @@ -46,7 +46,7 @@ import { halfPadding, padding, screens, - texts, + texts } from '../../../../../common/styles'; import { formatCurrency, formatHour } from '../../../../../common/utils/formatters'; import { t } from '../../../../../strings'; @@ -91,6 +91,8 @@ export const ItemDetail = ({ navigation, route }: Props) => { destination && business?.businessAddress?.latlng ? distanceBetweenLatLng(destination, business.businessAddress.latlng) : 0; + const distanceBetweenLocationAndDestination = + location && destination ? distanceBetweenLatLng(location, destination) : 0; const isOutOfRange = (business?.deliveryRange ?? 0) < (distance ?? 0); const isAcceptingOrders = useBusinessIsAcceptingOrders(business); const product = useProduct(businessId, productId); @@ -186,17 +188,25 @@ export const ItemDetail = ({ navigation, route }: Props) => { Keyboard.dismiss(); if (!orderItem) return; if (!activeOrder) { + console.log( + `distanceBetweenLocationAndDestination: ${distanceBetweenLocationAndDestination}` + ); + if (distanceBetweenLocationAndDestination > 2) { + // showModaldestination + } + api.order().createFoodOrder(business, consumer!, [orderItem], currentPlace ?? null); track('consumer created food order in database'); } else { const updatedOrder = !itemId ? helpers.addItemToOrder(activeOrder, orderItem) : quantity > 0 - ? helpers.updateItem(activeOrder, orderItem) - : helpers.removeItem(activeOrder, orderItem); + ? helpers.updateItem(activeOrder, orderItem) + : helpers.removeItem(activeOrder, orderItem); api.order().updateOrder(activeOrder.id, updatedOrder); track('consumer updated items in order'); } + navigation.pop(); })(); }; diff --git a/consumer/v2/food/restaurant/useBusinessIsAcceptingOrders.ts b/consumer/v2/food/restaurant/useBusinessIsAcceptingOrders.ts index f81baa111..0ade38568 100644 --- a/consumer/v2/food/restaurant/useBusinessIsAcceptingOrders.ts +++ b/consumer/v2/food/restaurant/useBusinessIsAcceptingOrders.ts @@ -5,6 +5,7 @@ import { useContextGetSeverTime } from '../../../../common/contexts/ServerTimeCo export const useBusinessIsAcceptingOrders = (business?: Business) => { const platformParams = usePlatformParamsContext(); const getServerTime = useContextGetSeverTime(); + return true; if (!platformParams) return false; if (!getServerTime) return false; if (!business) return false; From 552ba9cc332c4230590706edcf26b7950040fec1 Mon Sep 17 00:00:00 2001 From: Lucas Cassin Date: Mon, 29 Nov 2021 20:54:21 -0300 Subject: [PATCH 2/3] Add logic to show warning modal --- .../v2/food/restaurant/product/ItemDetail.tsx | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/consumer/v2/food/restaurant/product/ItemDetail.tsx b/consumer/v2/food/restaurant/product/ItemDetail.tsx index 699bf57e8..6791acae3 100644 --- a/consumer/v2/food/restaurant/product/ItemDetail.tsx +++ b/consumer/v2/food/restaurant/product/ItemDetail.tsx @@ -19,6 +19,7 @@ import PaddedView from '../../../../../common/components/containers/PaddedView'; import DefaultInput from '../../../../../common/components/inputs/DefaultInput'; import HR from '../../../../../common/components/views/HR'; import { IconSemaphoreSmall } from '../../../../../common/icons/icon-semaphore-small'; +import useLastKnownLocation from '../../../../../common/location/useLastKnownLocation'; import { UnloggedParamList } from '../../../../../common/screens/unlogged/types'; import { useProduct } from '../../../../../common/store/api/business/hooks/useProduct'; import { useProductImageURI } from '../../../../../common/store/api/business/hooks/useProductImageURI'; @@ -26,11 +27,7 @@ import { getNextAvailableDate } from '../../../../../common/store/api/business/s import { distanceBetweenLatLng } from '../../../../../common/store/api/helpers'; import * as helpers from '../../../../../common/store/api/order/helpers'; import { track, useSegmentScreen } from '../../../../../common/store/api/track'; -import { - getConsumer, - getCurrentLocation, - getCurrentPlace -} from '../../../../../common/store/consumer/selectors'; +import { getConsumer, getCurrentPlace } from '../../../../../common/store/consumer/selectors'; import { useContextBusiness, useContextBusinessId @@ -85,14 +82,18 @@ export const ItemDetail = ({ navigation, route }: Props) => { const consumer = useSelector(getConsumer); const currentPlace = useSelector(getCurrentPlace); // state - const location = useSelector(getCurrentLocation); - const destination = activeOrder?.destination?.location ?? currentPlace?.location ?? location; + + const { coords } = useLastKnownLocation(); + + const destination = activeOrder?.destination?.location; + const location = coords ?? currentPlace?.location; const distance = destination && business?.businessAddress?.latlng ? distanceBetweenLatLng(destination, business.businessAddress.latlng) : 0; - const distanceBetweenLocationAndDestination = + const distanceBetweenLocationAndDestinationMeters = location && destination ? distanceBetweenLatLng(location, destination) : 0; + const bottomLimitWarningModalShowMeters = 2000; const isOutOfRange = (business?.deliveryRange ?? 0) < (distance ?? 0); const isAcceptingOrders = useBusinessIsAcceptingOrders(business); const product = useProduct(businessId, productId); @@ -134,6 +135,7 @@ export const ItemDetail = ({ navigation, route }: Props) => { if (!product) return; const item = activeOrder.items?.find((i) => i.id === itemId); if (!item) return; + setComplements(item.complements ?? []); setQuantity(item.quantity); setNotes(item.notes ?? ''); @@ -187,14 +189,23 @@ export const ItemDetail = ({ navigation, route }: Props) => { (async () => { Keyboard.dismiss(); if (!orderItem) return; - if (!activeOrder) { + + //Log test + // console.log(`location = (${location?.latitude}, ${location?.longitude})`); + // console.log(`destination = (${destination?.latitude}, ${destination?.longitude})`); + + if ((activeOrder?.items?.length ?? 0) === 0) { + //0 itens na sacola console.log( - `distanceBetweenLocationAndDestination: ${distanceBetweenLocationAndDestination}` + `distanceBetweenLocationAndDestination 2: ${distanceBetweenLocationAndDestinationMeters}` ); - if (distanceBetweenLocationAndDestination > 2) { + if (distanceBetweenLocationAndDestinationMeters > bottomLimitWarningModalShowMeters) { // showModaldestination + console.log('Local de destino nao corresponde com sua localizacao atual'); } + } + if (!activeOrder) { api.order().createFoodOrder(business, consumer!, [orderItem], currentPlace ?? null); track('consumer created food order in database'); } else { From 07dfa3773533611cc740b321e233879a032def2e Mon Sep 17 00:00:00 2001 From: Lucas Cassin Date: Wed, 1 Dec 2021 21:19:19 -0300 Subject: [PATCH 3/3] Feat: Add modal when location is distant from destination Co-authored-by: Fernando Kenji --- .../v2/food/restaurant/product/ItemDetail.tsx | 48 ++++++++++------ .../LocationDistantFromDestinationModal.tsx | 56 +++++++++++++++++++ .../useBusinessIsAcceptingOrders.ts | 2 +- 3 files changed, 89 insertions(+), 17 deletions(-) create mode 100644 consumer/v2/food/restaurant/product/LocationDistantFromDestinationModal.tsx diff --git a/consumer/v2/food/restaurant/product/ItemDetail.tsx b/consumer/v2/food/restaurant/product/ItemDetail.tsx index 6791acae3..e19c216a5 100644 --- a/consumer/v2/food/restaurant/product/ItemDetail.tsx +++ b/consumer/v2/food/restaurant/product/ItemDetail.tsx @@ -53,6 +53,7 @@ import { RestaurantNavigatorParamList } from '../types'; import { useBusinessIsAcceptingOrders } from '../useBusinessIsAcceptingOrders'; import { ItemComplements } from './ItemComplements'; import { ItemQuantity } from './ItemQuantity'; +import { LocationDistantFromDestinationModal } from './LocationDistantFromDestinationModal'; type ScreenNavigationProp = CompositeNavigationProp< StackNavigationProp, @@ -102,6 +103,7 @@ export const ItemDetail = ({ navigation, route }: Props) => { const [quantity, setQuantity] = React.useState(1); const [complements, setComplements] = React.useState([]); const [notes, setNotes] = React.useState(''); + const [modalVisible, setModalVisible] = React.useState(false); const orderItem = React.useMemo(() => { if (!product) return undefined; return { @@ -117,6 +119,14 @@ export const ItemDetail = ({ navigation, route }: Props) => { complements, } as OrderItem; }, [product, itemId, quantity, notes, complements, getProductCategory]); + + const isLocationDistantFromDestination = React.useMemo( + (): boolean => + (activeOrder?.items?.length ?? 0) === 0 && + distanceBetweenLocationAndDestinationMeters > bottomLimitWarningModalShowMeters, + [activeOrder, distanceBetweenLocationAndDestinationMeters, bottomLimitWarningModalShowMeters] + ); + const canAddItemToOrder = React.useMemo(() => { if (!product) return false; return helpers.hasSatisfiedAllGroups(product, complements); @@ -140,6 +150,11 @@ export const ItemDetail = ({ navigation, route }: Props) => { setQuantity(item.quantity); setNotes(item.notes ?? ''); }, [itemId, activeOrder, product]); + React.useEffect(() => { + if (!activeOrder) { + api.order().createFoodOrder(business!, consumer!, [], currentPlace ?? null); + } + }, []); // tracking useSegmentScreen('ItemDetail'); // UI @@ -185,25 +200,20 @@ export const ItemDetail = ({ navigation, route }: Props) => { } }; // handlers + const handleAddItemToOrder = () => { + Keyboard.dismiss(); + if (isLocationDistantFromDestination) { + setModalVisible(true); + } else { + updateOrder(); + } + }; + const updateOrder = () => { (async () => { - Keyboard.dismiss(); if (!orderItem) return; - //Log test - // console.log(`location = (${location?.latitude}, ${location?.longitude})`); - // console.log(`destination = (${destination?.latitude}, ${destination?.longitude})`); - - if ((activeOrder?.items?.length ?? 0) === 0) { - //0 itens na sacola - console.log( - `distanceBetweenLocationAndDestination 2: ${distanceBetweenLocationAndDestinationMeters}` - ); - if (distanceBetweenLocationAndDestinationMeters > bottomLimitWarningModalShowMeters) { - // showModaldestination - console.log('Local de destino nao corresponde com sua localizacao atual'); - } - } + console.log(activeOrder?.items); if (!activeOrder) { api.order().createFoodOrder(business, consumer!, [orderItem], currentPlace ?? null); @@ -378,11 +388,17 @@ export const ItemDetail = ({ navigation, route }: Props) => { title={`${t('Adicionar')} ${formatCurrency(helpers.getItemTotal(orderItem!))}`} disabled={!canAddItemToOrder} onChange={(value) => setQuantity(value)} - onSubmit={updateOrder} + onSubmit={handleAddItemToOrder} /> ) : null} + setModalVisible(false)} + positiveAnswer={updateOrder} + negativeAnswer={() => setModalVisible(false)} + /> ); }; diff --git a/consumer/v2/food/restaurant/product/LocationDistantFromDestinationModal.tsx b/consumer/v2/food/restaurant/product/LocationDistantFromDestinationModal.tsx new file mode 100644 index 000000000..7b027ac57 --- /dev/null +++ b/consumer/v2/food/restaurant/product/LocationDistantFromDestinationModal.tsx @@ -0,0 +1,56 @@ +import { MaterialIcons } from '@expo/vector-icons'; +import React from 'react'; +import { Modal, Text, TouchableOpacity, View } from 'react-native'; +import DefaultButton from '../../../../../common/components/buttons/DefaultButton'; +import PaddedView from '../../../../../common/components/containers/PaddedView'; +import { borders, colors, halfPadding, padding, texts } from '../../../../../common/styles'; +import { t } from '../../../../../strings'; + +type Props = { + modalVisible: boolean; + onModalClose: () => void; + positiveAnswer: () => void; + negativeAnswer: () => void; +}; + +export const LocationDistantFromDestinationModal = ({ + modalVisible, + onModalClose, + positiveAnswer, + negativeAnswer, +}: Props) => { + return ( + + + + + + + + + + + + {t('Seu endereço de entrega não bate com a sua localização.')} + + + {t('Você quer continuar mesmo assim?')} + + + + + + + ); +}; diff --git a/consumer/v2/food/restaurant/useBusinessIsAcceptingOrders.ts b/consumer/v2/food/restaurant/useBusinessIsAcceptingOrders.ts index 0ade38568..a42ac7916 100644 --- a/consumer/v2/food/restaurant/useBusinessIsAcceptingOrders.ts +++ b/consumer/v2/food/restaurant/useBusinessIsAcceptingOrders.ts @@ -5,7 +5,7 @@ import { useContextGetSeverTime } from '../../../../common/contexts/ServerTimeCo export const useBusinessIsAcceptingOrders = (business?: Business) => { const platformParams = usePlatformParamsContext(); const getServerTime = useContextGetSeverTime(); - return true; + if (!platformParams) return false; if (!getServerTime) return false; if (!business) return false;