react-native-firework-sdk 2.0.0-beta.5 → 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 (139) 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 +193 -116
  82. package/lib/commonjs/components/StoryBlock.js.map +1 -1
  83. package/lib/commonjs/components/VideoFeed.js +32 -10
  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 +181 -116
  96. package/lib/module/components/StoryBlock.js.map +1 -1
  97. package/lib/module/components/VideoFeed.js +37 -10
  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 -16
  108. package/lib/typescript/components/VideoFeed.d.ts +15 -6
  109. package/lib/typescript/index.d.ts +6 -6
  110. package/lib/typescript/models/FWEventName.d.ts +2 -0
  111. package/lib/typescript/models/FWEvents.d.ts +14 -1
  112. package/lib/typescript/models/ProductInfoViewConfiguration.d.ts +36 -1
  113. package/lib/typescript/models/ShoppingCTAResult.d.ts +11 -0
  114. package/lib/typescript/modules/FireworkSDKModule.d.ts +1 -2
  115. package/lib/typescript/modules/ShoppingModule.d.ts +2 -1
  116. package/package.json +5 -4
  117. package/react-native-firework-sdk.podspec +26 -24
  118. package/src/FireworkSDK.ts +18 -5
  119. package/src/VideoShopping.ts +40 -53
  120. package/src/components/StoryBlock.tsx +201 -88
  121. package/src/components/VideoFeed.tsx +32 -12
  122. package/src/index.ts +15 -7
  123. package/src/models/FWEventName.ts +2 -0
  124. package/src/models/FWEvents.ts +14 -1
  125. package/src/models/ProductInfoViewConfiguration.ts +38 -1
  126. package/src/models/ShoppingCTAResult.ts +11 -0
  127. package/src/modules/FireworkSDKModule.ts +1 -2
  128. package/src/modules/ShoppingModule.ts +5 -5
  129. package/android/src/main/java/com/fireworksdk/bridge/constants/FWCommandConstant.kt +0 -6
  130. package/ios/Utils/UIView+ParentViewController.swift +0 -21
  131. package/lib/commonjs/models/AddToCartResult.js +0 -2
  132. package/lib/module/models/AddToCartResult.js +0 -2
  133. package/lib/typescript/models/AddToCartResult.d.ts +0 -10
  134. package/src/models/AddToCartResult.ts +0 -10
  135. /package/ios/Utils/{String+Color.swift → Extensions/String+Color.swift} +0 -0
  136. /package/ios/Utils/{UIView+Constraints.swift → Extensions/UIView+Constraints.swift} +0 -0
  137. /package/ios/Utils/{UIViewController+AttachChild.swift → Extensions/UIViewController+AttachChild.swift} +0 -0
  138. /package/lib/commonjs/models/{AddToCartResult.js.map → ShoppingCTAResult.js.map} +0 -0
  139. /package/lib/module/models/{AddToCartResult.js.map → ShoppingCTAResult.js.map} +0 -0
