diff --git a/src/App.tsx b/src/App.tsx index 535b6e47b..31fda5f0a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -89,7 +89,6 @@ LogBox.ignoreLogs([ 'Reduced motion setting is enabled on this device', 'React Native Firebase namespaced API', 'global process.env.EXPO_OS is not defined', - 'Support for defaultProps will be removed', 'A non-serializable value was detected in an action', ]); diff --git a/src/components/AddressAutocomplete.tsx b/src/components/AddressAutocomplete.tsx index 953b36625..29106baa6 100644 --- a/src/components/AddressAutocomplete.tsx +++ b/src/components/AddressAutocomplete.tsx @@ -11,7 +11,6 @@ import { Input, InputField } from '@/components/ui/input'; import { Pressable } from '@/components/ui/pressable'; import { Text } from '@/components/ui/text'; import { Button, ButtonText, ButtonIcon } from '@/components/ui/button'; -import PropTypes from 'prop-types'; import qs from 'qs'; import React, { useMemo, useState } from 'react'; import { withTranslation } from 'react-i18next'; @@ -49,6 +48,14 @@ const fuseOptions = { keys: ['contactName', 'streetAddress'], }; +interface AddressAutocompleteProps { + minChars?: number; + addresses?: unknown[]; + renderRight?(...args: unknown[]): unknown; + onMapPickerPress?(...args: unknown[]): unknown; + mapPickerStyle?: "small" | "large"; +} + function AddressAutocomplete({ country, t, @@ -67,7 +74,7 @@ function AddressAutocomplete({ location, mapPickerStyle = 'small', ...otherProps -}) { +}: AddressAutocompleteProps) { const props = arguments[0]; const [query, setQuery] = useState( @@ -503,14 +510,6 @@ function AddressAutocomplete({ ); } -AddressAutocomplete.propTypes = { - minChars: PropTypes.number, - addresses: PropTypes.array, - renderRight: PropTypes.func, - onMapPickerPress: PropTypes.func, - mapPickerStyle: PropTypes.oneOf(['small', 'large']), -}; - const styles = StyleSheet.create({ poweredContainer: { alignItems: 'flex-end', diff --git a/src/components/DeliveryList.tsx b/src/components/DeliveryList.tsx index 4467deb5b..4cb711efc 100644 --- a/src/components/DeliveryList.tsx +++ b/src/components/DeliveryList.tsx @@ -2,9 +2,7 @@ import _ from 'lodash'; import moment from 'moment'; import { Icon, ChevronRightIcon } from '@/components/ui/icon'; import { Text } from '@/components/ui/text'; -import PropTypes from 'prop-types'; import React, { Component } from 'react'; -import { withTranslation } from 'react-i18next'; import { ActivityIndicator, Platform, @@ -65,137 +63,137 @@ const captionTextProps = { ellipsizeMode: 'tail', }; -class DeliveryList extends Component { - _onItemPress(item) { - if (this.props.loading || this.props.refreshing) { - return; - } - - this.props.onItemPress(item); - } - - renderItemCaption(item) { - const lines = this.props.itemCaptionLines - ? this.props.itemCaptionLines(item) - : [item.pickup.address.streetAddress, item.dropoff.address.streetAddress]; +interface DeliveryListProps { + data?: object[]; + loading?: boolean; + onItemPress(...args: unknown[]): unknown; + onEndReached(...args: unknown[]): unknown; + itemCaptionLines?(...args: unknown[]): unknown; + onRefresh?(...args: unknown[]): unknown; + refreshing?: boolean; +} - return ( - - {lines.map((line, index) => ( - - {line} +const ListItem = ({ item, itemCaptionLines, onPress }) => { + + return ( + onPress(item)} + style={styles.item}> + + + + + + {`#${ + item.orderNumber ? item.orderNumber : item.id + }`} + + + + + {/* @see https://stackoverflow.com/questions/43143258/flex-vs-flexgrow-vs-flexshrink-vs-flexbasis-in-react-native */} + + + {moment(item.dropoff.doneBefore).format('LT')} - ))} + - ); - } + + + + + ); +} - renderItem(item) { - return ( - this._onItemPress(item)} - style={styles.item}> - - - - - - {`#${ - item.orderNumber ? item.orderNumber : item.id - }`} - - - {this.renderItemCaption(item)} - - {/* @see https://stackoverflow.com/questions/43143258/flex-vs-flexgrow-vs-flexshrink-vs-flexbasis-in-react-native */} - - - {moment(item.dropoff.doneBefore).format('LT')} - - - - - - - - ); - } +const ListItemCaption = ({ item, itemCaptionLines }) => { + const lines = itemCaptionLines + ? itemCaptionLines(item) + : [item.pickup.address.streetAddress, item.dropoff.address.streetAddress]; + + return ( + + {lines.map((line, index) => ( + + {line} + + ))} + + ); +} - renderFooter() { - if (!this.props.loading) { - return null; - } - - return ( - - - - ); +const ListFooter = ({ loading }) => { + + if (!loading) { + return null; } - render() { - if (this.props.data.length === 0) { - return ; - } - - const groups = _.groupBy(this.props.data, item => - moment(item.dropoff.doneBefore).format('LL'), - ); - const sections = _.map(groups, (value, key) => ({ - title: key, - data: value, - })); - - return ( - item['@id']} - renderItem={({ item }) => this.renderItem(item)} - ItemSeparatorComponent={ItemSeparator} - ListFooterComponent={this.renderFooter.bind(this)} - renderSectionHeader={({ section: { title } }) => ( - - )} - /> - ); + return ( + + + + ); +} + +function _onItemPress(item, loading, refreshing, onItemPress) { + if (loading || refreshing) { + return; } - static defaultProps = { - data: [], - loading: false, - refreshing: false, - onRefresh: () => {}, - }; + onItemPress(item); } -DeliveryList.propTypes = { - data: PropTypes.arrayOf(PropTypes.object), - loading: PropTypes.bool, - onItemPress: PropTypes.func.isRequired, - onEndReached: PropTypes.func.isRequired, - itemCaptionLines: PropTypes.func, - onRefresh: PropTypes.func, - refreshing: PropTypes.bool, -}; +const DeliveryList = ({ + onItemPress, + onEndReached, + itemCaptionLines, + onRefresh = () => {}, + data = [], + loading = false, + refreshing = false }: DeliveryListProps) => { + + if (data.length === 0) { + return ; + } + + const groups = _.groupBy(data, item => + moment(item.dropoff.doneBefore).format('LL'), + ); + const sections = _.map(groups, (value, key) => ({ + title: key, + data: value, + })); + + return ( + item['@id']} + renderItem={({ item }) => _onItemPress(item, loading, refreshing, onItemPress)} /> } + ItemSeparatorComponent={ItemSeparator} + ListFooterComponent={() => } + renderSectionHeader={({ section: { title } }) => ( + + )} + /> + ); +} -export default withTranslation()(DeliveryList); +export default DeliveryList; diff --git a/src/components/OrderItems.tsx b/src/components/OrderItems.tsx index ee076bbc5..c9a55d933 100644 --- a/src/components/OrderItems.tsx +++ b/src/components/OrderItems.tsx @@ -2,9 +2,8 @@ import _ from 'lodash'; import { Box } from '@/components/ui/box'; import { HStack } from '@/components/ui/hstack'; import { Text } from '@/components/ui/text'; -import PropTypes from 'prop-types'; import React, { Component } from 'react'; -import { withTranslation } from 'react-i18next'; +import { useTranslation } from 'react-i18next'; import { FlatList, SectionList, StyleSheet, View } from 'react-native'; import { formatPrice } from '../utils/formatting'; @@ -48,163 +47,164 @@ const SectionHeader = ({ section: { title } }) => ( ); +const ItemAdjustments = ({ adjustments, important = false }) => { + + const textStyle = [styles.adjustmentText]; + if (important) { + textStyle.push(styles.textHighlight); + } + + return ( + + {adjustments.map((adjustment, index) => ( + + {adjustment.label} + + ))} + + ); +} + +const Adjustments = ({ order }) => { + + const { t } = useTranslation(); + + return ( + <> + + + + ); +} + +const ItemsTotal = ({ order }) => { + + const { t } = useTranslation(); + + return ( + + ); +} + +const Total = ({ order }) => { + + const { t } = useTranslation(); + + return ( + + ); +} + +const ListItem = ({ item }) => { + + const itemQuantityStyle = [styles.itemQuantity]; + if (item.quantity > 1) { + itemQuantityStyle.push(styles.textHighlight); + } + + return ( + + + {`${item.quantity} ×`} + + + {item.name} + {item.adjustments && + item.adjustments.hasOwnProperty('menu_item_modifier') && + } + {item.adjustments && + item.adjustments.hasOwnProperty('reusable_packaging') && + } + + + {`${formatPrice(item.total)}`} + + + ); +} + const itemsToSections = itemsGroupedByVendor => _.map(itemsGroupedByVendor, items => ({ title: items[0].vendor.name, data: items, })); -class OrderItems extends Component { - renderItemAdjustments(adjustments, important = false) { - const textStyle = [styles.adjustmentText]; - if (important) { - textStyle.push(styles.textHighlight); - } - - return ( - - {adjustments.map((adjustment, index) => ( - - {adjustment.label} - - ))} - - ); +function deliveryTotal(order) { + if (!order.adjustments) { + return 0; } - renderItem(item) { - const itemQuantityStyle = [styles.itemQuantity]; - if (item.quantity > 1) { - itemQuantityStyle.push(styles.textHighlight); - } - - return ( - - - {`${item.quantity} ×`} - - - {item.name} - {item.adjustments && - item.adjustments.hasOwnProperty('menu_item_modifier') && - this.renderItemAdjustments(item.adjustments.menu_item_modifier)} - {item.adjustments && - item.adjustments.hasOwnProperty('reusable_packaging') && - this.renderItemAdjustments( - item.adjustments.reusable_packaging, - true, - )} - - - {`${formatPrice(item.total)}`} - - - ); + if (!order.adjustments.hasOwnProperty('delivery')) { + return 0; } - _deliveryTotal(order) { - if (!order.adjustments) { - return 0; - } - - if (!order.adjustments.hasOwnProperty('delivery')) { - return 0; - } - - return _.reduce( - order.adjustments.delivery, - function (total, adj) { - return total + adj.amount; - }, - 0, - ); - } + return _.reduce( + order.adjustments.delivery, + function (total, adj) { + return total + adj.amount; + }, + 0, + ); +} - renderAdjustments() { - return ( - <> - - - - ); - } +interface OrderItemsProps { + order?: unknown; + withDeliveryTotal?: boolean; + withTotals?: boolean; +} - renderItemsTotal() { - return ( - - ); - } +const OrderItems = ({ order, withDeliveryTotal = false, withTotals = true}: OrderItemsProps) => { - renderTotal() { - return ( - - ); - } + const itemsGroupedByVendor = _.groupBy(order.items, 'vendor.@id'); + const isMultiVendor = _.size(itemsGroupedByVendor) > 1; - render() { - const { order } = this.props; - - const itemsGroupedByVendor = _.groupBy(order.items, 'vendor.@id'); - const isMultiVendor = _.size(itemsGroupedByVendor) > 1; - - const items = isMultiVendor - ? itemsToSections(itemsGroupedByVendor) - : order.items; - - return ( - - - {isMultiVendor && ( - `ITEM#${item.id}`} - renderItem={({ item }) => this.renderItem(item)} - renderSectionHeader={SectionHeader} - /> - )} - {!isMultiVendor && ( - `ITEM#${item.id}`} - renderItem={({ item }) => this.renderItem(item)} - ItemSeparatorComponent={ItemSeparator} - /> - )} - - {this.props.withTotals && ( - - {this.renderItemsTotal()} - {this.props.withDeliveryTotal === true && this.renderAdjustments()} - {this.props.withDeliveryTotal === true && this.renderTotal()} - + const items = isMultiVendor + ? itemsToSections(itemsGroupedByVendor) + : order.items; + + return ( + + + {isMultiVendor && ( + `ITEM#${item.id}`} + renderItem={({ item }) => } + renderSectionHeader={SectionHeader} + /> + )} + {!isMultiVendor && ( + `ITEM#${item.id}`} + renderItem={({ item }) => } + ItemSeparatorComponent={ItemSeparator} + /> )} - ); - } - - static defaultProps = { - withDeliveryTotal: false, - withTotals: true, - }; + {withTotals && ( + + + {this.props.withDeliveryTotal === true && } + {this.props.withDeliveryTotal === true && } + + )} + + ); } -OrderItems.propTypes = { - withDeliveryTotal: PropTypes.bool, - withTotals: PropTypes.bool, -}; - -export default withTranslation()(OrderItems); +export default OrderItems; diff --git a/src/navigation/account/AccountAddressesPage.tsx b/src/navigation/account/AccountAddressesPage.tsx index 2419a220f..1d7410dac 100644 --- a/src/navigation/account/AccountAddressesPage.tsx +++ b/src/navigation/account/AccountAddressesPage.tsx @@ -2,7 +2,6 @@ import { Divider } from '@/components/ui/divider'; import { HStack } from '@/components/ui/hstack'; import { Heading } from '@/components/ui/heading'; import { Text } from '@/components/ui/text'; -import PropTypes from 'prop-types'; import React, { Component } from 'react'; import { withTranslation } from 'react-i18next'; import { FlatList, Image, TouchableOpacity, View } from 'react-native'; @@ -20,7 +19,11 @@ import { greyColor } from '../../styles/common'; import { useSecondaryTextColor } from '../../styles/theme'; import Address from '../../utils/Address'; -class AccountAddressesPage extends Component { +interface AccountAddressesPageProps { + onSelect?(...args: unknown[]): unknown; +} + +class AccountAddressesPage extends Component { constructor(props) { super(props); this.state = { @@ -123,10 +126,6 @@ class AccountAddressesPage extends Component { } } -AccountAddressesPage.propTypes = { - onSelect: PropTypes.func, -}; - function mapStateToProps(state, ownProps) { const fnSelect = () => { switch (ownProps.route.params?.action) { diff --git a/src/navigation/checkout/components/AddressModal.tsx b/src/navigation/checkout/components/AddressModal.tsx index 3035f75c9..dccc6b0af 100644 --- a/src/navigation/checkout/components/AddressModal.tsx +++ b/src/navigation/checkout/components/AddressModal.tsx @@ -1,5 +1,4 @@ import { Text } from '@/components/ui/text'; -import PropTypes from 'prop-types'; import React, { Component } from 'react'; import { withTranslation } from 'react-i18next'; import { @@ -26,7 +25,12 @@ import { } from '../../../redux/Checkout/actions'; import { selectIsCollectionEnabled } from '../../../redux/Checkout/selectors'; -class AddressModal extends Component { +interface AddressModalProps { + onSelect(...args: unknown[]): unknown; + value?: object; +} + +class AddressModal extends Component { constructor(props) { super(props); this.state = { @@ -144,11 +148,6 @@ class AddressModal extends Component { } } -AddressModal.propTypes = { - onSelect: PropTypes.func.isRequired, - value: PropTypes.object, -}; - const styles = StyleSheet.create({ autocompleteContainer: { position: 'absolute', diff --git a/src/navigation/checkout/components/CartFooter.tsx b/src/navigation/checkout/components/CartFooter.tsx index 8098e8fb1..5ecff56d2 100644 --- a/src/navigation/checkout/components/CartFooter.tsx +++ b/src/navigation/checkout/components/CartFooter.tsx @@ -1,59 +1,54 @@ import { Skeleton } from '@/components/ui/skeleton'; import { HStack } from '@/components/ui/hstack'; -import PropTypes from 'prop-types'; import React, { Component } from 'react'; -import { withTranslation } from 'react-i18next'; import { View } from 'react-native'; import { connect } from 'react-redux'; import CartFooterButton from './CartFooterButton'; -class CartFooter extends Component { - render() { - const { cart, restaurant, initLoading } = this.props; +interface CartFooterProps { + testID: string; + isLoading?: boolean; + initLoading?: boolean; + onSubmit(): void; + cart: unknown; + restaurant: unknown; + disabled?: boolean; +} - return ( - - - {initLoading && ( - - )} - {!initLoading && ( - this.props.onSubmit()} - loading={this.props.isLoading} - testID={this.props.testID} - disabled={this.props.disabled} - /> - )} - - - ); - } +const CartFooter = ({ testID, isLoading, initLoading, onSubmit, cart, restaurant, disabled = false }: CartFooterProps) => { - static defaultProps = { - disabled: false, - }; + return ( + + + {initLoading && ( + + )} + {!initLoading && ( + + )} + + + ); } -CartFooter.propTypes = { - testID: PropTypes.string.isRequired, - isLoading: PropTypes.bool, - disabled: PropTypes.bool, -}; - function mapStateToProps(state, ownProps) { return { isLoading: state.checkout.isLoading, }; } -export default connect(mapStateToProps)(withTranslation()(CartFooter)); +export default connect(mapStateToProps)(CartFooter); diff --git a/src/navigation/checkout/components/CartFooterButton.tsx b/src/navigation/checkout/components/CartFooterButton.tsx index 4848e5cd0..6bf532684 100644 --- a/src/navigation/checkout/components/CartFooterButton.tsx +++ b/src/navigation/checkout/components/CartFooterButton.tsx @@ -1,9 +1,8 @@ -import React, { Component } from 'react'; +import React, { useEffect, useRef, useCallback } from 'react'; import { Button, ButtonText, ButtonIcon } from '@/components/ui/button'; import { ShoppingCart } from 'lucide-react-native' import { Text } from '@/components/ui/text'; -import PropTypes from 'prop-types'; -import { withTranslation } from 'react-i18next'; +import { withTranslation, useTranslation } from 'react-i18next'; import { Animated, View } from 'react-native'; import { formatPrice } from '../../../utils/formatting'; @@ -12,96 +11,84 @@ import { shouldShowPreOrder, } from '../../../utils/checkout'; -class CartFooterButton extends Component { - constructor(props) { - super(props); - this.state = { - opacityAnim: new Animated.Value(1), - }; - } +interface CartFooterButtonProps { + testID: string; + isLoading?: boolean; + disabled?: boolean; + onPress(): unknown; +} - componentDidUpdate(prevProps, prevState) { - if (this.props.cart.total !== prevProps.cart.total) { - this.animate(); - } - } +const ButtonLeft = () => { + + return ( + + ); +} + +const ButtonRight = ({ opacity, total }) => { + + return ( + + + {`${formatPrice(total)}`} + + + ); +} + +const CartFooterButton = ({ cart, restaurant, onPress, testID, loading, disabled }: CartFooterButtonProps) => { + + const { t } = useTranslation(); - animate() { + const opacity = useRef(new Animated.Value(1)).current; + + const animate = useCallback(() => { Animated.sequence([ - Animated.timing(this.state.opacityAnim, { + Animated.timing(opacity, { toValue: 0.4, duration: 500, useNativeDriver: false, }), - Animated.timing(this.state.opacityAnim, { + Animated.timing(opacity, { toValue: 1, duration: 250, useNativeDriver: false, }), ]).start(); - } + }, [opacity]) - renderLeft() { - return ( - - ); - } + useEffect(animate, [cart.total, animate]); - renderRight() { - return ( - - - {`${formatPrice(this.props.cart.total)}`} - - - ); - } + const isAvailable = isRestaurantOrderingAvailable(restaurant); + const showPreOrder = shouldShowPreOrder(restaurant); - render() { - const { cart, restaurant } = this.props; - - const isAvailable = isRestaurantOrderingAvailable(restaurant); - const showPreOrder = shouldShowPreOrder(restaurant); - - if (!cart || cart.items.length === 0 || !isAvailable) { - return ; - } - - const label = showPreOrder - ? this.props.t('SCHEDULE_ORDER') - : this.props.t('ORDER'); - - return ( - - ); + if (!cart || cart.items.length === 0 || !isAvailable) { + return ; } - static defaultProps = { - isLoading: false, - disabled: false, - }; -} + const label = showPreOrder + ? t('SCHEDULE_ORDER') + : t('ORDER'); -CartFooterButton.propTypes = { - testID: PropTypes.string.isRequired, - isLoading: PropTypes.bool, - disabled: PropTypes.bool, -}; + return ( + + ); +} export default withTranslation()(CartFooterButton); diff --git a/src/navigation/checkout/components/CouponModal.tsx b/src/navigation/checkout/components/CouponModal.tsx index 1e68aeb59..482fbaf24 100644 --- a/src/navigation/checkout/components/CouponModal.tsx +++ b/src/navigation/checkout/components/CouponModal.tsx @@ -4,7 +4,6 @@ import { Box } from '@/components/ui/box'; import { FormControl, FormControlLabel, FormControlLabelText } from '@/components/ui/form-control'; import { VStack } from '@/components/ui/vstack'; import { Button, ButtonText } from '@/components/ui/button'; -import PropTypes from 'prop-types'; import React, { Component } from 'react'; import { withTranslation } from 'react-i18next'; import Modal from 'react-native-modal'; @@ -12,7 +11,12 @@ import { connect } from 'react-redux'; import ModalContent from '../../../components/ModalContent'; -class CouponModal extends Component { +interface CouponModalProps { + onSwipeComplete(...args: unknown[]): unknown; + onSubmit(...args: unknown[]): unknown; +} + +class CouponModal extends Component { _validate(values) { return {}; } @@ -78,11 +82,6 @@ class CouponModal extends Component { } } -CouponModal.propTypes = { - onSwipeComplete: PropTypes.func.isRequired, - onSubmit: PropTypes.func.isRequired, -}; - function mapStateToProps(state) { return {}; } diff --git a/src/navigation/checkout/components/ExpiredSessionModal.tsx b/src/navigation/checkout/components/ExpiredSessionModal.tsx index dec6adb17..502edd374 100644 --- a/src/navigation/checkout/components/ExpiredSessionModal.tsx +++ b/src/navigation/checkout/components/ExpiredSessionModal.tsx @@ -1,7 +1,6 @@ import { Button, ButtonText } from '@/components/ui/button'; import { Text } from '@/components/ui/text'; import { Box } from '@/components/ui/box'; -import PropTypes from 'prop-types'; import React, { Component } from 'react'; import { withTranslation, useTranslation } from 'react-i18next'; import { StyleSheet } from 'react-native'; @@ -10,7 +9,15 @@ import { connect } from 'react-redux'; import { hideExpiredSessionModal } from '../../../redux/Checkout/actions'; -const ExpiredSessionModal = ({ isVisible, onModalHide, hideExpiredSessionModal }) => { +interface ExpiredSessionModalProps { + onModalHide(...args: unknown[]): unknown; +} + +const ExpiredSessionModal = ({ + isVisible, + onModalHide, + hideExpiredSessionModal +}: ExpiredSessionModalProps) => { const { t } = useTranslation() @@ -32,10 +39,6 @@ const ExpiredSessionModal = ({ isVisible, onModalHide, hideExpiredSessionModal } ); } -ExpiredSessionModal.propTypes = { - onModalHide: PropTypes.func.isRequired, -}; - const styles = StyleSheet.create({ modalText: { padding: 10, diff --git a/src/navigation/checkout/components/FooterButton.tsx b/src/navigation/checkout/components/FooterButton.tsx index 90fdf9c63..d3913a9a3 100644 --- a/src/navigation/checkout/components/FooterButton.tsx +++ b/src/navigation/checkout/components/FooterButton.tsx @@ -1,9 +1,14 @@ import { Button, ButtonText } from '@/components/ui/button'; import { HStack } from '@/components/ui/hstack'; -import PropTypes from 'prop-types'; import React, { Component } from 'react'; -class FooterButton extends Component { +interface FooterButtonProps { + text: string; + testID?: string; + onPress?(...args: unknown[]): unknown; +} + +class FooterButton extends Component { render() { const { text, ...otherProps } = this.props; @@ -17,10 +22,4 @@ class FooterButton extends Component { } } -FooterButton.propTypes = { - text: PropTypes.string.isRequired, - testID: PropTypes.string, - onPress: PropTypes.func, -}; - export default FooterButton; diff --git a/src/navigation/checkout/components/Step.tsx b/src/navigation/checkout/components/Step.tsx index 22d6ec9d3..091e60d5f 100644 --- a/src/navigation/checkout/components/Step.tsx +++ b/src/navigation/checkout/components/Step.tsx @@ -2,80 +2,67 @@ import { Box } from '@/components/ui/box'; import { Text } from '@/components/ui/text'; import { HStack } from '@/components/ui/hstack'; import { VStack } from '@/components/ui/vstack'; -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; +import React from 'react'; import { ActivityIndicator, StyleSheet, View } from 'react-native'; import { greenColor, greyColor, redColor } from '../../../styles/common'; -class Step extends Component { - render() { - const { - active, - loading, - error, - hide, - start, - errorLabel, - activeLabel, - loadingLabel, - } = this.props; - const label = error ? errorLabel : active ? activeLabel : loadingLabel; - if (hide) { - return ; - } +interface StepProps { + start?: boolean; + active?: boolean; + loading?: boolean; + error?: boolean; + hide?: boolean; + activeLabel?: string; + loadingLabel?: string; + errorLabel?: string; +} + +const Step = ({ + start = false, + active = false, + loading = false, + error = false, + hide = false, + activeLabel = '', + loadingLabel = '', + errorLabel = '' +}: StepProps) => { + + const label = error ? errorLabel : active ? activeLabel : loadingLabel; + if (hide) { + return ; + } - return ( - - - {!start && ( - - )} + return ( + + + {!start && ( - - - {label} - {loading && ( - - )} - - - ); - } - - static defaultProps = { - start: false, - active: false, - loading: false, - error: false, - hide: false, - activeLabel: '', - loadingLabel: '', - errorLabel: '', - }; + )} + + + + {label} + {loading && ( + + )} + + + ); } -Step.propTypes = { - start: PropTypes.bool, - active: PropTypes.bool, - loading: PropTypes.bool, - error: PropTypes.bool, - hide: PropTypes.bool, - activeLabel: PropTypes.string, - loadingLabel: PropTypes.string, - errorLabel: PropTypes.string, -}; const styles = StyleSheet.create({ dot: { width: 16, diff --git a/src/navigation/checkout/components/TimingModal.tsx b/src/navigation/checkout/components/TimingModal.tsx index cdcef8698..a67fc28d1 100644 --- a/src/navigation/checkout/components/TimingModal.tsx +++ b/src/navigation/checkout/components/TimingModal.tsx @@ -2,136 +2,125 @@ import { Heading } from '@/components/ui/heading'; import { Button, ButtonText, ButtonGroup } from '@/components/ui/button'; import { Text } from '@/components/ui/text'; import { Divider } from '@/components/ui/divider'; -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; +import React, { useState } from 'react'; import { View } from 'react-native'; -import { withTranslation } from 'react-i18next'; +import { useTranslation } from 'react-i18next'; import { connect } from 'react-redux'; import BottomModal from '../../../components/BottomModal'; import { showTimingModal } from '../../../redux/Checkout/actions'; import TimingCartSelect from './TimingCartSelect'; -class TimingModal extends Component { - constructor(props) { - super(props); - this.state = { - timeSlot: {}, - closed: false, - closesSoon: false, - opensSoon: false, - value: null, - valid: true, - }; - } +interface TimingModalProps { + openingHoursSpecification: object; + fulfillmentMethods?: string[]; + orderNodeId?: string; + cartFulfillmentMethod?: string; + onFulfillmentMethodChange?(...args: unknown[]): unknown; + modalEnabled?: boolean; + isVisible?: boolean; + onSchedule?(...args: unknown[]): unknown; + onSkip?(...args: unknown[]): unknown; +} - showModal = show => this.props.showTimingModal(show); - setValue = value => this.setState({ value }); +const TimingModal = ({ + isVisible, + showTimingModal, + onSkip, + fulfillmentMethods, + onFulfillmentMethodChange, + cartFulfillmentMethod, + message, + orderNodeId, + onSchedule, + modalEnabled = true } :TimingModalProps) => { - render() { - return ( - <> - {this.props.modalEnabled && ( - { - this.showModal(false); - this.props.onSkip(); - }} - onBackButtonPress={() => { - this.showModal(false); - this.props.onSkip(); - }}> - - {this.props.t('CHECKOUT_SCHEDULE_ORDER')} - - - {this.props.fulfillmentMethods.length > 1 && ( - - - - - )} - {this.props.message && ( - {this.props.message} - )} - {!this.props.message && } - - - - - )} - - ); - } + const { t } = useTranslation() + const [ value, setValue ] = useState(null); - static defaultProps = { - modalEnabled: true, - fulfillmentMethods: [], - orderNodeId: null, - cartFulfillmentMethod: null, - onFulfillmentMethodChange: () => {}, - onSchedule: () => {}, - onSkip: () => {}, - }; -} + const showModal = show => showTimingModal(show); -TimingModal.propTypes = { - openingHoursSpecification: PropTypes.object.isRequired, - fulfillmentMethods: PropTypes.arrayOf(PropTypes.string), - orderNodeId: PropTypes.string, - cartFulfillmentMethod: PropTypes.string, - onFulfillmentMethodChange: PropTypes.func, - modalEnabled: PropTypes.bool, - onSchedule: PropTypes.func, - onSkip: PropTypes.func, -}; + return ( + <> + {modalEnabled && ( + { + showModal(false); + onSkip(); + }} + onBackButtonPress={() => { + showModal(false); + onSkip(); + }}> + + {t('CHECKOUT_SCHEDULE_ORDER')} + + + {fulfillmentMethods.length > 1 && ( + + + + + )} + {message && ( + {message} + )} + {!message && } + + + + + )} + + ); +} function mapStateToProps(state) { const { displayed, message } = state.checkout.timingModal; return { - timingModal: displayed, + isVisible: displayed, message, }; } @@ -145,4 +134,4 @@ function mapDispatchToProps(dispatch) { export default connect( mapStateToProps, mapDispatchToProps, -)(withTranslation()(TimingModal)); +)(TimingModal); diff --git a/src/navigation/checkout/components/Tips.tsx b/src/navigation/checkout/components/Tips.tsx index 8334177d0..7b6c9452d 100644 --- a/src/navigation/checkout/components/Tips.tsx +++ b/src/navigation/checkout/components/Tips.tsx @@ -1,7 +1,6 @@ import { Box } from '@/components/ui/box'; import { Button, ButtonText, ButtonGroup } from '@/components/ui/button'; import { Heading } from '@/components/ui/heading'; -import PropTypes from 'prop-types'; import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { View } from 'react-native'; @@ -16,7 +15,17 @@ const TipCart = props => ( ); -function Tips({ onTip, value = 0, values = [0, 100, 200, 400] }) { +interface TipsProps { + onTip(...args: unknown[]): unknown; + value?: number; + values?: number[]; +} + +function Tips({ + onTip, + value = 0, + values = [0, 100, 200, 400] +}: TipsProps) { const [tip, setTip] = useState(value); const [advancedView, setAdvancedView] = useState(!values.includes(tip)); const { t } = useTranslation(); @@ -73,10 +82,4 @@ function Tips({ onTip, value = 0, values = [0, 100, 200, 400] }) { ); } -Tips.propTypes = { - onTip: PropTypes.func.isRequired, - value: PropTypes.number, - values: PropTypes.arrayOf(PropTypes.number), -}; - export default Tips;