react-native-firework-sdk 1.3.0-beta.3 → 1.3.1-beta.1

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 (59) hide show
  1. package/android/build.gradle +2 -2
  2. package/android/src/main/AndroidManifest.xml +2 -2
  3. package/android/src/main/java/com/fireworksdk/bridge/{reactnative/FWInitializationProvider.kt → FWInitializationProvider.kt} +1 -1
  4. package/android/src/main/java/com/fireworksdk/bridge/models/FWVideoFeedConfigModel.kt +3 -1
  5. package/android/src/main/java/com/fireworksdk/bridge/models/FWVideoPlayerConfigModel.kt +1 -0
  6. package/android/src/main/java/com/fireworksdk/bridge/models/FWVideoShoppingProduct.kt +1 -0
  7. package/android/src/main/java/com/fireworksdk/bridge/reactnative/manager/FWVideoFeedManager.kt +5 -2
  8. package/android/src/main/java/com/fireworksdk/bridge/reactnative/module/FWNavigatorModule.kt +1 -1
  9. package/android/src/main/java/com/fireworksdk/bridge/reactnative/module/FWVideoShoppingModule.kt +69 -30
  10. package/android/src/main/java/com/fireworksdk/bridge/reactnative/module/FireworkSDKModule.kt +1 -1
  11. package/android/src/main/java/com/fireworksdk/bridge/reactnative/utils/FWEventUtils.kt +10 -5
  12. package/android/src/main/java/com/fireworksdk/bridge/utils/FWVideoPlayerUtils.kt +18 -0
  13. package/ios/Components/VideoFeed.swift +95 -35
  14. package/ios/Components/VideoFeedConfiguration.swift +15 -3
  15. package/ios/Components/VideoFeedManager.m +1 -0
  16. package/ios/Components/VideoPlayerConfiguration.swift +3 -2
  17. package/ios/Models/NativeToRN/FireworkEventName.swift +1 -0
  18. package/ios/Models/RNToNative/RCTConvert+FireworkSDKModule.swift +24 -4
  19. package/ios/Models/RNToNative/RCTConvert+VideoFeed.swift +1 -1
  20. package/ios/Modules/FireworkSDKModule/FireworkSDKModule+EventTracking.swift +5 -0
  21. package/ios/Modules/Shopping/CartViewController.swift +23 -40
  22. package/ios/Modules/Shopping/Product.swift +1 -0
  23. package/ios/Modules/Shopping/ProductInfoViewConfiguration.swift +1 -6
  24. package/ios/Modules/Shopping/ShoppingModule.m +6 -4
  25. package/ios/Modules/Shopping/ShoppingModule.swift +89 -31
  26. package/lib/commonjs/FireworkSDK.js +14 -3
  27. package/lib/commonjs/FireworkSDK.js.map +1 -1
  28. package/lib/commonjs/VideoShopping.js +77 -16
  29. package/lib/commonjs/VideoShopping.js.map +1 -1
  30. package/lib/commonjs/components/VideoFeed.js +28 -3
  31. package/lib/commonjs/components/VideoFeed.js.map +1 -1
  32. package/lib/commonjs/index.js.map +1 -1
  33. package/lib/commonjs/modules/ShoppingModule.js.map +1 -1
  34. package/lib/module/FireworkSDK.js +14 -3
  35. package/lib/module/FireworkSDK.js.map +1 -1
  36. package/lib/module/VideoShopping.js +70 -16
  37. package/lib/module/VideoShopping.js.map +1 -1
  38. package/lib/module/components/VideoFeed.js +28 -3
  39. package/lib/module/components/VideoFeed.js.map +1 -1
  40. package/lib/module/index.js.map +1 -1
  41. package/lib/module/modules/ShoppingModule.js.map +1 -1
  42. package/lib/typescript/FireworkSDK.d.ts +5 -0
  43. package/lib/typescript/VideoShopping.d.ts +15 -1
  44. package/lib/typescript/components/VideoFeed.d.ts +1 -0
  45. package/lib/typescript/index.d.ts +4 -4
  46. package/lib/typescript/models/ProductUnit.d.ts +12 -0
  47. package/lib/typescript/models/VideoFeedConfiguration.d.ts +33 -5
  48. package/lib/typescript/models/VideoPlayerConfiguration.d.ts +5 -1
  49. package/lib/typescript/modules/ShoppingModule.d.ts +2 -0
  50. package/package.json +2 -6
  51. package/react-native-firework-sdk.podspec +1 -1
  52. package/src/FireworkSDK.ts +15 -3
  53. package/src/VideoShopping.ts +94 -15
  54. package/src/components/VideoFeed.tsx +22 -1
  55. package/src/index.tsx +6 -1
  56. package/src/models/ProductUnit.ts +13 -0
  57. package/src/models/VideoFeedConfiguration.ts +34 -5
  58. package/src/models/VideoPlayerConfiguration.ts +5 -1
  59. package/src/modules/ShoppingModule.ts +2 -0
