@applicaster/zapp-react-native-utils 16.0.0-alpha.4416921324 → 16.0.0-alpha.4548770949

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 (173) hide show
  1. package/actionUtils/index.ts +3 -1
  2. package/actionsExecutor/ActionExecutor.ts +50 -3
  3. package/actionsExecutor/ActionExecutorContext.tsx +99 -17
  4. package/actionsExecutor/__tests__/ActionExecutor.test.ts +71 -0
  5. package/actionsExecutor/__tests__/StorageActions.test.ts +98 -0
  6. package/actionsExecutor/__tests__/feedDecorator.test.ts +91 -0
  7. package/actionsExecutor/__tests__/screenToggleFlag.test.ts +90 -0
  8. package/actionsExecutor/actions/__tests__/dismissBottomSheet.test.ts +25 -0
  9. package/actionsExecutor/actions/__tests__/goBack.test.ts +62 -0
  10. package/actionsExecutor/actions/__tests__/goHome.test.ts +19 -0
  11. package/actionsExecutor/actions/__tests__/loadItemsFromSource.test.ts +88 -0
  12. package/actionsExecutor/actions/__tests__/navigateToScreen.test.ts +267 -0
  13. package/actionsExecutor/actions/__tests__/openBottomSheet.customContent.test.ts +100 -0
  14. package/actionsExecutor/actions/__tests__/openBottomSheet.inlineItems.test.ts +372 -0
  15. package/actionsExecutor/actions/__tests__/presentScreen.test.ts +127 -0
  16. package/actionsExecutor/actions/__tests__/showAlert.test.ts +49 -0
  17. package/actionsExecutor/actions/__tests__/showToast.test.ts +88 -0
  18. package/actionsExecutor/actions/dismissBottomSheet.ts +23 -0
  19. package/actionsExecutor/actions/goBack.ts +60 -0
  20. package/actionsExecutor/actions/goHome.ts +31 -0
  21. package/actionsExecutor/actions/index.ts +12 -0
  22. package/actionsExecutor/actions/navigateToScreen.ts +43 -45
  23. package/actionsExecutor/actions/openBottomSheet.ts +340 -0
  24. package/actionsExecutor/actions/presentScreen.ts +78 -0
  25. package/actionsExecutor/actions/refreshComponent.ts +6 -0
  26. package/actionsExecutor/actions/screenToggleFlag.ts +1 -3
  27. package/actionsExecutor/actions/sendCloudEvent.ts +6 -1
  28. package/actionsExecutor/actions/showAlert.ts +51 -0
  29. package/actionsExecutor/actions/showToast.ts +64 -8
  30. package/actionsExecutor/consts.ts +27 -0
  31. package/actionsExecutor/feedDecorator.ts +12 -6
  32. package/analyticsUtils/PlayerAnalyticsManager.ts +1 -1
  33. package/appUtils/HooksManager/index.ts +9 -0
  34. package/appUtils/contextKeysManager/__tests__/getKey/failure.test.ts +1 -22
  35. package/appUtils/contextKeysManager/__tests__/getKey/success/fullSearch.test.ts +1 -25
  36. package/appUtils/contextKeysManager/__tests__/getKey/success/searchInSecure.test.ts +1 -7
  37. package/appUtils/contextKeysManager/__tests__/removeKey/failure.test.ts +0 -25
  38. package/appUtils/contextKeysManager/__tests__/removeKey/success.test.ts +0 -6
  39. package/appUtils/contextKeysManager/__tests__/setKey/failure/failedToSaveInStorage.test.ts +0 -40
  40. package/appUtils/contextKeysManager/__tests__/setKey/{success/saveItemAndReference.test.ts → success.test.ts} +2 -42
  41. package/appUtils/contextKeysManager/index.ts +28 -129
  42. package/appUtils/playerManager/OverlayObserver/OverlaysObserver.ts +142 -59
  43. package/appUtils/playerManager/OverlayObserver/__tests__/getUpcomingQueue.test.ts +67 -0
  44. package/appUtils/playerManager/OverlayObserver/__tests__/liveContent.test.ts +205 -0
  45. package/appUtils/playerManager/OverlayObserver/__tests__/utils.test.ts +56 -0
  46. package/appUtils/playerManager/OverlayObserver/getUpcomingQueue.android.tv.ts +4 -0
  47. package/appUtils/playerManager/OverlayObserver/getUpcomingQueue.ios.tv.ts +4 -0
  48. package/appUtils/playerManager/OverlayObserver/getUpcomingQueue.ts +24 -0
  49. package/appUtils/playerManager/OverlayObserver/getUpcomingQueue.web.ts +4 -0
  50. package/appUtils/playerManager/OverlayObserver/utils.ts +51 -20
  51. package/appUtils/playerManager/__tests__/playerContent.test.ts +403 -0
  52. package/appUtils/playerManager/__tests__/playerDiagnostics.test.ts +62 -0
  53. package/appUtils/playerManager/__tests__/playerFactory.test.ts +1 -1
  54. package/appUtils/playerManager/__tests__/playerNative.test.ts +114 -0
  55. package/appUtils/playerManager/__tests__/playerNativeCommand.test.ts +62 -0
  56. package/appUtils/playerManager/__tests__/playerToolbox.test.ts +281 -0
  57. package/appUtils/playerManager/__tests__/usePlayerContent.test.tsx +64 -0
  58. package/appUtils/playerManager/__tests__/usePlayerControllerSetup.test.tsx +62 -0
  59. package/appUtils/playerManager/{conts.ts → const.ts} +33 -0
  60. package/appUtils/playerManager/index.ts +8 -1
  61. package/appUtils/playerManager/player.ts +140 -3
  62. package/appUtils/playerManager/playerFactory.ts +1 -1
  63. package/appUtils/playerManager/playerNative.ts +28 -4
  64. package/appUtils/playerManager/playerNativeCommand.ts +3 -6
  65. package/appUtils/playerManager/playerToolbox.ts +196 -0
  66. package/appUtils/playerManager/usePlayerContent.tsx +43 -0
  67. package/appUtils/playerManager/usePlayerControllerSetup.tsx +7 -1
  68. package/appUtils/playerManager/userLanguagePreference.ts +1 -1
  69. package/arrayUtils/__tests__/reorderByIds.test.ts +63 -0
  70. package/arrayUtils/index.ts +62 -0
  71. package/cellUtils/__tests__/getButtonsEnabled.test.ts +28 -0
  72. package/cellUtils/__tests__/getCellNavigationEnabled.test.ts +36 -0
  73. package/cellUtils/__tests__/getIsCellNavigationEnabled.test.ts +35 -0
  74. package/cellUtils/__tests__/isCellNavigationEnabled.test.ts +21 -0
  75. package/cellUtils/index.ts +70 -3
  76. package/configurationUtils/__tests__/manifestKeyParser.test.ts +21 -0
  77. package/configurationUtils/__tests__/modalBlocksParser.test.ts +403 -0
  78. package/configurationUtils/manifestKeyParser.ts +50 -10
  79. package/configurationUtils/modalBlocksParser.ts +252 -0
  80. package/entryActions/__tests__/buildEntryActions.test.ts +235 -0
  81. package/entryActions/aliasPresentation.ts +108 -0
  82. package/entryActions/buildEntryActions.ts +167 -0
  83. package/entryActions/index.ts +15 -0
  84. package/entryActions/types.ts +39 -0
  85. package/focusManager/FocusManager.ts +26 -0
  86. package/focusManager/__tests__/FocusManager.test.ts +9 -0
  87. package/localizationUtils/index.ts +12 -6
  88. package/logger/__tests__/xrayToolbox.test.ts +69 -0
  89. package/logger/xrayToolbox.ts +67 -0
  90. package/manifestUtils/__tests__/player.audioControls.test.js +158 -0
  91. package/manifestUtils/_internals/index.js +8 -3
  92. package/manifestUtils/defaultManifestConfigurations/generalContent.js +6 -0
  93. package/manifestUtils/defaultManifestConfigurations/player.js +338 -1
  94. package/manifestUtils/index.js +3 -0
  95. package/manifestUtils/keys.js +7 -2
  96. package/manifestUtils/mobileAction/container/__tests__/mobileActionButtonsContainer.test.js +21 -0
  97. package/manifestUtils/mobileAction/groups/__tests__/buildMobileActionButtonGroups.test.js +1 -1
  98. package/manifestUtils/mobileAction/groups/defaults.js +2 -1
  99. package/manifestUtils/modalBlocks.js +304 -0
  100. package/modalState/ContentViewModel.ts +113 -0
  101. package/modalState/EditableCollection.ts +17 -0
  102. package/modalState/EditableCollectionRegistry.ts +51 -0
  103. package/modalState/ModalOrchestrator.ts +213 -0
  104. package/modalState/RemoteEditableCollection.ts +235 -0
  105. package/modalState/__tests__/ContentViewModel.editable.test.ts +69 -0
  106. package/modalState/__tests__/ContentViewModel.test.ts +165 -0
  107. package/modalState/__tests__/EditableCollectionRegistry.test.ts +112 -0
  108. package/modalState/__tests__/ModalOrchestrator.phase3.test.ts +86 -0
  109. package/modalState/__tests__/RemoteEditableCollection.test.ts +415 -0
  110. package/modalState/__tests__/index.test.ts +30 -1
  111. package/modalState/__tests__/useBottomSheetContent.test.ts +349 -0
  112. package/modalState/__tests__/useModalStoreState.test.ts +142 -0
  113. package/modalState/index.ts +37 -83
  114. package/modalState/store.ts +109 -0
  115. package/modalState/types.ts +173 -0
  116. package/modalState/useBottomSheetContent.ts +109 -0
  117. package/navigationUtils/xrayNavigator.ts +12 -0
  118. package/package.json +3 -2
  119. package/playerUtils/__tests__/contentDataKeys.test.ts +297 -0
  120. package/playerUtils/__tests__/resolvePlayerActions.test.ts +138 -0
  121. package/playerUtils/__tests__/resolvePlayerTitleSubtitle.test.ts +180 -0
  122. package/playerUtils/contentDataKeys.ts +134 -0
  123. package/playerUtils/index.ts +37 -16
  124. package/playerUtils/isAudioItem.ts +26 -0
  125. package/playerUtils/resolvePlayerActions.ts +71 -0
  126. package/playerUtils/resolvePlayerTitleSubtitle.ts +76 -0
  127. package/pluginUtils/__tests__/pluginUtils.test.js +32 -0
  128. package/pluginUtils/index.ts +29 -0
  129. package/quickBrickAssets/__tests__/index.test.ts +18 -0
  130. package/quickBrickAssets/index.ts +35 -0
  131. package/reactHooks/__tests__/getPluginConfiguration.test.ts +82 -0
  132. package/reactHooks/actions/index.ts +74 -3
  133. package/reactHooks/cell-click/__tests__/index.test.js +64 -0
  134. package/reactHooks/cell-click/index.ts +21 -14
  135. package/reactHooks/connection/index.tsx +8 -5
  136. package/reactHooks/feed/__tests__/feedInvalidation.test.tsx +338 -0
  137. package/reactHooks/feed/__tests__/useBatchLoading.test.tsx +8 -2
  138. package/reactHooks/feed/__tests__/useMarkPipesDataStale.test.tsx +243 -0
  139. package/reactHooks/feed/useFeedLoader.tsx +28 -1
  140. package/reactHooks/feed/useLoadPipesDataDispatch.ts +12 -11
  141. package/reactHooks/feed/useMarkPipesDataStale.ts +68 -23
  142. package/reactHooks/index.ts +2 -0
  143. package/reactHooks/layout/index.ts +1 -1
  144. package/reactHooks/navigation/__tests__/index.test.tsx +176 -1
  145. package/reactHooks/navigation/__tests__/usePresentScreen.test.ts +154 -0
  146. package/reactHooks/navigation/index.ts +2 -0
  147. package/reactHooks/navigation/usePresentScreen.ts +157 -0
  148. package/reactHooks/navigation/useRoute.ts +22 -5
  149. package/reactHooks/usePluginConfiguration.ts +88 -47
  150. package/reactHooks/utils/__tests__/index.test.js +22 -2
  151. package/reactHooks/utils/index.ts +11 -1
  152. package/riverComponetsMeasurementProvider/index.tsx +3 -1
  153. package/uiActionsRegistrator/__tests__/resolveActionIdentifiers.test.ts +93 -0
  154. package/uiActionsRegistrator/__tests__/subscribe.test.ts +133 -0
  155. package/uiActionsRegistrator/__tests__/usableActionItems.test.ts +52 -0
  156. package/uiActionsRegistrator/index.ts +25 -203
  157. package/uiActionsRegistrator/registry.ts +351 -0
  158. package/uiActionsRegistrator/resolveActionIdentifiers.ts +100 -0
  159. package/uiActionsRegistrator/usableActionItems.ts +66 -0
  160. package/zappFrameworkUtils/__tests__/localStorageHelper.test.ts +146 -0
  161. package/zappFrameworkUtils/localStorageHelper.ts +19 -15
  162. package/appUtils/contextKeysManager/__tests__/getKey/success/byReference.test.ts +0 -56
  163. package/appUtils/contextKeysManager/__tests__/getKeyByReference/failure.test.ts +0 -265
  164. package/appUtils/contextKeysManager/__tests__/getKeyByReference/success.test.ts +0 -303
  165. package/appUtils/contextKeysManager/__tests__/removeReferenceForKey/failure.test.ts +0 -82
  166. package/appUtils/contextKeysManager/__tests__/removeReferenceForKey/success.test.ts +0 -41
  167. package/appUtils/contextKeysManager/__tests__/setKey/success/saveItemButNotReference.test.ts +0 -208
  168. package/appUtils/contextKeysManager/__tests__/setReferenceForKey/failure.test.ts +0 -93
  169. package/appUtils/contextKeysManager/__tests__/setReferenceForKey/success.test.ts +0 -45
  170. package/appUtils/playerManager/usePlayerTitleSummaryOverlay.tsx +0 -56
  171. package/playerUtils/__tests__/getPlayerActionButtons.test.ts +0 -54
  172. package/playerUtils/getPlayerActionButtons.ts +0 -17
  173. /package/reactHooks/actions/__tests__/{index.test.js → index.test.tsx} +0 -0
