@wlloyalty/wll-react-sdk 1.0.101 → 1.0.103

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/dist/index.d.ts CHANGED
@@ -526,8 +526,9 @@ type TSection = {
526
526
 
527
527
  type CarouselProps = {
528
528
  section?: TSection;
529
+ autoRotateInterval?: number;
529
530
  };
530
- declare const Carousel: ({ section }: CarouselProps) => JSX.Element | null;
531
+ declare const Carousel: ({ section, autoRotateInterval, }: CarouselProps) => JSX.Element | null;
531
532
 
532
533
  type CarouselNavButtonProps = {
533
534
  direction: 'left' | 'right';
@@ -660,6 +661,12 @@ type NavigationConfig = {
660
661
  baseUrl?: string;
661
662
  };
662
663
 
664
+ /**
665
+ * Supported event types for the SDK event system
666
+ * @typedef {string} EventTypes
667
+ */
668
+ type EventTypes = 'GROUP_DATA_CHANGED' | 'SECTION_DATA_CHANGED' | 'TILE_DATA_CHANGED' | string;
669
+
663
670
  type Fetcher = <T>(endpoint: string, options?: RequestInit) => Promise<APIResponse<T>>;
664
671
  type SDKConfig = {
665
672
  apiKey: string;
@@ -678,6 +685,8 @@ type WllSdkContextType = ThemeContextType & {
678
685
  getTileByID: (id: string) => Promise<APIResponse<Tile>>;
679
686
  handleNavigation: (link: string, target: CTALinkTarget) => Promise<void>;
680
687
  refreshGroup: (id: string) => Promise<APIResponse<TGroup>>;
688
+ notifyDataChange: (eventType: EventTypes, data?: any) => void;
689
+ subscribeToDataChange: (eventType: EventTypes, callback: (data?: any) => void) => () => void;
681
690
  } & Readonly<{
682
691
  readonly config: SDKConfig;
683
692
  }>;
package/dist/native.js CHANGED
@@ -1595,6 +1595,66 @@ var useGetGroupByID = createResourceGetter('groups', true);
1595
1595
  var useGetSectionByID = createResourceGetter('sections');
1596
1596
  var useGetTileByID = createResourceGetter('tiles');
1597
1597
 
1598
+ /**
1599
+ * A simple event emitter for handling SDK events
1600
+ * Provides a pub-sub mechanism for components to communicate
1601
+ */
1602
+ var EventEmitter = /** @class */ (function () {
1603
+ function EventEmitter() {
1604
+ /**
1605
+ * Map of event types to arrays of callback functions
1606
+ * @private
1607
+ */
1608
+ this.events = new Map();
1609
+ }
1610
+ /**
1611
+ * Subscribe to an event
1612
+ * @param {EventTypes} event - The event type to subscribe to
1613
+ * @param {EventCallback} callback - The callback function to execute when the event is emitted
1614
+ * @returns {Function} An unsubscribe function that removes this callback when called
1615
+ */
1616
+ EventEmitter.prototype.on = function (event, callback) {
1617
+ var _this = this;
1618
+ var _a;
1619
+ if (!this.events.has(event)) {
1620
+ this.events.set(event, []);
1621
+ }
1622
+ (_a = this.events.get(event)) === null || _a === void 0 ? void 0 : _a.push(callback);
1623
+ return function () {
1624
+ var callbacks = _this.events.get(event);
1625
+ if (callbacks) {
1626
+ var index = callbacks.indexOf(callback);
1627
+ if (index !== -1) {
1628
+ callbacks.splice(index, 1);
1629
+ }
1630
+ }
1631
+ };
1632
+ };
1633
+ /**
1634
+ * Emit an event with optional data
1635
+ * @param {EventTypes} event - The event type to emit
1636
+ * @param {any} [data] - Optional data to pass to the event callbacks
1637
+ */
1638
+ EventEmitter.prototype.emit = function (event, data) {
1639
+ var callbacks = this.events.get(event);
1640
+ if (callbacks) {
1641
+ callbacks.forEach(function (callback) { return callback(data); });
1642
+ }
1643
+ };
1644
+ /**
1645
+ * Remove all event listeners
1646
+ * Use this method to clean up when the event emitter is no longer needed
1647
+ */
1648
+ EventEmitter.prototype.clear = function () {
1649
+ this.events.clear();
1650
+ };
1651
+ return EventEmitter;
1652
+ }());
1653
+ /**
1654
+ * Singleton instance of the EventEmitter for use throughout the SDK
1655
+ */
1656
+ var sdkEventEmitter = new EventEmitter();
1657
+
1598
1658
  var sizes = {
1599
1659
  borderRadiusSm: 15,
1600
1660
  borderRadiusLg: 20,
@@ -1961,6 +2021,12 @@ var WllSdkProvider = function (_a) {
1961
2021
  return [2 /*return*/, getGroupByID(id)];
1962
2022
  });
1963
2023
  }); }, [getGroupByID]);
