ordering-ui-react-native 0.16.66 → 0.16.69

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ordering-ui-react-native",
3
- "version": "0.16.66",
3
+ "version": "0.16.69",
4
4
  "description": "Reusable components made in react native",
5
5
  "main": "src/index.tsx",
6
6
  "author": "ordering.inc",
@@ -48,75 +48,73 @@ export const ActiveOrders = (props: ActiveOrdersParams) => {
48
48
  }, [orders.length])
49
49
 
50
50
  const Order = ({ order }: { order: any }) => (
51
- <React.Fragment>
52
- <Card
53
- isMiniCard={configs?.google_maps_api_key?.value}
54
- onPress={() => handleClickCard(order?.uuid)}
55
- >
56
- {!!(configs?.google_maps_api_key?.value) && (
57
- <Map>
51
+ <Card
52
+ isMiniCard={configs?.google_maps_api_key?.value}
53
+ onPress={() => handleClickCard(order?.uuid)}
54
+ >
55
+ {!!(configs?.google_maps_api_key?.value) && (
56
+ <Map>
57
+ <OIcon
58
+ url={getGoogleMapImage(order?.business?.location, configs?.google_maps_api_key?.value)}
59
+ height={100}
60
+ width={320}
61
+ style={{resizeMode: 'cover', borderTopRightRadius: 24, borderTopLeftRadius: 24}}
62
+ />
63
+ </Map>
64
+ )}
65
+ <Information>
66
+ {!!order.business?.logo && (
67
+ <Logo>
58
68
  <OIcon
59
- url={getGoogleMapImage(order?.business?.location, configs?.google_maps_api_key?.value)}
60
- height={100}
61
- width={320}
62
- style={{resizeMode: 'cover', borderTopRightRadius: 24, borderTopLeftRadius: 24}}
69
+ url={optimizeImage(order.business?.logo, 'h_300,c_limit')}
70
+ style={styles.logo}
63
71
  />
64
- </Map>
72
+ </Logo>
65
73
  )}
66
- <Information>
67
- {!!order.business?.logo && (
68
- <Logo>
69
- <OIcon
70
- url={optimizeImage(order.business?.logo, 'h_300,c_limit')}
71
- style={styles.logo}
72
- />
73
- </Logo>
74
- )}
75
- <OrderInformation>
76
- <BusinessInformation style={{ width: '60%' }}>
77
- <GestureHandlerScrollView
78
- showsVerticalScrollIndicator={false}
79
- showsHorizontalScrollIndicator={false}
80
- horizontal
81
- >
82
- <TouchableWithoutFeedback>
83
- <View>
84
- <OText
85
- size={16}
86
- numberOfLines={1}
87
- ellipsizeMode='tail'
88
- >
89
- {order.business?.name}
90
- </OText>
91
- </View>
92
- </TouchableWithoutFeedback>
93
- </GestureHandlerScrollView>
94
- <GestureHandlerScrollView
74
+ <OrderInformation>
75
+ <BusinessInformation style={{ width: '60%' }}>
76
+ <GestureHandlerScrollView
95
77
  showsVerticalScrollIndicator={false}
96
78
  showsHorizontalScrollIndicator={false}
97
79
  horizontal
98
- >
99
- <TouchableWithoutFeedback>
100
- <View style={styles.orderNumber}>
101
- <OText size={12} space color={theme.colors.textSecondary}>{t('ORDER_NUMBER', 'Order No.')}</OText>
102
- <OText size={12} color={theme.colors.textSecondary}>{order.id}</OText>
103
- </View>
104
- </TouchableWithoutFeedback>
105
- </GestureHandlerScrollView>
106
- <OText size={12} color={theme.colors.textSecondary}>{order?.delivery_datetime_utc
107
- ? parseDate(order?.delivery_datetime_utc)
108
- : parseDate(order?.delivery_datetime, { utc: false })}</OText>
109
- </BusinessInformation>
110
- <Price>
111
- <OText size={16}>{parsePrice(order?.summary?.total || order?.total)}</OText>
112
- {order?.status !== 0 && (
113
- <OText color={theme.colors.primary} size={12} numberOfLines={2}>{getOrderStatus(order.status)?.value}</OText>
114
- )}
115
- </Price>
116
- </OrderInformation>
117
- </Information>
118
- </Card>
119
- </React.Fragment>
80
+ >
81
+ <TouchableWithoutFeedback>
82
+ <View>
83
+ <OText
84
+ size={16}
85
+ numberOfLines={1}
86
+ ellipsizeMode='tail'
87
+ >
88
+ {order.business?.name}
89
+ </OText>
90
+ </View>
91
+ </TouchableWithoutFeedback>
92
+ </GestureHandlerScrollView>
93
+ <GestureHandlerScrollView
94
+ showsVerticalScrollIndicator={false}
95
+ showsHorizontalScrollIndicator={false}
96
+ horizontal
97
+ >
98
+ <TouchableWithoutFeedback>
99
+ <View style={styles.orderNumber}>
100
+ <OText size={12} space color={theme.colors.textSecondary}>{t('ORDER_NUMBER', 'Order No.')}</OText>
101
+ <OText size={12} color={theme.colors.textSecondary}>{order.id}</OText>
102
+ </View>
103
+ </TouchableWithoutFeedback>
104
+ </GestureHandlerScrollView>
105
+ <OText size={12} color={theme.colors.textSecondary}>{order?.delivery_datetime_utc
106
+ ? parseDate(order?.delivery_datetime_utc)
107
+ : parseDate(order?.delivery_datetime, { utc: false })}</OText>
108
+ </BusinessInformation>
109
+ <Price>
110
+ <OText size={16}>{parsePrice(order?.summary?.total || order?.total)}</OText>
111
+ {order?.status !== 0 && (
112
+ <OText color={theme.colors.primary} size={12} numberOfLines={2}>{getOrderStatus(order.status)?.value}</OText>
113
+ )}
114
+ </Price>
115
+ </OrderInformation>
116
+ </Information>
117
+ </Card>
120
118
  )
121
119
 
122
120
  return (
@@ -2,7 +2,7 @@ import styled from 'styled-components/native'
2
2
 
3
3
  export const ActiveOrdersContainer = styled.ScrollView`
4
4
  margin-bottom: 20px;
5
- height: ${({ isMiniCards }: { isMiniCards: boolean }) => !isMiniCards ? '150px' : '220px'};
5
+ height: ${({ isMiniCards }: { isMiniCards: boolean }) => !isMiniCards ? '150px' : '450px'};
6
6
  max-height: ${({ isMiniCards }: { isMiniCards: boolean }) => !isMiniCards ? '150px' : '220px'};
7
7
  `
8
8
 
@@ -0,0 +1,85 @@
1
+ import React, { useEffect } from 'react';
2
+ import { useTheme } from 'styled-components/native';
3
+ import { StyleSheet, View } from 'react-native';
4
+ import {
5
+ openSettings,
6
+ checkNotifications
7
+ } from 'react-native-permissions';
8
+ import { useLanguage } from 'ordering-components/native'
9
+ import { OBottomPopup, OButton, OText } from '../shared';
10
+ interface NotificationSettingPropsParams {
11
+ checkNotificationStatus: { open: boolean, checked: boolean };
12
+ setCheckNotificationStatus: (notificationStatus: any) => void;
13
+ }
14
+ export const NotificationSetting = (props: NotificationSettingPropsParams) => {
15
+ const { checkNotificationStatus, setCheckNotificationStatus } = props
16
+ const theme = useTheme();
17
+ const [, t] = useLanguage();
18
+
19
+ const requestLocationPermission = async () => {
20
+ const notificationStatus = await checkNotifications()
21
+ if (notificationStatus?.status === 'blocked') {
22
+ setCheckNotificationStatus({ open: true, checked: false })
23
+ return
24
+ }
25
+ setCheckNotificationStatus({ open: false, checked: true })
26
+ };
27
+
28
+ const callOpenSettings = () => {
29
+ openSettings().catch(() => console.warn('cannot open settings'));
30
+ setCheckNotificationStatus({ open: false, checked: true })
31
+ }
32
+
33
+ useEffect(() => {
34
+ requestLocationPermission()
35
+ }, [])
36
+ return (
37
+ <OBottomPopup
38
+ open={checkNotificationStatus?.open}
39
+ onClose={() => setCheckNotificationStatus({ open: false, checked: true })}
40
+ title={t('ENABLE_NOTIFICATIONS', 'Enable notifications')}
41
+ titleStyle={{ textAlign: 'center' }}
42
+ containerStyle={{ borderRadius: 10, borderTopRightRadius: 10, borderTopLeftRadius: 10 }}
43
+ >
44
+ <View style={styles.container}>
45
+ <View style={styles.textContainer}>
46
+ <OText style={{ textAlign: 'center' }}>
47
+ {t('ACTIVE_NOTIFICATION_TO_RECEIVE_INFORMATION', 'Activate notifications to receive information about your orders')}
48
+ </OText>
49
+ </View>
50
+ <OButton
51
+ text={t('ENABLE_NOTIFICATIONS', 'Enable notifications')}
52
+ bgColor={theme.colors.primary}
53
+ borderColor={theme.colors.primary}
54
+ parentStyle={styles.parentStyle}
55
+ style={styles.button}
56
+ textStyle={{ color: 'white' }}
57
+ onClick={() => callOpenSettings()}
58
+ />
59
+ </View>
60
+ </OBottomPopup>
61
+ );
62
+ };
63
+
64
+ const styles = StyleSheet.create({
65
+ container: {
66
+ width: '100%',
67
+ textAlign: 'center'
68
+ },
69
+ textContainer: {
70
+ width: '100%',
71
+ paddingHorizontal: 25,
72
+ paddingBottom: 20
73
+ },
74
+ parentStyle: {
75
+ display: 'flex',
76
+ width: '70%',
77
+ marginLeft: 'auto',
78
+ marginRight: 'auto',
79
+ marginBottom: 25
80
+
81
+ },
82
+ button: {
83
+ borderRadius: 5
84
+ }
85
+ });
@@ -4,14 +4,18 @@ const deviceHeight = Dimensions.get('window').height
4
4
 
5
5
  interface Props {
6
6
  open: boolean;
7
+ containerStyle: any;
7
8
  title?: string;
9
+ titleStyle?: any;
8
10
  children?: any;
9
11
  onClose?: any;
10
12
  }
11
13
  const OBottomPopup = (props: Props) => {
12
14
  const {
13
15
  open,
16
+ containerStyle,
14
17
  title,
18
+ titleStyle,
15
19
  onClose,
16
20
  children
17
21
  } = props
@@ -29,9 +33,9 @@ const OBottomPopup = (props: Props) => {
29
33
  >
30
34
  <View style={styles.touchableOutsideStyle} />
31
35
  </TouchableWithoutFeedback>
32
- <View style={styles.bottomContainer}>
36
+ <View style={{ ...styles.bottomContainer, ...containerStyle }}>
33
37
  <View>
34
- <Text style={styles.titleStyle}>
38
+ <Text style={{ ...styles.titleStyle, ...titleStyle }}>
35
39
  {title}
36
40
  </Text>
37
41
  {children}
package/src/index.tsx CHANGED
@@ -36,6 +36,7 @@ import { LogoutButton } from './components/LogoutButton';
36
36
  import { Messages } from './components/Messages';
37
37
  import { MomentOption } from './components/MomentOption';
38
38
  import NavBar from './components/NavBar';
39
+ import { NotificationSetting } from './components/NotificationSetting'
39
40
  import { NotFoundSource } from './components/NotFoundSource';
40
41
  import { OrderCreating } from './components/OrderCreating';
41
42
  import { OrderDetails } from './components/OrderDetails';
@@ -136,6 +137,7 @@ export {
136
137
  Messages,
137
138
  MomentOption,
138
139
  NavBar,
140
+ NotificationSetting,
139
141
  NotFoundSource,
140
142
  OrderCreating,
141
143
  OrderDetails,
@@ -6,7 +6,7 @@ import FontistoIcon from 'react-native-vector-icons/Fontisto'
6
6
  import FeatherIcon from 'react-native-vector-icons/Feather';
7
7
  import SelectDropdown from 'react-native-select-dropdown'
8
8
  import { useTheme } from 'styled-components/native';
9
-
9
+ import { NotificationSetting } from '../../../../../src/components/NotificationSetting'
10
10
  import {
11
11
  FiltersTab,
12
12
  TabsContainer,
@@ -611,7 +611,7 @@ const OrdersListManagerUI = (props: OrdersOptionParams) => {
611
611
  </LeftSide>
612
612
 
613
613
  <RightSide style={{ paddingBottom: 110, paddingHorizontal: 20 }}>
614
- {currentOrderSelected && (
614
+ {currentOrderSelected && (
615
615
  <OrderDetailsBusiness {...props.orderDetailsProps} order={currentOrderSelected} isCustomView />
616
616
  )}
617
617
  </RightSide>
@@ -756,6 +756,7 @@ const OrdersListManagerUI = (props: OrdersOptionParams) => {
756
756
 
757
757
  export const OrdersListManager = (props: OrdersOptionParams) => {
758
758
  const [, t] = useLanguage();
759
+ const [checkNotificationStatus, setCheckNotificationStatus] = useState({ open: false, checked: false })
759
760
  const ordersProps = {
760
761
  ...props,
761
762
  UIComponent: OrdersListManagerUI,
@@ -870,5 +871,11 @@ export const OrdersListManager = (props: OrdersOptionParams) => {
870
871
  ]
871
872
  };
872
873
 
873
- return <OrderListGroups {...ordersProps} />;
874
+ return (<>
875
+ <OrderListGroups {...ordersProps} />
876
+ {props?.checkNotification && (
877
+ <NotificationSetting checkNotificationStatus={checkNotificationStatus}
878
+ setCheckNotificationStatus={setCheckNotificationStatus} />
879
+ )}
880
+ </>);
874
881
  };
@@ -7,6 +7,7 @@ import FeatherIcon from 'react-native-vector-icons/Feather';
7
7
  import FontistoIcon from 'react-native-vector-icons/Fontisto'
8
8
  import { useTheme } from 'styled-components/native';
9
9
  import { DeviceOrientationMethods } from '../../../../../src/hooks/DeviceOrientation'
10
+ import { NotificationSetting } from '../../../../../src/components/NotificationSetting'
10
11
  import { NewOrderNotification } from '../NewOrderNotification';
11
12
 
12
13
  import { OText, OButton, OModal, OIconButton, OInput, OIcon } from '../shared';
@@ -726,11 +727,11 @@ const OrdersOptionUI = (props: OrdersOptionParams) => {
726
727
  </ScrollView>
727
728
  </View>
728
729
  {/* </GestureRecognizer> */}
729
-
730
+
730
731
  {isBusinessApp && (
731
732
  <NewOrderNotification isBusinessApp={isBusinessApp} />
732
733
  )}
733
-
734
+
734
735
  {(openSearchModal || openSLASettingModal) && (
735
736
  <OModal open={openSearchModal || openSLASettingModal} entireModal customClose>
736
737
  <ModalContainer
@@ -935,6 +936,7 @@ export const OrdersOption = (props: OrdersOptionParams) => {
935
936
  const [, t] = useLanguage();
936
937
  const [configState] = useConfig()
937
938
  const theme = useTheme()
939
+ const [checkNotificationStatus, setCheckNotificationStatus] = useState({ open: false, checked: false })
938
940
  const ordersProps = {
939
941
  ...props,
940
942
  UIComponent: OrdersOptionUI,
@@ -1049,5 +1051,11 @@ export const OrdersOption = (props: OrdersOptionParams) => {
1049
1051
  ]
1050
1052
  };
1051
1053
 
1052
- return <OrderListGroups {...ordersProps} />;
1054
+ return (<>
1055
+ <OrderListGroups {...ordersProps} />
1056
+ {props?.checkNotification && (
1057
+ <NotificationSetting checkNotificationStatus={checkNotificationStatus}
1058
+ setCheckNotificationStatus={setCheckNotificationStatus} />
1059
+ )}
1060
+ </>);
1053
1061
  };
@@ -21,8 +21,8 @@ export interface LoginParams {
21
21
  allowedLevels?: any;
22
22
  useRootPoint?: any;
23
23
  notificationState?: any;
24
- handleReCaptcha?: any;
25
- enableReCaptcha?: any;
24
+ handleReCaptcha?: any;
25
+ enableReCaptcha?: any;
26
26
  }
27
27
  export interface ProfileParams {
28
28
  navigation?: any;
@@ -266,7 +266,7 @@ export interface OrdersOptionParams {
266
266
  titleContent?: string;
267
267
  customArray?: Array<any>;
268
268
  loadMoreOrders?: () => {};
269
- loadOrders?: ({}: any) => {};
269
+ loadOrders?: ({ }: any) => {};
270
270
  messages?: any;
271
271
  setMessages?: () => {};
272
272
  loadMessages?: () => {};
@@ -280,7 +280,7 @@ export interface OrdersOptionParams {
280
280
  ordersGroup?: any;
281
281
  setOrdersGroup?: any;
282
282
  setCurrentFilters?: any;
283
- onFiltered?: ({}: any) => {};
283
+ onFiltered?: ({ }: any) => {};
284
284
  filtered?: any;
285
285
  handleClickOrder?: any;
286
286
  orderGroupStatusCustom?: {
@@ -291,10 +291,11 @@ export interface OrdersOptionParams {
291
291
  };
292
292
  isBusinessApp?: boolean;
293
293
  handleClickLogisticOrder: (status: number, orderId: number) => void,
294
- logisticOrders: {orders: Array<any>, loading: boolean, error: Array<string> | string},
294
+ logisticOrders: { orders: Array<any>, loading: boolean, error: Array<string> | string },
295
295
  loadLogisticOrders: () => void;
296
296
  isLogisticActivated?: boolean;
297
297
  isAlsea?: boolean;
298
+ checkNotification?: boolean;
298
299
  }
299
300
  export interface ActiveOrdersParams {
300
301
  orders?: any;
@@ -563,13 +564,13 @@ export interface AcceptOrRejectOrderParams {
563
564
  }
564
565
 
565
566
  export interface MapViewParams {
566
- onNavigationRedirect: (page : string, params ?: any) => void,
567
+ onNavigationRedirect: (page: string, params?: any) => void,
567
568
  getBusinessLocations: () => void,
568
569
  isLoadingBusinessMarkers?: boolean,
569
570
  markerGroups: Array<any>,
570
571
  customerMarkerGroups: Array<any>,
571
572
  alertState: { open: boolean, content: Array<string>, key?: string | null },
572
- setAlertState: ({open, content, key} : { open: boolean, content: Array<string>, key?: string | null }) => void
573
+ setAlertState: ({ open, content, key }: { open: boolean, content: Array<string>, key?: string | null }) => void
573
574
  }
574
575
 
575
576
  export interface ReviewCustomerParams {
@@ -59,6 +59,7 @@ import { PaymentOptions } from './src/components/PaymentOptions';
59
59
  import { DriverTips } from './src/components/DriverTips';
60
60
  import { UserDetails } from './src/components/UserDetails';
61
61
  import { OrderSummary } from './src/components/OrderSummary';
62
+ import { OrderItAgain } from './src/components/OrderItAgain';
62
63
  import { CartStoresListing } from './src/components/CartStoresListing';
63
64
  import { PaymentOptionsWebView } from '../../src/components/PaymentOptionsWebView';
64
65
  import { GoogleMap } from './src/components/GoogleMap';
@@ -219,6 +220,7 @@ export {
219
220
  DriverTips,
220
221
  UserDetails,
221
222
  OrderSummary,
223
+ OrderItAgain,
222
224
  CartStoresListing,
223
225
  PaymentOptionsWebView,
224
226
  GoogleMap,
@@ -1,4 +1,4 @@
1
- import React, { useState } from 'react';
1
+ import React, { useState, useRef, useEffect } from 'react';
2
2
  import { Fade, Placeholder, PlaceholderLine } from 'rn-placeholder';
3
3
  import {
4
4
  BusinessController as BusinessSingleCard,
@@ -11,7 +11,7 @@ import {
11
11
  ToastType
12
12
  } from 'ordering-components/native';
13
13
  import { OIcon, OText } from '../shared';
14
- import { StyleSheet, TouchableOpacity, View } from 'react-native';
14
+ import { StyleSheet, TouchableOpacity, View, Animated } from 'react-native';
15
15
  import { InView } from 'react-native-intersection-observer'
16
16
  import { BusinessControllerParams } from '../../types';
17
17
  import { convertHoursToMinutes, shape } from '../../utils';
@@ -59,6 +59,8 @@ export const BusinessControllerUI = (props: BusinessControllerParams) => {
59
59
  const [, t] = useLanguage();
60
60
  const theme = useTheme()
61
61
  const [isIntersectionObserver, setIsIntersectionObserver] = useState(!enableIntersection)
62
+ const fadeAnim = useRef(new Animated.Value(0)).current;
63
+
62
64
  const styles = StyleSheet.create({
63
65
  headerStyle: {
64
66
  borderTopLeftRadius: 7.6,
@@ -144,8 +146,25 @@ export const BusinessControllerUI = (props: BusinessControllerParams) => {
144
146
  }
145
147
  }
146
148
 
149
+ const fadeIn = () => {
150
+ Animated.timing(fadeAnim, {
151
+ toValue: 1,
152
+ duration: 500,
153
+ useNativeDriver: true
154
+ }).start();
155
+ };
156
+
157
+ const handleChangeInterSection = (inView: boolean) => {
158
+ setIsIntersectionObserver(inView)
159
+ fadeIn()
160
+ }
161
+
162
+ useEffect(() => {
163
+ if (!enableIntersection) fadeIn()
164
+ }, [enableIntersection])
165
+
147
166
  return (
148
- <InView style={{ minHeight: 200 }} triggerOnce={true} onChange={(inView: boolean) => setIsIntersectionObserver(inView)}>
167
+ <InView style={{ minHeight: 200 }} triggerOnce={true} onChange={(inView: boolean) => handleChangeInterSection(inView)}>
149
168
  {isIntersectionObserver ? (
150
169
  <Card activeOpacity={1} onPress={() => handleBusinessClick(business)} style={style}>
151
170
  {business?.ribbon?.enabled && (
@@ -167,6 +186,13 @@ export const BusinessControllerUI = (props: BusinessControllerParams) => {
167
186
  </RibbonBox>
168
187
  )}
169
188
  <BusinessHero>
189
+ <Animated.View
190
+ style={[
191
+ {
192
+ opacity: fadeAnim
193
+ }
194
+ ]}
195
+ >
170
196
  <FastImage
171
197
  style={{ height: 120 }}
172
198
  source={{
@@ -175,6 +201,7 @@ export const BusinessControllerUI = (props: BusinessControllerParams) => {
175
201
  }}
176
202
  resizeMode={FastImage.resizeMode.cover}
177
203
  />
204
+ </Animated.View>
178
205
  {(businessFeatured ?? business?.featured) && (
179
206
  <View style={styles.featured}>
180
207
  <FontAwesomeIcon name="crown" size={26} color="gold" />
@@ -11,6 +11,7 @@ import { StyleSheet } from 'react-native';
11
11
  import { useTheme } from 'styled-components/native';
12
12
  import { shape } from '../../utils'
13
13
  import { CategoryDescriptionMemoized } from './CategoryDescription';
14
+ import { OrderItAgain } from '../OrderItAgain'
14
15
 
15
16
  const BusinessProductsListUI = (props: BusinessProductsListParams) => {
16
17
  const {
@@ -35,6 +36,7 @@ const BusinessProductsListUI = (props: BusinessProductsListParams) => {
35
36
  onClickCategory,
36
37
  lazyLoadProductsRecommended,
37
38
  handleUpdateProducts,
39
+ previouslyProducts,
38
40
  isFiltMode,
39
41
  navigation
40
42
  } = props;
@@ -113,6 +115,17 @@ const BusinessProductsListUI = (props: BusinessProductsListParams) => {
113
115
  <SubcategoriesComponent category={category} />
114
116
  )}
115
117
  </HeaderWrapper>
118
+ {previouslyProducts?.length > 0 && (
119
+ <OrderItAgain
120
+ onProductClick={onProductClick}
121
+ productList={previouslyProducts}
122
+ businessId={businessId}
123
+ categoryState={categoryState}
124
+ navigation={navigation}
125
+ handleUpdateProducts={handleUpdateProducts}
126
+ currentCart={currentCart}
127
+ />
128
+ )}
116
129
  {category.id &&
117
130
  categoryState.products
118
131
  ?.filter((product: any) =>
@@ -123,6 +136,7 @@ const BusinessProductsListUI = (props: BusinessProductsListParams) => {
123
136
  <SingleProductCard
124
137
  key={'prod_' + product.id + `_${i}`}
125
138
  isSoldOut={product.inventoried && !product.quantity}
139
+ enableIntersection
126
140
  product={product}
127
141
  businessId={businessId}
128
142
  categoryState={categoryState}
@@ -150,6 +164,7 @@ const BusinessProductsListUI = (props: BusinessProductsListParams) => {
150
164
  key={'feat_' + product.id + `_${i}`}
151
165
  isSoldOut={product.inventoried && !product.quantity}
152
166
  product={product}
167
+ enableIntersection
153
168
  businessId={businessId}
154
169
  categoryState={categoryState}
155
170
  onProductClick={onProductClick}
@@ -323,6 +323,7 @@ const BusinessProductsListingUI = (props: BusinessProductsListingParams) => {
323
323
  setSubcategoriesSelected={setSubcategoriesSelected}
324
324
  onClickCategory={handleChangeCategory}
325
325
  handleUpdateProducts={handleUpdateProducts}
326
+ previouslyProducts={business?.previously_products}
326
327
  navigation={navigation}
327
328
  isFiltMode
328
329
  />
@@ -427,6 +428,7 @@ const BusinessProductsListingUI = (props: BusinessProductsListingParams) => {
427
428
  onClickCategory={handleChangeCategory}
428
429
  handleUpdateProducts={handleUpdateProducts}
429
430
  navigation={navigation}
431
+ previouslyProducts={business?.previously_products}
430
432
  />
431
433
  </WrapContent>
432
434
  </>
@@ -7,6 +7,7 @@ import { BusinessesListing as OriginalBusinessListing } from './Layout/Original'
7
7
  import { BusinessesListing as AppointmentBusinessListing } from './Layout/Appointment'
8
8
  import { OBottomPopup } from '../shared';
9
9
  import { ReviewTrigger } from '../ReviewTrigger';
10
+ import { NotificationSetting } from '../../../../../src/components/NotificationSetting';
10
11
 
11
12
  export const BusinessesListing = (props: any) => {
12
13
  const { logosLayout } = props
@@ -18,6 +19,7 @@ export const BusinessesListing = (props: any) => {
18
19
  const [, { getLastOrderHasNoReview }] = useOrder();
19
20
 
20
21
  const [, setIsReviewed] = useState()
22
+ const [checkNotificationStatus, setCheckNotificationStatus] = useState({ open: false, checked: false })
21
23
  const defaultOrder = {
22
24
  id: 0,
23
25
  business_id: 0,
@@ -72,9 +74,11 @@ export const BusinessesListing = (props: any) => {
72
74
  )
73
75
  }
74
76
 
77
+
78
+
75
79
  useEffect(() => {
76
- auth && _getLastOrderHasNoReview()
77
- }, [auth])
80
+ (checkNotificationStatus?.checked && auth) && _getLastOrderHasNoReview()
81
+ }, [checkNotificationStatus, auth])
78
82
 
79
83
  return (
80
84
  <>
@@ -92,9 +96,10 @@ export const BusinessesListing = (props: any) => {
92
96
  closeIcon={theme.images.general.close}
93
97
  >
94
98
  {lastOrderReview?.order && <ReviewTrigger order={lastOrderReview?.order} handleOpenOrderReview={handleOpenOrderReview} />}
95
-
96
99
  </OBottomPopup>
97
100
  )}
101
+ <NotificationSetting checkNotificationStatus={checkNotificationStatus}
102
+ setCheckNotificationStatus={setCheckNotificationStatus} />
98
103
  </>
99
104
  )
100
105
  }
@@ -0,0 +1,72 @@
1
+ import React from 'react'
2
+ import { OText } from '../shared'
3
+ import { useLanguage } from 'ordering-components/native'
4
+ import { useTheme } from 'styled-components/native'
5
+ import { SingleProductCard } from '../SingleProductCard'
6
+ import { OrderItAgainParams } from '../../types'
7
+ import { ScrollView, Dimensions } from 'react-native'
8
+ import moment from 'moment';
9
+ import {
10
+ Container,
11
+ ProductWrapper
12
+ } from './styles'
13
+
14
+ export const OrderItAgain = (props: OrderItAgainParams) => {
15
+ const {
16
+ onProductClick,
17
+ productList,
18
+ businessId,
19
+ categoryState,
20
+ currentCart,
21
+ handleUpdateProducts,
22
+ navigation
23
+ } = props
24
+
25
+ const [, t] = useLanguage()
26
+ const theme = useTheme()
27
+ const { width } = Dimensions.get('window');
28
+
29
+ return (
30
+ <Container>
31
+ <OText
32
+ size={16}
33
+ lineHeight={24}
34
+ color={theme.colors.textNormal}
35
+ style={{
36
+ fontWeight: '600',
37
+ marginBottom: 6
38
+ }}
39
+ >
40
+ {t('ORDER_IT_AGAIN', 'Order it again')}
41
+ </OText>
42
+ <OText
43
+ size={12}
44
+ lineHeight={18}
45
+ color={theme.colors.disabled}
46
+ >
47
+ {t('ORDER_IT_AGAIN_DESC', 'Quickly add items from your past orders.')}
48
+ </OText>
49
+ <ScrollView
50
+ horizontal
51
+ showsVerticalScrollIndicator={false}
52
+ showsHorizontalScrollIndicator={false}
53
+ >
54
+ {productList?.length > 0 && productList?.sort((a: any, b:any) => moment(b?.last_ordered_date).valueOf() - moment(a?.last_ordered_date).valueOf()).map((product: any, i: number) => (
55
+ <ProductWrapper key={'prod_' + product.id + `_${i}`} style={{ width: width - 120, }}>
56
+ <SingleProductCard
57
+ isSoldOut={product.inventoried && !product.quantity}
58
+ product={product}
59
+ businessId={businessId}
60
+ categoryState={categoryState}
61
+ onProductClick={() => onProductClick(product)}
62
+ productAddedToCartLength={currentCart?.products?.reduce((productsLength: number, Cproduct: any) => { return productsLength + (Cproduct?.id === product?.id ? Cproduct?.quantity : 0) }, 0)}
63
+ handleUpdateProducts={handleUpdateProducts}
64
+ navigation={navigation}
65
+ isPreviously
66
+ />
67
+ </ProductWrapper>
68
+ ))}
69
+ </ScrollView>
70
+ </Container>
71
+ )
72
+ }
@@ -0,0 +1,10 @@
1
+ import styled from 'styled-components/native'
2
+
3
+ export const Container = styled.View`
4
+ margin-top: 20px;
5
+ `
6
+
7
+ export const ProductWrapper = styled.View`
8
+ margin-right: 20px;
9
+ padding-top: 30px;
10
+ `
@@ -1,4 +1,4 @@
1
- import React, { useState } from 'react';
1
+ import React, { useState, useRef, useEffect } from 'react';
2
2
  import {
3
3
  useLanguage,
4
4
  useConfig,
@@ -11,7 +11,7 @@ import {
11
11
  import { useTheme } from 'styled-components/native';
12
12
  import { SingleProductCardParams } from '../../types';
13
13
  import { CardContainer, CardInfo, SoldOut, QuantityContainer, PricesContainer, RibbonBox, LogoWrapper } from './styles';
14
- import { StyleSheet, View, TouchableOpacity, Image } from 'react-native';
14
+ import { StyleSheet, View, TouchableOpacity, Image, Animated } from 'react-native';
15
15
  import { InView } from 'react-native-intersection-observer'
16
16
  import { Fade, Placeholder, PlaceholderLine } from 'rn-placeholder';
17
17
  import { OButton, OText } from '../shared';
@@ -26,7 +26,7 @@ function SingleProductCardPropsAreEqual(prevProps: any, nextProps: any) {
26
26
  prevProps.categoryState === nextProps.categoryState
27
27
  }
28
28
 
29
- const SinguleProductCardUI = React.memo((props: SingleProductCardParams) => {
29
+ const SingleProductCardUI = React.memo((props: SingleProductCardParams) => {
30
30
  const {
31
31
  product,
32
32
  isSoldOut,
@@ -36,13 +36,16 @@ const SinguleProductCardUI = React.memo((props: SingleProductCardParams) => {
36
36
  handleFavoriteProduct,
37
37
  enableIntersection,
38
38
  navigation,
39
- businessId
39
+ businessId,
40
+ isPreviously
40
41
  } = props;
41
42
 
42
43
  const theme = useTheme();
43
44
  const [orderingTheme] = useOrderingTheme()
44
45
  const hideAddButton = orderingTheme?.theme?.business_view?.components?.products?.components?.add_to_cart_button?.hidden
45
46
 
47
+ const fadeAnim = useRef(new Animated.Value(0)).current;
48
+
46
49
  const styles = StyleSheet.create({
47
50
  container: {
48
51
  borderWidth: 1,
@@ -94,7 +97,7 @@ const SinguleProductCardUI = React.memo((props: SingleProductCardParams) => {
94
97
  const [, t] = useLanguage();
95
98
  const [stateConfig] = useConfig();
96
99
  const [{ auth }] = useSession()
97
- const [{ parsePrice, optimizeImage }] = useUtils();
100
+ const [{ parsePrice, optimizeImage, parseDate }] = useUtils();
98
101
  const [orderState] = useOrder()
99
102
  const [isIntersectionObserver, setIsIntersectionObserver] = useState(!enableIntersection)
100
103
 
@@ -122,6 +125,14 @@ const SinguleProductCardUI = React.memo((props: SingleProductCardParams) => {
122
125
  maxCartProductInventory,
123
126
  );
124
127
 
128
+ const fadeIn = () => {
129
+ Animated.timing(fadeAnim, {
130
+ toValue: 1,
131
+ duration: 500,
132
+ useNativeDriver: true
133
+ }).start();
134
+ };
135
+
125
136
  const handleChangeFavorite = () => {
126
137
  if (auth) {
127
138
  handleFavoriteProduct && handleFavoriteProduct(!product?.favorite)
@@ -130,8 +141,17 @@ const SinguleProductCardUI = React.memo((props: SingleProductCardParams) => {
130
141
  }
131
142
  }
132
143
 
144
+ const handleChangeIntersection = () => {
145
+ setIsIntersectionObserver(true);
146
+ fadeIn();
147
+ }
148
+
149
+ useEffect(() => {
150
+ if (!enableIntersection) fadeIn()
151
+ }, [enableIntersection])
152
+
133
153
  return (
134
- <InView style={{ minHeight: 200 }} triggerOnce={true} onChange={(inView: boolean) => setIsIntersectionObserver(true)}>
154
+ <InView style={{ minHeight: 200 }} triggerOnce={true} onChange={(inView: boolean) => handleChangeIntersection()}>
135
155
  {isIntersectionObserver ? (
136
156
  <CardContainer
137
157
  showAddButton={!hideAddButton}
@@ -160,15 +180,17 @@ const SinguleProductCardUI = React.memo((props: SingleProductCardParams) => {
160
180
  style={{ ...styles.line18, flex: 1 }}>
161
181
  {product?.name}
162
182
  </OText>
163
- <TouchableOpacity
164
- onPress={handleChangeFavorite}
165
- >
166
- <IconAntDesign
167
- name={product?.favorite ? 'heart' : 'hearto'}
168
- color={theme.colors.danger5}
169
- size={18}
170
- />
171
- </TouchableOpacity>
183
+ {!isPreviously && (
184
+ <TouchableOpacity
185
+ onPress={handleChangeFavorite}
186
+ >
187
+ <IconAntDesign
188
+ name={product?.favorite ? 'heart' : 'hearto'}
189
+ color={theme.colors.danger5}
190
+ size={18}
191
+ />
192
+ </TouchableOpacity>
193
+ )}
172
194
  </View>
173
195
  <PricesContainer>
174
196
  <OText color={theme.colors.primary}>{product?.price ? parsePrice(product?.price) : ''}</OText>
@@ -178,12 +200,22 @@ const SinguleProductCardUI = React.memo((props: SingleProductCardParams) => {
178
200
  </PricesContainer>
179
201
  <OText
180
202
  size={10}
181
- numberOfLines={2}
203
+ numberOfLines={!isPreviously ? 2 : 1}
182
204
  ellipsizeMode="tail"
183
205
  color={theme.colors.textSecondary}
184
206
  style={styles.line15}>
185
207
  {product?.description}
186
208
  </OText>
209
+ {isPreviously && (
210
+ <OText
211
+ size={10}
212
+ numberOfLines={1}
213
+ ellipsizeMode="tail"
214
+ color={theme.colors.primary}
215
+ style={styles.line15}>
216
+ {t('LAST_ORDERED_ON', 'Last ordered on')} {parseDate(product?.last_ordered_date, { outputFormat: 'MMM DD, YYYY' })}
217
+ </OText>
218
+ )}
187
219
  </CardInfo>
188
220
  <LogoWrapper>
189
221
  {product?.ribbon?.enabled && (
@@ -205,14 +237,23 @@ const SinguleProductCardUI = React.memo((props: SingleProductCardParams) => {
205
237
  </RibbonBox>
206
238
  )}
207
239
  {product?.images && (
208
- <FastImage
209
- style={styles.productStyle}
210
- source={{
211
- uri: optimizeImage(product?.images, 'h_250,c_limit'),
212
- priority: FastImage.priority.normal,
213
- }}
214
- resizeMode={FastImage.resizeMode.cover}
215
- />
240
+ <Animated.View
241
+ style={[
242
+ {
243
+ // Bind opacity to animated value
244
+ opacity: fadeAnim
245
+ }
246
+ ]}
247
+ >
248
+ <FastImage
249
+ style={styles.productStyle}
250
+ source={{
251
+ uri: optimizeImage(product?.images, 'h_250,c_limit'),
252
+ priority: FastImage.priority.normal,
253
+ }}
254
+ resizeMode={FastImage.resizeMode.cover}
255
+ />
256
+ </Animated.View>
216
257
  )}
217
258
  </LogoWrapper>
218
259
 
@@ -262,7 +303,7 @@ const SinguleProductCardUI = React.memo((props: SingleProductCardParams) => {
262
303
  export const SingleProductCard = (props: SingleProductCardParams) => {
263
304
  const singleProductCardProps = {
264
305
  ...props,
265
- UIComponent: SinguleProductCardUI
306
+ UIComponent: SingleProductCardUI
266
307
  }
267
308
  return <SingleProductCardController {...singleProductCardProps} />
268
309
  }
@@ -290,6 +290,7 @@ export interface BusinessProductsListParams {
290
290
  isFiltMode?: boolean,
291
291
  handleUpdateProducts?: any,
292
292
  navigation?: any;
293
+ previouslyProducts?: any;
293
294
  }
294
295
  export interface SingleProductCardParams {
295
296
  businessId: any;
@@ -303,6 +304,7 @@ export interface SingleProductCardParams {
303
304
  handleUpdateProducts?: any;
304
305
  enableIntersection?: boolean;
305
306
  navigation?: any;
307
+ isPreviously?: any;
306
308
  }
307
309
  export interface BusinessInformationParams {
308
310
  navigation?: any,
@@ -737,6 +739,16 @@ export interface ProfessionalProfileParams {
737
739
  onClose: any
738
740
  }
739
741
 
742
+ export interface OrderItAgainParams {
743
+ onProductClick: any,
744
+ productList: any,
745
+ businessId: any,
746
+ categoryState: any,
747
+ currentCart: any,
748
+ handleUpdateProducts: any,
749
+ navigation: any
750
+ }
751
+
740
752
  export interface PreviousProductsOrderedParams {
741
753
  products?: any,
742
754
  onProductClick?: any,