@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.
Files changed (37) hide show
  1. package/commands.txt +34 -0
  2. package/index.d.ts +1 -0
  3. package/index.js +31 -0
  4. package/package.json +29 -0
  5. package/pre-integration-steps.txt +3 -0
  6. package/src/api/cart-commands/CartCommands.js +26 -0
  7. package/src/api/navigation-commands/NavigationCommands.js +17 -0
  8. package/src/api/partners/PartnerUtils.js +11 -0
  9. package/src/api/partners/appbrew/AppbrewCommands.js +27 -0
  10. package/src/api/product-details-commands/ProductDetailsCommands.js +390 -0
  11. package/src/api/shopify-commands/ShopifyCommands.js +29 -0
  12. package/src/api/whatmore-backend/WhatmoreMetricCommands.js +159 -0
  13. package/src/components/EventClickComponent.js +31 -0
  14. package/src/components/EventShoppingOverlay.js +75 -0
  15. package/src/components/EventShoppingView.js +32 -0
  16. package/src/components/EventsVerticalSwipeView.js +60 -0
  17. package/src/components/EventsVerticalSwipeViewNoModal.js +52 -0
  18. package/src/components/WhatmoreRootComponent.js +89 -0
  19. package/src/components/commons/AppMuteUnmuteIcon.js +36 -0
  20. package/src/components/cta-buttons/AddToCartButton.jsx +346 -0
  21. package/src/components/product-tiles/ProductPriceBadge.js +52 -0
  22. package/src/components/product-tiles/ProductTileV1.js +205 -0
  23. package/src/components/product-tiles/SelectVariantComponent.js +117 -0
  24. package/src/components/video-player/AppVideoPlayer.js +26 -0
  25. package/src/constants/AppGlobalVars.js +12 -0
  26. package/src/globals/useAppState_APP.js +18 -0
  27. package/src/hooks/useAddToCartStates.js +195 -0
  28. package/src/icons/icon-components/CartIcon.jsx +27 -0
  29. package/src/icons/icon-components/LoadingIcon.jsx +18 -0
  30. package/src/icons/icon-components/MuteIcon.jsx +23 -0
  31. package/src/icons/icon-components/PlusIcon.jsx +27 -0
  32. package/src/icons/icon-components/UnmuteIcon.jsx +23 -0
  33. package/src/icons/svg-utils.txt +6 -0
  34. package/src/utils/ColorUtils.js +40 -0
  35. package/src/utils/EventSanityUtils.js +21 -0
  36. package/src/utils/PriceUtils.js +28 -0
  37. package/src/utils/ProductUtils.js +6 -0