@@ -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,88 +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
- 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
- subscriptions: EmitterSubscription[] = [];
74
- componentDidMount() {
94
+ let commandId: string | number =
95
+ UIManager.getViewManagerConfig(NativeComponentName).Commands[command];
96
+ if (Platform.OS === 'android') {
97
+ commandId = commandId.toString();
98
+ }
99
+
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(() => {
75
118
  const subscriptionOfShareBaseURLUpdated =
76
119
  FireworkSDKModuleEventEmitter.addListener(
77
120
  FWEventName.ShareBaseURLUpdated,
78
121
  () => {
79
- this.setState({});
122
+ FWLoggerUtil.log('Receive FWEventName.ShareBaseURLUpdated');
123
+ forceUpdate();
80
124
  }
81
125
  );
82
- this.subscriptions.push(subscriptionOfShareBaseURLUpdated);
83
-
84
126
  const subscriptionOfAdBadgeConfigurationUpdated =
85
127
  FireworkSDKModuleEventEmitter.addListener(
86
128
  FWEventName.AdBadgeConfigurationUpdated,
87
129
  () => {
88
- this.setState({});
130
+ FWLoggerUtil.log('Receive FWEventName.AdBadgeConfigurationUpdated');
131
+ forceUpdate();
89
132
  }
90
133
  );
91
- this.subscriptions.push(subscriptionOfAdBadgeConfigurationUpdated);
92
134
 
93
135
  const subscriptionOfVideoLaunchBehaviorUpdated =
94
136
  FireworkSDKModuleEventEmitter.addListener(
95
137
  FWEventName.VideoLaunchBehaviorUpdated,
96
138
  () => {
97
139
  FWLoggerUtil.log('Receive FWEventName.VideoLaunchBehaviorUpdated');
98
- this.setState({});
140
+ forceUpdate();
99
141
  }
100
142
  );
101
- this.subscriptions.push(subscriptionOfVideoLaunchBehaviorUpdated);
102
143
 
103
- if (Platform.OS === 'android') {
104
- const viewId = findNodeHandle(this.nativeComponentRef.current);
105
- FWLoggerUtil.log(
106
- `StoryBlock componentDidMount createFragment viewId: ${viewId}`
144
+ const subscriptionOfAppLanguageUpdated =
145
+ FireworkSDKModuleEventEmitter.addListener(
146
+ FWEventName.AppLanguageUpdated,
147
+ () => {
148
+ FWLoggerUtil.log('Receive FWEventName.AppLanguageUpdated');
149
+ forceUpdate();
150
+ }
107
151
  );
108
- createFragment(viewId);
152
+
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);
109
165
  }
110
- }
111
-
112
- componentWillUnmount() {
113
- this.subscriptions.forEach((value) => {
114
- value.remove();
115
- });
116
-
117
- this.subscriptions = [];
118
- }
119
-
120
- render() {
121
- const { style } = this.props;
122
-
123
- return (
124
- <FWStoryBlock
125
- ref={this.nativeComponentRef}
126
- key={this._generateKey()}
127
- {...this.props}
128
- onStoryBlockLoadFinished={this._onStoryBlockLoadFinished}
129
- style={Object.assign({}, style, { zIndex: -1 })}
130
- />
131
- );
132
- }
133
166
 
134
- private _onStoryBlockLoadFinished = (event: NativeSyntheticEvent<any>) => {
167
+ return () => {
168
+ subscriptionOfShareBaseURLUpdated.remove();
169
+ subscriptionOfAdBadgeConfigurationUpdated.remove();
170
+ subscriptionOfVideoLaunchBehaviorUpdated.remove();
171
+ subscriptionOfAppLanguageUpdated.remove();
172
+ };
173
+ }, []);
174
+
175
+ const handleStoryBlockLoadFinished = (event: NativeSyntheticEvent<any>) => {
135
176
  FWLoggerUtil.log(
136
- `StoryBlock _onStoryBlockLoadFinished ${event.nativeEvent.name}`
177
+ `StoryBlock handleStoryBlockLoadFinished ${event.nativeEvent.name}`
137
178
  );
138
179
 
139
- const { onStoryBlockLoadFinished } = this.props;
180
+ const { onStoryBlockLoadFinished } = props;
140
181
  const { name, reason } = event.nativeEvent;
141
182
 
142
183
  if (onStoryBlockLoadFinished) {
@@ -152,60 +193,132 @@ class StoryBlock extends React.Component<IStoryBlockProps> {
152
193
  }
153
194
  };
154
195
 
155
- 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 => {
156
247
  const {
157
248
  source,
158
249
  channel = '',
159
250
  playlist = '',
160
251
  enablePictureInPicture = false,
161
- } = this.props;
252
+ adConfiguration,
253
+ } = props;
162
254
 
163
255
  const shareBaseURL = FireworkSDK.getInstance().getShareBaseURL() ?? '';
256
+ const videoLaunchBehavior =
257
+ FireworkSDK.getInstance().getVideoLaunchBehavior();
164
258
  const adBadgeConfiguration =
165
259
  FireworkSDK.getInstance().getAdBadgeConfiguration() ?? {};
166
260
  const adBadgeTextType = adBadgeConfiguration.badgeTextType ?? '';
167
261
  const backgroundColorOfAdBadge = adBadgeConfiguration.backgroundColor ?? '';
168
262
  const textColorOfAdBadge = adBadgeConfiguration.textColor ?? '';
169
263
  const dynamicContentParametersString =
170
- this._generateDynamicContentParametersString();
171
- const videoLaunchBehavior =
172
- 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();
173
269
 
174
270
  let key = `source:${source}`;
175
271
  key += `_channel:${channel}`;
176
272
  key += `_playlist:${playlist}`;
177
273
  key += `_shareBaseURL:${shareBaseURL}`;
274
+ key += `_videoLaunchBehavior:${videoLaunchBehavior}`;
178
275
  key += `_adBadgeTextType:${adBadgeTextType}`;
179
276
  key += `_backgroundColorOfAdBadge:${backgroundColorOfAdBadge}`;
180
277
  key += `_textColorOfAdBadge:${textColorOfAdBadge}`;
181
278
  key += `_dynamicContentParameters:${dynamicContentParametersString}`;
182
279
  key += `_enablePictureInPicture:${enablePictureInPicture}`;
183
- key += `_videoLaunchBehavior:${videoLaunchBehavior}`;
280
+ key += `_appLanguage:${appLanguage}`;
281
+ key += `_requiresAds:${requiresAds}`;
282
+ key += `_adsFetchTimeout:${adsFetchTimeout}`;
283
+ key += `_vastAttributes:${vastAttributesString}`;
184
284
 
185
285
  return key;
186
- }
187
-
188
- private _generateDynamicContentParametersString(): string {
189
- const { dynamicContentParameters } = this.props;
286
+ };
190
287
 
191
- if (!dynamicContentParameters) {
192
- 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
+ };
193
304
  }
