@oxyhq/services 23.2.0 → 24.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.
Files changed (172) hide show
  1. package/android/src/main/java/so/oxy/identity/OxyIdentityStore.kt +21 -127
  2. package/android/src/main/java/so/oxy/session/OxyBackgroundSession.kt +184 -0
  3. package/android/src/main/java/so/oxy/session/OxyBackgroundSessionApi.kt +162 -0
  4. package/android/src/main/java/so/oxy/session/OxyBackgroundSessionModule.kt +101 -0
  5. package/android/src/main/java/so/oxy/session/OxyBackgroundSessionStore.kt +170 -0
  6. package/android/src/main/java/so/oxy/session/OxyBackgroundToken.kt +61 -0
  7. package/android/src/main/java/so/oxy/storage/OxyEncryptedPrefs.kt +241 -0
  8. package/expo-module.config.json +4 -1
  9. package/lib/commonjs/ui/components/OxyProvider.js +3 -1
  10. package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
  11. package/lib/commonjs/ui/components/ProfileButton.js +1 -1
  12. package/lib/commonjs/ui/components/ProfileButton.js.map +1 -1
  13. package/lib/commonjs/ui/components/internal/PinInput.js +3 -1
  14. package/lib/commonjs/ui/components/internal/PinInput.js.map +1 -1
  15. package/lib/commonjs/ui/components/logo/LogoText.js +4 -4
  16. package/lib/commonjs/ui/components/logo/LogoText.js.map +1 -1
  17. package/lib/commonjs/ui/components/payment/PaymentSummaryStep.js +2 -2
  18. package/lib/commonjs/ui/components/payment/PaymentSummaryStep.js.map +1 -1
  19. package/lib/commonjs/ui/context/OxyContext.js +13 -0
  20. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  21. package/lib/commonjs/ui/screens/AccountSettingsScreen.js +2 -3
  22. package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
  23. package/lib/commonjs/ui/screens/ChangeAvatarScreen.js +2 -3
  24. package/lib/commonjs/ui/screens/ChangeAvatarScreen.js.map +1 -1
  25. package/lib/commonjs/ui/screens/EditProfileScreen.js +2 -3
  26. package/lib/commonjs/ui/screens/EditProfileScreen.js.map +1 -1
  27. package/lib/commonjs/ui/screens/FeedbackScreen.js +3 -1
  28. package/lib/commonjs/ui/screens/FeedbackScreen.js.map +1 -1
  29. package/lib/commonjs/ui/screens/ManageAccountScreen.js +1 -1
  30. package/lib/commonjs/ui/screens/ManageAccountScreen.js.map +1 -1
  31. package/lib/commonjs/ui/screens/PremiumSubscriptionScreen.js +2 -2
  32. package/lib/commonjs/ui/screens/PremiumSubscriptionScreen.js.map +1 -1
  33. package/lib/commonjs/ui/screens/PrivacySettingsScreen.js +8 -9
  34. package/lib/commonjs/ui/screens/PrivacySettingsScreen.js.map +1 -1
  35. package/lib/commonjs/ui/screens/ProfileScreen.js +1 -1
  36. package/lib/commonjs/ui/screens/ProfileScreen.js.map +1 -1
  37. package/lib/commonjs/ui/screens/UserListScreen.js +2 -3
  38. package/lib/commonjs/ui/screens/UserListScreen.js.map +1 -1
  39. package/lib/commonjs/ui/screens/trust/TrustCenterScreen.js +1 -1
  40. package/lib/commonjs/ui/screens/trust/TrustCenterScreen.js.map +1 -1
  41. package/lib/commonjs/ui/screens/trust/TrustLeaderboardScreen.js +2 -3
  42. package/lib/commonjs/ui/screens/trust/TrustLeaderboardScreen.js.map +1 -1
  43. package/lib/commonjs/ui/screens/trust/TrustRewardsScreen.js +1 -1
  44. package/lib/commonjs/ui/screens/trust/TrustRewardsScreen.js.map +1 -1
  45. package/lib/commonjs/ui/session/backgroundSession.js +233 -0
  46. package/lib/commonjs/ui/session/backgroundSession.js.map +1 -0
  47. package/lib/commonjs/ui/session/index.js +7 -0
  48. package/lib/commonjs/ui/session/index.js.map +1 -1
  49. package/lib/commonjs/ui/session/useBackgroundSessionSync.js +82 -0
  50. package/lib/commonjs/ui/session/useBackgroundSessionSync.js.map +1 -0
  51. package/lib/commonjs/ui/utils/userUtils.js +31 -22
  52. package/lib/commonjs/ui/utils/userUtils.js.map +1 -1
  53. package/lib/module/ui/components/OxyProvider.js +3 -1
  54. package/lib/module/ui/components/OxyProvider.js.map +1 -1
  55. package/lib/module/ui/components/ProfileButton.js +2 -2
  56. package/lib/module/ui/components/ProfileButton.js.map +1 -1
  57. package/lib/module/ui/components/internal/PinInput.js +3 -1
  58. package/lib/module/ui/components/internal/PinInput.js.map +1 -1
  59. package/lib/module/ui/components/logo/LogoText.js +4 -4
  60. package/lib/module/ui/components/logo/LogoText.js.map +1 -1
  61. package/lib/module/ui/components/payment/PaymentSummaryStep.js +2 -2
  62. package/lib/module/ui/components/payment/PaymentSummaryStep.js.map +1 -1
  63. package/lib/module/ui/context/OxyContext.js +14 -1
  64. package/lib/module/ui/context/OxyContext.js.map +1 -1
  65. package/lib/module/ui/screens/AccountSettingsScreen.js +2 -3
  66. package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
  67. package/lib/module/ui/screens/ChangeAvatarScreen.js +3 -4
  68. package/lib/module/ui/screens/ChangeAvatarScreen.js.map +1 -1
  69. package/lib/module/ui/screens/EditProfileScreen.js +3 -4
  70. package/lib/module/ui/screens/EditProfileScreen.js.map +1 -1
  71. package/lib/module/ui/screens/FeedbackScreen.js +3 -1
  72. package/lib/module/ui/screens/FeedbackScreen.js.map +1 -1
  73. package/lib/module/ui/screens/ManageAccountScreen.js +2 -2
  74. package/lib/module/ui/screens/ManageAccountScreen.js.map +1 -1
  75. package/lib/module/ui/screens/PremiumSubscriptionScreen.js +2 -2
  76. package/lib/module/ui/screens/PremiumSubscriptionScreen.js.map +1 -1
  77. package/lib/module/ui/screens/PrivacySettingsScreen.js +9 -10
  78. package/lib/module/ui/screens/PrivacySettingsScreen.js.map +1 -1
  79. package/lib/module/ui/screens/ProfileScreen.js +1 -1
  80. package/lib/module/ui/screens/ProfileScreen.js.map +1 -1
  81. package/lib/module/ui/screens/UserListScreen.js +2 -3
  82. package/lib/module/ui/screens/UserListScreen.js.map +1 -1
  83. package/lib/module/ui/screens/trust/TrustCenterScreen.js +1 -1
  84. package/lib/module/ui/screens/trust/TrustCenterScreen.js.map +1 -1
  85. package/lib/module/ui/screens/trust/TrustLeaderboardScreen.js +2 -3
  86. package/lib/module/ui/screens/trust/TrustLeaderboardScreen.js.map +1 -1
  87. package/lib/module/ui/screens/trust/TrustRewardsScreen.js +1 -1
  88. package/lib/module/ui/screens/trust/TrustRewardsScreen.js.map +1 -1
  89. package/lib/module/ui/session/backgroundSession.js +227 -0
  90. package/lib/module/ui/session/backgroundSession.js.map +1 -0
  91. package/lib/module/ui/session/index.js +1 -0
  92. package/lib/module/ui/session/index.js.map +1 -1
  93. package/lib/module/ui/session/useBackgroundSessionSync.js +77 -0
  94. package/lib/module/ui/session/useBackgroundSessionSync.js.map +1 -0
  95. package/lib/module/ui/utils/userUtils.js +31 -22
  96. package/lib/module/ui/utils/userUtils.js.map +1 -1
  97. package/lib/typescript/commonjs/ui/components/OxyProvider.d.ts.map +1 -1
  98. package/lib/typescript/commonjs/ui/components/ProfileButton.d.ts.map +1 -1
  99. package/lib/typescript/commonjs/ui/components/internal/PinInput.d.ts.map +1 -1
  100. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
  101. package/lib/typescript/commonjs/ui/context/oxyContextTypes.d.ts +22 -0
  102. package/lib/typescript/commonjs/ui/context/oxyContextTypes.d.ts.map +1 -1
  103. package/lib/typescript/commonjs/ui/screens/ChangeAvatarScreen.d.ts.map +1 -1
  104. package/lib/typescript/commonjs/ui/screens/EditProfileScreen.d.ts.map +1 -1
  105. package/lib/typescript/commonjs/ui/screens/FeedbackScreen.d.ts.map +1 -1
  106. package/lib/typescript/commonjs/ui/screens/ManageAccountScreen.d.ts.map +1 -1
  107. package/lib/typescript/commonjs/ui/screens/PrivacySettingsScreen.d.ts.map +1 -1
  108. package/lib/typescript/commonjs/ui/screens/ProfileScreen.d.ts.map +1 -1
  109. package/lib/typescript/commonjs/ui/screens/trust/trustTier.d.ts +1 -1
  110. package/lib/typescript/commonjs/ui/screens/trust/trustTier.d.ts.map +1 -1
  111. package/lib/typescript/commonjs/ui/session/backgroundSession.d.ts +42 -0
  112. package/lib/typescript/commonjs/ui/session/backgroundSession.d.ts.map +1 -0
  113. package/lib/typescript/commonjs/ui/session/index.d.ts +1 -0
  114. package/lib/typescript/commonjs/ui/session/index.d.ts.map +1 -1
  115. package/lib/typescript/commonjs/ui/session/useBackgroundSessionSync.d.ts +23 -0
  116. package/lib/typescript/commonjs/ui/session/useBackgroundSessionSync.d.ts.map +1 -0
  117. package/lib/typescript/commonjs/ui/types/navigation.d.ts +7 -0
  118. package/lib/typescript/commonjs/ui/types/navigation.d.ts.map +1 -1
  119. package/lib/typescript/commonjs/ui/utils/userUtils.d.ts +7 -12
  120. package/lib/typescript/commonjs/ui/utils/userUtils.d.ts.map +1 -1
  121. package/lib/typescript/module/ui/components/OxyProvider.d.ts.map +1 -1
  122. package/lib/typescript/module/ui/components/ProfileButton.d.ts.map +1 -1
  123. package/lib/typescript/module/ui/components/internal/PinInput.d.ts.map +1 -1
  124. package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
  125. package/lib/typescript/module/ui/context/oxyContextTypes.d.ts +22 -0
  126. package/lib/typescript/module/ui/context/oxyContextTypes.d.ts.map +1 -1
  127. package/lib/typescript/module/ui/screens/ChangeAvatarScreen.d.ts.map +1 -1
  128. package/lib/typescript/module/ui/screens/EditProfileScreen.d.ts.map +1 -1
  129. package/lib/typescript/module/ui/screens/FeedbackScreen.d.ts.map +1 -1
  130. package/lib/typescript/module/ui/screens/ManageAccountScreen.d.ts.map +1 -1
  131. package/lib/typescript/module/ui/screens/PrivacySettingsScreen.d.ts.map +1 -1
  132. package/lib/typescript/module/ui/screens/ProfileScreen.d.ts.map +1 -1
  133. package/lib/typescript/module/ui/screens/trust/trustTier.d.ts +1 -1
  134. package/lib/typescript/module/ui/screens/trust/trustTier.d.ts.map +1 -1
  135. package/lib/typescript/module/ui/session/backgroundSession.d.ts +42 -0
  136. package/lib/typescript/module/ui/session/backgroundSession.d.ts.map +1 -0
  137. package/lib/typescript/module/ui/session/index.d.ts +1 -0
  138. package/lib/typescript/module/ui/session/index.d.ts.map +1 -1
  139. package/lib/typescript/module/ui/session/useBackgroundSessionSync.d.ts +23 -0
  140. package/lib/typescript/module/ui/session/useBackgroundSessionSync.d.ts.map +1 -0
  141. package/lib/typescript/module/ui/types/navigation.d.ts +7 -0
  142. package/lib/typescript/module/ui/types/navigation.d.ts.map +1 -1
  143. package/lib/typescript/module/ui/utils/userUtils.d.ts +7 -12
  144. package/lib/typescript/module/ui/utils/userUtils.d.ts.map +1 -1
  145. package/package.json +5 -5
  146. package/src/ui/components/OxyProvider.tsx +2 -0
  147. package/src/ui/components/ProfileButton.tsx +5 -2
  148. package/src/ui/components/internal/PinInput.tsx +1 -0
  149. package/src/ui/components/logo/LogoText.tsx +4 -4
  150. package/src/ui/components/payment/PaymentSummaryStep.tsx +2 -2
  151. package/src/ui/context/OxyContext.tsx +14 -0
  152. package/src/ui/context/oxyContextTypes.ts +22 -0
  153. package/src/ui/screens/AccountSettingsScreen.tsx +4 -4
  154. package/src/ui/screens/ChangeAvatarScreen.tsx +6 -3
  155. package/src/ui/screens/EditProfileScreen.tsx +6 -3
  156. package/src/ui/screens/FeedbackScreen.tsx +1 -0
  157. package/src/ui/screens/ManageAccountScreen.tsx +8 -1
  158. package/src/ui/screens/PremiumSubscriptionScreen.tsx +2 -2
  159. package/src/ui/screens/PrivacySettingsScreen.tsx +13 -11
  160. package/src/ui/screens/ProfileScreen.tsx +3 -1
  161. package/src/ui/screens/UserListScreen.tsx +2 -2
  162. package/src/ui/screens/trust/TrustCenterScreen.tsx +2 -2
  163. package/src/ui/screens/trust/TrustLeaderboardScreen.tsx +3 -3
  164. package/src/ui/screens/trust/TrustRewardsScreen.tsx +1 -1
  165. package/src/ui/screens/trust/TrustRulesScreen.tsx +1 -1
  166. package/src/ui/screens/trust/trustTier.ts +1 -1
  167. package/src/ui/session/__tests__/backgroundSession.test.ts +337 -0
  168. package/src/ui/session/backgroundSession.ts +268 -0
  169. package/src/ui/session/index.ts +1 -0
  170. package/src/ui/session/useBackgroundSessionSync.ts +93 -0
  171. package/src/ui/types/navigation.ts +7 -0
  172. package/src/ui/utils/userUtils.ts +34 -20
