@lowdefy/shell 3.23.2 → 7.2.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 (356) hide show
  1. package/LICENSE +183 -183
  2. package/README.md +364 -2
  3. package/app/(t0,t1,t2,t3,t4)/[...path].js +38 -0
  4. package/app/(t0,t1,t2,t3,t4)/_layout.js +48 -0
  5. package/app/(t0,t1,t2,t3,t4)/index.js +38 -0
  6. package/app/+native-intent.js +39 -0
  7. package/app/_layout.js +36 -0
  8. package/app.config.ts +183 -0
  9. package/appLinks.js +65 -0
  10. package/applyShellPlugins.js +43 -0
  11. package/authCallbackIntentFilters.js +46 -0
  12. package/babel.config.js +25 -0
  13. package/checkShellConfig.js +172 -0
  14. package/checkShellPlugins.js +74 -0
  15. package/getPrivacyManifest.js +39 -0
  16. package/metro.config.js +73 -0
  17. package/native/LowdefyAppShortcuts.swift.template +84 -0
  18. package/native/withAppShortcuts.js +60 -0
  19. package/native-fingerprint.json +16 -0
  20. package/package.json +120 -40
  21. package/profiles.js +607 -0
  22. package/readShellConfig.js +36 -0
  23. package/resolveShellPlugins.js +109 -0
  24. package/runtimeVersion.js +112 -0
  25. package/scripts/checkReleaseBundle.mjs +358 -0
  26. package/scripts/createDayjsLocales.mjs +67 -0
  27. package/scripts/createShellAssets.mjs +54 -0
  28. package/scripts/createShellVersion.mjs +83 -0
  29. package/scripts/createTypeManifest.mjs +205 -0
  30. package/shell.dev.json +11 -0
  31. package/shellVariant.js +126 -0
  32. package/src/ShellContext.js +32 -0
  33. package/src/actions/CopyToClipboard.js +29 -0
  34. package/src/actions/CopyToClipboard.test.js +33 -0
  35. package/src/actions/GeolocationCurrentPosition.js +61 -0
  36. package/src/actions/GeolocationCurrentPosition.test.js +79 -0
  37. package/src/actions/GetLocalStorage.js +36 -0
  38. package/src/actions/RemoveLocalStorage.js +31 -0
  39. package/src/actions/ScrollTo.js +66 -0
  40. package/src/actions/ScrollTo.test.js +83 -0
  41. package/src/actions/SetFocus.js +33 -0
  42. package/src/actions/SetFocus.test.js +49 -0
  43. package/src/actions/SetLocalStorage.js +34 -0
  44. package/src/actions/localStorage.test.js +68 -0
  45. package/src/actions/measureInScrollView.js +36 -0
  46. package/src/actions/nativeActions.js +41 -0
  47. package/src/auth/AuthButton.js +44 -0
  48. package/src/auth/AuthGate.js +57 -0
  49. package/src/auth/AuthLink.js +43 -0
  50. package/src/auth/LockScreen.js +67 -0
  51. package/src/auth/LockScreen.test.js +37 -0
  52. package/src/auth/SignInScreen.js +268 -0
  53. package/src/auth/SignInScreen.test.js +187 -0
  54. package/src/auth/createAppAuth.js +325 -0
  55. package/src/auth/createAppAuth.test.js +424 -0
  56. package/src/auth/createAuthApi.js +115 -0
  57. package/src/auth/createAuthApi.test.js +138 -0
  58. package/src/auth/createCoreAuth.js +96 -0
  59. package/src/auth/createCoreAuth.test.js +188 -0
  60. package/src/auth/createDeviceKey.js +62 -0
  61. package/src/auth/createDeviceKey.test.js +54 -0
  62. package/src/auth/createSessionStores.js +45 -0
  63. package/src/auth/createTokenStore.js +103 -0
  64. package/src/auth/createTokenStore.test.js +116 -0
  65. package/src/auth/getAuthSettings.js +24 -0
  66. package/src/auth/getAuthView.js +39 -0
  67. package/src/auth/getAuthView.test.js +52 -0
  68. package/src/auth/getSheetReturnUrl.js +35 -0
  69. package/src/auth/getTokenStoreOptions.js +46 -0
  70. package/src/auth/isBackgroundShell.js +26 -0
  71. package/src/auth/isSheetReturnLink.js +35 -0
  72. package/src/auth/parseQuery.js +39 -0
  73. package/src/auth/purgeAppData.js +80 -0
  74. package/src/auth/purgeAppData.test.js +104 -0
  75. package/src/auth/shouldLock.js +26 -0
  76. package/src/auth/signInWithSheet.js +97 -0
  77. package/src/auth/signInWithSheet.test.js +205 -0
  78. package/src/auth/styles.js +78 -0
  79. package/src/auth/unregisterPushToken.js +62 -0
  80. package/src/auth/unregisterPushToken.test.js +67 -0
  81. package/src/auth/usesBiometricUnlock.js +23 -0
  82. package/src/auth/watchSession.js +47 -0
  83. package/src/auth/withoutCookies.js +26 -0
  84. package/src/cache/createSqliteCacheStore.js +113 -0
  85. package/src/cache/createSqliteCacheStore.test.js +93 -0
  86. package/src/cache/getCacheKey.js +25 -0
  87. package/src/cache/getLastCacheKeyName.js +22 -0
  88. package/src/components/AppProvider.js +118 -0
  89. package/src/components/Frame.js +116 -0
  90. package/src/components/Frame.test.js +101 -0
  91. package/src/components/LoadingScreen.js +35 -0
  92. package/src/components/MenuButton.js +121 -0
  93. package/src/components/MenuButton.test.js +74 -0
  94. package/src/components/MessageScreen.js +51 -0
  95. package/src/components/NavigationTheme.js +33 -0
  96. package/src/components/NoConnectionBanner.js +51 -0
  97. package/src/components/NoConnectionBanner.test.js +73 -0
  98. package/src/components/OtherPageScreen.js +95 -0
  99. package/src/components/OtherPageScreen.test.js +74 -0
  100. package/src/components/PageScreen.js +254 -0
  101. package/src/components/PageScreen.test.js +492 -0
  102. package/src/components/RouteErrorScreen.js +47 -0
  103. package/src/components/ShellRoot.js +34 -0
  104. package/src/components/ShellRoot.test.js +62 -0
  105. package/src/components/ShellThemeProvider.js +45 -0
  106. package/src/components/SwitchToTab.js +36 -0
  107. package/src/components/SwitchToTab.test.js +63 -0
  108. package/src/components/TabIconImages.js +72 -0
  109. package/src/components/UpdateScreen.js +40 -0
  110. package/src/components/getNavigationTheme.js +44 -0
  111. package/src/components/getNavigationTheme.test.js +41 -0
  112. package/src/components/styles.js +50 -0
  113. package/src/components/useSurfaceTokens.js +47 -0
  114. package/src/config/getAppApiBase.js +34 -0
  115. package/src/config/getShellConfig.js +37 -0
  116. package/src/config/getShellConfig.test.js +80 -0
  117. package/src/createShellApp.js +126 -0
  118. package/src/dev/DevShell.js +239 -0
  119. package/src/dev/DevShell.test.js +233 -0
  120. package/src/dev/connect/ConfirmSheet.js +71 -0
  121. package/src/dev/connect/ConnectScreen.js +117 -0
  122. package/src/dev/connect/QrScanner.js +89 -0
  123. package/src/dev/connection/createDevShellConfig.js +47 -0
  124. package/src/dev/connection/createDevTransport.js +51 -0
  125. package/src/dev/connection/createDevTransport.test.js +87 -0
  126. package/src/dev/connection/createServerList.js +48 -0
  127. package/src/dev/connection/isLoopbackHost.js +25 -0
  128. package/src/dev/connection/pairedShellApp.test.js +104 -0
  129. package/src/dev/connection/parseDevLink.js +66 -0
  130. package/src/dev/connection/parseDevLink.test.js +97 -0
  131. package/src/dev/connection/parseQuery.js +33 -0
  132. package/src/dev/connection/parseServerInput.js +40 -0
  133. package/src/dev/connection/parseServerUrl.js +45 -0
  134. package/src/dev/devStyles.js +97 -0
  135. package/src/dev/index.js +21 -0
  136. package/src/dev/inject/createDeviceInjections.js +188 -0
  137. package/src/dev/inject/createDeviceInjections.test.js +224 -0
  138. package/src/dev/inject/createOfflineFetch.js +35 -0
  139. package/src/dev/inject/createOfflineFetch.test.js +68 -0
  140. package/src/dev/inject/injectPush.js +60 -0
  141. package/src/dev/inject/injectShare.js +55 -0
  142. package/src/dev/inject/withInjectedDevice.js +98 -0
  143. package/src/dev/menu/BlockGallery.js +33 -0
  144. package/src/dev/menu/DevMenu.js +121 -0
  145. package/src/dev/menu/createLogBuffer.js +83 -0
  146. package/src/dev/menu/watchShake.js +52 -0
  147. package/src/dev/menu/watchShake.test.js +61 -0
  148. package/src/dev/recorder/createNativeRecorder.js +204 -0
  149. package/src/dev/recorder/createNativeRecorder.test.js +307 -0
  150. package/src/dev/redirectDevLink.js +30 -0
  151. package/src/dev/redirectDevLink.test.js +49 -0
  152. package/src/dev/steps/createStepRunner.js +184 -0
  153. package/src/dev/steps/createStepRunner.test.js +262 -0
  154. package/src/dev/steps/findBlock.js +78 -0
  155. package/src/dev/steps/isContextSettled.js +37 -0
  156. package/src/dev/steps/waitForSettle.js +39 -0
  157. package/src/dev/steps/waitUntil.js +40 -0
  158. package/src/dev/tab/DevTabClient.js +97 -0
  159. package/src/dev/tab/answerEval.js +39 -0
  160. package/src/dev/tab/answerInspect.js +45 -0
  161. package/src/dev/tab/createDevTabClient.js +272 -0
  162. package/src/dev/tab/createDevTabClient.test.js +317 -0
  163. package/src/dev/tab/describeTabError.js +35 -0
  164. package/src/dev/tab/devClientMarker.js +22 -0
  165. package/src/dev/tab/devClientMarker.test.js +23 -0
  166. package/src/dev/tab/findPageContext.js +43 -0
  167. package/src/dev/tab/getShownPage.js +42 -0
  168. package/src/dev/tab/readEventStream.js +68 -0
  169. package/src/dev/tab/reloadApp.js +42 -0
  170. package/src/dev/tab/reloadApp.test.js +115 -0
  171. package/src/device/DeviceHosts.js +32 -0
  172. package/src/device/basic/createFilesFeature.js +41 -0
  173. package/src/device/basic/createHapticsFeature.js +46 -0
  174. package/src/device/basic/createKeepAwakeFeature.js +37 -0
  175. package/src/device/basic/createPhotosFeature.js +40 -0
  176. package/src/device/basic/createRequestQueueFeature.js +37 -0
  177. package/src/device/basic/createShareFeature.js +63 -0
  178. package/src/device/basic/createShareFeature.test.js +47 -0
  179. package/src/device/basic/createSpeechFeature.js +43 -0
  180. package/src/device/basic/createUploadFeature.js +41 -0
  181. package/src/device/basic/toImagePickerOptions.js +41 -0
  182. package/src/device/basic/toPickedFile.js +54 -0
  183. package/src/device/calendar/index.js +68 -0
  184. package/src/device/contacts/index.js +67 -0
  185. package/src/device/createDevice.js +65 -0
  186. package/src/device/device.js +27 -0
  187. package/src/device/field/MapView.js +93 -0
  188. package/src/device/field/ScanCodeSheet.js +86 -0
  189. package/src/device/field/ScannerView.js +52 -0
  190. package/src/device/field/createAudioFeature.js +70 -0
  191. package/src/device/field/createBrightnessFeature.js +40 -0
  192. package/src/device/field/createCameraFeature.js +56 -0
  193. package/src/device/field/createDocumentScannerFeature.js +51 -0
  194. package/src/device/field/createLocationFeature.js +39 -0
  195. package/src/device/field/createScanRequests.js +55 -0
  196. package/src/device/field/index.js +39 -0
  197. package/src/device/field/scanRequests.js +22 -0
  198. package/src/device/injected.js +60 -0
  199. package/src/device/injected.test.js +39 -0
  200. package/src/device/nfc/index.js +89 -0
  201. package/src/device/nfc/toNfcRecord.js +44 -0
  202. package/src/device/nfc/toNfcRecord.test.js +48 -0
  203. package/src/device/tracking/createBacklog.js +63 -0
  204. package/src/device/tracking/createBacklog.test.js +47 -0
  205. package/src/device/tracking/index.js +211 -0
  206. package/src/device/tracking/index.test.js +110 -0
  207. package/src/host/createDeviceStore.js +32 -0
  208. package/src/host/createMemoryStore.js +32 -0
  209. package/src/host/createNetworkStatus.js +94 -0
  210. package/src/host/createNetworkStatus.test.js +94 -0
  211. package/src/host/createRouterAdapter.js +33 -0
  212. package/src/host/createShellFetch.js +62 -0
  213. package/src/host/createShellFetch.test.js +165 -0
  214. package/src/host/createShellGlobals.js +62 -0
  215. package/src/host/createShellHost.js +83 -0
  216. package/src/host/createShellHost.test.js +36 -0
  217. package/src/host/getOrigin.js +31 -0
  218. package/src/host/isNetworkError.js +24 -0
  219. package/src/host/isOnline.js +22 -0
  220. package/src/host/networkStatus.js +24 -0
  221. package/src/host/readServerError.js +45 -0
  222. package/src/host/readServerError.test.js +85 -0
  223. package/src/host/reportShellError.js +47 -0
  224. package/src/host/reportShellError.test.js +79 -0
  225. package/src/host/withNetworkErrors.js +38 -0
  226. package/src/host/withNetworkErrors.test.js +61 -0
  227. package/src/host/withShellHeader.js +25 -0
  228. package/src/host/withShellHeader.test.js +28 -0
  229. package/src/js/evaluateJsEntries.js +43 -0
  230. package/src/js/evaluateJsEntries.test.js +48 -0
  231. package/src/locale/createShellLocale.js +112 -0
  232. package/src/locale/createShellLocale.test.js +74 -0
  233. package/src/locale/dayjsLocales.js +165 -0
  234. package/src/locale/getDeviceLocales.js +23 -0
  235. package/src/locale/loadDayjsLocale.js +32 -0
  236. package/src/locale/resolveActiveLocale.js +46 -0
  237. package/src/locale/resolveActiveLocale.test.js +40 -0
  238. package/src/native/backgroundFetch.js +97 -0
  239. package/src/native/callEndpoint.js +63 -0
  240. package/src/native/createAppEventTrigger.js +70 -0
  241. package/src/native/currentApp.js +49 -0
  242. package/src/native/getPagePath.js +32 -0
  243. package/src/native/openPath.js +55 -0
  244. package/src/native/openPath.test.js +39 -0
  245. package/src/native/parseShellLink.js +38 -0
  246. package/src/native/parseShellLink.test.js +29 -0
  247. package/src/native/startAppServices.js +94 -0
  248. package/src/native/startAppServices.test.js +97 -0
  249. package/src/native/startShareIn.js +59 -0
  250. package/src/native/startShortcuts.js +88 -0
  251. package/src/native/startShortcuts.test.js +59 -0
  252. package/src/native/toSharePayload.js +47 -0
  253. package/src/native/toSharePayload.test.js +47 -0
  254. package/src/native/whenAppReady.js +48 -0
  255. package/src/navigation/SlotContext.js +24 -0
  256. package/src/navigation/frameSlots.js +27 -0
  257. package/src/navigation/getFrame.js +30 -0
  258. package/src/navigation/getInitialScreenOptions.js +63 -0
  259. package/src/navigation/getInitialScreenOptions.test.js +66 -0
  260. package/src/navigation/getPresentation.js +28 -0
  261. package/src/navigation/getSlotRootPath.js +27 -0
  262. package/src/navigation/getStackOptions.js +185 -0
  263. package/src/navigation/getTabHref.js +45 -0
  264. package/src/navigation/getTabIcons.js +43 -0
  265. package/src/navigation/isPageUnderHeader.js +46 -0
  266. package/src/navigation/navigation.test.js +342 -0
  267. package/src/navigation/toAppLinkPath.js +43 -0
  268. package/src/navigation/toAppLinkPath.test.js +53 -0
  269. package/src/navigation/toRouterHref.js +29 -0
  270. package/src/notifications/createNotificationsFeature.js +61 -0
  271. package/src/notifications/getCategoryAction.js +31 -0
  272. package/src/notifications/handleNotificationResponse.js +82 -0
  273. package/src/notifications/registerPushToken.js +71 -0
  274. package/src/notifications/startNotifications.js +114 -0
  275. package/src/notifications/toCategoryActions.js +42 -0
  276. package/src/notifications/toNotificationEvent.js +36 -0
  277. package/src/notifications/toScheduleRequest.js +62 -0
  278. package/src/plugins/appTypes.js +28 -0
  279. package/src/plugins/prefixTypes.js +28 -0
  280. package/src/plugins/shellPlugins.json +15 -0
  281. package/src/plugins/typeManifest.js +789 -0
  282. package/src/plugins/types.js +64 -0
  283. package/src/plugins/types.test.js +173 -0
  284. package/src/polyfills/installGetRandomValues.js +28 -0
  285. package/src/queue/createRequestQueue.js +198 -0
  286. package/src/queue/createRequestQueue.test.js +215 -0
  287. package/src/queue/createSqliteQueueStore.js +121 -0
  288. package/src/queue/summarizeQueue.js +39 -0
  289. package/src/session/createAppSession.js +323 -0
  290. package/src/session/createAppSession.test.js +391 -0
  291. package/src/session/getPageView.js +59 -0
  292. package/src/session/getPageView.test.js +137 -0
  293. package/src/sync/encodePagePath.js +27 -0
  294. package/src/sync/fetchPage.js +72 -0
  295. package/src/sync/fetchPage.test.js +84 -0
  296. package/src/sync/fetchRoot.js +43 -0
  297. package/src/sync/fetchUser.js +33 -0
  298. package/src/sync/runLimited.js +36 -0
  299. package/src/sync/toRootConfig.js +35 -0
  300. package/src/test/createFakeNetwork.js +40 -0
  301. package/src/test/createFakeSecureStore.js +40 -0
  302. package/src/test/createFakeServer.js +98 -0
  303. package/src/test/createMemoryCacheStore.js +59 -0
  304. package/src/test/createNetworkError.js +24 -0
  305. package/src/test/expoImage.js +42 -0
  306. package/src/test/expoNotifications.js +40 -0
  307. package/src/test/expoQuickActions.js +22 -0
  308. package/src/test/expoSpeechRecognition.js +31 -0
  309. package/src/test/expoTaskManager.js +25 -0
  310. package/src/test/expoUi.js +55 -0
  311. package/src/test/expoUiBottomSheet.js +64 -0
  312. package/src/test/expoUiDateTimePicker.js +26 -0
  313. package/src/test/expoUiMenu.js +46 -0
  314. package/src/test/expoUiSegmentedControl.js +47 -0
  315. package/src/test/expoUiSwiftUi.js +62 -0
  316. package/src/test/expoUiSwiftUiModifiers.js +23 -0
  317. package/src/test/expoWebBrowser.js +23 -0
  318. package/src/test/fixtures.js +98 -0
  319. package/src/test/nativeModule.js +33 -0
  320. package/src/test/renderPageScreen.js +109 -0
  321. package/src/types/checkTypeSet.js +39 -0
  322. package/src/types/collectPageTypes.js +49 -0
  323. package/src/types/collectPageTypes.test.js +90 -0
  324. package/src/types/findMissingPageTypes.js +40 -0
  325. package/src/version/compareVersions.js +38 -0
  326. package/src/version/isShellTooOld.js +30 -0
  327. package/src/version/shellVersion.js +21 -0
  328. package/src/version/shellVersion.test.js +32 -0
  329. package/src/widgets/fetchWidgetData.js +59 -0
  330. package/src/widgets/index.js +156 -0
  331. package/src/widgets/refreshWidgets.js +31 -0
  332. package/withExcludedNativeModules.js +93 -0
  333. package/dist/location.js +0 -33
  334. package/dist/public/apple-touch-icon.png +0 -0
  335. package/dist/public/icon-32.png +0 -0
  336. package/dist/public/icon-512.png +0 -0
  337. package/dist/public/icon.svg +0 -17
  338. package/dist/public/logo-dark-theme.png +0 -0
  339. package/dist/public/logo-light-theme.png +0 -0
  340. package/dist/public/logo-square-dark-theme.png +0 -0
  341. package/dist/public/logo-square-light-theme.png +0 -0
  342. package/dist/public/manifest.webmanifest +0 -16
  343. package/dist/shell/246.93432ff68186aae6c87b.js +0 -2
  344. package/dist/shell/246.93432ff68186aae6c87b.js.LICENSE.txt +0 -8
  345. package/dist/shell/454.b597ca21366afda000af.js +0 -2
  346. package/dist/shell/454.b597ca21366afda000af.js.LICENSE.txt +0 -14
  347. package/dist/shell/484.d645e6c09079f45ce044.js +0 -1
  348. package/dist/shell/706.a66931e02a549fd269bd.js +0 -2
  349. package/dist/shell/706.a66931e02a549fd269bd.js.LICENSE.txt +0 -8
  350. package/dist/shell/9.78be8c1b6c3986f404fc.js +0 -2
  351. package/dist/shell/9.78be8c1b6c3986f404fc.js.LICENSE.txt +0 -45
  352. package/dist/shell/922.43b7a3ebe22ef05b3049.js +0 -2
  353. package/dist/shell/922.43b7a3ebe22ef05b3049.js.LICENSE.txt +0 -14
  354. package/dist/shell/index.html +0 -57
  355. package/dist/shell/main.bde87d1433f0625faed6.js +0 -1
  356. package/dist/shell/runtime.1ead93159ba56c05b969.js +0 -1
