ordering-ui-react-native 0.16.65 → 0.16.68
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 +1 -1
- package/src/components/NotificationSetting/index.tsx +85 -0
- package/src/components/shared/OBottomPopup.tsx +6 -2
- package/src/index.tsx +2 -0
- package/themes/business/src/components/OrdersListManager/index.tsx +10 -3
- package/themes/business/src/components/OrdersOption/index.tsx +11 -3
- package/themes/business/src/types/index.tsx +8 -7
- package/themes/original/index.tsx +2 -0
- package/themes/original/src/components/BusinessController/index.tsx +30 -3
- package/themes/original/src/components/BusinessProductsList/index.tsx +15 -0
- package/themes/original/src/components/BusinessProductsListing/index.tsx +2 -0
- package/themes/original/src/components/BusinessesListing/index.tsx +8 -3
- package/themes/original/src/components/Cart/index.tsx +32 -1
- package/themes/original/src/components/Cart/styles.tsx +4 -0
- package/themes/original/src/components/OrderItAgain/index.tsx +72 -0
- package/themes/original/src/components/OrderItAgain/styles.tsx +10 -0
- package/themes/original/src/components/SingleProductCard/index.tsx +66 -25
- package/themes/original/src/types/index.tsx +12 -0
package/package.json
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
|
|
25
|
-
|
|
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
|
|
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}
|
|
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) =>
|
|
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
|
}
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
useValidationFields,
|
|
9
9
|
} from 'ordering-components/native';
|
|
10
10
|
import { useTheme } from 'styled-components/native';
|
|
11
|
-
import { CContainer, CheckoutAction, Divider } from './styles';
|
|
11
|
+
import { CContainer, CheckoutAction, Divider, DriverTipsContainer } from './styles';
|
|
12
12
|
|
|
13
13
|
import { OSBill, OSTable, OSCoupon, OSTotal, OSRow } from '../OrderSummary/styles';
|
|
14
14
|
|
|
@@ -25,6 +25,7 @@ import { TaxInformation } from '../TaxInformation';
|
|
|
25
25
|
import { CartStoresListing } from '../CartStoresListing';
|
|
26
26
|
import { OAlert } from '../../../../../src/components/shared'
|
|
27
27
|
import { PlaceSpot } from '../PlaceSpot'
|
|
28
|
+
import { DriverTips } from '../DriverTips'
|
|
28
29
|
|
|
29
30
|
const CartUI = (props: any) => {
|
|
30
31
|
const {
|
|
@@ -66,6 +67,10 @@ const CartUI = (props: any) => {
|
|
|
66
67
|
const businessId = business?.business_id ?? null
|
|
67
68
|
const placeSpotTypes = [4]
|
|
68
69
|
|
|
70
|
+
const driverTipsOptions = typeof configs?.driver_tip_options?.value === 'string'
|
|
71
|
+
? JSON.parse(configs?.driver_tip_options?.value) || []
|
|
72
|
+
: configs?.driver_tip_options?.value || []
|
|
73
|
+
|
|
69
74
|
const momentFormatted = !orderState?.option?.moment
|
|
70
75
|
? t('RIGHT_NOW', 'Right Now')
|
|
71
76
|
: parseDate(orderState?.option?.moment, { outputFormat: 'YYYY-MM-DD HH:mm' })
|
|
@@ -355,6 +360,32 @@ const CartUI = (props: any) => {
|
|
|
355
360
|
</OSTable>
|
|
356
361
|
)}
|
|
357
362
|
|
|
363
|
+
{isMultiCheckout &&
|
|
364
|
+
cart &&
|
|
365
|
+
cart?.valid &&
|
|
366
|
+
orderState?.options?.type === 1 &&
|
|
367
|
+
cart?.status !== 2 &&
|
|
368
|
+
validationFields?.fields?.checkout?.driver_tip?.enabled &&
|
|
369
|
+
driverTipsOptions && driverTipsOptions?.length > 0 &&
|
|
370
|
+
(
|
|
371
|
+
<DriverTipsContainer>
|
|
372
|
+
<OText size={14} lineHeight={20} color={theme.colors.textNormal}>
|
|
373
|
+
{t('DRIVER_TIPS', 'Driver Tips')}
|
|
374
|
+
</OText>
|
|
375
|
+
<DriverTips
|
|
376
|
+
uuid={cart?.uuid}
|
|
377
|
+
businessId={cart?.business_id}
|
|
378
|
+
driverTipsOptions={driverTipsOptions}
|
|
379
|
+
isFixedPrice={parseInt(configs?.driver_tip_type?.value, 10) === 1 || !!parseInt(configs?.driver_tip_use_custom?.value, 10)}
|
|
380
|
+
isDriverTipUseCustom={!!parseInt(configs?.driver_tip_use_custom?.value, 10)}
|
|
381
|
+
driverTip={parseInt(configs?.driver_tip_type?.value, 10) === 1 || !!parseInt(configs?.driver_tip_use_custom?.value, 10)
|
|
382
|
+
? cart?.driver_tip
|
|
383
|
+
: cart?.driver_tip_rate}
|
|
384
|
+
useOrderContext
|
|
385
|
+
/>
|
|
386
|
+
</DriverTipsContainer>
|
|
387
|
+
)}
|
|
388
|
+
|
|
358
389
|
<OSTotal>
|
|
359
390
|
<OSTable style={{ marginTop: 15 }}>
|
|
360
391
|
<OText size={14} lineHeight={21} weight={'600'}>
|
|
@@ -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
|
+
}
|
|
@@ -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
|
|
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) =>
|
|
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
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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
|
-
<
|
|
209
|
-
style={
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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:
|
|
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,
|