@oxyhq/services 28.0.0 → 29.0.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.
- package/NOTICE +10 -9
- package/README.md +65 -2
- package/android/src/main/java/so/oxy/devicesession/OxyDeviceSessionModule.kt +178 -0
- package/android/src/main/java/so/oxy/devicesession/OxyDeviceSessionProvider.kt +99 -0
- package/android/src/main/java/so/oxy/devicesession/OxyDeviceSessionStore.kt +115 -0
- package/expo-module.config.json +1 -0
- package/lib/commonjs/index.js +60 -16
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/ui/boot/runProviderColdBoot.js +7 -0
- package/lib/commonjs/ui/boot/runProviderColdBoot.js.map +1 -1
- package/lib/commonjs/ui/client.js +12 -7
- package/lib/commonjs/ui/client.js.map +1 -1
- package/lib/commonjs/ui/components/OxyAccountDialogScreen.js +10 -27
- package/lib/commonjs/ui/components/OxyAccountDialogScreen.js.map +1 -1
- package/lib/commonjs/ui/components/OxyAuthChooser.js +132 -44
- package/lib/commonjs/ui/components/OxyAuthChooser.js.map +1 -1
- package/lib/commonjs/ui/components/OxyProvider.js +4 -2
- package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
- package/lib/commonjs/ui/components/SurfaceScreen.js +31 -3
- package/lib/commonjs/ui/components/SurfaceScreen.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/AccountsMenuView.js +209 -84
- package/lib/commonjs/ui/components/authChooser/AccountsMenuView.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/SignInEntryView.js +25 -7
- package/lib/commonjs/ui/components/authChooser/SignInEntryView.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/primitives.js +35 -22
- package/lib/commonjs/ui/components/authChooser/primitives.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/types.js +11 -0
- package/lib/commonjs/ui/components/authChooser/types.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContext.js +257 -306
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js +27 -37
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/commonjs/ui/context/oxyContextHelpers.js +0 -29
- package/lib/commonjs/ui/context/oxyContextHelpers.js.map +1 -1
- package/lib/commonjs/ui/context/useOxyAccountGraph.js +6 -17
- package/lib/commonjs/ui/context/useOxyAccountGraph.js.map +1 -1
- package/lib/commonjs/ui/hooks/accountDialogSnapshot.js +42 -0
- package/lib/commonjs/ui/hooks/accountDialogSnapshot.js.map +1 -0
- package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js +0 -19
- package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js.map +1 -1
- package/lib/commonjs/ui/hooks/useDeviceSwitcher.js +57 -0
- package/lib/commonjs/ui/hooks/useDeviceSwitcher.js.map +1 -0
- package/lib/commonjs/ui/hooks/useI18n.js +22 -3
- package/lib/commonjs/ui/hooks/useI18n.js.map +1 -1
- package/lib/commonjs/ui/hooks/useSessionManagement.js +46 -153
- package/lib/commonjs/ui/hooks/useSessionManagement.js.map +1 -1
- package/lib/commonjs/ui/index.js +12 -7
- package/lib/commonjs/ui/index.js.map +1 -1
- package/lib/commonjs/ui/navigation/routes.js +85 -73
- package/lib/commonjs/ui/navigation/routes.js.map +1 -1
- package/lib/commonjs/ui/navigation/surfaces.js +8 -7
- package/lib/commonjs/ui/navigation/surfaces.js.map +1 -1
- package/lib/commonjs/ui/runtime/createOxyRuntime.js +433 -0
- package/lib/commonjs/ui/runtime/createOxyRuntime.js.map +1 -0
- package/lib/commonjs/ui/runtime/index.js +63 -0
- package/lib/commonjs/ui/runtime/index.js.map +1 -0
- package/lib/commonjs/ui/runtime/runtimeContext.js +83 -0
- package/lib/commonjs/ui/runtime/runtimeContext.js.map +1 -0
- package/lib/commonjs/ui/runtime/types.js +6 -0
- package/lib/commonjs/ui/runtime/types.js.map +1 -0
- package/lib/commonjs/ui/runtime/useRuntimeSnapshot.js +57 -0
- package/lib/commonjs/ui/runtime/useRuntimeSnapshot.js.map +1 -0
- package/lib/commonjs/ui/server.js +1 -2
- package/lib/commonjs/ui/server.js.map +1 -1
- package/lib/commonjs/ui/session/authStore.js +45 -9
- package/lib/commonjs/ui/session/authStore.js.map +1 -1
- package/lib/commonjs/ui/session/sharedDeviceCredentialStore.js +306 -0
- package/lib/commonjs/ui/session/sharedDeviceCredentialStore.js.map +1 -0
- package/lib/commonjs/ui/stores/authStore.js +154 -55
- package/lib/commonjs/ui/stores/authStore.js.map +1 -1
- package/lib/commonjs/ui/utils/avatarUtils.js +2 -36
- package/lib/commonjs/ui/utils/avatarUtils.js.map +1 -1
- package/lib/module/index.js +16 -8
- package/lib/module/index.js.map +1 -1
- package/lib/module/ui/boot/runProviderColdBoot.js +7 -0
- package/lib/module/ui/boot/runProviderColdBoot.js.map +1 -1
- package/lib/module/ui/client.js +1 -2
- package/lib/module/ui/client.js.map +1 -1
- package/lib/module/ui/components/OxyAccountDialogScreen.js +10 -27
- package/lib/module/ui/components/OxyAccountDialogScreen.js.map +1 -1
- package/lib/module/ui/components/OxyAuthChooser.js +134 -44
- package/lib/module/ui/components/OxyAuthChooser.js.map +1 -1
- package/lib/module/ui/components/OxyProvider.js +5 -3
- package/lib/module/ui/components/OxyProvider.js.map +1 -1
- package/lib/module/ui/components/SurfaceScreen.js +33 -5
- package/lib/module/ui/components/SurfaceScreen.js.map +1 -1
- package/lib/module/ui/components/authChooser/AccountsMenuView.js +210 -85
- package/lib/module/ui/components/authChooser/AccountsMenuView.js.map +1 -1
- package/lib/module/ui/components/authChooser/SignInEntryView.js +25 -7
- package/lib/module/ui/components/authChooser/SignInEntryView.js.map +1 -1
- package/lib/module/ui/components/authChooser/primitives.js +35 -22
- package/lib/module/ui/components/authChooser/primitives.js.map +1 -1
- package/lib/module/ui/components/authChooser/types.js +11 -0
- package/lib/module/ui/components/authChooser/types.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +251 -303
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/context/hooks/useAuthOperations.js +27 -37
- package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/module/ui/context/oxyContextHelpers.js +0 -28
- package/lib/module/ui/context/oxyContextHelpers.js.map +1 -1
- package/lib/module/ui/context/useOxyAccountGraph.js +6 -17
- package/lib/module/ui/context/useOxyAccountGraph.js.map +1 -1
- package/lib/module/ui/hooks/accountDialogSnapshot.js +38 -0
- package/lib/module/ui/hooks/accountDialogSnapshot.js.map +1 -0
- package/lib/module/ui/hooks/mutations/useAccountMutations.js +0 -19
- package/lib/module/ui/hooks/mutations/useAccountMutations.js.map +1 -1
- package/lib/module/ui/hooks/useDeviceSwitcher.js +53 -0
- package/lib/module/ui/hooks/useDeviceSwitcher.js.map +1 -0
- package/lib/module/ui/hooks/useI18n.js +24 -4
- package/lib/module/ui/hooks/useI18n.js.map +1 -1
- package/lib/module/ui/hooks/useSessionManagement.js +47 -155
- package/lib/module/ui/hooks/useSessionManagement.js.map +1 -1
- package/lib/module/ui/index.js +1 -2
- package/lib/module/ui/index.js.map +1 -1
- package/lib/module/ui/navigation/routes.js +84 -72
- package/lib/module/ui/navigation/routes.js.map +1 -1
- package/lib/module/ui/navigation/surfaces.js +6 -6
- package/lib/module/ui/navigation/surfaces.js.map +1 -1
- package/lib/module/ui/runtime/createOxyRuntime.js +430 -0
- package/lib/module/ui/runtime/createOxyRuntime.js.map +1 -0
- package/lib/module/ui/runtime/index.js +15 -0
- package/lib/module/ui/runtime/index.js.map +1 -0
- package/lib/module/ui/runtime/runtimeContext.js +75 -0
- package/lib/module/ui/runtime/runtimeContext.js.map +1 -0
- package/lib/module/ui/runtime/types.js +4 -0
- package/lib/module/ui/runtime/types.js.map +1 -0
- package/lib/module/ui/runtime/useRuntimeSnapshot.js +52 -0
- package/lib/module/ui/runtime/useRuntimeSnapshot.js.map +1 -0
- package/lib/module/ui/server.js +0 -1
- package/lib/module/ui/server.js.map +1 -1
- package/lib/module/ui/session/authStore.js +46 -11
- package/lib/module/ui/session/authStore.js.map +1 -1
- package/lib/module/ui/session/sharedDeviceCredentialStore.js +302 -0
- package/lib/module/ui/session/sharedDeviceCredentialStore.js.map +1 -0
- package/lib/module/ui/stores/authStore.js +154 -55
- package/lib/module/ui/stores/authStore.js.map +1 -1
- package/lib/module/ui/utils/avatarUtils.js +2 -35
- package/lib/module/ui/utils/avatarUtils.js.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +5 -3
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/boot/runProviderColdBoot.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/client.d.ts +1 -2
- package/lib/typescript/commonjs/ui/client.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxyAccountDialogScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxyAuthChooser.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxyProvider.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/SurfaceScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/AccountsMenuView.d.ts +11 -4
- package/lib/typescript/commonjs/ui/components/authChooser/AccountsMenuView.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/SignInEntryView.d.ts +3 -1
- package/lib/typescript/commonjs/ui/components/authChooser/SignInEntryView.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/primitives.d.ts +18 -6
- package/lib/typescript/commonjs/ui/components/authChooser/primitives.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/types.d.ts +33 -1
- package/lib/typescript/commonjs/ui/components/authChooser/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts +35 -4
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts +8 -12
- package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/oxyContextHelpers.d.ts +0 -2
- package/lib/typescript/commonjs/ui/context/oxyContextHelpers.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/oxyContextTypes.d.ts +9 -1
- package/lib/typescript/commonjs/ui/context/oxyContextTypes.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/useOxyAccountGraph.d.ts +1 -3
- package/lib/typescript/commonjs/ui/context/useOxyAccountGraph.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/accountDialogSnapshot.d.ts +15 -0
- package/lib/typescript/commonjs/ui/hooks/accountDialogSnapshot.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useDeviceSwitcher.d.ts +49 -0
- package/lib/typescript/commonjs/ui/hooks/useDeviceSwitcher.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/hooks/useI18n.d.ts +13 -0
- package/lib/typescript/commonjs/ui/hooks/useI18n.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useSessionManagement.d.ts +23 -18
- package/lib/typescript/commonjs/ui/hooks/useSessionManagement.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/index.d.ts +1 -2
- package/lib/typescript/commonjs/ui/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/navigation/routes.d.ts +15 -1
- package/lib/typescript/commonjs/ui/navigation/routes.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/navigation/surfaces.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/runtime/createOxyRuntime.d.ts +144 -0
- package/lib/typescript/commonjs/ui/runtime/createOxyRuntime.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/runtime/index.d.ts +16 -0
- package/lib/typescript/commonjs/ui/runtime/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/runtime/runtimeContext.d.ts +43 -0
- package/lib/typescript/commonjs/ui/runtime/runtimeContext.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/runtime/types.d.ts +89 -0
- package/lib/typescript/commonjs/ui/runtime/types.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/runtime/useRuntimeSnapshot.d.ts +23 -0
- package/lib/typescript/commonjs/ui/runtime/useRuntimeSnapshot.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/server.d.ts +0 -1
- package/lib/typescript/commonjs/ui/server.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/session/authStore.d.ts +60 -9
- package/lib/typescript/commonjs/ui/session/authStore.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/session/sharedDeviceCredentialStore.d.ts +32 -0
- package/lib/typescript/commonjs/ui/session/sharedDeviceCredentialStore.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/stores/authStore.d.ts +49 -5
- package/lib/typescript/commonjs/ui/stores/authStore.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/types/navigation.d.ts +26 -0
- package/lib/typescript/commonjs/ui/types/navigation.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/utils/avatarUtils.d.ts +2 -10
- package/lib/typescript/commonjs/ui/utils/avatarUtils.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +5 -3
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/boot/runProviderColdBoot.d.ts.map +1 -1
- package/lib/typescript/module/ui/client.d.ts +1 -2
- package/lib/typescript/module/ui/client.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxyAccountDialogScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxyAuthChooser.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxyProvider.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/SurfaceScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/AccountsMenuView.d.ts +11 -4
- package/lib/typescript/module/ui/components/authChooser/AccountsMenuView.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/SignInEntryView.d.ts +3 -1
- package/lib/typescript/module/ui/components/authChooser/SignInEntryView.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/primitives.d.ts +18 -6
- package/lib/typescript/module/ui/components/authChooser/primitives.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/types.d.ts +33 -1
- package/lib/typescript/module/ui/components/authChooser/types.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/OxyContext.d.ts +35 -4
- package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts +8 -12
- package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/oxyContextHelpers.d.ts +0 -2
- package/lib/typescript/module/ui/context/oxyContextHelpers.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/oxyContextTypes.d.ts +9 -1
- package/lib/typescript/module/ui/context/oxyContextTypes.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/useOxyAccountGraph.d.ts +1 -3
- package/lib/typescript/module/ui/context/useOxyAccountGraph.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/accountDialogSnapshot.d.ts +15 -0
- package/lib/typescript/module/ui/hooks/accountDialogSnapshot.d.ts.map +1 -0
- package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useDeviceSwitcher.d.ts +49 -0
- package/lib/typescript/module/ui/hooks/useDeviceSwitcher.d.ts.map +1 -0
- package/lib/typescript/module/ui/hooks/useI18n.d.ts +13 -0
- package/lib/typescript/module/ui/hooks/useI18n.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useSessionManagement.d.ts +23 -18
- package/lib/typescript/module/ui/hooks/useSessionManagement.d.ts.map +1 -1
- package/lib/typescript/module/ui/index.d.ts +1 -2
- package/lib/typescript/module/ui/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/navigation/routes.d.ts +15 -1
- package/lib/typescript/module/ui/navigation/routes.d.ts.map +1 -1
- package/lib/typescript/module/ui/navigation/surfaces.d.ts.map +1 -1
- package/lib/typescript/module/ui/runtime/createOxyRuntime.d.ts +144 -0
- package/lib/typescript/module/ui/runtime/createOxyRuntime.d.ts.map +1 -0
- package/lib/typescript/module/ui/runtime/index.d.ts +16 -0
- package/lib/typescript/module/ui/runtime/index.d.ts.map +1 -0
- package/lib/typescript/module/ui/runtime/runtimeContext.d.ts +43 -0
- package/lib/typescript/module/ui/runtime/runtimeContext.d.ts.map +1 -0
- package/lib/typescript/module/ui/runtime/types.d.ts +89 -0
- package/lib/typescript/module/ui/runtime/types.d.ts.map +1 -0
- package/lib/typescript/module/ui/runtime/useRuntimeSnapshot.d.ts +23 -0
- package/lib/typescript/module/ui/runtime/useRuntimeSnapshot.d.ts.map +1 -0
- package/lib/typescript/module/ui/server.d.ts +0 -1
- package/lib/typescript/module/ui/server.d.ts.map +1 -1
- package/lib/typescript/module/ui/session/authStore.d.ts +60 -9
- package/lib/typescript/module/ui/session/authStore.d.ts.map +1 -1
- package/lib/typescript/module/ui/session/sharedDeviceCredentialStore.d.ts +32 -0
- package/lib/typescript/module/ui/session/sharedDeviceCredentialStore.d.ts.map +1 -0
- package/lib/typescript/module/ui/stores/authStore.d.ts +49 -5
- package/lib/typescript/module/ui/stores/authStore.d.ts.map +1 -1
- package/lib/typescript/module/ui/types/navigation.d.ts +26 -0
- package/lib/typescript/module/ui/types/navigation.d.ts.map +1 -1
- package/lib/typescript/module/ui/utils/avatarUtils.d.ts +2 -10
- package/lib/typescript/module/ui/utils/avatarUtils.d.ts.map +1 -1
- package/package.json +6 -7
- package/plugins/withSharedDeviceSessionProvider.js +102 -0
- package/plugins/withSharedDeviceSessionReader.js +57 -0
- package/src/__tests__/esmSourcePurity.test.ts +61 -0
- package/src/index.ts +31 -9
- package/src/ui/boot/runProviderColdBoot.ts +7 -0
- package/src/ui/client.ts +1 -2
- package/src/ui/components/OxyAccountDialogScreen.tsx +10 -29
- package/src/ui/components/OxyAuthChooser.tsx +149 -50
- package/src/ui/components/OxyProvider.tsx +7 -5
- package/src/ui/components/SurfaceScreen.tsx +28 -4
- package/src/ui/components/authChooser/AccountsMenuView.tsx +246 -96
- package/src/ui/components/authChooser/SignInEntryView.tsx +31 -8
- package/src/ui/components/authChooser/primitives.tsx +37 -23
- package/src/ui/components/authChooser/types.ts +34 -1
- package/src/ui/context/OxyContext.tsx +269 -324
- package/src/ui/context/hooks/useAuthOperations.ts +32 -35
- package/src/ui/context/oxyContextHelpers.ts +0 -30
- package/src/ui/context/oxyContextTypes.ts +9 -1
- package/src/ui/context/useOxyAccountGraph.ts +5 -19
- package/src/ui/hooks/accountDialogSnapshot.ts +37 -0
- package/src/ui/hooks/mutations/useAccountMutations.ts +0 -19
- package/src/ui/hooks/queries/__tests__/userCache.test.ts +12 -1
- package/src/ui/hooks/useDeviceSwitcher.ts +110 -0
- package/src/ui/hooks/useI18n.ts +23 -3
- package/src/ui/hooks/useSessionManagement.ts +62 -211
- package/src/ui/index.ts +1 -2
- package/src/ui/navigation/routes.ts +85 -75
- package/src/ui/navigation/surfaces.ts +6 -9
- package/src/ui/runtime/createOxyRuntime.ts +623 -0
- package/src/ui/runtime/index.ts +34 -0
- package/src/ui/runtime/runtimeContext.tsx +111 -0
- package/src/ui/runtime/types.ts +110 -0
- package/src/ui/runtime/useRuntimeSnapshot.ts +52 -0
- package/src/ui/server.ts +0 -1
- package/src/ui/session/__tests__/authStore.test.ts +90 -0
- package/src/ui/session/__tests__/sharedDeviceCredentialStore.test.ts +289 -0
- package/src/ui/session/authStore.ts +78 -9
- package/src/ui/session/sharedDeviceCredentialStore.ts +318 -0
- package/src/ui/stores/authStore.ts +171 -43
- package/src/ui/types/navigation.ts +26 -0
- package/src/ui/utils/avatarUtils.ts +2 -37
- package/lib/commonjs/ui/hooks/useSwitchableAccounts.js +0 -64
- package/lib/commonjs/ui/hooks/useSwitchableAccounts.js.map +0 -1
- package/lib/commonjs/ui/stores/accountStore.js +0 -221
- package/lib/commonjs/ui/stores/accountStore.js.map +0 -1
- package/lib/module/ui/hooks/useSwitchableAccounts.js +0 -60
- package/lib/module/ui/hooks/useSwitchableAccounts.js.map +0 -1
- package/lib/module/ui/stores/accountStore.js +0 -213
- package/lib/module/ui/stores/accountStore.js.map +0 -1
- package/lib/typescript/commonjs/ui/hooks/useSwitchableAccounts.d.ts +0 -23
- package/lib/typescript/commonjs/ui/hooks/useSwitchableAccounts.d.ts.map +0 -1
- package/lib/typescript/commonjs/ui/stores/accountStore.d.ts +0 -27
- package/lib/typescript/commonjs/ui/stores/accountStore.d.ts.map +0 -1
- package/lib/typescript/module/ui/hooks/useSwitchableAccounts.d.ts +0 -23
- package/lib/typescript/module/ui/hooks/useSwitchableAccounts.d.ts.map +0 -1
- package/lib/typescript/module/ui/stores/accountStore.d.ts +0 -27
- package/lib/typescript/module/ui/stores/accountStore.d.ts.map +0 -1
- package/src/ui/hooks/useSwitchableAccounts.ts +0 -75
- package/src/ui/stores/accountStore.ts +0 -264
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config plugin: withSharedDeviceSessionReader (every other official Oxy app).
|
|
3
|
+
*
|
|
4
|
+
* A reader app JOINS the device's existing session — it adopts the shared
|
|
5
|
+
* `deviceId` + `deviceSecret` a hub app published, so it lands signed in with no
|
|
6
|
+
* QR and, crucially, without ever asking for Commons's private identity key.
|
|
7
|
+
* This plugin wires the minimal Android side of `@oxyhq/services`:
|
|
8
|
+
*
|
|
9
|
+
* - Requests the `signature`-level permission
|
|
10
|
+
* `so.oxy.shared.permission.READ_DEVICE_SESSION` (defined by the hub apps).
|
|
11
|
+
* Because it is `signature`, it is granted only when this app is signed with
|
|
12
|
+
* the SAME certificate as the hub — that is the entire trust boundary, and it
|
|
13
|
+
* is what makes an incorrectly-signed app unable to read the slot.
|
|
14
|
+
* - Adds `<queries>` entries for the hub authorities so package-visibility
|
|
15
|
+
* filtering (Android 11+) never hides them from `ContentResolver.call`.
|
|
16
|
+
*
|
|
17
|
+
* The provider itself is declared only by the hub apps
|
|
18
|
+
* (`withSharedDeviceSessionProvider`) — see the note there about why hosting is
|
|
19
|
+
* restricted to apps inside the `so.oxy.shared` UID.
|
|
20
|
+
*
|
|
21
|
+
* Keep this list identical to `PROVIDER_AUTHORITIES` in
|
|
22
|
+
* `android/src/main/java/so/oxy/devicesession/OxyDeviceSessionModule.kt`.
|
|
23
|
+
*/
|
|
24
|
+
const { withAndroidManifest } = require('@expo/config-plugins');
|
|
25
|
+
|
|
26
|
+
const READ_DEVICE_SESSION_PERMISSION = 'so.oxy.shared.permission.READ_DEVICE_SESSION';
|
|
27
|
+
const PROVIDER_AUTHORITIES = [
|
|
28
|
+
'so.oxy.accounts.devicesession',
|
|
29
|
+
'so.oxy.accounts.dev.devicesession',
|
|
30
|
+
'so.oxy.commons.devicesession',
|
|
31
|
+
'so.oxy.commons.dev.devicesession',
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
module.exports = function withSharedDeviceSessionReader(config) {
|
|
35
|
+
return withAndroidManifest(config, (modConfig) => {
|
|
36
|
+
const manifest = modConfig.modResults.manifest;
|
|
37
|
+
|
|
38
|
+
manifest['uses-permission'] = manifest['uses-permission'] ?? [];
|
|
39
|
+
if (!manifest['uses-permission'].some((p) => p.$['android:name'] === READ_DEVICE_SESSION_PERMISSION)) {
|
|
40
|
+
manifest['uses-permission'].push({ $: { 'android:name': READ_DEVICE_SESSION_PERMISSION } });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
manifest['queries'] = manifest['queries'] ?? [];
|
|
44
|
+
if (manifest['queries'].length === 0) {
|
|
45
|
+
manifest['queries'].push({});
|
|
46
|
+
}
|
|
47
|
+
const queries = manifest['queries'][0];
|
|
48
|
+
queries.provider = queries.provider ?? [];
|
|
49
|
+
for (const authority of PROVIDER_AUTHORITIES) {
|
|
50
|
+
if (!queries.provider.some((p) => p.$['android:authorities'] === authority)) {
|
|
51
|
+
queries.provider.push({ $: { 'android:authorities': authority } });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return modConfig;
|
|
56
|
+
});
|
|
57
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@oxyhq/services` ships an ESM build, and ESM has no `require`.
|
|
3
|
+
*
|
|
4
|
+
* This is not a style rule: a surviving `require()` puts web bundlers into
|
|
5
|
+
* CommonJS interop, which hands consumers `undefined` bindings with no build
|
|
6
|
+
* error and no warning. It blanked auth.oxy.so/authorize once — see the note on
|
|
7
|
+
* `screenComponents` in `../ui/navigation/routes` for the mechanism, and
|
|
8
|
+
* `README.md` for the incident.
|
|
9
|
+
*
|
|
10
|
+
* Deferring is still legitimate: a thunk over a static import defers a READ,
|
|
11
|
+
* `import()` defers a LOAD. Neither needs `require()`.
|
|
12
|
+
*/
|
|
13
|
+
import { readdirSync, readFileSync, statSync } from 'node:fs';
|
|
14
|
+
import { join, relative } from 'node:path';
|
|
15
|
+
|
|
16
|
+
const SRC_ROOT = join(__dirname, '..');
|
|
17
|
+
|
|
18
|
+
/** Source extensions that end up in the published ESM build. */
|
|
19
|
+
const SOURCE_EXTENSIONS = ['.ts', '.tsx'];
|
|
20
|
+
|
|
21
|
+
/** Tests are compiled to CommonJS by ts-jest and never ship, so they are exempt. */
|
|
22
|
+
const EXEMPT_SEGMENTS = ['__tests__'];
|
|
23
|
+
|
|
24
|
+
function collectSourceFiles(dir: string): string[] {
|
|
25
|
+
const found: string[] = [];
|
|
26
|
+
for (const entry of readdirSync(dir)) {
|
|
27
|
+
const path = join(dir, entry);
|
|
28
|
+
if (statSync(path).isDirectory()) {
|
|
29
|
+
if (EXEMPT_SEGMENTS.includes(entry)) continue;
|
|
30
|
+
found.push(...collectSourceFiles(path));
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (SOURCE_EXTENSIONS.some((ext) => path.endsWith(ext)) && !path.endsWith('.d.ts')) {
|
|
34
|
+
found.push(path);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return found;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Drop comments and string/template literals so prose about `require()` — such
|
|
42
|
+
* as the notes explaining this very rule — is not mistaken for a call.
|
|
43
|
+
*/
|
|
44
|
+
function stripCommentsAndStrings(code: string): string {
|
|
45
|
+
return code
|
|
46
|
+
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
47
|
+
.replace(/(^|[^:])\/\/.*$/gm, '$1')
|
|
48
|
+
.replace(/'(?:[^'\\\n]|\\.)*'/g, "''")
|
|
49
|
+
.replace(/"(?:[^"\\\n]|\\.)*"/g, '""')
|
|
50
|
+
.replace(/`(?:[^`\\]|\\.)*`/g, '``');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
describe('ESM source purity', () => {
|
|
54
|
+
it('never calls require() anywhere in the shipped source', () => {
|
|
55
|
+
const offenders = collectSourceFiles(SRC_ROOT)
|
|
56
|
+
.filter((file) => /\brequire\s*\(/.test(stripCommentsAndStrings(readFileSync(file, 'utf8'))))
|
|
57
|
+
.map((file) => relative(SRC_ROOT, file));
|
|
58
|
+
|
|
59
|
+
expect(offenders).toEqual([]);
|
|
60
|
+
});
|
|
61
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -33,6 +33,29 @@ setPlatformOS(Platform.OS as PlatformOS);
|
|
|
33
33
|
// ---------------------------------------------------------------------------
|
|
34
34
|
export { default as OxyProvider } from './ui/components/OxyProvider';
|
|
35
35
|
export { useOxy } from './ui/context/OxyContext';
|
|
36
|
+
// `useOxy()` THROWS when no provider is mounted. `useOptionalOxy()` returns
|
|
37
|
+
// `null` instead, for the rare component that renders on both sides of the
|
|
38
|
+
// provider boundary; there is no fabricated forever-loading runtime.
|
|
39
|
+
export { useOptionalOxy, OxyProviderMissingError } from './ui/context/OxyContext';
|
|
40
|
+
|
|
41
|
+
// The runtime surface (ADR 0004). These subscribe to the ONE runtime with a
|
|
42
|
+
// selector, so — unlike `useOxy()`, whose value is rebuilt by any of its fifty
|
|
43
|
+
// members moving — a locale change or a dialog opening does not re-render them.
|
|
44
|
+
export {
|
|
45
|
+
useOxyRuntime,
|
|
46
|
+
useOxySnapshot,
|
|
47
|
+
useActiveAccount,
|
|
48
|
+
useDeviceDirectory,
|
|
49
|
+
OxyRuntimeMissingError,
|
|
50
|
+
} from './ui/runtime';
|
|
51
|
+
export type {
|
|
52
|
+
ActiveAccount,
|
|
53
|
+
OxyRuntime,
|
|
54
|
+
OxyRuntimeError,
|
|
55
|
+
OxyRuntimeSnapshot,
|
|
56
|
+
OxyRuntimeStatus,
|
|
57
|
+
OxyTokenStatus,
|
|
58
|
+
} from './ui/runtime';
|
|
36
59
|
export type { OxyContextState } from './ui/context/OxyContext';
|
|
37
60
|
export { useAuth } from './ui/hooks/useAuth';
|
|
38
61
|
export type { AuthState, AuthActions, UseAuthReturn } from './ui/hooks/useAuth';
|
|
@@ -45,7 +68,6 @@ export { IdentityBoundSessionError } from './ui/session/identityBinding';
|
|
|
45
68
|
// Zustand stores
|
|
46
69
|
// ---------------------------------------------------------------------------
|
|
47
70
|
export { useAuthStore } from './ui/stores/authStore';
|
|
48
|
-
export { useAccountStore } from './ui/stores/accountStore';
|
|
49
71
|
export {
|
|
50
72
|
useAssetStore,
|
|
51
73
|
useAssets as useAssetsStore,
|
|
@@ -343,14 +365,14 @@ export { OxySignInRequestSurface } from './ui/components/OxySignInRequestSurface
|
|
|
343
365
|
export type { OxySignInRequestSurfaceProps } from './ui/components/OxySignInRequestSurface';
|
|
344
366
|
export type { OxySignInSurfaceAction } from './ui/components/authChooser/types';
|
|
345
367
|
|
|
346
|
-
//
|
|
347
|
-
//
|
|
348
|
-
//
|
|
349
|
-
//
|
|
350
|
-
//
|
|
351
|
-
//
|
|
352
|
-
export {
|
|
353
|
-
export type {
|
|
368
|
+
// The device switcher — the server's own directory (ADR 0002) of who is signed
|
|
369
|
+
// in here and what each of them may act as, grouped by PERSON, with the two
|
|
370
|
+
// removals an account id cannot name. Backed by the shared
|
|
371
|
+
// `AccountDialogController` in `@oxyhq/core`; selecting a row activates a
|
|
372
|
+
// `contextId`. `DevicePrincipalGroup` and `DeviceContext` live in `@oxyhq/core`
|
|
373
|
+
// — import them from there.
|
|
374
|
+
export { useDeviceSwitcher } from './ui/hooks/useDeviceSwitcher';
|
|
375
|
+
export type { UseDeviceSwitcherResult } from './ui/hooks/useDeviceSwitcher';
|
|
354
376
|
|
|
355
377
|
// Unified "Manage your Oxy Account" screen (the caller's own personal account)
|
|
356
378
|
export { default as ProfileScreen } from './ui/screens/ProfileScreen';
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
} from '@oxyhq/core';
|
|
9
9
|
import type { SessionClient } from '@oxyhq/core';
|
|
10
10
|
import { loadPersistedDeviceCredential } from '../utils/deviceCredential';
|
|
11
|
+
import { createPlatformSharedDeviceCredentialStore } from '../session/sharedDeviceCredentialStore';
|
|
11
12
|
import { tryCompleteOAuthReturn } from '../utils/oauthReturn';
|
|
12
13
|
import { isWebBrowser } from '../utils/isWebBrowser';
|
|
13
14
|
import { isNetConnectivityExplicitlyOffline } from '../utils/netConnectivity';
|
|
@@ -161,6 +162,12 @@ export async function runProviderColdBoot(opts: RunProviderColdBootOptions): Pro
|
|
|
161
162
|
platform: { isWeb: isWebBrowser(), isNative: !isWebBrowser() },
|
|
162
163
|
sessionMode,
|
|
163
164
|
identity,
|
|
165
|
+
// The device-wide shared credential slot, enabling the `shared-device-adopt`
|
|
166
|
+
// lane: a newly installed official app joins this device's existing session
|
|
167
|
+
// instead of falling back to signing a challenge with the Commons identity
|
|
168
|
+
// key. `null` on web, where each origin is its own device by design; the
|
|
169
|
+
// core lane is also skipped outright in `sessionMode: 'identity'`.
|
|
170
|
+
sharedDeviceCredential: createPlatformSharedDeviceCredentialStore() ?? undefined,
|
|
164
171
|
overallDeadlineMs: COLD_BOOT_OVERALL_DEADLINE_MS,
|
|
165
172
|
isOffline: () => offline,
|
|
166
173
|
onStepDeadline: (stepId) => {
|
package/src/ui/client.ts
CHANGED
|
@@ -19,7 +19,7 @@ export { default as FollowButton } from './components/FollowButton';
|
|
|
19
19
|
export { default as OxyPayButton } from './components/OxyPayButton';
|
|
20
20
|
|
|
21
21
|
// Context
|
|
22
|
-
export { useOxy } from './context/OxyContext';
|
|
22
|
+
export { useOxy, useOptionalOxy, OxyProviderMissingError } from './context/OxyContext';
|
|
23
23
|
|
|
24
24
|
// Hooks
|
|
25
25
|
export { useAuth } from './hooks/useAuth';
|
|
@@ -34,7 +34,6 @@ export { default as ManageAccountScreen } from './screens/ManageAccountScreen';
|
|
|
34
34
|
|
|
35
35
|
// Stores
|
|
36
36
|
export { useAuthStore } from './stores/authStore';
|
|
37
|
-
export { useAccountStore } from './stores/accountStore';
|
|
38
37
|
|
|
39
38
|
// Error handler utilities
|
|
40
39
|
export {
|
|
@@ -31,6 +31,7 @@ import { useSurfaceHeader } from '../hooks/useSurfaceHeader';
|
|
|
31
31
|
import type { BaseScreenProps } from '../types/navigation';
|
|
32
32
|
import LogoText from './logo/LogoText';
|
|
33
33
|
import OxyAuthChooser from './OxyAuthChooser';
|
|
34
|
+
import { EMPTY_ACCOUNT_DIALOG_SNAPSHOT } from '../hooks/accountDialogSnapshot';
|
|
34
35
|
|
|
35
36
|
type Translate = ReturnType<typeof useI18n>['t'];
|
|
36
37
|
|
|
@@ -61,20 +62,22 @@ const OxyAccountDialogScreen: React.FC<BaseScreenProps> = ({ canGoBack }) => {
|
|
|
61
62
|
|
|
62
63
|
// A lightweight, header-only binding to the same controller `OxyAuthChooser`
|
|
63
64
|
// binds independently — cheap, and the established pattern here (
|
|
64
|
-
// `
|
|
65
|
+
// `useDeviceSwitcher` also binds to this controller on its own).
|
|
65
66
|
const subscribe = useCallback(
|
|
66
67
|
(listener: () => void) => (controller ? controller.subscribe(listener) : () => undefined),
|
|
67
68
|
[controller],
|
|
68
69
|
);
|
|
69
70
|
const getSnapshot = useCallback(
|
|
70
|
-
() => (controller ? controller.getSnapshot() :
|
|
71
|
+
() => (controller ? controller.getSnapshot() : EMPTY_ACCOUNT_DIALOG_SNAPSHOT),
|
|
71
72
|
[controller],
|
|
72
73
|
);
|
|
73
74
|
const snapshot = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
74
75
|
|
|
75
76
|
const { view } = snapshot;
|
|
76
|
-
|
|
77
|
-
|
|
77
|
+
// "Add another account" only makes sense as a back destination when there IS
|
|
78
|
+
// another — i.e. somebody is already signed in on this device.
|
|
79
|
+
const hasSignedInAccounts = (snapshot.directory?.principals.length ?? 0) > 0;
|
|
80
|
+
const showBack = view === 'qr' || view === 'signup' || (view === 'add' && hasSignedInAccounts);
|
|
78
81
|
const goToAccounts = useCallback(() => {
|
|
79
82
|
if (!controller) return;
|
|
80
83
|
const { view: currentView, signIn } = controller.getSnapshot();
|
|
@@ -100,7 +103,7 @@ const OxyAccountDialogScreen: React.FC<BaseScreenProps> = ({ canGoBack }) => {
|
|
|
100
103
|
// by the bar. Every OTHER view keeps the SHARED Dialog nav header the rest of
|
|
101
104
|
// the SDK uses — a large in-content title/subtitle that collapses into the bar
|
|
102
105
|
// on scroll — because their copy is informative.
|
|
103
|
-
const copy = view === 'accounts' ? null : headerCopy(view,
|
|
106
|
+
const copy = view === 'accounts' ? null : headerCopy(view, hasSignedInAccounts, t);
|
|
104
107
|
|
|
105
108
|
useSurfaceHeader({
|
|
106
109
|
titleContent: copy ? undefined : NAV_LOGO,
|
|
@@ -126,7 +129,7 @@ const OxyAccountDialogScreen: React.FC<BaseScreenProps> = ({ canGoBack }) => {
|
|
|
126
129
|
|
|
127
130
|
function headerCopy(
|
|
128
131
|
view: Exclude<AccountDialogSnapshot['view'], 'accounts'>,
|
|
129
|
-
|
|
132
|
+
hasSignedInAccounts: boolean,
|
|
130
133
|
t: Translate,
|
|
131
134
|
): { title: string; subtitle: string | null } {
|
|
132
135
|
switch (view) {
|
|
@@ -147,7 +150,7 @@ function headerCopy(
|
|
|
147
150
|
subtitle: t('signup.subtitle') || 'One identity for the whole ecosystem.',
|
|
148
151
|
};
|
|
149
152
|
default:
|
|
150
|
-
return
|
|
153
|
+
return hasSignedInAccounts
|
|
151
154
|
? {
|
|
152
155
|
title: t('signin.addAccountTitle') || 'Add another account',
|
|
153
156
|
subtitle: t('signin.addAccountSubtitle') || 'Sign in with another account.',
|
|
@@ -159,28 +162,6 @@ function headerCopy(
|
|
|
159
162
|
}
|
|
160
163
|
}
|
|
161
164
|
|
|
162
|
-
const EMPTY_SNAPSHOT: AccountDialogSnapshot = {
|
|
163
|
-
view: 'accounts',
|
|
164
|
-
accounts: [],
|
|
165
|
-
activeAccountId: null,
|
|
166
|
-
loading: false,
|
|
167
|
-
error: null,
|
|
168
|
-
switchingAccountId: null,
|
|
169
|
-
signIn: {
|
|
170
|
-
phase: 'idle',
|
|
171
|
-
authorizeCode: null,
|
|
172
|
-
qrPayload: null,
|
|
173
|
-
expiresAt: null,
|
|
174
|
-
error: null,
|
|
175
|
-
route: null,
|
|
176
|
-
routeFailed: false,
|
|
177
|
-
pushSentAt: null,
|
|
178
|
-
openedAt: null,
|
|
179
|
-
progress: 'idle',
|
|
180
|
-
},
|
|
181
|
-
commonsAvailability: 'unknown',
|
|
182
|
-
};
|
|
183
|
-
|
|
184
165
|
/**
|
|
185
166
|
* The screen gutter. In the Dialog's nav-header mode the surface adds NO content
|
|
186
167
|
* padding of its own — the large title and each screen own theirs — so this must
|
|
@@ -37,11 +37,12 @@ import type React from 'react';
|
|
|
37
37
|
import { useCallback, useMemo, useState, useSyncExternalStore } from 'react';
|
|
38
38
|
import { Linking, Platform } from 'react-native';
|
|
39
39
|
import { toast } from '@oxyhq/bloom/toast';
|
|
40
|
+
import { surfaces } from '@oxyhq/bloom/surfaces';
|
|
40
41
|
import { useTheme } from '@oxyhq/bloom/theme';
|
|
41
|
-
import type
|
|
42
|
-
import { isOxyRpOrigin } from '@oxyhq/core';
|
|
42
|
+
import { getNormalizedUserHandle, isOxyRpOrigin, type User } from '@oxyhq/core';
|
|
43
43
|
import { useQueryClient } from '@tanstack/react-query';
|
|
44
44
|
import { useOxy } from '../context/OxyContext';
|
|
45
|
+
import { useDeviceSwitcher } from '../hooks/useDeviceSwitcher';
|
|
45
46
|
import { useI18n } from '../hooks/useI18n';
|
|
46
47
|
import {
|
|
47
48
|
getAccountDialogConsumerHooks,
|
|
@@ -54,13 +55,16 @@ import AccountsMenuView from './authChooser/AccountsMenuView';
|
|
|
54
55
|
import SignInEntryView from './authChooser/SignInEntryView';
|
|
55
56
|
import SignInRequestView from './authChooser/SignInRequestView';
|
|
56
57
|
import SignUpView from './authChooser/SignUpView';
|
|
57
|
-
import
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
58
|
+
import {
|
|
59
|
+
resolveAccentHex,
|
|
60
|
+
type AccountHeroModel,
|
|
61
|
+
type AccountStorageModel,
|
|
62
|
+
type AccountsMenuActions,
|
|
63
|
+
type OxyAuthChooserHandlers,
|
|
64
|
+
type PasskeyMode,
|
|
65
|
+
type SignInAlternatives,
|
|
63
66
|
} from './authChooser/types';
|
|
67
|
+
import { EMPTY_ACCOUNT_DIALOG_SNAPSHOT } from '../hooks/accountDialogSnapshot';
|
|
64
68
|
|
|
65
69
|
/** Commons' own identity-creation deep link (mirrors the `approve`/`attest`/`card` intents). */
|
|
66
70
|
const COMMONS_CREATE_IDENTITY_URL = 'oxycommons://create-identity';
|
|
@@ -96,16 +100,20 @@ const OxyAuthChooser: React.FC<OxyAuthChooserProps> = ({
|
|
|
96
100
|
const {
|
|
97
101
|
accountDialogController: controller,
|
|
98
102
|
showBottomSheet,
|
|
99
|
-
refreshAccounts,
|
|
100
103
|
signInWithPasskey,
|
|
101
104
|
registerWithPasskey,
|
|
102
105
|
oxyServices,
|
|
103
106
|
logout,
|
|
104
107
|
openAvatarPicker,
|
|
108
|
+
user,
|
|
105
109
|
} = useOxy();
|
|
106
110
|
const theme = useTheme();
|
|
107
111
|
const { t } = useI18n();
|
|
108
112
|
const queryClient = useQueryClient();
|
|
113
|
+
// The device's people and what each may act as, resolved for rendering — the
|
|
114
|
+
// SAME hook (and the same one builder) the auth.oxy.so chooser renders from,
|
|
115
|
+
// so the two switchers cannot drift.
|
|
116
|
+
const { principals } = useDeviceSwitcher();
|
|
109
117
|
|
|
110
118
|
// A credential minted for `oxy.so` can only be ASSERTED there (or a loopback
|
|
111
119
|
// dev host) — a hard WebAuthn RP-ID boundary the browser enforces, not
|
|
@@ -217,7 +225,7 @@ const OxyAuthChooser: React.FC<OxyAuthChooserProps> = ({
|
|
|
217
225
|
[controller, autoStartSignIn],
|
|
218
226
|
);
|
|
219
227
|
const getSnapshot = useCallback(
|
|
220
|
-
() => (controller ? controller.getSnapshot() :
|
|
228
|
+
() => (controller ? controller.getSnapshot() : EMPTY_ACCOUNT_DIALOG_SNAPSHOT),
|
|
221
229
|
[controller],
|
|
222
230
|
);
|
|
223
231
|
const snapshot = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
@@ -228,31 +236,97 @@ const OxyAuthChooser: React.FC<OxyAuthChooserProps> = ({
|
|
|
228
236
|
() => null,
|
|
229
237
|
);
|
|
230
238
|
|
|
231
|
-
const
|
|
232
|
-
async (
|
|
239
|
+
const handleActivate = useCallback(
|
|
240
|
+
async (contextId: string) => {
|
|
233
241
|
if (!controller) return;
|
|
234
|
-
if (controller.getSnapshot().
|
|
235
|
-
|
|
242
|
+
if (controller.getSnapshot().activatingContextId) return;
|
|
243
|
+
// Already live: activating it again bumps nothing server-side, so treat
|
|
244
|
+
// the press as "yes, this one" and close.
|
|
245
|
+
if (contextId === controller.getSnapshot().activeContext?.contextId) {
|
|
236
246
|
onComplete?.();
|
|
237
247
|
return;
|
|
238
248
|
}
|
|
239
249
|
try {
|
|
240
|
-
await controller.
|
|
241
|
-
//
|
|
242
|
-
//
|
|
243
|
-
//
|
|
250
|
+
await controller.activateContext(contextId);
|
|
251
|
+
// Failures are recorded on the controller snapshot rather than thrown,
|
|
252
|
+
// so the same path works with the real controller and with test doubles
|
|
253
|
+
// that resolve void.
|
|
244
254
|
if (controller.getSnapshot().error) {
|
|
245
|
-
toast.error(t('accountSwitcher.toasts.
|
|
255
|
+
toast.error(t('accountSwitcher.toasts.activateFailed'));
|
|
246
256
|
return;
|
|
247
257
|
}
|
|
248
|
-
|
|
258
|
+
// The subject changed, so every account-scoped query is now about
|
|
259
|
+
// somebody else. The runtime resets its own caches between the bearer
|
|
260
|
+
// commit and the notify; this drops the Query cache the same way the
|
|
261
|
+
// account switch did.
|
|
249
262
|
queryClient.invalidateQueries();
|
|
250
263
|
onComplete?.();
|
|
251
264
|
} catch {
|
|
252
|
-
toast.error(t('accountSwitcher.toasts.
|
|
265
|
+
toast.error(t('accountSwitcher.toasts.activateFailed'));
|
|
266
|
+
}
|
|
267
|
+
},
|
|
268
|
+
[controller, onComplete, queryClient, t],
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Remove ONE `principal → account` pair.
|
|
273
|
+
*
|
|
274
|
+
* Confirmed, and the confirmation says whose route is going: on a shared
|
|
275
|
+
* device the same organization is reachable through somebody else, and their
|
|
276
|
+
* access is untouched. That is the fact a single "remove account" control
|
|
277
|
+
* would get wrong.
|
|
278
|
+
*/
|
|
279
|
+
const handleRemoveContext = useCallback(
|
|
280
|
+
async (contextId: string) => {
|
|
281
|
+
if (!controller) return;
|
|
282
|
+
const group = principals.find((principal) =>
|
|
283
|
+
principal.contexts.some((context) => context.contextId === contextId),
|
|
284
|
+
);
|
|
285
|
+
const context = group?.contexts.find((row) => row.contextId === contextId);
|
|
286
|
+
if (!group || !context) return;
|
|
287
|
+
const confirmed = await surfaces.confirm({
|
|
288
|
+
title: t('accountSwitcher.confirms.removeContextTitle'),
|
|
289
|
+
message: t('accountSwitcher.confirms.removeContext', {
|
|
290
|
+
person: group.displayName,
|
|
291
|
+
account: context.displayName,
|
|
292
|
+
}),
|
|
293
|
+
confirmLabel: t('common.remove'),
|
|
294
|
+
cancelLabel: t('common.cancel'),
|
|
295
|
+
destructive: true,
|
|
296
|
+
});
|
|
297
|
+
if (!confirmed) return;
|
|
298
|
+
const removed = await controller.signOutContext(contextId);
|
|
299
|
+
if (!removed) {
|
|
300
|
+
toast.error(t('accountSwitcher.toasts.contextRemoveFailed'));
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
toast.success(t('accountSwitcher.toasts.contextRemoved', { account: context.displayName }));
|
|
304
|
+
},
|
|
305
|
+
[controller, principals, t],
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
/** Remove ONE PERSON and every account they reach here, and nobody else's. */
|
|
309
|
+
const handleRemovePrincipal = useCallback(
|
|
310
|
+
async (principalId: string) => {
|
|
311
|
+
if (!controller) return;
|
|
312
|
+
const group = principals.find((principal) => principal.principalId === principalId);
|
|
313
|
+
if (!group) return;
|
|
314
|
+
const confirmed = await surfaces.confirm({
|
|
315
|
+
title: t('accountSwitcher.confirms.removePrincipalTitle'),
|
|
316
|
+
message: t('accountSwitcher.confirms.removePrincipal', { name: group.displayName }),
|
|
317
|
+
confirmLabel: t('common.actions.signOut'),
|
|
318
|
+
cancelLabel: t('common.cancel'),
|
|
319
|
+
destructive: true,
|
|
320
|
+
});
|
|
321
|
+
if (!confirmed) return;
|
|
322
|
+
const removed = await controller.signOutPrincipal(principalId);
|
|
323
|
+
if (!removed) {
|
|
324
|
+
toast.error(t('accountSwitcher.toasts.principalRemoveFailed'));
|
|
325
|
+
return;
|
|
253
326
|
}
|
|
327
|
+
toast.success(t('accountSwitcher.toasts.principalRemoved', { name: group.displayName }));
|
|
254
328
|
},
|
|
255
|
-
[controller,
|
|
329
|
+
[controller, principals, t],
|
|
256
330
|
);
|
|
257
331
|
|
|
258
332
|
const handleManage = useCallback(() => {
|
|
@@ -278,8 +352,14 @@ const OxyAuthChooser: React.FC<OxyAuthChooserProps> = ({
|
|
|
278
352
|
|
|
279
353
|
const handlers = useMemo<OxyAuthChooserHandlers>(
|
|
280
354
|
() => ({
|
|
281
|
-
|
|
282
|
-
void
|
|
355
|
+
onActivate: (contextId) => {
|
|
356
|
+
void handleActivate(contextId);
|
|
357
|
+
},
|
|
358
|
+
onRemoveContext: (contextId) => {
|
|
359
|
+
void handleRemoveContext(contextId);
|
|
360
|
+
},
|
|
361
|
+
onRemovePrincipal: (principalId) => {
|
|
362
|
+
void handleRemovePrincipal(principalId);
|
|
283
363
|
},
|
|
284
364
|
onAdd: handleAdd,
|
|
285
365
|
onManage: handleManage,
|
|
@@ -287,7 +367,21 @@ const OxyAuthChooser: React.FC<OxyAuthChooserProps> = ({
|
|
|
287
367
|
// uploads / clears the picked photo — the single shared avatar write path.
|
|
288
368
|
onEditAvatar: () => openAvatarPicker(),
|
|
289
369
|
}),
|
|
290
|
-
[
|
|
370
|
+
[handleActivate, handleRemoveContext, handleRemovePrincipal, handleAdd, handleManage, openAvatarPicker],
|
|
371
|
+
);
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* The hero block — the account this client is signed in as, from its FULL
|
|
375
|
+
* profile rather than from a directory row.
|
|
376
|
+
*
|
|
377
|
+
* The directory carries the minimum that renders a row for every person on
|
|
378
|
+
* the device; this is the one account we legitimately hold everything for, so
|
|
379
|
+
* the hero keeps its real EMAIL, which the rows below do not pretend to have.
|
|
380
|
+
* The accent is not in that category — every row is drawn in its own account's
|
|
381
|
+
* (issue #961), this one included, from the same field.
|
|
382
|
+
*/
|
|
383
|
+
const hero = useMemo<AccountHeroModel | null>(() => (user ? buildHero(user, theme.colors.primary, oxyServices) : null),
|
|
384
|
+
[user, theme.colors.primary, oxyServices],
|
|
291
385
|
);
|
|
292
386
|
|
|
293
387
|
// Everything that is NOT the surface's one primary action. Wired once here;
|
|
@@ -373,6 +467,8 @@ const OxyAuthChooser: React.FC<OxyAuthChooserProps> = ({
|
|
|
373
467
|
return (
|
|
374
468
|
<AccountsMenuView
|
|
375
469
|
snapshot={snapshot}
|
|
470
|
+
principals={principals}
|
|
471
|
+
hero={hero}
|
|
376
472
|
theme={theme}
|
|
377
473
|
t={t}
|
|
378
474
|
handlers={handlers}
|
|
@@ -417,6 +513,7 @@ const OxyAuthChooser: React.FC<OxyAuthChooserProps> = ({
|
|
|
417
513
|
return (
|
|
418
514
|
<SignInEntryView
|
|
419
515
|
snapshot={snapshot}
|
|
516
|
+
principals={principals}
|
|
420
517
|
theme={theme}
|
|
421
518
|
t={t}
|
|
422
519
|
handlers={handlers}
|
|
@@ -426,30 +523,32 @@ const OxyAuthChooser: React.FC<OxyAuthChooserProps> = ({
|
|
|
426
523
|
);
|
|
427
524
|
};
|
|
428
525
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
526
|
+
/**
|
|
527
|
+
* The hero model for the signed-in account.
|
|
528
|
+
*
|
|
529
|
+
* The address line is the account's canonical `@oxy.so` email when it has one,
|
|
530
|
+
* else its normalized `@handle`. It never synthesizes a `username@oxy.so`
|
|
531
|
+
* address — the identity contract forbids it, and a non-Oxy or missing email
|
|
532
|
+
* falls through to the handle.
|
|
533
|
+
*/
|
|
534
|
+
function buildHero(
|
|
535
|
+
user: User,
|
|
536
|
+
fallbackAccent: string,
|
|
537
|
+
oxyServices: ReturnType<typeof useOxy>['oxyServices'],
|
|
538
|
+
): AccountHeroModel {
|
|
539
|
+
const handle = getNormalizedUserHandle(user);
|
|
540
|
+
const displayName = user.name?.displayName?.trim() || handle || '';
|
|
541
|
+
const addressLine = user.email?.toLowerCase().endsWith('@oxy.so')
|
|
542
|
+
? user.email
|
|
543
|
+
: handle
|
|
544
|
+
? `@${handle}`
|
|
545
|
+
: null;
|
|
546
|
+
return {
|
|
547
|
+
displayName,
|
|
548
|
+
addressLine,
|
|
549
|
+
avatarUrl: user.avatar ? oxyServices.getFileDownloadUrl(user.avatar, 'thumb') : undefined,
|
|
550
|
+
accentHex: resolveAccentHex(user.color ?? null, fallbackAccent),
|
|
551
|
+
};
|
|
552
|
+
}
|
|
454
553
|
|
|
455
554
|
export default OxyAuthChooser;
|
|
@@ -3,7 +3,7 @@ import { AppState, Platform, StyleSheet, View } from 'react-native';
|
|
|
3
3
|
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
|
4
4
|
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
|
5
5
|
import type { OxyProviderProps } from '../types/navigation';
|
|
6
|
-
import {
|
|
6
|
+
import { OxyRuntimeProvider, type OxyRuntimeProviderProps } from '../context/OxyContext';
|
|
7
7
|
import { QueryClientProvider, focusManager, onlineManager } from '@tanstack/react-query';
|
|
8
8
|
import { BloomDialogProvider } from '@oxyhq/bloom/dialog';
|
|
9
9
|
import { SurfaceProvider } from '@oxyhq/bloom/surfaces';
|
|
@@ -97,6 +97,7 @@ const OxyProvider: FC<OxyProviderProps> = ({
|
|
|
97
97
|
queryClient: providedQueryClient,
|
|
98
98
|
requireAuth = 'off',
|
|
99
99
|
backgroundSession = false,
|
|
100
|
+
deviceCredentialStorage = 'persistent',
|
|
100
101
|
}) => {
|
|
101
102
|
|
|
102
103
|
// Dynamic KeyboardProvider for native. Uses variable indirection
|
|
@@ -284,8 +285,8 @@ const OxyProvider: FC<OxyProviderProps> = ({
|
|
|
284
285
|
const coreContent = (
|
|
285
286
|
<QueryClientProvider client={queryClient}>
|
|
286
287
|
<BloomDialogProvider>
|
|
287
|
-
<
|
|
288
|
-
oxyServices={oxyServices as
|
|
288
|
+
<OxyRuntimeProvider
|
|
289
|
+
oxyServices={oxyServices as OxyRuntimeProviderProps['oxyServices']}
|
|
289
290
|
baseURL={baseURL}
|
|
290
291
|
authWebUrl={authWebUrl}
|
|
291
292
|
authRedirectUri={authRedirectUri}
|
|
@@ -295,7 +296,8 @@ const OxyProvider: FC<OxyProviderProps> = ({
|
|
|
295
296
|
sessionMode={sessionMode}
|
|
296
297
|
webAuthMode={webAuthMode}
|
|
297
298
|
backgroundSession={backgroundSession}
|
|
298
|
-
|
|
299
|
+
deviceCredentialStorage={deviceCredentialStorage}
|
|
300
|
+
onAuthStateChange={onAuthStateChange as OxyRuntimeProviderProps['onAuthStateChange']}
|
|
299
301
|
>
|
|
300
302
|
<SurfaceProvider>
|
|
301
303
|
{requireAuth === 'off' ? (
|
|
@@ -305,7 +307,7 @@ const OxyProvider: FC<OxyProviderProps> = ({
|
|
|
305
307
|
)}
|
|
306
308
|
</SurfaceProvider>
|
|
307
309
|
<ToastOutlet />
|
|
308
|
-
</
|
|
310
|
+
</OxyRuntimeProvider>
|
|
309
311
|
</BloomDialogProvider>
|
|
310
312
|
</QueryClientProvider>
|
|
311
313
|
);
|