@moneylion/react-native-offer-carousel 1.2.1 → 1.3.0
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/lib/commonjs/capabilities/offer-catalog/src/utils/getEventCallbackContext.js +11 -0
- package/lib/commonjs/capabilities/offer-catalog/src/utils/getEventCallbackContext.js.map +1 -0
- package/lib/commonjs/components/Button/index.js +4 -2
- package/lib/commonjs/components/Button/index.js.map +1 -1
- package/lib/commonjs/components/Common/BaseOfferCard/Stat/Stat.js +1 -1
- package/lib/commonjs/components/Common/BaseOfferCard/Stat/Stat.js.map +1 -1
- package/lib/commonjs/components/Common/BaseOfferCard/index.js +15 -8
- package/lib/commonjs/components/Common/BaseOfferCard/index.js.map +1 -1
- package/lib/commonjs/components/Common/DynamicOfferCard/CallToAction.js.map +1 -1
- package/lib/commonjs/components/Common/DynamicOfferCard/SeeMore.js +6 -3
- package/lib/commonjs/components/Common/DynamicOfferCard/SeeMore.js.map +1 -1
- package/lib/commonjs/components/Common/DynamicOfferCard/index.js +7 -3
- package/lib/commonjs/components/Common/DynamicOfferCard/index.js.map +1 -1
- package/lib/commonjs/components/DynamicOffers/DynamicOffers.js +10 -2
- package/lib/commonjs/components/DynamicOffers/DynamicOffers.js.map +1 -1
- package/lib/commonjs/components/DynamicOffers/Render/DynamicOffersRender.js +180 -63
- package/lib/commonjs/components/DynamicOffers/Render/DynamicOffersRender.js.map +1 -1
- package/lib/commonjs/components/DynamicOffers/Render/FallbackOfferTemplate.js +4 -2
- package/lib/commonjs/components/DynamicOffers/Render/FallbackOfferTemplate.js.map +1 -1
- package/lib/commonjs/components/DynamicOffers/Render/Offer.js +15 -5
- package/lib/commonjs/components/DynamicOffers/Render/Offer.js.map +1 -1
- package/lib/commonjs/components/DynamicOffers/SeeAllButton.js +30 -0
- package/lib/commonjs/components/DynamicOffers/SeeAllButton.js.map +1 -0
- package/lib/commonjs/components/Layouts/CreditCardOfferCard/index.js +9 -4
- package/lib/commonjs/components/Layouts/CreditCardOfferCard/index.js.map +1 -1
- package/lib/commonjs/components/Layouts/DefaultOfferCard/index.js +8 -3
- package/lib/commonjs/components/Layouts/DefaultOfferCard/index.js.map +1 -1
- package/lib/commonjs/components/Layouts/HeadlineWithDescriptionCard/index.js +8 -3
- package/lib/commonjs/components/Layouts/HeadlineWithDescriptionCard/index.js.map +1 -1
- package/lib/commonjs/components/Modal/AllOffersModal.js +180 -0
- package/lib/commonjs/components/Modal/AllOffersModal.js.map +1 -0
- package/lib/commonjs/components/Modal/OfferDetailsModal.js.map +1 -1
- package/lib/commonjs/components/MoneyLionOfferCarousel.js +2 -1
- package/lib/commonjs/components/MoneyLionOfferCarousel.js.map +1 -1
- package/lib/commonjs/context/EventHandlerProvider.js.map +1 -1
- package/lib/module/capabilities/offer-catalog/src/utils/getEventCallbackContext.js +4 -0
- package/lib/module/capabilities/offer-catalog/src/utils/getEventCallbackContext.js.map +1 -0
- package/lib/module/components/Button/index.js +4 -2
- package/lib/module/components/Button/index.js.map +1 -1
- package/lib/module/components/Common/BaseOfferCard/Stat/Stat.js +1 -1
- package/lib/module/components/Common/BaseOfferCard/Stat/Stat.js.map +1 -1
- package/lib/module/components/Common/BaseOfferCard/index.js +16 -9
- package/lib/module/components/Common/BaseOfferCard/index.js.map +1 -1
- package/lib/module/components/Common/DynamicOfferCard/CallToAction.js.map +1 -1
- package/lib/module/components/Common/DynamicOfferCard/SeeMore.js +6 -3
- package/lib/module/components/Common/DynamicOfferCard/SeeMore.js.map +1 -1
- package/lib/module/components/Common/DynamicOfferCard/index.js +7 -3
- package/lib/module/components/Common/DynamicOfferCard/index.js.map +1 -1
- package/lib/module/components/DynamicOffers/DynamicOffers.js +10 -2
- package/lib/module/components/DynamicOffers/DynamicOffers.js.map +1 -1
- package/lib/module/components/DynamicOffers/Render/DynamicOffersRender.js +180 -63
- package/lib/module/components/DynamicOffers/Render/DynamicOffersRender.js.map +1 -1
- package/lib/module/components/DynamicOffers/Render/FallbackOfferTemplate.js +4 -2
- package/lib/module/components/DynamicOffers/Render/FallbackOfferTemplate.js.map +1 -1
- package/lib/module/components/DynamicOffers/Render/Offer.js +15 -5
- package/lib/module/components/DynamicOffers/Render/Offer.js.map +1 -1
- package/lib/module/components/DynamicOffers/SeeAllButton.js +22 -0
- package/lib/module/components/DynamicOffers/SeeAllButton.js.map +1 -0
- package/lib/module/components/Layouts/CreditCardOfferCard/index.js +9 -4
- package/lib/module/components/Layouts/CreditCardOfferCard/index.js.map +1 -1
- package/lib/module/components/Layouts/DefaultOfferCard/index.js +8 -3
- package/lib/module/components/Layouts/DefaultOfferCard/index.js.map +1 -1
- package/lib/module/components/Layouts/HeadlineWithDescriptionCard/index.js +8 -3
- package/lib/module/components/Layouts/HeadlineWithDescriptionCard/index.js.map +1 -1
- package/lib/module/components/Modal/AllOffersModal.js +170 -0
- package/lib/module/components/Modal/AllOffersModal.js.map +1 -0
- package/lib/module/components/Modal/OfferDetailsModal.js.map +1 -1
- package/lib/module/components/MoneyLionOfferCarousel.js +2 -1
- package/lib/module/components/MoneyLionOfferCarousel.js.map +1 -1
- package/lib/module/context/EventHandlerProvider.js.map +1 -1
- package/lib/typescript/src/capabilities/offer-catalog/src/utils/getEventCallbackContext.d.ts +2 -0
- package/lib/typescript/src/capabilities/offer-catalog/src/utils/getEventCallbackContext.d.ts.map +1 -0
- package/lib/typescript/src/components/Button/index.d.ts +3 -0
- package/lib/typescript/src/components/Button/index.d.ts.map +1 -1
- package/lib/typescript/src/components/Common/BaseOfferCard/index.d.ts +2 -1
- package/lib/typescript/src/components/Common/BaseOfferCard/index.d.ts.map +1 -1
- package/lib/typescript/src/components/Common/DynamicOfferCard/CallToAction.d.ts +2 -2
- package/lib/typescript/src/components/Common/DynamicOfferCard/CallToAction.d.ts.map +1 -1
- package/lib/typescript/src/components/Common/DynamicOfferCard/SeeMore.d.ts +2 -1
- package/lib/typescript/src/components/Common/DynamicOfferCard/SeeMore.d.ts.map +1 -1
- package/lib/typescript/src/components/Common/DynamicOfferCard/index.d.ts +3 -1
- package/lib/typescript/src/components/Common/DynamicOfferCard/index.d.ts.map +1 -1
- package/lib/typescript/src/components/DynamicOffers/DynamicOffers.d.ts +2 -2
- package/lib/typescript/src/components/DynamicOffers/DynamicOffers.d.ts.map +1 -1
- package/lib/typescript/src/components/DynamicOffers/Render/DynamicOffersRender.d.ts +8 -1
- package/lib/typescript/src/components/DynamicOffers/Render/DynamicOffersRender.d.ts.map +1 -1
- package/lib/typescript/src/components/DynamicOffers/Render/FallbackOfferTemplate.d.ts +2 -1
- package/lib/typescript/src/components/DynamicOffers/Render/FallbackOfferTemplate.d.ts.map +1 -1
- package/lib/typescript/src/components/DynamicOffers/Render/Offer.d.ts +7 -2
- package/lib/typescript/src/components/DynamicOffers/Render/Offer.d.ts.map +1 -1
- package/lib/typescript/src/components/DynamicOffers/SeeAllButton.d.ts +7 -0
- package/lib/typescript/src/components/DynamicOffers/SeeAllButton.d.ts.map +1 -0
- package/lib/typescript/src/components/Layouts/CreditCardOfferCard/index.d.ts +3 -1
- package/lib/typescript/src/components/Layouts/CreditCardOfferCard/index.d.ts.map +1 -1
- package/lib/typescript/src/components/Layouts/DefaultOfferCard/index.d.ts +3 -1
- package/lib/typescript/src/components/Layouts/DefaultOfferCard/index.d.ts.map +1 -1
- package/lib/typescript/src/components/Layouts/HeadlineWithDescriptionCard/index.d.ts +3 -1
- package/lib/typescript/src/components/Layouts/HeadlineWithDescriptionCard/index.d.ts.map +1 -1
- package/lib/typescript/src/components/Modal/AllOffersModal.d.ts +10 -0
- package/lib/typescript/src/components/Modal/AllOffersModal.d.ts.map +1 -0
- package/lib/typescript/src/components/MoneyLionOfferCarousel.d.ts.map +1 -1
- package/lib/typescript/src/context/EventHandlerProvider.d.ts +4 -2
- package/lib/typescript/src/context/EventHandlerProvider.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/capabilities/offer-catalog/src/utils/getEventCallbackContext.ts +3 -0
- package/src/components/Button/index.tsx +7 -3
- package/src/components/Common/BaseOfferCard/Stat/Stat.tsx +1 -1
- package/src/components/Common/BaseOfferCard/index.tsx +19 -14
- package/src/components/Common/DynamicOfferCard/CallToAction.tsx +10 -8
- package/src/components/Common/DynamicOfferCard/SeeMore.tsx +16 -2
- package/src/components/Common/DynamicOfferCard/index.tsx +10 -2
- package/src/components/DynamicOffers/DynamicOffers.tsx +16 -1
- package/src/components/DynamicOffers/Render/DynamicOffersRender.tsx +227 -75
- package/src/components/DynamicOffers/Render/FallbackOfferTemplate.tsx +3 -0
- package/src/components/DynamicOffers/Render/Offer.tsx +17 -0
- package/src/components/DynamicOffers/SeeAllButton.tsx +31 -0
- package/src/components/Layouts/CreditCardOfferCard/index.tsx +15 -3
- package/src/components/Layouts/DefaultOfferCard/index.tsx +13 -1
- package/src/components/Layouts/HeadlineWithDescriptionCard/index.tsx +13 -1
- package/src/components/Modal/AllOffersModal.tsx +213 -0
- package/src/components/Modal/OfferDetailsModal.tsx +1 -1
- package/src/components/MoneyLionOfferCarousel.tsx +1 -0
- package/src/context/EventHandlerProvider.tsx +4 -2
|
@@ -20,6 +20,10 @@ import {
|
|
|
20
20
|
import { useEventHandler } from "../../../context/EventHandlerProvider";
|
|
21
21
|
import type { BaseOffer } from "../../../capabilities/offer-catalog/src";
|
|
22
22
|
import Text from "../../Text";
|
|
23
|
+
import { SeeAllButton } from "../SeeAllButton";
|
|
24
|
+
import type { CnfContext } from "../../../capabilities/core/src/system/cnfContext/CnfContext";
|
|
25
|
+
import { AllOffersModal } from "../../Modal/AllOffersModal";
|
|
26
|
+
import { getEventCallbackContext } from "../../../capabilities/offer-catalog/src/utils/getEventCallbackContext";
|
|
23
27
|
|
|
24
28
|
export type DynamicOffersRenderProps = {
|
|
25
29
|
title?: string;
|
|
@@ -34,6 +38,12 @@ export type DynamicOffersRenderProps = {
|
|
|
34
38
|
productTypeBuilder: typeof builder;
|
|
35
39
|
showCardBorder: boolean;
|
|
36
40
|
showProductTypeLabel?: boolean;
|
|
41
|
+
isHorizontalScroll?: boolean;
|
|
42
|
+
/** Used for vertical scroll only */
|
|
43
|
+
parentScrollOffset?: number;
|
|
44
|
+
/** Used for vertical scroll only */
|
|
45
|
+
parentScrollViewHeight?: number;
|
|
46
|
+
brand: CnfContext["brand"];
|
|
37
47
|
};
|
|
38
48
|
|
|
39
49
|
export const DynamicOffersRender = ({
|
|
@@ -45,13 +55,18 @@ export const DynamicOffersRender = ({
|
|
|
45
55
|
fallbackTemplate,
|
|
46
56
|
showCardBorder,
|
|
47
57
|
showProductTypeLabel,
|
|
58
|
+
isHorizontalScroll = true,
|
|
59
|
+
parentScrollOffset = 0,
|
|
60
|
+
parentScrollViewHeight = 0,
|
|
61
|
+
brand,
|
|
48
62
|
}: DynamicOffersRenderProps) => {
|
|
49
63
|
// Performance optimization: store card measurements
|
|
50
64
|
const [cardLayouts, setCardLayouts] = useState<
|
|
51
|
-
Record<string, { x: number; width: number }>
|
|
65
|
+
Record<string, { x: number; width: number; y: number; height: number }>
|
|
52
66
|
>({});
|
|
53
67
|
const [scrollViewWidth, setScrollViewWidth] = useState(0);
|
|
54
68
|
const [scrollOffset, setScrollOffset] = useState(0);
|
|
69
|
+
const [allOffersModalVisible, setAllOffersModalVisible] = useState(false);
|
|
55
70
|
|
|
56
71
|
// Callback to fire when offer is displayed in viewport
|
|
57
72
|
const { onOfferDisplayInViewport, leadUuid, rateTableUuid } =
|
|
@@ -70,86 +85,158 @@ export const DynamicOffersRender = ({
|
|
|
70
85
|
setCardLayouts({});
|
|
71
86
|
}, [offers]);
|
|
72
87
|
|
|
73
|
-
// Handle card layout measurements
|
|
74
88
|
const handleCardLayout = useCallback(
|
|
75
89
|
(offerId: string, event: LayoutChangeEvent) => {
|
|
76
|
-
const { x, width } = event.nativeEvent.layout;
|
|
90
|
+
const { x, y, width, height } = event.nativeEvent.layout;
|
|
77
91
|
|
|
78
92
|
setCardLayouts((prev) => {
|
|
93
|
+
const positionValue = isHorizontalScroll ? x : y;
|
|
94
|
+
const sizeValue = isHorizontalScroll ? width : height;
|
|
95
|
+
|
|
79
96
|
// Only update if measurements changed
|
|
80
97
|
if (
|
|
81
98
|
prev[offerId] &&
|
|
82
|
-
prev[offerId]?.x ===
|
|
83
|
-
prev[offerId]?.width ===
|
|
99
|
+
prev[offerId]?.x === positionValue &&
|
|
100
|
+
prev[offerId]?.width === sizeValue
|
|
84
101
|
) {
|
|
85
102
|
return prev;
|
|
86
103
|
}
|
|
87
|
-
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
...prev,
|
|
107
|
+
[offerId]: {
|
|
108
|
+
x: positionValue,
|
|
109
|
+
width: sizeValue,
|
|
110
|
+
y: y,
|
|
111
|
+
height: height,
|
|
112
|
+
},
|
|
113
|
+
};
|
|
88
114
|
});
|
|
89
115
|
},
|
|
90
|
-
[]
|
|
116
|
+
[isHorizontalScroll]
|
|
91
117
|
);
|
|
92
118
|
|
|
93
119
|
// Check which offers are visible
|
|
94
120
|
const checkVisibleOffers = useCallback(() => {
|
|
95
|
-
if (!onOfferDisplayInViewport
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
121
|
+
if (!onOfferDisplayInViewport) return;
|
|
122
|
+
|
|
123
|
+
if (isHorizontalScroll) {
|
|
124
|
+
if (!scrollViewWidth) return;
|
|
125
|
+
|
|
126
|
+
// Calculate visible range for horizontal scrolling
|
|
127
|
+
const visibleLeft = scrollOffset;
|
|
128
|
+
const visibleRight = scrollOffset + scrollViewWidth;
|
|
129
|
+
|
|
130
|
+
// Check each offer's visibility
|
|
131
|
+
offers.forEach((offer, index) => {
|
|
132
|
+
// Skip if already tracked
|
|
133
|
+
if (viewedOffers.current.has(offer.uuid)) return;
|
|
134
|
+
|
|
135
|
+
const layout = cardLayouts[offer.uuid];
|
|
136
|
+
if (!layout) return;
|
|
137
|
+
|
|
138
|
+
// Calculate card position
|
|
139
|
+
const cardLeft = layout.x;
|
|
140
|
+
const cardRight = cardLeft + layout.width;
|
|
141
|
+
const cardWidth = layout.width;
|
|
142
|
+
|
|
143
|
+
// Calculate visibility
|
|
144
|
+
let visibleWidth = 0;
|
|
145
|
+
|
|
146
|
+
if (cardLeft >= visibleLeft && cardRight <= visibleRight) {
|
|
147
|
+
// Fully visible
|
|
148
|
+
visibleWidth = cardWidth;
|
|
149
|
+
} else if (cardLeft < visibleLeft && cardRight > visibleLeft) {
|
|
150
|
+
// Partly visible from left
|
|
151
|
+
visibleWidth = cardRight - visibleLeft;
|
|
152
|
+
} else if (cardLeft < visibleRight && cardRight > visibleRight) {
|
|
153
|
+
// Partly visible from right
|
|
154
|
+
visibleWidth = visibleRight - cardLeft;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Calculate visibility percentage
|
|
158
|
+
const visibilityPercentage = (visibleWidth / cardWidth) * 100;
|
|
159
|
+
|
|
160
|
+
// If ≥50% visible, track it and fire callback
|
|
161
|
+
if (visibilityPercentage >= 50) {
|
|
162
|
+
viewedOffers.current.add(offer.uuid);
|
|
163
|
+
|
|
164
|
+
onOfferDisplayInViewport({
|
|
165
|
+
timestamp: new Date().toISOString(),
|
|
166
|
+
offerUuid: offer.uuid,
|
|
167
|
+
leadUuid,
|
|
168
|
+
offer,
|
|
169
|
+
offerIndex: index,
|
|
170
|
+
rateTableUuid,
|
|
171
|
+
context: getEventCallbackContext(isHorizontalScroll),
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
} else {
|
|
176
|
+
// For vertical scrolling with parent scroll view
|
|
177
|
+
if (parentScrollViewHeight > 0) {
|
|
178
|
+
// Calculate visible range for vertical scrolling
|
|
179
|
+
const visibleTop = parentScrollOffset;
|
|
180
|
+
const visibleBottom = parentScrollOffset + parentScrollViewHeight;
|
|
181
|
+
|
|
182
|
+
// Check each offer's visibility
|
|
183
|
+
offers.forEach((offer, index) => {
|
|
184
|
+
// Skip if already tracked
|
|
185
|
+
if (viewedOffers.current.has(offer.uuid)) return;
|
|
186
|
+
|
|
187
|
+
const layout = cardLayouts[offer.uuid];
|
|
188
|
+
if (!layout) return;
|
|
189
|
+
|
|
190
|
+
// Calculate card position
|
|
191
|
+
const cardTop = layout.y;
|
|
192
|
+
const cardHeight = layout.height;
|
|
193
|
+
const cardBottom = cardTop + cardHeight;
|
|
194
|
+
|
|
195
|
+
// Calculate visibility
|
|
196
|
+
let visibleHeight = 0;
|
|
127
197
|
|
|
128
|
-
|
|
129
|
-
|
|
198
|
+
if (cardTop >= visibleTop && cardBottom <= visibleBottom) {
|
|
199
|
+
// Fully visible
|
|
200
|
+
visibleHeight = cardHeight;
|
|
201
|
+
} else if (cardTop < visibleTop && cardBottom > visibleTop) {
|
|
202
|
+
// Partly visible from top
|
|
203
|
+
visibleHeight = cardBottom - visibleTop;
|
|
204
|
+
} else if (cardTop < visibleBottom && cardBottom > visibleBottom) {
|
|
205
|
+
// Partly visible from bottom
|
|
206
|
+
visibleHeight = visibleBottom - cardTop;
|
|
207
|
+
}
|
|
130
208
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
viewedOffers.current.add(offer.uuid);
|
|
209
|
+
// Calculate visibility percentage
|
|
210
|
+
const visibilityPercentage = (visibleHeight / cardHeight) * 100;
|
|
134
211
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
212
|
+
// If ≥50% visible, track it and fire callback
|
|
213
|
+
if (visibilityPercentage >= 50) {
|
|
214
|
+
viewedOffers.current.add(offer.uuid);
|
|
215
|
+
|
|
216
|
+
onOfferDisplayInViewport({
|
|
217
|
+
timestamp: new Date().toISOString(),
|
|
218
|
+
offerUuid: offer.uuid,
|
|
219
|
+
leadUuid,
|
|
220
|
+
offer,
|
|
221
|
+
offerIndex: index,
|
|
222
|
+
rateTableUuid,
|
|
223
|
+
context: getEventCallbackContext(isHorizontalScroll),
|
|
224
|
+
});
|
|
225
|
+
}
|
|
142
226
|
});
|
|
143
227
|
}
|
|
144
|
-
}
|
|
228
|
+
}
|
|
145
229
|
}, [
|
|
146
230
|
cardLayouts,
|
|
231
|
+
isHorizontalScroll,
|
|
147
232
|
leadUuid,
|
|
148
233
|
offers,
|
|
149
234
|
onOfferDisplayInViewport,
|
|
150
235
|
rateTableUuid,
|
|
151
236
|
scrollOffset,
|
|
152
237
|
scrollViewWidth,
|
|
238
|
+
parentScrollOffset,
|
|
239
|
+
parentScrollViewHeight,
|
|
153
240
|
]);
|
|
154
241
|
|
|
155
242
|
// Handle ScrollView layout
|
|
@@ -178,6 +265,8 @@ export const DynamicOffersRender = ({
|
|
|
178
265
|
|
|
179
266
|
// Memoize offer components to prevent unnecessary re-renders
|
|
180
267
|
const offerComponents = useMemo(() => {
|
|
268
|
+
const fullCardWidth = offers.length === 1 || !isHorizontalScroll;
|
|
269
|
+
|
|
181
270
|
// Create offer components
|
|
182
271
|
return displayLayout === "fixed"
|
|
183
272
|
? [
|
|
@@ -188,6 +277,8 @@ export const DynamicOffersRender = ({
|
|
|
188
277
|
showProductTypeLabel,
|
|
189
278
|
fallbackTemplate,
|
|
190
279
|
showCardBorder,
|
|
280
|
+
fullCardWidth,
|
|
281
|
+
isHorizontalScroll,
|
|
191
282
|
}),
|
|
192
283
|
]
|
|
193
284
|
: Offer({
|
|
@@ -196,6 +287,8 @@ export const DynamicOffersRender = ({
|
|
|
196
287
|
showProductTypeLabel,
|
|
197
288
|
fallbackTemplate,
|
|
198
289
|
showCardBorder,
|
|
290
|
+
fullCardWidth,
|
|
291
|
+
isHorizontalScroll,
|
|
199
292
|
});
|
|
200
293
|
}, [
|
|
201
294
|
displayLayout,
|
|
@@ -204,6 +297,7 @@ export const DynamicOffersRender = ({
|
|
|
204
297
|
showProductTypeLabel,
|
|
205
298
|
fallbackTemplate,
|
|
206
299
|
showCardBorder,
|
|
300
|
+
isHorizontalScroll,
|
|
207
301
|
]);
|
|
208
302
|
|
|
209
303
|
// Wrapped offer components with layout measurement
|
|
@@ -236,49 +330,107 @@ export const DynamicOffersRender = ({
|
|
|
236
330
|
return () => clearTimeout(timeoutId);
|
|
237
331
|
}, [checkVisibleOffers, scrollOffset, scrollViewWidth, cardLayouts]);
|
|
238
332
|
|
|
333
|
+
useEffect(() => {
|
|
334
|
+
if (parentScrollOffset !== undefined) {
|
|
335
|
+
// Check visibility when parent scrolls
|
|
336
|
+
checkVisibleOffers();
|
|
337
|
+
}
|
|
338
|
+
}, [parentScrollOffset, checkVisibleOffers]);
|
|
339
|
+
|
|
239
340
|
const showTitle = Boolean(title?.trim());
|
|
240
341
|
const showSubtitle = Boolean(subtitle?.trim());
|
|
241
342
|
const showHeaderText = showTitle || showSubtitle;
|
|
343
|
+
const showHeader = isHorizontalScroll;
|
|
344
|
+
|
|
345
|
+
const onPressSeeAll = () => setAllOffersModalVisible(true);
|
|
346
|
+
const onCloseModal = () => setAllOffersModalVisible(false);
|
|
347
|
+
|
|
348
|
+
const config = {
|
|
349
|
+
title,
|
|
350
|
+
subtitle,
|
|
351
|
+
displayLayout,
|
|
352
|
+
showCardBorder,
|
|
353
|
+
showProductTypeLabel,
|
|
354
|
+
shouldHideFooter: false,
|
|
355
|
+
isHorizontalScroll: false,
|
|
356
|
+
offers,
|
|
357
|
+
brand,
|
|
358
|
+
};
|
|
242
359
|
|
|
243
360
|
return (
|
|
244
361
|
<>
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
362
|
+
{showHeader && (
|
|
363
|
+
<View
|
|
364
|
+
style={[
|
|
365
|
+
styles.header,
|
|
366
|
+
{ alignItems: showSubtitle ? "flex-start" : "center" },
|
|
367
|
+
]}
|
|
368
|
+
>
|
|
369
|
+
{showHeaderText && (
|
|
370
|
+
<View style={styles.headerTextContainer}>
|
|
371
|
+
{showTitle && (
|
|
372
|
+
<Text variant="featured-3" weight="bold">
|
|
373
|
+
{title}
|
|
374
|
+
</Text>
|
|
375
|
+
)}
|
|
376
|
+
{showSubtitle && (
|
|
377
|
+
<Text variant="body-3" color="foregroundNeutralFaded">
|
|
378
|
+
{subtitle}
|
|
379
|
+
</Text>
|
|
380
|
+
)}
|
|
381
|
+
</View>
|
|
382
|
+
)}
|
|
383
|
+
<SeeAllButton onPress={onPressSeeAll} />
|
|
384
|
+
<AllOffersModal
|
|
385
|
+
visible={allOffersModalVisible}
|
|
386
|
+
onClose={onCloseModal}
|
|
387
|
+
config={config}
|
|
388
|
+
/>
|
|
389
|
+
</View>
|
|
390
|
+
)}
|
|
391
|
+
{isHorizontalScroll ? (
|
|
392
|
+
<ScrollView
|
|
393
|
+
horizontal={true}
|
|
394
|
+
showsHorizontalScrollIndicator={false}
|
|
395
|
+
contentContainerStyle={styles.contentContainer}
|
|
396
|
+
style={styles.scrollView}
|
|
397
|
+
onLayout={handleScrollViewLayout}
|
|
398
|
+
onScroll={handleScroll}
|
|
399
|
+
scrollEventThrottle={16} // Lower number for more precision, higher for better performance
|
|
400
|
+
>
|
|
401
|
+
{wrappedOfferComponents}
|
|
402
|
+
</ScrollView>
|
|
403
|
+
) : (
|
|
404
|
+
// For vertical scroll, parent component will handle the scroll.
|
|
405
|
+
// Event callback triggers will happen in the parent component.
|
|
406
|
+
<View
|
|
407
|
+
style={[styles.contentContainer, { flex: 1 }]}
|
|
408
|
+
onLayout={handleScrollViewLayout}
|
|
409
|
+
>
|
|
410
|
+
{wrappedOfferComponents}
|
|
411
|
+
</View>
|
|
412
|
+
)}
|
|
267
413
|
</>
|
|
268
414
|
);
|
|
269
415
|
};
|
|
270
416
|
|
|
271
417
|
const styles = StyleSheet.create({
|
|
272
418
|
contentContainer: {
|
|
419
|
+
// for horizontal scroll
|
|
273
420
|
columnGap: 20,
|
|
274
421
|
paddingHorizontal: 16,
|
|
422
|
+
|
|
423
|
+
// for all offers vertical scroll
|
|
424
|
+
rowGap: 16,
|
|
425
|
+
},
|
|
426
|
+
scrollView: {
|
|
427
|
+
flex: 1,
|
|
275
428
|
},
|
|
276
429
|
header: {
|
|
277
430
|
marginHorizontal: 16,
|
|
278
431
|
marginBottom: 16,
|
|
279
432
|
flexDirection: "row",
|
|
280
433
|
justifyContent: "space-between",
|
|
281
|
-
alignItems: "center",
|
|
282
434
|
},
|
|
283
435
|
headerTextContainer: {
|
|
284
436
|
flex: 1,
|
|
@@ -7,11 +7,13 @@ export function FallbackOfferTemplate({
|
|
|
7
7
|
offerIndex,
|
|
8
8
|
showCardBorder = true,
|
|
9
9
|
showProductTypeLabel = true,
|
|
10
|
+
isHorizontalScroll = true,
|
|
10
11
|
}: {
|
|
11
12
|
offer: BaseOffer;
|
|
12
13
|
offerIndex: number;
|
|
13
14
|
showCardBorder?: boolean;
|
|
14
15
|
showProductTypeLabel?: boolean;
|
|
16
|
+
isHorizontalScroll?: boolean;
|
|
15
17
|
}) {
|
|
16
18
|
return (
|
|
17
19
|
<HeadlineWithDescriptionCard
|
|
@@ -24,6 +26,7 @@ export function FallbackOfferTemplate({
|
|
|
24
26
|
}}
|
|
25
27
|
showCardBorder={showCardBorder}
|
|
26
28
|
showProductTypeLabel={showProductTypeLabel}
|
|
29
|
+
isHorizontalScroll={isHorizontalScroll}
|
|
27
30
|
/>
|
|
28
31
|
);
|
|
29
32
|
}
|
|
@@ -18,14 +18,21 @@ type RenderOfferListProps = {
|
|
|
18
18
|
offerIndex,
|
|
19
19
|
showProductTypeLabel,
|
|
20
20
|
showCardBorder,
|
|
21
|
+
fullCardWidth,
|
|
22
|
+
isHorizontalScroll,
|
|
21
23
|
}: {
|
|
22
24
|
offer: BaseOffer;
|
|
23
25
|
offerIndex: number;
|
|
24
26
|
showProductTypeLabel?: boolean;
|
|
25
27
|
showCardBorder?: boolean;
|
|
28
|
+
fullCardWidth?: boolean;
|
|
29
|
+
isHorizontalScroll?: boolean;
|
|
26
30
|
}) => React.ReactNode;
|
|
27
31
|
showCardBorder?: boolean;
|
|
28
32
|
showProductTypeLabel?: boolean;
|
|
33
|
+
/** Offer card should be full width when offers.length === 1 in horizontal scroll or offers are scrolled vertically (when !isHorizontalScroll is true) */
|
|
34
|
+
fullCardWidth?: boolean;
|
|
35
|
+
isHorizontalScroll?: boolean;
|
|
29
36
|
// featureClient?: CnfFeatureClient; TODO: this is the like split on cnf side it uses growthbook
|
|
30
37
|
};
|
|
31
38
|
|
|
@@ -35,6 +42,8 @@ export const Offer = ({
|
|
|
35
42
|
showProductTypeLabel = true,
|
|
36
43
|
fallbackTemplate,
|
|
37
44
|
showCardBorder = true,
|
|
45
|
+
fullCardWidth = false,
|
|
46
|
+
isHorizontalScroll = true,
|
|
38
47
|
}: RenderOfferListProps) =>
|
|
39
48
|
offers.map((offer: BaseOffer, offerIndex: number) => {
|
|
40
49
|
const builder = productTypeBuilder.find(({ productTypes }) =>
|
|
@@ -60,6 +69,8 @@ export const Offer = ({
|
|
|
60
69
|
productTypeBuilder={layout}
|
|
61
70
|
showCardBorder={showCardBorder}
|
|
62
71
|
showProductTypeLabel={showProductTypeLabel}
|
|
72
|
+
fullCardWidth={fullCardWidth}
|
|
73
|
+
isHorizontalScroll={isHorizontalScroll}
|
|
63
74
|
/>
|
|
64
75
|
))
|
|
65
76
|
.with({ layout: "creditCardOfferCard" }, (layout) => (
|
|
@@ -70,6 +81,8 @@ export const Offer = ({
|
|
|
70
81
|
productTypeBuilder={layout}
|
|
71
82
|
showCardBorder={showCardBorder}
|
|
72
83
|
showProductTypeLabel={showProductTypeLabel}
|
|
84
|
+
fullCardWidth={fullCardWidth}
|
|
85
|
+
isHorizontalScroll={isHorizontalScroll}
|
|
73
86
|
/>
|
|
74
87
|
))
|
|
75
88
|
.with({ layout: "headlineWithDescriptionCard" }, (layout) => (
|
|
@@ -80,6 +93,8 @@ export const Offer = ({
|
|
|
80
93
|
productTypeBuilder={layout}
|
|
81
94
|
showCardBorder={showCardBorder}
|
|
82
95
|
showProductTypeLabel={showProductTypeLabel}
|
|
96
|
+
fullCardWidth={fullCardWidth}
|
|
97
|
+
isHorizontalScroll={isHorizontalScroll}
|
|
83
98
|
/>
|
|
84
99
|
))
|
|
85
100
|
.with(undefined, () => {
|
|
@@ -94,6 +109,8 @@ export const Offer = ({
|
|
|
94
109
|
key={`${offerIndex}-${offer.uuid}`}
|
|
95
110
|
offer={offer}
|
|
96
111
|
showProductTypeLabel={showProductTypeLabel}
|
|
112
|
+
fullCardWidth={fullCardWidth}
|
|
113
|
+
isHorizontalScroll={isHorizontalScroll}
|
|
97
114
|
/>
|
|
98
115
|
);
|
|
99
116
|
})
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { StyleSheet } from "react-native";
|
|
3
|
+
import Button from "../Button";
|
|
4
|
+
|
|
5
|
+
interface SeeAllButtonProps {
|
|
6
|
+
onPress: () => void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const SeeAllButton = ({ onPress }: SeeAllButtonProps) => {
|
|
10
|
+
return (
|
|
11
|
+
<>
|
|
12
|
+
<Button
|
|
13
|
+
color={"backgroundNeutral"}
|
|
14
|
+
textVariant={"body-3"}
|
|
15
|
+
textFontWeight={"bold"}
|
|
16
|
+
style={styles.seeAllButton}
|
|
17
|
+
onPress={onPress}
|
|
18
|
+
>
|
|
19
|
+
{"See all"}
|
|
20
|
+
</Button>
|
|
21
|
+
</>
|
|
22
|
+
);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const styles = StyleSheet.create({
|
|
26
|
+
seeAllButton: {
|
|
27
|
+
height: "auto",
|
|
28
|
+
paddingVertical: 8,
|
|
29
|
+
paddingHorizontal: 12,
|
|
30
|
+
},
|
|
31
|
+
});
|
|
@@ -9,6 +9,7 @@ import Text from "../../Text";
|
|
|
9
9
|
import { CallToAction } from "../../Common/DynamicOfferCard/CallToAction";
|
|
10
10
|
import { StyleSheet } from "react-native";
|
|
11
11
|
import type { BaseOffer } from "../../../capabilities/offer-catalog/src";
|
|
12
|
+
import { getEventCallbackContext } from "../../../capabilities/offer-catalog/src/utils/getEventCallbackContext";
|
|
12
13
|
|
|
13
14
|
type CreditCardOfferCardProps = {
|
|
14
15
|
productTypeBuilder: CreditCardOfferCardBuilderProps;
|
|
@@ -16,6 +17,8 @@ type CreditCardOfferCardProps = {
|
|
|
16
17
|
showCardBorder: boolean;
|
|
17
18
|
showProductTypeLabel?: boolean;
|
|
18
19
|
offerIndex: number;
|
|
20
|
+
fullCardWidth?: boolean;
|
|
21
|
+
isHorizontalScroll?: boolean;
|
|
19
22
|
};
|
|
20
23
|
|
|
21
24
|
export const CreditCardOfferCard = ({
|
|
@@ -24,6 +27,8 @@ export const CreditCardOfferCard = ({
|
|
|
24
27
|
showCardBorder,
|
|
25
28
|
showProductTypeLabel,
|
|
26
29
|
offerIndex,
|
|
30
|
+
fullCardWidth = false,
|
|
31
|
+
isHorizontalScroll = true,
|
|
27
32
|
}: CreditCardOfferCardProps) => {
|
|
28
33
|
const { stats } = makeCreditCardOfferStats(productTypeBuilder)(offer);
|
|
29
34
|
const descriptionPoints = get(offer, "descriptionPoints", []);
|
|
@@ -33,7 +38,10 @@ export const CreditCardOfferCard = ({
|
|
|
33
38
|
const showBadge = badges.length > 0;
|
|
34
39
|
|
|
35
40
|
return (
|
|
36
|
-
<BaseOfferCard
|
|
41
|
+
<BaseOfferCard
|
|
42
|
+
showCardBorder={showCardBorder}
|
|
43
|
+
fullCardWidth={fullCardWidth}
|
|
44
|
+
>
|
|
37
45
|
{showProductTypeLabel && (
|
|
38
46
|
<BaseOfferCard.TopBar>
|
|
39
47
|
<BaseOfferCard.ProductType>
|
|
@@ -66,7 +74,11 @@ export const CreditCardOfferCard = ({
|
|
|
66
74
|
/>
|
|
67
75
|
))}
|
|
68
76
|
{showMoreInfo && (
|
|
69
|
-
<SeeMore
|
|
77
|
+
<SeeMore
|
|
78
|
+
offer={offer}
|
|
79
|
+
offerIndex={offerIndex}
|
|
80
|
+
isHorizontalScroll={isHorizontalScroll}
|
|
81
|
+
/>
|
|
70
82
|
)}
|
|
71
83
|
</BaseOfferCard.Stats>
|
|
72
84
|
</BaseOfferCard.Content>
|
|
@@ -78,7 +90,7 @@ export const CreditCardOfferCard = ({
|
|
|
78
90
|
offer={offer}
|
|
79
91
|
overrideUrl={offer.overrideUrl}
|
|
80
92
|
style={styles.cta}
|
|
81
|
-
context=
|
|
93
|
+
context={getEventCallbackContext(isHorizontalScroll)}
|
|
82
94
|
offerIndex={offerIndex}
|
|
83
95
|
>
|
|
84
96
|
Continue
|
|
@@ -13,6 +13,8 @@ type DefaultOfferCardProps = {
|
|
|
13
13
|
offerIndex: number;
|
|
14
14
|
showCardBorder: boolean;
|
|
15
15
|
showProductTypeLabel?: boolean;
|
|
16
|
+
fullCardWidth?: boolean;
|
|
17
|
+
isHorizontalScroll?: boolean;
|
|
16
18
|
};
|
|
17
19
|
|
|
18
20
|
export const DefaultOfferCard = ({
|
|
@@ -21,6 +23,8 @@ export const DefaultOfferCard = ({
|
|
|
21
23
|
offerIndex,
|
|
22
24
|
showCardBorder,
|
|
23
25
|
showProductTypeLabel,
|
|
26
|
+
fullCardWidth = false,
|
|
27
|
+
isHorizontalScroll = true,
|
|
24
28
|
}: DefaultOfferCardProps) => {
|
|
25
29
|
const { stats, topStats } =
|
|
26
30
|
makeDefaultOfferCardStats(productTypeBuilder)(offer);
|
|
@@ -41,6 +45,8 @@ export const DefaultOfferCard = ({
|
|
|
41
45
|
offerIndex={offerIndex}
|
|
42
46
|
showCardBorder={showCardBorder}
|
|
43
47
|
showProductTypeLabel={showProductTypeLabel}
|
|
48
|
+
fullCardWidth={fullCardWidth}
|
|
49
|
+
isHorizontalScroll={isHorizontalScroll}
|
|
44
50
|
>
|
|
45
51
|
<BaseOfferCard.TopContainer>
|
|
46
52
|
{topStats.map((stat) => (
|
|
@@ -67,7 +73,13 @@ export const DefaultOfferCard = ({
|
|
|
67
73
|
contentDescription={contentDescription}
|
|
68
74
|
/>
|
|
69
75
|
)}
|
|
70
|
-
{showMoreInfo &&
|
|
76
|
+
{showMoreInfo && (
|
|
77
|
+
<SeeMore
|
|
78
|
+
offer={offer}
|
|
79
|
+
offerIndex={offerIndex}
|
|
80
|
+
isHorizontalScroll={isHorizontalScroll}
|
|
81
|
+
/>
|
|
82
|
+
)}
|
|
71
83
|
</BaseOfferCard.Stats>
|
|
72
84
|
</DynamicOfferCard>
|
|
73
85
|
);
|
|
@@ -13,6 +13,8 @@ type HeadlineWithDescriptionCardProps = {
|
|
|
13
13
|
offerIndex: number;
|
|
14
14
|
showCardBorder: boolean;
|
|
15
15
|
showProductTypeLabel?: boolean;
|
|
16
|
+
fullCardWidth?: boolean;
|
|
17
|
+
isHorizontalScroll?: boolean;
|
|
16
18
|
};
|
|
17
19
|
export const HeadlineWithDescriptionCard = ({
|
|
18
20
|
offer,
|
|
@@ -20,6 +22,8 @@ export const HeadlineWithDescriptionCard = ({
|
|
|
20
22
|
offerIndex,
|
|
21
23
|
showCardBorder,
|
|
22
24
|
showProductTypeLabel,
|
|
25
|
+
fullCardWidth = false,
|
|
26
|
+
isHorizontalScroll = true,
|
|
23
27
|
}: HeadlineWithDescriptionCardProps) => {
|
|
24
28
|
const {
|
|
25
29
|
headlineField = "headline",
|
|
@@ -45,6 +49,8 @@ export const HeadlineWithDescriptionCard = ({
|
|
|
45
49
|
offerIndex={offerIndex}
|
|
46
50
|
showCardBorder={showCardBorder}
|
|
47
51
|
showProductTypeLabel={showProductTypeLabel}
|
|
52
|
+
fullCardWidth={fullCardWidth}
|
|
53
|
+
isHorizontalScroll={isHorizontalScroll}
|
|
48
54
|
>
|
|
49
55
|
<BaseOfferCard.TopContainer>
|
|
50
56
|
<BaseOfferCard.Headline>{headline}</BaseOfferCard.Headline>
|
|
@@ -56,7 +62,13 @@ export const HeadlineWithDescriptionCard = ({
|
|
|
56
62
|
hasDivider={false}
|
|
57
63
|
/>
|
|
58
64
|
)}
|
|
59
|
-
{showMoreInfo &&
|
|
65
|
+
{showMoreInfo && (
|
|
66
|
+
<SeeMore
|
|
67
|
+
offer={offer}
|
|
68
|
+
offerIndex={offerIndex}
|
|
69
|
+
isHorizontalScroll={isHorizontalScroll}
|
|
70
|
+
/>
|
|
71
|
+
)}
|
|
60
72
|
</DynamicOfferCard>
|
|
61
73
|
);
|
|
62
74
|
};
|