@@ -1 +1 @@
1
- {"version":3,"file":"FeedbackScreen.d.ts","sourceRoot":"","sources":["../../../../../src/ui/screens/FeedbackScreen.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAO/B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAqB,CAAC;AAyC3D,QAAA,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAsqB7C,CAAC;AAwBF,eAAe,cAAc,CAAC"}
1
+ {"version":3,"file":"FeedbackScreen.d.ts","sourceRoot":"","sources":["../../../../../src/ui/screens/FeedbackScreen.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAO/B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAqB,CAAC;AAyC3D,QAAA,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAuqB7C,CAAC;AAwBF,eAAe,cAAc,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"ManageAccountScreen.d.ts","sourceRoot":"","sources":["../../../../../src/ui/screens/ManageAccountScreen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAoB/B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAqB,CAAC;AAqD3D;;;;;;;GAOG;AACH,QAAA,MAAM,mBAAmB,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CA00BlD,CAAC;AAYF,eAAe,mBAAmB,CAAC"}
1
+ {"version":3,"file":"ManageAccountScreen.d.ts","sourceRoot":"","sources":["../../../../../src/ui/screens/ManageAccountScreen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAqB/B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAqB,CAAC;AAqD3D;;;;;;;GAOG;AACH,QAAA,MAAM,mBAAmB,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAg1BlD,CAAC;AAYF,eAAe,mBAAmB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"PrivacySettingsScreen.d.ts","sourceRoot":"","sources":["../../../../../src/ui/screens/PrivacySettingsScreen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA2C,MAAM,OAAO,CAAC;AAEhE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAqB,CAAC;;AAia3D,wBAAiD"}
1
+ {"version":3,"file":"PrivacySettingsScreen.d.ts","sourceRoot":"","sources":["../../../../../src/ui/screens/PrivacySettingsScreen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA2C,MAAM,OAAO,CAAC;AAEhE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAqB,CAAC;;AAma3D,wBAAiD"}
@@ -1 +1 @@
1
- {"version":3,"file":"ProfileScreen.d.ts","sourceRoot":"","sources":["../../../../../src/ui/screens/ProfileScreen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAqB,CAAC;AAe3D,UAAU,kBAAmB,SAAQ,eAAe;IAChD,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAOD,QAAA,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAkS/C,CAAC;AAuCF,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"ProfileScreen.d.ts","sourceRoot":"","sources":["../../../../../src/ui/screens/ProfileScreen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAqB,CAAC;AAe3D,UAAU,kBAAmB,SAAQ,eAAe;IAChD,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAOD,QAAA,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAoS/C,CAAC;AAuCF,eAAe,aAAa,CAAC"}
@@ -1,4 +1,4 @@
1
- import type { TrustTier } from '@oxyhq/core';
1
+ import type { TrustTier } from '@oxyhq/contracts';
2
2
  type Translate = (key: string, vars?: Record<string, string | number>) => string;
3
3
  /**
4
4
  * Resolve a human-readable label for a trust tier, preferring the localized
@@ -1 +1 @@
1
- {"version":3,"file":"trustTier.d.ts","sourceRoot":"","sources":["../../../../../../src/ui/screens/trust/trustTier.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,KAAK,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,KAAK,MAAM,CAAC;AAWjF;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,GAAG,MAAM,CAEvE"}
1
+ {"version":3,"file":"trustTier.d.ts","sourceRoot":"","sources":["../../../../../../src/ui/screens/trust/trustTier.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAElD,KAAK,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,KAAK,MAAM,CAAC;AAWjF;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,GAAG,MAAM,CAEvE"}
@@ -0,0 +1,42 @@
1
+ import type { OxyServices } from '@oxyhq/core';
2
+ /** Whether this platform/app can hold a background credential at all. */
3
+ export declare function isBackgroundSessionSupported(): boolean;
4
+ /**
5
+ * Drop the stored credential. No-op where unsupported, and never throws.
6
+ *
7
+ * Called on sign-out and BEFORE an account switch's network work, so the window
8
+ * in which background code could still serve the previous account is as short as
9
+ * one local write.
10
+ */
11
+ export declare function clearBackgroundSession(): Promise<void>;
12
+ export interface BackgroundSessionSyncInput {
13
+ oxyServices: OxyServices;
14
+ /** The signed-in account, or `null` when signed out. */
15
+ userId: string | null;
16
+ /** Whether a bearer is available yet — provisioning requires one. */
17
+ canUsePrivateApi: boolean;
18
+ /**
19
+ * Whether this run is still the current one. A run that loses its relevance
20
+ * mid-flight (unmount, or another identity change) must not write the
21
+ * credential it was about to write.
22
+ */
23
+ isCurrent: () => boolean;
24
+ }
25
+ /**
26
+ * Bring the native credential in line with the session JS currently holds.
27
+ *
28
+ * Ordering is the load-bearing part: a credential belonging to a DIFFERENT
29
+ * account is cleared before any network call, so a crash mid-provision leaves
30
+ * background code signed out rather than serving the previous account's data
31
+ * under the new account's name. The server enforces the same rule independently
32
+ * (a mint is re-authorized against the live device session, and against its ACTIVE
33
+ * account), so this is the fast local half of a belt-and-braces pair, not the only
34
+ * guard.
35
+ *
36
+ * Throws for exactly one thing — a `@oxyhq/core` too old to provision (see
37
+ * {@link assertProvisioningAvailable}). Every other failure is caught and logged:
38
+ * a background credential is an enhancement, and failing to provision one must
39
+ * not disturb the session that is working.
40
+ */
41
+ export declare function syncBackgroundSession(input: BackgroundSessionSyncInput): Promise<void>;
42
+ //# sourceMappingURL=backgroundSession.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backgroundSession.d.ts","sourceRoot":"","sources":["../../../../../src/ui/session/backgroundSession.ts"],"names":[],"mappings":"AAiCA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAgE/C,yEAAyE;AACzE,wBAAgB,4BAA4B,IAAI,OAAO,CAEtD;AAED;;;;;;GAMG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC,CAa5D;AAED,MAAM,WAAW,0BAA0B;IACzC,WAAW,EAAE,WAAW,CAAC;IACzB,wDAAwD;IACxD,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,qEAAqE;IACrE,gBAAgB,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,SAAS,EAAE,MAAM,OAAO,CAAC;CAC1B;AA8BD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,qBAAqB,CAAC,KAAK,EAAE,0BAA0B,GAAG,OAAO,CAAC,IAAI,CAAC,CAqF5F"}
@@ -13,6 +13,7 @@
13
13
  export { createSessionClient } from './createSessionClient.js';
14
14
  export { createTokenTransport } from './tokenTransport.js';
15
15
  export { createPlatformAuthStateStore } from './authStore.js';
16
+ export { useBackgroundSessionSync } from './useBackgroundSessionSync.js';
16
17
  export { createIdentitySessionBinding, IdentityBoundSessionError } from './identityBinding.js';
17
18
  export type { IdentitySessionBinding } from './identityBinding.js';
18
19
  export { createSessionClientHost, accountIdsOf, activeSessionIdOf, activeUserOf, deviceStateToClientSessions, } from '@oxyhq/core';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/ui/session/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAAuB,CAAC;AAC5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAkB,CAAC;AACxD,OAAO,EAAE,4BAA4B,EAAE,MAAM,gBAAa,CAAC;AAC3D,OAAO,EAAE,4BAA4B,EAAE,yBAAyB,EAAE,MAAM,sBAAmB,CAAC;AAC5F,YAAY,EAAE,sBAAsB,EAAE,MAAM,sBAAmB,CAAC;AAChE,OAAO,EACL,uBAAuB,EACvB,YAAY,EACZ,iBAAiB,EACjB,YAAY,EACZ,2BAA2B,GAC5B,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/ui/session/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAAuB,CAAC;AAC5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAkB,CAAC;AACxD,OAAO,EAAE,4BAA4B,EAAE,MAAM,gBAAa,CAAC;AAC3D,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA4B,CAAC;AACtE,OAAO,EAAE,4BAA4B,EAAE,yBAAyB,EAAE,MAAM,sBAAmB,CAAC;AAC5F,YAAY,EAAE,sBAAsB,EAAE,MAAM,sBAAmB,CAAC;AAChE,OAAO,EACL,uBAAuB,EACvB,YAAY,EACZ,iBAAiB,EACjB,YAAY,EACZ,2BAA2B,GAC5B,MAAM,aAAa,CAAC"}
@@ -0,0 +1,23 @@
1
+ import type { OxyServices } from '@oxyhq/core';
2
+ export interface UseBackgroundSessionSyncInput {
3
+ /** `OxyProviderProps.backgroundSession`. */
4
+ enabled: boolean;
5
+ oxyServices: OxyServices;
6
+ /** The signed-in account, or `null` when signed out. */
7
+ userId: string | null;
8
+ /** Whether a bearer is available yet — provisioning needs one. */
9
+ canUsePrivateApi: boolean;
10
+ }
11
+ /**
12
+ * An effect is the right shape here, unusually for this codebase: the work is a
13
+ * side effect on native storage, and it has to react both to an identity change
14
+ * (a value) and to the app being foregrounded (a subscription). There is no
15
+ * derived-state or React Query formulation of "write a credential to the
16
+ * keystore when the user changes or the app comes back".
17
+ *
18
+ * Re-runs on identity change, which is what makes sign-out and account switching
19
+ * work: `syncBackgroundSession` clears a credential belonging to anyone other
20
+ * than `userId` before it does any network work.
21
+ */
22
+ export declare function useBackgroundSessionSync({ enabled, oxyServices, userId, canUsePrivateApi, }: UseBackgroundSessionSyncInput): void;
23
+ //# sourceMappingURL=useBackgroundSessionSync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBackgroundSessionSync.d.ts","sourceRoot":"","sources":["../../../../../src/ui/session/useBackgroundSessionSync.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAO/C,MAAM,WAAW,6BAA6B;IAC5C,4CAA4C;IAC5C,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,WAAW,CAAC;IACzB,wDAAwD;IACxD,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,kEAAkE;IAClE,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CAAC,EACvC,OAAO,EACP,WAAW,EACX,MAAM,EACN,gBAAgB,GACjB,EAAE,6BAA6B,GAAG,IAAI,CAiDtC"}
@@ -116,5 +116,12 @@ export interface OxyProviderProps {
116
116
  * @default 'off'
117
117
  */
118
118
  requireAuth?: 'off' | 'soft' | 'hard';
119
+ /**
120
+ * When true, provisions a non-rotating background credential for native code
121
+ * (Android widgets) that runs without a JS runtime. Android-only today; inert
122
+ * on web and iOS.
123
+ * @default false
124
+ */
125
+ backgroundSession?: boolean;
119
126
  }
120
127
  //# sourceMappingURL=navigation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../../../../../src/ui/types/navigation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAClD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAsB,CAAC;AAEtD,OAAO,KAAK,EAAiB,WAAW,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAgB,CAAC;AAElD,MAAM,WAAW,cAAc;IAC3B,SAAS,EAAE,MAAM,OAAO,CAAC;IACzB,MAAM,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAE5B,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IACxE,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,mFAAmF;IACnF,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,eAAe,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAC9C;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAGrC,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IAGlC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IACrD,YAAY,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAGjE,aAAa,CAAC,EAAE,SAAS,CAAC;IAG1B,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAGnD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,OAAO,CAAC;IAMtB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAC7B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5C,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;;;;OAQG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B;;;;;;;;;;;;;;;;;OAiBG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;CACzC"}
1
+ {"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../../../../../src/ui/types/navigation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAClD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAsB,CAAC;AAEtD,OAAO,KAAK,EAAiB,WAAW,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAgB,CAAC;AAElD,MAAM,WAAW,cAAc;IAC3B,SAAS,EAAE,MAAM,OAAO,CAAC;IACzB,MAAM,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAE5B,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IACxE,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,mFAAmF;IACnF,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,eAAe,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAC9C;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAGrC,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IAGlC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IACrD,YAAY,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAGjE,aAAa,CAAC,EAAE,SAAS,CAAC;IAG1B,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAGnD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,OAAO,CAAC;IAMtB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAC7B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5C,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;;;;OAQG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B;;;;;;;;;;;;;;;;;OAiBG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;IACtC;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC/B"}
@@ -1,9 +1,10 @@
1
1
  /**
2
2
  * User display name and date formatting utilities.
3
3
  *
4
- * The display-name helpers are thin wrappers around the canonical
5
- * `getAccountDisplayName` in `@oxyhq/core`, which handles all the edge cases
6
- * (missing username, missing name, fallback to truncated `publicKey`, i18n).
4
+ * Display-name helpers follow the API contract: prefer `name.displayName` when
5
+ * present, otherwise fall back to the normalized handle. The legacy
6
+ * `getAccountDisplayName` chain is only used as a last resort for local
7
+ * account-switcher surfaces without a handle.
7
8
  */
8
9
  import { type DisplayNameUserShape } from '@oxyhq/core';
9
10
  /**
@@ -13,18 +14,12 @@ export declare const formatDate: (dateString: string | undefined | null | Date)
13
14
  /**
14
15
  * Gets a display name from user data.
15
16
  *
16
- * Prefers full name composed first+last username `Account 0x12345678…`
17
- * (derived from publicKey) → translated "Unnamed".
18
- *
19
- * @param user - User-like object with optional name/username/publicKey
20
- * @param locale - Optional locale for the final fallback label
17
+ * Prefers API `name.displayName`, then the normalized handle, then the local
18
+ * account-switcher fallback chain.
21
19
  */
22
20
  export declare const getDisplayName: (user: DisplayNameUserShape | null | undefined, locale?: string) => string;
23
21
  /**
24
- * Gets a short display name (first name or username).
25
- *
26
- * Used for compact UI (e.g. greetings). Falls back to the full display name
27
- * helper, which also handles publicKey-only accounts.
22
+ * Gets a short display name (first token) for compact UI.
28
23
  */
29
24
  export declare const getShortDisplayName: (user: DisplayNameUserShape | null | undefined, locale?: string) => string;
30
25
  //# sourceMappingURL=userUtils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"userUtils.d.ts","sourceRoot":"","sources":["../../../../../src/ui/utils/userUtils.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,aAAa,CAAC;AAErB;;GAEG;AACH,eAAO,MAAM,UAAU,GAAI,YAAY,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,IAAI,KAAG,MAezE,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,GACzB,MAAM,oBAAoB,GAAG,IAAI,GAAG,SAAS,EAC7C,SAAS,MAAM,KACd,MAAiD,CAAC;AAErD;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,GAC9B,MAAM,oBAAoB,GAAG,IAAI,GAAG,SAAS,EAC7C,SAAS,MAAM,KACd,MAiBF,CAAC"}
1
+ {"version":3,"file":"userUtils.d.ts","sourceRoot":"","sources":["../../../../../src/ui/utils/userUtils.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAGL,KAAK,oBAAoB,EAC1B,MAAM,aAAa,CAAC;AAErB;;GAEG;AACH,eAAO,MAAM,UAAU,GAAI,YAAY,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,IAAI,KAAG,MAezE,CAAC;AAQF;;;;;GAKG;AACH,eAAO,MAAM,cAAc,GACzB,MAAM,oBAAoB,GAAG,IAAI,GAAG,SAAS,EAC7C,SAAS,MAAM,KACd,MAQF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,mBAAmB,GAC9B,MAAM,oBAAoB,GAAG,IAAI,GAAG,SAAS,EAC7C,SAAS,MAAM,KACd,MAqBF,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxyhq/services",
3
- "version": "23.2.0",
3
+ "version": "24.0.0",
4
4
  "description": "OxyHQ Expo/React Native SDK — UI components, screens, and native features",
5
5
  "main": "lib/commonjs/index.js",
6
6
  "module": "lib/module/index.js",
@@ -138,8 +138,8 @@
138
138
  }
139
139
  },
140
140
  "dependencies": {
141
- "@oxyhq/contracts": "0.19.0",
142
- "@oxyhq/core": "^14.0.0",
141
+ "@oxyhq/contracts": "0.20.0",
142
+ "@oxyhq/core": "^15.0.0",
143
143
  "@simplewebauthn/browser": "^13.3.0",
144
144
  "color": "^4.2.3"
145
145
  },
@@ -148,7 +148,7 @@
148
148
  "@commitlint/cli": "^21.2.1",
149
149
  "@commitlint/config-conventional": "^21.2.0",
150
150
  "@expo/vector-icons": "^15.0.3",
151
- "@oxyhq/core": "14.0.0",
151
+ "@oxyhq/core": "15.0.0",
152
152
  "@react-native-async-storage/async-storage": "^2.0.0",
153
153
  "@react-native-community/netinfo": "^11.4.1",
154
154
  "@react-native/babel-preset": "^0.86.0",
@@ -190,7 +190,7 @@
190
190
  "peerDependencies": {
191
191
  "@expo/vector-icons": "^15.0.3",
192
192
  "@oxyhq/bloom": ">=0.59.0",
193
- "@oxyhq/core": "^13.0.0",
193
+ "@oxyhq/core": "^15.0.0",
194
194
  "@react-native-async-storage/async-storage": "^2.0.0",
195
195
  "@react-native-community/netinfo": ">=11.4.1",
196
196
  "@tanstack/query-async-storage-persister": "^5.101",
@@ -96,6 +96,7 @@ const OxyProvider: FC<OxyProviderProps> = ({
96
96
  webAuthMode = 'popup',
97
97
  queryClient: providedQueryClient,
98
98
  requireAuth = 'off',
99
+ backgroundSession = false,
99
100
  }) => {
100
101
 
101
102
  // Dynamic KeyboardProvider for native. Uses variable indirection
@@ -293,6 +294,7 @@ const OxyProvider: FC<OxyProviderProps> = ({
293
294
  clientId={clientId}
294
295
  sessionMode={sessionMode}
295
296
  webAuthMode={webAuthMode}
297
+ backgroundSession={backgroundSession}
296
298
  onAuthStateChange={onAuthStateChange as OxyContextProviderProps['onAuthStateChange']}
297
299
  >
298
300
  <SurfaceProvider>
@@ -13,7 +13,7 @@ import { Avatar } from '@oxyhq/bloom/avatar';
13
13
  import * as Skeleton from '@oxyhq/bloom/skeleton';
14
14
  import { Text } from '@oxyhq/bloom/typography';
15
15
  import { useTheme } from '@oxyhq/bloom/theme';
16
- import { getAccountDisplayName, getAccountFallbackHandle } from '@oxyhq/core';
16
+ import { getAccountDisplayName, getAccountFallbackHandle, getNormalizedUserHandle } from '@oxyhq/core';
17
17
  import { useAuth } from '../hooks/useAuth';
18
18
  import { useOxy } from '../context/OxyContext';
19
19
  import { useI18n } from '../hooks/useI18n';
@@ -201,7 +201,10 @@ const ProfileButton: React.FC<ProfileButtonProps> = ({
201
201
  }
202
202
 
203
203
  // ── Signed in: avatar + identity + chevron. ─────────────────────────────
204
- const displayName = getAccountDisplayName(user, locale);
204
+ const displayName =
205
+ user?.name?.displayName ??
206
+ getNormalizedUserHandle(user) ??
207
+ getAccountDisplayName(null, locale);
205
208
  const handle = getAccountFallbackHandle(user);
206
209
  const handleLine = handle ? `@${handle}` : null;
207
210
 
@@ -64,6 +64,7 @@ const PinInput = forwardRef<PinInputHandle, PinInputProps>(({ value, onChange, l
64
64
  <View style={styles.pinContainer}>
65
65
  {Array.from({ length }).map((_, idx) => (
66
66
  <TextInput
67
+ // biome-ignore lint/suspicious/noArrayIndexKey: fixed-position PIN cells, order never changes
67
68
  key={`pin-input-${idx}`}
68
69
  ref={(ref) => { inputs.current[idx] = ref; }}
69
70
  style={[
@@ -55,13 +55,13 @@ export const LogoText: React.FC<LogoTextProps> = ({
55
55
  return (
56
56
  <Svg viewBox={`0 0 ${VIEW_BOX_WIDTH} ${VIEW_BOX_HEIGHT}`} width={width} height={height} style={style}>
57
57
  <G>
58
- {OUTER_PATHS.map((d, i) => (
59
- <Path key={`outer-${i}`} d={d} fill={resolvedColor} />
58
+ {OUTER_PATHS.map((d) => (
59
+ <Path key={d} d={d} fill={resolvedColor} />
60
60
  ))}
61
61
  </G>
62
62
  <G transform="translate(10.316 2.495)">
63
- {INNER_LETTER_PATHS.map((d, i) => (
64
- <Path key={`inner-${i}`} d={d} fill={resolvedLetterColor} />
63
+ {INNER_LETTER_PATHS.map((d) => (
64
+ <Path key={d} d={d} fill={resolvedLetterColor} />
65
65
  ))}
66
66
  </G>
67
67
  </Svg>
@@ -85,9 +85,9 @@ const PaymentSummaryStep: React.FC<PaymentSummaryStepProps> = ({
85
85
  <>
86
86
  <View style={styles.summaryCardItems}>
87
87
  <SettingsListGroup>
88
- {paymentItems.map((item, idx) => (
88
+ {paymentItems.map((item) => (
89
89
  <SettingsListItem
90
- key={`item-${idx}`}
90
+ key={`${item.type}-${item.name}-${item.price}-${item.period ?? ''}`}
91
91
  icon={<Ionicons name={getItemTypeIcon(item.type)} size={20} color={colors.primary} />}
92
92
  title={`${item.type === 'product' && item.quantity ? `${item.quantity} \u00d7 ` : ''}${item.name}${item.type === 'subscription' && item.period ? ` (${item.period})` : ''}`}
93
93
  description={item.description || `${item.currency ? (CURRENCY_SYMBOLS[item.currency.toUpperCase()] || item.currency) : currencySymbol} ${item.price * (item.quantity ?? 1)}`}
@@ -70,6 +70,7 @@ import {
70
70
  activeUserOf,
71
71
  accountIdsOf,
72
72
  IdentityBoundSessionError,
73
+ useBackgroundSessionSync,
73
74
  type IdentitySessionBinding,
74
75
  } from '../session';
75
76
  import type {
@@ -103,6 +104,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
103
104
  clientId: clientIdProp,
104
105
  sessionMode = 'account',
105
106
  webAuthMode = 'popup',
107
+ backgroundSession = false,
106
108
  onAuthStateChange,
107
109
  onError,
108
110
  }) => {
@@ -1195,6 +1197,18 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
1195
1197
  const canUsePrivateApi = authResolved && isAuthenticated && tokenReady && hasAccessToken;
1196
1198
  const isPrivateApiPending = !authResolved || (isAuthenticated && (!tokenReady || !hasAccessToken));
1197
1199
 
1200
+ // Keep the native background credential in step with this session, so native
1201
+ // background code (a widget worker) can authenticate with no JS runtime. Inert
1202
+ // unless the app opted in. Placed AFTER `canUsePrivateApi` because provisioning
1203
+ // needs a bearer, and keyed on the signed-in account so a sign-out or an
1204
+ // account switch revokes the credential before anything else happens.
1205
+ useBackgroundSessionSync({
1206
+ enabled: backgroundSession,
1207
+ oxyServices,
1208
+ userId: user?.id ?? null,
1209
+ canUsePrivateApi,
1210
+ });
1211
+
1198
1212
  const contextValue: OxyContextState = useMemo(
1199
1213
  () => ({
1200
1214
  user,
@@ -176,6 +176,28 @@ export interface OxyContextProviderProps {
176
176
  * @default 'popup'
177
177
  */
178
178
  webAuthMode?: WebAuthMode;
179
+ /**
180
+ * Let NATIVE background code (a home-screen widget's WorkManager worker, a sync
181
+ * job) authenticate while the app process is dead and there is no JS runtime.
182
+ *
183
+ * With this on, the SDK provisions a purpose-built background credential
184
+ * whenever the app runs and stores it where `so.oxy.session.OxyBackgroundSession`
185
+ * can read it; Kotlin then exchanges it for a short access token on demand. The
186
+ * app writes no session code — this prop is the whole integration.
187
+ *
188
+ * OFF by default, because a credential that nobody needs is exposure that
189
+ * nobody needs. Turn it on only in an app that actually has background code to
190
+ * authenticate. Android-only today; inert on web and iOS.
191
+ *
192
+ * The credential cannot create a session (only the running app can provision
193
+ * one), cannot rotate or otherwise disturb the app's own device credential, and
194
+ * is re-authorized server-side on every use so it can never outlive a sign-out.
195
+ * It does NOT reduce authority: the token it mints is a full user access token,
196
+ * exactly like the device credential's.
197
+ *
198
+ * @default false
199
+ */
200
+ backgroundSession?: boolean;
179
201
  onAuthStateChange?: (user: User | null) => void;
180
202
  onError?: (error: ApiError) => void;
181
203
  }
@@ -12,7 +12,7 @@ import { Text } from '@oxyhq/bloom/typography';
12
12
  import { Button } from '@oxyhq/bloom/button';
13
13
  import { TextField, TextFieldInput } from '@oxyhq/bloom/text-field';
14
14
  import { SettingsListGroup, SettingsListItem } from '@oxyhq/bloom/settings-list';
15
- import type { UpdateAccountInput } from '@oxyhq/core';
15
+ import { getNormalizedUserHandle, type UpdateAccountInput } from '@oxyhq/core';
16
16
  import type { BaseScreenProps } from '../types/navigation';
17
17
  import { SettingsIcon } from '../components/SettingsIcon';
18
18
  import { useOxy } from '../context/OxyContext';
@@ -67,9 +67,9 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({ onClose, goBack, nav
67
67
  // Seed the form from the account during render (no useEffect): the first time
68
68
  // the query resolves we capture its values into local edit state.
69
69
  if (node && !seeded) {
70
- const first = node.account?.name?.first ?? '';
71
- const last = node.account?.name?.last ?? '';
72
- setDisplayName([first, last].filter(Boolean).join(' '));
70
+ setDisplayName(
71
+ node.account?.name?.displayName ?? getNormalizedUserHandle(node.account) ?? '',
72
+ );
73
73
  setBio(node.account?.bio ?? '');
74
74
  setSeeded(true);
75
75
  }
@@ -40,7 +40,7 @@ import {
40
40
  import { surfaces as bloomSurfaces } from '@oxyhq/bloom/surfaces';
41
41
  import { useTheme } from '@oxyhq/bloom/theme';
42
42
  import { toast } from '@oxyhq/bloom';
43
- import { getAccountDisplayName, logger } from '@oxyhq/core';
43
+ import { getNormalizedUserHandle, logger } from '@oxyhq/core';
44
44
  import type { FileMetadata } from '@oxyhq/core';
45
45
  import { useOxy } from '../context/OxyContext';
46
46
  import { useI18n } from '../hooks/useI18n';
@@ -133,7 +133,7 @@ async function loadImagePicker(): Promise<ImagePickerModule | null> {
133
133
 
134
134
  const ChangeAvatarScreen: React.FC<BaseScreenProps> = ({ navigate, dismiss }) => {
135
135
  const bloomTheme = useTheme();
136
- const { t, locale } = useI18n();
136
+ const { t } = useI18n();
137
137
  const { user, oxyServices } = useOxy();
138
138
 
139
139
  /** Set when a permission was refused — drives the inline denied notice. */
@@ -143,7 +143,10 @@ const ChangeAvatarScreen: React.FC<BaseScreenProps> = ({ navigate, dismiss }) =>
143
143
 
144
144
  useSurfaceHeader(useMemo(() => ({ title: t('changeAvatar.title') }), [t]));
145
145
 
146
- const displayName = useMemo(() => getAccountDisplayName(user, locale), [user, locale]);
146
+ const displayName = useMemo(
147
+ () => user?.name?.displayName ?? getNormalizedUserHandle(user) ?? '',
148
+ [user],
149
+ );
147
150
  const avatarUri = useMemo(
148
151
  // `thumb` is 256x256 — exactly a 2x source for the 132dp hero.
149
152
  () => (user?.avatar ? oxyServices.getFileDownloadUrl(user.avatar, 'thumb') : undefined),
@@ -3,7 +3,7 @@ import { useMemo } from 'react';
3
3
  import { View } from 'react-native';
4
4
  import { useTheme } from '@oxyhq/bloom/theme';
5
5
  import { SettingsListGroup, SettingsListItem } from '@oxyhq/bloom/settings-list';
6
- import { getAccountDisplayName } from '@oxyhq/core';
6
+ import { getNormalizedUserHandle } from '@oxyhq/core';
7
7
  import type { BaseScreenProps } from '../types/navigation';
8
8
  import { Avatar } from '@oxyhq/bloom/avatar';
9
9
  import ProfileSummaryCard from '../components/ProfileSummaryCard';
@@ -24,12 +24,15 @@ import type { ProfileFieldType } from './EditProfileFieldScreen';
24
24
  */
25
25
  const EditProfileScreen: React.FC<BaseScreenProps> = ({ navigate }) => {
26
26
  const bloomTheme = useTheme();
27
- const { t, locale } = useI18n();
27
+ const { t } = useI18n();
28
28
  const { user, oxyServices, openAvatarPicker } = useOxy();
29
29
 
30
30
  useSurfaceHeader({ title: t('editProfile.title') || 'Edit Profile' });
31
31
 
32
- const displayName = useMemo(() => getAccountDisplayName(user, locale), [user, locale]);
32
+ const displayName = useMemo(
33
+ () => user?.name?.displayName ?? getNormalizedUserHandle(user) ?? '',
34
+ [user],
35
+ );
33
36
  const avatarUri = useMemo(
34
37
  () => (user?.avatar ? oxyServices.getFileDownloadUrl(user.avatar, 'thumb') : undefined),
35
38
  [user?.avatar, oxyServices],
@@ -283,6 +283,7 @@ const FeedbackScreen: React.FC<BaseScreenProps> = ({
283
283
  >
284
284
  {Array.from({ length: TOTAL_STEPS }, (_, index) => (
285
285
  <View
286
+ // biome-ignore lint/suspicious/noArrayIndexKey: fixed step indicators, order never changes
286
287
  key={index}
287
288
  className={
288
289
  currentStep === index
@@ -15,6 +15,7 @@ import { SettingsListGroup, SettingsListItem } from '@oxyhq/bloom/settings-list'
15
15
  import {
16
16
  getAccountDisplayName,
17
17
  getAccountFallbackHandle,
18
+ getNormalizedUserHandle,
18
19
  logger as loggerUtil,
19
20
  packageInfo,
20
21
  } from '@oxyhq/core';
@@ -120,7 +121,13 @@ const ManageAccountScreen: React.FC<BaseScreenProps> = ({
120
121
  const [signingOutAllDevices, setSigningOutAllDevices] = useState(false);
121
122
  const [signingOut, setSigningOut] = useState(false);
122
123
 
123
- const displayName = useMemo(() => getAccountDisplayName(user, locale), [user, locale]);
124
+ const displayName = useMemo(
125
+ () =>
126
+ user?.name?.displayName ??
127
+ getNormalizedUserHandle(user) ??
128
+ getAccountDisplayName(null, locale),
129
+ [user, locale],
130
+ );
124
131
  const handle = useMemo(() => getAccountFallbackHandle(user), [user]);
125
132
  const avatarUri = useMemo(() => {
126
133
  return user?.avatar
@@ -741,9 +741,9 @@ const PremiumSubscriptionScreen: React.FC<BaseScreenProps> = ({
741
741
  </View>
742
742
 
743
743
  <BenefitList className="mb-space-24">
744
- {plan.features.map((feature, index) => (
744
+ {plan.features.map((feature) => (
745
745
  <BenefitRow
746
- key={index}
746
+ key={feature}
747
747
  icon={<Ionicons name="checkmark" size={18} color={colors.success} />}
748
748
  label={feature}
749
749
  />
@@ -14,7 +14,7 @@ import { useI18n } from '../hooks/useI18n';
14
14
  import { useSurfaceHeader } from '../hooks/useSurfaceHeader';
15
15
  import { useSettingToggles } from '../hooks/useSettingToggle';
16
16
  import type { BlockedUser, RestrictedUser } from '@oxyhq/core';
17
- import { getAccountDisplayName } from '@oxyhq/core';
17
+ import { getNormalizedUserHandle } from '@oxyhq/core';
18
18
  import { useOxy } from '../context/OxyContext';
19
19
 
20
20
  interface PrivacySettings {
@@ -68,7 +68,7 @@ const PrivacySettingsScreen: React.FC<BaseScreenProps> = ({
68
68
  // Privacy settings belong to the ACTIVE account (the org/project/bot when
69
69
  // switched, else the personal user).
70
70
  const { oxyServices, user } = useOxy();
71
- const { t, locale } = useI18n();
71
+ const { t } = useI18n();
72
72
 
73
73
  useSurfaceHeader({ title: t('privacySettings.title') || 'Privacy Settings' });
74
74
  const bloomTheme = useTheme();
@@ -176,35 +176,37 @@ const PrivacySettingsScreen: React.FC<BaseScreenProps> = ({
176
176
  }, [oxyServices, t]);
177
177
 
178
178
  // Helper to extract user info from blocked/restricted objects.
179
- // Display names go through the canonical helper so the fallback chain is
180
- // identical across every UI surface (name → username → publicKey → "Unnamed").
181
179
  const extractUserInfo = useCallback((
182
180
  item: BlockedUser | RestrictedUser,
183
181
  idField: 'blockedId' | 'restrictedId'
184
182
  ) => {
185
- let userIdField: string | { _id: string; username?: string; avatar?: string };
186
- let userShape: { username?: string };
183
+ let userIdField: string | { _id: string; username?: string; avatar?: string; name?: { displayName?: string } };
184
+ let userShape: { username?: string; name?: { displayName?: string } };
187
185
  let avatar: string | undefined;
188
186
 
189
187
  if (idField === 'blockedId' && 'blockedId' in item) {
190
188
  userIdField = item.blockedId;
191
189
  userShape = typeof item.blockedId === 'string'
192
190
  ? { username: item.username }
193
- : { username: item.blockedId.username };
191
+ : { username: item.blockedId.username, name: item.blockedId.name };
194
192
  avatar = typeof item.blockedId === 'string' ? item.avatar : item.blockedId.avatar;
195
193
  } else if (idField === 'restrictedId' && 'restrictedId' in item) {
196
194
  userIdField = item.restrictedId;
197
195
  userShape = typeof item.restrictedId === 'string'
198
196
  ? { username: item.username }
199
- : { username: item.restrictedId.username };
197
+ : { username: item.restrictedId.username, name: item.restrictedId.name };
200
198
  avatar = typeof item.restrictedId === 'string' ? item.avatar : item.restrictedId.avatar;
201
199
  } else {
202
- return { userId: '', displayName: getAccountDisplayName(null, locale), avatar: undefined };
200
+ return { userId: '', displayName: getNormalizedUserHandle(null) ?? '', avatar: undefined };
203
201
  }
204
202
 
205
203
  const userId = typeof userIdField === 'string' ? userIdField : userIdField._id;
206
- return { userId, displayName: getAccountDisplayName(userShape, locale), avatar };
207
- }, [locale]);
204
+ return {
205
+ userId,
206
+ displayName: userShape.name?.displayName ?? getNormalizedUserHandle(userShape) ?? '',
207
+ avatar,
208
+ };
209
+ }, []);
208
210
 
209
211
  if (isLoading) {
210
212
  return (
@@ -78,7 +78,9 @@ const ProfileScreen: React.FC<ProfileScreenProps> = ({ userId, username, theme,
78
78
  );
79
79
  throw err;
80
80
  }),
81
- oxyServices.getReputationBalance(userId)
81
+ (isOwnProfile
82
+ ? oxyServices.getMyReputationBalance()
83
+ : oxyServices.getReputationBalance(userId))
82
84
  .then((balance): { total: number | undefined } => ({ total: balance.total }))
83
85
  .catch((): { total: number | undefined } => ({ total: undefined })),
84
86
  oxyServices.getUserStats
@@ -65,7 +65,7 @@ const UserListScreen: React.FC<UserListScreenProps> = ({
65
65
  const [hasMore, setHasMore] = useState(true);
66
66
 
67
67
  const bloomTheme = useTheme();
68
- const { t, locale } = useI18n();
68
+ const { t } = useI18n();
69
69
 
70
70
  const currentUserId = currentUser ? resolveUserId(currentUser) : '';
71
71
 
@@ -199,7 +199,7 @@ const UserListScreen: React.FC<UserListScreenProps> = ({
199
199
  </TouchableOpacity>
200
200
  );
201
201
  },
202
- [handleUserPress, currentUserId, oxyServices, locale]
202
+ [handleUserPress, currentUserId, oxyServices]
203
203
  );
204
204
 
205
205
  const renderEmpty = useCallback(() => {
@@ -1,7 +1,7 @@
1
1
  import type React from 'react';
2
2
  import { useEffect, useMemo, useState } from 'react';
3
3
  import { View, StyleSheet } from 'react-native';
4
- import type { ReputationTransaction, TrustTier } from '@oxyhq/core';
4
+ import type { ReputationTransaction, TrustTier } from '@oxyhq/contracts';
5
5
  import Ionicons from '@expo/vector-icons/Ionicons';
6
6
  import { Chip } from '@oxyhq/bloom/chip';
7
7
  import { useTheme } from '@oxyhq/bloom/theme';
@@ -36,7 +36,7 @@ const TrustCenterScreen: React.FC<BaseScreenProps> = ({
36
36
  setIsLoading(true);
37
37
  setError(null);
38
38
  Promise.all([
39
- oxyServices.getReputationBalance(user.id),
39
+ oxyServices.getMyReputationBalance(),
40
40
  oxyServices.getReputationTransactions(user.id, 20, 0),
41
41
  ])
42
42
  .then(([balance, txns]) => {