@oxyhq/services 6.9.43 → 6.9.45

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 (205) hide show
  1. package/lib/commonjs/index.js +9 -0
  2. package/lib/commonjs/index.js.map +1 -1
  3. package/lib/commonjs/ui/components/ActingAsBanner.js +143 -0
  4. package/lib/commonjs/ui/components/ActingAsBanner.js.map +1 -0
  5. package/lib/commonjs/ui/components/OxyPayButton.js +4 -2
  6. package/lib/commonjs/ui/components/OxyPayButton.js.map +1 -1
  7. package/lib/commonjs/ui/components/OxySignInButton.js +83 -82
  8. package/lib/commonjs/ui/components/OxySignInButton.js.map +1 -1
  9. package/lib/commonjs/ui/components/SettingRow.js +11 -5
  10. package/lib/commonjs/ui/components/SettingRow.js.map +1 -1
  11. package/lib/commonjs/ui/components/fileManagement/FileDetailsModal.js +76 -121
  12. package/lib/commonjs/ui/components/fileManagement/FileDetailsModal.js.map +1 -1
  13. package/lib/commonjs/ui/components/fileManagement/UploadPreview.js +32 -18
  14. package/lib/commonjs/ui/components/fileManagement/UploadPreview.js.map +1 -1
  15. package/lib/commonjs/ui/components/icon/OxyIcon.js +5 -4
  16. package/lib/commonjs/ui/components/icon/OxyIcon.js.map +1 -1
  17. package/lib/commonjs/ui/components/internal/GroupedPillButtons.js +11 -9
  18. package/lib/commonjs/ui/components/internal/GroupedPillButtons.js.map +1 -1
  19. package/lib/commonjs/ui/components/internal/PinInput.js +3 -2
  20. package/lib/commonjs/ui/components/internal/PinInput.js.map +1 -1
  21. package/lib/commonjs/ui/components/modals/DeleteAccountModal.js +83 -219
  22. package/lib/commonjs/ui/components/modals/DeleteAccountModal.js.map +1 -1
  23. package/lib/commonjs/ui/constants/theme.js +2 -2
  24. package/lib/commonjs/ui/constants/theme.js.map +1 -1
  25. package/lib/commonjs/ui/context/OxyContext.js +78 -3
  26. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  27. package/lib/commonjs/ui/navigation/routes.js +3 -2
  28. package/lib/commonjs/ui/navigation/routes.js.map +1 -1
  29. package/lib/commonjs/ui/screens/AccountCenterScreen.js +33 -11
  30. package/lib/commonjs/ui/screens/AccountCenterScreen.js.map +1 -1
  31. package/lib/commonjs/ui/screens/AccountOverviewScreen.js +70 -79
  32. package/lib/commonjs/ui/screens/AccountOverviewScreen.js.map +1 -1
  33. package/lib/commonjs/ui/screens/AccountSettingsScreen.js +22 -10
  34. package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
  35. package/lib/commonjs/ui/screens/AccountSwitcherScreen.js +376 -82
  36. package/lib/commonjs/ui/screens/AccountSwitcherScreen.js.map +1 -1
  37. package/lib/commonjs/ui/screens/AccountVerificationScreen.js +21 -9
  38. package/lib/commonjs/ui/screens/AccountVerificationScreen.js.map +1 -1
  39. package/lib/commonjs/ui/screens/CreateManagedAccountScreen.js +346 -0
  40. package/lib/commonjs/ui/screens/CreateManagedAccountScreen.js.map +1 -0
  41. package/lib/commonjs/ui/screens/FileManagementScreen.js +83 -50
  42. package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -1
  43. package/lib/commonjs/ui/screens/HistoryViewScreen.js +80 -101
  44. package/lib/commonjs/ui/screens/HistoryViewScreen.js.map +1 -1
  45. package/lib/commonjs/ui/screens/PremiumSubscriptionScreen.js +66 -93
  46. package/lib/commonjs/ui/screens/PremiumSubscriptionScreen.js.map +1 -1
  47. package/lib/commonjs/ui/screens/SessionManagementScreen.js +101 -66
  48. package/lib/commonjs/ui/screens/SessionManagementScreen.js.map +1 -1
  49. package/lib/commonjs/ui/utils/fileManagement.js +0 -21
  50. package/lib/commonjs/ui/utils/fileManagement.js.map +1 -1
  51. package/lib/module/index.js +3 -0
  52. package/lib/module/index.js.map +1 -1
  53. package/lib/module/ui/components/ActingAsBanner.js +140 -0
  54. package/lib/module/ui/components/ActingAsBanner.js.map +1 -0
  55. package/lib/module/ui/components/OxyPayButton.js +4 -2
  56. package/lib/module/ui/components/OxyPayButton.js.map +1 -1
  57. package/lib/module/ui/components/OxySignInButton.js +84 -83
  58. package/lib/module/ui/components/OxySignInButton.js.map +1 -1
  59. package/lib/module/ui/components/SettingRow.js +11 -5
  60. package/lib/module/ui/components/SettingRow.js.map +1 -1
  61. package/lib/module/ui/components/fileManagement/FileDetailsModal.js +76 -122
  62. package/lib/module/ui/components/fileManagement/FileDetailsModal.js.map +1 -1
  63. package/lib/module/ui/components/fileManagement/UploadPreview.js +32 -19
  64. package/lib/module/ui/components/fileManagement/UploadPreview.js.map +1 -1
  65. package/lib/module/ui/components/icon/OxyIcon.js +5 -4
  66. package/lib/module/ui/components/icon/OxyIcon.js.map +1 -1
  67. package/lib/module/ui/components/internal/GroupedPillButtons.js +11 -9
  68. package/lib/module/ui/components/internal/GroupedPillButtons.js.map +1 -1
  69. package/lib/module/ui/components/internal/PinInput.js +3 -2
  70. package/lib/module/ui/components/internal/PinInput.js.map +1 -1
  71. package/lib/module/ui/components/modals/DeleteAccountModal.js +83 -220
  72. package/lib/module/ui/components/modals/DeleteAccountModal.js.map +1 -1
  73. package/lib/module/ui/constants/theme.js +2 -2
  74. package/lib/module/ui/constants/theme.js.map +1 -1
  75. package/lib/module/ui/context/OxyContext.js +78 -3
  76. package/lib/module/ui/context/OxyContext.js.map +1 -1
  77. package/lib/module/ui/navigation/routes.js +3 -2
  78. package/lib/module/ui/navigation/routes.js.map +1 -1
  79. package/lib/module/ui/screens/AccountCenterScreen.js +32 -11
  80. package/lib/module/ui/screens/AccountCenterScreen.js.map +1 -1
  81. package/lib/module/ui/screens/AccountOverviewScreen.js +71 -80
  82. package/lib/module/ui/screens/AccountOverviewScreen.js.map +1 -1
  83. package/lib/module/ui/screens/AccountSettingsScreen.js +22 -10
  84. package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
  85. package/lib/module/ui/screens/AccountSwitcherScreen.js +375 -82
  86. package/lib/module/ui/screens/AccountSwitcherScreen.js.map +1 -1
  87. package/lib/module/ui/screens/AccountVerificationScreen.js +22 -10
  88. package/lib/module/ui/screens/AccountVerificationScreen.js.map +1 -1
  89. package/lib/module/ui/screens/CreateManagedAccountScreen.js +342 -0
  90. package/lib/module/ui/screens/CreateManagedAccountScreen.js.map +1 -0
  91. package/lib/module/ui/screens/FileManagementScreen.js +85 -52
  92. package/lib/module/ui/screens/FileManagementScreen.js.map +1 -1
  93. package/lib/module/ui/screens/HistoryViewScreen.js +80 -101
  94. package/lib/module/ui/screens/HistoryViewScreen.js.map +1 -1
  95. package/lib/module/ui/screens/PremiumSubscriptionScreen.js +66 -94
  96. package/lib/module/ui/screens/PremiumSubscriptionScreen.js.map +1 -1
  97. package/lib/module/ui/screens/SessionManagementScreen.js +101 -67
  98. package/lib/module/ui/screens/SessionManagementScreen.js.map +1 -1
  99. package/lib/module/ui/utils/fileManagement.js +0 -20
  100. package/lib/module/ui/utils/fileManagement.js.map +1 -1
  101. package/lib/typescript/commonjs/index.d.ts +1 -0
  102. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  103. package/lib/typescript/commonjs/ui/components/ActingAsBanner.d.ts +4 -0
  104. package/lib/typescript/commonjs/ui/components/ActingAsBanner.d.ts.map +1 -0
  105. package/lib/typescript/commonjs/ui/components/OxyPayButton.d.ts.map +1 -1
  106. package/lib/typescript/commonjs/ui/components/OxySignInButton.d.ts +0 -1
  107. package/lib/typescript/commonjs/ui/components/OxySignInButton.d.ts.map +1 -1
  108. package/lib/typescript/commonjs/ui/components/SettingRow.d.ts +2 -2
  109. package/lib/typescript/commonjs/ui/components/SettingRow.d.ts.map +1 -1
  110. package/lib/typescript/commonjs/ui/components/fileManagement/FileDetailsModal.d.ts +3 -4
  111. package/lib/typescript/commonjs/ui/components/fileManagement/FileDetailsModal.d.ts.map +1 -1
  112. package/lib/typescript/commonjs/ui/components/fileManagement/UploadPreview.d.ts +2 -3
  113. package/lib/typescript/commonjs/ui/components/fileManagement/UploadPreview.d.ts.map +1 -1
  114. package/lib/typescript/commonjs/ui/components/icon/OxyIcon.d.ts.map +1 -1
  115. package/lib/typescript/commonjs/ui/components/internal/GroupedPillButtons.d.ts.map +1 -1
  116. package/lib/typescript/commonjs/ui/components/internal/PinInput.d.ts.map +1 -1
  117. package/lib/typescript/commonjs/ui/components/modals/DeleteAccountModal.d.ts +2 -2
  118. package/lib/typescript/commonjs/ui/components/modals/DeleteAccountModal.d.ts.map +1 -1
  119. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts +6 -0
  120. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
  121. package/lib/typescript/commonjs/ui/navigation/routes.d.ts +1 -1
  122. package/lib/typescript/commonjs/ui/navigation/routes.d.ts.map +1 -1
  123. package/lib/typescript/commonjs/ui/screens/AccountCenterScreen.d.ts.map +1 -1
  124. package/lib/typescript/commonjs/ui/screens/AccountOverviewScreen.d.ts.map +1 -1
  125. package/lib/typescript/commonjs/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
  126. package/lib/typescript/commonjs/ui/screens/AccountSwitcherScreen.d.ts.map +1 -1
  127. package/lib/typescript/commonjs/ui/screens/AccountVerificationScreen.d.ts.map +1 -1
  128. package/lib/typescript/commonjs/ui/screens/CreateManagedAccountScreen.d.ts +5 -0
  129. package/lib/typescript/commonjs/ui/screens/CreateManagedAccountScreen.d.ts.map +1 -0
  130. package/lib/typescript/commonjs/ui/screens/FileManagementScreen.d.ts.map +1 -1
  131. package/lib/typescript/commonjs/ui/screens/HistoryViewScreen.d.ts.map +1 -1
  132. package/lib/typescript/commonjs/ui/screens/PremiumSubscriptionScreen.d.ts.map +1 -1
  133. package/lib/typescript/commonjs/ui/screens/SessionManagementScreen.d.ts.map +1 -1
  134. package/lib/typescript/commonjs/ui/utils/fileManagement.d.ts +0 -4
  135. package/lib/typescript/commonjs/ui/utils/fileManagement.d.ts.map +1 -1
  136. package/lib/typescript/module/index.d.ts +1 -0
  137. package/lib/typescript/module/index.d.ts.map +1 -1
  138. package/lib/typescript/module/ui/components/ActingAsBanner.d.ts +4 -0
  139. package/lib/typescript/module/ui/components/ActingAsBanner.d.ts.map +1 -0
  140. package/lib/typescript/module/ui/components/OxyPayButton.d.ts.map +1 -1
  141. package/lib/typescript/module/ui/components/OxySignInButton.d.ts +0 -1
  142. package/lib/typescript/module/ui/components/OxySignInButton.d.ts.map +1 -1
  143. package/lib/typescript/module/ui/components/SettingRow.d.ts +2 -2
  144. package/lib/typescript/module/ui/components/SettingRow.d.ts.map +1 -1
  145. package/lib/typescript/module/ui/components/fileManagement/FileDetailsModal.d.ts +3 -4
  146. package/lib/typescript/module/ui/components/fileManagement/FileDetailsModal.d.ts.map +1 -1
  147. package/lib/typescript/module/ui/components/fileManagement/UploadPreview.d.ts +2 -3
  148. package/lib/typescript/module/ui/components/fileManagement/UploadPreview.d.ts.map +1 -1
  149. package/lib/typescript/module/ui/components/icon/OxyIcon.d.ts.map +1 -1
  150. package/lib/typescript/module/ui/components/internal/GroupedPillButtons.d.ts.map +1 -1
  151. package/lib/typescript/module/ui/components/internal/PinInput.d.ts.map +1 -1
  152. package/lib/typescript/module/ui/components/modals/DeleteAccountModal.d.ts +2 -2
  153. package/lib/typescript/module/ui/components/modals/DeleteAccountModal.d.ts.map +1 -1
  154. package/lib/typescript/module/ui/context/OxyContext.d.ts +6 -0
  155. package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
  156. package/lib/typescript/module/ui/navigation/routes.d.ts +1 -1
  157. package/lib/typescript/module/ui/navigation/routes.d.ts.map +1 -1
  158. package/lib/typescript/module/ui/screens/AccountCenterScreen.d.ts.map +1 -1
  159. package/lib/typescript/module/ui/screens/AccountOverviewScreen.d.ts.map +1 -1
  160. package/lib/typescript/module/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
  161. package/lib/typescript/module/ui/screens/AccountSwitcherScreen.d.ts.map +1 -1
  162. package/lib/typescript/module/ui/screens/AccountVerificationScreen.d.ts.map +1 -1
  163. package/lib/typescript/module/ui/screens/CreateManagedAccountScreen.d.ts +5 -0
  164. package/lib/typescript/module/ui/screens/CreateManagedAccountScreen.d.ts.map +1 -0
  165. package/lib/typescript/module/ui/screens/FileManagementScreen.d.ts.map +1 -1
  166. package/lib/typescript/module/ui/screens/HistoryViewScreen.d.ts.map +1 -1
  167. package/lib/typescript/module/ui/screens/PremiumSubscriptionScreen.d.ts.map +1 -1
  168. package/lib/typescript/module/ui/screens/SessionManagementScreen.d.ts.map +1 -1
  169. package/lib/typescript/module/ui/utils/fileManagement.d.ts +0 -4
  170. package/lib/typescript/module/ui/utils/fileManagement.d.ts.map +1 -1
  171. package/package.json +2 -2
  172. package/src/index.ts +3 -0
  173. package/src/ui/components/ActingAsBanner.tsx +135 -0
  174. package/src/ui/components/OxyPayButton.tsx +5 -3
  175. package/src/ui/components/OxySignInButton.tsx +82 -81
  176. package/src/ui/components/SettingRow.tsx +14 -7
  177. package/src/ui/components/fileManagement/FileDetailsModal.tsx +69 -99
  178. package/src/ui/components/fileManagement/UploadPreview.tsx +58 -46
  179. package/src/ui/components/icon/OxyIcon.tsx +5 -4
  180. package/src/ui/components/internal/GroupedPillButtons.tsx +15 -12
  181. package/src/ui/components/internal/PinInput.tsx +4 -3
  182. package/src/ui/components/modals/DeleteAccountModal.tsx +79 -221
  183. package/src/ui/constants/theme.ts +2 -2
  184. package/src/ui/context/OxyContext.tsx +85 -0
  185. package/src/ui/navigation/routes.ts +3 -1
  186. package/src/ui/screens/AccountCenterScreen.tsx +40 -122
  187. package/src/ui/screens/AccountOverviewScreen.tsx +63 -98
  188. package/src/ui/screens/AccountSettingsScreen.tsx +21 -7
  189. package/src/ui/screens/AccountSwitcherScreen.tsx +344 -87
  190. package/src/ui/screens/AccountVerificationScreen.tsx +24 -16
  191. package/src/ui/screens/CreateManagedAccountScreen.tsx +338 -0
  192. package/src/ui/screens/FileManagementScreen.tsx +62 -54
  193. package/src/ui/screens/HistoryViewScreen.tsx +57 -204
  194. package/src/ui/screens/PremiumSubscriptionScreen.tsx +73 -93
  195. package/src/ui/screens/SessionManagementScreen.tsx +101 -73
  196. package/src/ui/utils/fileManagement.ts +0 -30
  197. package/lib/commonjs/ui/utils/confirmAction.js +0 -28
  198. package/lib/commonjs/ui/utils/confirmAction.js.map +0 -1
  199. package/lib/module/ui/utils/confirmAction.js +0 -25
  200. package/lib/module/ui/utils/confirmAction.js.map +0 -1
  201. package/lib/typescript/commonjs/ui/utils/confirmAction.d.ts +0 -7
  202. package/lib/typescript/commonjs/ui/utils/confirmAction.d.ts.map +0 -1
  203. package/lib/typescript/module/ui/utils/confirmAction.d.ts +0 -7
  204. package/lib/typescript/module/ui/utils/confirmAction.d.ts.map +0 -1
  205. package/src/ui/utils/confirmAction.ts +0 -23
