@oxy.so/services 8.2.0 → 8.4.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 (251) hide show
  1. package/lib/commonjs/index.js +2 -0
  2. package/lib/commonjs/index.js.map +1 -1
  3. package/lib/commonjs/ui/components/BloomLocaleBridge.js +37 -0
  4. package/lib/commonjs/ui/components/BloomLocaleBridge.js.map +1 -0
  5. package/lib/commonjs/ui/components/OxyProvider.js +16 -7
  6. package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
  7. package/lib/commonjs/ui/components/OxySignInRequestSurface.js +0 -1
  8. package/lib/commonjs/ui/components/OxySignInRequestSurface.js.map +1 -1
  9. package/lib/commonjs/ui/components/RequireOxyAuth.js +9 -1
  10. package/lib/commonjs/ui/components/RequireOxyAuth.js.map +1 -1
  11. package/lib/commonjs/ui/components/SurfaceScreen.js +4 -10
  12. package/lib/commonjs/ui/components/SurfaceScreen.js.map +1 -1
  13. package/lib/commonjs/ui/components/authChooser/TroubleDisclosure.js +0 -1
  14. package/lib/commonjs/ui/components/authChooser/TroubleDisclosure.js.map +1 -1
  15. package/lib/commonjs/ui/components/authChooser/primitives.js +9 -11
  16. package/lib/commonjs/ui/components/authChooser/primitives.js.map +1 -1
  17. package/lib/commonjs/ui/components/authChooser/requestSurfaces.js +13 -10
  18. package/lib/commonjs/ui/components/authChooser/requestSurfaces.js.map +1 -1
  19. package/lib/commonjs/ui/components/authChooser/styles.js +4 -1
  20. package/lib/commonjs/ui/components/authChooser/styles.js.map +1 -1
  21. package/lib/commonjs/ui/components/payment/PaymentDetailsStep.js +2 -2
  22. package/lib/commonjs/ui/components/payment/PaymentDetailsStep.js.map +1 -1
  23. package/lib/commonjs/ui/components/signIn/InlineCommonsQr.js +5 -2
  24. package/lib/commonjs/ui/components/signIn/InlineCommonsQr.js.map +1 -1
  25. package/lib/commonjs/ui/components/signIn/OxyAccountPicker.js +13 -17
  26. package/lib/commonjs/ui/components/signIn/OxyAccountPicker.js.map +1 -1
  27. package/lib/commonjs/ui/components/signIn/OxyAuthScreen.js +9 -8
  28. package/lib/commonjs/ui/components/signIn/OxyAuthScreen.js.map +1 -1
  29. package/lib/commonjs/ui/components/signIn/OxyAuthenticatorPanel.js +38 -42
  30. package/lib/commonjs/ui/components/signIn/OxyAuthenticatorPanel.js.map +1 -1
  31. package/lib/commonjs/ui/components/signIn/OxyDeleteAccountPanel.js +21 -6
  32. package/lib/commonjs/ui/components/signIn/OxyDeleteAccountPanel.js.map +1 -1
  33. package/lib/commonjs/ui/components/signIn/OxyLinkCommonsPanel.js +4 -2
  34. package/lib/commonjs/ui/components/signIn/OxyLinkCommonsPanel.js.map +1 -1
  35. package/lib/commonjs/ui/components/signIn/OxyPasswordPanel.js +5 -16
  36. package/lib/commonjs/ui/components/signIn/OxyPasswordPanel.js.map +1 -1
  37. package/lib/commonjs/ui/components/signIn/OxySignInPanel.js +19 -20
  38. package/lib/commonjs/ui/components/signIn/OxySignInPanel.js.map +1 -1
  39. package/lib/commonjs/ui/components/signIn/OxySignUpPanel.js +12 -8
  40. package/lib/commonjs/ui/components/signIn/OxySignUpPanel.js.map +1 -1
  41. package/lib/commonjs/ui/components/signIn/ReauthStep.js +2 -7
  42. package/lib/commonjs/ui/components/signIn/ReauthStep.js.map +1 -1
  43. package/lib/commonjs/ui/components/signIn/accountFlowParts.js +64 -13
  44. package/lib/commonjs/ui/components/signIn/accountFlowParts.js.map +1 -1
  45. package/lib/commonjs/ui/context/OxyContext.js +25 -4
  46. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  47. package/lib/commonjs/ui/hooks/accountQueryPersistence.js +157 -0
  48. package/lib/commonjs/ui/hooks/accountQueryPersistence.js.map +1 -0
  49. package/lib/commonjs/ui/hooks/queryClient.js +17 -9
  50. package/lib/commonjs/ui/hooks/queryClient.js.map +1 -1
  51. package/lib/commonjs/ui/index.js.map +1 -1
  52. package/lib/commonjs/ui/screens/AppInfoScreen.js +2 -1
  53. package/lib/commonjs/ui/screens/AppInfoScreen.js.map +1 -1
  54. package/lib/commonjs/ui/utils/buildId.js +25 -0
  55. package/lib/commonjs/ui/utils/buildId.js.map +1 -0
  56. package/lib/commonjs/ui/utils/clipboard.js +21 -0
  57. package/lib/commonjs/ui/utils/clipboard.js.map +1 -0
  58. package/lib/commonjs/ui/utils/clipboard.native.js +28 -0
  59. package/lib/commonjs/ui/utils/clipboard.native.js.map +1 -0
  60. package/lib/module/index.js +3 -0
  61. package/lib/module/index.js.map +1 -1
  62. package/lib/module/ui/components/BloomLocaleBridge.js +33 -0
  63. package/lib/module/ui/components/BloomLocaleBridge.js.map +1 -0
  64. package/lib/module/ui/components/OxyProvider.js +16 -7
  65. package/lib/module/ui/components/OxyProvider.js.map +1 -1
  66. package/lib/module/ui/components/OxySignInRequestSurface.js +0 -1
  67. package/lib/module/ui/components/OxySignInRequestSurface.js.map +1 -1
  68. package/lib/module/ui/components/RequireOxyAuth.js +9 -1
  69. package/lib/module/ui/components/RequireOxyAuth.js.map +1 -1
  70. package/lib/module/ui/components/SurfaceScreen.js +5 -11
  71. package/lib/module/ui/components/SurfaceScreen.js.map +1 -1
  72. package/lib/module/ui/components/authChooser/TroubleDisclosure.js +0 -1
  73. package/lib/module/ui/components/authChooser/TroubleDisclosure.js.map +1 -1
  74. package/lib/module/ui/components/authChooser/primitives.js +11 -12
  75. package/lib/module/ui/components/authChooser/primitives.js.map +1 -1
  76. package/lib/module/ui/components/authChooser/requestSurfaces.js +15 -10
  77. package/lib/module/ui/components/authChooser/requestSurfaces.js.map +1 -1
  78. package/lib/module/ui/components/authChooser/styles.js +4 -1
  79. package/lib/module/ui/components/authChooser/styles.js.map +1 -1
  80. package/lib/module/ui/components/payment/PaymentDetailsStep.js +3 -3
  81. package/lib/module/ui/components/payment/PaymentDetailsStep.js.map +1 -1
  82. package/lib/module/ui/components/signIn/InlineCommonsQr.js +6 -3
  83. package/lib/module/ui/components/signIn/InlineCommonsQr.js.map +1 -1
  84. package/lib/module/ui/components/signIn/OxyAccountPicker.js +13 -17
  85. package/lib/module/ui/components/signIn/OxyAccountPicker.js.map +1 -1
  86. package/lib/module/ui/components/signIn/OxyAuthScreen.js +10 -9
  87. package/lib/module/ui/components/signIn/OxyAuthScreen.js.map +1 -1
  88. package/lib/module/ui/components/signIn/OxyAuthenticatorPanel.js +40 -44
  89. package/lib/module/ui/components/signIn/OxyAuthenticatorPanel.js.map +1 -1
  90. package/lib/module/ui/components/signIn/OxyDeleteAccountPanel.js +22 -7
  91. package/lib/module/ui/components/signIn/OxyDeleteAccountPanel.js.map +1 -1
  92. package/lib/module/ui/components/signIn/OxyLinkCommonsPanel.js +5 -3
  93. package/lib/module/ui/components/signIn/OxyLinkCommonsPanel.js.map +1 -1
  94. package/lib/module/ui/components/signIn/OxyPasswordPanel.js +6 -17
  95. package/lib/module/ui/components/signIn/OxyPasswordPanel.js.map +1 -1
  96. package/lib/module/ui/components/signIn/OxySignInPanel.js +20 -21
  97. package/lib/module/ui/components/signIn/OxySignInPanel.js.map +1 -1
  98. package/lib/module/ui/components/signIn/OxySignUpPanel.js +13 -9
  99. package/lib/module/ui/components/signIn/OxySignUpPanel.js.map +1 -1
  100. package/lib/module/ui/components/signIn/ReauthStep.js +3 -8
  101. package/lib/module/ui/components/signIn/ReauthStep.js.map +1 -1
  102. package/lib/module/ui/components/signIn/accountFlowParts.js +60 -11
  103. package/lib/module/ui/components/signIn/accountFlowParts.js.map +1 -1
  104. package/lib/module/ui/context/OxyContext.js +27 -6
  105. package/lib/module/ui/context/OxyContext.js.map +1 -1
  106. package/lib/module/ui/hooks/accountQueryPersistence.js +153 -0
  107. package/lib/module/ui/hooks/accountQueryPersistence.js.map +1 -0
  108. package/lib/module/ui/hooks/queryClient.js +18 -10
  109. package/lib/module/ui/hooks/queryClient.js.map +1 -1
  110. package/lib/module/ui/index.js +3 -0
  111. package/lib/module/ui/index.js.map +1 -1
  112. package/lib/module/ui/screens/AppInfoScreen.js +3 -2
  113. package/lib/module/ui/screens/AppInfoScreen.js.map +1 -1
  114. package/lib/module/ui/utils/buildId.js +21 -0
  115. package/lib/module/ui/utils/buildId.js.map +1 -0
  116. package/lib/module/ui/utils/clipboard.js +17 -0
  117. package/lib/module/ui/utils/clipboard.js.map +1 -0
  118. package/lib/module/ui/utils/clipboard.native.js +23 -0
  119. package/lib/module/ui/utils/clipboard.native.js.map +1 -0
  120. package/lib/typescript/commonjs/index.d.ts +1 -0
  121. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  122. package/lib/typescript/commonjs/ui/components/BloomLocaleBridge.d.ts +19 -0
  123. package/lib/typescript/commonjs/ui/components/BloomLocaleBridge.d.ts.map +1 -0
  124. package/lib/typescript/commonjs/ui/components/OxyProvider.d.ts.map +1 -1
  125. package/lib/typescript/commonjs/ui/components/OxySignInRequestSurface.d.ts.map +1 -1
  126. package/lib/typescript/commonjs/ui/components/RequireOxyAuth.d.ts.map +1 -1
  127. package/lib/typescript/commonjs/ui/components/SurfaceScreen.d.ts.map +1 -1
  128. package/lib/typescript/commonjs/ui/components/authChooser/TroubleDisclosure.d.ts.map +1 -1
  129. package/lib/typescript/commonjs/ui/components/authChooser/primitives.d.ts +4 -2
  130. package/lib/typescript/commonjs/ui/components/authChooser/primitives.d.ts.map +1 -1
  131. package/lib/typescript/commonjs/ui/components/authChooser/requestSurfaces.d.ts +4 -4
  132. package/lib/typescript/commonjs/ui/components/authChooser/requestSurfaces.d.ts.map +1 -1
  133. package/lib/typescript/commonjs/ui/components/authChooser/styles.d.ts +2 -1
  134. package/lib/typescript/commonjs/ui/components/authChooser/styles.d.ts.map +1 -1
  135. package/lib/typescript/commonjs/ui/components/payment/PaymentDetailsStep.d.ts.map +1 -1
  136. package/lib/typescript/commonjs/ui/components/signIn/InlineCommonsQr.d.ts.map +1 -1
  137. package/lib/typescript/commonjs/ui/components/signIn/OxyAccountPicker.d.ts.map +1 -1
  138. package/lib/typescript/commonjs/ui/components/signIn/OxyAuthScreen.d.ts +1 -5
  139. package/lib/typescript/commonjs/ui/components/signIn/OxyAuthScreen.d.ts.map +1 -1
  140. package/lib/typescript/commonjs/ui/components/signIn/OxyAuthenticatorPanel.d.ts.map +1 -1
  141. package/lib/typescript/commonjs/ui/components/signIn/OxyDeleteAccountPanel.d.ts.map +1 -1
  142. package/lib/typescript/commonjs/ui/components/signIn/OxyLinkCommonsPanel.d.ts.map +1 -1
  143. package/lib/typescript/commonjs/ui/components/signIn/OxyPasswordPanel.d.ts.map +1 -1
  144. package/lib/typescript/commonjs/ui/components/signIn/OxySignInPanel.d.ts.map +1 -1
  145. package/lib/typescript/commonjs/ui/components/signIn/OxySignUpPanel.d.ts.map +1 -1
  146. package/lib/typescript/commonjs/ui/components/signIn/ReauthStep.d.ts.map +1 -1
  147. package/lib/typescript/commonjs/ui/components/signIn/accountFlowParts.d.ts +29 -2
  148. package/lib/typescript/commonjs/ui/components/signIn/accountFlowParts.d.ts.map +1 -1
  149. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
  150. package/lib/typescript/commonjs/ui/context/oxyContextTypes.d.ts +9 -0
  151. package/lib/typescript/commonjs/ui/context/oxyContextTypes.d.ts.map +1 -1
  152. package/lib/typescript/commonjs/ui/hooks/accountQueryPersistence.d.ts +54 -0
  153. package/lib/typescript/commonjs/ui/hooks/accountQueryPersistence.d.ts.map +1 -0
  154. package/lib/typescript/commonjs/ui/hooks/queryClient.d.ts +8 -2
  155. package/lib/typescript/commonjs/ui/hooks/queryClient.d.ts.map +1 -1
  156. package/lib/typescript/commonjs/ui/icons/Ionicons.d.ts +1 -1
  157. package/lib/typescript/commonjs/ui/icons/MaterialCommunityIcons.d.ts +1 -1
  158. package/lib/typescript/commonjs/ui/index.d.ts +1 -0
  159. package/lib/typescript/commonjs/ui/index.d.ts.map +1 -1
  160. package/lib/typescript/commonjs/ui/screens/AppInfoScreen.d.ts.map +1 -1
  161. package/lib/typescript/commonjs/ui/types/navigation.d.ts +9 -0
  162. package/lib/typescript/commonjs/ui/types/navigation.d.ts.map +1 -1
  163. package/lib/typescript/commonjs/ui/utils/buildId.d.ts +16 -0
  164. package/lib/typescript/commonjs/ui/utils/buildId.d.ts.map +1 -0
  165. package/lib/typescript/commonjs/ui/utils/clipboard.d.ts +11 -0
  166. package/lib/typescript/commonjs/ui/utils/clipboard.d.ts.map +1 -0
  167. package/lib/typescript/commonjs/ui/utils/clipboard.native.d.ts +18 -0
  168. package/lib/typescript/commonjs/ui/utils/clipboard.native.d.ts.map +1 -0
  169. package/lib/typescript/module/index.d.ts +1 -0
  170. package/lib/typescript/module/index.d.ts.map +1 -1
  171. package/lib/typescript/module/ui/components/BloomLocaleBridge.d.ts +19 -0
  172. package/lib/typescript/module/ui/components/BloomLocaleBridge.d.ts.map +1 -0
  173. package/lib/typescript/module/ui/components/OxyProvider.d.ts.map +1 -1
  174. package/lib/typescript/module/ui/components/OxySignInRequestSurface.d.ts.map +1 -1
  175. package/lib/typescript/module/ui/components/RequireOxyAuth.d.ts.map +1 -1
  176. package/lib/typescript/module/ui/components/SurfaceScreen.d.ts.map +1 -1
  177. package/lib/typescript/module/ui/components/authChooser/TroubleDisclosure.d.ts.map +1 -1
  178. package/lib/typescript/module/ui/components/authChooser/primitives.d.ts +4 -2
  179. package/lib/typescript/module/ui/components/authChooser/primitives.d.ts.map +1 -1
  180. package/lib/typescript/module/ui/components/authChooser/requestSurfaces.d.ts +4 -4
  181. package/lib/typescript/module/ui/components/authChooser/requestSurfaces.d.ts.map +1 -1
  182. package/lib/typescript/module/ui/components/authChooser/styles.d.ts +2 -1
  183. package/lib/typescript/module/ui/components/authChooser/styles.d.ts.map +1 -1
  184. package/lib/typescript/module/ui/components/payment/PaymentDetailsStep.d.ts.map +1 -1
  185. package/lib/typescript/module/ui/components/signIn/InlineCommonsQr.d.ts.map +1 -1
  186. package/lib/typescript/module/ui/components/signIn/OxyAccountPicker.d.ts.map +1 -1
  187. package/lib/typescript/module/ui/components/signIn/OxyAuthScreen.d.ts +1 -5
  188. package/lib/typescript/module/ui/components/signIn/OxyAuthScreen.d.ts.map +1 -1
  189. package/lib/typescript/module/ui/components/signIn/OxyAuthenticatorPanel.d.ts.map +1 -1
  190. package/lib/typescript/module/ui/components/signIn/OxyDeleteAccountPanel.d.ts.map +1 -1
  191. package/lib/typescript/module/ui/components/signIn/OxyLinkCommonsPanel.d.ts.map +1 -1
  192. package/lib/typescript/module/ui/components/signIn/OxyPasswordPanel.d.ts.map +1 -1
  193. package/lib/typescript/module/ui/components/signIn/OxySignInPanel.d.ts.map +1 -1
  194. package/lib/typescript/module/ui/components/signIn/OxySignUpPanel.d.ts.map +1 -1
  195. package/lib/typescript/module/ui/components/signIn/ReauthStep.d.ts.map +1 -1
  196. package/lib/typescript/module/ui/components/signIn/accountFlowParts.d.ts +29 -2
  197. package/lib/typescript/module/ui/components/signIn/accountFlowParts.d.ts.map +1 -1
  198. package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
  199. package/lib/typescript/module/ui/context/oxyContextTypes.d.ts +9 -0
  200. package/lib/typescript/module/ui/context/oxyContextTypes.d.ts.map +1 -1
  201. package/lib/typescript/module/ui/hooks/accountQueryPersistence.d.ts +54 -0
  202. package/lib/typescript/module/ui/hooks/accountQueryPersistence.d.ts.map +1 -0
  203. package/lib/typescript/module/ui/hooks/queryClient.d.ts +8 -2
  204. package/lib/typescript/module/ui/hooks/queryClient.d.ts.map +1 -1
  205. package/lib/typescript/module/ui/icons/Ionicons.d.ts +1 -1
  206. package/lib/typescript/module/ui/icons/MaterialCommunityIcons.d.ts +1 -1
  207. package/lib/typescript/module/ui/index.d.ts +1 -0
  208. package/lib/typescript/module/ui/index.d.ts.map +1 -1
  209. package/lib/typescript/module/ui/screens/AppInfoScreen.d.ts.map +1 -1
  210. package/lib/typescript/module/ui/types/navigation.d.ts +9 -0
  211. package/lib/typescript/module/ui/types/navigation.d.ts.map +1 -1
  212. package/lib/typescript/module/ui/utils/buildId.d.ts +16 -0
  213. package/lib/typescript/module/ui/utils/buildId.d.ts.map +1 -0
  214. package/lib/typescript/module/ui/utils/clipboard.d.ts +11 -0
  215. package/lib/typescript/module/ui/utils/clipboard.d.ts.map +1 -0
  216. package/lib/typescript/module/ui/utils/clipboard.native.d.ts +18 -0
  217. package/lib/typescript/module/ui/utils/clipboard.native.d.ts.map +1 -0
  218. package/package.json +11 -6
  219. package/src/index.ts +2 -0
  220. package/src/ui/components/BloomLocaleBridge.tsx +23 -0
  221. package/src/ui/components/OxyProvider.tsx +18 -9
  222. package/src/ui/components/OxySignInRequestSurface.tsx +0 -1
  223. package/src/ui/components/RequireOxyAuth.tsx +7 -2
  224. package/src/ui/components/SurfaceScreen.tsx +3 -9
  225. package/src/ui/components/__tests__/BloomLocaleBridge.test.tsx +68 -0
  226. package/src/ui/components/authChooser/TroubleDisclosure.tsx +0 -1
  227. package/src/ui/components/authChooser/primitives.tsx +11 -9
  228. package/src/ui/components/authChooser/requestSurfaces.tsx +9 -8
  229. package/src/ui/components/authChooser/styles.ts +4 -1
  230. package/src/ui/components/payment/PaymentDetailsStep.tsx +5 -3
  231. package/src/ui/components/signIn/InlineCommonsQr.tsx +5 -2
  232. package/src/ui/components/signIn/OxyAccountPicker.tsx +14 -13
  233. package/src/ui/components/signIn/OxyAuthScreen.tsx +5 -8
  234. package/src/ui/components/signIn/OxyAuthenticatorPanel.tsx +33 -45
  235. package/src/ui/components/signIn/OxyDeleteAccountPanel.tsx +17 -3
  236. package/src/ui/components/signIn/OxyLinkCommonsPanel.tsx +3 -2
  237. package/src/ui/components/signIn/OxyPasswordPanel.tsx +6 -12
  238. package/src/ui/components/signIn/OxySignInPanel.tsx +18 -15
  239. package/src/ui/components/signIn/OxySignUpPanel.tsx +13 -5
  240. package/src/ui/components/signIn/ReauthStep.tsx +4 -7
  241. package/src/ui/components/signIn/accountFlowParts.tsx +61 -7
  242. package/src/ui/context/OxyContext.tsx +35 -5
  243. package/src/ui/context/oxyContextTypes.ts +9 -0
  244. package/src/ui/hooks/accountQueryPersistence.ts +201 -0
  245. package/src/ui/hooks/queryClient.ts +17 -8
  246. package/src/ui/index.ts +2 -0
  247. package/src/ui/screens/AppInfoScreen.tsx +2 -2
  248. package/src/ui/types/navigation.ts +9 -0
  249. package/src/ui/utils/buildId.ts +18 -0
  250. package/src/ui/utils/clipboard.native.ts +20 -0
  251. package/src/ui/utils/clipboard.ts +14 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxy.so/services",
