react-native-firework-sdk 2.0.0 → 2.1.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.
Files changed (138) hide show
  1. package/android/build.gradle +5 -3
  2. package/android/src/main/java/com/fireworksdk/bridge/components/videofeed/StoryBlockFragment.kt +129 -0
  3. package/android/src/main/java/com/fireworksdk/bridge/components/videofeed/StoryBlockFrameLayout.kt +84 -0
  4. package/android/src/main/java/com/fireworksdk/bridge/models/FWEventName.kt +2 -1
  5. package/android/src/main/java/com/fireworksdk/bridge/models/FWProductInfoViewConfiguration.kt +18 -5
  6. package/android/src/main/java/com/fireworksdk/bridge/models/FWProductInfoViewConfigurationDeserializer.kt +33 -9
  7. package/android/src/main/java/com/fireworksdk/bridge/models/FWShoppingCtaResult.kt +17 -0
  8. package/android/src/main/java/com/fireworksdk/bridge/models/FWShoppingCtaResultDeserializer.kt +33 -0
  9. package/android/src/main/java/com/fireworksdk/bridge/reactnative/manager/FWStoryBlockManager.kt +126 -45
  10. package/android/src/main/java/com/fireworksdk/bridge/reactnative/manager/FWVideoFeedManager.kt +6 -3
  11. package/android/src/main/java/com/fireworksdk/bridge/reactnative/models/FWVideoShoppingInterface.kt +2 -2
  12. package/android/src/main/java/com/fireworksdk/bridge/reactnative/module/FWNavigatorModule.kt +9 -1
  13. package/android/src/main/java/com/fireworksdk/bridge/reactnative/module/FWVideoShoppingModule.kt +81 -50
  14. package/android/src/main/java/com/fireworksdk/bridge/reactnative/utils/FWEventUtils.kt +9 -2
  15. package/android/src/main/java/com/fireworksdk/bridge/utils/FWConfigUtil.kt +1 -1
  16. package/android/src/main/java/com/fireworksdk/bridge/utils/FWGlobalDataUtil.kt +4 -0
  17. package/android/src/main/java/com/fireworksdk/bridge/utils/FWLanguageUtil.kt +48 -16
  18. package/android/src/main/res/layout/fw_bridge_story_block.xml +24 -0
  19. package/ios/Components/StoryBlock.swift +33 -2
  20. package/ios/Components/StoryBlockManager.m +32 -0
  21. package/ios/Components/VideoFeed.swift +9 -29
  22. package/ios/Components/VideoFeedManager.m +11 -6
  23. package/ios/FireworkSdk.xcodeproj/project.pbxproj +378 -104
  24. package/ios/Models/NativeToRN/FireworkEventName.swift +3 -1
  25. package/ios/Models/RNToNative/RCTConvert+Shopping.swift +21 -0
  26. package/ios/Models/RNToNative/RCTConvert+VideoFeed.swift +27 -0
  27. package/ios/Modules/FWNavigatorModule/FWNavigatorModule.swift +5 -1
  28. package/ios/Modules/FireworkSDKModule/FireworkSDKModule.m +1 -0
  29. package/ios/Modules/FireworkSDKModule/FireworkSDKModule.swift +30 -0
  30. package/ios/Modules/Shopping/ProductInfoViewConfiguration.swift +13 -0
  31. package/ios/Modules/Shopping/ShoppingCTAResult.swift +16 -0
  32. package/ios/Modules/Shopping/ShoppingModule.m +2 -1
  33. package/ios/Modules/Shopping/ShoppingModule.swift +106 -30
  34. package/ios/Support/MultiHostStreaming/FWMultiHostStreaming.podspec +24 -0
  35. package/ios/Support/MultiHostStreaming/src/MultiHostStreamingSDK.swift +17 -0
  36. package/ios/Utils/AppLanguage/Bundle+FWSwizzle.swift +58 -0
  37. package/ios/Utils/AppLanguage/FWAppLanguageManager.swift +139 -0
  38. package/ios/Utils/AppLanguage/FWLanguageUtil.swift +43 -0
  39. package/ios/Utils/AppLanguage/NumberFormatter+FWSwizzle.swift +25 -0
  40. package/ios/Utils/AppLanguage/UIImageView+FWSwizzle.swift +91 -0
  41. package/ios/Utils/AppLanguage/UILabel+FWSwizzle.swift +98 -0
  42. package/ios/Utils/AppLanguage/UITextField+FWSwizzle.swift +97 -0
  43. package/ios/Utils/AppLanguage/UITextView+FWSwizzle.swift +97 -0
  44. package/ios/Utils/AppLanguage/UIView+FWSwizzle.swift +38 -0
  45. package/ios/Utils/AppLanguage/UIViewController+FWSwizzle.swift +32 -0
  46. package/ios/Utils/AppLanguage/UIWindow+FWSwizzle.swift +26 -0
  47. package/ios/Utils/AppLanguage/URLSession+FWSwizzle.swift +69 -0
  48. package/ios/Utils/{DispatchQueue+FWOnce.swift → Extensions/DispatchQueue+FWOnce.swift} +3 -3
  49. package/ios/Utils/{UINavigationController+FWSwizzle.swift → Extensions/Swizzle/UINavigationController+FWSwizzle.swift} +6 -8
  50. package/ios/Utils/Extensions/UIView+FWUIHierarchy.swift +47 -0
  51. package/ios/Utils/FWRTL/Classes/Manager/FWRTLManager.h +25 -0
  52. package/ios/Utils/FWRTL/Classes/Manager/FWRTLManager.m +75 -0
  53. package/ios/Utils/FWRTL/Classes/UICategories/CALayer+FWRTL.h +21 -0
  54. package/ios/Utils/FWRTL/Classes/UICategories/CALayer+FWRTL.m +124 -0
  55. package/ios/Utils/FWRTL/Classes/UICategories/FWRTLRemoteViewControllerAdaptor.h +11 -0
  56. package/ios/Utils/FWRTL/Classes/UICategories/FWRTLRemoteViewControllerAdaptor.m +86 -0
  57. package/ios/Utils/FWRTL/Classes/UICategories/FWRTLWhiteListManager.h +16 -0
  58. package/ios/Utils/FWRTL/Classes/UICategories/FWRTLWhiteListManager.m +55 -0
  59. package/ios/Utils/FWRTL/Classes/UICategories/UILabel+FWRTL.h +18 -0
  60. package/ios/Utils/FWRTL/Classes/UICategories/UILabel+FWRTL.m +39 -0
  61. package/ios/Utils/FWRTL/Classes/UICategories/UIView+FWRTL.h +54 -0
  62. package/ios/Utils/FWRTL/Classes/UICategories/UIView+FWRTL.m +141 -0
  63. package/ios/Utils/FWRTL/Classes/UICategories/UIWindow+FWRTL.h +16 -0
  64. package/ios/Utils/FWRTL/Classes/UICategories/UIWindow+FWRTL.m +20 -0
  65. package/ios/Utils/FWRTL/Classes/Utils/FWRTLDefinitions.h +52 -0
  66. package/ios/Utils/FWRTL/Classes/Utils/NSObject+FWRTLReloadBlock.h +19 -0
  67. package/ios/Utils/FWRTL/Classes/Utils/NSObject+FWRTLReloadBlock.m +49 -0
  68. package/ios/Utils/FWRTL/Classes/Utils/NSString+FWRTL.h +21 -0
  69. package/ios/Utils/FWRTL/Classes/Utils/NSString+FWRTL.m +38 -0
  70. package/ios/Utils/FWRTL/Classes/Utils/UIImage+FWRTL.h +18 -0
  71. package/ios/Utils/FWRTL/Classes/Utils/UIImage+FWRTL.m +43 -0
  72. package/ios/Utils/FWSwizzleLoader.m +6 -1
  73. package/ios/Utils/FWSwizzleLoader.swift +13 -0
  74. package/ios/Utils/FWSwizzleUtil.swift +17 -9
  75. package/ios/react_native_firework_sdk.h +1 -0
  76. package/ios/scripts/react_native_firework_sdk_pods.rb +31 -0
  77. package/lib/commonjs/FireworkSDK.js +21 -4
  78. package/lib/commonjs/FireworkSDK.js.map +1 -1
  79. package/lib/commonjs/VideoShopping.js +20 -37
  80. package/lib/commonjs/VideoShopping.js.map +1 -1
  81. package/lib/commonjs/components/StoryBlock.js +190 -125
  82. package/lib/commonjs/components/StoryBlock.js.map +1 -1
  83. package/lib/commonjs/components/VideoFeed.js +11 -1
  84. package/lib/commonjs/components/VideoFeed.js.map +1 -1
  85. package/lib/commonjs/index.js.map +1 -1
  86. package/lib/commonjs/models/FWEventName.js +2 -0
  87. package/lib/commonjs/models/FWEventName.js.map +1 -1
  88. package/lib/commonjs/models/ShoppingCTAResult.js +2 -0
  89. package/lib/commonjs/modules/FireworkSDKModule.js.map +1 -1
  90. package/lib/commonjs/modules/ShoppingModule.js.map +1 -1
  91. package/lib/module/FireworkSDK.js +21 -4
  92. package/lib/module/FireworkSDK.js.map +1 -1
  93. package/lib/module/VideoShopping.js +20 -39
  94. package/lib/module/VideoShopping.js.map +1 -1
  95. package/lib/module/components/StoryBlock.js +179 -131
  96. package/lib/module/components/StoryBlock.js.map +1 -1
  97. package/lib/module/components/VideoFeed.js +10 -1
  98. package/lib/module/components/VideoFeed.js.map +1 -1
  99. package/lib/module/index.js.map +1 -1
  100. package/lib/module/models/FWEventName.js +2 -0
  101. package/lib/module/models/FWEventName.js.map +1 -1
  102. package/lib/module/models/ShoppingCTAResult.js +2 -0
  103. package/lib/module/modules/FireworkSDKModule.js.map +1 -1
  104. package/lib/module/modules/ShoppingModule.js.map +1 -1
  105. package/lib/typescript/FireworkSDK.d.ts +7 -4
  106. package/lib/typescript/VideoShopping.d.ts +9 -11
  107. package/lib/typescript/components/StoryBlock.d.ts +19 -25
  108. package/lib/typescript/index.d.ts +6 -6
  109. package/lib/typescript/models/FWEventName.d.ts +2 -0
  110. package/lib/typescript/models/FWEvents.d.ts +14 -1
  111. package/lib/typescript/models/ProductInfoViewConfiguration.d.ts +36 -1
  112. package/lib/typescript/models/ShoppingCTAResult.d.ts +11 -0
  113. package/lib/typescript/modules/FireworkSDKModule.d.ts +1 -2
  114. package/lib/typescript/modules/ShoppingModule.d.ts +2 -1
  115. package/package.json +2 -2
  116. package/react-native-firework-sdk.podspec +26 -24
  117. package/src/FireworkSDK.ts +18 -5
  118. package/src/VideoShopping.ts +40 -53
  119. package/src/components/StoryBlock.tsx +199 -96
  120. package/src/components/VideoFeed.tsx +11 -0
  121. package/src/index.ts +15 -7
  122. package/src/models/FWEventName.ts +2 -0
  123. package/src/models/FWEvents.ts +14 -1
  124. package/src/models/ProductInfoViewConfiguration.ts +38 -1
  125. package/src/models/ShoppingCTAResult.ts +11 -0
  126. package/src/modules/FireworkSDKModule.ts +1 -2
  127. package/src/modules/ShoppingModule.ts +5 -5
  128. package/android/src/main/java/com/fireworksdk/bridge/constants/FWCommandConstant.kt +0 -6
  129. package/ios/Utils/UIView+ParentViewController.swift +0 -21
  130. package/lib/commonjs/models/AddToCartResult.js +0 -2
  131. package/lib/module/models/AddToCartResult.js +0 -2
  132. package/lib/typescript/models/AddToCartResult.d.ts +0 -10
  133. package/src/models/AddToCartResult.ts +0 -10
  134. /package/ios/Utils/{String+Color.swift → Extensions/String+Color.swift} +0 -0
  135. /package/ios/Utils/{UIView+Constraints.swift → Extensions/UIView+Constraints.swift} +0 -0
  136. /package/ios/Utils/{UIViewController+AttachChild.swift → Extensions/UIViewController+AttachChild.swift} +0 -0
  137. /package/lib/commonjs/models/{AddToCartResult.js.map → ShoppingCTAResult.js.map} +0 -0
  138. /package/lib/module/models/{AddToCartResult.js.map → ShoppingCTAResult.js.map} +0 -0