@@ -0,0 +1,79 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ import { jest } from '@jest/globals';
18
+ import { installDevice } from '@lowdefy/blocks-native/device';
19
+
20
+ import createDeviceInjections from '../dev/inject/createDeviceInjections.js';
21
+ import createFakeNetwork from '../test/createFakeNetwork.js';
22
+ import createNetworkStatus from '../host/createNetworkStatus.js';
23
+ import GeolocationCurrentPosition from './GeolocationCurrentPosition.js';
24
+ import { installInjector } from '../device/injected.js';
25
+
26
+ let injections;
27
+ let remove;
28
+ beforeEach(() => {
29
+ injections = createDeviceInjections({
30
+ networkStatus: createNetworkStatus({ network: createFakeNetwork() }),
31
+ });
32
+ remove = installInjector(injections);
33
+ });
34
+ afterEach(() => remove());
35
+
36
+ test('GeolocationCurrentPosition answers with the injected location in the web shape', async () => {
37
+ injections.inject({
38
+ kind: 'location',
39
+ value: { latitude: -33.92, longitude: 18.42, accuracy: 12, timestamp: 1760778000000 },
40
+ });
41
+ await expect(GeolocationCurrentPosition({ params: {} })).resolves.toEqual({
42
+ coords: {
43
+ accuracy: 12,
44
+ altitude: null,
45
+ altitudeAccuracy: null,
46
+ heading: null,
47
+ latitude: -33.92,
48
+ longitude: 18.42,
49
+ speed: null,
50
+ },
51
+ timestamp: 1760778000000,
52
+ });
53
+ });
54
+
55
+ test('GeolocationCurrentPosition answers as the web does when location is refused', async () => {
56
+ injections.inject({ kind: 'location', value: { latitude: -33.92, longitude: 18.42 } });
57
+ injections.inject({ kind: 'permission', name: 'location', value: 'denied' });
58
+ await expect(GeolocationCurrentPosition({ params: {} })).resolves.toEqual({
59
+ code: 1,
60
+ error: 'PERMISSION_DENIED',
61
+ message: 'Location permission was denied.',
62
+ });
63
+ });
64
+
65
+ test('GeolocationCurrentPosition throws when the shell carries no location and none was injected', async () => {
66
+ await expect(GeolocationCurrentPosition({ params: {} })).rejects.toThrow(
67
+ 'GeolocationCurrentPosition needs a shell that carries location (profile field). In Lowdefy Dev, inject a location.'
68
+ );
69
+ });
70
+
71
+ test('GeolocationCurrentPosition reads the location feature when nothing was injected', async () => {
72
+ const current = jest.fn(async () => ({ coords: { latitude: 1, longitude: 2 }, timestamp: 5 }));
73
+ const removeDevice = installDevice({ location: { current } });
74
+ await expect(
75
+ GeolocationCurrentPosition({ params: { enableHighAccuracy: true } })
76
+ ).resolves.toEqual({ coords: { latitude: 1, longitude: 2 }, timestamp: 5 });
77
+ expect(current).toHaveBeenCalledWith({ enableHighAccuracy: true });
78
+ removeDevice();
79
+ });
@@ -0,0 +1,36 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ import { type } from '@lowdefy/helpers';
18
+ import parseStoredValue from '@lowdefy/actions-core/localStorage/parseStoredValue.js';
19
+ import validateStorageKey from '@lowdefy/actions-core/localStorage/validateStorageKey.js';
20
+
21
+ // The native body of actions-core's GetLocalStorage: reads the app's key from the device store
22
+ // (expo-sqlite's key-value store), where the web reads localStorage.
23
+ function GetLocalStorage({ globals, methods, params }) {
24
+ if (!type.isObject(params)) {
25
+ throw new Error('GetLocalStorage params should be an object.');
26
+ }
27
+ const { key } = params;
28
+ validateStorageKey({ actionType: 'GetLocalStorage', key });
29
+ const item = globals.storage.get(methods.getAppStorageKey(key));
30
+ if (type.isNone(item)) {
31
+ return params.default;
32
+ }
33
+ return parseStoredValue(item);
34
+ }
35
+
36
+ export default GetLocalStorage;
@@ -0,0 +1,31 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ import { type } from '@lowdefy/helpers';
18
+ import validateStorageKey from '@lowdefy/actions-core/localStorage/validateStorageKey.js';
19
+
20
+ // The native body of actions-core's RemoveLocalStorage: removes the app's key from the device
21
+ // store (expo-sqlite's key-value store), where the web removes it from localStorage.
22
+ function RemoveLocalStorage({ globals, methods, params }) {
23
+ if (!type.isObject(params)) {
24
+ throw new Error('RemoveLocalStorage params should be an object.');
25
+ }
26
+ const { key } = params;
27
+ validateStorageKey({ actionType: 'RemoveLocalStorage', key });
28
+ globals.storage.remove(methods.getAppStorageKey(key));
29
+ }
30
+
31
+ export default RemoveLocalStorage;
@@ -0,0 +1,66 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ import { type } from '@lowdefy/helpers';
18
+
19
+ import measureInScrollView from './measureInScrollView.js';
20
+
21
+ // Where the block's top lands, by scrollIntoView's `block` option. 'nearest' needs the current
22
+ // scroll position, which a ScrollView does not report, so it scrolls as 'start' does.
23
+ function getScrollTop({ align, element, viewport }) {
24
+ switch (align) {
25
+ case 'center':
26
+ return element.top - (viewport.height - element.height) / 2;
27
+ case 'end':
28
+ return element.top - viewport.height + element.height;
29
+ default:
30
+ return element.top;
31
+ }
32
+ }
33
+
34
+ // The native body of actions-core's ScrollTo: scrolls the page's ScrollView to a block's measured
35
+ // position, or to `top` and `left` as window.scrollTo does. A block not on the page is left alone,
36
+ // as on the web.
37
+ async function ScrollTo({ globals, methods, params }) {
38
+ if (!type.isObject(params)) {
39
+ throw new Error('Invalid ScrollTo, check action params.');
40
+ }
41
+ const pageId = methods.getPageId();
42
+ const scrollView = globals.elements.find({ kind: 'scroll', pageId });
43
+ if (scrollView === null) {
44
+ return;
45
+ }
46
+ if (type.isNone(params.blockId)) {
47
+ scrollView.scrollTo({
48
+ animated: params.behavior === 'smooth',
49
+ x: params.left ?? 0,
50
+ y: params.top ?? 0,
51
+ });
52
+ return;
53
+ }
54
+ const view = globals.elements.find({ blockId: params.blockId, kind: 'view', pageId });
55
+ if (view === null) {
56
+ return;
57
+ }
58
+ const { element, viewport } = await measureInScrollView({ scrollView, view });
59
+ scrollView.scrollTo({
60
+ animated: params.options?.behavior === 'smooth',
61
+ x: 0,
62
+ y: Math.max(0, getScrollTop({ align: params.options?.block, element, viewport })),
63
+ });
64
+ }
65
+
66
+ export default ScrollTo;
@@ -0,0 +1,83 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ import { jest } from '@jest/globals';
18
+
19
+ import ScrollTo from './ScrollTo.js';
20
+
21
+ const methods = { getPageId: () => 'home' };
22
+
23
+ // A ScrollView 600 high, and a block view 100 high at 1000 in its content.
24
+ function createPage({ withView = true } = {}) {
25
+ const innerView = { name: 'content' };
26
+ const scrollView = {
27
+ getInnerViewRef: () => innerView,
28
+ getNativeScrollRef: () => ({ measure: (callback) => callback(0, 0, 400, 600) }),
29
+ scrollTo: jest.fn(),
30
+ };
31
+ const view = {
32
+ measureLayout: jest.fn((relativeTo, onSuccess) => onSuccess(0, 1000, 400, 100)),
33
+ };
34
+ const registered = {
35
+ 'scroll:home:': scrollView,
36
+ ...(withView ? { 'view:home:jobs': view } : {}),
37
+ };
38
+ const globals = {
39
+ elements: {
40
+ find: ({ blockId, kind, pageId }) => registered[`${kind}:${pageId}:${blockId ?? ''}`] ?? null,
41
+ },
42
+ };
43
+ return { globals, innerView, scrollView, view };
44
+ }
45
+
46
+ test('ScrollTo throws without params', async () => {
47
+ await expect(ScrollTo({ globals: createPage().globals, methods })).rejects.toThrow(
48
+ 'Invalid ScrollTo, check action params.'
49
+ );
50
+ });
51
+
52
+ test('ScrollTo without a blockId scrolls the page to top, as window.scrollTo does', async () => {
53
+ const { globals, scrollView } = createPage();
54
+ await ScrollTo({ globals, methods, params: { top: 200, behavior: 'smooth' } });
55
+ expect(scrollView.scrollTo.mock.calls).toEqual([[{ animated: true, x: 0, y: 200 }]]);
56
+ });
57
+
58
+ test('ScrollTo scrolls a block to the top of the page, measured in the scroll content', async () => {
59
+ const { globals, innerView, scrollView, view } = createPage();
60
+ await ScrollTo({ globals, methods, params: { blockId: 'jobs' } });
61
+ expect(view.measureLayout.mock.calls[0][0]).toBe(innerView);
62
+ expect(scrollView.scrollTo.mock.calls).toEqual([[{ animated: false, x: 0, y: 1000 }]]);
63
+ });
64
+
65
+ test('ScrollTo places a block at the center or end of the page by options.block', async () => {
66
+ const { globals, scrollView } = createPage();
67
+ await ScrollTo({
68
+ globals,
69
+ methods,
70
+ params: { blockId: 'jobs', options: { block: 'center', behavior: 'smooth' } },
71
+ });
72
+ await ScrollTo({ globals, methods, params: { blockId: 'jobs', options: { block: 'end' } } });
73
+ expect(scrollView.scrollTo.mock.calls).toEqual([
74
+ [{ animated: true, x: 0, y: 750 }],
75
+ [{ animated: false, x: 0, y: 500 }],
76
+ ]);
77
+ });
78
+
79
+ test('ScrollTo leaves the page where it is for a block not on it', async () => {
80
+ const { globals, scrollView } = createPage({ withView: false });
81
+ await ScrollTo({ globals, methods, params: { blockId: 'jobs' } });
82
+ expect(scrollView.scrollTo).not.toHaveBeenCalled();
83
+ });
@@ -0,0 +1,33 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ import { type } from '@lowdefy/helpers';
18
+
19
+ // The native body of actions-core's SetFocus: focuses the control the block registered as its
20
+ // input (useElementRef kind 'input'). As on the web, a block with nothing to focus is left alone.
21
+ function SetFocus({ globals, methods, params }) {
22
+ if (!type.isString(params)) {
23
+ throw new Error('SetFocus parameter must be a string.');
24
+ }
25
+ const input = globals.elements.find({
26
+ blockId: params,
27
+ kind: 'input',
28
+ pageId: methods.getPageId(),
29
+ });
30
+ input?.focus?.();
31
+ }
32
+
33
+ export default SetFocus;
@@ -0,0 +1,49 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ import { jest } from '@jest/globals';
18
+
19
+ import SetFocus from './SetFocus.js';
20
+
21
+ function createGlobals(elements) {
22
+ return {
23
+ elements: {
24
+ find: ({ blockId, kind, pageId }) => elements[`${kind}:${pageId}:${blockId}`] ?? null,
25
+ },
26
+ };
27
+ }
28
+
29
+ const methods = { getPageId: () => 'home' };
30
+
31
+ test('SetFocus focuses the input the block registered on the page', () => {
32
+ const input = { focus: jest.fn() };
33
+ SetFocus({ globals: createGlobals({ 'input:home:name': input }), methods, params: 'name' });
34
+ expect(input.focus).toHaveBeenCalledTimes(1);
35
+ });
36
+
37
+ test('SetFocus leaves a block with nothing to focus alone', () => {
38
+ const other = { focus: jest.fn() };
39
+ expect(() =>
40
+ SetFocus({ globals: createGlobals({ 'input:other:name': other }), methods, params: 'name' })
41
+ ).not.toThrow();
42
+ expect(other.focus).not.toHaveBeenCalled();
43
+ });
44
+
45
+ test('SetFocus takes a block id string', () => {
46
+ expect(() => SetFocus({ globals: createGlobals({}), methods, params: { blockId: 'a' } })).toThrow(
47
+ 'SetFocus parameter must be a string.'
48
+ );
49
+ });
@@ -0,0 +1,34 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ import { serializer, type } from '@lowdefy/helpers';
18
+ import validateStorageKey from '@lowdefy/actions-core/localStorage/validateStorageKey.js';
19
+
20
+ // The native body of actions-core's SetLocalStorage: writes the app's key to the device store
21
+ // (expo-sqlite's key-value store), where the web writes localStorage.
22
+ function SetLocalStorage({ globals, methods, params }) {
23
+ if (!type.isObject(params)) {
24
+ throw new Error('SetLocalStorage params should be an object.');
25
+ }
26
+ const { key, value } = params;
27
+ validateStorageKey({ actionType: 'SetLocalStorage', key });
28
+ if (type.isUndefined(value)) {
29
+ throw new Error('SetLocalStorage "value" is required.');
30
+ }
31
+ globals.storage.set(methods.getAppStorageKey(key), serializer.serializeToString(value));
32
+ }
33
+
34
+ export default SetLocalStorage;
@@ -0,0 +1,68 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ import createMemoryStore from '../host/createMemoryStore.js';
18
+ import GetLocalStorage from './GetLocalStorage.js';
19
+ import RemoveLocalStorage from './RemoveLocalStorage.js';
20
+ import SetLocalStorage from './SetLocalStorage.js';
21
+
22
+ const methods = { getAppStorageKey: (key) => `lowdefy:field::${key}` };
23
+
24
+ function createGlobals() {
25
+ return { storage: createMemoryStore() };
26
+ }
27
+
28
+ test('SetLocalStorage keeps a serialized value under the app key, which GetLocalStorage reads', () => {
29
+ const globals = createGlobals();
30
+ const value = { due: new Date(1600000000000), title: 'Pump' };
31
+ SetLocalStorage({ globals, methods, params: { key: 'draft', value } });
32
+ expect(globals.storage.get('lowdefy:field::draft')).toBe(
33
+ '{"due":{"~d":1600000000000},"title":"Pump"}'
34
+ );
35
+ expect(GetLocalStorage({ globals, methods, params: { key: 'draft' } })).toEqual(value);
36
+ });
37
+
38
+ test('GetLocalStorage gives the default for a key not set, and a plain string as it is', () => {
39
+ const globals = createGlobals();
40
+ expect(GetLocalStorage({ globals, methods, params: { key: 'draft', default: 'none' } })).toBe(
41
+ 'none'
42
+ );
43
+ globals.storage.set('lowdefy:field::mode', 'dark');
44
+ expect(GetLocalStorage({ globals, methods, params: { key: 'mode' } })).toBe('dark');
45
+ });
46
+
47
+ test('RemoveLocalStorage removes the app key', () => {
48
+ const globals = createGlobals();
49
+ SetLocalStorage({ globals, methods, params: { key: 'draft', value: 1 } });
50
+ RemoveLocalStorage({ globals, methods, params: { key: 'draft' } });
51
+ expect(globals.storage.keys()).toEqual([]);
52
+ });
53
+
54
+ test('the local storage actions refuse keys Lowdefy keeps for itself', () => {
55
+ const globals = createGlobals();
56
+ expect(() =>
57
+ SetLocalStorage({ globals, methods, params: { key: 'lowdefy_darkMode', value: 'dark' } })
58
+ ).toThrow('SetLocalStorage "key" "lowdefy_darkMode" is reserved.');
59
+ expect(() => GetLocalStorage({ globals, methods, params: { key: '' } })).toThrow(
60
+ 'GetLocalStorage "key" should be a non-empty string.'
61
+ );
62
+ expect(() => RemoveLocalStorage({ globals, methods, params: 'draft' })).toThrow(
63
+ 'RemoveLocalStorage params should be an object.'
64
+ );
65
+ expect(() => SetLocalStorage({ globals, methods, params: { key: 'draft' } })).toThrow(
66
+ 'SetLocalStorage "value" is required.'
67
+ );
68
+ });
@@ -0,0 +1,36 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ // The view's position and height in the ScrollView's content, and the height of the ScrollView's
18
+ // visible area.
19
+ function measureInScrollView({ scrollView, view }) {
20
+ const measureElement = new Promise((resolve, reject) => {
21
+ view.measureLayout(
22
+ scrollView.getInnerViewRef(),
23
+ (left, top, width, height) => resolve({ height, top }),
24
+ () => reject(new Error('ScrollTo could not measure the block in the page.'))
25
+ );
26
+ });
27
+ const measureViewport = new Promise((resolve) => {
28
+ scrollView.getNativeScrollRef().measure((x, y, width, height) => resolve({ height }));
29
+ });
30
+ return Promise.all([measureElement, measureViewport]).then(([element, viewport]) => ({
31
+ element,
32
+ viewport,
33
+ }));
34
+ }
35
+
36
+ export default measureInScrollView;
@@ -0,0 +1,41 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ import CopyToClipboard from './CopyToClipboard.js';
18
+ import GeolocationCurrentPosition from './GeolocationCurrentPosition.js';
19
+ import GetLocalStorage from './GetLocalStorage.js';
20
+ import RemoveLocalStorage from './RemoveLocalStorage.js';
21
+ import ScrollTo from './ScrollTo.js';
22
+ import SetFocus from './SetFocus.js';
23
+ import SetLocalStorage from './SetLocalStorage.js';
24
+
25
+ // Native bodies of the actions-core actions whose web bodies reach the DOM, under the same names,
26
+ // so pages call them as on the web. They reach the device through the shell's host globals
27
+ // (createShellGlobals). SetDarkMode and SetLocale keep their actions-core bodies: they call the
28
+ // window members the shell sets (client-native's ThemeProvider and the shell's locale).
29
+ // GeolocationCurrentPosition answers with a location Lowdefy Dev injected until the shell carries
30
+ // device location.
31
+ const nativeActions = {
32
+ CopyToClipboard,
33
+ GeolocationCurrentPosition,
34
+ GetLocalStorage,
35
+ RemoveLocalStorage,
36
+ ScrollTo,
37
+ SetFocus,
38
+ SetLocalStorage,
39
+ };
40
+
41
+ export default nativeActions;
@@ -0,0 +1,44 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ import React from 'react';
18
+ import { Pressable, Text } from 'react-native';
19
+
20
+ import styles from './styles.js';
21
+ import useSurfaceTokens from '../components/useSurfaceTokens.js';
22
+
23
+ function AuthButton({ disabled = false, onPress, testID, title }) {
24
+ const tokens = useSurfaceTokens();
25
+ return (
26
+ <Pressable
27
+ accessibilityRole="button"
28
+ accessibilityState={{ disabled }}
29
+ disabled={disabled}
30
+ onPress={onPress}
31
+ style={({ pressed }) => [
32
+ styles.button,
33
+ { backgroundColor: tokens.colorTint },
34
+ pressed ? { opacity: 0.7 } : null,
35
+ disabled ? styles.buttonDisabled : null,
36
+ ]}
37
+ testID={testID}
38
+ >
39
+ <Text style={styles.buttonText}>{title}</Text>
40
+ </Pressable>
41
+ );
42
+ }
43
+
44
+ export default AuthButton;
@@ -0,0 +1,57 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ import React, { useEffect, useSyncExternalStore } from 'react';
18
+ import * as SplashScreen from 'expo-splash-screen';
19
+
20
+ import getAuthSettings from './getAuthSettings.js';
21
+ import getAuthView from './getAuthView.js';
22
+ import LoadingScreen from '../components/LoadingScreen.js';
23
+ import LockScreen from './LockScreen.js';
24
+ import SignInScreen from './SignInScreen.js';
25
+ import { useSessionState, useShell } from '../ShellContext.js';
26
+
27
+ // The lock screen or sign-in screen in place of the app, until it is unlocked and someone is
28
+ // signed in (getAuthView).
29
+ function AuthGate({ children }) {
30
+ const { auth } = useShell();
31
+ const authState = useSyncExternalStore(auth.subscribe, auth.getState);
32
+ const sessionState = useSessionState();
33
+ const view = getAuthView({ auth: authState, session: sessionState });
34
+
35
+ useEffect(() => {
36
+ if (view === 'lock' || view === 'signIn') SplashScreen.hideAsync();
37
+ }, [view]);
38
+
39
+ if (view === 'lock') {
40
+ return <LockScreen appName={auth.getAppName()} auth={auth} />;
41
+ }
42
+ if (view === 'loading') {
43
+ return <LoadingScreen />;
44
+ }
45
+ if (view === 'signIn') {
46
+ return (
47
+ <SignInScreen
48
+ appName={auth.getAppName()}
49
+ auth={auth}
50
+ settings={getAuthSettings(sessionState.root)}
51
+ />
52
+ );
53
+ }
54
+ return children;
55
+ }
56
+
57
+ export default AuthGate;