3
- "version": "8.2.0",
3
+ "version": "8.4.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",
@@ -163,7 +163,7 @@
163
163
  "@commitlint/cli": "^21.2.1",
164
164
  "@commitlint/config-conventional": "^21.2.0",
165
165
  "@expo/vector-icons": "^15.0.3",
166
- "@oxy.so/core": "3.1.0",
166
+ "@oxy.so/core": "3.2.0",
167
167
  "@react-native-async-storage/async-storage": "^2.0.0",
168
168
  "@react-native-community/netinfo": "^12.0.1",
169
169
  "@react-native/babel-preset": "^0.86.2",
@@ -178,8 +178,9 @@
178
178
  "@types/jest": "^30.0.0",
179
179
  "@types/node": "^20.19.43",
180
180
  "@types/react": "*",
181
- "@oxy.so/bloom": "^4.31.0",
181
+ "@oxy.so/bloom": "^4.35.0",
182
182
  "copyfiles": "^2.4.1",
183
+ "expo-clipboard": "~57.0.2",
183
184
  "expo-constants": "~57.0.5",
184
185
  "expo-file-system": "~57.0.1",
185
186
  "expo-haptics": "~57.0.1",
@@ -211,11 +212,11 @@
211
212
  "typescript": "^5.9.2",
212
213
  "zustand": "^5.0.14"