@@ -0,0 +1,338 @@
1
+ import type React from 'react';
2
+ import { useState, useCallback, useRef, useEffect } from 'react';
3
+ import {
4
+ View,
5
+ Text,
6
+ TextInput,
7
+ TouchableOpacity,
8
+ StyleSheet,
9
+ ActivityIndicator,
10
+ ScrollView,
11
+ Platform,
12
+ KeyboardAvoidingView,
13
+ } from 'react-native';
14
+ import type { BaseScreenProps } from '../types/navigation';
15
+ import { fontFamilies } from '../styles/fonts';
16
+ import { Header } from '../components';
17
+ import { useI18n } from '../hooks/useI18n';
18
+ import { useTheme } from '@oxyhq/bloom/theme';
19
+ import { useOxy } from '../context/OxyContext';
20
+ import { toast } from '../../lib/sonner';
21
+ import { screenContentStyle } from '../constants/spacing';
22
+
23
+ type UsernameStatus = 'idle' | 'checking' | 'available' | 'taken' | 'invalid';
24
+
25
+ const USERNAME_REGEX = /^[a-zA-Z0-9_-]{3,30}$/;
26
+ const DEBOUNCE_MS = 400;
27
+
28
+ const CreateManagedAccountScreen: React.FC<BaseScreenProps> = ({
29
+ onClose,
30
+ goBack,
31
+ }) => {
32
+ const bloomTheme = useTheme();
33
+ const { oxyServices, createManagedAccount, setActingAs } = useOxy();
34
+ const { t } = useI18n();
35
+
36
+ const [username, setUsername] = useState('');
37
+ const [displayName, setDisplayName] = useState('');
38
+ const [bio, setBio] = useState('');
39
+ const [usernameStatus, setUsernameStatus] = useState<UsernameStatus>('idle');
40
+ const [usernameMessage, setUsernameMessage] = useState('');
41
+ const [isCreating, setIsCreating] = useState(false);
42
+
43
+ const debounceTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
44
+
45
+ // Debounced username availability check
46
+ const checkUsername = useCallback((value: string) => {
47
+ if (debounceTimerRef.current) {
48
+ clearTimeout(debounceTimerRef.current);
49
+ }
50
+
51
+ if (!value || value.length < 3) {
52
+ setUsernameStatus(value.length > 0 ? 'invalid' : 'idle');
53
+ setUsernameMessage(value.length > 0 ? 'Username must be at least 3 characters' : '');
54
+ return;
55
+ }
56
+
57
+ if (!USERNAME_REGEX.test(value)) {
58
+ setUsernameStatus('invalid');
59
+ setUsernameMessage('Only letters, numbers, hyphens, and underscores');
60
+ return;
61
+ }
62
+
63
+ setUsernameStatus('checking');
64
+ setUsernameMessage('');
65
+
66
+ debounceTimerRef.current = setTimeout(async () => {
67
+ try {
68
+ const result = await oxyServices.checkUsernameAvailability(value);
69
+ setUsernameStatus(result.available ? 'available' : 'taken');
70
+ setUsernameMessage(result.message || (result.available ? 'Username is available' : 'Username is taken'));
71
+ } catch {
72
+ setUsernameStatus('idle');
73
+ setUsernameMessage('Could not check availability');
74
+ }
75
+ }, DEBOUNCE_MS);
76
+ }, [oxyServices]);
77
+
78
+ const handleUsernameChange = useCallback((value: string) => {
79
+ const cleaned = value.toLowerCase().replace(/[^a-z0-9_-]/g, '');
80
+ setUsername(cleaned);
81
+ checkUsername(cleaned);
82
+ }, [checkUsername]);
83
+
84
+ // Cleanup debounce timer
85
+ useEffect(() => {
86
+ return () => {
87
+ if (debounceTimerRef.current) {
88
+ clearTimeout(debounceTimerRef.current);
89
+ }
90
+ };
91
+ }, []);
92
+
93
+ const canCreate = usernameStatus === 'available' && displayName.trim().length > 0 && !isCreating;
94
+
95
+ const handleCreate = useCallback(async () => {
96
+ if (!canCreate) return;
97
+
98
+ setIsCreating(true);
99
+ try {
100
+ // Split display name into first/last
101
+ const nameParts = displayName.trim().split(/\s+/);
102
+ const firstName = nameParts[0] || '';
103
+ const lastName = nameParts.length > 1 ? nameParts.slice(1).join(' ') : undefined;
104
+
105
+ const account = await createManagedAccount({
106
+ username,
107
+ name: { first: firstName, last: lastName },
108
+ bio: bio.trim() || undefined,
109
+ });
110
+
111
+ toast.success('Identity created successfully');
112
+
113
+ // Switch to the new managed account
114
+ if (account.accountId) {
115
+ setActingAs(account.accountId);
116
+ }
117
+
118
+ onClose?.();
119
+ } catch (error) {
120
+ const message = error instanceof Error ? error.message : 'Failed to create identity';
121
+ toast.error(message);
122
+ } finally {
123
+ setIsCreating(false);
124
+ }
125
+ }, [canCreate, username, displayName, bio, createManagedAccount, setActingAs, onClose]);
126
+
127
+ const getStatusColor = (): string => {
128
+ switch (usernameStatus) {
129
+ case 'available': return '#34C759';
130
+ case 'taken':
131
+ case 'invalid': return bloomTheme.colors.error;
132
+ case 'checking': return bloomTheme.colors.primary;
133
+ default: return 'transparent';
134
+ }
135
+ };
136
+
137
+ return (
138
+ <View style={[styles.container, { backgroundColor: bloomTheme.colors.background }]}>
139
+ <Header
140
+ title="Create Identity"
141
+ onBack={goBack}
142
+ onClose={onClose}
143
+ showBackButton={true}
144
+ showCloseButton={true}
145
+ elevation="subtle"
146
+ />
147
+
148
+ <KeyboardAvoidingView
149
+ style={styles.flex}
150
+ behavior={Platform.OS === 'ios' ? 'padding' : undefined}
151
+ keyboardVerticalOffset={Platform.OS === 'ios' ? 88 : 0}
152
+ >
153
+ <ScrollView
154
+ style={styles.flex}
155
+ contentContainerStyle={screenContentStyle}
156
+ keyboardShouldPersistTaps="handled"
157
+ >
158
+ <Text style={[styles.description, { color: bloomTheme.colors.text }]}>
159
+ Create a managed identity that you control. It will have its own profile, posts, and interactions.
160
+ </Text>
161
+
162
+ {/* Username */}
163
+ <View style={styles.fieldGroup}>
164
+ <Text style={[styles.label, { color: bloomTheme.colors.text }]}>Username</Text>
165
+ <View style={[
166
+ styles.inputContainer,
167
+ { borderColor: usernameStatus !== 'idle' ? getStatusColor() : bloomTheme.colors.border },
168
+ ]}>
169
+ <Text style={[styles.inputPrefix, { color: bloomTheme.colors.text + '80' }]}>@</Text>
170
+ <TextInput
171
+ style={[styles.input, { color: bloomTheme.colors.text }]}
172
+ value={username}
173
+ onChangeText={handleUsernameChange}
174
+ placeholder="username"
175
+ placeholderTextColor={bloomTheme.colors.text + '40'}
176
+ autoCapitalize="none"
177
+ autoCorrect={false}
178
+ autoComplete="off"
179
+ maxLength={30}
180
+ />
181
+ {usernameStatus === 'checking' && (
182
+ <ActivityIndicator size="small" color={bloomTheme.colors.primary} />
183
+ )}
184
+ {usernameStatus === 'available' && (
185
+ <Text style={styles.statusIcon}>OK</Text>
186
+ )}
187
+ </View>
188
+ {usernameMessage ? (
189
+ <Text style={[styles.statusMessage, { color: getStatusColor() }]}>
190
+ {usernameMessage}
191
+ </Text>
192
+ ) : null}
193
+ </View>
194
+
195
+ {/* Display Name */}
196
+ <View style={styles.fieldGroup}>
197
+ <Text style={[styles.label, { color: bloomTheme.colors.text }]}>Display Name</Text>
198
+ <View style={[styles.inputContainer, { borderColor: bloomTheme.colors.border }]}>
199
+ <TextInput
200
+ style={[styles.input, { color: bloomTheme.colors.text }]}
201
+ value={displayName}
202
+ onChangeText={setDisplayName}
203
+ placeholder="Display name"
204
+ placeholderTextColor={bloomTheme.colors.text + '40'}
205
+ maxLength={50}
206
+ />
207
+ </View>
208
+ </View>
209
+
210
+ {/* Bio */}
211
+ <View style={styles.fieldGroup}>
212
+ <Text style={[styles.label, { color: bloomTheme.colors.text }]}>Bio (optional)</Text>
213
+ <View style={[styles.inputContainer, styles.bioContainer, { borderColor: bloomTheme.colors.border }]}>
214
+ <TextInput
215
+ style={[styles.input, styles.bioInput, { color: bloomTheme.colors.text }]}
216
+ value={bio}
217
+ onChangeText={setBio}
218
+ placeholder="Tell people about this identity"
219
+ placeholderTextColor={bloomTheme.colors.text + '40'}
220
+ maxLength={160}
221
+ multiline
222
+ numberOfLines={3}
223
+ textAlignVertical="top"
224
+ />
225
+ </View>
226
+ <Text style={[styles.charCount, { color: bloomTheme.colors.text + '60' }]}>
227
+ {bio.length}/160
228
+ </Text>
229
+ </View>
230
+
231
+ {/* Create Button */}
232
+ <TouchableOpacity
233
+ style={[
234
+ styles.createButton,
235
+ { backgroundColor: bloomTheme.colors.primary },
236
+ !canCreate && styles.createButtonDisabled,
237
+ ]}
238
+ onPress={handleCreate}
239
+ disabled={!canCreate}
240
+ activeOpacity={0.7}
241
+ >
242
+ {isCreating ? (
243
+ <ActivityIndicator size="small" color="#fff" />
244
+ ) : (
245
+ <Text style={styles.createButtonText}>Create Identity</Text>
246
+ )}
247
+ </TouchableOpacity>
248
+ </ScrollView>
249
+ </KeyboardAvoidingView>
250
+ </View>
251
+ );
252
+ };
253
+
254
+ const styles = StyleSheet.create({
255
+ container: {
256
+ flex: 1,
257
+ },
258
+ flex: {
259
+ flex: 1,
260
+ },
261
+ description: {
262
+ fontSize: 15,
263
+ lineHeight: 22,
264
+ marginBottom: 24,
265
+ fontFamily: fontFamilies.inter,
266
+ },
267
+ fieldGroup: {
268
+ marginBottom: 20,
269
+ },
270
+ label: {
271
+ fontSize: 14,
272
+ fontFamily: fontFamilies.interSemiBold,
273
+ fontWeight: Platform.OS === 'web' ? '600' : undefined,
274
+ marginBottom: 8,
275
+ },
276
+ inputContainer: {
277
+ flexDirection: 'row',
278
+ alignItems: 'center',
279
+ borderWidth: 1,
280
+ borderRadius: 12,
281
+ paddingHorizontal: 14,
282
+ height: 48,
283
+ },
284
+ inputPrefix: {
285
+ fontSize: 16,
286
+ fontFamily: fontFamilies.inter,
287
+ marginRight: 2,
288
+ },
289
+ input: {
290
+ flex: 1,
291
+ fontSize: 16,
292
+ fontFamily: fontFamilies.inter,
293
+ paddingVertical: 0,
294
+ },
295
+ bioContainer: {
296
+ height: 88,
297
+ alignItems: 'flex-start',
298
+ paddingVertical: 12,
299
+ },
300
+ bioInput: {
301
+ height: 64,
302
+ },
303
+ statusIcon: {
304
+ fontSize: 13,
305
+ fontFamily: fontFamilies.interSemiBold,
306
+ fontWeight: Platform.OS === 'web' ? '600' : undefined,
307
+ color: '#34C759',
308
+ },
309
+ statusMessage: {
310
+ fontSize: 13,
311
+ fontFamily: fontFamilies.inter,
312
+ marginTop: 6,
313
+ },
314
+ charCount: {
315
+ fontSize: 12,
316
+ fontFamily: fontFamilies.inter,
317
+ textAlign: 'right',
318
+ marginTop: 4,
319
+ },
320
+ createButton: {
321
+ height: 50,
322
+ borderRadius: 25,
323
+ alignItems: 'center',
324
+ justifyContent: 'center',
325
+ marginTop: 12,
326
+ },
327
+ createButtonDisabled: {
328
+ opacity: 0.5,
329
+ },
330
+ createButtonText: {
331
+ color: '#fff',
332
+ fontSize: 16,
333
+ fontFamily: fontFamilies.interSemiBold,
334
+ fontWeight: Platform.OS === 'web' ? '600' : undefined,
335
+ },
336
+ });
337
+
338
+ export default CreateManagedAccountScreen;
@@ -11,7 +11,6 @@ import {
11
11
  Image,
12
12
  Animated,
13
13
  Easing,
14
- Alert,
15
14
  } from 'react-native';