2024
+ var notifyDataChange = React.useCallback(function (eventType, data) {
2025
+ sdkEventEmitter.emit(eventType, data);
2026
+ }, []);
2027
+ var subscribeToDataChange = React.useCallback(function (eventType, callback) {
2028
+ return sdkEventEmitter.on(eventType, callback);
2029
+ }, []);
1964
2030
  var contextValue = React.useMemo(function () { return ({
1965
2031
  theme: theme,
1966
2032
  setTheme: setTheme,
@@ -1969,6 +2035,8 @@ var WllSdkProvider = function (_a) {
1969
2035
  getTileByID: getTileByID,
1970
2036
  handleNavigation: handleNavigation,
1971
2037
  refreshGroup: refreshGroup,
2038
+ notifyDataChange: notifyDataChange,
2039
+ subscribeToDataChange: subscribeToDataChange,
1972
2040
  config: config,
1973
2041
  }); }, [
1974
2042
  theme,
@@ -1978,6 +2046,8 @@ var WllSdkProvider = function (_a) {
1978
2046
  getTileByID,
1979
2047
  handleNavigation,
1980
2048
  refreshGroup,
2049
+ notifyDataChange,
2050
+ subscribeToDataChange,
1981
2051
  config,
1982
2052
  ]);
1983
2053
  return (jsxRuntimeExports.jsx(WllSdkContext.Provider, { value: contextValue, children: jsxRuntimeExports.jsx(ResponsiveProvider, { children: children }) }));
@@ -3670,7 +3740,7 @@ var carouselReducer = function (state, action) {
3670
3740
  }
3671
3741
  };
3672
3742
  var Carousel = function (_a) {
3673
- var section = _a.section;
3743
+ var section = _a.section, _b = _a.autoRotateInterval, autoRotateInterval = _b === void 0 ? 5000 : _b;
3674
3744
  if (!section)
3675
3745
  return null;
3676
3746
  var WINDOW_WIDTH = reactNative.useWindowDimensions().width;
@@ -3678,9 +3748,9 @@ var Carousel = function (_a) {
3678
3748
  var styles = useCarouselStyles(BUTTON_SIZE);
3679
3749
  var animatedIndex = React.useRef(new reactNative.Animated.Value(0)).current;
3680
3750
  var theme = useWllSdk().theme;
3681
- var _b = useResponsive(), isDesktop = _b.isDesktop, isTablet = _b.isTablet;
3751
+ var _c = useResponsive(), isDesktop = _c.isDesktop, isTablet = _c.isTablet;
3682
3752
  var scrollViewRef = React.useRef(null);
3683
- var _c = React.useReducer(carouselReducer, __assign(__assign({}, initialState), { containerWidth: WINDOW_WIDTH })), state = _c[0], dispatch = _c[1];
3753
+ var _d = React.useReducer(carouselReducer, __assign(__assign({}, initialState), { containerWidth: WINDOW_WIDTH })), state = _d[0], dispatch = _d[1];
3684
3754
  var currentIndex = state.currentIndex, containerWidth = state.containerWidth;
3685
3755
  var sortedTiles = sortByPriority(section.tiles.filter(function (tile) { return tile.type === exports.TileType.Banner; }));
3686
3756
  var handleScroll = React.useCallback(function (event) {
@@ -3703,12 +3773,34 @@ var Carousel = function (_a) {
3703
3773
  };
3704
3774
  var handleNext = function () {
3705
3775
  var _a;
3776
+ var nextIndex = Math.min(sortedTiles.length - 1, currentIndex + 1);
3706
3777
  dispatch({ type: 'NEXT_SLIDE', maxIndex: sortedTiles.length - 1 });
3707
3778
  (_a = scrollViewRef.current) === null || _a === void 0 ? void 0 : _a.scrollTo({
3708
- x: (currentIndex + 1) * containerWidth,
3779
+ x: nextIndex * containerWidth,
3709
3780
  animated: true,
3710
3781
  });
3711
3782
  };
3783
+ var rotateToNextSlide = React.useCallback(function () {
3784
+ var _a;
3785
+ if (currentIndex >= sortedTiles.length - 1) {
3786
+ dispatch({ type: 'SET_CURRENT_INDEX', payload: 0 });
3787
+ (_a = scrollViewRef.current) === null || _a === void 0 ? void 0 : _a.scrollTo({
3788
+ x: 0,
3789
+ animated: true,
3790
+ });
3791
+ }
3792
+ else {
3793
+ handleNext();
3794
+ }
3795
+ }, [currentIndex, containerWidth, sortedTiles.length]);
3796
+ React.useEffect(function () {
3797
+ if (sortedTiles.length <= 1)
3798
+ return;
3799
+ var rotationTimer = setInterval(function () {
3800
+ rotateToNextSlide();
3801
+ }, autoRotateInterval);
3802
+ return function () { return clearInterval(rotationTimer); };
3803
+ }, [rotateToNextSlide, autoRotateInterval, sortedTiles.length]);
3712
3804
  var displayControls = sortedTiles.length > 1;
3713
3805
  var showPrevButton = displayControls && currentIndex > 0;
3714
3806
  var showNextButton = displayControls && currentIndex < sortedTiles.length - 1;
@@ -4050,6 +4142,14 @@ var useGroupContext = function () {
4050
4142
  /**
4051
4143
  * Custom hook to fetch and manage group data
4052
4144
  *
4145
+ * This hook handles two distinct data fetching scenarios:
4146
+ * 1. Initial fetch: Shows loading state while first loading data
4147
+ * 2. Background refresh: Updates data silently without loading states
4148
+ *
4149
+ * The hook subscribes to GROUP_DATA_CHANGED events to refresh data
4150
+ * when notified by the host application, ensuring UI remains responsive
4151
+ * without showing loading indicators during refreshes.
4152
+ *
4053
4153
  * @param {string} id - The ID of the group to fetch
4054
4154
  * @returns {Object} Object containing group data, loading state, and any error
4055
4155
  */
@@ -4058,60 +4158,86 @@ var useGroupData = function (id) {
4058
4158
  var _a = React.useState(null), groupData = _a[0], setGroupData = _a[1];
4059
4159
  var _b = React.useState(null), error = _b[0], setError = _b[1];
4060
4160
  var _c = React.useState(true), isLoading = _c[0], setIsLoading = _c[1];
4061
- var fetchGroup = React.useCallback(function () {
4062
- var args_1 = [];
4063
- for (var _i = 0; _i < arguments.length; _i++) {
4064
- args_1[_i] = arguments[_i];
4065
- }
4066
- return __awaiter(void 0, __spreadArray([], args_1, true), void 0, function (showLoading) {
4067
- var response, err_1;
4068
- if (showLoading === void 0) { showLoading = true; }
4069
- return __generator(this, function (_a) {
4070
- switch (_a.label) {
4071
- case 0:
4072
- if (!id || !sdk || !sdk.getGroupByID) {
4073
- setError('Unable to fetch group data: invalid configuration');
4074
- setIsLoading(false);
4075
- return [2 /*return*/];
4076
- }
4077
- if (showLoading) {
4078
- setIsLoading(true);
4079
- }
4080
- setError(null);
4081
- _a.label = 1;
4082
- case 1:
4083
- _a.trys.push([1, 3, 4, 5]);
4084
- return [4 /*yield*/, sdk.refreshGroup(id)];
4085
- case 2:
4086
- response = _a.sent();
4087
- if (response && response.status === 'success' && response.data) {
4088
- setGroupData(response.data);
4089
- }
4090
- else {
4091
- setError((response && response.error) || 'Failed to fetch group data.');
4092
- }
4093
- return [3 /*break*/, 5];
4094
- case 3:
4095
- err_1 = _a.sent();
4096
- setError('Failed to fetch group data. Please try again later.');
4097
- console.error('Error fetching group:', err_1);
4098
- return [3 /*break*/, 5];
4099
- case 4:
4100
- if (showLoading) {
4101
- setIsLoading(false);
4102
- }
4103
- return [7 /*endfinally*/];
4104
- case 5: return [2 /*return*/];
4105
- }
4106
- });
4161
+ var _d = React.useState(false), isInitialFetchDone = _d[0], setIsInitialFetchDone = _d[1];
4162
+ var initialFetch = React.useCallback(function () { return __awaiter(void 0, void 0, void 0, function () {
4163
+ var response, err_1;
4164
+ return __generator(this, function (_a) {
4165
+ switch (_a.label) {
4166
+ case 0:
4167
+ if (!id || !sdk || !sdk.getGroupByID) {
4168
+ setError('Unable to fetch group data: invalid configuration');
4169
+ setIsLoading(false);
4170
+ setIsInitialFetchDone(true);
4171
+ return [2 /*return*/];
4172
+ }
4173
+ setIsLoading(true);
4174
+ setError(null);
4175
+ _a.label = 1;
4176
+ case 1:
4177
+ _a.trys.push([1, 3, 4, 5]);
4178
+ return [4 /*yield*/, sdk.refreshGroup(id)];
4179
+ case 2:
4180
+ response = _a.sent();
4181
+ if (response && response.status === 'success' && response.data) {
4182
+ setGroupData(response.data);
4183
+ }
4184
+ else {
4185
+ setError((response && response.error) || 'Failed to fetch group data.');
4186
+ }
4187
+ return [3 /*break*/, 5];
4188
+ case 3:
4189
+ err_1 = _a.sent();
4190
+ setError('Failed to fetch group data. Please try again later.');
4191
+ console.error('Error fetching group:', err_1);
4192
+ return [3 /*break*/, 5];
4193
+ case 4:
4194
+ setIsLoading(false);
4195
+ setIsInitialFetchDone(true);
4196
+ return [7 /*endfinally*/];
4197
+ case 5: return [2 /*return*/];
4198
+ }
4107
4199
  });
4108
- }, [id, sdk]);
4200
+ }); }, [id, sdk]);
4201
+ var silentRefresh = React.useCallback(function () { return __awaiter(void 0, void 0, void 0, function () {
4202
+ var response, err_2;
4203
+ return __generator(this, function (_a) {
4204
+ switch (_a.label) {
4205
+ case 0:
4206
+ if (!id || !sdk || !sdk.getGroupByID || !isInitialFetchDone) {
4207
+ return [2 /*return*/];
4208
+ }
4209
+ _a.label = 1;
4210
+ case 1:
4211
+ _a.trys.push([1, 3, , 4]);
4212
+ return [4 /*yield*/, sdk.refreshGroup(id)];
4213
+ case 2:
4214
+ response = _a.sent();
4215
+ if (response && response.status === 'success' && response.data) {
4216
+ setGroupData(response.data);
4217
+ }
4218
+ return [3 /*break*/, 4];
4219
+ case 3:
4220
+ err_2 = _a.sent();
4221
+ console.error('Error during silent refresh:', err_2);
4222
+ return [3 /*break*/, 4];
4223
+ case 4: return [2 /*return*/];
4224
+ }
4225
+ });
4226
+ }); }, [id, sdk, isInitialFetchDone]);
4109
4227
  var refreshGroup = React.useCallback(function () {
4110
- return fetchGroup(false);
4111
- }, [fetchGroup]);
4228
+ return silentRefresh();
4229
+ }, [silentRefresh]);
4112
4230
  React.useEffect(function () {
4113
- fetchGroup(true);
4114
- }, [fetchGroup]);
4231
+ if (sdk) {
4232
+ initialFetch();
4233
+ var unsubscribeGroup_1 = sdk.subscribeToDataChange('GROUP_DATA_CHANGED', function () {
4234
+ silentRefresh();
4235
+ });
4236
+ return function () {
4237
+ unsubscribeGroup_1();
4238
+ };
4239
+ }
4240
+ }, [initialFetch, id, sdk, silentRefresh]);
4115
4241
  return { groupData: groupData, isLoading: isLoading, error: error, refreshGroup: refreshGroup };
4116
4242
  };
4117
4243
  /**
@@ -4169,7 +4295,7 @@ var Group = function (_a) {
4169
4295
  if (error || !groupData) {
4170
4296
  return jsxRuntimeExports.jsx(GroupEmptyState, { message: error || 'No group data available' });
4171
4297
  }
4172
- return (jsxRuntimeExports.jsx(GroupContext.Provider, { value: { groupData: groupData }, children: jsxRuntimeExports.jsx(reactNative.View, { "data-testid": "group-container", children: jsxRuntimeExports.jsx(GroupSections, {}) }) }));
4298
+ return (jsxRuntimeExports.jsx(GroupContext.Provider, { value: { groupData: groupData }, children: jsxRuntimeExports.jsx(reactNative.View, { testID: "group-container", children: jsxRuntimeExports.jsx(GroupSections, {}) }) }));
4173
4299
  };
4174
4300
 
4175
4301
  /**