@oxy.so/services 9.1.0 → 10.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 (236) hide show
  1. package/lib/commonjs/index.js +4 -3
  2. package/lib/commonjs/index.js.map +1 -1
  3. package/lib/commonjs/ui/client.js +174 -0
  4. package/lib/commonjs/ui/client.js.map +1 -1
  5. package/lib/commonjs/ui/components/BenefitList.js +1 -1
  6. package/lib/commonjs/ui/components/BenefitList.js.map +1 -1
  7. package/lib/commonjs/ui/components/FollowButton.js +2 -2
  8. package/lib/commonjs/ui/components/FollowButton.js.map +1 -1
  9. package/lib/commonjs/ui/components/FollowTargetButton.js +13 -103
  10. package/lib/commonjs/ui/components/FollowTargetButton.js.map +1 -1
  11. package/lib/commonjs/ui/components/PeableButton.js +1 -2
  12. package/lib/commonjs/ui/components/PeableButton.js.map +1 -1
  13. package/lib/commonjs/ui/components/SurfaceHeaderAction.js +1 -1
  14. package/lib/commonjs/ui/components/SurfaceHeaderAction.js.map +1 -1
  15. package/lib/commonjs/ui/components/SurfaceScreen.js +1 -1
  16. package/lib/commonjs/ui/components/SurfaceScreen.js.map +1 -1
  17. package/lib/commonjs/ui/components/authChooser/AccountsMenuView.js +1 -1
  18. package/lib/commonjs/ui/components/authChooser/AccountsMenuView.js.map +1 -1
  19. package/lib/commonjs/ui/components/authChooser/primitives.js +1 -1
  20. package/lib/commonjs/ui/components/authChooser/primitives.js.map +1 -1
  21. package/lib/commonjs/ui/components/authChooser/requestSurfaces.js +1 -1
  22. package/lib/commonjs/ui/components/authChooser/requestSurfaces.js.map +1 -1
  23. package/lib/commonjs/ui/components/followRules.js +105 -0
  24. package/lib/commonjs/ui/components/followRules.js.map +1 -0
  25. package/lib/commonjs/ui/components/payment/PaymentDetailsStep.js +2 -2
  26. package/lib/commonjs/ui/components/payment/PaymentDetailsStep.js.map +1 -1
  27. package/lib/commonjs/ui/components/payment/PaymentMethodStep.js +2 -2
  28. package/lib/commonjs/ui/components/payment/PaymentMethodStep.js.map +1 -1
  29. package/lib/commonjs/ui/components/payment/PaymentReviewStep.js +2 -2
  30. package/lib/commonjs/ui/components/payment/PaymentReviewStep.js.map +1 -1
  31. package/lib/commonjs/ui/components/payment/PaymentSuccessStep.js +1 -1
  32. package/lib/commonjs/ui/components/payment/PaymentSuccessStep.js.map +1 -1
  33. package/lib/commonjs/ui/components/payment/PaymentSummaryStep.js +2 -2
  34. package/lib/commonjs/ui/components/payment/PaymentSummaryStep.js.map +1 -1
  35. package/lib/commonjs/ui/components/signIn/OxyAuthScreen.js +1 -1
  36. package/lib/commonjs/ui/components/signIn/OxyAuthScreen.js.map +1 -1
  37. package/lib/commonjs/ui/components/signIn/OxyLinkCommonsPanel.js +1 -1
  38. package/lib/commonjs/ui/components/signIn/OxyLinkCommonsPanel.js.map +1 -1
  39. package/lib/commonjs/ui/components/signIn/OxyPasswordPanel.js +2 -2
  40. package/lib/commonjs/ui/components/signIn/OxyPasswordPanel.js.map +1 -1
  41. package/lib/commonjs/ui/components/surfaces/ActionSheetSurface.js +2 -1
  42. package/lib/commonjs/ui/components/surfaces/ActionSheetSurface.js.map +1 -1
  43. package/lib/commonjs/ui/screens/AccountMembersScreen.js +4 -4
  44. package/lib/commonjs/ui/screens/AccountMembersScreen.js.map +1 -1
  45. package/lib/commonjs/ui/screens/AccountVerificationScreen.js +1 -1
  46. package/lib/commonjs/ui/screens/AccountVerificationScreen.js.map +1 -1
  47. package/lib/commonjs/ui/screens/ConnectedAppsScreen.js +1 -1
  48. package/lib/commonjs/ui/screens/ConnectedAppsScreen.js.map +1 -1
  49. package/lib/commonjs/ui/screens/DomainsScreen.js +1 -1
  50. package/lib/commonjs/ui/screens/DomainsScreen.js.map +1 -1
  51. package/lib/commonjs/ui/screens/EditProfileFieldScreen.js +4 -2
  52. package/lib/commonjs/ui/screens/EditProfileFieldScreen.js.map +1 -1
  53. package/lib/commonjs/ui/screens/FileManagementScreen.js +5 -5
  54. package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -1
  55. package/lib/commonjs/ui/screens/HistoryViewScreen.js +1 -1
  56. package/lib/commonjs/ui/screens/HistoryViewScreen.js.map +1 -1
  57. package/lib/commonjs/ui/screens/LanguageSelectorScreen.js +1 -1
  58. package/lib/commonjs/ui/screens/LanguageSelectorScreen.js.map +1 -1
  59. package/lib/commonjs/ui/screens/LegalDocumentsScreen.js +1 -1
  60. package/lib/commonjs/ui/screens/LegalDocumentsScreen.js.map +1 -1
  61. package/lib/commonjs/ui/screens/NotificationsScreen.js +8 -8
  62. package/lib/commonjs/ui/screens/NotificationsScreen.js.map +1 -1
  63. package/lib/commonjs/ui/screens/PaymentGatewayScreen.js +1 -1
  64. package/lib/commonjs/ui/screens/PaymentGatewayScreen.js.map +1 -1
  65. package/lib/commonjs/ui/screens/PreferencesScreen.js +2 -2
  66. package/lib/commonjs/ui/screens/PreferencesScreen.js.map +1 -1
  67. package/lib/commonjs/ui/screens/PrivacySettingsScreen.js +37 -37
  68. package/lib/commonjs/ui/screens/PrivacySettingsScreen.js.map +1 -1
  69. package/lib/commonjs/ui/screens/ProfileScreen.js +1 -1
  70. package/lib/commonjs/ui/screens/ProfileScreen.js.map +1 -1
  71. package/lib/commonjs/ui/screens/SearchSettingsScreen.js +5 -5
  72. package/lib/commonjs/ui/screens/SearchSettingsScreen.js.map +1 -1
  73. package/lib/commonjs/ui/screens/UserListScreen.js +1 -1
  74. package/lib/commonjs/ui/screens/UserListScreen.js.map +1 -1
  75. package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js +11 -11
  76. package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js.map +1 -1
  77. package/lib/commonjs/ui/screens/fileManagement/UploadBar.js +1 -1
  78. package/lib/commonjs/ui/screens/fileManagement/UploadBar.js.map +1 -1
  79. package/lib/commonjs/ui/screens/trust/TrustCenterScreen.js +1 -1
  80. package/lib/commonjs/ui/screens/trust/TrustCenterScreen.js.map +1 -1
  81. package/lib/commonjs/ui/screens/trust/TrustRulesScreen.js +1 -1
  82. package/lib/commonjs/ui/screens/trust/TrustRulesScreen.js.map +1 -1
  83. package/lib/module/index.js +3 -2
  84. package/lib/module/index.js.map +1 -1
  85. package/lib/module/ui/client.js +17 -4
  86. package/lib/module/ui/client.js.map +1 -1
  87. package/lib/module/ui/components/BenefitList.js +1 -1
  88. package/lib/module/ui/components/BenefitList.js.map +1 -1
  89. package/lib/module/ui/components/FollowButton.js +2 -2
  90. package/lib/module/ui/components/FollowButton.js.map +1 -1
  91. package/lib/module/ui/components/FollowTargetButton.js +3 -91
  92. package/lib/module/ui/components/FollowTargetButton.js.map +1 -1
  93. package/lib/module/ui/components/PeableButton.js +2 -3
  94. package/lib/module/ui/components/PeableButton.js.map +1 -1
  95. package/lib/module/ui/components/SurfaceHeaderAction.js +1 -1
  96. package/lib/module/ui/components/SurfaceHeaderAction.js.map +1 -1
  97. package/lib/module/ui/components/SurfaceScreen.js +1 -1
  98. package/lib/module/ui/components/SurfaceScreen.js.map +1 -1
  99. package/lib/module/ui/components/authChooser/AccountsMenuView.js +1 -1
  100. package/lib/module/ui/components/authChooser/AccountsMenuView.js.map +1 -1
  101. package/lib/module/ui/components/authChooser/primitives.js +1 -1
  102. package/lib/module/ui/components/authChooser/primitives.js.map +1 -1
  103. package/lib/module/ui/components/authChooser/requestSurfaces.js +1 -1
  104. package/lib/module/ui/components/authChooser/requestSurfaces.js.map +1 -1
  105. package/lib/module/ui/components/followRules.js +99 -0
  106. package/lib/module/ui/components/followRules.js.map +1 -0
  107. package/lib/module/ui/components/payment/PaymentDetailsStep.js +2 -2
  108. package/lib/module/ui/components/payment/PaymentDetailsStep.js.map +1 -1
  109. package/lib/module/ui/components/payment/PaymentMethodStep.js +2 -2
  110. package/lib/module/ui/components/payment/PaymentMethodStep.js.map +1 -1
  111. package/lib/module/ui/components/payment/PaymentReviewStep.js +2 -2
  112. package/lib/module/ui/components/payment/PaymentReviewStep.js.map +1 -1
  113. package/lib/module/ui/components/payment/PaymentSuccessStep.js +1 -1
  114. package/lib/module/ui/components/payment/PaymentSuccessStep.js.map +1 -1
  115. package/lib/module/ui/components/payment/PaymentSummaryStep.js +2 -2
  116. package/lib/module/ui/components/payment/PaymentSummaryStep.js.map +1 -1
  117. package/lib/module/ui/components/signIn/OxyAuthScreen.js +1 -1
  118. package/lib/module/ui/components/signIn/OxyAuthScreen.js.map +1 -1
  119. package/lib/module/ui/components/signIn/OxyLinkCommonsPanel.js +1 -1
  120. package/lib/module/ui/components/signIn/OxyLinkCommonsPanel.js.map +1 -1
  121. package/lib/module/ui/components/signIn/OxyPasswordPanel.js +2 -2
  122. package/lib/module/ui/components/signIn/OxyPasswordPanel.js.map +1 -1
  123. package/lib/module/ui/components/surfaces/ActionSheetSurface.js +2 -1
  124. package/lib/module/ui/components/surfaces/ActionSheetSurface.js.map +1 -1
  125. package/lib/module/ui/screens/AccountMembersScreen.js +4 -4
  126. package/lib/module/ui/screens/AccountMembersScreen.js.map +1 -1
  127. package/lib/module/ui/screens/AccountVerificationScreen.js +1 -1
  128. package/lib/module/ui/screens/AccountVerificationScreen.js.map +1 -1
  129. package/lib/module/ui/screens/ConnectedAppsScreen.js +1 -1
  130. package/lib/module/ui/screens/ConnectedAppsScreen.js.map +1 -1
  131. package/lib/module/ui/screens/DomainsScreen.js +1 -1
  132. package/lib/module/ui/screens/DomainsScreen.js.map +1 -1
  133. package/lib/module/ui/screens/EditProfileFieldScreen.js +4 -2
  134. package/lib/module/ui/screens/EditProfileFieldScreen.js.map +1 -1
  135. package/lib/module/ui/screens/FileManagementScreen.js +5 -5
  136. package/lib/module/ui/screens/FileManagementScreen.js.map +1 -1
  137. package/lib/module/ui/screens/HistoryViewScreen.js +1 -1
  138. package/lib/module/ui/screens/HistoryViewScreen.js.map +1 -1
  139. package/lib/module/ui/screens/LanguageSelectorScreen.js +1 -1
  140. package/lib/module/ui/screens/LanguageSelectorScreen.js.map +1 -1
  141. package/lib/module/ui/screens/LegalDocumentsScreen.js +1 -1
  142. package/lib/module/ui/screens/LegalDocumentsScreen.js.map +1 -1
  143. package/lib/module/ui/screens/NotificationsScreen.js +8 -8
  144. package/lib/module/ui/screens/NotificationsScreen.js.map +1 -1
  145. package/lib/module/ui/screens/PaymentGatewayScreen.js +1 -1
  146. package/lib/module/ui/screens/PaymentGatewayScreen.js.map +1 -1
  147. package/lib/module/ui/screens/PreferencesScreen.js +2 -2
  148. package/lib/module/ui/screens/PreferencesScreen.js.map +1 -1
  149. package/lib/module/ui/screens/PrivacySettingsScreen.js +37 -37
  150. package/lib/module/ui/screens/PrivacySettingsScreen.js.map +1 -1
  151. package/lib/module/ui/screens/ProfileScreen.js +1 -1
  152. package/lib/module/ui/screens/ProfileScreen.js.map +1 -1
  153. package/lib/module/ui/screens/SearchSettingsScreen.js +5 -5
  154. package/lib/module/ui/screens/SearchSettingsScreen.js.map +1 -1
  155. package/lib/module/ui/screens/UserListScreen.js +1 -1
  156. package/lib/module/ui/screens/UserListScreen.js.map +1 -1
  157. package/lib/module/ui/screens/WelcomeNewUserScreen.js +11 -11
  158. package/lib/module/ui/screens/WelcomeNewUserScreen.js.map +1 -1
  159. package/lib/module/ui/screens/fileManagement/UploadBar.js +1 -1
  160. package/lib/module/ui/screens/fileManagement/UploadBar.js.map +1 -1
  161. package/lib/module/ui/screens/trust/TrustCenterScreen.js +1 -1
  162. package/lib/module/ui/screens/trust/TrustCenterScreen.js.map +1 -1
  163. package/lib/module/ui/screens/trust/TrustRulesScreen.js +1 -1
  164. package/lib/module/ui/screens/trust/TrustRulesScreen.js.map +1 -1
  165. package/lib/typescript/commonjs/index.d.ts +4 -2
  166. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  167. package/lib/typescript/commonjs/ui/client.d.ts +19 -4
  168. package/lib/typescript/commonjs/ui/client.d.ts.map +1 -1
  169. package/lib/typescript/commonjs/ui/components/BenefitList.d.ts.map +1 -1
  170. package/lib/typescript/commonjs/ui/components/FollowButton.d.ts +1 -1
  171. package/lib/typescript/commonjs/ui/components/FollowButton.d.ts.map +1 -1
  172. package/lib/typescript/commonjs/ui/components/FollowTargetButton.d.ts +3 -63
  173. package/lib/typescript/commonjs/ui/components/FollowTargetButton.d.ts.map +1 -1
  174. package/lib/typescript/commonjs/ui/components/PeableButton.d.ts.map +1 -1
  175. package/lib/typescript/commonjs/ui/components/followRules.d.ts +71 -0
  176. package/lib/typescript/commonjs/ui/components/followRules.d.ts.map +1 -0
  177. package/lib/typescript/commonjs/ui/components/surfaces/ActionSheetSurface.d.ts.map +1 -1
  178. package/lib/typescript/commonjs/ui/screens/EditProfileFieldScreen.d.ts.map +1 -1
  179. package/lib/typescript/module/index.d.ts +4 -2
  180. package/lib/typescript/module/index.d.ts.map +1 -1
  181. package/lib/typescript/module/ui/client.d.ts +19 -4
  182. package/lib/typescript/module/ui/client.d.ts.map +1 -1
  183. package/lib/typescript/module/ui/components/BenefitList.d.ts.map +1 -1
  184. package/lib/typescript/module/ui/components/FollowButton.d.ts +1 -1
  185. package/lib/typescript/module/ui/components/FollowButton.d.ts.map +1 -1
  186. package/lib/typescript/module/ui/components/FollowTargetButton.d.ts +3 -63
  187. package/lib/typescript/module/ui/components/FollowTargetButton.d.ts.map +1 -1
  188. package/lib/typescript/module/ui/components/PeableButton.d.ts.map +1 -1
  189. package/lib/typescript/module/ui/components/followRules.d.ts +71 -0
  190. package/lib/typescript/module/ui/components/followRules.d.ts.map +1 -0
  191. package/lib/typescript/module/ui/components/surfaces/ActionSheetSurface.d.ts.map +1 -1
  192. package/lib/typescript/module/ui/screens/EditProfileFieldScreen.d.ts.map +1 -1
  193. package/package.json +5 -5
  194. package/src/index.ts +3 -3
  195. package/src/ui/client.ts +55 -4
  196. package/src/ui/components/BenefitList.tsx +1 -2
  197. package/src/ui/components/FollowButton.tsx +3 -3
  198. package/src/ui/components/FollowTargetButton.tsx +11 -115
  199. package/src/ui/components/PeableButton.tsx +3 -4
  200. package/src/ui/components/SurfaceHeaderAction.tsx +1 -1
  201. package/src/ui/components/SurfaceScreen.tsx +1 -1
  202. package/src/ui/components/__tests__/followButtonAccessibility.test.tsx +9 -0
  203. package/src/ui/components/__tests__/followTargetButton.test.ts +1 -1
  204. package/src/ui/components/authChooser/AccountsMenuView.tsx +1 -1
  205. package/src/ui/components/authChooser/primitives.tsx +1 -1
  206. package/src/ui/components/authChooser/requestSurfaces.tsx +1 -1
  207. package/src/ui/components/followRules.ts +121 -0
  208. package/src/ui/components/payment/PaymentDetailsStep.tsx +2 -2
  209. package/src/ui/components/payment/PaymentMethodStep.tsx +2 -2
  210. package/src/ui/components/payment/PaymentReviewStep.tsx +2 -2
  211. package/src/ui/components/payment/PaymentSuccessStep.tsx +1 -1
  212. package/src/ui/components/payment/PaymentSummaryStep.tsx +2 -2
  213. package/src/ui/components/signIn/OxyAuthScreen.tsx +1 -1
  214. package/src/ui/components/signIn/OxyLinkCommonsPanel.tsx +1 -1
  215. package/src/ui/components/signIn/OxyPasswordPanel.tsx +1 -1
  216. package/src/ui/components/surfaces/ActionSheetSurface.tsx +2 -1
  217. package/src/ui/screens/AccountMembersScreen.tsx +4 -4
  218. package/src/ui/screens/AccountVerificationScreen.tsx +1 -1
  219. package/src/ui/screens/ConnectedAppsScreen.tsx +1 -1
  220. package/src/ui/screens/DomainsScreen.tsx +1 -1
  221. package/src/ui/screens/EditProfileFieldScreen.tsx +1 -2
  222. package/src/ui/screens/FileManagementScreen.tsx +5 -5
  223. package/src/ui/screens/HistoryViewScreen.tsx +1 -1
  224. package/src/ui/screens/LanguageSelectorScreen.tsx +1 -1
  225. package/src/ui/screens/LegalDocumentsScreen.tsx +1 -1
  226. package/src/ui/screens/NotificationsScreen.tsx +8 -8
  227. package/src/ui/screens/PaymentGatewayScreen.tsx +1 -1
  228. package/src/ui/screens/PreferencesScreen.tsx +2 -2
  229. package/src/ui/screens/PrivacySettingsScreen.tsx +21 -21
  230. package/src/ui/screens/ProfileScreen.tsx +1 -1
  231. package/src/ui/screens/SearchSettingsScreen.tsx +5 -5
  232. package/src/ui/screens/UserListScreen.tsx +1 -1
  233. package/src/ui/screens/WelcomeNewUserScreen.tsx +11 -11
  234. package/src/ui/screens/fileManagement/UploadBar.tsx +1 -1
  235. package/src/ui/screens/trust/TrustCenterScreen.tsx +1 -1
  236. package/src/ui/screens/trust/TrustRulesScreen.tsx +1 -1