16
15
  import { Image as ExpoImage } from 'expo-image';
17
16
  import type { FileManagementScreenProps } from '../types/fileManagement';
@@ -29,6 +28,8 @@ const loadDocumentPicker = async () => {
29
28
  }
30
29
  };
31
30
  import { toast } from '../../lib/sonner';
31
+ import * as Prompt from '@oxyhq/bloom/prompt';
32
+ import { usePromptControl } from '@oxyhq/bloom/prompt';
32
33
  import { Ionicons } from '@expo/vector-icons';
33
34
  // @ts-ignore - MaterialCommunityIcons is available at runtime
34
35
  import { MaterialCommunityIcons } from '@expo/vector-icons';
@@ -42,7 +43,6 @@ import { useOxy } from '../context/OxyContext';
42
43
  import { useI18n } from '../hooks/useI18n';
43
44
  import { useUploadFile } from '../hooks/mutations/useAccountMutations';
44
45
  import {
45
- confirmAction,
46
46
  convertDocumentPickerAssetToFile,
47
47
  formatFileSize,
48
48
  getFileIcon,
@@ -51,6 +51,7 @@ import {
51
51
  import { FileViewer } from '../components/fileManagement/FileViewer';
52
52
  import { FileDetailsModal } from '../components/fileManagement/FileDetailsModal';
53
53
  import { UploadPreview } from '../components/fileManagement/UploadPreview';
54
+ import { useDialogControl } from '@oxyhq/bloom/dialog';
54
55
  import { fileManagementStyles } from '../components/fileManagement/styles';
55
56
  import type { OnConfirmFileSelection } from '../types/fileManagement';
56
57
 
@@ -133,6 +134,11 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
133
134
  const { user, oxyServices } = useOxy();
134
135
  const { t } = useI18n();
135
136
  const uploadFileMutation = useUploadFile();
137
+ // Prompt controls
138
+ const fileDeletePrompt = usePromptControl();
139
+ const bulkDeletePrompt = usePromptControl();
140
+ const visibilityChangePrompt = usePromptControl();
141
+ const [pendingDeleteFile, setPendingDeleteFile] = useState<{ id: string; name: string } | null>(null);
136
142
  const files = useFiles();
137
143
  // Ensure containerWidth is a number (TypeScript guard)
138
144
  const safeContainerWidth: number = typeof containerWidth === 'number' ? containerWidth : 400;
@@ -143,7 +149,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
143
149
  const [refreshing, setRefreshing] = useState(false);
144
150
  const [paging, setPaging] = useState({ offset: 0, limit: 40, total: 0, hasMore: true, loadingMore: false });
145
151
  const [selectedFile, setSelectedFile] = useState<FileMetadata | null>(null);
146
- const [showFileDetails, setShowFileDetails] = useState(false);
152
+ const fileDetailsControl = useDialogControl();
147
153
  // In selectMode we never open the detailed viewer
148
154
  const [openedFile, setOpenedFile] = useState<FileMetadata | null>(null);
149
155
  const [fileContent, setFileContent] = useState<string | null>(null);
@@ -889,18 +895,14 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
889
895
  }
890
896
  };