@@ -1,5 +1,5 @@
1
- import { ContextKeysManager } from "../../..";
2
- import { StorageLevel } from "../../../consts";
1
+ import { ContextKeysManager } from "../..";
2
+ import { StorageLevel } from "../../consts";
3
3
 
4
4
  describe("Context Keys Manager - setKey", () => {
5
5
  it("returns true if saved with success with storage-level=undefined", async () => {
@@ -37,10 +37,6 @@ describe("Context Keys Manager - setKey", () => {
37
37
  secureStorage: mockedSecureStorage,
38
38
  });
39
39
 
40
- const setReferenceForKey = jest
41
- .spyOn(contextManager, "setReferenceForKey")
42
- .mockResolvedValueOnce(true);
43
-
44
40
  // run
45
41
  const result = await contextManager.setKey({ key, value, storageLevel });
46
42
 
@@ -62,12 +58,6 @@ describe("Context Keys Manager - setKey", () => {
62
58
 
63
59
  expect(mockedSecureStorage.getItem).not.toHaveBeenCalled();
64
60
  expect(mockedSecureStorage.setItem).not.toHaveBeenCalled();
65
-
66
- expect(setReferenceForKey).toHaveBeenCalledWith(
67
- "key",
68
- "namespace",
69
- StorageLevel.default
70
- );
71
61
  });
72
62
 
73
63
  it("returns true if saved with success with storage-level=default", async () => {
@@ -105,10 +95,6 @@ describe("Context Keys Manager - setKey", () => {
105
95
  secureStorage: mockedSecureStorage,
106
96
  });
107
97
 
108
- const setReferenceForKey = jest
109
- .spyOn(contextManager, "setReferenceForKey")
110
- .mockResolvedValueOnce(true);
111
-
112
98
  // run
113
99
  const result = await contextManager.setKey({ key, value, storageLevel });
114
100
 
@@ -130,12 +116,6 @@ describe("Context Keys Manager - setKey", () => {
130
116
 
131
117
  expect(mockedSecureStorage.getItem).not.toHaveBeenCalled();
132
118
  expect(mockedSecureStorage.setItem).not.toHaveBeenCalled();
133
-
134
- expect(setReferenceForKey).toHaveBeenCalledWith(
135
- "key",
136
- "namespace",
137
- StorageLevel.default
138
- );
139
119
  });
140
120
 
141
121
  it("returns true if saved with success with storage-level=persisted", async () => {
@@ -173,10 +153,6 @@ describe("Context Keys Manager - setKey", () => {
173
153
  secureStorage: mockedSecureStorage,
174
154
  });
175
155
 
176
- const setReferenceForKey = jest
177
- .spyOn(contextManager, "setReferenceForKey")
178
- .mockResolvedValueOnce(true);
179
-
180
156
  // run
181
157
  const result = await contextManager.setKey({ key, value, storageLevel });
182
158
 
@@ -198,12 +174,6 @@ describe("Context Keys Manager - setKey", () => {
198
174
 
199
175
  expect(mockedSecureStorage.getItem).not.toHaveBeenCalled();
200
176
  expect(mockedSecureStorage.setItem).not.toHaveBeenCalled();
201
-
202
- expect(setReferenceForKey).toHaveBeenCalledWith(
203
- "key",
204
- "namespace",
205
- storageLevel
206
- );
207
177
  });
208
178
 
209
179
  it("returns true if saved with success with storage-level=secure", async () => {
@@ -241,10 +211,6 @@ describe("Context Keys Manager - setKey", () => {
241
211
  secureStorage: mockedSecureStorage,
242
212
  });
243
213
 
244
- const setReferenceForKey = jest
245
- .spyOn(contextManager, "setReferenceForKey")
246
- .mockResolvedValueOnce(true);
247
-
248
214
  // run
249
215
  const result = await contextManager.setKey({ key, value, storageLevel });
250
216
 
@@ -266,11 +232,5 @@ describe("Context Keys Manager - setKey", () => {
266
232
  value,
267
233
  "namespace"
268
234
  );
269
-
270
- expect(setReferenceForKey).toHaveBeenCalledWith(
271
- "key",
272
- "namespace",
273
- storageLevel
274
- );
275
235
  });
276
236
  });
@@ -1,6 +1,6 @@
1
1
  import { isNil } from "@applicaster/zapp-react-native-utils/utils";
2
2
 
3
- import { StorageLevel, StorageType } from "./consts";
3
+ import { StorageLevel } from "./consts";
4
4
  import {
5
5
  getNamespaceAndKey,
6
6
  keyIsValid,
@@ -59,18 +59,14 @@ type Setup = {
59
59
  sessionStorage?: Storage;
60
60
  localStorage?: Storage;
61
61
  secureStorage?: Storage;
62
- referenceStorage?: Storage;
63
62
  };
64
63
 
65
- export const REFERENCE_NAMESPACE = "applicaster.v2.references";
66
-
67
64
  export class ContextKeysManager {
68
65
  private static _instance: ContextKeysManager;
69
66
  private _logger: Logger;
70
67
  private _sessionStorage: Storage;
71
68
  private _localStorage: Storage;
72
69
  private _secureStorage: Storage;
73
- private _referenceStorage: Storage;
74
70
 
75
71
  public static get instance() {
76
72
  if (!this._instance) {
@@ -79,7 +75,6 @@ export class ContextKeysManager {
79
75
  sessionStorage: SessionStorage,
80
76
  localStorage: LocalStorage,
81
77
  secureStorage: SecureStorage,
82
- referenceStorage: SessionStorage,
83
78
  });
84
79
 
85
80
  this._instance = instance;
@@ -94,13 +89,11 @@ export class ContextKeysManager {
94
89
  sessionStorage = SessionStorage,
95
90
  localStorage = LocalStorage,
96
91
  secureStorage = SecureStorage,
97
- referenceStorage = SessionStorage,
98
- }: Setup) {
92
+ }: Setup = {}) {
99
93
  this._logger = logger;
100
94
  this._sessionStorage = sessionStorage;
101
95
  this._localStorage = localStorage;
102
96
  this._secureStorage = secureStorage;
103
- this._referenceStorage = referenceStorage;
104
97
  }
105
98
 
106
99
  private parseKey(key: KeyName): ParsedKeyAndNamespace {
@@ -119,41 +112,6 @@ export class ContextKeysManager {
119
112
  return { isValid: true, ...parsedKey };
120
113
  }
121
114
 
122
- // GET
123
- public async getKeyByReference(
124
- key: string,
125
- namespace: string
126
- ): Promise<ValueOrNothing> {
127
- // 1) look for storage
128
- // 2) if storage found - look value in it
129
- // 3) if storage not found return NOTHING
130
-
131
- return this._referenceStorage
132
- .getItem(buildNamespaceKey(key, namespace), REFERENCE_NAMESPACE)
133
- .then((storage: StorageType | unknown) => {
134
- switch (storage) {
135
- case StorageType.session:
136
- return this._sessionStorage.getItem(key, namespace);
137
- case StorageType.local:
138
- return this._localStorage.getItem(key, namespace);
139
- case StorageType.secure:
140
- return this._secureStorage.getItem(key, namespace);
141
- default:
142
- // unknown storage
143
- return NOTHING;
144
- }
145
- })
146
- .catch((error) => {
147
- // something went wrong
148
- this._logger.warn({
149
- message: error.message,
150
- data: { key, namespace },
151
- });
152
-
153
- return NOTHING;
154
- });
155
- }
156
-
157
115
  public async getKeys(
158
116
  keys: Array<KeyName>,
159
117
  contextObj?: { key: string; required?: boolean }[]
@@ -228,19 +186,7 @@ export class ContextKeysManager {
228
186
  return NOTHING;
229
187
  }
230
188
 
231
- // at first - try to find by reference
232
- // if not found, look in session > local > secure
233
-
234
- const resultByReference = await this.getKeyByReference(
235
- parsedKey,
236
- parsedNamespace
237
- );
238
-
239
- if (!isNil(resultByReference)) {
240
- return resultByReference;
241
- }
242
-
243
- // try to full-search
189
+ // look in session > local > secure
244
190
  try {
245
191
  const resultFromSessionStorage = await this._sessionStorage.getItem(
246
192
  parsedKey,
@@ -284,41 +230,6 @@ export class ContextKeysManager {
284
230
  // GET
285
231
 
286
232
  // SET
287
- public async setReferenceForKey(
288
- key: string,
289
- namespace: string,
290
- storageLevel: StorageLevel
291
- ): Promise<boolean> {
292
- return this._referenceStorage
293
- .setItem(
294
- buildNamespaceKey(key, namespace),
295
- storageLevel,
296
- REFERENCE_NAMESPACE
297
- )
298
- .then((result) => {
299
- if (savingResultIsSuccess(result)) {
300
- return true;
301
- }
302
-
303
- this._logger.warn({
304
- message:
305
- "While saving reference - reference storage return non expected value: true",
306
- data: { key, namespace, storageLevel },
307
- });
308
-
309
- return false;
310
- })
311
- .catch((error) => {
312
- // something went wrong
313
- this._logger.warn({
314
- message: error.message,
315
- data: { key, namespace, storageLevel },
316
- });
317
-
318
- return false;
319
- });
320
- }
321
-
322
233
  // for all keys where saving were succeed return true
323
234
  // for others return false
324
235
  public async setKeys(
@@ -352,29 +263,36 @@ export class ContextKeysManager {
352
263
  }
353
264
 
354
265
  try {
266
+ let setItemResult: boolean;
267
+
355
268
  switch (storageLevel) {
356
269
  case StorageLevel.persisted:
357
- return await Promise.all([
358
- this._localStorage.setItem(parsedKey, value, parsedNamespace),
359
- this.setReferenceForKey(parsedKey, parsedNamespace, storageLevel),
360
- ]).then(([setItemResult]) => {
361
- return savingResultIsSuccess(setItemResult);
362
- });
270
+ setItemResult = await this._localStorage.setItem(
271
+ parsedKey,
272
+ value,
273
+ parsedNamespace
274
+ );
275
+
276
+ break;
363
277
  case StorageLevel.secure:
364
- return await Promise.all([
365
- this._secureStorage.setItem(parsedKey, value, parsedNamespace),
366
- this.setReferenceForKey(parsedKey, parsedNamespace, storageLevel),
367
- ]).then(([setItemResult]) => {
368
- return savingResultIsSuccess(setItemResult);
369
- });
278
+ setItemResult = await this._secureStorage.setItem(
279
+ parsedKey,
280
+ value,
281
+ parsedNamespace
282
+ );
283
+
284
+ break;
370
285
  default:
371
- return await Promise.all([
372
- this._sessionStorage.setItem(parsedKey, value, parsedNamespace),
373
- this.setReferenceForKey(parsedKey, parsedNamespace, storageLevel),
374
- ]).then(([setItemResult]) => {
375
- return savingResultIsSuccess(setItemResult);
376
- });
286
+ setItemResult = await this._sessionStorage.setItem(
287
+ parsedKey,
288
+ value,
289
+ parsedNamespace
290
+ );
291
+
292
+ break;
377
293
  }
294
+
295
+ return savingResultIsSuccess(setItemResult);
378
296
  } catch (error) {
379
297
  // something went wrong
380
298
  this._logger.warn({
@@ -388,24 +306,6 @@ export class ContextKeysManager {
388
306
  // SET
389
307
 
390
308
  // REMOVE
391
- public async removeReferenceForKey(
392
- key: string,
393
- namespace: string
394
- ): Promise<boolean> {
395
- return this._referenceStorage
396
- .removeItem(buildNamespaceKey(key, namespace), REFERENCE_NAMESPACE)
397
- .then(() => true)
398
- .catch((error) => {
399
- // something went wrong
400
- this._logger.warn({
401
- message: error.message,
402
- data: { key, namespace },
403
- });
404
-
405
- return false;
406
- });
407
- }
408
-
409
309
  public async removeKey(key: KeyName): Promise<boolean> {
410
310
  const {
411
311
  isValid: isValidKey,
@@ -422,7 +322,6 @@ export class ContextKeysManager {
422
322
  this._sessionStorage.removeItem(parsedKey, parsedNamespace),
423
323
  this._localStorage.removeItem(parsedKey, parsedNamespace),
424
324
  this._secureStorage.removeItem(parsedKey, parsedNamespace),
425
- this.removeReferenceForKey(parsedKey, parsedNamespace),
426
325
  ])
427
326
  .then(() => true)
428
327
  .catch((error) => {
@@ -2,9 +2,8 @@ import { BehaviorSubject, Observable } from "rxjs";
2
2
  import { distinctUntilChanged } from "rxjs/operators";
3
3
  import { Player } from "../player";
4
4
  import { createLogger, utilsLogger } from "../../../logger";
5
- import { appStore } from "@applicaster/zapp-react-native-redux/AppStore";
5
+ import { getPluginByIdentifier } from "@applicaster/zapp-react-native-utils/pluginUtils";
6
6
  import {
7
- findPluginByIdentifier,
8
7
  loadFeedEntry,
9
8
  loadFeedAndPrefetchThumbnailImage,
10
9
  parseTimeToSeconds,
@@ -19,6 +18,12 @@ export const { log_verbose, log_debug, log_info, log_error } = createLogger({
19
18
  parent: utilsLogger,
20
19
  });
21
20
 
21
+ /**
22
+ * Mirrors `initial_value` for `audio_live_player_update_interval` in
23
+ * `defaultManifestConfigurations/player.js`.
24
+ */
25
+ const DEFAULT_LIVE_CONTENT_UPDATE_INTERVAL_SECONDS = 60;
26
+
22
27
  type ChapterMarkerOriginal = {
23
28
  id: string;
24
29
  title: string;
@@ -40,11 +45,6 @@ export type LiveMetadataConfig = {
40
45
  updateInterval: number;
41
46
  };
42
47
 
43
- export type TitleSummaryEvent = {
44
- title: string | number;
45
- summary: string | number;
46
- };
47
-
48
48
  type ChapterMarkersObserverProps = {
49
49
  player: Player;
50
50
  };
@@ -59,24 +59,23 @@ type PlayNextConfig = {
59
59
  export type PlayNextState = PlayNextConfig & {
60
60
  triggerTime: number;
61
61
  handleUserCancelPlayNext: () => void;
62
+ hidePlayNext: () => void;
62
63
  };
63
64
 
64
65
  export class OverlaysObserver {
65
66
  readonly chapterSubject: BehaviorSubject<ChapterMarkerEvent>;
66
67
  private playNextSubject: BehaviorSubject<PlayNextState>;
67
68
  private liveMetadataSubject: BehaviorSubject<LiveMetadataEvent>;
68
- private titleSummarySubject: BehaviorSubject<TitleSummaryEvent>;
69
- private feedUrl: string;
70
- private reloadData: () => void;
71
- private updateTitleAndDescription: (data: any) => void;
72
- private feedDataInterval: any;
69
+ private liveContentInterval: any;
70
+ private liveContentGeneration = 0;
71
+ private _liveEntry: ZappEntry | null = null;
73
72
  private liveMetadataUpdateInterval: any;
74
73
  private releasePlayerObserver?: () => void;
75
74
  readonly entry: ZappEntry;
76
75
  private chapterMarkerEvents: ChapterMarkerEvent[];
77
76
  readonly player: Player;
78
77
  private playNextConfig?: PlayNextConfig;
79
- private isCanceledByUser = false;
78
+ private isPlayNextSuppressed = false;
80
79
 
81
80
  constructor({ player }: ChapterMarkersObserverProps) {
82
81
  this.chapterSubject = new BehaviorSubject(null);
@@ -85,41 +84,140 @@ export class OverlaysObserver {
85
84
  this.player = player;
86
85
  this.entry = player.getEntry();
87
86
 
88
- this.titleSummarySubject = new BehaviorSubject<TitleSummaryEvent>({
89
- title: this.entry?.title || "",
90
- summary: this.entry?.summary || "",
91
- });
92
-
93
87
  this.liveMetadataSubject = new BehaviorSubject<LiveMetadataEvent>(
94
88
  this.entry?.extensions?.liveMetadata || null
95
89
  );
96
90
 
97
91
  this.chapterMarkerEvents = this.prepareChapterMarkers();
98
92
  this.releasePlayerObserver = this.subscribeToPlayerEvents();
99
- this.feedUrl = "";
100
- this.reloadData = () => {};
101
- this.updateTitleAndDescription = () => {};
102
- this.feedDataInterval = null;
93
+ this.liveContentInterval = null;
103
94
  this.liveMetadataUpdateInterval = null;
104
95
  void this.preparePlayNext();
105
96
  void this.prepareLiveMetadata();
106
97
  }
107
98
 
108
- private setupFeedDataInterval(interval: number) {
109
- if (this.feedUrl && this.reloadData && this.updateTitleAndDescription) {
110
- this.feedDataInterval = setInterval(() => {
111
- this.reloadData();
112
- }, interval * 1000);
99
+ /**
100
+ * Polls the live feed and pushes each loaded entry into the player, which
101
+ * re-resolves its Title/Subtitle and publishes on its content channel.
102
+ *
103
+ * Runs entirely outside React: `loadFeedEntry` builds its request through
104
+ * `RequestBuilder.setUrl`, which inflates URL macros with the entry and
105
+ * screen contexts, so no `useInflatedUrl` is needed. `prepareLiveMetadata`
106
+ * below already polls this way.
107
+ */
108
+ public startLiveContentUpdates() {
109
+ this.stopLiveContentUpdates();
110
+
111
+ if (!this.player?.isLive?.()) {
112
+ log_debug("startLiveContentUpdates: Player is not live. Skipping...");
113
+
114
+ return;
113
115
  }
116
+
117
+ const feedUrl = this.getLiveContentFeedUrl();
118
+
119
+ if (!feedUrl) {
120
+ log_debug(
121
+ "startLiveContentUpdates: Entry carries no live channel feed. Skipping..."
122
+ );
123
+
124
+ return;
125
+ }
126
+
127
+ const intervalSeconds = this.getLiveContentUpdateInterval();
128
+
129
+ // Clearing the interval cannot cancel a request already in flight, so each
130
+ // run carries the generation it started in. A response that arrives after
131
+ // a stop or a restart belongs to a channel that is no longer playing and
132
+ // is dropped, rather than publishing the previous programme's title over
133
+ // the current one.
134
+ const generation = ++this.liveContentGeneration;
135
+
136
+ const load = async () => {
137
+ try {
138
+ const entry = await loadFeedEntry(feedUrl, this.entry);
139
+
140
+ if (generation !== this.liveContentGeneration) {
141
+ return;
142
+ }
143
+
144
+ this._liveEntry = entry;
145
+ this.player.publishContent();
146
+ } catch {
147
+ // `loadFeedEntry` already logs the failure. Keep whatever content is
148
+ // currently published rather than blanking the title on a transient
149
+ // network error.
150
+ }
151
+ };
152
+
153
+ void load();
154
+
155
+ this.liveContentInterval = setInterval(load, intervalSeconds * 1000);
114
156
  }
115
157
 
116
- public clearFeedDataInterval() {
117
- if (this.feedDataInterval) {
118
- clearInterval(this.feedDataInterval);
119
- this.feedDataInterval = null;
158
+ public stopLiveContentUpdates() {
159
+ // Invalidates any in-flight request as well as the interval.
160
+ this.liveContentGeneration += 1;
161
+
162
+ if (this.liveContentInterval) {
163
+ clearInterval(this.liveContentInterval);
164
+ this.liveContentInterval = null;
120
165
  }
121
166
  }
122
167
 
168
+ /**
169
+ * The programme feed for the channel currently playing. Derived here rather
170
+ * than by the caller so no UI component needs to know where a live channel
171
+ * keeps its feed, or that the URL has to be rewritten to reach the
172
+ * programme ("livePage") view of it.
173
+ */
174
+ private getLiveContentFeedUrl(): string {
175
+ const channelSrc =
176
+ this.player.getEntry()?.extensions?.live?.channel_src ?? "";
177
+
178
+ return channelSrc.replace(new RegExp("subClass", "g"), "livePage");
179
+ }
180
+
181
+ /**
182
+ * Falls back to the manifest's own `initial_value` when the app ships no
183
+ * value: `intervalSeconds * 1000` would otherwise be `NaN`, and
184
+ * `setInterval` treats `NaN` as `0` - a tight loop hammering the feed.
185
+ */
186
+ private getLiveContentUpdateInterval(): number {
187
+ const configured =
188
+ this.player.getPluginConfiguration()?.audio_live_player_update_interval;
189
+
190
+ const seconds = Number(configured);
191
+
192
+ return Number.isFinite(seconds) && seconds > 0
193
+ ? seconds
194
+ : DEFAULT_LIVE_CONTENT_UPDATE_INTERVAL_SECONDS;
195
+ }
196
+
197
+ /**
198
+ * The entry most recently loaded from the live channel feed - the programme
199
+ * currently on air, as opposed to `player.entry`, which is the channel and
200
+ * stays put for the whole listening session.
201
+ *
202
+ * Read by `Player.getContent()`, which prefers it over the player's own
203
+ * entry. It lives here rather than on `Player` because it is live-feed
204
+ * state, like `liveMetadata` and play-next: a VOD or cast player never has
205
+ * an observer at all, so it never carries this either.
206
+ */
207
+ public get liveEntry(): ZappEntry | null {
208
+ return this._liveEntry;
209
+ }
210
+
211
+ /**
212
+ * Called by `Player`'s `entry` setter. The live entry is an overlay on the
213
+ * current entry, so its lifetime is scoped to that entry: without this, a
214
+ * programme from a previous channel would shadow the new one forever - the
215
+ * content would change but the title would not.
216
+ */
217
+ public clearLiveEntry() {
218
+ this._liveEntry = null;
219
+ }
220
+
123
221
  public clearLiveMetadataUpdateInterval() {
124
222
  if (this.liveMetadataUpdateInterval) {
125
223
  clearInterval(this.liveMetadataUpdateInterval);
@@ -127,34 +225,19 @@ export class OverlaysObserver {
127
225
  }
128
226
  }
129
227
 
130
- public setFeedDataHandlers(
131
- feedUrl: string,
132
- reloadData: () => void,
133
- interval: number,
134
- updateTitleAndDescription: (data: any) => void
135
- ) {
136
- this.feedUrl = feedUrl;
137
- this.reloadData = reloadData;
138
- this.updateTitleAndDescription = updateTitleAndDescription;
139
- this.setupFeedDataInterval(interval);
140
- }
141
-
142
- public getTitleSummaryObservable(): Observable<TitleSummaryEvent> {
143
- return this.titleSummarySubject.asObservable().pipe(distinctUntilChanged());
144
- }
228
+ hidePlayNext = () => {
229
+ this.isPlayNextSuppressed = true;
230
+ this.playNextSubject.next(null);
231
+ };
145
232
 
146
233
  handleUserCancelPlayNext = () => {
147
- this.isCanceledByUser = true;
148
- this.playNextSubject.next(null);
234
+ this.hidePlayNext();
149
235
  };
150
236
 
151
237
  preparePlayNext = async () => {
152
238
  try {
153
- const plugins = appStore.get("plugins");
154
-
155
- const playNextPlugin: any = findPluginByIdentifier(
156
- "QuickBrickPlayNextOverlay",
157
- plugins
239
+ const playNextPlugin: any = getPluginByIdentifier(
240
+ "QuickBrickPlayNextOverlay"
158
241
  );
159
242
 
160
243
  if (!playNextPlugin) {
@@ -165,7 +248,7 @@ export class OverlaysObserver {
165
248
  return;
166
249
  }
167
250
 
168
- const playNextFeedUrl: string = retrieveFeedUrl(this.entry);
251
+ const playNextFeedUrl: string = await retrieveFeedUrl(this.entry);
169
252
 
170
253
  if (!playNextFeedUrl || typeof playNextFeedUrl !== "string") {
171
254
  log_debug(
@@ -270,7 +353,7 @@ export class OverlaysObserver {
270
353
 
271
354
  // TODO: Hack for video end, will be replaced with playlist prev/next in the future
272
355
  getPlayNextEntry = () =>
273
- !this.isCanceledByUser ? this.playNextConfig?.entry : null;
356
+ !this.isPlayNextSuppressed ? this.playNextConfig?.entry : null;
274
357
 
275
358
  prepareChapterMarkers = () => {
276
359
  const chapterMarkers: ChapterMarkerOriginal[] =
@@ -353,7 +436,7 @@ export class OverlaysObserver {
353
436
  const shouldPlayNextBeVisible = currentTime > triggerTime;
354
437
 
355
438
  if (shouldPlayNextBeVisible) {
356
- if (this.isCanceledByUser) {
439
+ if (this.isPlayNextSuppressed) {
357
440
  return;
358
441
  }
359
442
 
@@ -361,10 +444,11 @@ export class OverlaysObserver {
361
444
  ...this.playNextConfig,
362
445
  triggerTime,
363
446
  handleUserCancelPlayNext: this.handleUserCancelPlayNext,
447
+ hidePlayNext: this.hidePlayNext,
364
448
  });
365
449
  } else {
366
450
  this.playNextSubject.next(null);
367
- this.isCanceledByUser = false;
451
+ this.isPlayNextSuppressed = false;
368
452
  }
369
453
  };
370
454
 
@@ -389,9 +473,8 @@ export class OverlaysObserver {
389
473
  this.chapterSubject.complete();
390
474
  this.playNextSubject.complete();
391
475
  this.liveMetadataSubject.complete();
392
- this.titleSummarySubject.complete();
393
476
  this.releasePlayerObserver?.();
394
- this.clearFeedDataInterval();
477
+ this.stopLiveContentUpdates();
395
478
  this.clearLiveMetadataUpdateInterval();
396
479
  this.releasePlayerObserver = null;
397
480
  };