@@ -0,0 +1,346 @@
1
+
2
+ import { useState } from "react";
3
+ import { getGlobalState } from "../../globals/useAppState_APP";
4
+ import { Text, TouchableOpacity, View } from "react-native";
5
+ import { shadeColorHex } from "../../utils/ColorUtils";
6
+ import * as Animatable from 'react-native-animatable';
7
+ import LoadingIcon from "../../icons/icon-components/LoadingIcon";
8
+ import CartIcon from "../../icons/icon-components/CartIcon";
9
+ import PlusIcon from "../../icons/icon-components/PlusIcon";
10
+
11
+ function LabelledButton(props){
12
+ const whatmorePrimaryColor = getGlobalState('whatmorePrimaryColor');
13
+ const whatmorePrimaryFont = getGlobalState('whatmorePrimaryFont');
14
+ const whatmoreShopId = getGlobalState('whatmoreShopId');
15
+
16
+ const event = props.event;
17
+ const product = props.product;
18
+ const BASE_FONT_SIZE = props.baseFontSize ?? 10;
19
+ const onClick = props.onClick;
20
+ const width = props.width;
21
+ const height = props.height;
22
+ const title = props.title;
23
+ const showInProgressIcon = props.showInProgressIcon;
24
+
25
+ const BOX_PADDING = 2;
26
+
27
+ return (
28
+ <TouchableOpacity
29
+ style={{
30
+ width: width, height: height,
31
+ padding: BOX_PADDING,
32
+ backgroundColor: (showInProgressIcon ? shadeColorHex(whatmorePrimaryColor, 8) : whatmorePrimaryColor)
33
+ }}
34
+ onPress={(e)=>{
35
+ e.stopPropagation();
36
+ onClick();
37
+ }}
38
+ >
39
+ <View
40
+ style={{
41
+ width: '100%', height: height,
42
+ flexDirection: 'row', justifyContent: 'center', alignItems: 'center',
43
+ paddingVertical: 4
44
+ }}
45
+ >
46
+ {
47
+ title
48
+ ?
49
+ <Text
50
+ style={{
51
+ fontSize: BASE_FONT_SIZE * 4,
52
+ fontFamily: whatmorePrimaryFont,
53
+ fontWeight: 'bold',
54
+ color: 'white'
55
+ }}
56
+ >
57
+ &nbsp;{title}&nbsp;
58
+ </Text>
59
+ :
60
+ <></>
61
+ }
62
+ {
63
+ showInProgressIcon
64
+ ?
65
+ <Animatable.View
66
+ animation="rotate"
67
+ duration={400} iterationCount={'infinite'}
68
+ easing="linear"
69
+ style={{
70
+ height: 'auto', width: 'auto'
71
+ }}
72
+ >
73
+ <LoadingIcon
74
+ color={"#F5F5F5"}
75
+ size={BASE_FONT_SIZE * 8}
76
+ />
77
+ </Animatable.View>
78
+ :
79
+ <></>
80
+ }
81
+ </View>
82
+ </TouchableOpacity>
83
+ );
84
+ }
85
+
86
+
87
+
88
+ function AddToCartInitialButton(props){
89
+ const whatmorePrimaryColor = getGlobalState('whatmorePrimaryColor');
90
+ const whatmorePrimaryFont = getGlobalState('whatmorePrimaryFont');
91
+ const whatmoreShopId = getGlobalState('whatmoreShopId');
92
+
93
+ const event = props.event;
94
+ const product = props.product;
95
+ const BASE_FONT_SIZE = props.baseFontSize ?? 10;
96
+ const onClick = props.onClick;
97
+ const width = props.width;
98
+ const height = props.height;
99
+
100
+ const BOX_PADDING = 2;
101
+
102
+ return (
103
+ <TouchableOpacity
104
+ style={{
105
+ width: '100%',
106
+ height: height,
107
+ padding: BOX_PADDING,
108
+ backgroundColor: whatmorePrimaryColor
109
+ }}
110
+ onPress={(e) => {
111
+ e.stopPropagation();
112
+ onClick();
113
+ }}
114
+ >
115
+ <View
116
+ style={{
117
+ width: '100%',
118
+ height: 'auto',
119
+ alignItems: 'center',
120
+ justifyContent: 'center',
121
+ paddingVertical: 5
122
+ }}
123
+ >
124
+ <Text
125
+ style={{
126
+ fontSize: 26,
127
+ fontWeight: 'bold',
128
+ fontFamily: whatmorePrimaryFont,
129
+ color: 'white'
130
+ }}
131
+ >
132
+ &nbsp;{"ADD TO CART"}&nbsp;
133
+ </Text>
134
+ </View>
135
+ </TouchableOpacity>
136
+ );
137
+ }
138
+
139
+ function SoldOutButton(props){
140
+ const whatmorePrimaryFont = getGlobalState('whatmorePrimaryFont');
141
+ const whatmoreShopId = getGlobalState('whatmoreShopId');
142
+
143
+ const event = props.event;
144
+ const product = props.product;
145
+ const BASE_FONT_SIZE = props.baseFontSize ?? 10;
146
+ const width = props.width;
147
+ const height = props.height;
148
+
149
+ const BOX_PADDING = 2;
150
+
151
+ return (
152
+ <TouchableOpacity
153
+ style={{
154
+ width: width, height: height,
155
+ padding: BOX_PADDING, backgroundColor: 'white',
156
+ borderColor: 'gray', borderWidth: 1
157
+ }}
158
+ onPress={(e)=>{
159
+ e.stopPropagation();
160
+ }}
161
+ >
162
+ <View
163
+ style={{
164
+ width: '100%', height: height,
165
+ flexDirection: 'row', justifyContent: 'center', alignItems: 'center',
166
+ paddingVertical: 4, paddingHorizontal: 5
167
+ }}
168
+ >
169
+ <Text
170
+ style={{
171
+ fontSize: BASE_FONT_SIZE * 4,
172
+ fontWeight: 'bold',
173
+ fontFamily: whatmorePrimaryFont,
174
+ color: 'gray'
175
+ }}
176
+ >
177
+ &nbsp;{' Sold Out '}&nbsp;
178
+ </Text>
179
+ </View>
180
+ </TouchableOpacity>
181
+ );
182
+ }
183
+
184
+ function AddedToCartButton(props){
185
+ const whatmorePrimaryColor = getGlobalState('whatmorePrimaryColor');
186
+ const whatmorePrimaryFont = getGlobalState('whatmorePrimaryFont');
187
+ const whatmoreShopId = getGlobalState('whatmoreShopId');
188
+
189
+ const event = props.event;
190
+ const product = props.product;
191
+ const BASE_FONT_SIZE = props.baseFontSize ?? 10;
192
+ const onClick = props.onClick;
193
+ const cartCount = props.cartCount;
194
+ const width = props.width;
195
+ const height = props.height;
196
+ const onCartIconClick = props.onCartIconClick;
197
+
198
+ const BOX_PADDING = 2;
199
+
200
+ return (
201
+ <TouchableOpacity
202
+ style={{
203
+ width: width, height: height,
204
+ padding: BOX_PADDING,
205
+ backgroundColor: 'white', borderColor: whatmorePrimaryColor, borderWidth: 1
206
+ }}
207
+ onPress={(e)=>{
208
+ e.stopPropagation();
209
+ }}
210
+ >
211
+ <View
212
+ style={{
213
+ width: '100%', height: height,
214
+ flexDirection: 'row', justifyContent: 'center', alignItems: 'center',
215
+ paddingLeft: 4, paddingRight: 4,
216
+ }}
217
+ >
218
+ <TouchableOpacity
219
+ onPress={()=>{onCartIconClick();}}
220
+ >
221
+ <CartIcon
222
+ color={whatmorePrimaryColor}
223
+ size={BASE_FONT_SIZE * 6}
224
+ />
225
+ </TouchableOpacity>
226
+
227
+ <Animatable.Text
228
+ animation="bounceInUp"
229
+ duration={500} iterationCount={1}
230
+ style={{
231
+ fontSize: BASE_FONT_SIZE * 4,
232
+ fontWeight: 'bold',
233
+ fontFamily: whatmorePrimaryFont,
234
+ color: whatmorePrimaryColor,
235
+ marginHorizontal: 20
236
+ }}
237
+ >
238
+ {cartCount}
239
+ </Animatable.Text >
240
+
241
+ <TouchableOpacity
242
+ onPress={(e)=>{
243
+ e.stopPropagation();
244
+ onClick();
245
+ }}
246
+ >
247
+ <PlusIcon
248
+ color={whatmorePrimaryColor}
249
+ size={BASE_FONT_SIZE * 6}
250
+ />
251
+ </TouchableOpacity>
252
+ </View>
253
+ </TouchableOpacity>
254
+ );
255
+ }
256
+
257
+ function AddToCartButton(props){
258
+ const whatmoreShopId = getGlobalState('whatmoreShopId');
259
+
260
+ const event = props.event;
261
+ const product = props.product;
262
+ const BASE_FONT_SIZE = props.baseFontSize ?? 10;
263
+ const showAddedToCart = props.showAddedToCart ?? true;
264
+ const cartState = props.cartState;
265
+ const cartCount = props.cartCount;
266
+ const onClickAtc = props.onClickAtc;
267
+ const onCartIconClick = props.onCartIconClick;
268
+
269
+ function _handleAddToCartClick() {
270
+ onClickAtc();
271
+ }
272
+
273
+ const width = props.width ?? '100%';
274
+ const height = props.height ?? 'auto';
275
+
276
+ if(cartState == "errored"){
277
+ return (
278
+ <Animatable.View
279
+ animation="fadeIn"
280
+ duration={500} iterationCount={1}
281
+ easing="ease-in-out"
282
+ style={{
283
+ height: height, width: width
284
+ }}
285
+ >
286
+ <SoldOutButton
287
+ event={event} product={product} baseFontSize={BASE_FONT_SIZE}
288
+ width={width} height={height}
289
+ />
290
+ </Animatable.View>
291
+ );
292
+ }
293
+
294
+ if(cartState == 'init'){
295
+ if(cartCount == 0){
296
+ return(
297
+ <AddToCartInitialButton
298
+ onClick={_handleAddToCartClick}
299
+ event={event} product={product} baseFontSize={BASE_FONT_SIZE}
300
+ width={width} height={height}
301
+ />
302
+ );
303
+ }else{
304
+ if(showAddedToCart){
305
+ return (
306
+ <Animatable.View
307
+ animation="fadeIn"
308
+ duration={500} iterationCount={1}
309
+ easing="ease-in-out"
310
+ style={{
311
+ height: height, width: width
312
+ }}
313
+ >
314
+ <AddedToCartButton
315
+ onClick={_handleAddToCartClick}
316
+ event={event} product={product} baseFontSize={BASE_FONT_SIZE}
317
+ cartCount={cartCount}
318
+ width={width} height={height}
319
+ onCartIconClick={onCartIconClick}
320
+ />
321
+ </Animatable.View>
322
+ );
323
+ }else{
324
+ return(
325
+ <AddToCartInitialButton
326
+ onClick={_handleAddToCartClick}
327
+ event={event} product={product} baseFontSize={BASE_FONT_SIZE}
328
+ width={width} height={height}
329
+ />
330
+ );
331
+ }
332
+ }
333
+ }
334
+
335
+ return(
336
+ <LabelledButton
337
+ onClick={_handleAddToCartClick}
338
+ event={event} product={product} baseFontSize={BASE_FONT_SIZE}
339
+ width={width} height={height}
340
+ title={cartState == "awaitingVariantSelection" ? "Done" : ""}
341
+ showInProgressIcon={cartState == "atcApiCallInProgress" || cartState == "productVariantDetailsApiCallInProgress"}
342
+ />
343
+ );
344
+ }
345
+
346
+ export default AddToCartButton;
@@ -0,0 +1,52 @@
1
+ import * as React from 'react';
2
+ import { useGlobalState } from '../../globals/useAppState_APP.js';
3
+ import { Text, View } from 'react-native';
4
+ import { getPriceStringWithCurrency } from '../../utils/PriceUtils.js';
5
+
6
+ export default function ProductPriceBadge(props) {
7
+ const event = props.event;
8
+ const product = props.product;
9
+ const fontSize = props.fontSize;
10
+ const color = props.color;
11
+
12
+ return (
13
+ <View
14
+ style={{
15
+ width: 'auto', height: 'auto',
16
+ backgroundColor: '#00000022',
17
+ flexDirection: 'row',
18
+ justifyContent: 'space-between', alignItems: 'center',
19
+ borderRadius: 20, overflow: 'hidden',
20
+ paddingLeft: 10, paddingRight: 10, paddingTop: 5, paddingBottom: 5
21
+ }}
22
+ >
23
+ <Text
24
+ numberOfLines={1}
25
+ style={{
26
+ fontWeight: 'bold',
27
+ fontSize: fontSize,
28
+ color: color
29
+ }}
30
+ >
31
+ {getPriceStringWithCurrency(product.price)}
32
+ </Text>
33
+ <Text
34
+ numberOfLines={1}
35
+ >
36
+ {' '}
37
+ </Text>
38
+ <Text
39
+ numberOfLines={1}
40
+ style={{
41
+ fontWeight: 'normal',
42
+ textDecorationLine: 'line-through',
43
+ fontSize: fontSize
44
+ }}
45
+ >
46
+ {getPriceStringWithCurrency(product.compare_price)}
47
+ </Text>
48
+
49
+ </View>
50
+ );
51
+
52
+ }
@@ -0,0 +1,205 @@
1
+ import { getGlobalState, setGlobalState, useGlobalState } from '../../globals/useAppState_APP.js';
2
+ import { Image, Text} from 'react-native';
3
+ import ProductPriceBadge from './ProductPriceBadge.js';
4
+
5
+ import React, { useEffect, useRef, useState } from 'react';
6
+ import { View, TouchableOpacity, ScrollView, Dimensions, TouchableWithoutFeedback } from 'react-native';
7
+ import SelectVariantComponent from './SelectVariantComponent';
8
+ import useAddToCartStates from '../../hooks/useAddToCartStates.js';
9
+ import AddToCartButton from '../cta-buttons/AddToCartButton.jsx';
10
+ import * as Animatable from 'react-native-animatable';
11
+ import { navigateToProductPage } from '../../api/navigation-commands/NavigationCommands.js';
12
+
13
+ export default function ProductTileV1(props) {
14
+ const event = props.event;
15
+ const product = props.product;
16
+
17
+ const [cartState, productVariantOptions, initiateVariantSelection, initiateAtcApiCall, getPriceDetailsFromSelectedOption, initiateAtcCartIconClickAction] = useAddToCartStates(0, product, props.event);
18
+ const [selectedOptions, setSelectedOptions] = useState(null);
19
+ const [tileTapAnimate, setTileTapAnimate] = useState(false);
20
+
21
+ const [productVariantPrice, setProductVariantPrice] = useState(parseFloat(product.price));
22
+ const [productVariantComparePrice, setProductVariantComparePrice] = useState(parseFloat(product.compare_price));
23
+
24
+ const whatmorePrimaryColor = getGlobalState('whatmorePrimaryColor');
25
+ const whatmorePrimaryFont = getGlobalState('whatmorePrimaryFont');
26
+
27
+ const brandDomainContext = getGlobalState('brandDomainContext');
28
+ const whatmoreShopId = getGlobalState('whatmoreShopId');
29
+
30
+ const PRODUCT_TILE_TOTAL_HEIGHT = Dimensions.get('window').height * 0.2;
31
+ const SELECT_VARIANT_TILE_HEIGHT_FACTOR = 3.0;
32
+ const BASE_FONT_SIZE = 20;
33
+
34
+ useEffect(() => {
35
+ if(tileTapAnimate){
36
+ setTimeout(() => {
37
+ setTileTapAnimate(false);
38
+ }, 1000);
39
+ }
40
+ }, [tileTapAnimate]);
41
+
42
+ function _updateSelectedOptions(_selectedOptions){
43
+ setSelectedOptions(_selectedOptions);
44
+
45
+ // set price to selected option
46
+ const optionPriceDetails = getPriceDetailsFromSelectedOption(_selectedOptions);
47
+
48
+ if(optionPriceDetails){
49
+ setProductVariantPrice(optionPriceDetails['price']);
50
+ setProductVariantComparePrice(optionPriceDetails['compare_price']);
51
+ }
52
+ }
53
+
54
+
55
+ return (
56
+ <TouchableWithoutFeedback
57
+ onPress={() => {
58
+ setTileTapAnimate(true);
59
+ navigateToProductPage(product, brandDomainContext, whatmoreShopId);
60
+ }}
61
+ >
62
+ <Animatable.View
63
+ animation={
64
+ tileTapAnimate
65
+ ?
66
+ {
67
+ 0: {
68
+ scale: 1
69
+ },
70
+ 0.5: {
71
+ scale: 0.9
72
+ },
73
+ 1: {
74
+ scale: 1
75
+ }
76
+ }
77
+ :
78
+ null
79
+ }
80
+ duration={100} iterationCount={1}
81
+ easing="ease-out"
82
+ style={{
83
+ width: '100%', height: 'auto',
84
+ flexDirection: 'column', justifyContent: 'flex-start', alignItems: 'center',
85
+ overflow: 'hidden',
86
+ borderRadius: 5, overflow: 'hidden',
87
+ shadowColor: 'black', shadowOpacity: 1, shadowOffset: {width: 0, height: 0},
88
+ shadowRadius: 20, elevation: 10
89
+ }}
90
+ >
91
+ <View
92
+ style={{
93
+ width: '100%', height: 'auto',
94
+ backgroundColor: '#ffffffdd',
95
+ flexDirection: 'row', justifyContent: 'flex-start', alignItems: 'center'
96
+ }}
97
+ >
98
+ <Image
99
+ style={{
100
+ width: 120, height: 120
101
+ }}
102
+ source={{
103
+ uri: product.thumbnail_image,
104
+ }}
105
+ >
106
+
107
+ </Image>
108
+ <View
109
+ style={{
110
+ width: 'auto', height: 'auto',
111
+ flexDirection: 'column', justifyContent: 'space-between', alignItems: 'flex-start',
112
+ paddingLeft: 10, paddingRight: 10
113
+ }}
114
+ >
115
+ <Text
116
+ numberOfLines={2}
117
+ ellipsizeMode='tail'
118
+ style={{
119
+ fontWeight: 'normal',
120
+ fontFamily: whatmorePrimaryFont,
121
+ fontSize: BASE_FONT_SIZE * 1,
122
+ color: 'black'
123
+ }}
124
+ >
125
+ {product.title.toString()}
126
+ </Text>
127
+
128
+ <View
129
+ style={{
130
+ height: 10, width: 'auto'
131
+ }}
132
+ ></View>
133
+
134
+ <ProductPriceBadge product={product} event={event} fontSize={BASE_FONT_SIZE * 1} color={'black'} />
135
+ {/* <Text>
136
+ {product.price.toString()}
137
+ </Text> */}
138
+ </View>
139
+
140
+
141
+ </View>
142
+
143
+ <View
144
+ style={{
145
+ width: '100%', height: 'auto',
146
+ backgroundColor: '#ffffffdd',
147
+ overflow: 'hidden'
148
+ }}
149
+
150
+ >
151
+ {cartState['state'] === "awaitingVariantSelection" && productVariantOptions &&
152
+ <ScrollView
153
+ style={{
154
+ width: '100%',
155
+ height: 'auto',
156
+ marginTop: 5,
157
+ marginBottom: 10,
158
+ maxHeight: PRODUCT_TILE_TOTAL_HEIGHT * SELECT_VARIANT_TILE_HEIGHT_FACTOR
159
+ }}
160
+ >
161
+ <Animatable.View
162
+ animation="fadeIn"
163
+ duration={500} iterationCount={1}
164
+ easing="ease-in-out"
165
+ style={{
166
+ height: 'auto', width: '100%'
167
+ }}
168
+ >
169
+ <SelectVariantComponent
170
+ width='100%'
171
+ height='auto'
172
+ event={event}
173
+ product={product}
174
+ productVariantOptions={productVariantOptions}
175
+ baseFontSize={BASE_FONT_SIZE * 0.9}
176
+ onOptionSelected={_updateSelectedOptions}
177
+ />
178
+ </Animatable.View>
179
+ </ScrollView>
180
+ }
181
+
182
+ <View style={{ flexDirection: 'row', width: '100%', justifyContent: 'center', alignItems: 'center' }}>
183
+ <AddToCartButton
184
+ baseFontSize={BASE_FONT_SIZE * 0.3}
185
+ event={event}
186
+ product={product}
187
+ cartState={cartState['state']}
188
+ cartCount={cartState['cart_count']}
189
+ onClickAtc={(e) => {
190
+ if(cartState['state'] == "init"){
191
+ initiateVariantSelection();
192
+ } else if(cartState['state'] == "awaitingVariantSelection"){
193
+ initiateAtcApiCall(selectedOptions);
194
+ }
195
+ }}
196
+ onCartIconClick={initiateAtcCartIconClickAction}
197
+ height={PRODUCT_TILE_TOTAL_HEIGHT * 0.3}
198
+ />
199
+ </View>
200
+ </View>
201
+ </Animatable.View>
202
+ </TouchableWithoutFeedback>
203
+ );
204
+
205
+ }