@@ -32,7 +32,7 @@
32
32
  */
33
33
 
34
34
  import { memo, useCallback, useMemo } from 'react';
35
- import type { FollowApplicationMode, FollowStatus } from '@oxy.so/contracts';
35
+ import type { FollowStatus } from '@oxy.so/contracts';
36
36
  import type { StyleProp, ViewStyle } from 'react-native';
37
37
  import { View } from 'react-native';
38
38
  import { FollowButton as BloomFollowButton } from '@oxy.so/bloom/media-header';
@@ -51,6 +51,13 @@ import {
51
51
  import { toast } from '@oxy.so/bloom/toast';
52
52
  import { useFollowTarget } from '../hooks/useFollowTarget';
53
53
  import { useFollowTargetStore } from '../stores/followTargetStore';
54
+ import {
55
+ buildFollowMenuItems,
56
+ FOLLOW_ACTION_LEAVES_ACTIVE,
57
+ resolveFollowPrimaryAction,
58
+ type FollowDuration,
59
+ type FollowMenuItem,
60
+ } from './followRules';
54
61
 
55
62
  /**
56
63
  * The small vocabulary of verbs the ecosystem actually uses. An application
@@ -82,12 +89,6 @@ const VERB_LABELS: Record<FollowVerb, FollowLabels> = {
82
89
  watch: { idle: 'Watch', active: 'Watching', pending: 'Requested', disabled: 'Off here' },
83
90
  };
84
91
 
85
- /** A timed-follow choice offered in the menu. */
86
- export interface FollowDuration {
87
- label: string;
88
- seconds: number;
89
- }
90
-
91
92
  const HOUR = 60 * 60;
92
93
 
93
94
  /**
@@ -101,118 +102,13 @@ const DEFAULT_DURATIONS: FollowDuration[] = [
101
102
  { label: 'A week', seconds: 7 * 24 * HOUR },
102
103
  ];
103
104
 
104
- /**
105
- * Whether an action leaves the target ACTIVE in this application.
106
- *
107
- * One table, read by both the primary button and the menu, because the same
108
- * action reached from two controls once reported differently — and a rule
109
- * living in two switch statements is a rule that drifts. `Record` rather than
110
- * a function with a default, so a new action is a compile error instead of a
111
- * silent `false`.
112
- */
113
- export const FOLLOW_ACTION_LEAVES_ACTIVE: Record<
114
- 'follow' | 'follow-timed' | 'enable-here' | 'disable-here' | 'unfollow',
115
- boolean
116
- > = {
117
- follow: true,
118
- 'follow-timed': true,
119
- // Re-enabling here does not change the global follow — the user already had
120
- // it — but it does change whether this application acts on it, which is what
121
- // a mirror is asking about.
122
- 'enable-here': true,
123
- 'disable-here': false,
124
- unfollow: false,
125
- };
126
-
127
- /** One line in the disclosure menu. */
128
- export interface FollowMenuItem {
129
- key: string;
130
- label: string;
131
- /** What the component should call. Named so the table below stays pure. */
132
- action:
133
- | { type: 'follow-timed'; seconds: number; durationLabel: string }
134
- | { type: 'enable-here' }
135
- | { type: 'disable-here' }
136
- | { type: 'unfollow' };
137
- }
138
-
139
- /**
140
- * Which choices exist, given the state.
141
- *
142
- * Pure and exported because this is the product decision, not a rendering
143
- * detail: a timed follow is only offered before following, turning it off here
144
- * is only offered while following, and NEITHER is offered before the server has
145
- * answered — every one of them addresses a relationship that does not exist
146
- * yet, so offering them mid-write would mean sending a guessed id.
147
- */
148
- /**
149
- * What the main button should do for the current state.
150
- *
151
- * Exported because the product rule is not obvious from the label alone: a
152
- * follow switched off here still reads as "following" globally, so the primary
153
- * press must re-enable here — not unfollow everywhere.
154
- */
155
- export function resolveFollowPrimaryAction(input: {
156
- isFollowing: boolean;
157
- applicationMode: FollowApplicationMode;
158
- }): 'follow' | 'unfollow' | 'enable-here' {
159
- if (!input.isFollowing) return 'follow';
160
- if (input.applicationMode === 'disabled') return 'enable-here';
161
- return 'unfollow';
162
- }
163
-
164
- export function buildFollowMenuItems(input: {
165
- following: boolean;
166
- applicationMode: FollowApplicationMode;
167
- hasRelationship: boolean;
168
- isPending: boolean;
169
- durations: FollowDuration[] | false;
170
- idleVerb: string;
171
- applicationName: string;
172
- }): FollowMenuItem[] {
173
- const items: FollowMenuItem[] = [];
174
-
175
- if (!input.following) {
176
- if (input.durations === false) return items;
177
- for (const d of input.durations) {
178
- items.push({
179
- key: `for-${d.seconds}`,
180
- label: `${input.idleVerb} for ${d.label.toLowerCase()}`,
181
- action: { type: 'follow-timed', seconds: d.seconds, durationLabel: d.label },
182
- });
183
- }
184
- return items;
185
- }
186
-
187
- if (!input.hasRelationship || input.isPending) return items;
188
-
189
- items.push(
190
- input.applicationMode === 'disabled'
191
- ? { key: 'enable-here', label: `Show in ${input.applicationName}`, action: { type: 'enable-here' } }
192
- : {
193
- key: 'disable-here',
194
- label: `Don’t show in ${input.applicationName}`,
195
- action: { type: 'disable-here' },
196
- }
197
- );
198
- items.push({
199
- // Named for what it does. "Unfollow" beside "don't show here" would read as
200
- // the same action twice, and the user would pick the wrong one.
201
- key: 'unfollow-everywhere',
202
- label: 'Unfollow everywhere',
203
- action: { type: 'unfollow' },
204
- });
205
-
206
- return items;
207
- }
208
-
209
105
  export interface FollowTargetButtonProps {
210
106
  /** The registered target's id. Registration is separate — see the SDK. */
211
107
  targetId: string;
212
108
  verb?: FollowVerb;
213
109
  /** Full override, for a verb the vocabulary above does not carry. */
214
110
  labels?: Partial<FollowLabels>;
215
- size?: 'small' | 'medium' | 'large';
111
+ size?: 'sm' | 'md' | 'lg';
216
112
  style?: StyleProp<ViewStyle>;
217
113
  disabled?: boolean;
218
114
  /**
@@ -256,7 +152,7 @@ export const FollowTargetButton = memo(function FollowTargetButton({
256
152
  targetId,
257
153
  verb = 'follow',
258
154
  labels,
259
- size = 'medium',
155
+ size = 'md',
260
156
  style,
261
157
  disabled = false,
262
158
  showOptions = true,
@@ -413,7 +309,7 @@ export const FollowTargetButton = memo(function FollowTargetButton({
413
309
  <DropdownMenuTrigger asChild disabled={disabled || isUnknown}>
414
310
  <Button
415
311
  appearance="subtle" tone="support"
416
- size={size === 'large' ? 'large' : 'small'}
312
+ size={size === 'lg' ? 'lg' : 'sm'}
417
313
  icon={<ChevronDown width={16} />}
418
314
  disabled={disabled || isUnknown}
419
315
  accessibilityLabel={`${label} options`}
@@ -1,7 +1,7 @@
1
1
  import type React from 'react';
2
2
  import type { ViewStyle, TextStyle, StyleProp } from 'react-native';
3
3
  import { useTheme } from '@oxy.so/bloom/theme';
4
- import { Button } from '@oxy.so/bloom/button';
4
+ import { InverseButton } from '@oxy.so/bloom/button';
5
5
  import type { PaymentItem, PaymentGatewayResult } from '../screens/PaymentGatewayScreen';
6
6
  import { LogoIcon } from './logo/LogoIcon';
7
7
 
@@ -46,8 +46,7 @@ const PeableButton: React.FC<PeableButtonProps> = ({
46
46
  const textColor = variant === 'black' || (color && isColorDark(color)) ? theme.colors.background : '#1b1f0a';
47
47
 
48
48
  return (
49
- <Button
50
- variant="inverse"
49
+ <InverseButton
51
50
  onPress={handlePress}
52
51
  disabled={disabled}
53
52
  style={[{ backgroundColor, borderColor: textColor, borderWidth: 1 }, style]}
@@ -55,7 +54,7 @@ const PeableButton: React.FC<PeableButtonProps> = ({
55
54
  icon={<LogoIcon height={16} color={textColor} style={{ marginRight: 6 }} />}
56
55
  >
57
56
  {text}
58
- </Button>
57
+ </InverseButton>
59
58
  );
60
59
  };
61
60
 
@@ -12,7 +12,7 @@ export interface SurfaceHeaderActionProps {
12
12
 
13
13
  /** Header actions share Bloom's sizing, surface, disabled and loading behavior. */
14
14
  export const SurfaceHeaderAction: React.FC<SurfaceHeaderActionProps> = ({ label, onPress, loading, disabled }) => (
15
- <Button size="medium" onPress={onPress} loading={loading} disabled={disabled}>
15
+ <Button size="md" onPress={onPress} loading={loading} disabled={disabled}>
16
16
  {label}
17
17
  </Button>
18
18
  );
@@ -88,7 +88,7 @@ const errorStyles = StyleSheet.create({
88
88
  function ScreenPending() {
89
89
  return (
90
90
  <View style={errorStyles.container}>
91
- <Loading variant="spinner" size="large" />
91
+ <Loading variant="spinner" size="lg" />
92
92
  </View>
93
93
  );
94
94
  }
@@ -16,6 +16,7 @@ import {
16
16
  } from '../followButtonAccessibility';
17
17
 
18
18
  interface BloomFollowProps {
19
+ size?: 'sm' | 'md' | 'lg';
19
20
  following: boolean;
20
21
  label?: string;
21
22
  followingLabel?: string;
@@ -189,3 +190,11 @@ describe('FollowTargetButton names the state it shows', () => {
189
190
  expect(lastBloomProps()?.accessibilityLabel).toBe(label);
190
191
  });
191
192
  });
193
+
194
+
195
+ it.each(['sm', 'md', 'lg'] as const)('forwards canonical %s geometry in both follow modes', size => {
196
+ render(<FollowButton userId="nate-id" size={size} />);
197
+ expect(lastBloomProps()?.size).toBe(size);
198
+ render(<FollowButton userIds={['a', 'b']} size={size} />);
199
+ expect(lastBloomProps()?.size).toBe(size);
200
+ });
@@ -16,7 +16,7 @@ import {
16
16
  buildFollowMenuItems,
17
17
  FOLLOW_ACTION_LEAVES_ACTIVE,
18
18
  resolveFollowPrimaryAction,
19
- } from '../FollowTargetButton';
19
+ } from '../followRules';
20
20
  import {
21
21
  isCompleteFollowStatus,
22
22
  followRecordToStatus,
@@ -385,7 +385,7 @@ const AccountsMenuView: React.FC<AccountsMenuViewProps> = ({
385
385
  spanning the surface. */}
386
386
  <Button
387
387
  appearance="subtle" tone="neutral"
388
- size="small"
388
+ size="sm"
389
389
  onPress={handlers.onManage}
390
390
  accessibilityLabel={manageLabel}
391
391
  >
@@ -27,7 +27,7 @@ export const SubtleLink: React.FC<{
27
27
  testID?: string;
28
28
  }> = ({ label, onPress, disabled, testID }) => (
29
29
  <LinkButton
30
- variant="secondary"
30
+ linkTone="secondary"
31
31
  size="sm"
32
32
  onPress={onPress}
33
33
  disabled={disabled}
@@ -40,7 +40,7 @@ const ROUTE_GLYPH_SIZE = 44;
40
40
  */
41
41
  export const PreparingSurface: React.FC = () => {
42
42
  const { t } = useI18n();
43
- return <Loading variant="spinner" size="large" accessibilityLabel={t('common.status.loading')} />;
43
+ return <Loading variant="spinner" size="lg" accessibilityLabel={t('common.status.loading')} />;
44
44
  };
45
45
 
46
46
  /** The leading glyph for a route whose surface is elsewhere (the phone, Commons). */
@@ -0,0 +1,121 @@
1
+ /**
2
+ * The follow button's product rules, without the button.
3
+ *
4
+ * Pure: no React, no Bloom. `FollowTargetButton` renders them, and an
5
+ * application drawing its own affordance (a chip grid, a compact row) imports
6
+ * them from here — through `@oxy.so/services/ui/client` — without paying for the
7
+ * button's dropdown menu.
8
+ */
9
+
10
+ import type { FollowApplicationMode } from '@oxy.so/contracts';
11
+
12
+ /** A timed-follow choice offered in the menu. */
13
+ export interface FollowDuration {
14
+ label: string;
15
+ seconds: number;
16
+ }
17
+
18
+ /**
19
+ * Whether an action leaves the target ACTIVE in this application.
20
+ *
21
+ * One table, read by both the primary button and the menu, because the same
22
+ * action reached from two controls once reported differently — and a rule
23
+ * living in two switch statements is a rule that drifts. `Record` rather than
24
+ * a function with a default, so a new action is a compile error instead of a
25
+ * silent `false`.
26
+ */
27
+ export const FOLLOW_ACTION_LEAVES_ACTIVE: Record<
28
+ 'follow' | 'follow-timed' | 'enable-here' | 'disable-here' | 'unfollow',
29
+ boolean
30
+ > = {
31
+ follow: true,
32
+ 'follow-timed': true,
33
+ // Re-enabling here does not change the global follow — the user already had
34
+ // it — but it does change whether this application acts on it, which is what
35
+ // a mirror is asking about.
36
+ 'enable-here': true,
37
+ 'disable-here': false,
38
+ unfollow: false,
39
+ };
40
+
41
+ /** One line in the disclosure menu. */
42
+ export interface FollowMenuItem {
43
+ key: string;
44
+ label: string;
45
+ /** What the component should call. Named so the table below stays pure. */
46
+ action:
47
+ | { type: 'follow-timed'; seconds: number; durationLabel: string }
48
+ | { type: 'enable-here' }
49
+ | { type: 'disable-here' }
50
+ | { type: 'unfollow' };
51
+ }
52
+
53
+ /**
54
+ * Which choices exist, given the state.
55
+ *
56
+ * Pure and exported because this is the product decision, not a rendering
57
+ * detail: a timed follow is only offered before following, turning it off here
58
+ * is only offered while following, and NEITHER is offered before the server has
59
+ * answered — every one of them addresses a relationship that does not exist
60
+ * yet, so offering them mid-write would mean sending a guessed id.
61
+ */
62
+ /**
63
+ * What the main button should do for the current state.
64
+ *
65
+ * Exported because the product rule is not obvious from the label alone: a
66
+ * follow switched off here still reads as "following" globally, so the primary
67
+ * press must re-enable here — not unfollow everywhere.
68
+ */
69
+ export function resolveFollowPrimaryAction(input: {
70
+ isFollowing: boolean;
71
+ applicationMode: FollowApplicationMode;
72
+ }): 'follow' | 'unfollow' | 'enable-here' {
73
+ if (!input.isFollowing) return 'follow';
74
+ if (input.applicationMode === 'disabled') return 'enable-here';
75
+ return 'unfollow';
76
+ }
77
+
78
+ export function buildFollowMenuItems(input: {
79
+ following: boolean;
80
+ applicationMode: FollowApplicationMode;
81
+ hasRelationship: boolean;
82
+ isPending: boolean;
83
+ durations: FollowDuration[] | false;
84
+ idleVerb: string;
85
+ applicationName: string;
86
+ }): FollowMenuItem[] {
87
+ const items: FollowMenuItem[] = [];
88
+
89
+ if (!input.following) {
90
+ if (input.durations === false) return items;
91
+ for (const d of input.durations) {
92
+ items.push({
93
+ key: `for-${d.seconds}`,
94
+ label: `${input.idleVerb} for ${d.label.toLowerCase()}`,
95
+ action: { type: 'follow-timed', seconds: d.seconds, durationLabel: d.label },
96
+ });
97
+ }
98
+ return items;
99
+ }
100
+
101
+ if (!input.hasRelationship || input.isPending) return items;
102
+
103
+ items.push(
104
+ input.applicationMode === 'disabled'
105
+ ? { key: 'enable-here', label: `Show in ${input.applicationName}`, action: { type: 'enable-here' } }
106
+ : {
107
+ key: 'disable-here',
108
+ label: `Don’t show in ${input.applicationName}`,
109
+ action: { type: 'disable-here' },
110
+ }
111
+ );
112
+ items.push({
113
+ // Named for what it does. "Unfollow" beside "don't show here" would read as
114
+ // the same action twice, and the user would pick the wrong one.
115
+ key: 'unfollow-everywhere',
116
+ label: 'Unfollow everywhere',
117
+ action: { type: 'unfollow' },
118
+ });
119
+
120
+ return items;
121
+ }
@@ -214,13 +214,13 @@ const PaymentDetailsStep: React.FC<PaymentDetailsStepProps> = ({
214
214
  </View>
215
215
 
216
216
  <View style={{ flexDirection: 'row', gap: 8, justifyContent: 'flex-end' }}>
217
- <Button appearance="subtle" tone="neutral" onPress={onBack} size="small" icon={<Ionicons name="arrow-back" size={16} />}>
217
+ <Button appearance="subtle" tone="neutral" onPress={onBack} size="sm" icon={<Ionicons name="arrow-back" size={16} />}>
218
218
  {t('payment.actions.back')}
219
219
  </Button>
220
220
  <Button
221
221
  appearance="solid" tone="accent"
222
222
  onPress={onNext}
223
- size="small"
223
+ size="sm"
224
224
  icon={<Ionicons name="arrow-forward" size={16} />}
225
225
  iconPosition="right"
226
226
  disabled={paymentMethod === 'card' && !isCardValid}
@@ -77,10 +77,10 @@ const PaymentMethodStep: React.FC<PaymentMethodStepProps> = ({
77
77
  </View>
78
78
 
79
79
  <View style={{ flexDirection: 'row', gap: 8, justifyContent: 'flex-end' }}>
80
- <Button appearance="subtle" tone="neutral" onPress={onBack} size="small" icon={<Ionicons name="arrow-back" size={16} />}>
80
+ <Button appearance="subtle" tone="neutral" onPress={onBack} size="sm" icon={<Ionicons name="arrow-back" size={16} />}>
81
81
  {t('payment.actions.back')}
82
82
  </Button>
83
- <Button appearance="solid" tone="accent" onPress={onNext} size="small" icon={<Ionicons name="arrow-forward" size={16} />} iconPosition="right">
83
+ <Button appearance="solid" tone="accent" onPress={onNext} size="sm" icon={<Ionicons name="arrow-forward" size={16} />} iconPosition="right">
84
84
  {t('payment.actions.continue')}
85
85
  </Button>
86
86
  </View>
@@ -105,13 +105,13 @@ const PaymentReviewStep: React.FC<PaymentReviewStepProps> = ({
105
105
  </View>
106
106
 
107
107
  <View style={{ flexDirection: 'row', gap: 8, justifyContent: 'flex-end' }}>
108
- <Button appearance="subtle" tone="neutral" onPress={onBack} size="small" disabled={isPaying} icon={<Ionicons name="arrow-back" size={16} />}>
108
+ <Button appearance="subtle" tone="neutral" onPress={onBack} size="sm" disabled={isPaying} icon={<Ionicons name="arrow-back" size={16} />}>
109
109
  {t('payment.actions.back')}
110
110
  </Button>
111
111
  <Button
112
112
  appearance="solid" tone="accent"
113
113
  onPress={onPay}
114
- size="small"
114
+ size="sm"
115
115
  disabled={isPaying}
116
116
  icon={isPaying ? <ActivityIndicator size="small" color="#FFFFFF" /> : <Ionicons name="checkmark" size={16} />}
117
117
  iconPosition="right"
@@ -57,7 +57,7 @@ const PaymentSuccessStep: React.FC<PaymentSuccessStepProps> = ({
57
57
  </View>
58
58
  </View>
59
59
 
60
- <Button appearance="solid" tone="accent" onPress={onDone} size="small" icon={<Ionicons name="checkmark" size={16} />} iconPosition="right">
60
+ <Button appearance="solid" tone="accent" onPress={onDone} size="sm" icon={<Ionicons name="checkmark" size={16} />} iconPosition="right">
61
61
  {t('payment.actions.done')}
62
62
  </Button>
63
63
  </Animated.View>
@@ -145,10 +145,10 @@ const PaymentSummaryStep: React.FC<PaymentSummaryStepProps> = ({
145
145
  </View>
146
146
 
147
147
  <View style={{ flexDirection: 'row', gap: 8, justifyContent: 'flex-end' }}>
148
- <Button appearance="subtle" tone="neutral" onPress={onClose} size="small" icon={<Ionicons name="close" size={16} />}>
148
+ <Button appearance="subtle" tone="neutral" onPress={onClose} size="sm" icon={<Ionicons name="close" size={16} />}>
149
149
  {t('payment.actions.close')}
150
150
  </Button>
151
- <Button appearance="solid" tone="accent" onPress={onNext} size="small" icon={<Ionicons name="arrow-forward" size={16} />} iconPosition="right">
151
+ <Button appearance="solid" tone="accent" onPress={onNext} size="sm" icon={<Ionicons name="arrow-forward" size={16} />} iconPosition="right">
152
152
  {t('payment.actions.continue')}
153
153
  </Button>
154
154
  </View>
@@ -103,7 +103,7 @@ export const OxyAuthLoading: React.FC = () => {
103
103
  const { t } = useI18n();
104
104
  return (
105
105
  <View className="min-h-[300px] items-center justify-center">
106
- <Loading variant="spinner" size="large" accessibilityLabel={t('common.status.loading')} testID="oxy-auth-loading" />
106
+ <Loading variant="spinner" size="lg" accessibilityLabel={t('common.status.loading')} testID="oxy-auth-loading" />
107
107
  </View>
108
108
  );
109
109
  };
@@ -204,7 +204,7 @@ export const OxyLinkCommonsPanel: React.FC<OxyLinkCommonsPanelProps> = ({ onLink
204
204
  <OxyAuthScreen>
205
205
  <OxyAuthScreenHeader title={t('linkCommons.title')} description={t('linkCommons.subtitle')} />
206
206
  <View style={styles.plateRow}>
207
- <Loading variant="spinner" size="large" accessibilityLabel={t('common.status.loading')} />
207
+ <Loading variant="spinner" size="lg" accessibilityLabel={t('common.status.loading')} />
208
208
  </View>
209
209
  </OxyAuthScreen>
210
210
  );
@@ -95,7 +95,7 @@ export const OxyPasswordPanel: React.FC<OxyPasswordPanelProps> = ({ onDone, onCa
95
95
  {/* The field names the switch ("Sign out everywhere else, switch, off")
96
96
  and, on web, its label toggles it. One row: label, then the switch. */}
97
97
  <Field label={t('signInSecurity.password.signOutOthers')} style={styles.row}>
98
- <Switch value={signOutOthers} onValueChange={setSignOutOthers} testID="password-sign-out-others" />
98
+ <Switch checked={signOutOthers} onCheckedChange={setSignOutOthers} testID="password-sign-out-others" />
99
99
  </Field>
100
100
  </ReauthStep>
101
101
  );
@@ -72,7 +72,8 @@ export function ActionSheetSurface<T extends string>({
72
72
  {options.map((option) => (
73
73
  <Button
74
74
  key={option.value}
75
- variant={option.destructive ? 'destructive' : 'primary'}
75
+ appearance="solid"
76
+ tone={option.destructive ? 'danger' : 'accent'}
76
77
  onPress={() => surface.dismiss(option.value)}
77
78
  >
78
79
  {option.label}
@@ -291,7 +291,7 @@ const AccountMembersScreen: React.FC<BaseScreenProps> = ({ onClose, goBack, acco
291
291
  <Text className="text-caption font-caption text-text-secondary px-space-4">
292
292
  {t('accounts.members.invite.roleLabel') || 'Role'}
293
293
  </Text>
294
- <SegmentedControl type="radio" label={t('accounts.members.invite.roleLabel') || 'Role'} value={inviteRole} onValueChange={setInviteRole} size="small">
294
+ <SegmentedControl type="radio" label={t('accounts.members.invite.roleLabel') || 'Role'} value={inviteRole} onValueChange={setInviteRole} size="sm">
295
295
  {ASSIGNABLE_ROLES.map(role => <SegmentedControlItem key={role} value={role}>
296
296
  <SegmentedControlItemText>{roleLabel(role)}</SegmentedControlItemText>
297
297
  </SegmentedControlItem>)}
@@ -372,7 +372,7 @@ const AccountMembersScreen: React.FC<BaseScreenProps> = ({ onClose, goBack, acco
372
372
  {/* Role chips (editable) or static role label */}
373
373
  {canEditThisRole ? (
374
374
  <SegmentedControl type="radio" label={t('accounts.members.invite.roleLabel') || 'Role'}
375
- value={member.role} onValueChange={role => handleChangeRole(member, role)} disabled={updateMutation.isPending} size="small">
375
+ value={member.role} onValueChange={role => handleChangeRole(member, role)} disabled={updateMutation.isPending} size="sm">
376
376
  {ASSIGNABLE_ROLES.map(role => <SegmentedControlItem key={role} value={role}>
377
377
  <SegmentedControlItemText>{roleLabel(role)}</SegmentedControlItemText>
378
378
  </SegmentedControlItem>)}
@@ -387,7 +387,7 @@ const AccountMembersScreen: React.FC<BaseScreenProps> = ({ onClose, goBack, acco
387
387
  {(canTransferToThis || canRemoveThisMember) ? (
388
388
  <View className="flex-row gap-space-16 pt-space-4">
389
389
  {canTransferToThis ? (
390
- <Button appearance="plain" tone="neutral" size="small"
390
+ <Button appearance="plain" tone="neutral" size="sm"
391
391
  accessibilityRole="button"
392
392
  accessibilityLabel={t('accounts.members.actions.transfer') || 'Transfer ownership'}
393
393
  onPress={() => confirmTransfer(member)}
@@ -400,7 +400,7 @@ const AccountMembersScreen: React.FC<BaseScreenProps> = ({ onClose, goBack, acco
400
400
  </Button>
401
401
  ) : null}
402
402
  {canRemoveThisMember ? (
403
- <Button appearance="plain" tone="neutral" size="small"
403
+ <Button appearance="plain" tone="neutral" size="sm"
404
404
  accessibilityRole="button"
405
405
  accessibilityLabel={t('accounts.members.actions.remove') || 'Remove member'}
406
406
  onPress={() => confirmRemove(member)}
@@ -156,7 +156,7 @@ const AccountVerificationScreen: React.FC<BaseScreenProps> = ({
156
156
 
157
157
  <Button
158
158
  appearance="solid" tone="accent"
159
- size="large"
159
+ size="lg"
160
160
  fullWidth
161
161
  className="mt-space-24"
162
162
  onPress={handleSubmit}
@@ -146,7 +146,7 @@ const ConnectedAppsScreen: React.FC<BaseScreenProps> = ({ onClose, goBack }) =>
146
146
  return (
147
147
  <View className="flex-1 bg-bg">
148
148
  {isLoading && !apps ? (
149
- <Loading size="large" color={bloomTheme.colors.primary} />
149
+ <Loading size="lg" color={bloomTheme.colors.primary} />
150
150
  ) : (
151
151
  <FlatList
152
152
  data={apps ?? []}
@@ -94,7 +94,7 @@ const DomainsScreen: React.FC<BaseScreenProps> = () => {
94
94
  };
95
95
 
96
96
  if (domains.isPending && isAuthenticated) {
97
- return <Loading size="large" color={bloomTheme.colors.primary} />;
97
+ return <Loading size="lg" color={bloomTheme.colors.primary} />;
98
98
  }
99
99
 
100
100
  return (
@@ -704,11 +704,10 @@ const EditProfileFieldScreen: React.FC<EditProfileFieldScreenProps> = ({
704
704
  </TextField>
705
705
  </View>
706
706
  <Button
707
- variant="icon"
708
707
  onPress={handleAddItem}
709
708
  disabled={!newItemValue.trim()}
710
709
  accessibilityLabel={addLabel}
711
- icon={<Ionicons name="add" size={20} color={bloomTheme.colors.primaryForeground} />}
710
+ icon={<Ionicons name="add" size={20} color={bloomTheme.colors.primaryForeground} />} iconOnly tone="neutral" appearance="outline"
712
711
  />
713
712
  </View>
714
713
 
@@ -776,13 +776,13 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
776
776
  // Hide action buttons when selecting (in selectMode or bulk operations mode)
777
777
  rightElement: (!selectMode && selectedIds.size === 0) ? (
778
778
  <View className="flex-row items-center gap-1.5 ml-3">
779
- {(isImage || isVideo || file.contentType.includes('pdf')) ? <Button appearance="subtle" tone="neutral" size="small" iconOnly stopPropagation
779
+ {(isImage || isVideo || file.contentType.includes('pdf')) ? <Button appearance="subtle" tone="neutral" size="sm" iconOnly stopPropagation
780
780
  accessibilityLabel={`Preview ${file.filename}`} onPress={() => handleFileOpen(file)}
781
781
  icon={<Ionicons name="eye" size={18} color={colors.text} />} /> : null}
782
- <Button appearance="subtle" tone="neutral" size="small" iconOnly stopPropagation
782
+ <Button appearance="subtle" tone="neutral" size="sm" iconOnly stopPropagation
783
783
  accessibilityLabel={`Download ${file.filename}`} onPress={() => handleFileDownload(file.id, file.filename)}
784
784
  icon={<Ionicons name="download" size={18} color={colors.text} />} />
785
- <Button appearance="subtle" tone="danger" size="small" iconOnly stopPropagation loading={deletingId === file.id}
785
+ <Button appearance="subtle" tone="danger" size="sm" iconOnly stopPropagation loading={deletingId === file.id}
786
786
  accessibilityLabel={`Delete ${file.filename}`} onPress={() => confirmFileDelete(file.id, file.filename)}
787
787
  icon={<Ionicons name="trash" size={18} color={colors.error} />} />
788
788
  </View>
@@ -1144,7 +1144,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1144
1144
  style={{ maxWidth: '80%' }}
1145
1145
  >
1146
1146
  <SegmentedControl type="radio" label={t('fileManagement.viewMode') || 'File type'}
1147
- value={viewMode} onValueChange={setViewMode} size="small">
1147
+ value={viewMode} onValueChange={setViewMode} size="sm">
1148
1148
  {(['all', 'photos', 'videos', 'documents', 'audio'] as const).map(mode => (
1149
1149
  <SegmentedControlItem key={mode} value={mode} accessibilityLabel={mode}>
1150
1150
  <MaterialCommunityIcons
@@ -1154,7 +1154,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1154
1154
  ))}
1155
1155
  </SegmentedControl>
1156
1156
  </ScrollView>
1157
- <Button appearance="subtle" tone="neutral" size="small"
1157
+ <Button appearance="subtle" tone="neutral" size="sm"
1158
1158
  accessibilityRole="button"
1159
1159
  accessibilityLabel={t('fileManagement.a11y.sortBy', {
1160
1160
  field: sortBy,
@@ -109,7 +109,7 @@ const HistoryViewScreen: React.FC<BaseScreenProps> = ({ onClose, goBack }) => {
109
109
  />
110
110
  </SettingsListGroup>
111
111
  <SettingsListGroup title={t('history.recent') || 'Recent History'}>
112
- {isLoading ? <Loading size="large" color={bloomTheme.colors.text} text={t('history.loading') || 'Loading history...'} />
112
+ {isLoading ? <Loading size="lg" color={bloomTheme.colors.text} text={t('history.loading') || 'Loading history...'} />
113
113
  : history.length === 0 ? <Text className="text-text-secondary text-center p-space-40">{t('history.empty') || 'No history yet'}</Text>
114
114
  : history.map(item => (
115
115
  <SettingsListItem