194
305
 
195
- let resultString = '';
196
- const sortedKeyList = Object.keys(dynamicContentParameters).sort();
197
- for (const key of sortedKeyList) {
198
- const value = dynamicContentParameters[key];
199
- const valueString = value.join(',');
200
- if (resultString.length > 0) {
201
- resultString += '_';
202
- }
203
-
204
- resultString += `${key}:${valueString}`;
205
- }
306
+ return;
307
+ }, [isFullscreenState]);
206
308
 
207
- return resultString;
208
- }
209
- }
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
+ };
210
323
 
211
- export default StoryBlock;
324
+ export default forwardRef(StoryBlock);
@@ -83,21 +83,21 @@ const NativeComponentName = 'FWVideoFeed';
83
83
  * VideoFeed component.
84
84
  */
85
85
  class VideoFeed extends React.Component<IVideoFeedProps> {
86
+ /**
87
+ * @ignore
88
+ */
86
89
  static defaultProps = {
87
90
  mode: 'row',
88
91
  };
89
92
 
90
- nativeComponentRef = React.createRef<any>();
93
+ private _nativeComponentRef = React.createRef<any>();
91
94
 
92
- subscriptions: EmitterSubscription[] = [];
95
+ private _subscriptions: EmitterSubscription[] = [];
93
96
 
94
- /**
95
- * Force refreshing the video feed.
96
- */
97
97
  public refresh = () => {
98
98
  FWLoggerUtil.log(`VideoFeed refresh ${JSON.stringify(this.props)}`);
99
99
 
100
- const nativeNodeHandle = findNodeHandle(this.nativeComponentRef.current);
100
+ const nativeNodeHandle = findNodeHandle(this._nativeComponentRef.current);
101
101
 
102
102
  let commandId: string | number =
103
103
  UIManager.getViewManagerConfig(NativeComponentName).Commands.refresh;
@@ -133,6 +133,9 @@ class VideoFeed extends React.Component<IVideoFeedProps> {
133
133
  }
134
134
  };
135
135
 
136
+ /**
137
+ * @ignore
138
+ */
136
139
  componentDidMount() {
137
140
  FWLoggerUtil.log(
138
141
  `VideoFeed componentDidMount ${JSON.stringify(this.props)}`
@@ -146,7 +149,7 @@ class VideoFeed extends React.Component<IVideoFeedProps> {
146
149
  this.setState({});
147
150
  }
148
151
  );
149
- this.subscriptions.push(subscriptionOfShareBaseURLUpdated);
152
+ this._subscriptions.push(subscriptionOfShareBaseURLUpdated);
150
153
 
151
154
  const subscriptionOfAdBadgeConfigurationUpdated =
152
155
  FireworkSDKModuleEventEmitter.addListener(
@@ -156,7 +159,7 @@ class VideoFeed extends React.Component<IVideoFeedProps> {
156
159
  this.setState({});
157
160
  }
158
161
  );
159
- this.subscriptions.push(subscriptionOfAdBadgeConfigurationUpdated);
162
+ this._subscriptions.push(subscriptionOfAdBadgeConfigurationUpdated);
160
163
 
161
164
  const subscriptionOfVideoLaunchBehaviorUpdated =
162
165
  FireworkSDKModuleEventEmitter.addListener(
@@ -166,21 +169,36 @@ class VideoFeed extends React.Component<IVideoFeedProps> {
166
169
  this.setState({});
167
170
  }
168
171
  );
169
- this.subscriptions.push(subscriptionOfVideoLaunchBehaviorUpdated);
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);
170
182
  }
