@mentra/engine 0.1.0-dev.0 → 0.1.0-dev.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (441) hide show
  1. package/README.md +42 -1
  2. package/build/engine.d.ts +11 -3
  3. package/build/engine.d.ts.map +1 -1
  4. package/build/engine.js +7 -2
  5. package/build/engine.js.map +1 -1
  6. package/build/facades/dev.d.ts +3 -2
  7. package/build/facades/dev.d.ts.map +1 -1
  8. package/build/facades/dev.js +18 -12
  9. package/build/facades/dev.js.map +1 -1
  10. package/build/facades/gallery.js +1 -1
  11. package/build/facades/gallery.js.map +1 -1
  12. package/build/facades/glasses.d.ts +1 -0
  13. package/build/facades/glasses.d.ts.map +1 -1
  14. package/build/facades/glasses.js +19 -4
  15. package/build/facades/glasses.js.map +1 -1
  16. package/build/facades/glassesSettings.d.ts.map +1 -1
  17. package/build/facades/glassesSettings.js +1 -0
  18. package/build/facades/glassesSettings.js.map +1 -1
  19. package/build/facades/miniapps.d.ts +4 -0
  20. package/build/facades/miniapps.d.ts.map +1 -1
  21. package/build/facades/miniapps.js +5 -0
  22. package/build/facades/miniapps.js.map +1 -1
  23. package/build/facades/ota.d.ts +11 -2
  24. package/build/facades/ota.d.ts.map +1 -1
  25. package/build/facades/ota.js +19 -4
  26. package/build/facades/ota.js.map +1 -1
  27. package/build/facades/pairing.d.ts +4 -0
  28. package/build/facades/pairing.d.ts.map +1 -1
  29. package/build/facades/pairing.js +10 -8
  30. package/build/facades/pairing.js.map +1 -1
  31. package/build/facades/permissions.d.ts +10 -0
  32. package/build/facades/permissions.d.ts.map +1 -1
  33. package/build/facades/permissions.js +10 -0
  34. package/build/facades/permissions.js.map +1 -1
  35. package/build/facades/phoneNotifications.d.ts +0 -4
  36. package/build/facades/phoneNotifications.d.ts.map +1 -1
  37. package/build/facades/phoneNotifications.js +4 -14
  38. package/build/facades/phoneNotifications.js.map +1 -1
  39. package/build/index.d.ts +6 -3
  40. package/build/index.d.ts.map +1 -1
  41. package/build/index.js +4 -2
  42. package/build/index.js.map +1 -1
  43. package/build/internal.d.ts +3 -2
  44. package/build/internal.d.ts.map +1 -1
  45. package/build/internal.js +2 -1
  46. package/build/internal.js.map +1 -1
  47. package/build/react/MentraLiveOtaFlow.d.ts +38 -0
  48. package/build/react/MentraLiveOtaFlow.d.ts.map +1 -0
  49. package/build/react/MentraLiveOtaFlow.js +528 -0
  50. package/build/react/MentraLiveOtaFlow.js.map +1 -0
  51. package/build/react/index.d.ts +2 -0
  52. package/build/react/index.d.ts.map +1 -0
  53. package/build/react/index.js +2 -0
  54. package/build/react/index.js.map +1 -0
  55. package/build/react/useEngineSnapshot.d.ts +2 -0
  56. package/build/react/useEngineSnapshot.d.ts.map +1 -0
  57. package/build/react/useEngineSnapshot.js +14 -0
  58. package/build/react/useEngineSnapshot.js.map +1 -0
  59. package/build/runtime/bootstrap.d.ts +24 -1
  60. package/build/runtime/bootstrap.d.ts.map +1 -1
  61. package/build/runtime/bootstrap.js +17 -0
  62. package/build/runtime/bootstrap.js.map +1 -1
  63. package/build/runtime/scanQrSeam.d.ts +7 -0
  64. package/build/runtime/scanQrSeam.d.ts.map +1 -0
  65. package/build/runtime/scanQrSeam.js +16 -0
  66. package/build/runtime/scanQrSeam.js.map +1 -0
  67. package/build/services/AppRegistry.d.ts +33 -57
  68. package/build/services/AppRegistry.d.ts.map +1 -1
  69. package/build/services/AppRegistry.js +193 -144
  70. package/build/services/AppRegistry.js.map +1 -1
  71. package/build/services/AudioCloudUplink.d.ts +12 -0
  72. package/build/services/AudioCloudUplink.d.ts.map +1 -1
  73. package/build/services/AudioCloudUplink.js +22 -1
  74. package/build/services/AudioCloudUplink.js.map +1 -1
  75. package/build/services/AudioPlaybackService.d.ts +41 -2
  76. package/build/services/AudioPlaybackService.d.ts.map +1 -1
  77. package/build/services/AudioPlaybackService.js +250 -33
  78. package/build/services/AudioPlaybackService.js.map +1 -1
  79. package/build/services/BlobStore.d.ts +2 -1
  80. package/build/services/BlobStore.d.ts.map +1 -1
  81. package/build/services/BlobStore.js +17 -9
  82. package/build/services/BlobStore.js.map +1 -1
  83. package/build/services/CloudAudioSubscriptionSync.d.ts.map +1 -1
  84. package/build/services/CloudAudioSubscriptionSync.js +13 -1
  85. package/build/services/CloudAudioSubscriptionSync.js.map +1 -1
  86. package/build/services/CloudClientService.d.ts +15 -0
  87. package/build/services/CloudClientService.d.ts.map +1 -1
  88. package/build/services/CloudClientService.js +93 -1
  89. package/build/services/CloudClientService.js.map +1 -1
  90. package/build/services/DeviceEventRouter.js +5 -5
  91. package/build/services/DeviceEventRouter.js.map +1 -1
  92. package/build/services/ForegroundLocationPermission.d.ts +13 -0
  93. package/build/services/ForegroundLocationPermission.d.ts.map +1 -0
  94. package/build/services/ForegroundLocationPermission.js +12 -0
  95. package/build/services/ForegroundLocationPermission.js.map +1 -0
  96. package/build/services/GlassesSettingsSync.d.ts.map +1 -1
  97. package/build/services/GlassesSettingsSync.js +9 -3
  98. package/build/services/GlassesSettingsSync.js.map +1 -1
  99. package/build/services/GlassesStatusProjection.d.ts +2 -1
  100. package/build/services/GlassesStatusProjection.d.ts.map +1 -1
  101. package/build/services/GlassesStatusProjection.js +15 -8
  102. package/build/services/GlassesStatusProjection.js.map +1 -1
  103. package/build/services/HotspotOtaTransport.d.ts +29 -0
  104. package/build/services/HotspotOtaTransport.d.ts.map +1 -0
  105. package/build/services/HotspotOtaTransport.js +146 -0
  106. package/build/services/HotspotOtaTransport.js.map +1 -0
  107. package/build/services/HotspotShutdown.d.ts +17 -0
  108. package/build/services/HotspotShutdown.d.ts.map +1 -0
  109. package/build/services/HotspotShutdown.js +29 -0
  110. package/build/services/HotspotShutdown.js.map +1 -0
  111. package/build/services/LocalDisplayManager.d.ts +15 -0
  112. package/build/services/LocalDisplayManager.d.ts.map +1 -1
  113. package/build/services/LocalDisplayManager.js +106 -20
  114. package/build/services/LocalDisplayManager.js.map +1 -1
  115. package/build/services/LocalMiniappRuntime.d.ts +74 -2
  116. package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
  117. package/build/services/LocalMiniappRuntime.js +785 -291
  118. package/build/services/LocalMiniappRuntime.js.map +1 -1
  119. package/build/services/LocalMiniappStorage.d.ts +32 -0
  120. package/build/services/LocalMiniappStorage.d.ts.map +1 -0
  121. package/build/services/LocalMiniappStorage.js +66 -0
  122. package/build/services/LocalMiniappStorage.js.map +1 -0
  123. package/build/services/LocalMiniappUserIdentity.d.ts +20 -0
  124. package/build/services/LocalMiniappUserIdentity.d.ts.map +1 -0
  125. package/build/services/LocalMiniappUserIdentity.js +43 -0
  126. package/build/services/LocalMiniappUserIdentity.js.map +1 -0
  127. package/build/services/LocalSttFallbackCoordinator.d.ts +9 -6
  128. package/build/services/LocalSttFallbackCoordinator.d.ts.map +1 -1
  129. package/build/services/LocalSttFallbackCoordinator.js +18 -12
  130. package/build/services/LocalSttFallbackCoordinator.js.map +1 -1
  131. package/build/services/MicStateCoordinator.d.ts +42 -1
  132. package/build/services/MicStateCoordinator.d.ts.map +1 -1
  133. package/build/services/MicStateCoordinator.js +141 -3
  134. package/build/services/MicStateCoordinator.js.map +1 -1
  135. package/build/services/MiniappLauncher.d.ts.map +1 -1
  136. package/build/services/MiniappLauncher.js +23 -3
  137. package/build/services/MiniappLauncher.js.map +1 -1
  138. package/build/services/MiniappLiveness.d.ts +12 -0
  139. package/build/services/MiniappLiveness.d.ts.map +1 -0
  140. package/build/services/MiniappLiveness.js +16 -0
  141. package/build/services/MiniappLiveness.js.map +1 -0
  142. package/build/services/NativeHttpResponse.d.ts +3 -0
  143. package/build/services/NativeHttpResponse.d.ts.map +1 -0
  144. package/build/services/NativeHttpResponse.js +6 -0
  145. package/build/services/NativeHttpResponse.js.map +1 -0
  146. package/build/services/OtaArtifactDownloader.d.ts +65 -0
  147. package/build/services/OtaArtifactDownloader.d.ts.map +1 -0
  148. package/build/services/OtaArtifactDownloader.js +226 -0
  149. package/build/services/OtaArtifactDownloader.js.map +1 -0
  150. package/build/services/OtaAutoChain.d.ts +34 -0
  151. package/build/services/OtaAutoChain.d.ts.map +1 -0
  152. package/build/services/OtaAutoChain.js +83 -0
  153. package/build/services/OtaAutoChain.js.map +1 -0
  154. package/build/services/OtaErrorMapping.d.ts +18 -0
  155. package/build/services/OtaErrorMapping.d.ts.map +1 -0
  156. package/build/services/OtaErrorMapping.js +63 -0
  157. package/build/services/OtaErrorMapping.js.map +1 -0
  158. package/build/services/OtaInstallCoordinator.d.ts +94 -14
  159. package/build/services/OtaInstallCoordinator.d.ts.map +1 -1
  160. package/build/services/OtaInstallCoordinator.js +574 -130
  161. package/build/services/OtaInstallCoordinator.js.map +1 -1
  162. package/build/services/OtaService.d.ts.map +1 -1
  163. package/build/services/OtaService.js +8 -1
  164. package/build/services/OtaService.js.map +1 -1
  165. package/build/services/OtaUpdateCheckService.d.ts +38 -3
  166. package/build/services/OtaUpdateCheckService.d.ts.map +1 -1
  167. package/build/services/OtaUpdateCheckService.js +142 -22
  168. package/build/services/OtaUpdateCheckService.js.map +1 -1
  169. package/build/services/PhoneCalendarHelpers.d.ts +32 -0
  170. package/build/services/PhoneCalendarHelpers.d.ts.map +1 -0
  171. package/build/services/PhoneCalendarHelpers.js +157 -0
  172. package/build/services/PhoneCalendarHelpers.js.map +1 -0
  173. package/build/services/PhoneCalendarService.d.ts +5 -0
  174. package/build/services/PhoneCalendarService.d.ts.map +1 -0
  175. package/build/services/PhoneCalendarService.js +45 -0
  176. package/build/services/PhoneCalendarService.js.map +1 -0
  177. package/build/services/PhoneCameraFovCoordinator.d.ts +30 -0
  178. package/build/services/PhoneCameraFovCoordinator.d.ts.map +1 -0
  179. package/build/services/PhoneCameraFovCoordinator.js +194 -0
  180. package/build/services/PhoneCameraFovCoordinator.js.map +1 -0
  181. package/build/services/PhoneNotificationsSync.d.ts.map +1 -1
  182. package/build/services/PhoneNotificationsSync.js +12 -10
  183. package/build/services/PhoneNotificationsSync.js.map +1 -1
  184. package/build/services/PhonePhotoCoordinator.d.ts +26 -2
  185. package/build/services/PhonePhotoCoordinator.d.ts.map +1 -1
  186. package/build/services/PhonePhotoCoordinator.js +79 -9
  187. package/build/services/PhonePhotoCoordinator.js.map +1 -1
  188. package/build/services/PhoneStreamCoordinator.d.ts +8 -1
  189. package/build/services/PhoneStreamCoordinator.d.ts.map +1 -1
  190. package/build/services/PhoneStreamCoordinator.js +125 -13
  191. package/build/services/PhoneStreamCoordinator.js.map +1 -1
  192. package/build/services/PhoneVideoCoordinator.d.ts +2 -0
  193. package/build/services/PhoneVideoCoordinator.d.ts.map +1 -1
  194. package/build/services/PhoneVideoCoordinator.js +10 -0
  195. package/build/services/PhoneVideoCoordinator.js.map +1 -1
  196. package/build/services/SceneRenderer.d.ts.map +1 -1
  197. package/build/services/SceneRenderer.js +15 -0
  198. package/build/services/SceneRenderer.js.map +1 -1
  199. package/build/services/SentenceTtsPipeline.d.ts +31 -0
  200. package/build/services/SentenceTtsPipeline.d.ts.map +1 -0
  201. package/build/services/SentenceTtsPipeline.js +67 -0
  202. package/build/services/SentenceTtsPipeline.js.map +1 -0
  203. package/build/services/SupportProfileSync.d.ts +22 -0
  204. package/build/services/SupportProfileSync.d.ts.map +1 -0
  205. package/build/services/SupportProfileSync.js +213 -0
  206. package/build/services/SupportProfileSync.js.map +1 -0
  207. package/build/services/TranscriptionRouting.d.ts +12 -0
  208. package/build/services/TranscriptionRouting.d.ts.map +1 -0
  209. package/build/services/TranscriptionRouting.js +24 -0
  210. package/build/services/TranscriptionRouting.js.map +1 -0
  211. package/build/services/TtsTextSanitizer.d.ts +5 -0
  212. package/build/services/TtsTextSanitizer.d.ts.map +1 -0
  213. package/build/services/TtsTextSanitizer.js +61 -0
  214. package/build/services/TtsTextSanitizer.js.map +1 -0
  215. package/build/services/WifiAdbRequest.d.ts +17 -0
  216. package/build/services/WifiAdbRequest.d.ts.map +1 -0
  217. package/build/services/WifiAdbRequest.js +35 -0
  218. package/build/services/WifiAdbRequest.js.map +1 -0
  219. package/build/services/asg/asgCameraApi.d.ts +43 -0
  220. package/build/services/asg/asgCameraApi.d.ts.map +1 -1
  221. package/build/services/asg/asgCameraApi.js +509 -26
  222. package/build/services/asg/asgCameraApi.js.map +1 -1
  223. package/build/services/asg/cameraRollExportCoordinator.d.ts +60 -0
  224. package/build/services/asg/cameraRollExportCoordinator.d.ts.map +1 -0
  225. package/build/services/asg/cameraRollExportCoordinator.js +618 -0
  226. package/build/services/asg/cameraRollExportCoordinator.js.map +1 -0
  227. package/build/services/asg/cameraRollExportLedger.d.ts +44 -0
  228. package/build/services/asg/cameraRollExportLedger.d.ts.map +1 -0
  229. package/build/services/asg/cameraRollExportLedger.js +78 -0
  230. package/build/services/asg/cameraRollExportLedger.js.map +1 -0
  231. package/build/services/asg/galleryNotices.d.ts +1 -1
  232. package/build/services/asg/galleryNotices.d.ts.map +1 -1
  233. package/build/services/asg/galleryNotices.js.map +1 -1
  234. package/build/services/asg/galleryPathMigration.d.ts +3 -0
  235. package/build/services/asg/galleryPathMigration.d.ts.map +1 -0
  236. package/build/services/asg/galleryPathMigration.js +26 -0
  237. package/build/services/asg/galleryPathMigration.js.map +1 -0
  238. package/build/services/asg/gallerySettingsService.d.ts +1 -0
  239. package/build/services/asg/gallerySettingsService.d.ts.map +1 -1
  240. package/build/services/asg/gallerySettingsService.js +2 -0
  241. package/build/services/asg/gallerySettingsService.js.map +1 -1
  242. package/build/services/asg/gallerySyncService.d.ts.map +1 -1
  243. package/build/services/asg/gallerySyncService.js +162 -56
  244. package/build/services/asg/gallerySyncService.js.map +1 -1
  245. package/build/services/asg/galleryTransferLedger.d.ts +58 -0
  246. package/build/services/asg/galleryTransferLedger.d.ts.map +1 -0
  247. package/build/services/asg/galleryTransferLedger.js +225 -0
  248. package/build/services/asg/galleryTransferLedger.js.map +1 -0
  249. package/build/services/asg/localNetworkTransport.d.ts +15 -0
  250. package/build/services/asg/localNetworkTransport.d.ts.map +1 -0
  251. package/build/services/asg/localNetworkTransport.js +144 -0
  252. package/build/services/asg/localNetworkTransport.js.map +1 -0
  253. package/build/services/asg/localStorageService.d.ts +18 -0
  254. package/build/services/asg/localStorageService.d.ts.map +1 -1
  255. package/build/services/asg/localStorageService.js +168 -19
  256. package/build/services/asg/localStorageService.js.map +1 -1
  257. package/build/services/asg/mediaProcessingQueue.d.ts +10 -0
  258. package/build/services/asg/mediaProcessingQueue.d.ts.map +1 -1
  259. package/build/services/asg/mediaProcessingQueue.js +193 -62
  260. package/build/services/asg/mediaProcessingQueue.js.map +1 -1
  261. package/build/services/audioPlaybackAssets.d.ts +8 -0
  262. package/build/services/audioPlaybackAssets.d.ts.map +1 -0
  263. package/build/services/audioPlaybackAssets.js +7 -0
  264. package/build/services/audioPlaybackAssets.js.map +1 -0
  265. package/build/services/gallerySyncClock.js +1 -1
  266. package/build/services/gallerySyncClock.js.map +1 -1
  267. package/build/services/glassesClockSync.js +2 -2
  268. package/build/services/glassesClockSync.js.map +1 -1
  269. package/build/services/manifestActions.d.ts +7 -0
  270. package/build/services/manifestActions.d.ts.map +1 -0
  271. package/build/services/manifestActions.js +25 -0
  272. package/build/services/manifestActions.js.map +1 -0
  273. package/build/services/otaDisplayState.d.ts +22 -11
  274. package/build/services/otaDisplayState.d.ts.map +1 -1
  275. package/build/services/otaDisplayState.js +36 -14
  276. package/build/services/otaDisplayState.js.map +1 -1
  277. package/build/services/otaInstallPolicy.d.ts +15 -3
  278. package/build/services/otaInstallPolicy.d.ts.map +1 -1
  279. package/build/services/otaInstallPolicy.js +24 -3
  280. package/build/services/otaInstallPolicy.js.map +1 -1
  281. package/build/services/otaManifestUrl.d.ts +2 -0
  282. package/build/services/otaManifestUrl.d.ts.map +1 -0
  283. package/build/services/{asgOtaVersionUrl.js → otaManifestUrl.js} +15 -5
  284. package/build/services/otaManifestUrl.js.map +1 -0
  285. package/build/services/slimStreamStatus.d.ts +11 -0
  286. package/build/services/slimStreamStatus.d.ts.map +1 -1
  287. package/build/services/slimStreamStatus.js +14 -4
  288. package/build/services/slimStreamStatus.js.map +1 -1
  289. package/build/stores/apps.d.ts.map +1 -1
  290. package/build/stores/apps.js +11 -0
  291. package/build/stores/apps.js.map +1 -1
  292. package/build/stores/bluetoothSettingKeys.d.ts.map +1 -1
  293. package/build/stores/bluetoothSettingKeys.js +2 -0
  294. package/build/stores/bluetoothSettingKeys.js.map +1 -1
  295. package/build/stores/gallerySync.d.ts +2 -0
  296. package/build/stores/gallerySync.d.ts.map +1 -1
  297. package/build/stores/gallerySync.js +2 -0
  298. package/build/stores/gallerySync.js.map +1 -1
  299. package/build/stores/glasses.d.ts.map +1 -1
  300. package/build/stores/glasses.js +3 -0
  301. package/build/stores/glasses.js.map +1 -1
  302. package/build/stores/settings.d.ts +17 -0
  303. package/build/stores/settings.d.ts.map +1 -1
  304. package/build/stores/settings.js +157 -17
  305. package/build/stores/settings.js.map +1 -1
  306. package/build/types/applet.d.ts +2 -0
  307. package/build/types/applet.d.ts.map +1 -1
  308. package/build/types/applet.js.map +1 -1
  309. package/build/types/asg.d.ts +7 -0
  310. package/build/types/asg.d.ts.map +1 -1
  311. package/build/types/asg.js.map +1 -1
  312. package/build/types/enums.d.ts +1 -0
  313. package/build/types/enums.d.ts.map +1 -1
  314. package/build/types/enums.js +1 -0
  315. package/build/types/enums.js.map +1 -1
  316. package/build/types/hardware.d.ts +1 -0
  317. package/build/types/hardware.d.ts.map +1 -1
  318. package/build/types/hardware.js +14 -0
  319. package/build/types/hardware.js.map +1 -1
  320. package/build/utils/configuredDevHost.d.ts +6 -0
  321. package/build/utils/configuredDevHost.d.ts.map +1 -0
  322. package/build/utils/configuredDevHost.js +19 -0
  323. package/build/utils/configuredDevHost.js.map +1 -0
  324. package/build/utils/devMiniappLaunch.d.ts +19 -8
  325. package/build/utils/devMiniappLaunch.d.ts.map +1 -1
  326. package/build/utils/devMiniappLaunch.js +144 -21
  327. package/build/utils/devMiniappLaunch.js.map +1 -1
  328. package/build/utils/diagnosticContext.d.ts.map +1 -1
  329. package/build/utils/diagnosticContext.js +15 -17
  330. package/build/utils/diagnosticContext.js.map +1 -1
  331. package/build/utils/hardware/hardware.d.ts.map +1 -1
  332. package/build/utils/hardware/hardware.js +7 -3
  333. package/build/utils/hardware/hardware.js.map +1 -1
  334. package/build/utils/miniappDiagnostics.d.ts +64 -0
  335. package/build/utils/miniappDiagnostics.d.ts.map +1 -0
  336. package/build/utils/miniappDiagnostics.js +173 -0
  337. package/build/utils/miniappDiagnostics.js.map +1 -0
  338. package/build/utils/permissions/MediaLibraryPermissions.d.ts +11 -0
  339. package/build/utils/permissions/MediaLibraryPermissions.d.ts.map +1 -1
  340. package/build/utils/permissions/MediaLibraryPermissions.js +27 -4
  341. package/build/utils/permissions/MediaLibraryPermissions.js.map +1 -1
  342. package/build/utils/timers/timers.d.ts.map +1 -1
  343. package/build/utils/timers/timers.js +55 -18
  344. package/build/utils/timers/timers.js.map +1 -1
  345. package/package.json +25 -15
  346. package/src/assets/silence.wav +0 -0
  347. package/src/engine.ts +7 -2
  348. package/src/facades/dev.ts +17 -12
  349. package/src/facades/gallery.ts +1 -1
  350. package/src/facades/glasses.ts +17 -4
  351. package/src/facades/glassesSettings.ts +5 -0
  352. package/src/facades/miniapps.ts +7 -0
  353. package/src/facades/ota.ts +19 -4
  354. package/src/facades/pairing.ts +11 -7
  355. package/src/facades/permissions.ts +14 -0
  356. package/src/facades/phoneNotifications.ts +4 -13
  357. package/src/index.ts +26 -2
  358. package/src/internal.ts +3 -1
  359. package/src/react/MentraLiveOtaFlow.tsx +856 -0
  360. package/src/react/index.ts +7 -0
  361. package/src/react/useEngineSnapshot.ts +16 -0
  362. package/src/runtime/bootstrap.ts +33 -1
  363. package/src/runtime/scanQrSeam.ts +20 -0
  364. package/src/services/AppRegistry.ts +224 -149
  365. package/src/services/AudioCloudUplink.ts +23 -1
  366. package/src/services/AudioPlaybackService.ts +287 -38
  367. package/src/services/BlobStore.ts +22 -9
  368. package/src/services/CloudAudioSubscriptionSync.ts +13 -1
  369. package/src/services/CloudClientService.ts +96 -1
  370. package/src/services/DeviceEventRouter.ts +5 -5
  371. package/src/services/ForegroundLocationPermission.ts +24 -0
  372. package/src/services/GlassesSettingsSync.ts +9 -3
  373. package/src/services/GlassesStatusProjection.ts +16 -8
  374. package/src/services/HotspotOtaTransport.ts +184 -0
  375. package/src/services/HotspotShutdown.ts +41 -0
  376. package/src/services/LocalDisplayManager.ts +112 -21
  377. package/src/services/LocalMiniappRuntime.ts +967 -346
  378. package/src/services/LocalMiniappStorage.ts +82 -0
  379. package/src/services/LocalMiniappUserIdentity.ts +46 -0
  380. package/src/services/LocalSttFallbackCoordinator.ts +19 -12
  381. package/src/services/MicStateCoordinator.ts +169 -4
  382. package/src/services/MiniappLauncher.ts +30 -3
  383. package/src/services/MiniappLiveness.ts +24 -0
  384. package/src/services/NativeHttpResponse.ts +6 -0
  385. package/src/services/OtaArtifactDownloader.ts +312 -0
  386. package/src/services/OtaAutoChain.ts +106 -0
  387. package/src/services/OtaErrorMapping.ts +80 -0
  388. package/src/services/OtaInstallCoordinator.ts +645 -143
  389. package/src/services/OtaService.ts +11 -5
  390. package/src/services/OtaUpdateCheckService.ts +180 -20
  391. package/src/services/PhoneCalendarHelpers.ts +212 -0
  392. package/src/services/PhoneCalendarService.ts +65 -0
  393. package/src/services/PhoneCameraFovCoordinator.ts +210 -0
  394. package/src/services/PhoneNotificationsSync.ts +12 -10
  395. package/src/services/PhonePhotoCoordinator.ts +116 -18
  396. package/src/services/PhoneStreamCoordinator.ts +139 -25
  397. package/src/services/PhoneVideoCoordinator.ts +11 -0
  398. package/src/services/SceneRenderer.ts +16 -0
  399. package/src/services/SentenceTtsPipeline.ts +106 -0
  400. package/src/services/SupportProfileSync.ts +218 -0
  401. package/src/services/TranscriptionRouting.ts +37 -0
  402. package/src/services/TtsTextSanitizer.ts +66 -0
  403. package/src/services/WifiAdbRequest.ts +53 -0
  404. package/src/services/asg/asgCameraApi.ts +615 -33
  405. package/src/services/asg/cameraRollExportCoordinator.ts +627 -0
  406. package/src/services/asg/cameraRollExportLedger.ts +122 -0
  407. package/src/services/asg/galleryNotices.ts +1 -0
  408. package/src/services/asg/galleryPathMigration.ts +22 -0
  409. package/src/services/asg/gallerySettingsService.ts +3 -0
  410. package/src/services/asg/gallerySyncService.ts +184 -73
  411. package/src/services/asg/galleryTransferLedger.ts +312 -0
  412. package/src/services/asg/localNetworkTransport.ts +169 -0
  413. package/src/services/asg/localStorageService.ts +179 -24
  414. package/src/services/asg/mediaProcessingQueue.ts +209 -62
  415. package/src/services/audioPlaybackAssets.ts +8 -0
  416. package/src/services/gallerySyncClock.ts +1 -1
  417. package/src/services/glassesClockSync.ts +2 -2
  418. package/src/services/manifestActions.ts +30 -0
  419. package/src/services/otaDisplayState.ts +57 -16
  420. package/src/services/otaInstallPolicy.ts +29 -3
  421. package/src/services/{asgOtaVersionUrl.ts → otaManifestUrl.ts} +16 -4
  422. package/src/services/slimStreamStatus.ts +16 -5
  423. package/src/stores/apps.ts +12 -0
  424. package/src/stores/bluetoothSettingKeys.ts +2 -0
  425. package/src/stores/gallerySync.ts +4 -0
  426. package/src/stores/glasses.ts +3 -0
  427. package/src/stores/settings.ts +179 -18
  428. package/src/types/applet.ts +2 -0
  429. package/src/types/asg.ts +7 -0
  430. package/src/types/enums.ts +2 -1
  431. package/src/types/hardware.ts +17 -0
  432. package/src/utils/configuredDevHost.ts +17 -0
  433. package/src/utils/devMiniappLaunch.ts +151 -23
  434. package/src/utils/diagnosticContext.ts +17 -17
  435. package/src/utils/hardware/hardware.ts +7 -9
  436. package/src/utils/miniappDiagnostics.ts +226 -0
  437. package/src/utils/permissions/MediaLibraryPermissions.ts +35 -4
  438. package/src/utils/timers/timers.ts +57 -21
  439. package/build/services/asgOtaVersionUrl.d.ts +0 -2
  440. package/build/services/asgOtaVersionUrl.d.ts.map +0 -1
  441. package/build/services/asgOtaVersionUrl.js.map +0 -1