213
214
  },
214
- "//@oxy.so/bloom": "RE-RAISE THIS FLOOR ON EVERY BREAKING BLOOM RELEASE, in the same commit as the root package.json catalog entry and the code that requires it. The breaking position is the MAJOR, so `^3.0.1` means `>=3.0.1 <4.0.0`: 3.x minors are admitted on semver's additive promise, and Bloom 4.0.0 is refused until someone raises this deliberately. The CAP is the part that is load-bearing. An earlier revision of this key recorded the opposite trade -- an uncapped `>=` floor, accepted so that intentionally-breaking Bloom 0.x minors would not be blocked -- and that trade is closed by evidence rather than by preference: an unbounded range is exactly what let `bun install` pair this package with Bloom 1.0.0 and emit no warning at all, and 1.0.0 broke it four ways (a deleted provider, a renamed confirm option, a renamed Chip fill, and two REMOVED SUBPATHS that fail at bundle time, before any typecheck runs). A floor is a claim about what was measured; a cap is the claim that nothing beyond it was. RAISED TO 3.0.1 from `^2.1.0`. Bloom 3.0.0 breaks exactly two things -- `PageHeader` defaults to `presentation=\"floating\"` and stops hiding the title under `transparent`, and `Field disabled` now reaches the control that reads the field context -- and THIS PACKAGE IMPORTS NEITHER: it renders no `PageHeader` and no `Field`, and 3.0 removes no subpath (unlike 2.0.0, which is why that floor cost code changes and this one does not). Its six `transparent` occurrences are CSS colour values, not the PageHeader prop. The cap had to move rather than widen to a union, because leaving `^2.1.0` in place is what made `bun install` resolve a SECOND, nested Bloom 2.12.4 under this package for any app on Bloom 3 -- two copies of a library whose 3.0 contracts (`control-surface`, `TopEdgeProvider`, `ScrollOffsetProvider`) are React contexts, so a provider from one copy cannot be read by a consumer from the other. Mention's `oxyRuntimeCompatibility` test is what caught it; nothing in a typecheck can. RAISED AGAIN TO 3.2.0 from `^3.0.1`, for the same class of reason the 2.x note records, only in the other direction: 3.2.0 ADDS the per-glyph icon subpaths (`@oxy.so/bloom/icons/RiAtLine`) that this package's screens now import instead of the `@oxy.so/bloom/icons` barrel, and a missing subpath fails at bundle time, in the consumer's Metro, long after this package has typechecked and shipped. `^3.0.1` would have admitted Bloom 3.0.1 and 3.1.0, neither of which has those subpaths, so the floor is the only thing standing between a consumer and a red build. Nothing else about 3.1.0 or 3.2.0 is required here; the cap stays at the major. RAISED TO ^4.2.0: FollowButton now delegates its visuals to Bloom media-header FollowButton with loading, textStyle and large size; the new shared UI contracts require one Bloom4 context across Services and its consumer. RAISED TO ^4.31.0: the sign-in and account screens type one-time codes into `InputOtp` with `type=\"alphanumeric\"` (the long sign-in code and backup codes), which 4.31.0 adds; older 4.x drops the letters.",
215
+ "//@oxy.so/bloom": "RE-RAISE THIS FLOOR ON EVERY BREAKING BLOOM RELEASE, in the same commit as the root package.json catalog entry and the code that requires it. The breaking position is the MAJOR, so `^3.0.1` means `>=3.0.1 <4.0.0`: 3.x minors are admitted on semver's additive promise, and Bloom 4.0.0 is refused until someone raises this deliberately. The CAP is the part that is load-bearing. An earlier revision of this key recorded the opposite trade -- an uncapped `>=` floor, accepted so that intentionally-breaking Bloom 0.x minors would not be blocked -- and that trade is closed by evidence rather than by preference: an unbounded range is exactly what let `bun install` pair this package with Bloom 1.0.0 and emit no warning at all, and 1.0.0 broke it four ways (a deleted provider, a renamed confirm option, a renamed Chip fill, and two REMOVED SUBPATHS that fail at bundle time, before any typecheck runs). A floor is a claim about what was measured; a cap is the claim that nothing beyond it was. RAISED TO 3.0.1 from `^2.1.0`. Bloom 3.0.0 breaks exactly two things -- `PageHeader` defaults to `presentation=\"floating\"` and stops hiding the title under `transparent`, and `Field disabled` now reaches the control that reads the field context -- and THIS PACKAGE IMPORTS NEITHER: it renders no `PageHeader` and no `Field`, and 3.0 removes no subpath (unlike 2.0.0, which is why that floor cost code changes and this one does not). Its six `transparent` occurrences are CSS colour values, not the PageHeader prop. The cap had to move rather than widen to a union, because leaving `^2.1.0` in place is what made `bun install` resolve a SECOND, nested Bloom 2.12.4 under this package for any app on Bloom 3 -- two copies of a library whose 3.0 contracts (`control-surface`, `TopEdgeProvider`, `ScrollOffsetProvider`) are React contexts, so a provider from one copy cannot be read by a consumer from the other. Mention's `oxyRuntimeCompatibility` test is what caught it; nothing in a typecheck can. RAISED AGAIN TO 3.2.0 from `^3.0.1`, for the same class of reason the 2.x note records, only in the other direction: 3.2.0 ADDS the per-glyph icon subpaths (`@oxy.so/bloom/icons/RiAtLine`) that this package's screens now import instead of the `@oxy.so/bloom/icons` barrel, and a missing subpath fails at bundle time, in the consumer's Metro, long after this package has typechecked and shipped. `^3.0.1` would have admitted Bloom 3.0.1 and 3.1.0, neither of which has those subpaths, so the floor is the only thing standing between a consumer and a red build. Nothing else about 3.1.0 or 3.2.0 is required here; the cap stays at the major. RAISED TO ^4.2.0: FollowButton now delegates its visuals to Bloom media-header FollowButton with loading, textStyle and large size; the new shared UI contracts require one Bloom4 context across Services and its consumer. RAISED TO ^4.31.0: the sign-in and account screens type one-time codes into `InputOtp` with `type=\"alphanumeric\"` (the long sign-in code and backup codes), which 4.31.0 adds; older 4.x drops the letters. RAISED TO ^4.35.0: every password field passes `revealable` to `TextFieldInput` (the eye button, 4.35.0); older 4.x drops the prop silently and the password cannot be shown. The provider also mounts `@oxy.so/bloom/locale` (4.32.0+).",
215
216
  "peerDependencies": {
216
217
  "@expo/vector-icons": "^15.0.3",
217
- "@oxy.so/bloom": "^4.31.0",
218
- "@oxy.so/core": "^3.1.0",
218
+ "@oxy.so/bloom": "^4.35.0",
219
+ "@oxy.so/core": "^3.2.0",
219
220
  "@react-native-async-storage/async-storage": "^2.0.0",
220
221
  "@react-native-community/netinfo": ">=11.4.1",
221
222
  "@tanstack/query-async-storage-persister": "^5.101",
@@ -225,6 +226,7 @@
225
226
  "@types/react": "*",
226
227
  "@types/react-native": "*",
227
228
  "expo": ">=56.0.0",
229
+ "expo-clipboard": ">=56.0.0 <59.0.0",
228
230
  "expo-constants": ">=56.0.0",
229
231
  "expo-document-picker": ">=56.0.0",
230
232
  "expo-file-system": ">=56.0.0",
@@ -293,6 +295,9 @@
293
295
  },