171
183
 
184
+ /**
185
+ * @ignore
186
+ */
172
187
  componentWillUnmount() {
173
188
  FWLoggerUtil.log(
174
189
  `VideoFeed componentWillUnmount ${JSON.stringify(this.props)}`
175
190
  );
176
191
 
177
- this.subscriptions.forEach((value) => {
192
+ this._subscriptions.forEach((value) => {
178
193
  value.remove();
179
194
  });
180
195
 
181
- this.subscriptions = [];
196
+ this._subscriptions = [];
182
197
  }
183
198
 
199
+ /**
200
+ * @ignore
201
+ */
184
202
  render() {
185
203
  FWLoggerUtil.log(`VideoFeed render ${JSON.stringify(this.props)}`);
186
204
 
@@ -213,6 +231,7 @@ class VideoFeed extends React.Component<IVideoFeedProps> {
213
231
  const showAdBadge = videoFeedConfiguration?.showAdBadge ?? false;
214
232
  const videoLaunchBehavior =
215
233
  FireworkSDK.getInstance().getVideoLaunchBehavior() ?? 'default';
234
+ const appLanguage = FireworkSDK.getInstance().appLanguage ?? '';
216
235
 
217
236
  let key = `source:${source}`;
218
237
  key += `_channel:${channel}`;
@@ -234,12 +253,13 @@ class VideoFeed extends React.Component<IVideoFeedProps> {
234
253
  key += `_vastAttributes:${vastAttributesString}`;
235
254
  key += `_showAdBadge:${showAdBadge}`;
236
255
  key += `_videoLaunchBehavior:${videoLaunchBehavior}`;
256
+ key += `_appLanguage:${appLanguage}`;
237
257
 
238
258
  return (
239
259
  <FWVideoFeed
240
260
  key={key}
241
261
  {...this.props}
242
- ref={this.nativeComponentRef}
262
+ ref={this._nativeComponentRef}
243
263
  onVideoFeedLoadFinished={this._onVideoFeedLoadFinished}
244
264
  mode={mode}
245
265
  style={Object.assign({}, this.props.style, { zIndex: -1 })}