@volley/vwr-loader 1.6.0 → 1.7.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 (133) hide show
  1. package/dist/cli.js +51 -25
  2. package/dist/cli.js.map +1 -1
  3. package/dist/vwr/src/envDefaults.d.ts.map +1 -1
  4. package/dist/vwr/src/envDefaults.js +6 -39
  5. package/dist/vwr/src/envDefaults.js.map +1 -1
  6. package/dist/vwr/src/iframe.d.ts +25 -0
  7. package/dist/vwr/src/iframe.d.ts.map +1 -0
  8. package/dist/vwr/src/iframe.js +82 -0
  9. package/dist/vwr/src/iframe.js.map +1 -0
  10. package/dist/vwr/src/native/app-lifecycle/factory.d.ts +7 -0
  11. package/dist/vwr/src/native/app-lifecycle/factory.d.ts.map +1 -0
  12. package/dist/vwr/src/native/app-lifecycle/factory.js +68 -0
  13. package/dist/vwr/src/native/app-lifecycle/factory.js.map +1 -0
  14. package/dist/vwr/src/native/app-lifecycle/rpc.d.ts +15 -0
  15. package/dist/vwr/src/native/app-lifecycle/rpc.d.ts.map +1 -0
  16. package/dist/vwr/src/native/app-lifecycle/rpc.js +46 -0
  17. package/dist/vwr/src/native/app-lifecycle/rpc.js.map +1 -0
  18. package/dist/vwr/src/native/capacitor-bridge/rpc.d.ts +33 -0
  19. package/dist/vwr/src/native/capacitor-bridge/rpc.d.ts.map +1 -0
  20. package/dist/vwr/src/native/capacitor-bridge/rpc.js +129 -0
  21. package/dist/vwr/src/native/capacitor-bridge/rpc.js.map +1 -0
  22. package/dist/vwr/src/native/device-info/factory.d.ts +9 -0
  23. package/dist/vwr/src/native/device-info/factory.d.ts.map +1 -0
  24. package/dist/vwr/src/native/device-info/factory.js +28 -0
  25. package/dist/vwr/src/native/device-info/factory.js.map +1 -0
  26. package/dist/vwr/src/native/device-info/firetvCollector.d.ts +9 -0
  27. package/dist/vwr/src/native/device-info/firetvCollector.d.ts.map +1 -0
  28. package/dist/vwr/src/native/device-info/firetvCollector.js +180 -0
  29. package/dist/vwr/src/native/device-info/firetvCollector.js.map +1 -0
  30. package/dist/vwr/src/native/device-info/lgCollector.d.ts +8 -0
  31. package/dist/vwr/src/native/device-info/lgCollector.d.ts.map +1 -0
  32. package/dist/vwr/src/native/device-info/lgCollector.js +136 -0
  33. package/dist/vwr/src/native/device-info/lgCollector.js.map +1 -0
  34. package/dist/vwr/src/native/device-info/mobileCollector.d.ts +9 -0
  35. package/dist/vwr/src/native/device-info/mobileCollector.d.ts.map +1 -0
  36. package/dist/vwr/src/native/device-info/mobileCollector.js +47 -0
  37. package/dist/vwr/src/native/device-info/mobileCollector.js.map +1 -0
  38. package/dist/vwr/src/native/device-info/rpc.d.ts +51 -0
  39. package/dist/vwr/src/native/device-info/rpc.d.ts.map +1 -0
  40. package/dist/vwr/src/native/device-info/rpc.js +46 -0
  41. package/dist/vwr/src/native/device-info/rpc.js.map +1 -0
  42. package/dist/vwr/src/native/device-info/samsungCollector.d.ts +9 -0
  43. package/dist/vwr/src/native/device-info/samsungCollector.d.ts.map +1 -0
  44. package/dist/vwr/src/native/device-info/samsungCollector.js +86 -0
  45. package/dist/vwr/src/native/device-info/samsungCollector.js.map +1 -0
  46. package/dist/vwr/src/native/device-info/webCollector.d.ts +4 -0
  47. package/dist/vwr/src/native/device-info/webCollector.d.ts.map +1 -0
  48. package/dist/vwr/src/native/device-info/webCollector.js +56 -0
  49. package/dist/vwr/src/native/device-info/webCollector.js.map +1 -0
  50. package/dist/vwr/src/native/native-bridge/rpc.d.ts +33 -0
  51. package/dist/vwr/src/native/native-bridge/rpc.d.ts.map +1 -0
  52. package/dist/vwr/src/native/native-bridge/rpc.js +114 -0
  53. package/dist/vwr/src/native/native-bridge/rpc.js.map +1 -0
  54. package/dist/vwr/src/native/screensaver-prevention/LGLunaService.d.ts +14 -0
  55. package/dist/vwr/src/native/screensaver-prevention/LGLunaService.d.ts.map +1 -0
  56. package/dist/vwr/src/native/screensaver-prevention/LGLunaService.js +187 -0
  57. package/dist/vwr/src/native/screensaver-prevention/LGLunaService.js.map +1 -0
  58. package/dist/vwr/src/native/screensaver-prevention/rpc.d.ts +27 -0
  59. package/dist/vwr/src/native/screensaver-prevention/rpc.d.ts.map +1 -0
  60. package/dist/vwr/src/native/screensaver-prevention/rpc.js +71 -0
  61. package/dist/vwr/src/native/screensaver-prevention/rpc.js.map +1 -0
  62. package/dist/vwr/src/observability.d.ts +35 -0
  63. package/dist/vwr/src/observability.d.ts.map +1 -0
  64. package/dist/vwr/src/observability.js +68 -0
  65. package/dist/vwr/src/observability.js.map +1 -0
  66. package/dist/vwr/src/shellEnvDefaults.d.ts +15 -0
  67. package/dist/vwr/src/shellEnvDefaults.d.ts.map +1 -0
  68. package/dist/vwr/src/shellEnvDefaults.js +34 -0
  69. package/dist/vwr/src/shellEnvDefaults.js.map +1 -0
  70. package/dist/vwr/src/types.d.ts +56 -0
  71. package/dist/vwr/src/types.d.ts.map +1 -0
  72. package/dist/vwr/src/types.js +27 -0
  73. package/dist/vwr/src/types.js.map +1 -0
  74. package/dist/vwr/src/urlUtils.d.ts +17 -0
  75. package/dist/vwr/src/urlUtils.d.ts.map +1 -0
  76. package/dist/vwr/src/urlUtils.js +42 -0
  77. package/dist/vwr/src/urlUtils.js.map +1 -0
  78. package/dist/vwr/src/vwrBootstrap.d.ts +26 -0
  79. package/dist/vwr/src/vwrBootstrap.d.ts.map +1 -0
  80. package/dist/vwr/src/vwrBootstrap.js +176 -0
  81. package/dist/vwr/src/vwrBootstrap.js.map +1 -0
  82. package/dist/vwr/src/vwrConfig.d.ts.map +1 -1
  83. package/dist/vwr/src/vwrConfig.js +11 -2
  84. package/dist/vwr/src/vwrConfig.js.map +1 -1
  85. package/{src → dist/vwr-loader}/index.html +1 -1
  86. package/dist/vwr-loader/src/__mocks__/invalidVwrModule.d.ts +2 -0
  87. package/dist/vwr-loader/src/__mocks__/invalidVwrModule.d.ts.map +1 -0
  88. package/dist/vwr-loader/src/__mocks__/invalidVwrModule.js +2 -0
  89. package/dist/vwr-loader/src/__mocks__/invalidVwrModule.js.map +1 -0
  90. package/dist/vwr-loader/src/index.d.ts +3 -3
  91. package/dist/vwr-loader/src/index.d.ts.map +1 -1
  92. package/dist/vwr-loader/src/index.js +2 -2
  93. package/dist/vwr-loader/src/index.js.map +1 -1
  94. package/dist/vwr-loader/src/loadVwr.d.ts.map +1 -1
  95. package/dist/vwr-loader/src/loadVwr.js +28 -22
  96. package/dist/vwr-loader/src/loadVwr.js.map +1 -1
  97. package/dist/vwr-loader/src/logger.d.ts +1 -1
  98. package/dist/vwr-loader/src/logger.d.ts.map +1 -1
  99. package/dist/vwr-loader/src/logger.js +1 -1
  100. package/dist/vwr-loader/src/logger.js.map +1 -1
  101. package/dist/vwr-loader/src/main.js +1 -1
  102. package/dist/vwr-loader/src/main.js.map +1 -1
  103. package/package.json +4 -5
  104. package/dist/assets/profiler-BRmTNL9s.js +0 -2
  105. package/dist/assets/profiler-BRmTNL9s.js.map +0 -1
  106. package/dist/assets/startRecording-CuFdVhxx.js +0 -3
  107. package/dist/assets/startRecording-CuFdVhxx.js.map +0 -1
  108. package/dist/index.html +0 -25
  109. package/dist/main.js +0 -12
  110. package/dist/main.js.map +0 -1
  111. package/src/__mocks__/@datadog/browser-logs.ts +0 -35
  112. package/src/__mocks__/@datadog/browser-rum.ts +0 -27
  113. package/src/__mocks__/vwrModule.ts +0 -3
  114. package/src/amplitudeFlagFetcher.test.ts +0 -175
  115. package/src/amplitudeFlagFetcher.ts +0 -105
  116. package/src/errors/InitializationError.ts +0 -105
  117. package/src/errors/ensureError.ts +0 -6
  118. package/src/errors/index.ts +0 -16
  119. package/src/exitHandler.test.ts +0 -417
  120. package/src/exitHandler.ts +0 -233
  121. package/src/getEnvironment.test.ts +0 -417
  122. package/src/getEnvironment.ts +0 -144
  123. package/src/getShellVersion.test.ts +0 -403
  124. package/src/getShellVersion.ts +0 -133
  125. package/src/index.ts +0 -7
  126. package/src/loadVwr.test.ts +0 -554
  127. package/src/loadVwr.ts +0 -363
  128. package/src/logger.ts +0 -11
  129. package/src/main.test.ts +0 -157
  130. package/src/main.ts +0 -128
  131. package/src/polyfills.ts +0 -32
  132. package/src/test-setup.ts +0 -5
  133. package/src/vite-env.d.ts +0 -33
