@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,159 @@
1
+ const WHATMORE_BACKEND_BASE_URL = "https://api.whatmore.live";
2
+
3
+ export function registerUser(eventId, userName, phoneNumber) {
4
+ const requestBody = {
5
+ "event_id": eventId,
6
+ "customer_name": userName,
7
+ "customer_phone": phoneNumber
8
+ }
9
+ const response = fetch(WHATMORE_BACKEND_BASE_URL + "/notification/register", {
10
+ method: 'POST',
11
+ headers: {
12
+ 'Accept': 'application/json',
13
+ 'Content-Type': 'application/json'
14
+ },
15
+ body: JSON.stringify(requestBody)
16
+ },
17
+ ).then(response => {
18
+ console.debug("user registration response : HTTP " + response.status);
19
+ return response.json();
20
+ }).then(data => {
21
+ console.debug("user registration response : " + JSON.stringify(data));
22
+ }).catch(error => {
23
+ console.error("error while registering user for event : ", error);
24
+ });
25
+ }
26
+
27
+
28
+ export function increaseViewCount(event) {
29
+ console.debug("updating user view count for event.");
30
+ const requestBody = {
31
+ "event_id": event.event_id,
32
+ "increase_by": 1
33
+ }
34
+ const response = fetch(WHATMORE_BACKEND_BASE_URL + "/event/metrics/views", {
35
+ method: 'POST',
36
+ headers: {
37
+ 'Accept': 'application/json',
38
+ 'Content-Type': 'application/json'
39
+ },
40
+ body: JSON.stringify(requestBody)
41
+ },
42
+ ).then(response => {
43
+ console.debug("updating user view count response : HTTP " + response.status);
44
+ return response.json();
45
+ }).then(data => {
46
+ console.debug("updating user view count response : " + JSON.stringify(data));
47
+ }).catch(error => {
48
+ console.error("error updating user view count user for event : ", error);
49
+ });
50
+ }
51
+
52
+
53
+ export function increaseEventProductClickCount(event, product) {
54
+ console.debug("updating event-product click count for event {} and product {}.", event.event_id, product.product_id);
55
+ const requestBody = {
56
+ "event_id": event.event_id,
57
+ "product_id": product.product_id,
58
+ "increase_by": 1
59
+ }
60
+ const response = fetch(WHATMORE_BACKEND_BASE_URL + "/event/metrics/clicks", {
61
+ method: 'POST',
62
+ headers: {
63
+ 'Accept': 'application/json',
64
+ 'Content-Type': 'application/json'
65
+ },
66
+ body: JSON.stringify(requestBody)
67
+ },
68
+ ).then(response => {
69
+ console.debug("updating user event-product click count response : HTTP " + response.status);
70
+ return response.json();
71
+ }).then(data => {
72
+ console.debug("updating user event-product click count response : " + JSON.stringify(data));
73
+ }).catch(error => {
74
+ console.error("error updating user event-product click count for event {} : ", event.event_id, error);
75
+ });
76
+ }
77
+
78
+ export function increaseEventProductAddToCartCount(event, product, quantity) {
79
+ console.debug("updating event-product add to cart count for event {} and product {}.", event.event_id, product.product_id);
80
+ const requestBody = {
81
+ "event_id": event.event_id,
82
+ "product_id": product.product_id,
83
+ "increase_by": quantity
84
+ }
85
+ const response = fetch(WHATMORE_BACKEND_BASE_URL + "/event/metrics/add_to_cart", {
86
+ method: 'POST',
87
+ headers: {
88
+ 'Accept': 'application/json',
89
+ 'Content-Type': 'application/json'
90
+ },
91
+ body: JSON.stringify(requestBody)
92
+ },
93
+ ).then(response => {
94
+ console.debug("updating user event-product add to cart count response : HTTP " + response.status);
95
+ return response.json();
96
+ }).then(data => {
97
+ console.debug("updating user event-product add to cart count response : " + JSON.stringify(data));
98
+ }).catch(error => {
99
+ console.error("error updating user event-product add to cart count for event {} : ", event.event_id, error);
100
+ });
101
+ }
102
+
103
+ export function increaseEventProductBuyNowCount(event, product) {
104
+ console.debug("updating event-product buy now count for event {} and product {}.", event.event_id, product.product_id);
105
+ const requestBody = {
106
+ "event_id": event.event_id,
107
+ "product_id": product.product_id,
108
+ "increase_by": 1
109
+ }
110
+ const response = fetch(WHATMORE_BACKEND_BASE_URL + "/event/metrics/buy_now", {
111
+ method: 'POST',
112
+ headers: {
113
+ 'Accept': 'application/json',
114
+ 'Content-Type': 'application/json'
115
+ },
116
+ body: JSON.stringify(requestBody)
117
+ },
118
+ ).then(response => {
119
+ console.debug("updating user event-product buy now count response : HTTP " + response.status);
120
+ return response.json();
121
+ }).then(data => {
122
+ console.debug("updating user event-product buy now count response : " + JSON.stringify(data));
123
+ }).catch(error => {
124
+ console.error("error updating user event-product buy now count for event {} : ", event.event_id, error);
125
+ });
126
+ }
127
+
128
+ export async function orderTrackingAddToCartEvent(
129
+ {storeId = '', eventId = 0, productId = '', variantId = '', cartToken = '', checkoutToken = '', quantity = 0, price = '', actionType = '', trackingId = ''} = {}
130
+ ) {
131
+ let data = {
132
+ 'event_id': eventId,
133
+ 'product_id': productId,
134
+ 'cart_token': cartToken,
135
+ 'checkout_token': checkoutToken,
136
+ 'quantity': quantity,
137
+ 'price': price,
138
+ 'variant_id': variantId,
139
+ 'action_type': actionType,
140
+ 'tracking_id' : trackingId
141
+ };
142
+ console.debug("initiating ATC metadata event for eventId {}, productId {}, variantId {} ", eventId, productId, variantId);
143
+ const res = await fetch(WHATMORE_BACKEND_BASE_URL + '/cart-metadata/' + storeId, {
144
+ method: 'POST',
145
+ headers: {
146
+ 'Accept': 'application/json',
147
+ 'Content-Type': 'application/json'
148
+ },
149
+ body: JSON.stringify(data)
150
+ }).then(response => {
151
+ console.debug("initiating ATC metadata event for eventId {}, productId {}, variantId {} : HTTP " + response.status);
152
+ return response.json();
153
+ }).catch(error => {
154
+ console.error("error initiating ATC metadata event for eventId {}, productId {}, variantId {} : ", eventId, productId, variantId);
155
+ });
156
+
157
+ return res
158
+ }
159
+
@@ -0,0 +1,31 @@
1
+ import * as React from 'react';
2
+ import { SafeAreaView, StatusBar, View } from "react-native";
3
+ import EventsVerticalSwipeView from './EventsVerticalSwipeView';
4
+ import { getGlobalState } from '../globals/useAppState_APP';
5
+ import EventsVerticalSwipeViewNoModal from './EventsVerticalSwipeViewNoModal';
6
+
7
+ export default function EventClickComponent(props) {
8
+ const modalUsed = getGlobalState('modalUsed');
9
+
10
+ return (
11
+ <SafeAreaView style={{
12
+ width: '100%',
13
+ height: '100%'
14
+ }}>
15
+ <StatusBar
16
+ animated={true}
17
+ backgroundColor='black'
18
+ barStyle={"default"}
19
+ hidden={false}
20
+ showHideTransition={"fade"}
21
+ />
22
+ {
23
+ modalUsed
24
+ ?
25
+ <EventsVerticalSwipeView events={props.events} initialEventIndex={props.initialEventIndex} />
26
+ :
27
+ <EventsVerticalSwipeViewNoModal events={props.events} initialEventIndex={props.initialEventIndex} />
28
+ }
29
+ </SafeAreaView>
30
+ );
31
+ }
@@ -0,0 +1,75 @@
1
+ import * as React from 'react';
2
+ import { Text, TouchableOpacity, View } from "react-native";
3
+ import { getGlobalState, setGlobalState, useGlobalState } from '../globals/useAppState_APP.js';
4
+ import AppMuteUnmuteIcon from './commons/AppMuteUnmuteIcon.js';
5
+ import ProductTileV1 from './product-tiles/ProductTileV1.js';
6
+
7
+ export default function EventShoppingOverlay(props) {
8
+ var event = props.event;
9
+
10
+ return (
11
+ <View
12
+ style={{
13
+ width: '100%', height: '100%',
14
+ flex: 1, flexDirection: 'column',
15
+ justifyContent: 'flex-start', alignItems: 'center'
16
+ }}
17
+ >
18
+ {/* <Text style={{fontSize: 20, fontWeight: 'bold'}}> ABCD </Text> */}
19
+
20
+ <View
21
+ style={{
22
+ width: '100%',
23
+ height: 'auto',
24
+ flexDirection: 'row',
25
+ justifyContent: 'flex-end', alignItems: 'center', backgroundColor: 'transparent',
26
+ paddingLeft: 10, paddingRight: 10, paddingTop: 10, paddingBottom: 10
27
+ }}
28
+ >
29
+ <AppMuteUnmuteIcon />
30
+ </View>
31
+ <View
32
+ style={{
33
+ width: '100%',
34
+ height: 'auto',
35
+ flex: 1,
36
+ flexDirection: 'column',
37
+ justifyContent: 'space-between', alignItems: 'center', backgroundColor: 'transparent'
38
+ }}
39
+ >
40
+ <TouchableOpacity
41
+ style={{
42
+ width: '100%',
43
+ height: 'auto',
44
+ flex: 1,
45
+ flexDirection: 'row',
46
+ justifyContent: 'center', alignItems: 'center', backgroundColor: 'transparent'
47
+ }}
48
+ onPress={()=>{
49
+ setGlobalState("isAppMuted", getGlobalState("isAppMuted") ? false : true);
50
+ }}
51
+ >
52
+ </TouchableOpacity>
53
+ <View
54
+ // add style here to make it horizontally scrollable
55
+ style={{
56
+ width: '100%', height: 'auto',
57
+ flexDirection: 'row', justifyContent: 'space-around', alignItems: 'center',
58
+ paddingLeft: 10, paddingRight: 10, paddingBottom: 10
59
+ }}
60
+ >
61
+ {
62
+ event.products.map((product, index) => {
63
+ return (
64
+ <ProductTileV1 key={index} event={event} product={product} />
65
+ );
66
+ })
67
+ }
68
+ </View>
69
+ </View>
70
+
71
+
72
+ </View>
73
+ );
74
+
75
+ }
@@ -0,0 +1,32 @@
1
+ import * as React from 'react';
2
+ import { View } from "react-native";
3
+ import AppVideoPlayer from './video-player/AppVideoPlayer.js'
4
+ import { useGlobalState } from '../globals/useAppState_APP.js';
5
+ import EventShoppingOverlay from './EventShoppingOverlay.js';
6
+
7
+ export default function EventShoppingView(props) {
8
+ const [activeSwiperIndex, setActiveSwiperIndex] = useGlobalState('verticalSwiperViewActiveIndex');
9
+
10
+ var event = props.event;
11
+ var eventIndex = props.eventIndex;
12
+
13
+ return (
14
+ <View
15
+ style={{
16
+ width: '100%', height: '100%'
17
+ }}
18
+ >
19
+ <AppVideoPlayer videoUrl={event.event_hls_url} isMuted={activeSwiperIndex != eventIndex} />
20
+ <View
21
+ style={{
22
+ width: '100%', height: '100%',
23
+ position: 'relative', zIndex: 1,
24
+ backgroundColor: 'transparent',
25
+ }}
26
+ >
27
+ <EventShoppingOverlay event={event} />
28
+ </View>
29
+ </View>
30
+ );
31
+
32
+ }
@@ -0,0 +1,60 @@
1
+ import * as React from 'react';
2
+ import { Dimensions, Modal, View } from "react-native";
3
+ import AppVideoPlayer from './video-player/AppVideoPlayer.js'
4
+ import Swiper from 'react-native-swiper'
5
+ import { InView } from 'react-native-intersection-observer'
6
+ import EventShoppingView from './EventShoppingView.js';
7
+ import { setGlobalState } from '../globals/useAppState_APP.js';
8
+
9
+ export default function EventsVerticalSwipeView(props) {
10
+ const [modalVisible, setModalVisible] = React.useState(true);
11
+ var events = props.events;
12
+
13
+ return (
14
+ <Modal
15
+ style={{
16
+ flex: 1,
17
+ height: '100%',
18
+ width: '100%'
19
+ }}
20
+ animationType="slide"
21
+ transparent={true}
22
+ visible={modalVisible}
23
+ onRequestClose={() => {
24
+ setModalVisible(!modalVisible);
25
+ }}
26
+ >
27
+ <Swiper
28
+ style={{}}
29
+ horizontal={false}
30
+ loop={true}
31
+ showsPagination={false}
32
+ loadMinimal={true}
33
+ loadMinimalSize={1}
34
+ index={props.initialEventIndex}
35
+ onIndexChanged={(index) => {
36
+ setGlobalState('verticalSwiperViewActiveIndex', index);
37
+ }}
38
+
39
+ >
40
+ {
41
+ events.map((event, eventIndex) => {
42
+ return(
43
+ <View
44
+ key={event.event_id}
45
+ style={{
46
+ flex: 1,
47
+ justifyContent: 'center',
48
+ alignItems: 'center',
49
+ backgroundColor: 'transparent'
50
+ }}
51
+ >
52
+ <EventShoppingView event={event} eventIndex={eventIndex} />
53
+ </View>
54
+ )
55
+ })
56
+ }
57
+ </Swiper>
58
+ </Modal>
59
+ );
60
+ }
@@ -0,0 +1,52 @@
1
+ import * as React from 'react';
2
+ import { Dimensions, Modal, View } from "react-native";
3
+ import AppVideoPlayer from './video-player/AppVideoPlayer.js'
4
+ import Swiper from 'react-native-swiper'
5
+ import { InView } from 'react-native-intersection-observer'
6
+ import EventShoppingView from './EventShoppingView.js';
7
+ import { setGlobalState } from '../globals/useAppState_APP.js';
8
+
9
+ export default function EventsVerticalSwipeViewNoModal(props) {
10
+ var events = props.events;
11
+ return (
12
+ <View
13
+ style={{
14
+ flex: 1,
15
+ height: '100%',
16
+ width: '100%'
17
+ }}
18
+ >
19
+ <Swiper
20
+ style={{}}
21
+ horizontal={false}
22
+ loop={true}
23
+ showsPagination={false}
24
+ loadMinimal={true}
25
+ loadMinimalSize={1}
26
+ index={props.initialEventIndex}
27
+ onIndexChanged={(index) => {
28
+ setGlobalState('verticalSwiperViewActiveIndex', index);
29
+ }}
30
+
31
+ >
32
+ {
33
+ events.map((event, eventIndex) => {
34
+ return(
35
+ <View
36
+ key={event.event_id}
37
+ style={{
38
+ flex: 1,
39
+ justifyContent: 'center',
40
+ alignItems: 'center',
41
+ backgroundColor: 'transparent'
42
+ }}
43
+ >
44
+ <EventShoppingView event={event} eventIndex={eventIndex} />
45
+ </View>
46
+ )
47
+ })
48
+ }
49
+ </Swiper>
50
+ </View>
51
+ );
52
+ }
@@ -0,0 +1,89 @@
1
+ import * as React from 'react';
2
+ import { useState, useEffect } from 'react';
3
+ import EventClickComponent from "./EventClickComponent";
4
+ import { WHATMORE_BACKEND_BASE_URL } from '../constants/AppGlobalVars';
5
+ import { extractEvents, removeInvalidEvents } from '../utils/EventSanityUtils';
6
+ import { View } from 'react-native';
7
+
8
+ const logEventData = false;
9
+
10
+ function logResponse(data, response = '200'){
11
+ // log response details
12
+ console.log("###=======Whatmore Log start.======####");
13
+ console.log("Fetched Events from Whatmore")
14
+ console.log("response status code : " + response.status)
15
+ console.log("total events received : " + (data && data.length));
16
+ logEventData && console.log("events data : " + JSON.stringify(data));
17
+ console.log("###=======Whatmore Log end.======####")
18
+ }
19
+
20
+ function transformEventData(data, brandId){
21
+ if(data == null){
22
+ return;
23
+ }
24
+
25
+ var index = 0;
26
+ data.forEach((event) => {
27
+ event.index = index++;
28
+ });
29
+
30
+ }
31
+
32
+ export default function WhatmoreRootComponent(props) {
33
+ const [data, setData] = useState();
34
+ const [error, setError] = useState();
35
+
36
+ var brandId = props.shopId;
37
+ var eventStatuses = "live,upcoming";
38
+ var isActive = "true";
39
+
40
+ var whatmoreBackendEventsUrl = WHATMORE_BACKEND_BASE_URL + "/v1/events/";
41
+
42
+ /**
43
+ * external network calls
44
+ */
45
+
46
+ useEffect(() => {
47
+ var url;
48
+ url = whatmoreBackendEventsUrl.toString() + brandId.toString()
49
+ + '?status=' + eventStatuses.toString()
50
+ + '&is_active=' + isActive.toString();
51
+ fetch(url)
52
+ .then((response) => response.json())
53
+ .then((data) => {
54
+ return extractEvents(data, false);
55
+ })
56
+ .then((data) => {
57
+ logEventData && console.log("initial received events size : " + data.length);
58
+ return removeInvalidEvents(data, brandId);
59
+ })
60
+ .then((data) => {
61
+ setData(data);
62
+ })
63
+ .catch((err) => {
64
+ setError(err.message);
65
+ });
66
+ }, []);
67
+
68
+ // show nothing if any errors occurs or if no close events are scheduled or if still loading
69
+ if((data == null || data.length == 0) || error){
70
+ return (
71
+ <View
72
+ style={{
73
+ width: '100%',
74
+ height: '100%'
75
+ }}
76
+ >
77
+ </View>
78
+ );
79
+ }
80
+
81
+ logEventData && logResponse(data);
82
+ transformEventData(data, brandId);
83
+ logEventData && console.log("final transformed events data : " + JSON.stringify(data));
84
+ logEventData && console.log("valid events size : " + data.length);
85
+
86
+ return (
87
+ <EventClickComponent events={data} initialEventIndex={0} />
88
+ );
89
+ }
@@ -0,0 +1,36 @@
1
+ import * as React from 'react';
2
+ import { getGlobalState, setGlobalState, useGlobalState } from '../../globals/useAppState_APP.js';
3
+ import { Text, TouchableOpacity, View } from "react-native";
4
+ import UnmuteIcon from '../../icons/icon-components/UnmuteIcon.jsx';
5
+ import MuteIcon from '../../icons/icon-components/MuteIcon.jsx';
6
+
7
+ export default function AppMuteUnmuteIcon(props) {
8
+ const [isAppMuted] = useGlobalState("isAppMuted");
9
+
10
+ return (
11
+ isAppMuted
12
+ ?
13
+ <TouchableOpacity
14
+ style={{
15
+ padding: 10
16
+ }}
17
+ onPress={()=>{
18
+ setGlobalState("isAppMuted", false);
19
+ }}
20
+ >
21
+ <MuteIcon color={"#F5F5F5"} size={40} />
22
+ </TouchableOpacity>
23
+ :
24
+ <TouchableOpacity
25
+ style={{
26
+ padding: 10
27
+ }}
28
+ onPress={()=>{
29
+ setGlobalState("isAppMuted", true);
30
+ }}
31
+ >
32
+ <UnmuteIcon color={"#F5F5F5"} size={40} />
33
+ </TouchableOpacity>
34
+ );
35
+
36
+ }