@@ -120,6 +120,16 @@ class FireworkSDK {
120
120
  }
121
121
  private _appComponentName: string | undefined;
122
122
 
123
+ /**
124
+ * Defaults to false. You can enable debug logs by setting this property to true.
125
+ */
126
+ public get debugLogsEnabled(): boolean {
127
+ return FWLoggerUtil.enabled;
128
+ }
129
+ public set debugLogsEnabled(value: boolean) {
130
+ FWLoggerUtil.enabled = value;
131
+ }
132
+
123
133
  private get eventEmitter(): NativeEventEmitter {
124
134
  return FireworkSDKModuleEventEmitter;
125
135
  }
@@ -171,7 +181,7 @@ class FireworkSDK {
171
181
  });
172
182
 
173
183
  this.eventEmitter.addListener(FWEventName.CustomCTAClick, (event) => {
174
- FWLoggerUtil.log(`Receive CustomCTAClick url ${event?.url}`);
184
+ FWLoggerUtil.log(`Receive CustomCTAClick url: ${event?.url}`);
175
185
 
176
186
  if (this.onCustomCTAClick) {
177
187
  this.onCustomCTAClick(event ?? {});
@@ -179,7 +189,9 @@ class FireworkSDK {
179
189
  });
180
190
 
181
191
  this.eventEmitter.addListener(FWEventName.VideoPlayback, (event) => {
182
- FWLoggerUtil.log(`Receive VideoPlayback event ${event?.eventName}`);
192
+ FWLoggerUtil.log(
193
+ `Receive VideoPlayback event eventName: ${event?.eventName}`
194
+ );
183
195
 
184
196
  if (this.onVideoPlayback) {
185
197
  this.onVideoPlayback(event ?? {});
@@ -187,7 +199,7 @@ class FireworkSDK {
187
199
  });
188
200
 
189
201
  this.eventEmitter.addListener(FWEventName.VideoFeedClick, (event) => {
190
- FWLoggerUtil.log(`Receive VideoFeedClick event ${event?.info.id}`);
202
+ FWLoggerUtil.log(`Receive VideoFeedClick event id: ${event?.info.id}`);
191
203
 
192
204
  if (this.onVideoFeedClick) {
193
205
  this.onVideoFeedClick(event ?? {});
@@ -1,4 +1,4 @@
1
- import type { NativeEventEmitter } from 'react-native';
1
+ import { NativeEventEmitter, Platform } from 'react-native';
2
2
 
3
3
  import type AddToCartResult from './models/AddToCartResult';
4
4
  import type {
@@ -13,6 +13,7 @@ import ShoppingModule, {
13
13
  ShoppingModuleEventEmitter,
14
14
  } from './modules/ShoppingModule';
15
15
  import type { NewNativeContainerProps } from './models/NewNativeContainerProps';
16
+ import FWLoggerUtil from './utils/FWLoggerUtil';
16
17
 
17
18
  export type AddToCartCallback = (
18
19
  event: AddToCartEvent
@@ -22,6 +23,8 @@ export type ClickCartIconCallback = () => Promise<
22
23
  NewNativeContainerProps | undefined | null
23
24
  >;
24
25
 
26
+ export type CustomClickCartIconCallback = () => Promise<void>;
27
+
25
28
  export type UpdateProductDetailsCallback = (
26
29
  event: UpdateProductDetailsEvent
27
30
  ) => Promise<Product[] | undefined | null>;
@@ -48,10 +51,33 @@ class VideoShopping {
48
51
  /**
49
52
  * This callback is triggered when the user clicks the shopping cart icon.
50
53
  *
51
- * The host app can return a React.Node to integrate custom cart page to shopping flow.
54
+ * The host app can return NewNativeContainerProps object and we will push a new native container with the props.
52
55
  */
53
56
  public onClickCartIcon?: ClickCartIconCallback;
54
57
 
58
+ /**
59
+ * This callback is triggered when the user clicks the shopping cart icon.
60
+ *
61
+ * The host app can customize the processing logic of clicking the shopping cart icon by setting the callback.
62
+ * In the callack, you could call FireworkSDK.getInstance().navigator.popNativeContainer to close the player
63
+ * and call navigation.navigate to push the RN cart page.
64
+ * Currently, there is a limitation on the iOS side when you set the callback.
65
+ * That is, if you set the callback, you need to call FireworkSDK.getInstance().navigator.popNativeContainer in the callback.
66
+ * Otherwise there will be bugs on the iOS side.
67
+ */
68
+ public get onCustomClickCartIcon(): CustomClickCartIconCallback | undefined {
69
+ return this._onCustomClickCartIcon;
70
+ }
71
+ public set onCustomClickCartIcon(
72
+ value: CustomClickCartIconCallback | undefined
73
+ ) {
74
+ this._onCustomClickCartIcon = value;
75
+ if (Platform.OS === 'ios') {
76
+ ShoppingModule.setCustomClickCartIconEnabled(!!value);
77
+ }
78
+ }
79
+ private _onCustomClickCartIcon?: CustomClickCartIconCallback | undefined;
80
+
55
81
  /**
56
82
  * This callback is triggered when the video will be shown.
57
83
  *
@@ -100,6 +126,9 @@ class VideoShopping {
100
126
  });
101
127
 
102
128
  this.eventEmitter.addListener(FWEventName.UpdateProductDetails, (event) => {
129
+ FWLoggerUtil.log(
130
+ `Receive UpdateProductDetails event productIds: ${event.productIds}`
131
+ );
103
132
  this.handleUpdateProductDetailsEvent(event);
104
133
  });
105
134
 
@@ -117,22 +146,38 @@ class VideoShopping {
117
146
  }
118
147
 
119
148
  private async handleAddToCartEvent(event: AddToCartEvent & CallbackInfo) {
149
+ const callbackId = event.callbackId;
150
+ delete event.callbackId;
120
151
  if (this.onAddToCart) {
121
- const callbackId = event.callbackId;
122
- delete event.callbackId;
123
152
  const result = await this.onAddToCart(event as AddToCartEvent);
124
153
  if (result) {
125
- ShoppingModule.updateAddToCartStatus(
126
- result.res,
127
- result.tips,
128
- callbackId!
129
- );
154
+ if (callbackId) {
155
+ ShoppingModule.updateAddToCartStatus(
156
+ result.res,
157
+ result.tips,
158
+ callbackId
159
+ );
160
+ }
161
+ } else {
162
+ if (callbackId) {
163
+ if (Platform.OS === 'ios') {
164
+ ShoppingModule.clearCallbackId(callbackId, FWEventName.AddToCart);
165
+ }
166
+ }
167
+ }
168
+ } else {
169
+ if (callbackId) {
170
+ if (Platform.OS === 'ios') {
171
+ ShoppingModule.clearCallbackId(callbackId, FWEventName.AddToCart);
172
+ }
130
173
  }
131
174
  }
132
175
  }
133
176
 
134
177
  private async handleClickCartIconEvent(event: CallbackInfo) {
135
- if (this.onClickCartIcon) {
178
+ if (this.onCustomClickCartIcon) {
179
+ this.onCustomClickCartIcon();
180
+ } else if (this.onClickCartIcon) {
136
181
  const callbackId = event.callbackId;
137
182
  delete event.callbackId;
138
183
  const props = await this.onClickCartIcon();
@@ -145,14 +190,48 @@ class VideoShopping {
145
190
  private async handleUpdateProductDetailsEvent(
146
191
  event: UpdateProductDetailsEvent & CallbackInfo
147
192
  ) {
193
+ const callbackId = event.callbackId;
194
+ delete event.callbackId;
148
195
  if (this.onUpdateProductDetails) {
149
- const callbackId = event.callbackId;
150
- delete event.callbackId;
196
+ FWLoggerUtil.log(
197
+ `Call onUpdateProductDetails callback productIds: ${event.productIds} callbackId: ${callbackId}`
198
+ );
151
199
  const productList = await this.onUpdateProductDetails(
152
200
  event as UpdateProductDetailsEvent
153
201
  );
202
+
203
+ const productIds = (productList || []).map(
204
+ (product) => product.productId ?? ''
205
+ );
206
+
207
+ FWLoggerUtil.log(
208
+ `Get result from onUpdateProductDetails callback productIds: ${productIds} productListLength: ${
209
+ (productList || []).length
210
+ }`
211
+ );
212
+
154
213
  if (productList) {
155
- ShoppingModule.updateVideoProducts(productList, callbackId!);
214
+ if (callbackId) {
215
+ ShoppingModule.updateVideoProducts(productList, callbackId);
216
+ }
217
+ } else {
218
+ if (callbackId) {
219
+ if (Platform.OS === 'ios') {
220
+ ShoppingModule.clearCallbackId(
221
+ callbackId,
222
+ FWEventName.UpdateProductDetails
223
+ );
224
+ }
225
+ }
226
+ }
227
+ } else {
228
+ if (callbackId) {
229
+ if (Platform.OS === 'ios') {
230
+ ShoppingModule.clearCallbackId(
231
+ callbackId,
232
+ FWEventName.UpdateProductDetails
233
+ );
234
+ }
156
235
  }
157
236
  }
158
237
  }
@@ -166,8 +245,8 @@ class VideoShopping {
166
245
  const config = await this.onWillDisplayProduct(
167
246
  event as WillDisplayProductEvent
168
247
  );
169
- if (config) {
170
- ShoppingModule.updateProductViewConfig(config, callbackId!);
248
+ if (config && callbackId) {
249
+ ShoppingModule.updateProductViewConfig(config, callbackId);
171
250
  }
172
251
  }
173
252
  }
@@ -140,8 +140,8 @@ export default class VideoFeed extends React.Component<IVideoFeedProps> {
140
140
  playlist = '',
141
141
  playlistGroup = '',
142
142
  mode = 'row',
143
- videoFeedConfiguration,
144
143
  } = this.props;
144
+ const videoFeedConfiguration = this._getVideoFeedConfiguration();
145
145
  const titleHidden = videoFeedConfiguration?.title?.hidden ?? false;
146
146
  const titlePosition = videoFeedConfiguration?.titlePosition ?? 'nested';
147
147
  const customLayoutName = videoFeedConfiguration?.customLayoutName ?? '';
@@ -153,6 +153,8 @@ export default class VideoFeed extends React.Component<IVideoFeedProps> {
153
153
  const textColorOfAdBadge = adBadgeConfiguration.textColor ?? '';
154
154
  const dynamicContentParametersString =
155
155
  this._generateDynamicContentParametersString();
156
+ const enableAutoplay = videoFeedConfiguration?.enableAutoplay ?? false;
157
+ const gridColumns = videoFeedConfiguration?.gridColumns ?? 2;
156
158
 
157
159
  let key = `source:${source}`;
158
160
  key += `_channel:${channel}`;
@@ -167,6 +169,8 @@ export default class VideoFeed extends React.Component<IVideoFeedProps> {
167
169
  key += `_backgroundColorOfAdBadge:${backgroundColorOfAdBadge}`;
168
170
  key += `_textColorOfAdBadge:${textColorOfAdBadge}`;
169
171
  key += `_dynamicContentParameters:${dynamicContentParametersString}`;
172
+ key += `_enableAutoplay:${enableAutoplay}`;
173
+ key += `_gridColumns:${gridColumns}`;
170
174
 
171
175
  return (
172
176
  <FWVideoFeed
@@ -200,4 +204,21 @@ export default class VideoFeed extends React.Component<IVideoFeedProps> {
200
204
 
201
205
  return resultString;
202
206
  }
207
+
208
+ private _getVideoFeedConfiguration(): VideoFeedConfiguration | undefined {
209
+ const { videoFeedConfiguration } = this.props;
210
+ if (!videoFeedConfiguration) {
211
+ return undefined;
212
+ }
213
+
214
+ let resultFeedConfiguration: VideoFeedConfiguration = {
215
+ ...videoFeedConfiguration,
216
+ };
217
+ if (resultFeedConfiguration.gridColumns) {
218
+ resultFeedConfiguration.gridColumns = Math.floor(
219
+ resultFeedConfiguration.gridColumns!
220
+ );
221
+ }
222
+ return resultFeedConfiguration;
223
+ }
203
224
  }
package/src/index.tsx CHANGED
@@ -38,9 +38,10 @@ import type Product from './models/Product';
38
38
  import type ProductInfoViewConfiguration from './models/ProductInfoViewConfiguration';
39
39
  import type { AddToCartButtonConfiguration } from './models/ProductInfoViewConfiguration';
40
40
  import type ProductUnit from './models/ProductUnit';
41
- import type { ProductPrice } from './models/ProductUnit';
41
+ import type { ProductPrice, ProductUnitOption } from './models/ProductUnit';
42
42
  import type VideoFeedConfiguration from './models/VideoFeedConfiguration';
43
43
  import type {
44
+ VideoFeedContentPadding,
44
45
  VideoFeedPlayIconConfiguration,
45
46
  VideoFeedTitleConfiguration,
46
47
  VideoFeedTitlePosition,
@@ -59,6 +60,7 @@ import type {
59
60
  import type {
60
61
  AddToCartCallback,
61
62
  ClickCartIconCallback,
63
+ CustomClickCartIconCallback,
62
64
  UpdateProductDetailsCallback,
63
65
  WillDisplayProductCallback,
64
66
  } from './VideoShopping';
@@ -74,6 +76,7 @@ export {
74
76
  AddToCartEvent,
75
77
  AddToCartResult,
76
78
  ClickCartIconCallback,
79
+ CustomClickCartIconCallback,
77
80
  CustomCTAClickCallback,
78
81
  CustomCTAClickEvent,
79
82
  CustomCTALinkContentRender,
@@ -95,6 +98,7 @@ export {
95
98
  ProductInfoViewConfiguration,
96
99
  ProductPrice,
97
100
  ProductUnit,
101
+ ProductUnitOption,
98
102
  SDKInitCallback,
99
103
  SDKInitEvent,
100
104
  UpdateProductDetailsCallback,
@@ -102,6 +106,7 @@ export {
102
106
  VideoFeed,
103
107
  VideoFeedClickCallback,
104
108
  VideoFeedConfiguration,
109
+ VideoFeedContentPadding,
105
110
  VideoFeedMode,
106
111
  VideoFeedPlayIconConfiguration,
107
112
  VideoFeedSource,
@@ -3,6 +3,11 @@ export interface ProductPrice {
3
3
  currencyCode: string;
4
4
  }
5
5
 
6
+ export interface ProductUnitOption {
7
+ name: string;
8
+ value: string;
9
+ }
10
+
6
11
  export default interface ProductUnit {
7
12
  /**
8
13
  * The unique identifier of the product unit.
@@ -16,8 +21,16 @@ export default interface ProductUnit {
16
21
  * The url of the product unit.
17
22
  */
18
23
  url?: string;
24
+ /**
25
+ * The image url of the product unit.
26
+ */
27
+ imageUrl?: string;
19
28
  /**
20
29
  * The price of the product unit.
21
30
  */
22
31
  price?: ProductPrice;
32
+ /**
33
+ * The options of the product unit.
34
+ */
35
+ options?: ProductUnitOption[];
23
36
  }
@@ -11,33 +11,62 @@ export interface VideoFeedPlayIconConfiguration {
11
11
 
12
12
  export type VideoFeedTitlePosition = 'stacked' | 'nested';
13
13
 
14
+ export interface VideoFeedContentPadding {
15
+ top?: number;
16
+ right?: number;
17
+ bottom?: number;
18
+ left?: number;
19
+ }
20
+
14
21
  export default interface VideoFeedConfiguration {
15
22
  /**
16
23
  * Background color of video feed.
17
24
  */
18
25
  backgroundColor?: string;
19
26
  /**
20
- * Corner radius of video feed. Only supported on iOS.
27
+ * Corner radius of video feed item. Only supported on iOS.
21
28
  */
22
29
  cornerRadius?: number;
23
30
  /**
24
- * Configuration of video feed title.
31
+ * Configuration of video feed item title.
25
32
  */
26
33
  title?: VideoFeedTitleConfiguration;
27
34
  /**
28
- * Title position of video feed.
35
+ * Title position of video feed item.
29
36
  */
30
37
  titlePosition?: VideoFeedTitlePosition;
31
38
  /**
32
- * Configuration of video feed play icon. Only supported on iOS.
39
+ * Configuration of video feed item play icon. Only supported on iOS.
33
40
  */
34
41
  playIcon?: VideoFeedPlayIconConfiguration;
35
42
  /**
36
- * Indicates if the video feed shows ad badge.
43
+ * Indicates if the video feed item shows ad badge.
37
44
  */
38
45
  showAdBadge?: boolean;
39
46
  /**
40
47
  * Custom layout name for video feed item. Only supported on Android.
41
48
  */
42
49
  customLayoutName?: string;
50
+ /**
51
+ * The aspect ratio(width / height) for video feed item. Only supported on iOS.
52
+ */
53
+ aspectRatio?: number;
54
+ /**
55
+ * The content padding for video feed. Only supported on iOS.
56
+ */
57
+ contentPadding?: VideoFeedContentPadding;
58
+ /**
59
+ * The item spacing for video feed. Only supported on iOS.
60
+ */
61
+ itemSpacing?: number;
62
+ /**
63
+ * Specifies if autoplay is enabled.
64
+ */
65
+ enableAutoplay?: boolean;
66
+ /**
67
+ * The number of feed items to be displayed per row.
68
+ * The property only takes effect when video feed mode is grid.
69
+ * The property value needs to be an integer and greater than 0.
70
+ */
71
+ gridColumns?: number;
43
72
  }
@@ -34,7 +34,11 @@ export default interface VideoPlayerConfiguration {
34
34
  */
35
35
  showMuteButton?: boolean;
36
36
  /**
37
- * The property is valid only if showMuteButton is true.
37
+ * Specifies the video player launch behavior.
38
38
  */
39
39
  launchBehavior?: VideoLaunchBehavior;
40
+ /**
41
+ * Indicates if Firework branding should be showed or not.
42
+ */
43
+ showBranding?: boolean;
40
44
  }
@@ -33,6 +33,8 @@ interface IShoppingModule extends NativeModule {
33
33
  ): void;
34
34
  setCartIconVisible(visible: boolean): void;
35
35
  setCartItemCount(count: number): void;
36
+ setCustomClickCartIconEnabled(enabled: boolean): Promise<void>; // Only supported on iOS
37
+ clearCallbackId(callbackId: number | string, eventName: string): void; // Only supported on iOS
36
38
  }
37
39
 
38
40
  const ShoppingModuleEventEmitter = new NativeEventEmitter(ShoppingModule);