@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.
- package/lib/commonjs/index.js +9 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/ui/components/ActingAsBanner.js +143 -0
- package/lib/commonjs/ui/components/ActingAsBanner.js.map +1 -0
- package/lib/commonjs/ui/components/OxyPayButton.js +4 -2
- package/lib/commonjs/ui/components/OxyPayButton.js.map +1 -1
- package/lib/commonjs/ui/components/OxySignInButton.js +83 -82
- package/lib/commonjs/ui/components/OxySignInButton.js.map +1 -1
- package/lib/commonjs/ui/components/SettingRow.js +11 -5
- package/lib/commonjs/ui/components/SettingRow.js.map +1 -1
- package/lib/commonjs/ui/components/fileManagement/FileDetailsModal.js +76 -121
- package/lib/commonjs/ui/components/fileManagement/FileDetailsModal.js.map +1 -1
- package/lib/commonjs/ui/components/fileManagement/UploadPreview.js +32 -18
- package/lib/commonjs/ui/components/fileManagement/UploadPreview.js.map +1 -1
- package/lib/commonjs/ui/components/icon/OxyIcon.js +5 -4
- package/lib/commonjs/ui/components/icon/OxyIcon.js.map +1 -1
- package/lib/commonjs/ui/components/internal/GroupedPillButtons.js +11 -9
- package/lib/commonjs/ui/components/internal/GroupedPillButtons.js.map +1 -1
- package/lib/commonjs/ui/components/internal/PinInput.js +3 -2
- package/lib/commonjs/ui/components/internal/PinInput.js.map +1 -1
- package/lib/commonjs/ui/components/modals/DeleteAccountModal.js +83 -219
- package/lib/commonjs/ui/components/modals/DeleteAccountModal.js.map +1 -1
- package/lib/commonjs/ui/constants/theme.js +2 -2
- package/lib/commonjs/ui/constants/theme.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContext.js +78 -3
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/navigation/routes.js +3 -2
- package/lib/commonjs/ui/navigation/routes.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountCenterScreen.js +33 -11
- package/lib/commonjs/ui/screens/AccountCenterScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountOverviewScreen.js +70 -79
- package/lib/commonjs/ui/screens/AccountOverviewScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js +22 -10
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountSwitcherScreen.js +376 -82
- package/lib/commonjs/ui/screens/AccountSwitcherScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountVerificationScreen.js +21 -9
- package/lib/commonjs/ui/screens/AccountVerificationScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/CreateManagedAccountScreen.js +346 -0
- package/lib/commonjs/ui/screens/CreateManagedAccountScreen.js.map +1 -0
- package/lib/commonjs/ui/screens/FileManagementScreen.js +83 -50
- package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/HistoryViewScreen.js +80 -101
- package/lib/commonjs/ui/screens/HistoryViewScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/PremiumSubscriptionScreen.js +66 -93
- package/lib/commonjs/ui/screens/PremiumSubscriptionScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/SessionManagementScreen.js +101 -66
- package/lib/commonjs/ui/screens/SessionManagementScreen.js.map +1 -1
- package/lib/commonjs/ui/utils/fileManagement.js +0 -21
- package/lib/commonjs/ui/utils/fileManagement.js.map +1 -1
- package/lib/module/index.js +3 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/ui/components/ActingAsBanner.js +140 -0
- package/lib/module/ui/components/ActingAsBanner.js.map +1 -0
- package/lib/module/ui/components/OxyPayButton.js +4 -2
- package/lib/module/ui/components/OxyPayButton.js.map +1 -1
- package/lib/module/ui/components/OxySignInButton.js +84 -83
- package/lib/module/ui/components/OxySignInButton.js.map +1 -1
- package/lib/module/ui/components/SettingRow.js +11 -5
- package/lib/module/ui/components/SettingRow.js.map +1 -1
- package/lib/module/ui/components/fileManagement/FileDetailsModal.js +76 -122
- package/lib/module/ui/components/fileManagement/FileDetailsModal.js.map +1 -1
- package/lib/module/ui/components/fileManagement/UploadPreview.js +32 -19
- package/lib/module/ui/components/fileManagement/UploadPreview.js.map +1 -1
- package/lib/module/ui/components/icon/OxyIcon.js +5 -4
- package/lib/module/ui/components/icon/OxyIcon.js.map +1 -1
- package/lib/module/ui/components/internal/GroupedPillButtons.js +11 -9
- package/lib/module/ui/components/internal/GroupedPillButtons.js.map +1 -1
- package/lib/module/ui/components/internal/PinInput.js +3 -2
- package/lib/module/ui/components/internal/PinInput.js.map +1 -1
- package/lib/module/ui/components/modals/DeleteAccountModal.js +83 -220
- package/lib/module/ui/components/modals/DeleteAccountModal.js.map +1 -1
- package/lib/module/ui/constants/theme.js +2 -2
- package/lib/module/ui/constants/theme.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +78 -3
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/navigation/routes.js +3 -2
- package/lib/module/ui/navigation/routes.js.map +1 -1
- package/lib/module/ui/screens/AccountCenterScreen.js +32 -11
- package/lib/module/ui/screens/AccountCenterScreen.js.map +1 -1
- package/lib/module/ui/screens/AccountOverviewScreen.js +71 -80
- package/lib/module/ui/screens/AccountOverviewScreen.js.map +1 -1
- package/lib/module/ui/screens/AccountSettingsScreen.js +22 -10
- package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/module/ui/screens/AccountSwitcherScreen.js +375 -82
- package/lib/module/ui/screens/AccountSwitcherScreen.js.map +1 -1
- package/lib/module/ui/screens/AccountVerificationScreen.js +22 -10
- package/lib/module/ui/screens/AccountVerificationScreen.js.map +1 -1
- package/lib/module/ui/screens/CreateManagedAccountScreen.js +342 -0
- package/lib/module/ui/screens/CreateManagedAccountScreen.js.map +1 -0
- package/lib/module/ui/screens/FileManagementScreen.js +85 -52
- package/lib/module/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/module/ui/screens/HistoryViewScreen.js +80 -101
- package/lib/module/ui/screens/HistoryViewScreen.js.map +1 -1
- package/lib/module/ui/screens/PremiumSubscriptionScreen.js +66 -94
- package/lib/module/ui/screens/PremiumSubscriptionScreen.js.map +1 -1
- package/lib/module/ui/screens/SessionManagementScreen.js +101 -67
- package/lib/module/ui/screens/SessionManagementScreen.js.map +1 -1
- package/lib/module/ui/utils/fileManagement.js +0 -20
- package/lib/module/ui/utils/fileManagement.js.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +1 -0
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/ActingAsBanner.d.ts +4 -0
- package/lib/typescript/commonjs/ui/components/ActingAsBanner.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/OxyPayButton.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxySignInButton.d.ts +0 -1
- package/lib/typescript/commonjs/ui/components/OxySignInButton.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/SettingRow.d.ts +2 -2
- package/lib/typescript/commonjs/ui/components/SettingRow.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/fileManagement/FileDetailsModal.d.ts +3 -4
- package/lib/typescript/commonjs/ui/components/fileManagement/FileDetailsModal.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/fileManagement/UploadPreview.d.ts +2 -3
- package/lib/typescript/commonjs/ui/components/fileManagement/UploadPreview.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/icon/OxyIcon.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/internal/GroupedPillButtons.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/internal/PinInput.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/modals/DeleteAccountModal.d.ts +2 -2
- package/lib/typescript/commonjs/ui/components/modals/DeleteAccountModal.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts +6 -0
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/navigation/routes.d.ts +1 -1
- package/lib/typescript/commonjs/ui/navigation/routes.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/AccountCenterScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/AccountOverviewScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/AccountSwitcherScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/AccountVerificationScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/CreateManagedAccountScreen.d.ts +5 -0
- package/lib/typescript/commonjs/ui/screens/CreateManagedAccountScreen.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/screens/FileManagementScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/HistoryViewScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/PremiumSubscriptionScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/SessionManagementScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/utils/fileManagement.d.ts +0 -4
- package/lib/typescript/commonjs/ui/utils/fileManagement.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +1 -0
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/ActingAsBanner.d.ts +4 -0
- package/lib/typescript/module/ui/components/ActingAsBanner.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/OxyPayButton.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxySignInButton.d.ts +0 -1
- package/lib/typescript/module/ui/components/OxySignInButton.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/SettingRow.d.ts +2 -2
- package/lib/typescript/module/ui/components/SettingRow.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/fileManagement/FileDetailsModal.d.ts +3 -4
- package/lib/typescript/module/ui/components/fileManagement/FileDetailsModal.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/fileManagement/UploadPreview.d.ts +2 -3
- package/lib/typescript/module/ui/components/fileManagement/UploadPreview.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/icon/OxyIcon.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/internal/GroupedPillButtons.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/internal/PinInput.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/modals/DeleteAccountModal.d.ts +2 -2
- package/lib/typescript/module/ui/components/modals/DeleteAccountModal.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/OxyContext.d.ts +6 -0
- package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/module/ui/navigation/routes.d.ts +1 -1
- package/lib/typescript/module/ui/navigation/routes.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/AccountCenterScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/AccountOverviewScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/AccountSwitcherScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/AccountVerificationScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/CreateManagedAccountScreen.d.ts +5 -0
- package/lib/typescript/module/ui/screens/CreateManagedAccountScreen.d.ts.map +1 -0
- package/lib/typescript/module/ui/screens/FileManagementScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/HistoryViewScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/PremiumSubscriptionScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/SessionManagementScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/utils/fileManagement.d.ts +0 -4
- package/lib/typescript/module/ui/utils/fileManagement.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +3 -0
- package/src/ui/components/ActingAsBanner.tsx +135 -0
- package/src/ui/components/OxyPayButton.tsx +5 -3
- package/src/ui/components/OxySignInButton.tsx +82 -81
- package/src/ui/components/SettingRow.tsx +14 -7
- package/src/ui/components/fileManagement/FileDetailsModal.tsx +69 -99
- package/src/ui/components/fileManagement/UploadPreview.tsx +58 -46
- package/src/ui/components/icon/OxyIcon.tsx +5 -4
- package/src/ui/components/internal/GroupedPillButtons.tsx +15 -12
- package/src/ui/components/internal/PinInput.tsx +4 -3
- package/src/ui/components/modals/DeleteAccountModal.tsx +79 -221
- package/src/ui/constants/theme.ts +2 -2
- package/src/ui/context/OxyContext.tsx +85 -0
- package/src/ui/navigation/routes.ts +3 -1
- package/src/ui/screens/AccountCenterScreen.tsx +40 -122
- package/src/ui/screens/AccountOverviewScreen.tsx +63 -98
- package/src/ui/screens/AccountSettingsScreen.tsx +21 -7
- package/src/ui/screens/AccountSwitcherScreen.tsx +344 -87
- package/src/ui/screens/AccountVerificationScreen.tsx +24 -16
- package/src/ui/screens/CreateManagedAccountScreen.tsx +338 -0
- package/src/ui/screens/FileManagementScreen.tsx +62 -54
- package/src/ui/screens/HistoryViewScreen.tsx +57 -204
- package/src/ui/screens/PremiumSubscriptionScreen.tsx +73 -93
- package/src/ui/screens/SessionManagementScreen.tsx +101 -73
- package/src/ui/utils/fileManagement.ts +0 -30
- package/lib/commonjs/ui/utils/confirmAction.js +0 -28
- package/lib/commonjs/ui/utils/confirmAction.js.map +0 -1
- package/lib/module/ui/utils/confirmAction.js +0 -25
- package/lib/module/ui/utils/confirmAction.js.map +0 -1
- package/lib/typescript/commonjs/ui/utils/confirmAction.d.ts +0 -7
- package/lib/typescript/commonjs/ui/utils/confirmAction.d.ts.map +0 -1
- package/lib/typescript/module/ui/utils/confirmAction.d.ts +0 -7
- package/lib/typescript/module/ui/utils/confirmAction.d.ts.map +0 -1
- 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
|
|
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
|
-
|
|
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
|
|
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
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
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;
|
|
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
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
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={
|
|
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={() =>
|
|
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
|
|
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
|
-
|
|
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
|
-
//
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
119
|
-
const
|
|
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
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
157
|
+
// Handle logout all sessions - executes after prompt confirmation
|
|
149
158
|
const handleLogoutAllSessions = useCallback(async () => {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
-
|
|
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={() =>
|
|
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,
|
|
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:
|
|
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:
|
|
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,
|
|
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
|