@whatmore-repo/whatmore-reactnative-sdk 1.0.8
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/commands.txt +34 -0
- package/index.d.ts +1 -0
- package/index.js +31 -0
- package/package.json +29 -0
- package/pre-integration-steps.txt +3 -0
- package/src/api/cart-commands/CartCommands.js +26 -0
- package/src/api/navigation-commands/NavigationCommands.js +17 -0
- package/src/api/partners/PartnerUtils.js +11 -0
- package/src/api/partners/appbrew/AppbrewCommands.js +27 -0
- package/src/api/product-details-commands/ProductDetailsCommands.js +390 -0
- package/src/api/shopify-commands/ShopifyCommands.js +29 -0
- package/src/api/whatmore-backend/WhatmoreMetricCommands.js +159 -0
- package/src/components/EventClickComponent.js +31 -0
- package/src/components/EventShoppingOverlay.js +75 -0
- package/src/components/EventShoppingView.js +32 -0
- package/src/components/EventsVerticalSwipeView.js +60 -0
- package/src/components/EventsVerticalSwipeViewNoModal.js +52 -0
- package/src/components/WhatmoreRootComponent.js +89 -0
- package/src/components/commons/AppMuteUnmuteIcon.js +36 -0
- package/src/components/cta-buttons/AddToCartButton.jsx +346 -0
- package/src/components/product-tiles/ProductPriceBadge.js +52 -0
- package/src/components/product-tiles/ProductTileV1.js +205 -0
- package/src/components/product-tiles/SelectVariantComponent.js +117 -0
- package/src/components/video-player/AppVideoPlayer.js +26 -0
- package/src/constants/AppGlobalVars.js +12 -0
- package/src/globals/useAppState_APP.js +18 -0
- package/src/hooks/useAddToCartStates.js +195 -0
- package/src/icons/icon-components/CartIcon.jsx +27 -0
- package/src/icons/icon-components/LoadingIcon.jsx +18 -0
- package/src/icons/icon-components/MuteIcon.jsx +23 -0
- package/src/icons/icon-components/PlusIcon.jsx +27 -0
- package/src/icons/icon-components/UnmuteIcon.jsx +23 -0
- package/src/icons/svg-utils.txt +6 -0
- package/src/utils/ColorUtils.js +40 -0
- package/src/utils/EventSanityUtils.js +21 -0
- package/src/utils/PriceUtils.js +28 -0
- package/src/utils/ProductUtils.js +6 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import { useEffect } from "react";
|
|
3
|
+
import { getGlobalState, useGlobalState } from "../../globals/useAppState_APP";
|
|
4
|
+
import { View, Text, TouchableOpacity } from 'react-native';
|
|
5
|
+
// import { Divider } from 'react-native-paper';
|
|
6
|
+
|
|
7
|
+
function SelectVariantComponent(props){
|
|
8
|
+
const product = props.product;
|
|
9
|
+
const event = props.event;
|
|
10
|
+
const productVariantOptions = props.productVariantOptions;
|
|
11
|
+
const width = props.width ?? '100%';
|
|
12
|
+
const height = props.height ?? 'auto';
|
|
13
|
+
const baseFontSize = props.baseFontSize ?? 10;
|
|
14
|
+
const onOptionSelected = props.onOptionSelected;
|
|
15
|
+
const optionTitleColor = props.optionTitleColor ?? "black";
|
|
16
|
+
|
|
17
|
+
const isInDesignMode = getGlobalState('isInDesignMode');
|
|
18
|
+
const isDemoBrand = getGlobalState('isDemoBrand');
|
|
19
|
+
const whatmorePrimaryColor = getGlobalState('whatmorePrimaryColor');
|
|
20
|
+
const whatmorePrimaryFont = getGlobalState('whatmorePrimaryFont');
|
|
21
|
+
const whatmoreShopId = getGlobalState('whatmoreShopId');
|
|
22
|
+
|
|
23
|
+
function _getFirstAvailableOption(options){
|
|
24
|
+
if(options.length == 0){return null;}
|
|
25
|
+
|
|
26
|
+
const availableOptions = options.filter(option => option['available']);
|
|
27
|
+
if(availableOptions.length == 0){return null;}
|
|
28
|
+
|
|
29
|
+
return availableOptions[0]['option'];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const [selectedOptions, setSelectedOptions] = useState(
|
|
33
|
+
[
|
|
34
|
+
_getFirstAvailableOption(productVariantOptions[0]),
|
|
35
|
+
_getFirstAvailableOption(productVariantOptions[1]),
|
|
36
|
+
_getFirstAvailableOption(productVariantOptions[2])
|
|
37
|
+
]
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
useEffect(()=>{
|
|
41
|
+
onOptionSelected(selectedOptions);
|
|
42
|
+
}, [selectedOptions]);
|
|
43
|
+
|
|
44
|
+
function _getPaddingForOption(optionName){
|
|
45
|
+
return optionName.length >= 3 ? 0 : (baseFontSize * 0.5);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<View style={{flexDirection: 'column', width: '100%', justifyContent: 'flex-start', alignItems: 'center'}}>
|
|
50
|
+
{productVariantOptions.map((options, index) => {
|
|
51
|
+
if(options.length === 0) return null;
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<View style={{flexDirection: 'column', alignItems: 'center', justifyContent: 'flex-start', width: '100%'}} key={index}>
|
|
55
|
+
<View
|
|
56
|
+
style={{
|
|
57
|
+
width: '30%', height: 2, alignItems: 'center',
|
|
58
|
+
backgroundColor: 'gray', marginTop: 10, marginBottom: 10
|
|
59
|
+
}}></View>
|
|
60
|
+
<Text
|
|
61
|
+
style={{
|
|
62
|
+
fontSize: baseFontSize * 1.2,
|
|
63
|
+
color: optionTitleColor,
|
|
64
|
+
fontFamily: whatmorePrimaryFont,
|
|
65
|
+
textAlign: 'center',
|
|
66
|
+
fontWeight: 'bold'
|
|
67
|
+
}}
|
|
68
|
+
>
|
|
69
|
+
{options[0]['type']}
|
|
70
|
+
</Text>
|
|
71
|
+
<View
|
|
72
|
+
style={{
|
|
73
|
+
flexDirection: 'row', justifyContent: 'center', flexWrap: 'wrap',
|
|
74
|
+
marginTop: 5
|
|
75
|
+
}}
|
|
76
|
+
>
|
|
77
|
+
{options.map(option => (
|
|
78
|
+
<TouchableOpacity key={option['option']}
|
|
79
|
+
style={{
|
|
80
|
+
borderRadius: 2,
|
|
81
|
+
padding: 2,
|
|
82
|
+
backgroundColor: option['option'] === selectedOptions[index] ? whatmorePrimaryColor : 'white',
|
|
83
|
+
marginLeft: 5, marginRight: 5
|
|
84
|
+
}}
|
|
85
|
+
onPress={(e) => {
|
|
86
|
+
e.stopPropagation();
|
|
87
|
+
option['available'] && setSelectedOptions([
|
|
88
|
+
index === 0 ? option['option'] : selectedOptions[0],
|
|
89
|
+
index === 1 ? option['option'] : selectedOptions[1],
|
|
90
|
+
index === 2 ? option['option'] : selectedOptions[2]
|
|
91
|
+
]);
|
|
92
|
+
}}
|
|
93
|
+
>
|
|
94
|
+
<Text
|
|
95
|
+
style={{
|
|
96
|
+
textDecorationLine: option['available'] ? 'none' : 'line-through',
|
|
97
|
+
fontSize: baseFontSize,
|
|
98
|
+
color: option['available'] ? (option['option'] === selectedOptions[index] ? 'white' : 'black') : 'gray',
|
|
99
|
+
fontFamily: whatmorePrimaryFont,
|
|
100
|
+
textAlign: 'center',
|
|
101
|
+
paddingHorizontal: _getPaddingForOption(option['option'])
|
|
102
|
+
}}
|
|
103
|
+
>
|
|
104
|
+
{option['option']}
|
|
105
|
+
</Text>
|
|
106
|
+
</TouchableOpacity>
|
|
107
|
+
))}
|
|
108
|
+
</View>
|
|
109
|
+
</View>
|
|
110
|
+
);
|
|
111
|
+
})}
|
|
112
|
+
</View>
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export default SelectVariantComponent;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { useGlobalState } from '../../globals/useAppState_APP';
|
|
3
|
+
import Video from 'react-native-video';
|
|
4
|
+
|
|
5
|
+
export default function AppVideoPlayer(props) {
|
|
6
|
+
const video = React.useRef(null);
|
|
7
|
+
const [isAppMuted] = useGlobalState("isAppMuted");
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<Video
|
|
11
|
+
source={{uri: props.videoUrl}}
|
|
12
|
+
ref={video}
|
|
13
|
+
muted={isAppMuted ? true : props.isMuted}
|
|
14
|
+
controls={false}
|
|
15
|
+
resizeMode='cover'
|
|
16
|
+
style={{
|
|
17
|
+
position: 'absolute',
|
|
18
|
+
top: 0,
|
|
19
|
+
left: 0,
|
|
20
|
+
bottom: 0,
|
|
21
|
+
right: 0,
|
|
22
|
+
width: '100%', height: '100%'
|
|
23
|
+
}}
|
|
24
|
+
/>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const WHATMORE_BACKEND_BASE_URL = "https://api.whatmore.live";
|
|
2
|
+
export const WHATMORE_BACKEND_CACHE_BASE_URL = "https://whatmore-api-cdn-cache.azureedge.net";
|
|
3
|
+
|
|
4
|
+
// widget types
|
|
5
|
+
export const carousel = "carousel"
|
|
6
|
+
export const videoPopup = "video_popup"
|
|
7
|
+
export const stories = "stories"
|
|
8
|
+
|
|
9
|
+
// page types
|
|
10
|
+
export const homepage = "homepage"
|
|
11
|
+
export const productPage = "product_page"
|
|
12
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { createGlobalState } from "react-hooks-global-state";
|
|
2
|
+
|
|
3
|
+
export const { GlobalStateProvider, useGlobalState, getGlobalState, setGlobalState } = createGlobalState({
|
|
4
|
+
verticalSwiperViewActiveIndex: 0,
|
|
5
|
+
isAppMuted: false,
|
|
6
|
+
brandDomainContext: 'shopify',
|
|
7
|
+
isInDesignMode: true,
|
|
8
|
+
isDemoBrand: true,
|
|
9
|
+
whatmoreShopId: '58669793418',
|
|
10
|
+
whatmorePrimaryFont: 'sans-serif',
|
|
11
|
+
whatmorePrimaryColor: '#d97777',
|
|
12
|
+
whatmoreAllowAutoSlide: true,
|
|
13
|
+
whatmoreAddedToCart: false,
|
|
14
|
+
commandsObject: {},
|
|
15
|
+
template: 'template-swipe-a',
|
|
16
|
+
modalUsed: true,
|
|
17
|
+
activateMock: false
|
|
18
|
+
});
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { useRef } from "react";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { getGlobalState, setGlobalState, useGlobalState } from "../globals/useAppState_APP";
|
|
4
|
+
import { increaseEventProductAddToCartCount } from "../api/whatmore-backend/WhatmoreMetricCommands.js";
|
|
5
|
+
import { productDetailsDetailed } from "../api/product-details-commands/ProductDetailsCommands.js";
|
|
6
|
+
import { addToCartCommand } from "../api/cart-commands/CartCommands";
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
*
|
|
11
|
+
* @param {*} initialCartCount initial count of product in cart
|
|
12
|
+
* @returns
|
|
13
|
+
*/
|
|
14
|
+
function useAddToCartStates(initialCartCount, product, event){
|
|
15
|
+
const whatmoreShopId = getGlobalState('whatmoreShopId');
|
|
16
|
+
const isInDesignMode = getGlobalState('isInDesignMode');
|
|
17
|
+
const isDemoBrand = getGlobalState('isDemoBrand');
|
|
18
|
+
const brandDomainContext = getGlobalState('brandDomainContext');
|
|
19
|
+
|
|
20
|
+
const [productVariantOptions, setProductVariantOptions] = useState(null);
|
|
21
|
+
const productVariantDetailsRef = useRef(null);
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
*
|
|
25
|
+
* init -> productVariantDetailsApiCallInProgress -> awaitingVariantSelection -> atcApiCallInProgress -> init(+1)
|
|
26
|
+
* -> errored
|
|
27
|
+
*/
|
|
28
|
+
const [cartState, setCartState] = useState(
|
|
29
|
+
{"state" : "init", "cart_count" : initialCartCount}
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
function updateCartState(newState, newCartCount){
|
|
33
|
+
setCartState({"state" : newState, "cart_count" : (newCartCount ?? cartState["cart_count"])});
|
|
34
|
+
|
|
35
|
+
// disable auto slide according to cart state
|
|
36
|
+
if(!(newState == "init" || newState == "errored")){
|
|
37
|
+
setGlobalState("whatmoreAllowAutoSlide", false);
|
|
38
|
+
}else{
|
|
39
|
+
setGlobalState("whatmoreAllowAutoSlide", true);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function _getVariantIdFromSelectedOption(selectedOptions){
|
|
44
|
+
const matchedVariants = productVariantDetailsRef.current.filter(variant =>
|
|
45
|
+
variant.option1 == selectedOptions[0] && variant.option2 == selectedOptions[1] && variant.option3 == selectedOptions[2]);
|
|
46
|
+
|
|
47
|
+
if(matchedVariants.length == 0){return null;}
|
|
48
|
+
return matchedVariants[0]['id'];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function getPriceDetailsFromSelectedOption(selectedOptions){
|
|
52
|
+
const matchedVariants = productVariantDetailsRef.current.filter(variant =>
|
|
53
|
+
variant.option1 == selectedOptions[0] && variant.option2 == selectedOptions[1] && variant.option3 == selectedOptions[2]);
|
|
54
|
+
|
|
55
|
+
if(matchedVariants.length == 0){return null;}
|
|
56
|
+
|
|
57
|
+
const price = matchedVariants[0]['price'] ?? 0;
|
|
58
|
+
const comparePrice = matchedVariants[0]['compare_at_price'] ?? 0;
|
|
59
|
+
return {
|
|
60
|
+
'price': (price / 100.0),
|
|
61
|
+
'compare_price': (comparePrice / 100.0)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function _getVariantOptions(result){
|
|
66
|
+
var options1 = [];
|
|
67
|
+
var options2 = [];
|
|
68
|
+
var options3 = [];
|
|
69
|
+
|
|
70
|
+
result.variants.forEach(variant => {
|
|
71
|
+
variant.option1 && options1.push({"option" : variant.option1, "available" : variant.available ?? true, "type": result.options[0].name});
|
|
72
|
+
variant.option2 && options2.push({"option" : variant.option2, "available" : variant.available ?? true, "type": result.options[1].name});
|
|
73
|
+
variant.option3 && options3.push({"option" : variant.option3, "available" : variant.available ?? true, "type": result.options[2].name});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
options1 = options1.reduce((result, option, index) => {
|
|
77
|
+
var res = result.filter(_option => _option['option'] == option['option']);
|
|
78
|
+
if(res.length == 0){
|
|
79
|
+
return [...result, option];
|
|
80
|
+
}
|
|
81
|
+
return result;
|
|
82
|
+
}, []);
|
|
83
|
+
|
|
84
|
+
options2 = options2.reduce((result, option, index) => {
|
|
85
|
+
var res = result.filter(_option => _option['option'] == option['option']);
|
|
86
|
+
if(res.length == 0){
|
|
87
|
+
return [...result, option];
|
|
88
|
+
}
|
|
89
|
+
return result;
|
|
90
|
+
}, []);
|
|
91
|
+
|
|
92
|
+
options3 = options3.reduce((result, option, index) => {
|
|
93
|
+
var res = result.filter(_option => _option['option'] == option['option']);
|
|
94
|
+
if(res.length == 0){
|
|
95
|
+
return [...result, option];
|
|
96
|
+
}
|
|
97
|
+
return result;
|
|
98
|
+
}, []);
|
|
99
|
+
|
|
100
|
+
return [options1, options2, options3];
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function _getFirstAvailableOption(options){
|
|
104
|
+
if(options.length == 0){return null;}
|
|
105
|
+
|
|
106
|
+
const availableOptions = options.filter(option => option['available']);
|
|
107
|
+
if(availableOptions.length == 0){return null;}
|
|
108
|
+
|
|
109
|
+
return availableOptions[0]['option'];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function _isSingleVariantProduct(variantOptions){
|
|
113
|
+
return variantOptions[0].length <= 1 && variantOptions[1].length <= 1 && variantOptions[2].length <= 1;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function _atleastOneVariantAvailableInStock(){
|
|
117
|
+
if(productVariantDetailsRef.current && productVariantDetailsRef.current.length >= 1){
|
|
118
|
+
const availableVariants = productVariantDetailsRef.current.filter(variant => variant.available);
|
|
119
|
+
return availableVariants.length >= 1;
|
|
120
|
+
}
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function initiateVariantSelection(handleAtcCall){
|
|
125
|
+
updateCartState("productVariantDetailsApiCallInProgress", null)
|
|
126
|
+
productDetailsDetailed(product, brandDomainContext, whatmoreShopId)
|
|
127
|
+
.then(result => {
|
|
128
|
+
productVariantDetailsRef.current = result.variants;
|
|
129
|
+
const variantOptions = _getVariantOptions(result);
|
|
130
|
+
setProductVariantOptions(variantOptions);
|
|
131
|
+
|
|
132
|
+
if(_isSingleVariantProduct(variantOptions)){
|
|
133
|
+
if(!_atleastOneVariantAvailableInStock()){
|
|
134
|
+
updateCartState("errored", null);
|
|
135
|
+
}else{
|
|
136
|
+
initiateAtcApiCall([
|
|
137
|
+
_getFirstAvailableOption(variantOptions[0]),
|
|
138
|
+
_getFirstAvailableOption(variantOptions[1]),
|
|
139
|
+
_getFirstAvailableOption(variantOptions[2])
|
|
140
|
+
], handleAtcCall);
|
|
141
|
+
}
|
|
142
|
+
}else{
|
|
143
|
+
updateCartState("awaitingVariantSelection", null);
|
|
144
|
+
}
|
|
145
|
+
})
|
|
146
|
+
.catch(error => {
|
|
147
|
+
updateCartState("errored", null);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function pushAddToCartMetric(event, product, quantity){
|
|
152
|
+
if(!isInDesignMode && !isDemoBrand){
|
|
153
|
+
(async () => {
|
|
154
|
+
increaseEventProductAddToCartCount(event, product, quantity);
|
|
155
|
+
})();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function initiateAtcApiCall(selectedOptions){
|
|
160
|
+
const variantId = _getVariantIdFromSelectedOption(selectedOptions);
|
|
161
|
+
if(!variantId){
|
|
162
|
+
updateCartState("init", cartState["cart_count"]);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const itemQuantity = 1;
|
|
167
|
+
|
|
168
|
+
updateCartState("atcApiCallInProgress", null);
|
|
169
|
+
|
|
170
|
+
const res = addToCartCommand(variantId, itemQuantity, product, brandDomainContext, whatmoreShopId);
|
|
171
|
+
res.then((isSuccess) => {
|
|
172
|
+
if(isSuccess){
|
|
173
|
+
pushAddToCartMetric(event, product, itemQuantity);
|
|
174
|
+
updateCartState("init", cartState["cart_count"] + 1);
|
|
175
|
+
setGlobalState("whatmoreAddedToCart", true);
|
|
176
|
+
}else{
|
|
177
|
+
updateCartState("errored", null);
|
|
178
|
+
console.error('Error while adding to cart:', error);
|
|
179
|
+
}
|
|
180
|
+
})
|
|
181
|
+
.catch((error) => {
|
|
182
|
+
updateCartState("errored", null);
|
|
183
|
+
console.error('Error while adding to cart:', error);
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function initiateAtcCartIconClickAction(){
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return [cartState, productVariantOptions, initiateVariantSelection, initiateAtcApiCall, getPriceDetailsFromSelectedOption, initiateAtcCartIconClickAction];
|
|
192
|
+
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export default useAddToCartStates;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import Svg, { G, Path } from 'react-native-svg';
|
|
3
|
+
|
|
4
|
+
function CartIcon(props) {
|
|
5
|
+
const fillColor = props.color || '#FFFFFF';
|
|
6
|
+
const width = props.size || 40;
|
|
7
|
+
const height = props.size || 40;
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<Svg
|
|
11
|
+
width={width} height={height}
|
|
12
|
+
viewBox="0 0 24 24"
|
|
13
|
+
{...props}
|
|
14
|
+
>
|
|
15
|
+
<G fill={fillColor}>
|
|
16
|
+
<Path d="M4.468 10.266l.024.216.126 1.131c.088.794.164 1.483.298 2.033.143.59.38 1.148.873 1.59.494.441 1.074.615 1.677.692C8.026 16 8.72 16 9.519 16h5.353c.548 0 1.032 0 1.435-.041.438-.044.863-.143 1.271-.389.41-.246.695-.576.94-.941.224-.338.45-.765.707-1.25l2.49-4.702C22.596 7.01 21.387 5 19.5 5H9.399c-.983 0-1.824 0-2.49.093a3.974 3.974 0 00-.976.25L5.79 4.05A2.303 2.303 0 003.5 2H3a1 1 0 100 2h.5c.155 0 .285.116.302.27l.666 5.996z" />
|
|
17
|
+
<Path
|
|
18
|
+
fillRule="evenodd"
|
|
19
|
+
clipRule="evenodd"
|
|
20
|
+
d="M14 19.5a2.5 2.5 0 115 0 2.5 2.5 0 01-5 0zM5 19.5a2.5 2.5 0 115 0 2.5 2.5 0 01-5 0z"
|
|
21
|
+
/>
|
|
22
|
+
</G>
|
|
23
|
+
</Svg>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export default CartIcon;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import Svg, { G, Path } from 'react-native-svg';
|
|
3
|
+
|
|
4
|
+
function LoadingIcon(props) {
|
|
5
|
+
const fillColor = props.color || '#FFFFFF';
|
|
6
|
+
const width = props.size || 40;
|
|
7
|
+
const height = props.size || 40;
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<Svg width={width} height={height} viewBox="-7 0 32 32" xmlns="http://www.w3.org/2000/svg" {...props}>
|
|
11
|
+
<G fill={fillColor}>
|
|
12
|
+
<Path d="M2.08 14.04l4-1.04c.44-.12.72-.56.6-1.04a.847.847 0 00-1.04-.6l-2.08.56c.68-.88 1.56-1.6 2.64-2.08 1.64-.72 3.44-.76 5.12-.12A6.75 6.75 0 0115 13.24c.2.44.68.6 1.12.44.44-.2.6-.68.44-1.12-.88-2.04-2.52-3.6-4.6-4.44-2.08-.8-4.36-.76-6.4.12-1.36.6-2.48 1.52-3.36 2.68l-.52-1.96a.847.847 0 00-1.04-.6c-.44.12-.72.56-.6 1.04l1.04 4c.12.56.4.8 1 .64zm15.64 8.48l-1.04-3.96s-.16-.8-.96-.6l-4 1.04c-.44.12-.72.56-.6 1.04.12.44.56.72 1.04.6l2.08-.56a6.62 6.62 0 01-7.72 2.16 6.75 6.75 0 01-3.68-3.52c-.2-.44-.68-.6-1.12-.44-.44.2-.6.68-.44 1.12.88 2.04 2.52 3.6 4.6 4.44 1 .4 2 .56 3.04.56 2.64 0 5.12-1.24 6.72-3.4l.52 1.96c.08.36.44.64.8.64.08 0 .16 0 .2-.04.4-.16.68-.6.56-1.04z" />
|
|
13
|
+
</G>
|
|
14
|
+
</Svg>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default LoadingIcon;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import Svg, { G, Path } from 'react-native-svg';
|
|
3
|
+
|
|
4
|
+
function MuteIcon(props) {
|
|
5
|
+
const fillColor = props.color || '#FFFFFF';
|
|
6
|
+
const width = props.size || 40;
|
|
7
|
+
const height = props.size || 40;
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<Svg width={width} height={height} viewBox="-1.44 -1.44 26.88 26.88" {...props}>
|
|
11
|
+
<G fill={fillColor}>
|
|
12
|
+
<Path
|
|
13
|
+
fillRule="evenodd"
|
|
14
|
+
clipRule="evenodd"
|
|
15
|
+
d="M20.515 6.316a.75.75 0 01.991.376c.468 1.035.994 2.768.994 5.308 0 2.192-.392 3.783-.8 4.844-.204.53-.41.925-.572 1.195a4.73 4.73 0 01-.289.425l-.007.01-.003.003-.002.002L20.25 18l.576.48a.75.75 0 01-1.156-.956l.003-.004.031-.041a3.27 3.27 0 00.137-.212c.12-.199.288-.516.459-.961.342-.889.7-2.298.7-4.306 0-2.326-.48-3.849-.86-4.692a.75.75 0 01.375-.992zM18.414 9.266a.75.75 0 01.887.582c.11.53.199 1.24.199 2.152 0 1.11-.132 1.923-.273 2.474-.071.275-.144.484-.203.631a2.947 2.947 0 01-.102.228l-.01.018-.003.007-.002.003v.002s-.001.001-.657-.363l.656.364a.75.75 0 01-1.317-.719l.005-.01c.007-.015.02-.043.038-.087a3.66 3.66 0 00.141-.447c.11-.424.227-1.111.227-2.101 0-.813-.08-1.421-.168-1.848a.75.75 0 01.582-.886z"
|
|
16
|
+
/>
|
|
17
|
+
<Path d="M21.78 3.53a.75.75 0 00-1.06-1.06l-4.45 4.449a11.265 11.265 0 00-.193-1.39c-.172-.788-.477-1.473-1.116-1.923a3.066 3.066 0 00-.769-.39c-.818-.28-1.631-.057-2.457.345-.814.395-1.8 1.046-3.032 1.857l-.267.176c-.447.295-.602.394-.76.464-.171.076-.35.13-.535.16-.171.03-.354.032-.89.032h-.162c-1.217 0-2.062-.001-2.814.347A3.962 3.962 0 001.548 8.22c-.392.729-.438 1.491-.504 2.575l-.008.13C1.014 11.294 1 11.658 1 12c0 .342.014.706.036 1.074l.008.13c.066 1.084.112 1.846.504 2.575a3.962 3.962 0 001.727 1.624c.61.283 1.283.336 2.166.345L2.72 20.47a.75.75 0 101.06 1.06l18-18zM16.5 12a.75.75 0 00-1.255-.554l-.071.074-6 6.274A.778.778 0 009.34 19c1.039.68 1.899 1.225 2.631 1.549.743.328 1.48.489 2.222.236.272-.093.534-.226.769-.391.706-.497 1.005-1.28 1.167-2.18.159-.884.213-2.056.281-3.516l.003-.058c.052-1.115.088-2.088.088-2.64z" />
|
|
18
|
+
</G>
|
|
19
|
+
</Svg>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export default MuteIcon;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import Svg, { G, Path } from 'react-native-svg';
|
|
3
|
+
|
|
4
|
+
function PlusIcon(props) {
|
|
5
|
+
const fillColor = props.color || '#FFFFFF';
|
|
6
|
+
const width = props.size || 40;
|
|
7
|
+
const height = props.size || 40;
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<Svg
|
|
11
|
+
width={width} height={height}
|
|
12
|
+
viewBox="0 0 24 24"
|
|
13
|
+
{...props}
|
|
14
|
+
>
|
|
15
|
+
<G fill={fillColor}>
|
|
16
|
+
<Path fill="#fff" d="M0 0H24V24H0z" />
|
|
17
|
+
<Path
|
|
18
|
+
fillRule="evenodd"
|
|
19
|
+
clipRule="evenodd"
|
|
20
|
+
d="M13 9a1 1 0 10-2 0v2H9a1 1 0 100 2h2v2a1 1 0 102 0v-2h2a1 1 0 100-2h-2V9zM7.25 2.388C8.55 2.099 10.124 2 12 2s3.451.1 4.75.388c1.31.291 2.399.788 3.236 1.626.838.837 1.335 1.926 1.626 3.236C21.901 8.55 22 10.124 22 12s-.1 3.451-.388 4.75c-.291 1.31-.788 2.399-1.626 3.236-.837.838-1.926 1.335-3.236 1.626-1.299.289-2.874.388-4.75.388s-3.451-.1-4.75-.388c-1.31-.291-2.399-.788-3.236-1.626-.838-.837-1.335-1.926-1.626-3.236C2.099 15.45 2 13.876 2 12s.1-3.451.388-4.75c.291-1.31.788-2.399 1.626-3.236.837-.838 1.926-1.335 3.236-1.626z"
|
|
21
|
+
/>
|
|
22
|
+
</G>
|
|
23
|
+
</Svg>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export default PlusIcon;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import Svg, { G, Path } from 'react-native-svg';
|
|
3
|
+
|
|
4
|
+
function UnmuteIcon(props) {
|
|
5
|
+
const fillColor = props.color || '#FFFFFF';
|
|
6
|
+
const width = props.size || 40;
|
|
7
|
+
const height = props.size || 40;
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<Svg width={width} height={height} viewBox="0 0 24 24" {...props}>
|
|
11
|
+
<G fill={fillColor}>
|
|
12
|
+
<Path d="M2.003 11.716c.037-1.843.056-2.764.668-3.552.112-.144.276-.315.413-.431.752-.636 1.746-.636 3.733-.636.71 0 1.065 0 1.403-.092.07-.02.14-.042.209-.067.33-.121.627-.33 1.22-.746 2.338-1.645 3.508-2.467 4.489-2.11.188.069.37.168.533.29.848.635.913 2.115 1.042 5.073.048 1.096.08 2.034.08 2.555 0 .521-.032 1.46-.08 2.555-.13 2.958-.194 4.438-1.042 5.073-.163.122-.345.221-.533.29-.982.357-2.15-.465-4.49-2.11-.592-.416-.889-.625-1.22-.746a2.61 2.61 0 00-.208-.067c-.338-.092-.693-.092-1.403-.092-1.987 0-2.98 0-3.733-.636a3.145 3.145 0 01-.413-.43c-.612-.79-.63-1.71-.668-3.553a14.06 14.06 0 010-.569z" />
|
|
13
|
+
<Path
|
|
14
|
+
fillRule="evenodd"
|
|
15
|
+
clipRule="evenodd"
|
|
16
|
+
d="M19.49 5.552a.66.66 0 01.97.094l-.529.471.53-.47.002.002.003.004.007.009a2.572 2.572 0 01.079.112c.047.071.111.173.186.305.149.264.339.652.526 1.171C21.64 8.291 22 9.851 22 12c0 2.15-.36 3.71-.736 4.75-.187.52-.377.907-.526 1.172a4.655 4.655 0 01-.265.417l-.007.009-.003.003-.001.002s-.001.001-.531-.47l.53.471a.66.66 0 01-.971.094.77.77 0 01-.09-1.035l.03-.041c.026-.04.07-.11.125-.207a6.28 6.28 0 00.422-.943c.314-.871.644-2.253.644-4.222 0-1.97-.33-3.35-.644-4.222a6.28 6.28 0 00-.422-.942 3.152 3.152 0 00-.157-.253M17.757 8.416c.333-.197.753-.07.938.286l-.603.357.603-.357.001.002.002.003.003.007.01.018.024.053c.018.042.042.099.07.17.053.145.12.35.185.62.13.54.252 1.337.252 2.425 0 1.089-.122 1.886-.252 2.426-.065.27-.132.475-.186.619a3.04 3.04 0 01-.094.223l-.009.018-.003.007-.002.003v.002s-.001.001-.604-.356l.603.357c-.185.355-.605.483-.938.286-.33-.196-.45-.638-.272-.991l.004-.01.035-.085c.032-.086.08-.23.13-.438.1-.416.208-1.09.208-2.06 0-.971-.108-1.645-.208-2.06a3.757 3.757 0 00-.165-.524l-.004-.01c-.179-.354-.058-.795.272-.991z"
|
|
17
|
+
/>
|
|
18
|
+
</G>
|
|
19
|
+
</Svg>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export default UnmuteIcon;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Version 4.1
|
|
2
|
+
|
|
3
|
+
// https://stackoverflow.com/questions/5560248/programmatically-lighten-or-darken-a-hex-color-or-rgb-and-blend-colors
|
|
4
|
+
// https://github.com/PimpTrizkit/PJs/wiki/12.-Shade,-Blend-and-Convert-a-Web-Color-(pSBC.js)
|
|
5
|
+
|
|
6
|
+
// Opacity level 0-15
|
|
7
|
+
export const shadeColorHex = function(hexCode, opacityLevel){
|
|
8
|
+
return hexCode + opacityLevel.toString(16) + opacityLevel.toString(16);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const shadeColor=(p,c0,c1,l)=>{
|
|
12
|
+
let r,g,b,P,f,t,h,m=Math.round,a=typeof(c1)=="string";
|
|
13
|
+
if(typeof(p)!="number"||p<-1||p>1||typeof(c0)!="string"||(c0[0]!='r'&&c0[0]!='#')||(c1&&!a))return null;
|
|
14
|
+
h=c0.length>9,h=a?c1.length>9?true:c1=="c"?!h:false:h,f=shadeColor.pSBCr(c0),P=p<0,t=c1&&c1!="c"?shadeColor.pSBCr(c1):P?{r:0,g:0,b:0,a:-1}:{r:255,g:255,b:255,a:-1},p=P?p*-1:p,P=1-p;
|
|
15
|
+
if(!f||!t)return null;
|
|
16
|
+
if(l)r=m(P*f.r+p*t.r),g=m(P*f.g+p*t.g),b=m(P*f.b+p*t.b);
|
|
17
|
+
else r=m((P*f.r**2+p*t.r**2)**0.5),g=m((P*f.g**2+p*t.g**2)**0.5),b=m((P*f.b**2+p*t.b**2)**0.5);
|
|
18
|
+
a=f.a,t=t.a,f=a>=0||t>=0,a=f?a<0?t:t<0?a:a*P+t*p:0;
|
|
19
|
+
if(h)return"rgb"+(f?"a(":"(")+r+","+g+","+b+(f?","+m(a*1000)/1000:"")+")";
|
|
20
|
+
else return"#"+(4294967296+r*16777216+g*65536+b*256+(f?m(a*255):0)).toString(16).slice(1,f?undefined:-2)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
shadeColor.pSBCr=(d)=>{
|
|
24
|
+
const i=parseInt;
|
|
25
|
+
let n=d.length,x={};
|
|
26
|
+
if(n>9){
|
|
27
|
+
const [r, g, b, a] = (d = d.split(','));
|
|
28
|
+
n = d.length;
|
|
29
|
+
if(n<3||n>4)return null;
|
|
30
|
+
x.r=i(r[3]=="a"?r.slice(5):r.slice(4)),x.g=i(g),x.b=i(b),x.a=a?parseFloat(a):-1
|
|
31
|
+
}else{
|
|
32
|
+
if(n==8||n==6||n<4)return null;
|
|
33
|
+
if(n<6)d="#"+d[1]+d[1]+d[2]+d[2]+d[3]+d[3]+(n>4?d[4]+d[4]:"");
|
|
34
|
+
d=i(d.slice(1),16);
|
|
35
|
+
if(n==9||n==5)x.r=d>>24&255,x.g=d>>16&255,x.b=d>>8&255,x.a=Math.round((d&255)/0.255)/1000;
|
|
36
|
+
else x.r=d>>16,x.g=d>>8&255,x.b=d&255,x.a=-1
|
|
37
|
+
}return x
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export default shadeColor;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export const removeInvalidEvents = function(data, brandId){
|
|
2
|
+
if(data == null){
|
|
3
|
+
return;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
return data.filter((event) => {
|
|
7
|
+
return event.products.length > 0;
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const extractEvents = function(data, isCollectionsTemplate){
|
|
12
|
+
if(data == null){
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if(isCollectionsTemplate){
|
|
17
|
+
return (data[0]['events']);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return data;
|
|
21
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
|
|
2
|
+
import getSymbolFromCurrency from 'currency-symbol-map'
|
|
3
|
+
|
|
4
|
+
function priceWithCommas(price){
|
|
5
|
+
return price.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function roundedPrice(price){
|
|
9
|
+
// is decimal more than 0.1
|
|
10
|
+
if(price % 1 >= 0.1){
|
|
11
|
+
// if decimal value is more than 0.1 but not significant in that currency
|
|
12
|
+
const priceAsString = price.toString();
|
|
13
|
+
if(priceAsString.split('.')[0].length >= 4){
|
|
14
|
+
return parseInt(price);
|
|
15
|
+
}
|
|
16
|
+
return price.toFixed(2);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return parseInt(price);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function getPriceStringWithCurrency(price, spaceString = "", currencyCode = "INR"){
|
|
23
|
+
if(!price || typeof price == undefined){
|
|
24
|
+
return "";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return getSymbolFromCurrency(currencyCode) + spaceString + priceWithCommas(roundedPrice(price));
|
|
28
|
+
}
|