@@ -5,12 +5,15 @@
5
5
  import * as RNFS from "@dr.pogodin/react-native-fs";
6
6
  import { BgTimer } from "../../utils/timers";
7
7
  import { storage, StorageValueNotFoundError } from "../../utils/storage";
8
+ import { galleryTransferLedger } from "./galleryTransferLedger";
9
+ import { toPortableGalleryPath } from "./galleryPathMigration";
8
10
  export class LocalStorageService {
9
11
  static instance;
10
12
  DOWNLOADED_FILES_KEY = "asg_downloaded_files";
11
13
  SYNC_STATE_KEY = "asg_sync_state";
12
14
  CLIENT_ID_KEY = "asg_client_id";
13
15
  SYNC_QUEUE_KEY = "asg_sync_queue";
16
+ QUARANTINED_FILES_KEY = "asg_quarantined_downloaded_files_v1";
14
17
  ASG_PHOTOS_DIR = `${RNFS.DocumentDirectoryPath}/MentraPhotos`;
15
18
  ASG_THUMBNAILS_DIR = `${RNFS.DocumentDirectoryPath}/MentraPhotos/thumbnails`;
16
19
  constructor() {
@@ -112,7 +115,22 @@ export class LocalStorageService {
112
115
  */
113
116
  async saveDownloadedFile(file) {
114
117
  try {
115
- const files = await this.getDownloadedFiles();
118
+ const rawFiles = this.loadRawDownloadedFiles();
119
+ const files = {};
120
+ const quarantined = this.loadQuarantinedFiles();
121
+ const missingCaptureIds = [];
122
+ for (const [name, existing] of Object.entries(rawFiles)) {
123
+ const existingPath = this.toPortableRelativePath(existing.filePath);
124
+ const existingThumbnail = existing.thumbnailPath
125
+ ? this.toPortableRelativePath(existing.thumbnailPath) || undefined
126
+ : undefined;
127
+ if (!existingPath) {
128
+ quarantined[name] = existing;
129
+ missingCaptureIds.push(name);
130
+ continue;
131
+ }
132
+ files[name] = { ...existing, filePath: existingPath, thumbnailPath: existingThumbnail };
133
+ }
116
134
  // 🔍 DIAGNOSTIC: Check if file already exists
117
135
  // const existingFile = files[file.name]
118
136
  // if (existingFile) {
@@ -133,22 +151,28 @@ export class LocalStorageService {
133
151
  // }
134
152
  // Store relative paths to handle iOS app container changes between launches
135
153
  // Convert absolute paths to relative (remove DocumentDirectoryPath prefix)
136
- const docPath = RNFS.DocumentDirectoryPath;
137
- const relativePath = file.filePath.startsWith(docPath)
138
- ? file.filePath.substring(docPath.length + 1) // +1 for the slash
139
- : file.filePath;
154
+ const relativePath = this.toPortableRelativePath(file.filePath);
140
155
  const relativeThumbnailPath = file.thumbnailPath
141
- ? file.thumbnailPath.startsWith(docPath)
142
- ? file.thumbnailPath.substring(docPath.length + 1)
143
- : file.thumbnailPath
156
+ ? this.toPortableRelativePath(file.thumbnailPath) || undefined
144
157
  : undefined;
158
+ if (!relativePath) {
159
+ throw new Error(`Cannot persist non-portable gallery path: ${file.filePath}`);
160
+ }
145
161
  files[file.name] = {
146
162
  ...file,
147
163
  // Store relative paths instead of absolute
148
164
  filePath: relativePath,
149
165
  thumbnailPath: relativeThumbnailPath,
150
166
  };
151
- await storage.save(this.DOWNLOADED_FILES_KEY, files);
167
+ const saveResult = storage.save(this.DOWNLOADED_FILES_KEY, files);
168
+ if (saveResult.is_error())
169
+ throw saveResult.error;
170
+ const quarantineResult = storage.save(this.QUARANTINED_FILES_KEY, quarantined);
171
+ if (quarantineResult.is_error())
172
+ throw quarantineResult.error;
173
+ for (const captureId of missingCaptureIds) {
174
+ galleryTransferLedger.releaseMissingLocalCommit(captureId, true);
175
+ }
152
176
  // console.log(`[LocalStorage] 💾 Saved metadata for ${file.name} with relative path: ${relativePath}`)
153
177
  }
154
178
  catch (error) {
@@ -169,26 +193,109 @@ export class LocalStorageService {
169
193
  }
170
194
  const files = res.value;
171
195
  const reconstructedFiles = {};
172
- const docPath = RNFS.DocumentDirectoryPath;
173
- // Reconstruct absolute paths from relative paths
196
+ const normalizedFiles = {};
197
+ const quarantined = this.loadQuarantinedFiles();
198
+ const missingCaptureIds = [];
199
+ let didMigrate = false;
200
+ // Normalize every record before saving. This fixes the old read-absolute/write-one-relative
201
+ // bug and migrates stale iOS container UUID paths via their Documents/MentraPhotos suffix.
174
202
  for (const [name, file] of Object.entries(files)) {
175
- // Check if path is already absolute (legacy data) or relative
176
- const absolutePath = file.filePath.startsWith("/")
177
- ? file.filePath // Already absolute (legacy data)
178
- : `${docPath}/${file.filePath}`; // Relative path, prepend DocumentDirectoryPath
179
- const absoluteThumbnailPath = file.thumbnailPath
180
- ? file.thumbnailPath.startsWith("/")
181
- ? file.thumbnailPath // Already absolute (legacy data)
182
- : `${docPath}/${file.thumbnailPath}` // Relative path
203
+ const relativePath = this.toPortableRelativePath(file.filePath);
204
+ const relativeThumbnailPath = file.thumbnailPath
205
+ ? this.toPortableRelativePath(file.thumbnailPath) || undefined
206
+ : undefined;
207
+ if (!relativePath) {
208
+ quarantined[name] = { ...file, downloaded_at: file.downloaded_at || Date.now() };
209
+ missingCaptureIds.push(name);
210
+ didMigrate = true;
211
+ continue;
212
+ }
213
+ const absolutePath = `${RNFS.DocumentDirectoryPath}/${relativePath}`;
214
+ const absoluteThumbnailPath = relativeThumbnailPath
215
+ ? `${RNFS.DocumentDirectoryPath}/${relativeThumbnailPath}`
183
216
  : undefined;
217
+ normalizedFiles[name] = {
218
+ ...file,
219
+ filePath: relativePath,
220
+ thumbnailPath: relativeThumbnailPath,
221
+ };
184
222
  reconstructedFiles[name] = {
185
223
  ...file,
186
224
  filePath: absolutePath,
187
225
  thumbnailPath: absoluteThumbnailPath,
188
226
  };
227
+ if (file.filePath !== relativePath || file.thumbnailPath !== relativeThumbnailPath)
228
+ didMigrate = true;
229
+ }
230
+ if (didMigrate) {
231
+ const saveResult = storage.save(this.DOWNLOADED_FILES_KEY, normalizedFiles);
232
+ if (saveResult.is_error())
233
+ throw saveResult.error;
234
+ const quarantineResult = storage.save(this.QUARANTINED_FILES_KEY, quarantined);
235
+ if (quarantineResult.is_error())
236
+ throw quarantineResult.error;
237
+ for (const captureId of missingCaptureIds) {
238
+ galleryTransferLedger.releaseMissingLocalCommit(captureId, true);
239
+ }
189
240
  }
190
241
  return reconstructedFiles;
191
242
  }
243
+ /**
244
+ * Reconcile the remote manifest against the app-local gallery index before ledger filtering.
245
+ * Android backup/restore and data-preserving reinstalls can restore MMKV ledger rows without
246
+ * restoring app-private media. Only captures currently advertised by the glasses are released,
247
+ * so acknowledged captures that remain in glasses trash are not resurrected unnecessarily.
248
+ */
249
+ async reconcileRemoteCaptures(captureIds, downloadedFiles) {
250
+ const localFiles = downloadedFiles || (await this.getDownloadedFiles());
251
+ let released = 0;
252
+ for (const captureId of captureIds) {
253
+ const entry = galleryTransferLedger.get(captureId);
254
+ if (!entry || !galleryTransferLedger.isLocallyCommitted(captureId))
255
+ continue;
256
+ const localFile = localFiles[captureId];
257
+ const localFileExists = localFile?.filePath ? await RNFS.exists(localFile.filePath).catch(() => false) : false;
258
+ if (localFileExists)
259
+ continue;
260
+ // The transfer ledger and gallery index are persisted independently. A crash can leave the
261
+ // committed media in place after its index write was lost, so repair the index from the
262
+ // ledger before deciding that the capture needs to be restored from the glasses.
263
+ const committedPath = entry.finalPath?.replace(/^file:\/\//, "");
264
+ const committedFileExists = committedPath ? await RNFS.exists(committedPath).catch(() => false) : false;
265
+ if (committedPath && committedFileExists) {
266
+ try {
267
+ const stat = await RNFS.stat(committedPath);
268
+ const size = Number(stat.size);
269
+ if (Number.isFinite(size) && size > 0) {
270
+ const recovered = this.convertToDownloadedFile({
271
+ name: captureId,
272
+ url: "",
273
+ download: "",
274
+ size,
275
+ modified: entry.timestamp,
276
+ mime_type: entry.captureType === "video" ? "video/mp4" : "image/jpeg",
277
+ is_video: entry.captureType === "video",
278
+ filePath: committedPath,
279
+ }, committedPath, entry.thumbnailPath);
280
+ recovered.capture_id = captureId;
281
+ recovered.assetReceipt = entry.assetReceipt;
282
+ await this.saveDownloadedFile(recovered);
283
+ localFiles[captureId] = recovered;
284
+ continue;
285
+ }
286
+ }
287
+ catch (error) {
288
+ console.warn(`[LocalStorage] Could not recover gallery index for ${captureId}:`, error);
289
+ }
290
+ }
291
+ galleryTransferLedger.releaseMissingLocalCommit(captureId, true);
292
+ released++;
293
+ }
294
+ if (released > 0) {
295
+ console.warn(`[LocalStorage] Released ${released} stale gallery ledger commit(s) missing local media`);
296
+ }
297
+ return released;
298
+ }
192
299
  /**
193
300
  * Get downloaded file by name
194
301
  */
@@ -202,6 +309,14 @@ export class LocalStorageService {
202
309
  return null;
203
310
  }
204
311
  }
312
+ /** Persist a durable camera-roll receipt without replacing the local-file generation. */
313
+ async updateDownloadedFileAssetReceipt(fileName, assetReceipt) {
314
+ const files = await this.getDownloadedFiles();
315
+ const file = files[fileName];
316
+ if (!file)
317
+ throw new Error(`Downloaded file is missing: ${fileName}`);
318
+ await this.saveDownloadedFile({ ...file, assetReceipt });
319
+ }
205
320
  /**
206
321
  * Delete downloaded file (both metadata and actual files)
207
322
  */
@@ -229,6 +344,7 @@ export class LocalStorageService {
229
344
  const rawFiles = res.value;
230
345
  delete rawFiles[fileName];
231
346
  await storage.save(this.DOWNLOADED_FILES_KEY, rawFiles);
347
+ galleryTransferLedger.releaseMissingLocalCommit(fileName);
232
348
  return true;
233
349
  }
234
350
  return false;
@@ -238,6 +354,26 @@ export class LocalStorageService {
238
354
  return false;
239
355
  }
240
356
  }
357
+ /** Preserve unresolved metadata for repair instead of silently deleting it from the gallery. */
358
+ async quarantineDownloadedFile(fileName, reason) {
359
+ const rawFiles = this.loadRawDownloadedFiles();
360
+ const file = rawFiles[fileName];
361
+ if (!file)
362
+ return;
363
+ const quarantined = this.loadQuarantinedFiles();
364
+ quarantined[fileName] = { ...file, quarantineReason: reason };
365
+ delete rawFiles[fileName];
366
+ const quarantineResult = storage.save(this.QUARANTINED_FILES_KEY, quarantined);
367
+ if (quarantineResult.is_error())
368
+ throw quarantineResult.error;
369
+ const filesResult = storage.save(this.DOWNLOADED_FILES_KEY, rawFiles);
370
+ if (filesResult.is_error())
371
+ throw filesResult.error;
372
+ galleryTransferLedger.releaseMissingLocalCommit(fileName, true);
373
+ }
374
+ async getQuarantinedDownloadedFiles() {
375
+ return this.loadQuarantinedFiles();
376
+ }
241
377
  /**
242
378
  * Convert PhotoInfo to DownloadedFile (assumes files are already saved to filesystem)
243
379
  */
@@ -335,6 +471,8 @@ export class LocalStorageService {
335
471
  console.error("[LocalStorage] Error clearing downloaded files:", res.error);
336
472
  throw res.error;
337
473
  }
474
+ storage.remove(this.QUARANTINED_FILES_KEY);
475
+ galleryTransferLedger.releaseAllMissingLocalCommits();
338
476
  console.log("[LocalStorage] Cleared all downloaded files and thumbnails");
339
477
  }
340
478
  // ============================================
@@ -418,6 +556,17 @@ export class LocalStorageService {
418
556
  // Has resumable queue if there are still files to process
419
557
  return queue.currentIndex < queue.files.length;
420
558
  }
559
+ loadRawDownloadedFiles() {
560
+ const result = storage.load(this.DOWNLOADED_FILES_KEY);
561
+ return result.is_error() ? {} : result.value;
562
+ }
563
+ loadQuarantinedFiles() {
564
+ const result = storage.load(this.QUARANTINED_FILES_KEY);
565
+ return result.is_error() ? {} : result.value;
566
+ }
567
+ toPortableRelativePath(path) {
568
+ return toPortableGalleryPath(path, RNFS.DocumentDirectoryPath);
569
+ }
421
570
  }
422
571
  export const localStorageService = LocalStorageService.getInstance();
423
572
  //# sourceMappingURL=localStorageService.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"localStorageService.js","sourceRoot":"","sources":["../../../src/services/asg/localStorageService.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Local Storage Service for ASG Gallery\n * Manages downloaded files and sync state\n */\n\nimport * as RNFS from \"@dr.pogodin/react-native-fs\"\n\nimport {PhotoInfo} from \"../../types/asg\"\nimport {BgTimer} from \"../../utils/timers\"\nimport {storage, StorageValueNotFoundError} from \"../../utils/storage\"\n\nexport interface DownloadedFile {\n name: string\n filePath: string // Path to file on filesystem\n size: number\n modified: number\n mime_type: string\n is_video: boolean\n thumbnailPath?: string // Path to thumbnail file\n downloaded_at: number\n glassesModel?: string // Model of glasses that captured this media\n duration?: number // Video duration in milliseconds\n capture_id?: string // Capture folder name (when synced via capture-aware pipeline)\n}\n\ninterface SyncState {\n last_sync_time: number\n client_id: string\n total_downloaded: number\n total_size: number\n}\n\nexport interface SyncQueueData {\n files: PhotoInfo[]\n currentIndex: number\n startedAt: number\n hotspotInfo: {\n ssid: string\n password: string\n ip: string\n }\n}\n\nexport class LocalStorageService {\n private static instance: LocalStorageService\n private readonly DOWNLOADED_FILES_KEY = \"asg_downloaded_files\"\n private readonly SYNC_STATE_KEY = \"asg_sync_state\"\n private readonly CLIENT_ID_KEY = \"asg_client_id\"\n private readonly SYNC_QUEUE_KEY = \"asg_sync_queue\"\n private readonly ASG_PHOTOS_DIR = `${RNFS.DocumentDirectoryPath}/MentraPhotos`\n private readonly ASG_THUMBNAILS_DIR = `${RNFS.DocumentDirectoryPath}/MentraPhotos/thumbnails`\n\n private constructor() {\n this.initializeDirectories()\n }\n\n static getInstance(): LocalStorageService {\n if (!LocalStorageService.instance) {\n LocalStorageService.instance = new LocalStorageService()\n }\n return LocalStorageService.instance\n }\n\n /**\n * Initialize ASG photo directories\n */\n private async initializeDirectories(): Promise<void> {\n try {\n // Create main photos directory if it doesn't exist\n const photoDirExists = await RNFS.exists(this.ASG_PHOTOS_DIR)\n if (!photoDirExists) {\n await RNFS.mkdir(this.ASG_PHOTOS_DIR)\n console.log(`[LocalStorage] Created Mentra photos directory: ${this.ASG_PHOTOS_DIR}`)\n }\n\n // Create thumbnails directory if it doesn't exist\n const thumbDirExists = await RNFS.exists(this.ASG_THUMBNAILS_DIR)\n if (!thumbDirExists) {\n await RNFS.mkdir(this.ASG_THUMBNAILS_DIR)\n console.log(`[LocalStorage] Created Mentra thumbnails directory: ${this.ASG_THUMBNAILS_DIR}`)\n }\n } catch (error) {\n console.error(\"[LocalStorage] Error initializing directories:\", error)\n }\n }\n\n /**\n * Get the full file path for a photo\n */\n getPhotoFilePath(filename: string): string {\n return `${this.ASG_PHOTOS_DIR}/${filename}`\n }\n\n /**\n * Get the full file path for a thumbnail\n */\n getThumbnailFilePath(filename: string): string {\n return `${this.ASG_THUMBNAILS_DIR}/${filename}_thumb.jpg`\n }\n\n /**\n * Initialize client ID if not exists\n */\n async initializeClientId(): Promise<string> {\n let res = storage.load<string>(this.CLIENT_ID_KEY)\n let clientId\n if (res.is_error()) {\n clientId = `mobile_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`\n storage.save(this.CLIENT_ID_KEY, clientId)\n } else {\n clientId = res.value\n }\n return clientId\n }\n\n /**\n * Get current sync state\n */\n async getSyncState(): Promise<SyncState> {\n const clientId = await this.initializeClientId()\n const res = storage.load<SyncState>(this.SYNC_STATE_KEY)\n\n if (res.is_error()) {\n // A missing key is the expected first-sync state (fresh install / never\n // synced) — start from scratch quietly. Real storage failures still log.\n if (!(res.error instanceof StorageValueNotFoundError)) {\n console.error(\"Error getting sync state:\", res.error)\n }\n return {\n last_sync_time: 0,\n client_id: clientId,\n total_downloaded: 0,\n total_size: 0,\n }\n }\n\n const syncState = res.value\n return {...syncState, client_id: clientId}\n }\n\n /**\n * Update sync state\n */\n async updateSyncState(updates: Partial<SyncState>): Promise<void> {\n try {\n const currentState = await this.getSyncState()\n const newState = {...currentState, ...updates}\n await storage.save(this.SYNC_STATE_KEY, newState)\n } catch (error) {\n console.error(\"Error updating sync state:\", error)\n throw error\n }\n }\n\n /**\n * Save downloaded file (only metadata, file should already be written to filesystem)\n */\n async saveDownloadedFile(file: DownloadedFile): Promise<void> {\n try {\n const files = await this.getDownloadedFiles()\n\n // 🔍 DIAGNOSTIC: Check if file already exists\n // const existingFile = files[file.name]\n // if (existingFile) {\n // console.log(`[LocalStorage] ⚠️ WARNING: File ${file.name} ALREADY EXISTS - will be REPLACED!`)\n // console.log(`[LocalStorage] 📁 Existing file:`)\n // console.log(`[LocalStorage] - Downloaded at: ${new Date(existingFile.downloaded_at).toISOString()}`)\n // console.log(`[LocalStorage] - Modified: ${new Date(existingFile.modified).toISOString()}`)\n // console.log(`[LocalStorage] - Size: ${existingFile.size} bytes`)\n // console.log(`[LocalStorage] - Path: ${existingFile.filePath}`)\n // console.log(`[LocalStorage] 📁 New file:`)\n // console.log(`[LocalStorage] - Downloaded at: ${new Date(file.downloaded_at).toISOString()}`)\n // console.log(`[LocalStorage] - Modified: ${new Date(file.modified).toISOString()}`)\n // console.log(`[LocalStorage] - Size: ${file.size} bytes`)\n // console.log(`[LocalStorage] - Path: ${file.filePath}`)\n // console.log(`[LocalStorage] 🔄 This will OVERWRITE the existing entry!`)\n // } else {\n // console.log(`[LocalStorage] ✅ File ${file.name} is NEW - saving metadata`)\n // }\n\n // Store relative paths to handle iOS app container changes between launches\n // Convert absolute paths to relative (remove DocumentDirectoryPath prefix)\n const docPath = RNFS.DocumentDirectoryPath\n const relativePath = file.filePath.startsWith(docPath)\n ? file.filePath.substring(docPath.length + 1) // +1 for the slash\n : file.filePath\n const relativeThumbnailPath = file.thumbnailPath\n ? file.thumbnailPath.startsWith(docPath)\n ? file.thumbnailPath.substring(docPath.length + 1)\n : file.thumbnailPath\n : undefined\n\n files[file.name] = {\n ...file,\n // Store relative paths instead of absolute\n filePath: relativePath,\n thumbnailPath: relativeThumbnailPath,\n }\n await storage.save(this.DOWNLOADED_FILES_KEY, files)\n // console.log(`[LocalStorage] 💾 Saved metadata for ${file.name} with relative path: ${relativePath}`)\n } catch (error) {\n console.error(\"Error saving downloaded file metadata:\", error)\n throw error\n }\n }\n\n /**\n * Get all downloaded files\n */\n async getDownloadedFiles(): Promise<Record<string, DownloadedFile>> {\n const res = storage.load<Record<string, DownloadedFile>>(this.DOWNLOADED_FILES_KEY)\n if (res.is_error()) {\n // Not an error - just means no files have been downloaded yet (first use)\n // Only log as debug, not error, since this is expected on first launch\n console.log(\"[LocalStorage] No downloaded files found (first use or empty gallery)\")\n return {}\n }\n\n const files = res.value\n const reconstructedFiles: Record<string, DownloadedFile> = {}\n const docPath = RNFS.DocumentDirectoryPath\n\n // Reconstruct absolute paths from relative paths\n for (const [name, file] of Object.entries(files as Record<string, DownloadedFile>)) {\n // Check if path is already absolute (legacy data) or relative\n const absolutePath = file.filePath.startsWith(\"/\")\n ? file.filePath // Already absolute (legacy data)\n : `${docPath}/${file.filePath}` // Relative path, prepend DocumentDirectoryPath\n\n const absoluteThumbnailPath = file.thumbnailPath\n ? file.thumbnailPath.startsWith(\"/\")\n ? file.thumbnailPath // Already absolute (legacy data)\n : `${docPath}/${file.thumbnailPath}` // Relative path\n : undefined\n\n reconstructedFiles[name] = {\n ...file,\n filePath: absolutePath,\n thumbnailPath: absoluteThumbnailPath,\n }\n }\n\n return reconstructedFiles\n }\n\n /**\n * Get downloaded file by name\n */\n async getDownloadedFile(fileName: string): Promise<DownloadedFile | null> {\n try {\n const files = await this.getDownloadedFiles()\n return files[fileName] || null\n } catch (error) {\n console.error(\"Error getting downloaded file:\", error)\n return null\n }\n }\n\n /**\n * Delete downloaded file (both metadata and actual files)\n */\n async deleteDownloadedFile(fileName: string): Promise<boolean> {\n try {\n const files = await this.getDownloadedFiles()\n if (files[fileName]) {\n const file = files[fileName]\n\n // Delete actual file from filesystem\n if (file.filePath && (await RNFS.exists(file.filePath))) {\n await RNFS.unlink(file.filePath)\n console.log(`[LocalStorage] Deleted file: ${file.filePath}`)\n }\n\n // Delete thumbnail if exists\n if (file.thumbnailPath && (await RNFS.exists(file.thumbnailPath))) {\n await RNFS.unlink(file.thumbnailPath)\n console.log(`[LocalStorage] Deleted thumbnail: ${file.thumbnailPath}`)\n }\n\n // Delete metadata - need to get raw data to maintain relative paths\n const res = storage.load<Record<string, DownloadedFile>>(this.DOWNLOADED_FILES_KEY)\n if (res.is_error()) {\n console.error(\"Error loading downloaded files:\", res.error)\n return false\n }\n const rawFiles = res.value\n delete rawFiles[fileName]\n await storage.save(this.DOWNLOADED_FILES_KEY, rawFiles)\n return true\n }\n return false\n } catch (error) {\n console.error(\"Error deleting downloaded file:\", error)\n return false\n }\n }\n\n /**\n * Convert PhotoInfo to DownloadedFile (assumes files are already saved to filesystem)\n */\n convertToDownloadedFile(\n photoInfo: PhotoInfo,\n filePath: string,\n thumbnailPath?: string,\n glassesModel?: string,\n ): DownloadedFile {\n return {\n name: photoInfo.name,\n filePath: filePath,\n size: photoInfo.size,\n modified: new Date(photoInfo.modified).getTime(),\n mime_type: photoInfo.mime_type || (photoInfo.is_video ? \"video/mp4\" : \"image/jpeg\"),\n is_video: photoInfo.is_video || false,\n thumbnailPath: thumbnailPath,\n downloaded_at: Date.now(),\n glassesModel: glassesModel || photoInfo.glassesModel,\n duration: photoInfo.duration,\n }\n }\n\n /**\n * Convert DownloadedFile to PhotoInfo\n */\n convertToPhotoInfo(downloadedFile: DownloadedFile): PhotoInfo {\n // Use file:// URLs for local files\n // On iOS, ensure we have proper file:// URL format with correct number of slashes\n const fileUrl = downloadedFile.filePath.startsWith(\"file://\")\n ? downloadedFile.filePath\n : downloadedFile.filePath.startsWith(\"/\")\n ? `file://${downloadedFile.filePath}` // Path already has leading slash\n : `file:///${downloadedFile.filePath}` // Path needs leading slash\n\n const thumbnailUrl = downloadedFile.thumbnailPath\n ? downloadedFile.thumbnailPath.startsWith(\"file://\")\n ? downloadedFile.thumbnailPath\n : downloadedFile.thumbnailPath.startsWith(\"/\")\n ? `file://${downloadedFile.thumbnailPath}` // Path already has leading slash\n : `file:///${downloadedFile.thumbnailPath}` // Path needs leading slash\n : undefined\n\n return {\n name: downloadedFile.name,\n url: fileUrl,\n download: fileUrl,\n size: downloadedFile.size,\n modified: new Date(downloadedFile.modified).toISOString(),\n mime_type: downloadedFile.mime_type,\n is_video: downloadedFile.is_video,\n thumbnail_data: undefined,\n downloaded_at: downloadedFile.downloaded_at,\n filePath: downloadedFile.filePath,\n glassesModel: downloadedFile.glassesModel,\n thumbnailPath: thumbnailUrl, // Use the file:// URL version for thumbnailPath\n duration: downloadedFile.duration,\n }\n }\n\n /**\n * Get storage statistics\n */\n async getStorageStats(): Promise<{\n total_files: number\n total_size: number\n last_sync: number\n }> {\n try {\n const files = await this.getDownloadedFiles()\n const syncState = await this.getSyncState()\n\n const totalSize = Object.values(files).reduce((sum, file) => sum + file.size, 0)\n\n return {\n total_files: Object.keys(files).length,\n total_size: totalSize,\n last_sync: syncState.last_sync_time,\n }\n } catch (error) {\n console.error(\"Error getting storage stats:\", error)\n return {\n total_files: 0,\n total_size: 0,\n last_sync: 0,\n }\n }\n }\n\n /**\n * Clear all downloaded files (both metadata and actual files)\n */\n async clearAllFiles(): Promise<void> {\n // Nuke the entire photos directory (includes thumbnails subdirectory)\n // This is more reliable than deleting individual files, which can miss orphaned thumbnails\n try {\n if (await RNFS.exists(this.ASG_PHOTOS_DIR)) {\n await RNFS.unlink(this.ASG_PHOTOS_DIR)\n console.log(\"[LocalStorage] Deleted entire photos directory\")\n }\n } catch (error) {\n console.error(\"[LocalStorage] Error deleting photos directory:\", error)\n }\n\n // Recreate empty directories\n await this.initializeDirectories()\n\n // Clear metadata\n const res = await storage.remove(this.DOWNLOADED_FILES_KEY)\n if (res.is_error()) {\n console.error(\"[LocalStorage] Error clearing downloaded files:\", res.error)\n throw res.error\n }\n console.log(\"[LocalStorage] Cleared all downloaded files and thumbnails\")\n }\n\n // ============================================\n // Sync Queue Persistence (for background sync resume)\n // ============================================\n\n /**\n * Save sync queue for potential resume after app restart\n */\n async saveSyncQueue(queue: SyncQueueData): Promise<void> {\n try {\n await storage.save(this.SYNC_QUEUE_KEY, queue)\n console.log(`[LocalStorage] Saved sync queue: ${queue.files.length} files, index ${queue.currentIndex}`)\n } catch (error) {\n console.error(\"[LocalStorage] Error saving sync queue:\", error)\n throw error\n }\n }\n\n /**\n * Get saved sync queue (for resume on app restart)\n */\n async getSyncQueue(): Promise<SyncQueueData | null> {\n try {\n const res = storage.load<SyncQueueData>(this.SYNC_QUEUE_KEY)\n if (res.is_error()) {\n // No queue saved - this is normal\n return null\n }\n const queue = res.value\n console.log(`[LocalStorage] Loaded sync queue: ${queue.files.length} files, index ${queue.currentIndex}`)\n return queue\n } catch (error) {\n console.error(\"[LocalStorage] Error loading sync queue:\", error)\n return null\n }\n }\n\n /**\n * Update the current index of the sync queue (called after each file completes)\n */\n async updateSyncQueueIndex(newIndex: number): Promise<void> {\n // S6: Let errors propagate — caller uses .catch() for non-critical updates\n const queue = await this.getSyncQueue()\n if (queue) {\n queue.currentIndex = newIndex\n await this.saveSyncQueue(queue)\n }\n }\n\n /**\n * Clear sync queue (called on sync complete or cancel)\n */\n async clearSyncQueue(): Promise<void> {\n // S6: Retry up to 3 times to ensure queue is cleared\n for (let attempt = 1; attempt <= 3; attempt++) {\n try {\n const res = await storage.remove(this.SYNC_QUEUE_KEY)\n if (res.is_error()) {\n console.error(`[LocalStorage] Error clearing sync queue (attempt ${attempt}/3):`, res.error)\n if (attempt < 3) continue\n return\n }\n console.log(\"[LocalStorage] Cleared sync queue\")\n return\n } catch (error) {\n console.error(`[LocalStorage] Error clearing sync queue (attempt ${attempt}/3):`, error)\n if (attempt < 3) {\n await new Promise<void>((resolve) => BgTimer.setTimeout(() => resolve(), 100))\n }\n }\n }\n }\n\n /**\n * Check if there's a resumable sync queue\n */\n async hasResumableSyncQueue(): Promise<boolean> {\n const queue = await this.getSyncQueue()\n if (!queue) return false\n // Has resumable queue if there are still files to process\n return queue.currentIndex < queue.files.length\n }\n}\n\nexport const localStorageService = LocalStorageService.getInstance()\n"]}
1
+ {"version":3,"file":"localStorageService.js","sourceRoot":"","sources":["../../../src/services/asg/localStorageService.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Local Storage Service for ASG Gallery\n * Manages downloaded files and sync state\n */\n\nimport * as RNFS from \"@dr.pogodin/react-native-fs\"\n\nimport {PhotoInfo} from \"../../types/asg\"\nimport {BgTimer} from \"../../utils/timers\"\nimport {storage, StorageValueNotFoundError} from \"../../utils/storage\"\nimport {galleryTransferLedger, GalleryAssetReceipt} from \"./galleryTransferLedger\"\nimport {toPortableGalleryPath} from \"./galleryPathMigration\"\n\nexport interface DownloadedFile {\n name: string\n filePath: string // Path to file on filesystem\n size: number\n modified: number\n mime_type: string\n is_video: boolean\n thumbnailPath?: string // Path to thumbnail file\n downloaded_at: number\n glassesModel?: string // Model of glasses that captured this media\n duration?: number // Video duration in milliseconds\n capture_id?: string // Capture folder name (when synced via capture-aware pipeline)\n assetReceipt?: GalleryAssetReceipt\n}\n\ninterface SyncState {\n last_sync_time: number\n client_id: string\n total_downloaded: number\n total_size: number\n}\n\nexport interface SyncQueueData {\n files: PhotoInfo[]\n currentIndex: number\n startedAt: number\n hotspotInfo: {\n ssid: string\n password: string\n ip: string\n }\n}\n\nexport class LocalStorageService {\n private static instance: LocalStorageService\n private readonly DOWNLOADED_FILES_KEY = \"asg_downloaded_files\"\n private readonly SYNC_STATE_KEY = \"asg_sync_state\"\n private readonly CLIENT_ID_KEY = \"asg_client_id\"\n private readonly SYNC_QUEUE_KEY = \"asg_sync_queue\"\n private readonly QUARANTINED_FILES_KEY = \"asg_quarantined_downloaded_files_v1\"\n private readonly ASG_PHOTOS_DIR = `${RNFS.DocumentDirectoryPath}/MentraPhotos`\n private readonly ASG_THUMBNAILS_DIR = `${RNFS.DocumentDirectoryPath}/MentraPhotos/thumbnails`\n\n private constructor() {\n this.initializeDirectories()\n }\n\n static getInstance(): LocalStorageService {\n if (!LocalStorageService.instance) {\n LocalStorageService.instance = new LocalStorageService()\n }\n return LocalStorageService.instance\n }\n\n /**\n * Initialize ASG photo directories\n */\n private async initializeDirectories(): Promise<void> {\n try {\n // Create main photos directory if it doesn't exist\n const photoDirExists = await RNFS.exists(this.ASG_PHOTOS_DIR)\n if (!photoDirExists) {\n await RNFS.mkdir(this.ASG_PHOTOS_DIR)\n console.log(`[LocalStorage] Created Mentra photos directory: ${this.ASG_PHOTOS_DIR}`)\n }\n\n // Create thumbnails directory if it doesn't exist\n const thumbDirExists = await RNFS.exists(this.ASG_THUMBNAILS_DIR)\n if (!thumbDirExists) {\n await RNFS.mkdir(this.ASG_THUMBNAILS_DIR)\n console.log(`[LocalStorage] Created Mentra thumbnails directory: ${this.ASG_THUMBNAILS_DIR}`)\n }\n } catch (error) {\n console.error(\"[LocalStorage] Error initializing directories:\", error)\n }\n }\n\n /**\n * Get the full file path for a photo\n */\n getPhotoFilePath(filename: string): string {\n return `${this.ASG_PHOTOS_DIR}/${filename}`\n }\n\n /**\n * Get the full file path for a thumbnail\n */\n getThumbnailFilePath(filename: string): string {\n return `${this.ASG_THUMBNAILS_DIR}/${filename}_thumb.jpg`\n }\n\n /**\n * Initialize client ID if not exists\n */\n async initializeClientId(): Promise<string> {\n let res = storage.load<string>(this.CLIENT_ID_KEY)\n let clientId\n if (res.is_error()) {\n clientId = `mobile_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`\n storage.save(this.CLIENT_ID_KEY, clientId)\n } else {\n clientId = res.value\n }\n return clientId\n }\n\n /**\n * Get current sync state\n */\n async getSyncState(): Promise<SyncState> {\n const clientId = await this.initializeClientId()\n const res = storage.load<SyncState>(this.SYNC_STATE_KEY)\n\n if (res.is_error()) {\n // A missing key is the expected first-sync state (fresh install / never\n // synced) — start from scratch quietly. Real storage failures still log.\n if (!(res.error instanceof StorageValueNotFoundError)) {\n console.error(\"Error getting sync state:\", res.error)\n }\n return {\n last_sync_time: 0,\n client_id: clientId,\n total_downloaded: 0,\n total_size: 0,\n }\n }\n\n const syncState = res.value\n return {...syncState, client_id: clientId}\n }\n\n /**\n * Update sync state\n */\n async updateSyncState(updates: Partial<SyncState>): Promise<void> {\n try {\n const currentState = await this.getSyncState()\n const newState = {...currentState, ...updates}\n await storage.save(this.SYNC_STATE_KEY, newState)\n } catch (error) {\n console.error(\"Error updating sync state:\", error)\n throw error\n }\n }\n\n /**\n * Save downloaded file (only metadata, file should already be written to filesystem)\n */\n async saveDownloadedFile(file: DownloadedFile): Promise<void> {\n try {\n const rawFiles = this.loadRawDownloadedFiles()\n const files: Record<string, DownloadedFile> = {}\n const quarantined = this.loadQuarantinedFiles()\n const missingCaptureIds: string[] = []\n for (const [name, existing] of Object.entries(rawFiles)) {\n const existingPath = this.toPortableRelativePath(existing.filePath)\n const existingThumbnail = existing.thumbnailPath\n ? this.toPortableRelativePath(existing.thumbnailPath) || undefined\n : undefined\n if (!existingPath) {\n quarantined[name] = existing\n missingCaptureIds.push(name)\n continue\n }\n files[name] = {...existing, filePath: existingPath, thumbnailPath: existingThumbnail}\n }\n\n // 🔍 DIAGNOSTIC: Check if file already exists\n // const existingFile = files[file.name]\n // if (existingFile) {\n // console.log(`[LocalStorage] ⚠️ WARNING: File ${file.name} ALREADY EXISTS - will be REPLACED!`)\n // console.log(`[LocalStorage] 📁 Existing file:`)\n // console.log(`[LocalStorage] - Downloaded at: ${new Date(existingFile.downloaded_at).toISOString()}`)\n // console.log(`[LocalStorage] - Modified: ${new Date(existingFile.modified).toISOString()}`)\n // console.log(`[LocalStorage] - Size: ${existingFile.size} bytes`)\n // console.log(`[LocalStorage] - Path: ${existingFile.filePath}`)\n // console.log(`[LocalStorage] 📁 New file:`)\n // console.log(`[LocalStorage] - Downloaded at: ${new Date(file.downloaded_at).toISOString()}`)\n // console.log(`[LocalStorage] - Modified: ${new Date(file.modified).toISOString()}`)\n // console.log(`[LocalStorage] - Size: ${file.size} bytes`)\n // console.log(`[LocalStorage] - Path: ${file.filePath}`)\n // console.log(`[LocalStorage] 🔄 This will OVERWRITE the existing entry!`)\n // } else {\n // console.log(`[LocalStorage] ✅ File ${file.name} is NEW - saving metadata`)\n // }\n\n // Store relative paths to handle iOS app container changes between launches\n // Convert absolute paths to relative (remove DocumentDirectoryPath prefix)\n const relativePath = this.toPortableRelativePath(file.filePath)\n const relativeThumbnailPath = file.thumbnailPath\n ? this.toPortableRelativePath(file.thumbnailPath) || undefined\n : undefined\n\n if (!relativePath) {\n throw new Error(`Cannot persist non-portable gallery path: ${file.filePath}`)\n }\n\n files[file.name] = {\n ...file,\n // Store relative paths instead of absolute\n filePath: relativePath,\n thumbnailPath: relativeThumbnailPath,\n }\n const saveResult = storage.save(this.DOWNLOADED_FILES_KEY, files)\n if (saveResult.is_error()) throw saveResult.error\n const quarantineResult = storage.save(this.QUARANTINED_FILES_KEY, quarantined)\n if (quarantineResult.is_error()) throw quarantineResult.error\n for (const captureId of missingCaptureIds) {\n galleryTransferLedger.releaseMissingLocalCommit(captureId, true)\n }\n // console.log(`[LocalStorage] 💾 Saved metadata for ${file.name} with relative path: ${relativePath}`)\n } catch (error) {\n console.error(\"Error saving downloaded file metadata:\", error)\n throw error\n }\n }\n\n /**\n * Get all downloaded files\n */\n async getDownloadedFiles(): Promise<Record<string, DownloadedFile>> {\n const res = storage.load<Record<string, DownloadedFile>>(this.DOWNLOADED_FILES_KEY)\n if (res.is_error()) {\n // Not an error - just means no files have been downloaded yet (first use)\n // Only log as debug, not error, since this is expected on first launch\n console.log(\"[LocalStorage] No downloaded files found (first use or empty gallery)\")\n return {}\n }\n\n const files = res.value\n const reconstructedFiles: Record<string, DownloadedFile> = {}\n const normalizedFiles: Record<string, DownloadedFile> = {}\n const quarantined = this.loadQuarantinedFiles()\n const missingCaptureIds: string[] = []\n let didMigrate = false\n\n // Normalize every record before saving. This fixes the old read-absolute/write-one-relative\n // bug and migrates stale iOS container UUID paths via their Documents/MentraPhotos suffix.\n for (const [name, file] of Object.entries(files as Record<string, DownloadedFile>)) {\n const relativePath = this.toPortableRelativePath(file.filePath)\n const relativeThumbnailPath = file.thumbnailPath\n ? this.toPortableRelativePath(file.thumbnailPath) || undefined\n : undefined\n if (!relativePath) {\n quarantined[name] = {...file, downloaded_at: file.downloaded_at || Date.now()}\n missingCaptureIds.push(name)\n didMigrate = true\n continue\n }\n const absolutePath = `${RNFS.DocumentDirectoryPath}/${relativePath}`\n const absoluteThumbnailPath = relativeThumbnailPath\n ? `${RNFS.DocumentDirectoryPath}/${relativeThumbnailPath}`\n : undefined\n\n normalizedFiles[name] = {\n ...file,\n filePath: relativePath,\n thumbnailPath: relativeThumbnailPath,\n }\n\n reconstructedFiles[name] = {\n ...file,\n filePath: absolutePath,\n thumbnailPath: absoluteThumbnailPath,\n }\n if (file.filePath !== relativePath || file.thumbnailPath !== relativeThumbnailPath) didMigrate = true\n }\n\n if (didMigrate) {\n const saveResult = storage.save(this.DOWNLOADED_FILES_KEY, normalizedFiles)\n if (saveResult.is_error()) throw saveResult.error\n const quarantineResult = storage.save(this.QUARANTINED_FILES_KEY, quarantined)\n if (quarantineResult.is_error()) throw quarantineResult.error\n for (const captureId of missingCaptureIds) {\n galleryTransferLedger.releaseMissingLocalCommit(captureId, true)\n }\n }\n\n return reconstructedFiles\n }\n\n /**\n * Reconcile the remote manifest against the app-local gallery index before ledger filtering.\n * Android backup/restore and data-preserving reinstalls can restore MMKV ledger rows without\n * restoring app-private media. Only captures currently advertised by the glasses are released,\n * so acknowledged captures that remain in glasses trash are not resurrected unnecessarily.\n */\n async reconcileRemoteCaptures(\n captureIds: string[],\n downloadedFiles?: Record<string, DownloadedFile>,\n ): Promise<number> {\n const localFiles = downloadedFiles || (await this.getDownloadedFiles())\n let released = 0\n for (const captureId of captureIds) {\n const entry = galleryTransferLedger.get(captureId)\n if (!entry || !galleryTransferLedger.isLocallyCommitted(captureId)) continue\n const localFile = localFiles[captureId]\n const localFileExists = localFile?.filePath ? await RNFS.exists(localFile.filePath).catch(() => false) : false\n if (localFileExists) continue\n\n // The transfer ledger and gallery index are persisted independently. A crash can leave the\n // committed media in place after its index write was lost, so repair the index from the\n // ledger before deciding that the capture needs to be restored from the glasses.\n const committedPath = entry.finalPath?.replace(/^file:\\/\\//, \"\")\n const committedFileExists = committedPath ? await RNFS.exists(committedPath).catch(() => false) : false\n if (committedPath && committedFileExists) {\n try {\n const stat = await RNFS.stat(committedPath)\n const size = Number(stat.size)\n if (Number.isFinite(size) && size > 0) {\n const recovered = this.convertToDownloadedFile(\n {\n name: captureId,\n url: \"\",\n download: \"\",\n size,\n modified: entry.timestamp,\n mime_type: entry.captureType === \"video\" ? \"video/mp4\" : \"image/jpeg\",\n is_video: entry.captureType === \"video\",\n filePath: committedPath,\n },\n committedPath,\n entry.thumbnailPath,\n )\n recovered.capture_id = captureId\n recovered.assetReceipt = entry.assetReceipt\n await this.saveDownloadedFile(recovered)\n localFiles[captureId] = recovered\n continue\n }\n } catch (error) {\n console.warn(`[LocalStorage] Could not recover gallery index for ${captureId}:`, error)\n }\n }\n galleryTransferLedger.releaseMissingLocalCommit(captureId, true)\n released++\n }\n if (released > 0) {\n console.warn(`[LocalStorage] Released ${released} stale gallery ledger commit(s) missing local media`)\n }\n return released\n }\n\n /**\n * Get downloaded file by name\n */\n async getDownloadedFile(fileName: string): Promise<DownloadedFile | null> {\n try {\n const files = await this.getDownloadedFiles()\n return files[fileName] || null\n } catch (error) {\n console.error(\"Error getting downloaded file:\", error)\n return null\n }\n }\n\n /** Persist a durable camera-roll receipt without replacing the local-file generation. */\n async updateDownloadedFileAssetReceipt(fileName: string, assetReceipt: GalleryAssetReceipt): Promise<void> {\n const files = await this.getDownloadedFiles()\n const file = files[fileName]\n if (!file) throw new Error(`Downloaded file is missing: ${fileName}`)\n await this.saveDownloadedFile({...file, assetReceipt})\n }\n\n /**\n * Delete downloaded file (both metadata and actual files)\n */\n async deleteDownloadedFile(fileName: string): Promise<boolean> {\n try {\n const files = await this.getDownloadedFiles()\n if (files[fileName]) {\n const file = files[fileName]\n\n // Delete actual file from filesystem\n if (file.filePath && (await RNFS.exists(file.filePath))) {\n await RNFS.unlink(file.filePath)\n console.log(`[LocalStorage] Deleted file: ${file.filePath}`)\n }\n\n // Delete thumbnail if exists\n if (file.thumbnailPath && (await RNFS.exists(file.thumbnailPath))) {\n await RNFS.unlink(file.thumbnailPath)\n console.log(`[LocalStorage] Deleted thumbnail: ${file.thumbnailPath}`)\n }\n\n // Delete metadata - need to get raw data to maintain relative paths\n const res = storage.load<Record<string, DownloadedFile>>(this.DOWNLOADED_FILES_KEY)\n if (res.is_error()) {\n console.error(\"Error loading downloaded files:\", res.error)\n return false\n }\n const rawFiles = res.value\n delete rawFiles[fileName]\n await storage.save(this.DOWNLOADED_FILES_KEY, rawFiles)\n galleryTransferLedger.releaseMissingLocalCommit(fileName)\n return true\n }\n return false\n } catch (error) {\n console.error(\"Error deleting downloaded file:\", error)\n return false\n }\n }\n\n /** Preserve unresolved metadata for repair instead of silently deleting it from the gallery. */\n async quarantineDownloadedFile(fileName: string, reason: string): Promise<void> {\n const rawFiles = this.loadRawDownloadedFiles()\n const file = rawFiles[fileName]\n if (!file) return\n const quarantined = this.loadQuarantinedFiles()\n quarantined[fileName] = {...file, quarantineReason: reason} as DownloadedFile\n delete rawFiles[fileName]\n const quarantineResult = storage.save(this.QUARANTINED_FILES_KEY, quarantined)\n if (quarantineResult.is_error()) throw quarantineResult.error\n const filesResult = storage.save(this.DOWNLOADED_FILES_KEY, rawFiles)\n if (filesResult.is_error()) throw filesResult.error\n galleryTransferLedger.releaseMissingLocalCommit(fileName, true)\n }\n\n async getQuarantinedDownloadedFiles(): Promise<Record<string, DownloadedFile>> {\n return this.loadQuarantinedFiles()\n }\n\n /**\n * Convert PhotoInfo to DownloadedFile (assumes files are already saved to filesystem)\n */\n convertToDownloadedFile(\n photoInfo: PhotoInfo,\n filePath: string,\n thumbnailPath?: string,\n glassesModel?: string,\n ): DownloadedFile {\n return {\n name: photoInfo.name,\n filePath: filePath,\n size: photoInfo.size,\n modified: new Date(photoInfo.modified).getTime(),\n mime_type: photoInfo.mime_type || (photoInfo.is_video ? \"video/mp4\" : \"image/jpeg\"),\n is_video: photoInfo.is_video || false,\n thumbnailPath: thumbnailPath,\n downloaded_at: Date.now(),\n glassesModel: glassesModel || photoInfo.glassesModel,\n duration: photoInfo.duration,\n }\n }\n\n /**\n * Convert DownloadedFile to PhotoInfo\n */\n convertToPhotoInfo(downloadedFile: DownloadedFile): PhotoInfo {\n // Use file:// URLs for local files\n // On iOS, ensure we have proper file:// URL format with correct number of slashes\n const fileUrl = downloadedFile.filePath.startsWith(\"file://\")\n ? downloadedFile.filePath\n : downloadedFile.filePath.startsWith(\"/\")\n ? `file://${downloadedFile.filePath}` // Path already has leading slash\n : `file:///${downloadedFile.filePath}` // Path needs leading slash\n\n const thumbnailUrl = downloadedFile.thumbnailPath\n ? downloadedFile.thumbnailPath.startsWith(\"file://\")\n ? downloadedFile.thumbnailPath\n : downloadedFile.thumbnailPath.startsWith(\"/\")\n ? `file://${downloadedFile.thumbnailPath}` // Path already has leading slash\n : `file:///${downloadedFile.thumbnailPath}` // Path needs leading slash\n : undefined\n\n return {\n name: downloadedFile.name,\n url: fileUrl,\n download: fileUrl,\n size: downloadedFile.size,\n modified: new Date(downloadedFile.modified).toISOString(),\n mime_type: downloadedFile.mime_type,\n is_video: downloadedFile.is_video,\n thumbnail_data: undefined,\n downloaded_at: downloadedFile.downloaded_at,\n filePath: downloadedFile.filePath,\n glassesModel: downloadedFile.glassesModel,\n thumbnailPath: thumbnailUrl, // Use the file:// URL version for thumbnailPath\n duration: downloadedFile.duration,\n }\n }\n\n /**\n * Get storage statistics\n */\n async getStorageStats(): Promise<{\n total_files: number\n total_size: number\n last_sync: number\n }> {\n try {\n const files = await this.getDownloadedFiles()\n const syncState = await this.getSyncState()\n\n const totalSize = Object.values(files).reduce((sum, file) => sum + file.size, 0)\n\n return {\n total_files: Object.keys(files).length,\n total_size: totalSize,\n last_sync: syncState.last_sync_time,\n }\n } catch (error) {\n console.error(\"Error getting storage stats:\", error)\n return {\n total_files: 0,\n total_size: 0,\n last_sync: 0,\n }\n }\n }\n\n /**\n * Clear all downloaded files (both metadata and actual files)\n */\n async clearAllFiles(): Promise<void> {\n // Nuke the entire photos directory (includes thumbnails subdirectory)\n // This is more reliable than deleting individual files, which can miss orphaned thumbnails\n try {\n if (await RNFS.exists(this.ASG_PHOTOS_DIR)) {\n await RNFS.unlink(this.ASG_PHOTOS_DIR)\n console.log(\"[LocalStorage] Deleted entire photos directory\")\n }\n } catch (error) {\n console.error(\"[LocalStorage] Error deleting photos directory:\", error)\n }\n\n // Recreate empty directories\n await this.initializeDirectories()\n\n // Clear metadata\n const res = await storage.remove(this.DOWNLOADED_FILES_KEY)\n if (res.is_error()) {\n console.error(\"[LocalStorage] Error clearing downloaded files:\", res.error)\n throw res.error\n }\n storage.remove(this.QUARANTINED_FILES_KEY)\n galleryTransferLedger.releaseAllMissingLocalCommits()\n console.log(\"[LocalStorage] Cleared all downloaded files and thumbnails\")\n }\n\n // ============================================\n // Sync Queue Persistence (for background sync resume)\n // ============================================\n\n /**\n * Save sync queue for potential resume after app restart\n */\n async saveSyncQueue(queue: SyncQueueData): Promise<void> {\n try {\n await storage.save(this.SYNC_QUEUE_KEY, queue)\n console.log(`[LocalStorage] Saved sync queue: ${queue.files.length} files, index ${queue.currentIndex}`)\n } catch (error) {\n console.error(\"[LocalStorage] Error saving sync queue:\", error)\n throw error\n }\n }\n\n /**\n * Get saved sync queue (for resume on app restart)\n */\n async getSyncQueue(): Promise<SyncQueueData | null> {\n try {\n const res = storage.load<SyncQueueData>(this.SYNC_QUEUE_KEY)\n if (res.is_error()) {\n // No queue saved - this is normal\n return null\n }\n const queue = res.value\n console.log(`[LocalStorage] Loaded sync queue: ${queue.files.length} files, index ${queue.currentIndex}`)\n return queue\n } catch (error) {\n console.error(\"[LocalStorage] Error loading sync queue:\", error)\n return null\n }\n }\n\n /**\n * Update the current index of the sync queue (called after each file completes)\n */\n async updateSyncQueueIndex(newIndex: number): Promise<void> {\n // S6: Let errors propagate — caller uses .catch() for non-critical updates\n const queue = await this.getSyncQueue()\n if (queue) {\n queue.currentIndex = newIndex\n await this.saveSyncQueue(queue)\n }\n }\n\n /**\n * Clear sync queue (called on sync complete or cancel)\n */\n async clearSyncQueue(): Promise<void> {\n // S6: Retry up to 3 times to ensure queue is cleared\n for (let attempt = 1; attempt <= 3; attempt++) {\n try {\n const res = await storage.remove(this.SYNC_QUEUE_KEY)\n if (res.is_error()) {\n console.error(`[LocalStorage] Error clearing sync queue (attempt ${attempt}/3):`, res.error)\n if (attempt < 3) continue\n return\n }\n console.log(\"[LocalStorage] Cleared sync queue\")\n return\n } catch (error) {\n console.error(`[LocalStorage] Error clearing sync queue (attempt ${attempt}/3):`, error)\n if (attempt < 3) {\n await new Promise<void>((resolve) => BgTimer.setTimeout(() => resolve(), 100))\n }\n }\n }\n }\n\n /**\n * Check if there's a resumable sync queue\n */\n async hasResumableSyncQueue(): Promise<boolean> {\n const queue = await this.getSyncQueue()\n if (!queue) return false\n // Has resumable queue if there are still files to process\n return queue.currentIndex < queue.files.length\n }\n\n private loadRawDownloadedFiles(): Record<string, DownloadedFile> {\n const result = storage.load<Record<string, DownloadedFile>>(this.DOWNLOADED_FILES_KEY)\n return result.is_error() ? {} : result.value\n }\n\n private loadQuarantinedFiles(): Record<string, DownloadedFile> {\n const result = storage.load<Record<string, DownloadedFile>>(this.QUARANTINED_FILES_KEY)\n return result.is_error() ? {} : result.value\n }\n\n private toPortableRelativePath(path: string): string | null {\n return toPortableGalleryPath(path, RNFS.DocumentDirectoryPath)\n }\n}\n\nexport const localStorageService = LocalStorageService.getInstance()\n"]}
@@ -34,6 +34,8 @@ export interface ProcessingItem {
34
34
  thumbnailPath?: string;
35
35
  /** File names to delete from glasses after processing completes */
36
36
  deleteFromGlasses?: string[];
37
+ /** Negotiated gallery protocol; absent entries are inferred from the transfer ledger. */
38
+ protocolVersion?: number;
37
39
  }
38
40
  declare class MediaProcessingQueue {
39
41
  private queue;
@@ -50,10 +52,18 @@ declare class MediaProcessingQueue {
50
52
  get hasPending(): boolean;
51
53
  /** Returns a promise that resolves when the queue is fully drained, or rejects on timeout. */
52
54
  waitUntilDrained(timeoutMs?: number): Promise<void>;
55
+ /** Retry durable export/ack work after an app restart without downloading media again. */
56
+ retryPending(): Promise<{
57
+ retried: number;
58
+ failed: number;
59
+ }>;
60
+ private acknowledgeEntry;
53
61
  /** Process items one at a time. */
54
62
  private processLoop;
55
63
  /** Process a single item through the full pipeline. */
56
64
  private processItem;
65
+ /** Rebuild a missing app-local index row from the durable transfer ledger. */
66
+ private recoverDownloadedFile;
57
67
  }
58
68
  export declare const mediaProcessingQueue: MediaProcessingQueue;
59
69
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"mediaProcessingQueue.d.ts","sourceRoot":"","sources":["../../../src/services/asg/mediaProcessingQueue.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAeH,MAAM,WAAW,cAAc;IAC7B,oDAAoD;IACpD,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,OAAO,GAAG,OAAO,CAAA;IACvB,0CAA0C;IAC1C,WAAW,EAAE,MAAM,CAAA;IACnB,kCAAkC;IAClC,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,4BAA4B;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,iCAAiC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,sBAAsB;IACtB,SAAS,EAAE,MAAM,CAAA;IACjB,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,oBAAoB;IACpB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,oDAAoD;IACpD,aAAa,EAAE,OAAO,CAAA;IACtB,0CAA0C;IAC1C,cAAc,EAAE,OAAO,CAAA;IACvB,qDAAqD;IACrD,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,mEAAmE;IACnE,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAA;CAC7B;AAED,cAAM,oBAAoB;IACxB,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,UAAU,CAAI;IAEtB,qFAAqF;IACrF,OAAO,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI;IAenC,qCAAqC;IACrC,KAAK,IAAI,IAAI;IAMb,0CAA0C;IAC1C,KAAK,IAAI,IAAI;IAOb,sEAAsE;IACtE,IAAI,UAAU,IAAI,OAAO,CAExB;IAED,8FAA8F;IAC9F,gBAAgB,CAAC,SAAS,GAAE,MAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAmB3D,mCAAmC;YACrB,WAAW;IAyCzB,uDAAuD;YACzC,WAAW;CAmN1B;AAED,eAAO,MAAM,oBAAoB,sBAA6B,CAAA"}
1
+ {"version":3,"file":"mediaProcessingQueue.d.ts","sourceRoot":"","sources":["../../../src/services/asg/mediaProcessingQueue.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAgBH,MAAM,WAAW,cAAc;IAC7B,oDAAoD;IACpD,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,OAAO,GAAG,OAAO,CAAA;IACvB,0CAA0C;IAC1C,WAAW,EAAE,MAAM,CAAA;IACnB,kCAAkC;IAClC,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,4BAA4B;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,iCAAiC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,sBAAsB;IACtB,SAAS,EAAE,MAAM,CAAA;IACjB,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,oBAAoB;IACpB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,oDAAoD;IACpD,aAAa,EAAE,OAAO,CAAA;IACtB,0CAA0C;IAC1C,cAAc,EAAE,OAAO,CAAA;IACvB,qDAAqD;IACrD,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,mEAAmE;IACnE,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC5B,yFAAyF;IACzF,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB;AAED,cAAM,oBAAoB;IACxB,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,UAAU,CAAI;IAEtB,qFAAqF;IACrF,OAAO,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI;IAenC,qCAAqC;IACrC,KAAK,IAAI,IAAI;IAMb,0CAA0C;IAC1C,KAAK,IAAI,IAAI;IAOb,sEAAsE;IACtE,IAAI,UAAU,IAAI,OAAO,CAExB;IAED,8FAA8F;IAC9F,gBAAgB,CAAC,SAAS,GAAE,MAA2B,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBvE,0FAA0F;IACpF,YAAY,IAAI,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAC,CAAC;YAmDlD,gBAAgB;IAa9B,mCAAmC;YACrB,WAAW;IA4CzB,uDAAuD;YACzC,WAAW;IA8OzB,8EAA8E;YAChE,qBAAqB;CAgDpC;AAED,eAAO,MAAM,oBAAoB,sBAA6B,CAAA"}