@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
@@ -1,5 +1,5 @@
1
1
  import type React from 'react';
2
- import { useState, useEffect } from 'react';
2
+ import { useState, useEffect, useCallback, useMemo } from 'react';
3
3
  import {
4
4
  View,
5
5
  Text,
@@ -7,7 +7,6 @@ import {
7
7
  StyleSheet,
8
8
  ScrollView,
9
9
  ActivityIndicator,
10
- Alert,
11
10
  Platform,
12
11
  Animated,
13
12
  Dimensions,
@@ -15,7 +14,8 @@ import {
15
14
  import type { BaseScreenProps } from '../types/navigation';
16
15
  import { fontFamilies } from '../styles/fonts';
17
16
  import { toast } from '../../lib/sonner';
18
- import { confirmAction } from '../utils/confirmAction';
17
+ import * as Prompt from '@oxyhq/bloom/prompt';
18
+ import { usePromptControl } from '@oxyhq/bloom/prompt';
19
19
  import { Ionicons } from '@expo/vector-icons';
20
20
  import Avatar from '../components/Avatar';
21
21
  import { useI18n } from '../hooks/useI18n';
@@ -80,9 +80,15 @@ const PremiumSubscriptionScreen: React.FC<BaseScreenProps> = ({
80
80
  const [billingInterval, setBillingInterval] = useState<'month' | 'year'>('month');
81
81
  const [activeTab, setActiveTab] = useState<'plans' | 'features'>('plans');
82
82
  const [currentAppPackage, setCurrentAppPackage] = useState<string>('mention'); // Default to mention for demo
83
+ const [pendingUnsubscribeFeatureId, setPendingUnsubscribeFeatureId] = useState<string | null>(null);
83
84
 
84
85
  const { t } = useI18n();
85
86
  const bloomTheme = useTheme();
87
+
88
+ // Prompt controls
89
+ const cancelSubscriptionPrompt = usePromptControl();
90
+ const unsubscribeFeaturePrompt = usePromptControl();
91
+
86
92
  // Extract commonly used colors for readability
87
93
  const textColor = bloomTheme.colors.text;
88
94
  const backgroundColor = bloomTheme.colors.background;
@@ -315,35 +321,7 @@ const PremiumSubscriptionScreen: React.FC<BaseScreenProps> = ({
315
321
  }, [currentAppPackage, user?.isPremium]);
316
322
 
317
323
  const detectCurrentApp = () => {
318
- // In a real implementation, this would detect the actual app package name
319
- // For now, we'll use a mock detection based on available methods
320
-
321
- // Real app detection methods you could use:
322
- // 1. Check bundle identifier in React Native:
323
- // import DeviceInfo from 'react-native-device-info';
324
- // const bundleId = DeviceInfo.getBundleId();
325
- // Example: com.oxy.mention -> 'mention'
326
-
327
- // 2. Environment variables or build configuration
328
- // const appPackage = __DEV__ ? process.env.APP_PACKAGE : 'mention';
329
-
330
- // 3. Check specific app capabilities or modules
331
- // if (typeof MentionModule !== 'undefined') return 'mention';
332
- // if (typeof OxyWorkspaceModule !== 'undefined') return 'oxy-workspace';
333
-
334
- // 4. Use build-time configuration with Metro or similar
335
- // const appPackage = require('../config/app.json').packageName;
336
-
337
- // For demo purposes, we'll simulate different apps
338
- // You would replace this with actual app detection logic
339
-
340
- // IMPORTANT: This ensures subscription restrictions work properly:
341
- // - Mention+ plan can only be subscribed to when app package == 'mention'
342
- // - Other app-specific plans follow the same pattern
343
- // - Ecosystem plans work across all apps
344
-
345
- const detectedApp = 'mention'; // This would be dynamic in real implementation
346
-
324
+ const detectedApp = 'mention';
347
325
  setCurrentAppPackage(detectedApp);
348
326
  };
349
327
 
@@ -351,13 +329,11 @@ const PremiumSubscriptionScreen: React.FC<BaseScreenProps> = ({
351
329
  try {
352
330
  setLoading(true);
353
331
 
354
- // Filter plans available for current app
355
332
  const availablePlans = mockPlans.filter(plan =>
356
333
  plan.applicableApps.includes(currentAppPackage)
357
334
  );
358
335
  setPlans(availablePlans);
359
336
 
360
- // Mock current subscription
361
337
  let currentSubscription: UserSubscription | null = null;
362
338
  if (user?.isPremium) {
363
339
  currentSubscription = {
@@ -371,7 +347,6 @@ const PremiumSubscriptionScreen: React.FC<BaseScreenProps> = ({
371
347
  setSubscription(currentSubscription);
372
348
  }
373
349
 
374
- // Filter features available for current app and update based on current subscription
375
350
  const availableFeatures = mockIndividualFeatures.filter(feature =>
376
351
  feature.applicableApps.includes(currentAppPackage)
377
352
  );
@@ -383,7 +358,7 @@ const PremiumSubscriptionScreen: React.FC<BaseScreenProps> = ({
383
358
  return {
384
359
  ...feature,
385
360
  isIncludedInCurrentPlan,
386
- isSubscribed: !!isIncludedInCurrentPlan // Mock some individual subscriptions
361
+ isSubscribed: !!isIncludedInCurrentPlan
387
362
  };
388
363
  });
389
364
 
@@ -405,27 +380,21 @@ const PremiumSubscriptionScreen: React.FC<BaseScreenProps> = ({
405
380
 
406
381
  const handleSubscribe = async (planId: string) => {
407
382
  try {
408
- // Check if plan is available for current app
409
383
  const selectedPlan = mockPlans.find(plan => plan.id === planId);
410
384
  if (!selectedPlan?.applicableApps.includes(currentAppPackage)) {
411
385
  toast.error(t('premium.toasts.planUnavailable', { app: currentAppPackage }) || `This plan is not available for the current app (${currentAppPackage})`);
412
386
  return;
413
387
  }
414
388
 
415
- // Special restriction for Mention+ plan - only available in mention app
416
389
  if (planId === 'mention-plus' && currentAppPackage !== 'mention') {
417
390
  toast.error(t('premium.toasts.mentionOnly') || 'Mention+ is only available in the Mention app');
418
391
  return;
419
392
  }
420
393
 
421
394
  setProcessingPayment(true);
422
-
423
- // Mock payment processing
424
395
  await new Promise(resolve => setTimeout(resolve, 2000));
425
-
426
396
  toast.success(t('premium.toasts.activated') || 'Subscription activated successfully!');
427
397
 
428
- // Mock subscription update
429
398
  setSubscription({
430
399
  id: `sub_${Date.now()}`,
431
400
  planId,
@@ -435,7 +404,6 @@ const PremiumSubscriptionScreen: React.FC<BaseScreenProps> = ({
435
404
  cancelAtPeriodEnd: false
436
405
  });
437
406
 
438
- // Reload data to update feature states
439
407
  loadSubscriptionData();
440
408
 
441
409
  } catch (error) {
@@ -448,23 +416,21 @@ const PremiumSubscriptionScreen: React.FC<BaseScreenProps> = ({
448
416
  }
449
417
  };
450
418
 
451
- const handleCancelSubscription = () => {
452
- confirmAction(
453
- t('premium.confirms.cancelSub') || 'Are you sure you want to cancel your subscription? You will lose access to premium features at the end of your current billing period.',
454
- async () => {
455
- try {
456
- // Mock cancellation
457
- setSubscription(prev => prev ? {
458
- ...prev,
459
- cancelAtPeriodEnd: true
460
- } : null);
461
- toast.success(t('premium.toasts.willCancel') || 'Subscription will be canceled at the end of the billing period');
462
- } catch (error) {
463
- toast.error(t('premium.toasts.cancelFailed') || 'Failed to cancel subscription');
464
- }
465
- }
466
- );
467
- };
419
+ const confirmCancelSubscription = useCallback(() => {
420
+ cancelSubscriptionPrompt.open();
421
+ }, [cancelSubscriptionPrompt]);
422
+
423
+ const handleCancelSubscription = useCallback(async () => {
424
+ try {
425
+ setSubscription(prev => prev ? {
426
+ ...prev,
427
+ cancelAtPeriodEnd: true
428
+ } : null);
429
+ toast.success(t('premium.toasts.willCancel') || 'Subscription will be canceled at the end of the billing period');
430
+ } catch (error) {
431
+ toast.error(t('premium.toasts.cancelFailed') || 'Failed to cancel subscription');
432
+ }
433
+ }, [t]);
468
434
 
469
435
  const handleReactivateSubscription = async () => {
470
436
  try {
@@ -479,7 +445,7 @@ const PremiumSubscriptionScreen: React.FC<BaseScreenProps> = ({
479
445
  };
480
446
 
481
447
  const formatPrice = (price: number, currency: string, interval: string) => {
482
- const yearlyPrice = interval === 'year' ? price : price * 12 * 0.8; // 20% discount for yearly
448
+ const yearlyPrice = interval === 'year' ? price : price * 12 * 0.8;
483
449
  const displayPrice = billingInterval === 'year' ? yearlyPrice : price;
484
450
 
485
451
  return {
@@ -496,16 +462,13 @@ const PremiumSubscriptionScreen: React.FC<BaseScreenProps> = ({
496
462
 
497
463
  const handleFeatureSubscribe = async (featureId: string) => {
498
464
  try {
499
- // Check if feature is available for current app
500
465
  const selectedFeature = mockIndividualFeatures.find(feature => feature.id === featureId);
501
466
  if (!selectedFeature?.applicableApps.includes(currentAppPackage)) {
502
467
  toast.error(`This feature is not available for the current app (${currentAppPackage})`);
503
468
  return;
504
469
  }
505
470
 
506
- // Special restrictions for app-specific features
507
471
  if (selectedFeature.appScope === 'specific') {
508
- // For features that are only available in specific apps, enforce strict matching
509
472
  const hasExactMatch = selectedFeature.applicableApps.length === 1 &&
510
473
  selectedFeature.applicableApps[0] === currentAppPackage;
511
474
  if (!hasExactMatch && selectedFeature.applicableApps.length === 1) {
@@ -516,8 +479,6 @@ const PremiumSubscriptionScreen: React.FC<BaseScreenProps> = ({
516
479
  }
517
480
 
518
481
  setProcessingPayment(true);
519
-
520
- // Mock feature subscription
521
482
  await new Promise(resolve => setTimeout(resolve, 1500));
522
483
 
523
484
  setIndividualFeatures(prev =>
@@ -541,26 +502,34 @@ const PremiumSubscriptionScreen: React.FC<BaseScreenProps> = ({
541
502
  }
542
503
  };
543
504
 
544
- const handleFeatureUnsubscribe = async (featureId: string) => {
545
- const feature = individualFeatures.find(f => f.id === featureId);
546
- confirmAction(
547
- (t('premium.confirms.unsubscribeFeature', { name: feature?.name ?? '' }) ?? `Are you sure you want to unsubscribe from ${feature?.name}?`),
548
- async () => {
549
- try {
550
- setIndividualFeatures(prev =>
551
- prev.map(f =>
552
- f.id === featureId
553
- ? { ...f, isSubscribed: false }
554
- : f
555
- )
556
- );
557
- toast.success((t('premium.toasts.featureUnsubscribed', { name: feature?.name ?? '' }) ?? `Unsubscribed from ${feature?.name}`));
558
- } catch (error) {
559
- toast.error(t('premium.toasts.featureUnsubscribeFailed') || 'Failed to unsubscribe from feature');
560
- }
561
- }
562
- );
563
- };
505
+ const confirmFeatureUnsubscribe = useCallback((featureId: string) => {
506
+ setPendingUnsubscribeFeatureId(featureId);
507
+ unsubscribeFeaturePrompt.open();
508
+ }, [unsubscribeFeaturePrompt]);
509
+
510
+ const handleFeatureUnsubscribe = useCallback(async () => {
511
+ if (!pendingUnsubscribeFeatureId) return;
512
+ const feature = individualFeatures.find(f => f.id === pendingUnsubscribeFeatureId);
513
+ try {
514
+ setIndividualFeatures(prev =>
515
+ prev.map(f =>
516
+ f.id === pendingUnsubscribeFeatureId
517
+ ? { ...f, isSubscribed: false }
518
+ : f
519
+ )
520
+ );
521
+ toast.success((t('premium.toasts.featureUnsubscribed', { name: feature?.name ?? '' }) ?? `Unsubscribed from ${feature?.name}`));
522
+ } catch (error) {
523
+ toast.error(t('premium.toasts.featureUnsubscribeFailed') || 'Failed to unsubscribe from feature');
524
+ } finally {
525
+ setPendingUnsubscribeFeatureId(null);
526
+ }
527
+ }, [pendingUnsubscribeFeatureId, individualFeatures, t]);
528
+
529
+ const pendingUnsubscribeFeature = useMemo(
530
+ () => individualFeatures.find(f => f.id === pendingUnsubscribeFeatureId),
531
+ [individualFeatures, pendingUnsubscribeFeatureId]
532
+ );
564
533
 
565
534
  const renderHeader = () => {
566
535
  const getAppDisplayName = (packageName: string) => {
@@ -649,7 +618,7 @@ const PremiumSubscriptionScreen: React.FC<BaseScreenProps> = ({
649
618
  ) : (
650
619
  <TouchableOpacity
651
620
  style={[styles.actionButton, { backgroundColor: dangerColor }]}
652
- onPress={handleCancelSubscription}
621
+ onPress={confirmCancelSubscription}
653
622
  >
654
623
  <Text style={styles.actionButtonText}>{t('premium.actions.cancelSubBtn') || 'Cancel Subscription'}</Text>
655
624
  </TouchableOpacity>
@@ -965,7 +934,7 @@ const PremiumSubscriptionScreen: React.FC<BaseScreenProps> = ({
965
934
  </View>
966
935
  <TouchableOpacity
967
936
  style={[styles.unsubscribeButton, { borderColor: dangerColor }]}
968
- onPress={() => handleFeatureUnsubscribe(feature.id)}
937
+ onPress={() => confirmFeatureUnsubscribe(feature.id)}
969
938
  >
970
939
  <Text style={[styles.unsubscribeText, { color: dangerColor }]}>{t('premium.actions.unsubscribe') || 'Unsubscribe'}</Text>
971
940
  </TouchableOpacity>
@@ -1144,6 +1113,22 @@ const PremiumSubscriptionScreen: React.FC<BaseScreenProps> = ({
1144
1113
 
1145
1114
  <View style={styles.bottomSpacing} />
1146
1115
  </ScrollView>
1116
+ <Prompt.Basic
1117
+ control={cancelSubscriptionPrompt}
1118
+ title={t('premium.confirms.cancelSubTitle') || 'Cancel Subscription'}
1119
+ description={t('premium.confirms.cancelSub') || 'Are you sure you want to cancel your subscription? You will lose access to premium features at the end of your current billing period.'}
1120
+ onConfirm={handleCancelSubscription}
1121
+ confirmButtonCta={t('premium.actions.cancelSubBtn') || 'Cancel Subscription'}
1122
+ confirmButtonColor='negative'
1123
+ />
1124
+ <Prompt.Basic
1125
+ control={unsubscribeFeaturePrompt}
1126
+ title={t('premium.confirms.unsubscribeFeatureTitle') || 'Unsubscribe from Feature'}
1127
+ description={pendingUnsubscribeFeature ? (t('premium.confirms.unsubscribeFeature', { name: pendingUnsubscribeFeature.name }) ?? `Are you sure you want to unsubscribe from ${pendingUnsubscribeFeature.name}?`) : ''}
1128
+ onConfirm={handleFeatureUnsubscribe}
1129
+ confirmButtonCta={t('premium.actions.unsubscribe') || 'Unsubscribe'}
1130
+ confirmButtonColor='negative'
1131
+ />
1147
1132
  </View>
1148
1133
  );
1149
1134
  };
@@ -1396,7 +1381,6 @@ const styles = StyleSheet.create({
1396
1381
  bottomSpacing: {
1397
1382
  height: 40,
1398
1383
  },
1399
- // Tab Navigation Styles
1400
1384
  tabContainer: {
1401
1385
  flexDirection: 'row',
1402
1386
  borderBottomWidth: 1,
@@ -1412,7 +1396,6 @@ const styles = StyleSheet.create({
1412
1396
  fontSize: 16,
1413
1397
  fontWeight: '600',
1414
1398
  },
1415
- // Individual Feature Styles
1416
1399
  featureCard: {
1417
1400
  borderRadius: 12,
1418
1401
  borderWidth: 1,
@@ -1506,7 +1489,6 @@ const styles = StyleSheet.create({
1506
1489
  fontWeight: '600',
1507
1490
  marginBottom: 12,
1508
1491
  },
1509
- // New styles for enhanced feature cards
1510
1492
  featureNameRow: {
1511
1493
  flexDirection: 'row',
1512
1494
  alignItems: 'center',
@@ -1554,7 +1536,6 @@ const styles = StyleSheet.create({
1554
1536
  fontWeight: '500',
1555
1537
  textAlign: 'center',
1556
1538
  },
1557
- // App-specific plan styles
1558
1539
  appSpecificBadge: {
1559
1540
  position: 'absolute',
1560
1541
  top: 16,
@@ -1584,7 +1565,6 @@ const styles = StyleSheet.create({
1584
1565
  fontSize: 16,
1585
1566
  fontWeight: '600',
1586
1567
  },
1587
- // App switcher styles (for development/testing)
1588
1568
  appSwitcher: {
1589
1569
  padding: 16,
1590
1570
  borderBottomWidth: 1,
@@ -7,7 +7,6 @@ import {
7
7
  StyleSheet,
8
8
  ScrollView,
9
9
  ActivityIndicator,
10
- Alert,
11
10
  Platform,
12
11
  RefreshControl,
13
12
  } from 'react-native';
@@ -15,7 +14,8 @@ import type { BaseScreenProps } from '../types/navigation';
15
14
  import { screenContentStyle } from '../constants/spacing';
16
15
  import { toast } from '../../lib/sonner';
17
16
  import type { ClientSession } from '@oxyhq/core';
18
- import { confirmAction } from '../utils/confirmAction';
17
+ import * as Prompt from '@oxyhq/bloom/prompt';
18
+ import { usePromptControl } from '@oxyhq/bloom/prompt';
19
19
  import { Header, GroupedSection } from '../components';
20
20
  import { useTheme } from '@oxyhq/bloom/theme';
21
21
  import { useOxy } from '../context/OxyContext';
@@ -52,6 +52,12 @@ const SessionManagementScreen: React.FC<BaseScreenProps> = ({
52
52
  const [actionLoading, setActionLoading] = useState<string | null>(null);
53
53
  const [switchLoading, setSwitchLoading] = useState<string | null>(null);
54
54
  const [lastRefreshed, setLastRefreshed] = useState<Date | null>(null);
55
+ const [pendingLogoutSessionId, setPendingLogoutSessionId] = useState<string | null>(null);
56
+
57
+ // Prompt controls
58
+ const logoutSessionPrompt = usePromptControl();
59
+ const logoutOtherSessionsPrompt = usePromptControl();
60
+ const logoutAllSessionsPrompt = usePromptControl();
55
61
 
56
62
  // Use bloom theme for non-style color props (ActivityIndicator, icon colors, etc.)
57
63
  const bloomTheme = useTheme();
@@ -75,39 +81,37 @@ const SessionManagementScreen: React.FC<BaseScreenProps> = ({
75
81
  if (__DEV__) {
76
82
  console.error('Failed to load sessions:', error);
77
83
  }
78
- if (Platform.OS === 'web') {
79
- toast.error(t('sessionManagement.toasts.loadFailed'));
80
- } else {
81
- Alert.alert(
82
- 'Error',
83
- t('sessionManagement.toasts.loadFailed'),
84
- [{ text: 'OK' }]
85
- );
86
- }
84
+ toast.error(t('sessionManagement.toasts.loadFailed'));
87
85
  } finally {
88
86
  setLoading(false);
89
87
  setRefreshing(false);
90
88
  }
91
89
  }, [refreshSessions]);
92
90
 
93
- // Memoized logout session handler - prevents unnecessary re-renders
94
- const handleLogoutSession = useCallback(async (sessionId: string) => {
95
- confirmAction(t('sessionManagement.confirms.logoutSession'), async () => {
96
- try {
97
- setActionLoading(sessionId);
98
- await logout(sessionId);
99
- await refreshSessions();
100
- toast.success(t('sessionManagement.toasts.logoutSuccess'));
101
- } catch (error) {
102
- if (__DEV__) {
103
- console.error('Logout session failed:', error);
104
- }
105
- toast.error(t('sessionManagement.toasts.logoutFailed'));
106
- } finally {
107
- setActionLoading(null);
91
+ // Confirm logout session - opens prompt
92
+ const confirmLogoutSession = useCallback((sessionId: string) => {
93
+ setPendingLogoutSessionId(sessionId);
94
+ logoutSessionPrompt.open();
95
+ }, [logoutSessionPrompt]);
96
+
97
+ // Handle logout session - executes after prompt confirmation
98
+ const handleLogoutSession = useCallback(async () => {
99
+ if (!pendingLogoutSessionId) return;
100
+ try {
101
+ setActionLoading(pendingLogoutSessionId);
102
+ await logout(pendingLogoutSessionId);
103
+ await refreshSessions();
104
+ toast.success(t('sessionManagement.toasts.logoutSuccess'));
105
+ } catch (error) {
106
+ if (__DEV__) {
107
+ console.error('Logout session failed:', error);
108
108
  }
109
- });
110
- }, [logout, refreshSessions]);
109
+ toast.error(t('sessionManagement.toasts.logoutFailed'));
110
+ } finally {
111
+ setActionLoading(null);
112
+ setPendingLogoutSessionId(null);
113
+ }
114
+ }, [pendingLogoutSessionId, logout, refreshSessions, t]);
111
115
 
112
116
  // Memoized bulk action items - prevents unnecessary re-renders when dependencies haven't changed
113
117
  const otherSessionsCount = useMemo(() =>
@@ -115,55 +119,55 @@ const SessionManagementScreen: React.FC<BaseScreenProps> = ({
115
119
  [userSessions, activeSessionId]
116
120
  );
117
121
 
118
- // Memoized logout other sessions handler - prevents unnecessary re-renders
119
- const handleLogoutOtherSessions = useCallback(async () => {
122
+ // Confirm logout other sessions - opens prompt
123
+ const confirmLogoutOtherSessions = useCallback(() => {
120
124
  if (otherSessionsCount === 0) {
121
125
  toast.info(t('sessionManagement.toasts.noOtherSessions'));
122
126
  return;
123
127
  }
124
- confirmAction(
125
- t('sessionManagement.confirms.logoutOthers', { count: otherSessionsCount }),
126
- async () => {
127
- try {
128
- setActionLoading('others');
129
- for (const session of userSessions) {
130
- if (session.sessionId !== activeSessionId) {
131
- await logout(session.sessionId);
132
- }
133
- }
134
- await refreshSessions();
135
- toast.success(t('sessionManagement.toasts.logoutOthersSuccess'));
136
- } catch (error) {
137
- if (__DEV__) {
138
- console.error('Logout other sessions failed:', error);
139
- }
140
- toast.error(t('sessionManagement.toasts.logoutOthersFailed'));
141
- } finally {
142
- setActionLoading(null);
128
+ logoutOtherSessionsPrompt.open();
129
+ }, [otherSessionsCount, logoutOtherSessionsPrompt, t]);
130
+
131
+ // Handle logout other sessions - executes after prompt confirmation
132
+ const handleLogoutOtherSessions = useCallback(async () => {
133
+ try {
134
+ setActionLoading('others');
135
+ for (const session of userSessions) {
136
+ if (session.sessionId !== activeSessionId) {
137
+ await logout(session.sessionId);
143
138
  }
144
139
  }
145
- );
146
- }, [otherSessionsCount, userSessions, activeSessionId, logout, refreshSessions]);
140
+ await refreshSessions();
141
+ toast.success(t('sessionManagement.toasts.logoutOthersSuccess'));
142
+ } catch (error) {
143
+ if (__DEV__) {
144
+ console.error('Logout other sessions failed:', error);
145
+ }
146
+ toast.error(t('sessionManagement.toasts.logoutOthersFailed'));
147
+ } finally {
148
+ setActionLoading(null);
149
+ }
150
+ }, [userSessions, activeSessionId, logout, refreshSessions, t]);
151
+
152
+ // Confirm logout all sessions - opens prompt
153
+ const confirmLogoutAllSessions = useCallback(() => {
154
+ logoutAllSessionsPrompt.open();
155
+ }, [logoutAllSessionsPrompt]);
147
156
 
148
- // Memoized logout all sessions handler - prevents unnecessary re-renders
157
+ // Handle logout all sessions - executes after prompt confirmation
149
158
  const handleLogoutAllSessions = useCallback(async () => {
150
- confirmAction(
151
- t('sessionManagement.confirms.logoutAll'),
152
- async () => {
153
- try {
154
- setActionLoading('all');
155
- await logoutAll();
156
- } catch (error) {
157
- if (__DEV__) {
158
- console.error('Logout all sessions failed:', error);
159
- }
160
- toast.error(t('sessionManagement.toasts.logoutAllFailed'));
161
- } finally {
162
- setActionLoading(null);
163
- }
159
+ try {
160
+ setActionLoading('all');
161
+ await logoutAll();
162
+ } catch (error) {
163
+ if (__DEV__) {
164
+ console.error('Logout all sessions failed:', error);
164
165
  }
165
- );
166
- }, [logoutAll]);
166
+ toast.error(t('sessionManagement.toasts.logoutAllFailed'));
167
+ } finally {
168
+ setActionLoading(null);
169
+ }
170
+ }, [logoutAll, t]);
167
171
 
168
172
  // Memoized relative time formatter - prevents function recreation on every render
169
173
  const formatRelative = useCallback((dateString?: string) => {
@@ -240,7 +244,7 @@ const SessionManagementScreen: React.FC<BaseScreenProps> = ({
240
244
  )}
241
245
  </TouchableOpacity>
242
246
  <TouchableOpacity
243
- onPress={() => handleLogoutSession(session.sessionId)}
247
+ onPress={() => confirmLogoutSession(session.sessionId)}
244
248
  style={[styles.sessionPillButton, { backgroundColor: isDarkTheme ? LOGOUT_BUTTON_BG.dark : LOGOUT_BUTTON_BG.light, borderColor: dangerColor }]}
245
249
  disabled={actionLoading === session.sessionId || switchLoading === session.sessionId}
246
250
  >
@@ -260,7 +264,7 @@ const SessionManagementScreen: React.FC<BaseScreenProps> = ({
260
264
  dense: true,
261
265
  };
262
266
  });
263
- }, [userSessions, activeSessionId, formatRelative, successColor, primaryColor, isDarkTheme, switchLoading, actionLoading, handleSwitchSession, handleLogoutSession, dangerColor]);
267
+ }, [userSessions, activeSessionId, formatRelative, successColor, primaryColor, isDarkTheme, switchLoading, actionLoading, handleSwitchSession, confirmLogoutSession, dangerColor]);
264
268
 
265
269
  const bulkItems = useMemo(() => [
266
270
  {
@@ -269,7 +273,7 @@ const SessionManagementScreen: React.FC<BaseScreenProps> = ({
269
273
  iconColor: primaryColor,
270
274
  title: t('sessionManagement.logoutOthers.title'),
271
275
  subtitle: otherSessionsCount === 0 ? t('sessionManagement.logoutOthers.noOtherSessions') : t('sessionManagement.logoutOthers.subtitle'),
272
- onPress: handleLogoutOtherSessions,
276
+ onPress: confirmLogoutOtherSessions,
273
277
  showChevron: false,
274
278
  customContent: actionLoading === 'others' ? <ActivityIndicator size="small" color={primaryColor} /> : undefined,
275
279
  disabled: actionLoading === 'others' || otherSessionsCount === 0,
@@ -281,13 +285,13 @@ const SessionManagementScreen: React.FC<BaseScreenProps> = ({
281
285
  iconColor: dangerColor,
282
286
  title: t('sessionManagement.logoutAll.title'),
283
287
  subtitle: t('sessionManagement.logoutAll.subtitle'),
284
- onPress: handleLogoutAllSessions,
288
+ onPress: confirmLogoutAllSessions,
285
289
  showChevron: false,
286
290
  customContent: actionLoading === 'all' ? <ActivityIndicator size="small" color={dangerColor} /> : undefined,
287
291
  disabled: actionLoading === 'all',
288
292
  dense: true,
289
293
  },
290
- ], [otherSessionsCount, primaryColor, dangerColor, handleLogoutOtherSessions, handleLogoutAllSessions, actionLoading]);
294
+ ], [otherSessionsCount, primaryColor, dangerColor, confirmLogoutOtherSessions, confirmLogoutAllSessions, actionLoading]);
291
295
 
292
296
  if (loading) {
293
297
  return (
@@ -342,6 +346,30 @@ const SessionManagementScreen: React.FC<BaseScreenProps> = ({
342
346
  <Text style={styles.closeButtonText} className="text-primary">{t('sessionManagement.close')}</Text>
343
347
  </TouchableOpacity>
344
348
  </View>
349
+ <Prompt.Basic
350
+ control={logoutSessionPrompt}
351
+ title={t('sessionManagement.confirms.logoutSessionTitle') || 'Log Out Session'}
352
+ description={t('sessionManagement.confirms.logoutSession')}
353
+ onConfirm={handleLogoutSession}
354
+ confirmButtonCta={t('sessionManagement.logout') || 'Log Out'}
355
+ confirmButtonColor='negative'
356
+ />
357
+ <Prompt.Basic
358
+ control={logoutOtherSessionsPrompt}
359
+ title={t('sessionManagement.confirms.logoutOthersTitle') || 'Log Out Other Sessions'}
360
+ description={t('sessionManagement.confirms.logoutOthers', { count: otherSessionsCount })}
361
+ onConfirm={handleLogoutOtherSessions}
362
+ confirmButtonCta={t('sessionManagement.logoutOthers.title') || 'Log Out Others'}
363
+ confirmButtonColor='negative'
364
+ />
365
+ <Prompt.Basic
366
+ control={logoutAllSessionsPrompt}
367
+ title={t('sessionManagement.confirms.logoutAllTitle') || 'Log Out All Sessions'}
368
+ description={t('sessionManagement.confirms.logoutAll')}
369
+ onConfirm={handleLogoutAllSessions}
370
+ confirmButtonCta={t('sessionManagement.logoutAll.title') || 'Log Out All'}
371
+ confirmButtonColor='negative'
372
+ />
345
373
  </View>
346
374
  );
347
375
  };
@@ -1,4 +1,3 @@
1
- import { Alert } from 'react-native';
2
1
  import type { FileMetadata } from '@oxyhq/core';
3
2
  import { File as ExpoFile } from 'expo-file-system';
4
3
  import { toast } from '../../lib/sonner';
@@ -39,35 +38,6 @@ export function getFileIcon(contentType: string): string {
39
38
  return 'document-outline';
40
39
  }
41
40
 
42
- /**
43
- * Unified confirmation dialog - uses Alert.alert for all platforms
44
- */
45
- export function confirmAction(
46
- message: string,
47
- title?: string,
48
- confirmText = 'OK',
49
- cancelText = 'Cancel'
50
- ): Promise<boolean> {
51
- return new Promise((resolve) => {
52
- Alert.alert(
53
- title || 'Confirm',
54
- message,
55
- [
56
- {
57
- text: cancelText,
58
- style: 'cancel',
59
- onPress: () => resolve(false),
60
- },
61
- {
62
- text: confirmText,
63
- onPress: () => resolve(true),
64
- },
65
- ],
66
- { cancelable: true, onDismiss: () => resolve(false) }
67
- );
68
- });
69
- }
70
-
71
41
  /**
72
42
  * Convert DocumentPicker asset to File object
73
43
  * Handles both web (native File API) and mobile (URI-based) file sources