@@ -100,6 +100,11 @@ class FireworkSDK {
100
100
  }
101
101
  private _adBadgeConfiguration: AdBadgeConfiguration | undefined;
102
102
 
103
+ public get appLanguage(): string | undefined | null {
104
+ return this._appLanguage;
105
+ }
106
+ private _appLanguage: string | undefined | null;
107
+
103
108
  /**
104
109
  * Defaults to false.
105
110
  * You can enable debug logs by setting this property to true.
@@ -245,13 +250,21 @@ class FireworkSDK {
245
250
  }
246
251
 
247
252
  /**
248
- * Change App level language. Only supported on Android.
249
- * @param {string} language Such as en, ar and en-US
250
- * @returns {Promise<boolean>}
253
+ * Change App level language. Only supported on iOS.
254
+ * @param {string | undefined | null} language Such as en, ar and en-US
255
+ * If language is null or undefined or empty string, SDK will use system language.
256
+ * @returns {Promise<boolean>} The result of changing app language.
251
257
  */
252
- public async changeAppLanguage(language: string): Promise<boolean> {
253
- if (Platform.OS === 'android') {
258
+ public async changeAppLanguage(language?: string | null): Promise<boolean> {
259
+ if (Platform.OS === 'ios') {
254
260
  const result = await FireworkSDKModule.changeAppLanguage(language);
261
+ if (result) {
262
+ const valueHasChanged = this._appLanguage !== language;
263
+ this._appLanguage = language;
264
+ if (valueHasChanged) {
265
+ this.eventEmitter.emit(FWEventName.AppLanguageUpdated);
266
+ }
267
+ }
255
268
  return result;
256
269
  }
257
270
 
@@ -1,10 +1,9 @@
1
1
  import { NativeEventEmitter, Platform } from 'react-native';
2
2
 
3
- import type AddToCartResult from './models/AddToCartResult';
4
3
  import { FWEventName } from './models/FWEventName';
5
4
  import type {
6
- AddToCartEvent,
7
5
  CustomClickLinkButtonEvent,
6
+ ShoppingCTAEvent,
8
7
  UpdateProductDetailsEvent,
9
8
  } from './models/FWEvents';
10
9
  import type Product from './models/Product';
@@ -13,10 +12,11 @@ import ShoppingModule, {
13
12
  ShoppingModuleEventEmitter,
14
13
  } from './modules/ShoppingModule';
15
14
  import FWLoggerUtil from './utils/FWLoggerUtil';
15
+ import type ShoppingCTAResult from './models/ShoppingCTAResult';
16
16
 
17
- export type AddToCartCallback = (
18
- event: AddToCartEvent
19
- ) => Promise<AddToCartResult | undefined | null>;
17
+ export type ShoppingCTACallback = (
18
+ event: ShoppingCTAEvent
19
+ ) => Promise<ShoppingCTAResult>;
20
20
 
21
21
  export type CustomClickCartIconCallback = () => Promise<void>;
22
22
 
@@ -35,12 +35,10 @@ class VideoShopping {
35
35
  private static _instance?: VideoShopping;
36
36
 
37
37
  /**
38
- * This callback is triggered when the user clicks the "Add to cart" button.
39
- *
40
- * The host apps can return an AddToCartResult object to tell FireworkSDK the result of adding to cart.
41
- * If the host apps want to customize the processing logic of clicking "Add to cart" button, they could return null or undefined in the callback.
38
+ * This callback is triggered when the user clicks the "Add to cart" or "Shop now" button.
39
+ * The host app can return a ShoppingCTAResult object to tell SDK how to handle the result.
42
40
  */
43
- public onAddToCart?: AddToCartCallback;
41
+ public onShoppingCTA?: ShoppingCTACallback;
44
42
 
45
43
  /**
46
44
  * This callback is triggered when the user clicks the shopping cart icon.
@@ -82,8 +80,8 @@ class VideoShopping {
82
80
  private _cartIconVisible: boolean = true;
83
81
 
84
82
  /**
85
- * The host app can use this property to configure "Add to cart" button style
86
- * and hide the link button next to "Add to cart" button. Only supported on iOS.
83
+ * The configuration of product info view.
84
+ * Please refer to {@link ProductInfoViewConfiguration} for more details.
87
85
  */
88
86
  public get productInfoViewConfiguration():
89
87
  | ProductInfoViewConfiguration
@@ -95,9 +93,7 @@ class VideoShopping {
95
93
  value: ProductInfoViewConfiguration | undefined
96
94
  ) {
97
95
  this._productInfoViewConfiguration = value;
98
- if (Platform.OS === 'ios') {
99
- ShoppingModule.setProductInfoViewConfiguration(value ?? {});
100
- }
96
+ ShoppingModule.setProductInfoViewConfiguration(value ?? {});
101
97
  }
102
98
 
103
99
  private _productInfoViewConfiguration?:
@@ -120,9 +116,7 @@ class VideoShopping {
120
116
  value: CustomClickLinkButtonCallback | undefined
121
117
  ) {
122
118
  this._onCustomClickLinkButton = value;
123
- if (Platform.OS === 'android') {
124
- ShoppingModule.setCustomClickLinkButtonEnabled(!!value);
125
- }
119
+ ShoppingModule.setCustomClickLinkButtonEnabled(!!value);
126
120
  }
127
121
  private _onCustomClickLinkButton?: CustomClickLinkButtonCallback | undefined;
128
122
 
@@ -140,12 +134,15 @@ class VideoShopping {
140
134
  }
141
135
 
142
136
  private constructor() {
143
- this.eventEmitter.addListener(FWEventName.AddToCart, (event) => {
144
- FWLoggerUtil.log(
145
- `Receive AddToCart event productId: ${event?.productId} unitId: ${event?.unitId}`
146
- );
147
- this.handleAddToCartEvent(event);
148
- });
137
+ this.eventEmitter.addListener(
138
+ FWEventName.ShoppingCTAButtonClick,
139
+ (event) => {
140
+ FWLoggerUtil.log(
141
+ `Receive ShoppingCTA event productId: ${event?.productId} unitId: ${event?.unitId} url: ${event?.url}`
142
+ );
143
+ this.handleShoppingCTAEvent(event);
144
+ }
145
+ );
149
146
 
150
147
  this.eventEmitter.addListener(FWEventName.ClickCartIcon, () => {
151
148
  FWLoggerUtil.log('Receive ClickCartIcon event');
@@ -161,17 +158,15 @@ class VideoShopping {
161
158
 
162
159
  this.eventEmitter.addListener(FWEventName.LogMessage, () => {});
163
160
 
164
- if (Platform.OS === 'android') {
165
- this.eventEmitter.addListener(
166
- FWEventName.CustomLinkButtonClick,
167
- (event) => {
168
- FWLoggerUtil.log(
169
- `Receive CustomLinkButtonClick event url: ${event?.url}`
170
- );
171
- this.handleCustomLinkButtonClickEvent(event);
172
- }
173
- );
174
- }
161
+ this.eventEmitter.addListener(
162
+ FWEventName.CustomLinkButtonClick,
163
+ (event) => {
164
+ FWLoggerUtil.log(
165
+ `Receive CustomLinkButtonClick event url: ${event?.url}`
166
+ );
167
+ this.handleCustomLinkButtonClickEvent(event);
168
+ }
169
+ );
175
170
  }
176
171
 
177
172
  /**
@@ -186,30 +181,22 @@ class VideoShopping {
186
181
  ShoppingModule.setCartItemCount(count);
187
182
  }
188
183
 
189
- private async handleAddToCartEvent(event: any) {
184
+ private async handleShoppingCTAEvent(event: any) {
190
185
  const callbackId = event.callbackId;
191
186
  delete event.callbackId;
192
- if (this.onAddToCart) {
193
- const result = await this.onAddToCart(event as AddToCartEvent);
194
- if (result) {
195
- if (callbackId) {
196
- ShoppingModule.updateAddToCartStatus(
197
- result.res,
198
- result.tips,
199
- callbackId
200
- );
201
- }
202
- } else {
203
- if (callbackId) {
204
- if (Platform.OS === 'ios') {
205
- ShoppingModule.clearCallbackId(callbackId, FWEventName.AddToCart);
206
- }
207
- }
187
+
188
+ if (this.onShoppingCTA) {
189
+ const result = await this.onShoppingCTA(event as ShoppingCTAEvent);
190
+ if (callbackId) {
191
+ ShoppingModule.updateShoppingCTAResult(result, callbackId);
208
192
  }
209
193
  } else {
210
194
  if (callbackId) {
211
195
  if (Platform.OS === 'ios') {
212
- ShoppingModule.clearCallbackId(callbackId, FWEventName.AddToCart);
196
+ ShoppingModule.clearCallbackId(
197
+ callbackId,
198
+ FWEventName.ShoppingCTAButtonClick
199
+ );
213
200
  }
214
201
  }
215
202
  }
@@ -1,14 +1,25 @@
1
- import React from 'react';
1
+ import type { ForwardRefRenderFunction } from 'react';
2
+ import React, {
3
+ forwardRef,
4
+ useEffect,
5
+ useImperativeHandle,
6
+ useReducer,
7
+ useRef,
8
+ useState,
9
+ } from 'react';
2
10
 
3
- import type {
4
- EmitterSubscription,
11
+ import {
12
+ BackHandler,
13
+ findNodeHandle,
5
14
  NativeSyntheticEvent,
15
+ Platform,
6
16
  StyleProp,
17
+ UIManager,
7
18
  ViewStyle,
8
19
  } from 'react-native';
9
- import { findNodeHandle, Platform, UIManager } from 'react-native';
10
20
 
11
21
  import FireworkSDK from '../FireworkSDK';
22
+ import type AdConfiguration from '../models/AdConfiguration';
12
23
  import type FWError from '../models/FWError';
13
24
  import { FWEventName } from '../models/FWEventName';
14
25
  import type { StoryBlockSource } from '../models/StoryBlockSource';
@@ -18,17 +29,16 @@ import FWStoryBlock from './FWStoryBlock';
18
29
 
19
30
  const NativeComponentName = 'FWStoryBlock';
20
31
 
21
- const createFragment = (viewId: any) => {
22
- if (Platform.OS === 'android') {
23
- UIManager.dispatchViewManagerCommand(
24
- viewId,
25
- UIManager.getViewManagerConfig(
26
- NativeComponentName
27
- ).Commands.create.toString(),
28
- [viewId]
29
- );
30
- }
31
- };
32
+ export interface IStoryBlockMethods {
33
+ /**
34
+ * Play the story block.
35
+ */
36
+ play: () => void;
37
+ /**
38
+ * Pause the story block.
39
+ */
40
+ pause: () => void;
41
+ }
32
42
 
33
43
  /**
34
44
  * The props type of StoryBlock component.
@@ -55,98 +65,119 @@ export interface IStoryBlockProps {
55
65
  */
56
66
  dynamicContentParameters?: { [key: string]: string[] };
57
67
  /**
58
- * Specifies if Picture in Picture is enabled.
68
+ * Specifies if Picture in Picture is enabled. Only supported on iOS.
59
69
  */
60
70
  enablePictureInPicture?: boolean;
71
+ /**
72
+ * Ad configuration of the feed. Only supported on iOS.
73
+ */
74
+ adConfiguration?: AdConfiguration;
61
75
  /**
62
76
  * The feed loading result callback. It means loading successfully when error equals to undefined.
63
77
  */
64
78
  onStoryBlockLoadFinished?: (error?: FWError) => void;
65
79
  }
66
80
 
67
- /**
68
- * StoryBlock component. Only supported on iOS.
69
- */
70
- class StoryBlock extends React.Component<IStoryBlockProps> {
71
- private _nativeComponentRef = React.createRef<any>();
81
+ const StoryBlock: ForwardRefRenderFunction<
82
+ IStoryBlockMethods,
83
+ IStoryBlockProps
84
+ > = (props: IStoryBlockProps, forwardedRef) => {
85
+ const nativeComponentRef = useRef(null);
86
+ const [isFullscreenState, setIsFullscreenState] = useState<boolean>(false);
87
+ const [, forceUpdate] = useReducer((x) => x + 1, 0);
88
+ useImperativeHandle(
89
+ forwardedRef,
90
+ () => {
91
+ const sendCommand = (command: string) => {
92
+ const nativeNodeHandle = findNodeHandle(nativeComponentRef.current);
72
93
 
73
- private _subscriptions: EmitterSubscription[] = [];
94
+ let commandId: string | number =
95
+ UIManager.getViewManagerConfig(NativeComponentName).Commands[command];
96
+ if (Platform.OS === 'android') {
97
+ commandId = commandId.toString();
98
+ }
74
99
 
75
- /**
76
- * @ignore
77
- */
78
- componentDidMount() {
100
+ UIManager.dispatchViewManagerCommand(
101
+ findNodeHandle(nativeNodeHandle),
102
+ commandId,
103
+ []
104
+ );
105
+ };
106
+ return {
107
+ play: () => {
108
+ sendCommand('play');
109
+ },
110
+ pause: () => {
111
+ sendCommand('pause');
112
+ },
113
+ };
114
+ },
115
+ []
116
+ );
117
+ useEffect(() => {
79
118
  const subscriptionOfShareBaseURLUpdated =
80
119
  FireworkSDKModuleEventEmitter.addListener(
81
120
  FWEventName.ShareBaseURLUpdated,
82
121
  () => {
83
- this.setState({});
122
+ FWLoggerUtil.log('Receive FWEventName.ShareBaseURLUpdated');
123
+ forceUpdate();
84
124
  }
85
125
  );
86
- this._subscriptions.push(subscriptionOfShareBaseURLUpdated);
87
-
88
126
  const subscriptionOfAdBadgeConfigurationUpdated =
89
127
  FireworkSDKModuleEventEmitter.addListener(
90
128
  FWEventName.AdBadgeConfigurationUpdated,
91
129
  () => {
92
- this.setState({});
130
+ FWLoggerUtil.log('Receive FWEventName.AdBadgeConfigurationUpdated');
131
+ forceUpdate();
93
132
  }
94
133
  );
95
- this._subscriptions.push(subscriptionOfAdBadgeConfigurationUpdated);
96
134
 
97
135
  const subscriptionOfVideoLaunchBehaviorUpdated =
98
136
  FireworkSDKModuleEventEmitter.addListener(
99
137
  FWEventName.VideoLaunchBehaviorUpdated,
100
138
  () => {
101
139
  FWLoggerUtil.log('Receive FWEventName.VideoLaunchBehaviorUpdated');
102
- this.setState({});
140
+ forceUpdate();
103
141
  }
104
142
  );
105
- this._subscriptions.push(subscriptionOfVideoLaunchBehaviorUpdated);
106
143
 
107
- if (Platform.OS === 'android') {
108
- const viewId = findNodeHandle(this._nativeComponentRef.current);
109
- FWLoggerUtil.log(
110
- `StoryBlock componentDidMount createFragment viewId: ${viewId}`
144
+ const subscriptionOfAppLanguageUpdated =
145
+ FireworkSDKModuleEventEmitter.addListener(
146
+ FWEventName.AppLanguageUpdated,
147
+ () => {
148
+ FWLoggerUtil.log('Receive FWEventName.AppLanguageUpdated');
149
+ forceUpdate();
150
+ }
111
151
  );
112
- createFragment(viewId);
113
- }
114
- }
115
-
116
- /**
117
- * @ignore
118
- */
119
- componentWillUnmount() {
120
- this._subscriptions.forEach((value) => {
121
- value.remove();
122
- });
123
152
 
124
- this._subscriptions = [];
125
- }
153
+ if (Platform.OS === 'android') {
154
+ setTimeout(() => {
155
+ const viewId = findNodeHandle(nativeComponentRef.current);
156
+ FWLoggerUtil.log(`StoryBlock createFragment viewId: ${viewId}`);
157
+ UIManager.dispatchViewManagerCommand(
158
+ viewId,
159
+ UIManager.getViewManagerConfig(
160
+ NativeComponentName
161
+ ).Commands.create.toString(),
162
+ [viewId]
163
+ );
164
+ }, 500);
165
+ }
126
166
 
127
- /**
128
- * @ignore
129
- */
130
- render() {
131
- const { style } = this.props;
132
-
133
- return (
134
- <FWStoryBlock
135
- ref={this._nativeComponentRef}
136
- key={this._generateKey()}
137
- {...this.props}
138
- onStoryBlockLoadFinished={this._onStoryBlockLoadFinished}
139
- style={Object.assign({}, style, { zIndex: -1 })}
140
- />
141
- );
142
- }
167
+ return () => {
168
+ subscriptionOfShareBaseURLUpdated.remove();
169
+ subscriptionOfAdBadgeConfigurationUpdated.remove();
170
+ subscriptionOfVideoLaunchBehaviorUpdated.remove();
171
+ subscriptionOfAppLanguageUpdated.remove();
172
+ };
173
+ }, []);
143
174
 
144
- private _onStoryBlockLoadFinished = (event: NativeSyntheticEvent<any>) => {
175
+ const handleStoryBlockLoadFinished = (event: NativeSyntheticEvent<any>) => {
145
176
  FWLoggerUtil.log(
146
- `StoryBlock _onStoryBlockLoadFinished ${event.nativeEvent.name}`
177
+ `StoryBlock handleStoryBlockLoadFinished ${event.nativeEvent.name}`
147
178
  );
148
179
 
149
- const { onStoryBlockLoadFinished } = this.props;
180
+ const { onStoryBlockLoadFinished } = props;
150
181
  const { name, reason } = event.nativeEvent;
151
182
 
152
183
  if (onStoryBlockLoadFinished) {
@@ -162,60 +193,132 @@ class StoryBlock extends React.Component<IStoryBlockProps> {
162
193
  }
163
194
  };
164
195
 
165
- private _generateKey(): string {
196
+ const handleStoryBlockFullscreenStateChanged = (
197
+ event: NativeSyntheticEvent<any>
198
+ ) => {
199
+ FWLoggerUtil.log(
200
+ `StoryBlock handleStoryBlockFullscreenStateChanged ${event.nativeEvent.isFullScreen}`
201
+ );
202
+ const { isFullScreen } = event.nativeEvent;
203
+ setIsFullscreenState(isFullScreen);
204
+ };
205
+
206
+ const generateDynamicContentParametersString = (): string => {
207
+ const { dynamicContentParameters } = props;
208
+
209
+ if (!dynamicContentParameters) {
210
+ return '';
211
+ }
212
+
213
+ let resultString = '';
214
+ const sortedKeyList = Object.keys(dynamicContentParameters).sort();
215
+ for (const key of sortedKeyList) {
216
+ const value = dynamicContentParameters[key];
217
+ const valueString = value.join(',');
218
+ if (resultString.length > 0) {
219
+ resultString += '_';
220
+ }
221
+
222
+ resultString += `${key}:${valueString}`;
223
+ }
224
+
225
+ return resultString;
226
+ };
227
+
228
+ const generateVastAttributesString = () => {
229
+ const { adConfiguration } = props;
230
+ const vastAttributes = adConfiguration?.vastAttributes ?? '';
231
+ if (!vastAttributes) {
232
+ return '';
233
+ }
234
+
235
+ let resultString = '';
236
+ for (const attribute of vastAttributes) {
237
+ if (resultString.length > 0) {
238
+ resultString += '_';
239
+ }
240
+ resultString += `${attribute.name ?? ''}:${attribute.value}`;
241
+ }
242
+
243
+ return resultString;
244
+ };
245
+
246
+ const generateKey = (): string => {
166
247
  const {
167
248
  source,
168
249
  channel = '',
169
250
  playlist = '',
170
251
  enablePictureInPicture = false,
171
- } = this.props;
252
+ adConfiguration,
253
+ } = props;
172
254
 
173
255
  const shareBaseURL = FireworkSDK.getInstance().getShareBaseURL() ?? '';
256
+ const videoLaunchBehavior =
257
+ FireworkSDK.getInstance().getVideoLaunchBehavior();
174
258
  const adBadgeConfiguration =
175
259
  FireworkSDK.getInstance().getAdBadgeConfiguration() ?? {};
176
260
  const adBadgeTextType = adBadgeConfiguration.badgeTextType ?? '';
177
261
  const backgroundColorOfAdBadge = adBadgeConfiguration.backgroundColor ?? '';
178
262
  const textColorOfAdBadge = adBadgeConfiguration.textColor ?? '';
179
263
  const dynamicContentParametersString =
180
- this._generateDynamicContentParametersString();
181
- const videoLaunchBehavior =
182
- FireworkSDK.getInstance().getVideoLaunchBehavior() ?? 'default';
264
+ generateDynamicContentParametersString();
265
+ const appLanguage = FireworkSDK.getInstance().appLanguage ?? '';
266
+ const requiresAds = adConfiguration?.requiresAds ?? false;
267
+ const adsFetchTimeout = adConfiguration?.adsFetchTimeout ?? 10;
268
+ const vastAttributesString = generateVastAttributesString();
183
269
 
184
270
  let key = `source:${source}`;
185
271
  key += `_channel:${channel}`;
186
272
  key += `_playlist:${playlist}`;
187
273
  key += `_shareBaseURL:${shareBaseURL}`;
274
+ key += `_videoLaunchBehavior:${videoLaunchBehavior}`;
188
275
  key += `_adBadgeTextType:${adBadgeTextType}`;
189
276
  key += `_backgroundColorOfAdBadge:${backgroundColorOfAdBadge}`;
190
277
  key += `_textColorOfAdBadge:${textColorOfAdBadge}`;
191
278
  key += `_dynamicContentParameters:${dynamicContentParametersString}`;
192
279
  key += `_enablePictureInPicture:${enablePictureInPicture}`;
193
- key += `_videoLaunchBehavior:${videoLaunchBehavior}`;
280
+ key += `_appLanguage:${appLanguage}`;
281
+ key += `_requiresAds:${requiresAds}`;
282
+ key += `_adsFetchTimeout:${adsFetchTimeout}`;
283
+ key += `_vastAttributes:${vastAttributesString}`;
194
284
 
195
285
  return key;
196
- }
197
-
198
- private _generateDynamicContentParametersString(): string {
199
- const { dynamicContentParameters } = this.props;
286
+ };
200
287
 
201
- if (!dynamicContentParameters) {
202
- return '';
288
+ useEffect(() => {
289
+ if (Platform.OS === 'android') {
290
+ const onBackPress = () => {
291
+ if (isFullscreenState) {
292
+ FireworkSDK.getInstance().navigator.popNativeContainer();
293
+ return true;
294
+ }
295
+ return false;
296
+ };
297
+ const subscription = BackHandler.addEventListener(
298
+ 'hardwareBackPress',
299
+ onBackPress
300
+ );
301
+ return () => {
302
+ subscription.remove();
303
+ };
203
304
  }
204
305
 
205
- let resultString = '';
206
- const sortedKeyList = Object.keys(dynamicContentParameters).sort();
207
- for (const key of sortedKeyList) {
208
- const value = dynamicContentParameters[key];
209
- const valueString = value.join(',');
210
- if (resultString.length > 0) {
211
- resultString += '_';
212
- }
213
-
214
- resultString += `${key}:${valueString}`;
215
- }
306
+ return;
307
+ }, [isFullscreenState]);
216
308
 
217
- return resultString;
218
- }
219
- }
309
+ const { style } = props;
310
+ return (
311
+ <FWStoryBlock
312
+ ref={nativeComponentRef}
313
+ key={generateKey()}
314
+ {...props}
315
+ onStoryBlockLoadFinished={handleStoryBlockLoadFinished}
316
+ onStoryBlockFullScreenStateChanged={
317
+ handleStoryBlockFullscreenStateChanged
318
+ }
319
+ style={Object.assign({}, style, { zIndex: -1 })}
320
+ />
321
+ );
322
+ };
220
323
 
221
- export default StoryBlock;
324
+ export default forwardRef(StoryBlock);
@@ -170,6 +170,15 @@ class VideoFeed extends React.Component<IVideoFeedProps> {
170
170
  }
171
171
  );
172
172
  this._subscriptions.push(subscriptionOfVideoLaunchBehaviorUpdated);
173
+ const subscriptionOfAppLanguageUpdated =
174
+ FireworkSDKModuleEventEmitter.addListener(
175
+ FWEventName.AppLanguageUpdated,
176
+ () => {
177
+ FWLoggerUtil.log('Receive FWEventName.AppLanguageUpdated');
178
+ this.setState({});
179
+ }
180
+ );
181
+ this._subscriptions.push(subscriptionOfAppLanguageUpdated);
173
182
  }
174
183
 
175
184
  /**
@@ -222,6 +231,7 @@ class VideoFeed extends React.Component<IVideoFeedProps> {
222
231
  const showAdBadge = videoFeedConfiguration?.showAdBadge ?? false;
223
232
  const videoLaunchBehavior =
224
233
  FireworkSDK.getInstance().getVideoLaunchBehavior() ?? 'default';
234
+ const appLanguage = FireworkSDK.getInstance().appLanguage ?? '';
225
235
 
226
236
  let key = `source:${source}`;
227
237
  key += `_channel:${channel}`;
@@ -243,6 +253,7 @@ class VideoFeed extends React.Component<IVideoFeedProps> {
243
253
  key += `_vastAttributes:${vastAttributesString}`;
244
254
  key += `_showAdBadge:${showAdBadge}`;
245
255
  key += `_videoLaunchBehavior:${videoLaunchBehavior}`;
256
+ key += `_appLanguage:${appLanguage}`;
246
257
 
247
258
  return (
248
259
  <FWVideoFeed