@@ -1,417 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"
2
-
3
- import {
4
- exitIfBackNavigation,
5
- setupFireTVBackButtonHandler,
6
- setupLGBackButtonHandler,
7
- } from "./exitHandler"
8
- import { logger } from "./logger"
9
-
10
- describe("exitHandler", () => {
11
- beforeEach(() => {
12
- vi.clearAllMocks()
13
- })
14
-
15
- afterEach(() => {
16
- vi.restoreAllMocks()
17
- })
18
-
19
- describe("exitIfBackNavigation", () => {
20
- it("should not exit when navigation type is not back_forward", () => {
21
- vi.spyOn(performance, "getEntriesByType").mockReturnValue([
22
- { type: "navigate" } as PerformanceNavigationTiming,
23
- ])
24
-
25
- const mockExit = vi.fn()
26
- window.tizen = {
27
- application: {
28
- getAppInfo: () => ({ version: "1.0.0" }),
29
- getCurrentApplication: () => ({ exit: mockExit }),
30
- },
31
- }
32
-
33
- exitIfBackNavigation("SAMSUNG_TV")
34
-
35
- expect(mockExit).not.toHaveBeenCalled()
36
- })
37
-
38
- it("should exit Samsung app when back_forward detected", () => {
39
- vi.spyOn(performance, "getEntriesByType").mockReturnValue([
40
- { type: "back_forward" } as PerformanceNavigationTiming,
41
- ])
42
-
43
- const mockExit = vi.fn()
44
- window.tizen = {
45
- application: {
46
- getAppInfo: () => ({ version: "1.0.0" }),
47
- getCurrentApplication: () => ({ exit: mockExit }),
48
- },
49
- }
50
-
51
- exitIfBackNavigation("SAMSUNG_TV")
52
-
53
- expect(mockExit).toHaveBeenCalled()
54
- })
55
-
56
- it("should close window for LG app when back_forward detected", () => {
57
- vi.spyOn(performance, "getEntriesByType").mockReturnValue([
58
- { type: "back_forward" } as PerformanceNavigationTiming,
59
- ])
60
-
61
- const mockClose = vi.fn()
62
- vi.spyOn(window, "close").mockImplementation(mockClose)
63
-
64
- exitIfBackNavigation("LG_TV")
65
-
66
- expect(mockClose).toHaveBeenCalled()
67
- })
68
-
69
- it("should not exit when no navigation entries exist", () => {
70
- vi.spyOn(performance, "getEntriesByType").mockReturnValue([])
71
-
72
- const mockClose = vi.fn()
73
- vi.spyOn(window, "close").mockImplementation(mockClose)
74
-
75
- exitIfBackNavigation("LG_TV")
76
-
77
- expect(mockClose).not.toHaveBeenCalled()
78
- })
79
-
80
- it("should handle errors gracefully", () => {
81
- vi.spyOn(performance, "getEntriesByType").mockImplementation(() => {
82
- throw new Error("Performance API not available")
83
- })
84
-
85
- // Should not throw
86
- expect(() => exitIfBackNavigation("SAMSUNG_TV")).not.toThrow()
87
- })
88
-
89
- it("should exit Fire TV app when back_forward detected", () => {
90
- vi.spyOn(performance, "getEntriesByType").mockReturnValue([
91
- { type: "back_forward" } as PerformanceNavigationTiming,
92
- ])
93
-
94
- const mockExitApp = vi.fn().mockResolvedValue(undefined)
95
- window.Capacitor = {
96
- Plugins: {
97
- AppLifecycle: { exitApp: mockExitApp },
98
- },
99
- }
100
-
101
- exitIfBackNavigation("FIRE_TV")
102
-
103
- expect(mockExitApp).toHaveBeenCalled()
104
- })
105
-
106
- it("should handle Fire TV without Capacitor gracefully", () => {
107
- vi.spyOn(performance, "getEntriesByType").mockReturnValue([
108
- { type: "back_forward" } as PerformanceNavigationTiming,
109
- ])
110
-
111
- window.Capacitor = undefined
112
-
113
- expect(() => exitIfBackNavigation("FIRE_TV")).not.toThrow()
114
- })
115
-
116
- it("should log when Fire TV exit promise rejects", async () => {
117
- vi.spyOn(performance, "getEntriesByType").mockReturnValue([
118
- { type: "back_forward" } as PerformanceNavigationTiming,
119
- ])
120
-
121
- const mockExitApp = vi
122
- .fn()
123
- .mockRejectedValue(new Error("exit failed"))
124
- const loggerErrorSpy = vi.spyOn(logger, "error")
125
-
126
- window.Capacitor = {
127
- Plugins: {
128
- AppLifecycle: { exitApp: mockExitApp },
129
- },
130
- }
131
-
132
- exitIfBackNavigation("FIRE_TV")
133
-
134
- await vi.waitFor(() => {
135
- expect(loggerErrorSpy).toHaveBeenCalledWith(
136
- { error: expect.any(Error) },
137
- "[Shell] Failed to exit Fire TV app"
138
- )
139
- })
140
- })
141
- })
142
-
143
- describe("setupFireTVBackButtonHandler", () => {
144
- const ANDROID_BACK_KEYCODE = 4
145
- type KeyDownListener = (event: { keyCode: number }) => void
146
-
147
- it("should call Capacitor AppLifecycle.exitApp when InputHandler emits back keyDown", async () => {
148
- const mockExitApp = vi.fn().mockResolvedValue(undefined)
149
- const mockRemoveListener = vi.fn().mockResolvedValue(undefined)
150
- let capturedListener: KeyDownListener | null = null
151
- const mockAddListener = vi
152
- .fn()
153
- .mockImplementation(
154
- async (eventName: string, listener: KeyDownListener) => {
155
- if (eventName === "keyDown") {
156
- capturedListener = listener
157
- }
158
-
159
- return {
160
- remove: mockRemoveListener,
161
- }
162
- }
163
- )
164
-
165
- window.Capacitor = {
166
- Plugins: {
167
- AppLifecycle: { exitApp: mockExitApp },
168
- InputHandler: {
169
- addListener: mockAddListener,
170
- },
171
- },
172
- }
173
-
174
- const cleanup = setupFireTVBackButtonHandler()
175
-
176
- await vi.waitFor(() => {
177
- expect(mockAddListener).toHaveBeenCalledWith(
178
- "keyDown",
179
- expect.any(Function)
180
- )
181
- })
182
-
183
- capturedListener?.({ keyCode: ANDROID_BACK_KEYCODE })
184
-
185
- expect(mockExitApp).toHaveBeenCalled()
186
-
187
- cleanup()
188
- })
189
-
190
- it("should handle missing Capacitor gracefully", () => {
191
- window.Capacitor = undefined
192
-
193
- const cleanup = setupFireTVBackButtonHandler()
194
-
195
- expect(() => cleanup()).not.toThrow()
196
- cleanup()
197
- })
198
-
199
- it("should not trigger when InputHandler emits non-back keyDown", async () => {
200
- const mockExitApp = vi.fn().mockResolvedValue(undefined)
201
- let capturedListener: KeyDownListener | null = null
202
- const mockAddListener = vi
203
- .fn()
204
- .mockImplementation(
205
- async (eventName: string, listener: KeyDownListener) => {
206
- if (eventName === "keyDown") {
207
- capturedListener = listener
208
- }
209
-
210
- return {
211
- remove: vi.fn().mockResolvedValue(undefined),
212
- }
213
- }
214
- )
215
-
216
- window.Capacitor = {
217
- Plugins: {
218
- AppLifecycle: { exitApp: mockExitApp },
219
- InputHandler: {
220
- addListener: mockAddListener,
221
- },
222
- },
223
- }
224
-
225
- const cleanup = setupFireTVBackButtonHandler()
226
-
227
- await vi.waitFor(() => {
228
- expect(mockAddListener).toHaveBeenCalledWith(
229
- "keyDown",
230
- expect.any(Function)
231
- )
232
- })
233
-
234
- capturedListener?.({ keyCode: 13 })
235
-
236
- expect(mockExitApp).not.toHaveBeenCalled()
237
-
238
- cleanup()
239
- })
240
-
241
- it("should remove InputHandler listener when cleanup is called", async () => {
242
- const mockExitApp = vi.fn().mockResolvedValue(undefined)
243
- const mockRemoveListener = vi.fn().mockResolvedValue(undefined)
244
- const mockAddListener = vi.fn().mockResolvedValue({
245
- remove: mockRemoveListener,
246
- })
247
-
248
- window.Capacitor = {
249
- Plugins: {
250
- AppLifecycle: { exitApp: mockExitApp },
251
- InputHandler: {
252
- addListener: mockAddListener,
253
- },
254
- },
255
- }
256
-
257
- const cleanup = setupFireTVBackButtonHandler()
258
-
259
- await vi.waitFor(() => {
260
- expect(mockAddListener).toHaveBeenCalledWith(
261
- "keyDown",
262
- expect.any(Function)
263
- )
264
- })
265
-
266
- cleanup()
267
-
268
- await vi.waitFor(() => {
269
- expect(mockRemoveListener).toHaveBeenCalled()
270
- })
271
- })
272
-
273
- it("should support InputHandler addListener returning a sync handle", async () => {
274
- const mockExitApp = vi.fn().mockResolvedValue(undefined)
275
- const mockRemoveListener = vi.fn().mockResolvedValue(undefined)
276
- let capturedListener: KeyDownListener | null = null
277
- const mockAddListener = vi
278
- .fn()
279
- .mockImplementation(
280
- (eventName: string, listener: KeyDownListener) => {
281
- if (eventName === "keyDown") {
282
- capturedListener = listener
283
- }
284
-
285
- return {
286
- remove: mockRemoveListener,
287
- }
288
- }
289
- )
290
-
291
- window.Capacitor = {
292
- Plugins: {
293
- AppLifecycle: { exitApp: mockExitApp },
294
- InputHandler: {
295
- addListener: mockAddListener,
296
- },
297
- },
298
- }
299
-
300
- const cleanup = setupFireTVBackButtonHandler()
301
-
302
- expect(mockAddListener).toHaveBeenCalledWith(
303
- "keyDown",
304
- expect.any(Function)
305
- )
306
-
307
- capturedListener?.({ keyCode: ANDROID_BACK_KEYCODE })
308
- expect(mockExitApp).toHaveBeenCalled()
309
-
310
- cleanup()
311
-
312
- await vi.waitFor(() => {
313
- expect(mockRemoveListener).toHaveBeenCalled()
314
- })
315
- })
316
-
317
- it("should handle missing InputHandler plugin gracefully", () => {
318
- const mockExitApp = vi.fn().mockResolvedValue(undefined)
319
- window.Capacitor = {
320
- Plugins: {
321
- AppLifecycle: { exitApp: mockExitApp },
322
- },
323
- }
324
-
325
- const cleanup = setupFireTVBackButtonHandler()
326
-
327
- expect(mockExitApp).not.toHaveBeenCalled()
328
- expect(() => cleanup()).not.toThrow()
329
- })
330
- })
331
-
332
- describe("setupLGBackButtonHandler", () => {
333
- const LG_BACK_KEYCODE = 461
334
-
335
- it("should call webOS.platformBack when back button is pressed", () => {
336
- const mockPlatformBack = vi.fn()
337
- window.webOS = { platformBack: mockPlatformBack }
338
-
339
- const cleanup = setupLGBackButtonHandler()
340
-
341
- // Simulate LG back button press
342
- const event = new KeyboardEvent("keydown", {
343
- keyCode: LG_BACK_KEYCODE,
344
- })
345
- document.dispatchEvent(event)
346
-
347
- expect(mockPlatformBack).toHaveBeenCalled()
348
-
349
- cleanup()
350
- })
351
-
352
- it("should fall back to window.close when platformBack is not available", () => {
353
- window.webOS = {} // No platformBack
354
- const mockClose = vi.fn()
355
- vi.spyOn(window, "close").mockImplementation(mockClose)
356
-
357
- const cleanup = setupLGBackButtonHandler()
358
-
359
- const event = new KeyboardEvent("keydown", {
360
- keyCode: LG_BACK_KEYCODE,
361
- })
362
- document.dispatchEvent(event)
363
-
364
- expect(mockClose).toHaveBeenCalled()
365
-
366
- cleanup()
367
- })
368
-
369
- it("should not trigger on other key presses", () => {
370
- const mockPlatformBack = vi.fn()
371
- window.webOS = { platformBack: mockPlatformBack }
372
-
373
- const cleanup = setupLGBackButtonHandler()
374
-
375
- // Simulate a different key press (Enter = 13)
376
- const event = new KeyboardEvent("keydown", { keyCode: 13 })
377
- document.dispatchEvent(event)
378
-
379
- expect(mockPlatformBack).not.toHaveBeenCalled()
380
-
381
- cleanup()
382
- })
383
-
384
- it("should remove event listener when cleanup is called", () => {
385
- const mockPlatformBack = vi.fn()
386
- window.webOS = { platformBack: mockPlatformBack }
387
-
388
- const cleanup = setupLGBackButtonHandler()
389
- cleanup()
390
-
391
- // Simulate back button press after cleanup
392
- const event = new KeyboardEvent("keydown", {
393
- keyCode: LG_BACK_KEYCODE,
394
- })
395
- document.dispatchEvent(event)
396
-
397
- expect(mockPlatformBack).not.toHaveBeenCalled()
398
- })
399
-
400
- it("should handle missing webOS object gracefully", () => {
401
- window.webOS = undefined
402
- const mockClose = vi.fn()
403
- vi.spyOn(window, "close").mockImplementation(mockClose)
404
-
405
- const cleanup = setupLGBackButtonHandler()
406
-
407
- const event = new KeyboardEvent("keydown", {
408
- keyCode: LG_BACK_KEYCODE,
409
- })
410
- document.dispatchEvent(event)
411
-
412
- expect(mockClose).toHaveBeenCalled()
413
-
414
- cleanup()
415
- })
416
- })
417
- })
@@ -1,233 +0,0 @@
1
- /**
2
- * Back button / exit handling for vwr-loader.
3
- *
4
- * This is a safety net for when VWR fails to load and user presses back.
5
- * When VWR loads successfully, it handles exit behavior itself.
6
- */
7
-
8
- import { logger } from "./logger"
9
-
10
- /** LG webOS back button keycode */
11
- const LG_BACK_KEYCODE = 461
12
-
13
- /** Android back button keycode (used by Fire TV InputHandler plugin) */
14
- const ANDROID_BACK_KEYCODE = 4
15
-
16
- /**
17
- * Exit the app if the page was loaded via back/forward navigation.
18
- *
19
- * This is a safety net for Samsung/LG TVs: if VWR failed to load and user
20
- * pressed back, they'd land on the loader again. This detects that and exits.
21
- *
22
- * Note: On LG TV with disableBackHistoryAPI: true, this won't trigger
23
- * because the platform doesn't automatically navigate back. Use
24
- * setupLGBackButtonHandler() for LG back button support.
25
- */
26
- export function exitIfBackNavigation(platform: Platform): void {
27
- try {
28
- const navEntries = performance.getEntriesByType("navigation")
29
- const navEntry = navEntries[0] as
30
- | PerformanceNavigationTiming
31
- | undefined
32
-
33
- if (navEntry?.type === "back_forward") {
34
- logger.info(
35
- { platform, navigationType: navEntry.type },
36
- "[Shell] Back navigation detected, exiting app"
37
- )
38
- exitApp(platform)
39
- }
40
- } catch (error) {
41
- // Performance API may not be available on all platforms
42
- logger.warn({ error }, "[Shell] Could not check navigation type")
43
- }
44
- }
45
-
46
- /**
47
- * Set up LG back button (keycode 461) handler.
48
- *
49
- * On LG TV with disableBackHistoryAPI: true in appinfo.json, the platform
50
- * doesn't automatically handle the back button. This sets up a keydown
51
- * listener to call webOS.platformBack() which shows the system exit popup
52
- * on webOS 6.0+ or launches Home on older versions.
53
- *
54
- * Once VWR loads successfully, call the returned cleanup function - VWR/PSDK
55
- * will take over back button handling with its own custom modal.
56
- *
57
- * @returns Cleanup function to remove the listener
58
- */
59
- export function setupLGBackButtonHandler(): () => void {
60
- const handleKeyDown = (event: KeyboardEvent): void => {
61
- if (event.keyCode === LG_BACK_KEYCODE) {
62
- logger.info(
63
- "[Shell] LG back button pressed, triggering platform exit"
64
- )
65
-
66
- if (window.webOS?.platformBack) {
67
- window.webOS.platformBack()
68
- } else {
69
- logger.warn(
70
- "[Shell] webOS.platformBack not available, using window.close"
71
- )
72
- window.close()
73
- }
74
- }
75
- }
76
-
77
- document.addEventListener("keydown", handleKeyDown)
78
- logger.info("[Shell] LG back button handler registered")
79
-
80
- return () => {
81
- document.removeEventListener("keydown", handleKeyDown)
82
- logger.info("[Shell] LG back button handler removed")
83
- }
84
- }
85
-
86
- /**
87
- * Set up Fire TV back button (keycode 4) handler.
88
- *
89
- * On Fire TV, the native shell intercepts the back button at the Activity
90
- * level and forwards it to JavaScript via the InputHandler Capacitor plugin.
91
- * During the vwr-loader loading phase, we subscribe directly to InputHandler
92
- * keyDown events and call Capacitor AppLifecycle.exitApp().
93
- *
94
- * Once VWR loads successfully, call the returned cleanup function - VWR
95
- * will take over back button handling.
96
- *
97
- * @returns Cleanup function to remove the listener
98
- */
99
- export function setupFireTVBackButtonHandler(): () => void {
100
- const inputHandler = window.Capacitor?.Plugins?.InputHandler
101
-
102
- if (!inputHandler?.addListener) {
103
- logger.warn("[Shell] Capacitor InputHandler plugin not available")
104
- return () => {
105
- logger.info("[Shell] Fire TV back button handler removed")
106
- }
107
- }
108
-
109
- let listenerHandle: { remove: () => Promise<void> | void } | null = null
110
- let isCleanedUp = false
111
-
112
- const listenerRegistration = Promise.resolve(
113
- inputHandler.addListener("keyDown", (event: { keyCode: number }) => {
114
- if (event.keyCode === ANDROID_BACK_KEYCODE) {
115
- logger.info("[Shell] Fire TV back button pressed, exiting app")
116
- exitFireTVApp()
117
- }
118
- })
119
- )
120
- .then((handle: { remove: () => Promise<void> | void }) => {
121
- listenerHandle = handle
122
-
123
- if (isCleanedUp) {
124
- return Promise.resolve(handle.remove()).catch(
125
- (error: unknown) => {
126
- logger.error(
127
- { error },
128
- "[Shell] Failed to remove Fire TV back button handler"
129
- )
130
- }
131
- )
132
- }
133
-
134
- return undefined
135
- })
136
- .catch((error: unknown) => {
137
- logger.error(
138
- { error },
139
- "[Shell] Failed to register Fire TV back button handler"
140
- )
141
- })
142
-
143
- logger.info("[Shell] Fire TV back button handler registered")
144
-
145
- return () => {
146
- isCleanedUp = true
147
-
148
- if (listenerHandle) {
149
- void Promise.resolve(listenerHandle.remove()).catch(
150
- (error: unknown) => {
151
- logger.error(
152
- { error },
153
- "[Shell] Failed to remove Fire TV back button handler"
154
- )
155
- }
156
- )
157
- listenerHandle = null
158
- } else {
159
- void listenerRegistration
160
- }
161
-
162
- logger.info("[Shell] Fire TV back button handler removed")
163
- }
164
- }
165
-
166
- /**
167
- * Exit the app using platform-specific methods.
168
- *
169
- * @param platform - Platform identifier
170
- */
171
- function exitApp(platform: Platform): void {
172
- switch (platform) {
173
- case "SAMSUNG_TV":
174
- exitSamsungApp()
175
- break
176
- case "LG_TV":
177
- exitLGAppViaBackNavigation()
178
- break
179
- case "FIRE_TV":
180
- exitFireTVApp()
181
- break
182
- // Mobile apps handle exit at the native layer
183
- // Web doesn't have a meaningful "exit" concept
184
- default:
185
- logger.info({ platform }, "[Shell] No platform-specific exit")
186
- }
187
- }
188
-
189
- function exitSamsungApp(): void {
190
- try {
191
- if (window.tizen?.application?.getCurrentApplication) {
192
- window.tizen.application.getCurrentApplication().exit()
193
- } else {
194
- logger.warn("[Shell] Tizen exit API not available")
195
- }
196
- } catch (error) {
197
- logger.error({ error }, "[Shell] Failed to exit Samsung app")
198
- }
199
- }
200
-
201
- /**
202
- * Exit LG app when back navigation is detected.
203
- * Uses window.close() for immediate exit since user already navigated back.
204
- */
205
- function exitLGAppViaBackNavigation(): void {
206
- try {
207
- window.close()
208
- } catch (error) {
209
- logger.error({ error }, "[Shell] Failed to exit LG app")
210
- }
211
- }
212
-
213
- /**
214
- * Exit Fire TV app via Capacitor AppLifecycle plugin.
215
- */
216
- function exitFireTVApp(): void {
217
- try {
218
- if (window.Capacitor?.Plugins?.AppLifecycle?.exitApp) {
219
- void window.Capacitor.Plugins.AppLifecycle.exitApp().catch(
220
- (error: unknown) => {
221
- logger.error(
222
- { error },
223
- "[Shell] Failed to exit Fire TV app"
224
- )
225
- }
226
- )
227
- } else {
228
- logger.warn("[Shell] Capacitor AppLifecycle plugin not available")
229
- }
230
- } catch (error) {
231
- logger.error({ error }, "[Shell] Failed to exit Fire TV app")
232
- }
233
- }