891
897
 
892
- const handleFileDelete = async (fileId: string, filename: string) => {
893
- // Use platform-aware confirmation dialog
894
- const confirmed = await confirmAction(
895
- t('fileManagement.confirms.deleteFile', { filename }),
896
- t('fileManagement.deleteFile'),
897
- t('fileManagement.confirm'),
898
- t('common.cancel')
899
- );
898
+ const confirmFileDelete = useCallback((fileId: string, filename: string) => {
899
+ setPendingDeleteFile({ id: fileId, name: filename });
900
+ fileDeletePrompt.open();
901
+ }, [fileDeletePrompt]);
900
902
 
901
- if (!confirmed) {
902
- return;
903
- }
903
+ const handleFileDelete = useCallback(async () => {
904
+ if (!pendingDeleteFile) return;
905
+ const { id: fileId } = pendingDeleteFile;
904
906
 
905
907
  try {
906
908
  storeSetDeleting(fileId);
@@ -927,24 +929,17 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
927
929
  }
928
930
  } finally {
929
931
  storeSetDeleting(null);
932
+ setPendingDeleteFile(null);
930
933
  }
931
- };
934
+ }, [pendingDeleteFile, storeSetDeleting, oxyServices, loadFiles, t]);
932
935
 
933
- const handleBulkDelete = useCallback(async () => {
936
+ const confirmBulkDelete = useCallback(() => {
934
937
  if (selectedIds.size === 0) return;
938
+ bulkDeletePrompt.open();
939
+ }, [selectedIds.size, bulkDeletePrompt]);
935
940
 
936
- const fileMap: Record<string, FileMetadata> = {};
937
- files.forEach(f => { fileMap[f.id] = f; });
938
- const selectedFiles = Array.from(selectedIds).map(id => fileMap[id]).filter(Boolean);
939
-
940
- const confirmed = await confirmAction(
941
- t('fileManagement.confirms.deleteFiles', { count: selectedFiles.length }),
942
- t('fileManagement.deleteFiles'),
943
- t('fileManagement.confirm'),
944
- t('common.cancel')
945
- );
946
-
947
- if (!confirmed) return;
941
+ const handleBulkDelete = useCallback(async () => {
942
+ if (selectedIds.size === 0) return;
948
943
 
949
944
  try {
950
945
  const deletePromises = Array.from(selectedIds).map(async (fileId) => {
@@ -1119,7 +1114,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1119
1114
 
1120
1115
  const showFileDetailsModal = (file: FileMetadata) => {
1121
1116
  setSelectedFile(file);
1122
- setShowFileDetails(true);
1117
+ fileDetailsControl.open();
1123
1118
  };
1124
1119
 
1125
1120
  const renderSimplePhotoItem = useCallback((photo: FileMetadata, index: number) => {
@@ -1350,9 +1345,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1350
1345
  {/* Always show delete button for debugging */}
1351
1346
  <TouchableOpacity
1352
1347
  style={[fileManagementStyles.actionButton, { backgroundColor: bloomTheme.isDark ? '#400000' : '#FFEBEE' }]}
1353
- onPress={() => {
1354
- handleFileDelete(file.id, file.filename);
1355
- }}
1348
+ onPress={() => confirmFileDelete(file.id, file.filename)}
1356
1349
  disabled={deleting === file.id}
1357
1350
  >
1358
1351
  {deleting === file.id ? (
@@ -1480,7 +1473,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1480
1473
  </TouchableOpacity>
1481
1474
  <TouchableOpacity
1482
1475
  style={[fileManagementStyles.groupedActionBtn, { backgroundColor: bloomTheme.isDark ? '#400000' : '#FFEBEE' }]}
1483
- onPress={() => handleFileDelete(file.id, file.filename)}
1476
+ onPress={() => confirmFileDelete(file.id, file.filename)}
1484
1477
  disabled={deleting === file.id}
1485
1478
  >
1486
1479
  {deleting === file.id ? (
@@ -1498,7 +1491,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1498
1491
  ) : undefined,
1499
1492
  };
1500
1493
  });
1501
- }, [filteredFiles, theme, deleting, handleFileDownload, handleFileDelete, handleFileOpen, getSafeDownloadUrlCallback, selectMode, selectedIds]);
1494
+ }, [filteredFiles, theme, deleting, handleFileDownload, confirmFileDelete, handleFileOpen, getSafeDownloadUrlCallback, selectMode, selectedIds]);
1502
1495
 
1503
1496
  // Scroll to selected file after selection
1504
1497
  useEffect(() => {
@@ -1952,15 +1945,14 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1952
1945
  onToggleDetails={() => setShowFileDetailsInViewer(!showFileDetailsInViewer)}
1953
1946
  onClose={handleCloseFile}
1954
1947
  onDownload={handleFileDownload}
1955
- onDelete={handleFileDelete}
1948
+ onDelete={confirmFileDelete}
1956
1949
  isOwner={user?.id === targetUserId}
1957
1950
  />
1958
1951
  <FileDetailsModal
1959
- visible={showFileDetails}
1952
+ control={fileDetailsControl}
1960
1953
  file={selectedFile}
1961
- onClose={() => setShowFileDetails(false)}
1962
1954
  onDownload={handleFileDownload}
1963
- onDelete={handleFileDelete}
1955
+ onDelete={confirmFileDelete}
1964
1956
  isOwner={user?.id === targetUserId}
1965
1957
  />
1966
1958
  </>
@@ -1981,7 +1973,6 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1981
1973
  titleAlignment="left"
1982
1974
  />
1983
1975
  <UploadPreview
1984
- visible={true}
1985
1976
  pendingFiles={pendingFiles}
1986
1977
  onConfirm={handleConfirmUpload}
1987
1978
  onCancel={handleCancelUpload}
@@ -2019,24 +2010,14 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
2019
2010
  {
2020
2011
  key: 'delete',
2021
2012
  text: t('fileManagement.delete', { count: selectedIds.size }),
2022
- onPress: handleBulkDelete,
2013
+ onPress: confirmBulkDelete,
2023
2014
  icon: 'delete',
2024
2015
  },
2025
2016
  {
2026
2017
  key: 'visibility',
2027
2018
  text: t('fileManagement.visibility'),
2028
2019
  onPress: () => {
2029
- // Show visibility options menu
2030
- Alert.alert(
2031
- t('fileManagement.changeVisibility'),
2032
- t('fileManagement.changeVisibilityConfirm', { count: selectedIds.size }),
2033
- [
2034
- { text: t('common.cancel'), style: 'cancel' },
2035
- { text: t('fileManagement.private'), onPress: () => handleBulkVisibilityChange('private') },
2036
- { text: t('fileManagement.public'), onPress: () => handleBulkVisibilityChange('public') },
2037
- { text: t('fileManagement.unlisted'), onPress: () => handleBulkVisibilityChange('unlisted') },
2038
- ]
2039
- );
2020
+ visibilityChangePrompt.open();
2040
2021
  },
2041
2022
  icon: 'eye',
2042
2023
  }
@@ -2273,11 +2254,10 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
2273
2254
 
2274
2255
  {!selectMode && (
2275
2256
  <FileDetailsModal
2276
- visible={showFileDetails}
2257
+ control={fileDetailsControl}
2277
2258
  file={selectedFile}
2278
- onClose={() => setShowFileDetails(false)}
2279
2259
  onDownload={handleFileDownload}
2280
- onDelete={handleFileDelete}
2260
+ onDelete={confirmFileDelete}
2281
2261
  isOwner={user?.id === targetUserId}
2282
2262
  />
2283
2263
  )}
@@ -2312,6 +2292,34 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
2312
2292
 
2313
2293
  {/* Selection bar removed; actions are now in header */}
2314
2294
  {/* Global loadingMore bar removed; now inline in scroll areas */}
2295
+ <Prompt.Basic
2296
+ control={fileDeletePrompt}
2297
+ title={t('fileManagement.deleteFile') || 'Delete File'}
2298
+ description={pendingDeleteFile ? t('fileManagement.confirms.deleteFile', { filename: pendingDeleteFile.name }) : ''}
2299
+ onConfirm={handleFileDelete}
2300
+ confirmButtonCta={t('fileManagement.confirm') || 'Delete'}
2301
+ confirmButtonColor='negative'
2302
+ />
2303
+ <Prompt.Basic
2304
+ control={bulkDeletePrompt}
2305
+ title={t('fileManagement.deleteFiles') || 'Delete Files'}
2306
+ description={t('fileManagement.confirms.deleteFiles', { count: selectedIds.size })}
2307
+ onConfirm={handleBulkDelete}
2308
+ confirmButtonCta={t('fileManagement.confirm') || 'Delete'}
2309
+ confirmButtonColor='negative'
2310
+ />
2311
+ <Prompt.Outer control={visibilityChangePrompt}>
2312
+ <Prompt.Content>
2313
+ <Prompt.TitleText>{t('fileManagement.changeVisibility') || 'Change Visibility'}</Prompt.TitleText>
2314
+ <Prompt.DescriptionText>{t('fileManagement.changeVisibilityConfirm', { count: selectedIds.size })}</Prompt.DescriptionText>
2315
+ </Prompt.Content>
2316
+ <Prompt.Actions>
2317
+ <Prompt.Action cta={t('fileManagement.private') || 'Private'} onPress={() => handleBulkVisibilityChange('private')} color='primary' />
2318
+ <Prompt.Action cta={t('fileManagement.public') || 'Public'} onPress={() => handleBulkVisibilityChange('public')} color='primary_subtle' />
2319
+ <Prompt.Action cta={t('fileManagement.unlisted') || 'Unlisted'} onPress={() => handleBulkVisibilityChange('unlisted')} color='primary_subtle' />
2320
+ <Prompt.Cancel cta={t('common.cancel') || 'Cancel'} />
2321
+ </Prompt.Actions>
2322
+ </Prompt.Outer>
2315
2323
  </View>
2316
2324
  );
2317
2325
  };