294
296
  "expo-web-browser": {
295
297
  "optional": true
298
+ },
299
+ "expo-clipboard": {
300
+ "optional": true
296
301
  }
297
302
  },
298
303
  "react-native-builder-bob": {
package/src/index.ts CHANGED
@@ -306,6 +306,8 @@ export type {
306
306
  // wall before the device-first cold boot resolves. Opens the ONE account dialog.
307
307
  export { RequireOxyAuth } from './ui/components/RequireOxyAuth';
308
308
  export type { RequireOxyAuthProps, RequireOxyAuthPrompt } from './ui/components/RequireOxyAuth';
309
+ // `OxyProvider`'s `accountQueries`: the app's per-account offline cache.
310
+ export type { AccountQueriesConfig } from './ui/hooks/accountQueryPersistence';
309
311
 
310
312
  export { default as FollowButton } from './ui/components/FollowButton';
311
313
  export type { FollowButtonProps, SingleFollowButtonProps, MultiFollowButtonProps } from './ui/components/FollowButton';
@@ -0,0 +1,23 @@
1
+ import type { FC, ReactNode } from 'react';
2
+ import { LocaleProvider, useBloomLocale } from '@oxy.so/bloom/locale';
3
+ import { useOxy } from '../context/OxyContext';
4
+
5
+ /**
6
+ * Bloom's own words — a field's "Show password", a dialog's "Close", "Step 2
7
+ * of 3" — in the language Oxy resolved for this person (`currentLanguage`: the
8
+ * account's, or signed out, the device's). Mounted by `OxyProvider` around
9
+ * everything it renders, the app included, so the SDK's screens and the app's
10
+ * Bloom components speak one language without the app doing anything.
11
+ *
12
+ * An app that sets Bloom's locale itself keeps it; the nearest provider wins:
13
+ * - a `LocaleProvider` (or `BloomProvider locale`) INSIDE `OxyProvider` is
14
+ * nearer than this one, and wins for its subtree as usual;
15
+ * - one OUTSIDE `OxyProvider` is the app's explicit choice, so this bridge
16
+ * passes it through instead of overriding it from within.
17
+ * Only where nothing above sets a locale does Oxy's language apply.
18
+ */
19
+ export const BloomLocaleBridge: FC<{ children: ReactNode }> = ({ children }) => {
20
+ const { currentLanguage } = useOxy();
21
+ const inherited = useBloomLocale();
22
+ return <LocaleProvider locale={inherited ?? currentLanguage}>{children}</LocaleProvider>;
23
+ };
@@ -15,6 +15,7 @@ import { isNetConnectivityOnline } from '../utils/netConnectivity';
15
15
  import { KeyboardBoundary } from './KeyboardBoundary';
16
16
  import { ProductAnalyticsObserver } from '../analytics/productAnalytics';
17
17
  import { LanguageBridge } from './LanguageBridge';
18
+ import { BloomLocaleBridge } from './BloomLocaleBridge';
18
19
 
19
20
  const bootStyles = StyleSheet.create({
20
21
  providerRoot: {
@@ -88,6 +89,7 @@ const OxyProvider: FC<OxyProviderProps> = ({
88
89
  sessionMode = 'account',
89
90
  webAuthMode = 'popup',
90
91
  queryClient: providedQueryClient,
92
+ accountQueries,
91
93
  requireAuth = 'off',
92
94
  backgroundSession = false,
93
95
  language,
@@ -102,6 +104,8 @@ const OxyProvider: FC<OxyProviderProps> = ({
102
104
  const queryClientRef = useRef<ReturnType<typeof createQueryClient> | null>(null);
103
105
  const persistenceUnsubRef = useRef<(() => void) | null>(null);
104
106
  const ownsQueryClientRef = useRef(providedQueryClient === undefined);
107
+ // `accountQueries` is read once, at mount, like the client itself.
108
+ const accountMutationKeysRef = useRef(accountQueries?.mutationKeys);
105
109
  const [platformStorage, setPlatformStorage] = useState<StorageInterface | null>(null);
106
110
 
107
111
  // If the consumer supplied their own QueryClient we use it as-is and skip
@@ -135,7 +139,7 @@ const OxyProvider: FC<OxyProviderProps> = ({
135
139
  setPlatformStorage(storage);
136
140
  const client = queryClientRef.current;
137
141
  if (!client || !ownsQueryClientRef.current) return;
138
- const persistence = attachQueryPersistence(client, storage);
142
+ const persistence = attachQueryPersistence(client, storage, accountMutationKeysRef.current);
139
143
  persistenceUnsubRef.current = persistence.unsubscribe;
140
144
  await persistence.restored;
141
145
  };
@@ -226,6 +230,8 @@ const OxyProvider: FC<OxyProviderProps> = ({
226
230
  // (defaultColorPreset, defaultMode, persistKey, storage, fonts, etc.).
227
231
  // OxyProvider does NOT wrap a BloomThemeProvider — that would create a
228
232
  // duplicate scope that silently shadows the consumer's configuration.
233
+ // It DOES set Bloom's locale (`BloomLocaleBridge`) to Oxy's language, but
234
+ // defers to a locale the app already set above it.
229
235
  const coreContent = (
230
236
  <QueryClientProvider client={queryClient}>
231
237
  <OxyRuntimeProvider
@@ -240,18 +246,21 @@ const OxyProvider: FC<OxyProviderProps> = ({
240
246
  webAuthMode={webAuthMode}
241
247
  backgroundSession={backgroundSession}
242
248
  platformStorage={platformStorage}
249
+ accountQueries={accountQueries}
243
250
  onAuthStateChange={onAuthStateChange as OxyRuntimeProviderProps['onAuthStateChange']}
244
251
  >
245
252
  {productAnalytics ? <ProductAnalyticsObserver analytics={productAnalytics} /> : null}
246
253
  {language ? <LanguageBridge {...language} /> : null}
247
- <SurfaceProvider>
248
- {requireAuth === 'off' ? (
249
- children
250
- ) : (
251
- <RequireOxyAuth prompt={requireAuth}>{children}</RequireOxyAuth>
252
- )}
253
- </SurfaceProvider>
254
- <ToastOutlet />
254
+ <BloomLocaleBridge>
255
+ <SurfaceProvider>
256
+ {requireAuth === 'off' ? (
257
+ children
258
+ ) : (
259
+ <RequireOxyAuth prompt={requireAuth}>{children}</RequireOxyAuth>
260
+ )}
261
+ </SurfaceProvider>
262
+ <ToastOutlet />
263
+ </BloomLocaleBridge>
255
264
  </OxyRuntimeProvider>
256
265
  </QueryClientProvider>
257
266
  );
@@ -138,7 +138,6 @@ export const OxySignInRequestSurface: React.FC<OxySignInRequestSurfaceProps> = (
138
138
  <SubtleLink
139
139
  key={action.key}
140
140
  label={action.label}
141
- theme={theme}
142
141
  onPress={action.onPress}
143
142
  disabled={action.disabled}
144
143
  testID={action.key}
@@ -75,7 +75,7 @@ export const RequireOxyAuth: React.FC<RequireOxyAuthProps> = ({
75
75
  bannerMessage,
76
76
  bannerActionLabel,
77
77
  }) => {
78
- const { canUsePrivateApi, isPrivateApiPending, openAccountDialog } = useOxy();
78
+ const { canUsePrivateApi, isPrivateApiPending, isAccountCacheReady, openAccountDialog } = useOxy();
79
79
 
80
80
  // Public app: render straight through. Cheap enough to short-circuit before
81
81
  // touching any gate UI.
@@ -83,8 +83,13 @@ export const RequireOxyAuth: React.FC<RequireOxyAuthProps> = ({
83
83
  return <>{children}</>;
84
84
  }
85
85
 
86
- // Signed in (and token ready): render the protected subtree in every mode.
86
+ // Signed in (and token ready): render the protected subtree in every mode,
87
+ // once the account's offline cache (`accountQueries`) has restored. Before
88
+ // that it would render with no rows, or with the previous account's.
87
89
  if (canUsePrivateApi) {
90
+ if (!isAccountCacheReady) {
91
+ return loadingFallback ? <>{loadingFallback}</> : <NeutralLoading />;
92
+ }
88
93
  return <>{children}</>;
89
94
  }
90
95
 
@@ -1,5 +1,6 @@
1
1
  import React, { Suspense, useCallback, useEffect, useMemo, useState, type ErrorInfo } from 'react';
2
- import { ActivityIndicator, StyleSheet, View, useWindowDimensions } from 'react-native';
2
+ import { StyleSheet, View, useWindowDimensions } from 'react-native';
3
+ import { Loading } from '@oxy.so/bloom/loading';
3
4
  import { Text } from '@oxy.so/bloom/typography';
4
5
  import { useStore } from 'zustand';
5
6
  import { useTheme } from '@oxy.so/bloom/theme';
@@ -83,18 +84,11 @@ const errorStyles = StyleSheet.create({
83
84
  * as `lazy(() => import(...))` (see `../navigation/routes`), so the very first
84
85
  * presentation of a surface can land here for a frame; the surface chrome is
85
86
  * already on screen by then, so this fills only the body.
86
- *
87
- * Deliberately RN's `ActivityIndicator` rather than `@oxy.so/bloom/loading`,
88
- * matching `authChooser/requestSurfaces`: Bloom still declares
89
- * `sideEffects: false`, so its `Loading` can still tree-shake to `undefined` in
90
- * a rolldown-vite production bundle. Removing that risk from the first frame of
91
- * every route is not worth a themed spinner.
92
87
  */
93
88
  function ScreenPending() {
94
- const theme = useTheme();
95
89
  return (
96
90
  <View style={errorStyles.container}>
97
- <ActivityIndicator size="large" color={theme.colors.primary} />
91
+ <Loading variant="spinner" size="large" />
98
92
  </View>
99
93
  );
100
94
  }
@@ -0,0 +1,68 @@
1
+ import React from 'react';
2
+ import { render } from '@testing-library/react';
3
+ import { LocaleProvider, useBloomLocale } from '@oxy.so/bloom/locale';
4
+ import { BloomLocaleBridge } from '../BloomLocaleBridge';
5
+
6
+ /**
7
+ * Bloom's own words follow Oxy's language unless the app already set Bloom's
8
+ * locale: the nearest provider wins, in both directions.
9
+ */
10
+
11
+ let mockCurrentLanguage = 'es-ES';
12
+ jest.mock('../../context/OxyContext', () => ({
13
+ useOxy: () => ({ currentLanguage: mockCurrentLanguage }),
14
+ }));
15
+
16
+ function Probe() {
17
+ return <span data-testid="locale">{useBloomLocale() ?? 'runtime'}</span>;
18
+ }
19
+
20
+ beforeEach(() => {
21
+ mockCurrentLanguage = 'es-ES';
22
+ });
23
+
24
+ it("gives Bloom the account's language", () => {
25
+ const { getByTestId } = render(
26
+ <BloomLocaleBridge>
27
+ <Probe />
28
+ </BloomLocaleBridge>,
29
+ );
30
+ expect(getByTestId('locale').textContent).toBe('es-ES');
31
+ });
32
+
33
+ it('follows a language change', () => {
34
+ const { getByTestId, rerender } = render(
35
+ <BloomLocaleBridge>
36
+ <Probe />
37
+ </BloomLocaleBridge>,
38
+ );
39
+ mockCurrentLanguage = 'fr-FR';
40
+ rerender(
41
+ <BloomLocaleBridge>
42
+ <Probe />
43
+ </BloomLocaleBridge>,
44
+ );
45
+ expect(getByTestId('locale').textContent).toBe('fr-FR');
46
+ });
47
+
48
+ it("keeps a locale the app set above OxyProvider", () => {
49
+ const { getByTestId } = render(
50
+ <LocaleProvider locale="de-DE">
51
+ <BloomLocaleBridge>
52
+ <Probe />
53
+ </BloomLocaleBridge>
54
+ </LocaleProvider>,
55
+ );
56
+ expect(getByTestId('locale').textContent).toBe('de-DE');
57
+ });
58
+
59
+ it('yields to a provider the app mounts inside it', () => {
60
+ const { getByTestId } = render(
61
+ <BloomLocaleBridge>
62
+ <LocaleProvider locale="ja-JP">
63
+ <Probe />
64
+ </LocaleProvider>
65
+ </BloomLocaleBridge>,
66
+ );
67
+ expect(getByTestId('locale').textContent).toBe('ja-JP');
68
+ });
@@ -56,7 +56,6 @@ const TroubleDisclosure: React.FC<TroubleDisclosureProps> = ({ actions, revealed
56
56
  <SubtleLink
57
57
  key={action.key}
58
58
  label={action.label}
59
- theme={theme}
60
59
  onPress={action.onPress}
61
60
  disabled={action.disabled}
62
61
  testID={action.key}
@@ -5,10 +5,8 @@
5
5
  */
6
6
 
7
7
  import type React from 'react';
8
- import { Pressable } from 'react-native';
9
- import { Text } from '@oxy.so/bloom/typography';
8
+ import { LinkButton } from '@oxy.so/bloom/button';
10
9
  import { authChooserStyles as styles } from './styles';
11
- import type { Theme } from './types';
12
10
 
13
11
  /**
14
12
  * A SUBORDINATE action: a small centred text link, never a button.
@@ -17,22 +15,26 @@ import type { Theme } from './types';
17
15
  * primary action (issue #691) — the sign-up entry, and every alternative revealed
18
16
  * behind "Having trouble?". Keeping them all on one component is what stops an
19
17
  * alternative from quietly growing into a co-equal button again.
18
+ *
19
+ * Bloom's `LinkButton` in the secondary tone at `sm`: no fill or border, the
20
+ * secondary text colour, underlined under a pointer, and Bloom's link hit slop
21
+ * for the touch target. The vertical padding keeps the stack's old rhythm.
20
22
  */
21
23
  export const SubtleLink: React.FC<{
22
24
  label: string;
23
- theme: Theme;
24
25
  onPress: () => void;
25
26
  disabled?: boolean;
26
27
  testID?: string;
27
- }> = ({ label, theme, onPress, disabled, testID }) => (
28
- <Pressable
28
+ }> = ({ label, onPress, disabled, testID }) => (
29
+ <LinkButton
30
+ variant="secondary"
31
+ size="sm"
29
32
  onPress={onPress}
30
33
  disabled={disabled}
31
- accessibilityRole="button"
32
34
  accessibilityLabel={label}
33
35
  style={styles.footerLink}
34
36
  testID={testID}
35
37
  >
36
- <Text style={[styles.linkText, { color: theme.colors.textSecondary }]}>{label}</Text>
37
- </Pressable>
38
+ {label}
39
+ </LinkButton>
38
40
  );
@@ -13,16 +13,17 @@
13
13
  */
14
14
 
15
15
  import type React from 'react';
16
- import { ActivityIndicator, View } from 'react-native';
16
+ import { View } from 'react-native';
17
17
  import MaterialCommunityIcons from '../../icons/MaterialCommunityIcons';
18
18
  import QRCode from 'react-native-qrcode-svg';
19
19
  import { Button } from '@oxy.so/bloom/button';
20
- import { useTheme } from '@oxy.so/bloom/theme';
20
+ import { Loading } from '@oxy.so/bloom/loading';
21
21
  import { Text } from '@oxy.so/bloom/typography';
22
22
  import type { CommonsDeliveryRoute } from '@oxy.so/core';
23
23
  import type { SignInProgress } from '@oxy.so/core/session';
24
24
  import { authChooserStyles as styles } from './styles';
25
25
  import type { Theme, Translate } from './types';
26
+ import { useI18n } from '../../hooks/useI18n';
26
27
 
27
28
  /** High-contrast QR colors — intentionally fixed (NOT themed) for scan reliability. */
28
29
  const QR_PLATE_BG = '#FFFFFF';
@@ -32,14 +33,14 @@ const QR_SIZE = 196;
32
33
  const ROUTE_GLYPH_SIZE = 44;
33
34
 
34
35
  /**
35
- * The indeterminate leading visual: the request exists but has nothing to show yet.
36
- * Uses RN's ActivityIndicator — `@oxy.so/bloom/loading` can tree-shake to `undefined`
37
- * in rolldown-vite production bundles when co-imported with `@oxy.so/bloom/button`
38
- * (auth.oxy.so/authorize blank screen, React #130).
36
+ * The indeterminate leading visual: the request exists but has nothing to show
37
+ * yet. Bloom's spinner, named "Loading…" so assistive technology hears a busy
38
+ * progressbar where the QR will appear; the status sentence under it
39
+ * (`signInProgressLabel`) says what is happening.
39
40
  */
40
41
  export const PreparingSurface: React.FC = () => {
41
- const theme = useTheme();
42
- return <ActivityIndicator size="large" color={theme.colors.primary} />;
42
+ const { t } = useI18n();
43
+ return <Loading variant="spinner" size="large" accessibilityLabel={t('common.status.loading')} />;
43
44
  };
44
45
 
45
46
  /** The leading glyph for a route whose surface is elsewhere (the phone, Commons). */
@@ -98,9 +98,12 @@ export const authChooserStyles = StyleSheet.create({
98
98
  alignItems: 'center',
99
99
  justifyContent: 'center',
100
100
  },
101
+ // Longhands: Bloom's web `Button` (`SubtleLink`) is a raw DOM <button>,
102
+ // where `paddingVertical` is no CSS property and is silently dropped.
101
103
  footerLink: {
102
104
  alignSelf: 'center',
103
- paddingVertical: 10,
105
+ paddingTop: 10,
106
+ paddingBottom: 10,
104
107
  marginTop: 12,
105
108
  },
106
109
  linkText: {
@@ -1,6 +1,6 @@
1
1
  import type React from 'react';
2
2
  import { useMemo } from 'react';
3
- import { View, Animated, TouchableOpacity, Clipboard, Linking } from 'react-native';
3
+ import { View, Animated, TouchableOpacity, Linking } from 'react-native';
4
4
  import { Text } from '@oxy.so/bloom/typography';
5
5
  import Ionicons from '../../icons/Ionicons';
6
6
  import { Button } from '@oxy.so/bloom/button';
@@ -8,6 +8,7 @@ import { TextField, TextFieldInput } from '@oxy.so/bloom/text-field';
8
8
  import FAIRWalletIcon from '../icon/FAIRWalletIcon';
9
9
  import { createPaymentStyles } from './paymentStyles';
10
10
  import { toast } from '@oxy.so/bloom/toast';
11
+ import { copyText } from '../../utils/clipboard';
11
12
  import type { CardDetails, PaymentColors, PaymentStepAnimations } from './types';
12
13
  import { useI18n } from '../../hooks/useI18n';
13
14
 
@@ -43,8 +44,9 @@ const PaymentDetailsStep: React.FC<PaymentDetailsStepProps> = ({
43
44
  const { fadeAnim, slideAnim, scaleAnim } = animations;
44
45
 
45
46
  const handleCopyAddress = () => {
46
- Clipboard.setString(faircoinAddress);
47
- toast(t('payment.details.addressCopied'));
47
+ copyText(faircoinAddress)
48
+ .then(() => toast(t('payment.details.addressCopied')))
49
+ .catch(() => toast.error(t('appInfo.toasts.copyFailed')));
48
50
  };
49
51
 
50
52
  const handleOpenFairWallet = () => {
@@ -15,11 +15,12 @@
15
15
 
16
16
  import type React from 'react';
17
17
  import { useEffect, useState } from 'react';
18
- import { ActivityIndicator, StyleSheet, View } from 'react-native';
18
+ import { StyleSheet, View } from 'react-native';
19
19
  import QRCode from 'react-native-qrcode-svg';
20
20
  import { Button } from '@oxy.so/bloom/button';
21
21
  import { RiCheckboxCircleLine } from '@oxy.so/bloom/icons/RiCheckboxCircleLine';
22
22
  import { RiRefreshLine } from '@oxy.so/bloom/icons/RiRefreshLine';
23
+ import { SpinnerIcon } from '@oxy.so/bloom/loading';
23
24
  import { useTheme } from '@oxy.so/bloom/theme';
24
25
  import { Text } from '@oxy.so/bloom/typography';
25
26
  import type { AccountDialogController } from '@oxy.so/core/session';
@@ -32,6 +33,8 @@ const QR_FOREGROUND = '#000000';
32
33
  const QR_SIZE = 112;
33
34
  const PLATE_PADDING = 8;
34
35
  const PLATE_SIZE = QR_SIZE + PLATE_PADDING * 2;
36
+ /** The spinner while a code is on its way: React Native's `large` indicator size. */
37
+ const SPINNER_SIZE = 36;
35
38
 
36
39
  export const InlineCommonsQr: React.FC<{ controller: AccountDialogController | null }> = ({ controller }) => {
37
40
  const theme = useTheme();
@@ -70,7 +73,7 @@ export const InlineCommonsQr: React.FC<{ controller: AccountDialogController | n
70
73
  } else if (live && signIn.qrPayload && signIn.progress !== 'confirming-identity') {
71
74
  plate = <QRCode value={signIn.qrPayload} size={QR_SIZE} backgroundColor={QR_PLATE_BG} color={QR_FOREGROUND} />;
72
75
  } else if (live || expired || (signIn.inline && signIn.phase === 'completed')) {
73
- plate = <ActivityIndicator size="large" color={QR_FOREGROUND} />;
76
+ plate = <SpinnerIcon size={SPINNER_SIZE} color={QR_FOREGROUND} />;
74
77
  } else {
75
78
  // Failed for a reason renewing will not fix by itself, or superseded and
76
79
  // then withdrawn: one press for a new code.
@@ -23,8 +23,8 @@ import { Pressable, View } from 'react-native-css/components';
23
23
  import { Avatar } from '@oxy.so/bloom/avatar';
24
24
  import { RiArrowRightSLine } from '@oxy.so/bloom/icons/RiArrowRightSLine';
25
25
  import { RiCheckLine } from '@oxy.so/bloom/icons/RiCheckLine';
26
- import { RiLoader4Line } from '@oxy.so/bloom/icons/RiLoader4Line';
27
26
  import { RiUserAddLine } from '@oxy.so/bloom/icons/RiUserAddLine';
27
+ import { SpinnerIcon } from '@oxy.so/bloom/loading';
28
28
  import { useTheme } from '@oxy.so/bloom/theme';
29
29
  import { Text } from '@oxy.so/bloom/typography';
30
30
  import { showsPrincipalHeaders, type SwitcherContextRow, type SwitcherPrincipalRow } from '@oxy.so/core/session';
@@ -134,24 +134,25 @@ export const OxyAccountPicker: React.FC<OxyAccountPickerProps> = ({
134
134
  ) : null}
135
135
  </View>
136
136
  <View className="flex-row items-center gap-[8px] shrink-0">
137
- <View style={{ width: AVATAR_SIZE, height: AVATAR_SIZE }}>
138
- <Avatar
139
- source={context.avatarUrl ?? undefined}
140
- variant="thumb"
141
- name={context.displayName}
142
- size={AVATAR_SIZE}
143
- />
144
- {current ? (
137
+ {/* The current account: Avatar's own verified slot (bottom-right,
138
+ above the image), holding the accent check badge. */}
139
+ <Avatar
140
+ source={context.avatarUrl ?? undefined}
141
+ variant="thumb"
142
+ name={context.displayName}
143
+ size={AVATAR_SIZE}
144
+ verified={current}
145
+ verifiedIcon={
145
146
  <View
146
- className="absolute -bottom-[2px] -right-[2px] items-center justify-center rounded-full border-2 border-background"
147
+ className="items-center justify-center rounded-full border-2 border-background"
147
148
  style={{ width: 18, height: 18, backgroundColor: accent }}
148
149
  >
149
150
  <RiCheckLine size="xs" fill="#ffffff" />
150
151
  </View>
151
- ) : null}
152
- </View>
152
+ }
153
+ />
153
154
  {activating ? (
154
- <RiLoader4Line size="sm" fill={accent} />
155
+ <SpinnerIcon size={20} color={accent} />
155
156
  ) : (
156
157
  <RiArrowRightSLine size="sm" fill={theme.colors.textSecondary} />
157
158
  )}
@@ -17,8 +17,9 @@
17
17
  */
18
18
 
19
19
  import type React from 'react';
20
- import { ActivityIndicator, Linking, Text as RNText } from 'react-native';
20
+ import { Linking, Text as RNText } from 'react-native';
21
21
  import { View } from 'react-native-css/components';
22
+ import { Loading } from '@oxy.so/bloom/loading';
22
23
  import { useTheme } from '@oxy.so/bloom/theme';
23
24
  import { Text } from '@oxy.so/bloom/typography';
24
25
  import { useI18n } from '../../hooks/useI18n';
@@ -97,16 +98,12 @@ export const OxyAuthSplit: React.FC<{ children: React.ReactNode; aside: React.Re
97
98
  );
98
99
  };
99
100
 
100
- /**
101
- * The screen while there is nothing to show yet. React Native's own indicator:
102
- * `@oxy.so/bloom/loading` can tree-shake to `undefined` in a rolldown-vite
103
- * production bundle when co-imported with `@oxy.so/bloom/button`.
104
- */
101
+ /** The screen while there is nothing to show yet: Bloom's spinner, named for assistive technology. */
105
102
  export const OxyAuthLoading: React.FC = () => {
106
- const theme = useTheme();
103
+ const { t } = useI18n();
107
104
  return (
108
105
  <View className="min-h-[300px] items-center justify-center">
109
- <ActivityIndicator size="large" color={theme.colors.primary} />
106
+ <Loading variant="spinner" size="large" accessibilityLabel={t('common.status.loading')} testID="oxy-auth-loading" />
110
107
  </View>
111
108
  );
112
109
  };