UNPKG

@oxyhq/services

Version:

Reusable OxyHQ module to handle authentication, user management, karma system, device-based session management and more 🚀

1,249 lines (1,225 loc) 56.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _react = _interopRequireWildcard(require("react")); var _reactNative = require("react-native"); var _OxyContext = require("../context/OxyContext"); var _fonts = require("../styles/fonts"); var _sonner = require("../../lib/sonner"); var _vectorIcons = require("@expo/vector-icons"); var _jsxRuntime = require("react/jsx-runtime"); function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } const PremiumSubscriptionScreen = ({ onClose, theme, navigate, goBack }) => { const { user, oxyServices } = (0, _OxyContext.useOxy)(); const [loading, setLoading] = (0, _react.useState)(true); const [subscription, setSubscription] = (0, _react.useState)(null); const [plans, setPlans] = (0, _react.useState)([]); const [individualFeatures, setIndividualFeatures] = (0, _react.useState)([]); const [selectedPlan, setSelectedPlan] = (0, _react.useState)(null); const [processingPayment, setProcessingPayment] = (0, _react.useState)(false); const [billingInterval, setBillingInterval] = (0, _react.useState)('month'); const [activeTab, setActiveTab] = (0, _react.useState)('plans'); const [currentAppPackage, setCurrentAppPackage] = (0, _react.useState)('mention'); // Default to mention for demo const isDarkTheme = theme === 'dark'; const textColor = isDarkTheme ? '#FFFFFF' : '#000000'; const backgroundColor = isDarkTheme ? '#121212' : '#FFFFFF'; const secondaryBackgroundColor = isDarkTheme ? '#222222' : '#F5F5F5'; const borderColor = isDarkTheme ? '#444444' : '#E0E0E0'; const primaryColor = '#007AFF'; const successColor = '#30D158'; const warningColor = '#FF9500'; const dangerColor = '#FF3B30'; // Oxy+ subscription plans const mockPlans = [{ id: 'mention-plus', name: 'Mention+', description: 'Enhanced features for better social experience', price: 4.99, currency: 'USD', interval: 'month', appScope: 'specific', applicableApps: ['mention'], // Only available in mention app features: ['Undo posts option', 'Improved reading mode', 'Organize bookmarks into folders', 'Early access to select features', 'Edit posts capability', 'Enhanced customization options'], includedFeatures: ['reading-mode-plus', 'custom-themes'] }, { id: 'oxy-insider', name: 'Oxy+ Insider', description: 'Exclusive access to behind-the-scenes content', price: 9.99, currency: 'USD', interval: 'month', appScope: 'ecosystem', applicableApps: ['mention', 'oxy-social', 'oxy-workspace', 'oxy-creator'], features: ['Everything in Mention+', 'Behind-the-scenes updates from creators', 'Early access to new features', 'Dedicated support team', 'Exclusive content access', 'Beta feature testing'], includedFeatures: ['reading-mode-plus', 'custom-themes', 'analytics-basic'], isPopular: true }, { id: 'oxy-connect', name: 'Oxy+ Connect', description: 'Advanced networking and community features', price: 14.99, currency: 'USD', interval: 'month', appScope: 'ecosystem', applicableApps: ['mention', 'oxy-social', 'oxy-workspace', 'oxy-creator'], features: ['Everything in Oxy+ Insider', 'Create and join private groups', 'Advanced search and filtering tools', 'Customizable profile highlighting', 'Enhanced connection features', 'Priority in community events'], includedFeatures: ['reading-mode-plus', 'custom-themes', 'analytics-basic', 'group-management'] }, { id: 'oxy-premium', name: 'Oxy+ Premium', description: 'Complete premium experience with all perks', price: 24.99, currency: 'USD', interval: 'month', appScope: 'ecosystem', applicableApps: ['mention', 'oxy-social', 'oxy-workspace', 'oxy-creator', 'oxy-analytics'], features: ['Everything in Oxy+ Connect', 'Priority customer support', 'Access to premium content and events', 'Advanced analytics dashboard', 'VIP community status', 'Exclusive premium events'], includedFeatures: ['reading-mode-plus', 'custom-themes', 'analytics-basic', 'analytics-advanced', 'group-management'] }, { id: 'oxy-creator', name: 'Oxy+ Creator', description: 'Professional tools for content creators', price: 39.99, currency: 'USD', interval: 'month', appScope: 'ecosystem', applicableApps: ['mention', 'oxy-social', 'oxy-workspace', 'oxy-creator', 'oxy-analytics', 'oxy-studio'], features: ['Everything in Oxy+ Premium', 'Advanced analytics and insights', 'Promotional tools and resources', 'Content monetization features', 'Creator support program', 'Revenue sharing opportunities'], includedFeatures: ['reading-mode-plus', 'custom-themes', 'analytics-basic', 'analytics-advanced', 'group-management', 'creator-tools', 'monetization-features'] }]; // Individual feature subscriptions const mockIndividualFeatures = [{ id: 'analytics-basic', name: 'Basic Analytics', description: 'View post performance and engagement metrics', price: 2.99, currency: 'USD', interval: 'month', category: 'analytics', appScope: 'ecosystem', applicableApps: ['mention', 'oxy-social', 'oxy-workspace'], canBePurchasedSeparately: true, includedInPlans: ['oxy-insider', 'oxy-connect', 'oxy-premium', 'oxy-creator'] }, { id: 'analytics-advanced', name: 'Advanced Analytics', description: 'Detailed insights, trends, and audience demographics', price: 7.99, currency: 'USD', interval: 'month', category: 'analytics', appScope: 'ecosystem', applicableApps: ['mention', 'oxy-social', 'oxy-workspace', 'oxy-creator', 'oxy-analytics'], canBePurchasedSeparately: true, includedInPlans: ['oxy-premium', 'oxy-creator'] }, { id: 'custom-themes', name: 'Custom Themes', description: 'Personalize your app with custom colors and layouts', price: 1.99, currency: 'USD', interval: 'month', category: 'customization', appScope: 'ecosystem', applicableApps: ['mention', 'oxy-social', 'oxy-workspace', 'oxy-creator'], canBePurchasedSeparately: false, // Included in all plans includedInPlans: ['mention-plus', 'oxy-insider', 'oxy-connect', 'oxy-premium', 'oxy-creator'] }, { id: 'reading-mode-plus', name: 'Reading Mode Plus', description: 'Enhanced reading experience with focus modes', price: 1.99, currency: 'USD', interval: 'month', category: 'content', appScope: 'specific', applicableApps: ['mention', 'oxy-social'], canBePurchasedSeparately: false, // Included in all plans includedInPlans: ['mention-plus', 'oxy-insider', 'oxy-connect', 'oxy-premium', 'oxy-creator'] }, { id: 'group-management', name: 'Group Management', description: 'Create and manage private groups and communities', price: 4.99, currency: 'USD', interval: 'month', category: 'networking', appScope: 'ecosystem', applicableApps: ['mention', 'oxy-social', 'oxy-workspace'], canBePurchasedSeparately: true, includedInPlans: ['oxy-connect', 'oxy-premium', 'oxy-creator'] }, { id: 'creator-tools', name: 'Creator Tools Suite', description: 'Professional content creation and editing tools', price: 9.99, currency: 'USD', interval: 'month', category: 'productivity', appScope: 'specific', applicableApps: ['oxy-creator', 'oxy-studio'], canBePurchasedSeparately: true, includedInPlans: ['oxy-creator'] }, { id: 'monetization-features', name: 'Monetization Features', description: 'Revenue sharing, sponsorship tools, and creator fund access', price: 12.99, currency: 'USD', interval: 'month', category: 'productivity', appScope: 'specific', applicableApps: ['oxy-creator'], canBePurchasedSeparately: true, includedInPlans: ['oxy-creator'] }, { id: 'workspace-collaboration', name: 'Workspace Collaboration', description: 'Advanced team features and project management tools', price: 6.99, currency: 'USD', interval: 'month', category: 'productivity', appScope: 'specific', applicableApps: ['oxy-workspace'], canBePurchasedSeparately: true, includedInPlans: ['oxy-premium', 'oxy-creator'] }]; (0, _react.useEffect)(() => { detectCurrentApp(); }, []); (0, _react.useEffect)(() => { if (currentAppPackage) { loadSubscriptionData(); } }, [currentAppPackage, user?.isPremium]); const detectCurrentApp = () => { // In a real implementation, this would detect the actual app package name // For now, we'll use a mock detection based on available methods // Real app detection methods you could use: // 1. Check bundle identifier in React Native: // import DeviceInfo from 'react-native-device-info'; // const bundleId = DeviceInfo.getBundleId(); // Example: com.oxy.mention -> 'mention' // 2. Environment variables or build configuration // const appPackage = __DEV__ ? process.env.APP_PACKAGE : 'mention'; // 3. Check specific app capabilities or modules // if (typeof MentionModule !== 'undefined') return 'mention'; // if (typeof OxyWorkspaceModule !== 'undefined') return 'oxy-workspace'; // 4. Use build-time configuration with Metro or similar // const appPackage = require('../config/app.json').packageName; // For demo purposes, we'll simulate different apps // You would replace this with actual app detection logic // IMPORTANT: This ensures subscription restrictions work properly: // - Mention+ plan can only be subscribed to when app package == 'mention' // - Other app-specific plans follow the same pattern // - Ecosystem plans work across all apps const detectedApp = 'mention'; // This would be dynamic in real implementation setCurrentAppPackage(detectedApp); // Log for debugging console.log('Detected app package:', detectedApp); console.log('Available plans for this app will be filtered accordingly'); }; const loadSubscriptionData = async () => { try { setLoading(true); // Filter plans available for current app const availablePlans = mockPlans.filter(plan => plan.applicableApps.includes(currentAppPackage)); setPlans(availablePlans); // Mock current subscription let currentSubscription = null; if (user?.isPremium) { currentSubscription = { id: 'sub_12345', planId: 'oxy-insider', status: 'active', currentPeriodStart: new Date().toISOString(), currentPeriodEnd: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(), cancelAtPeriodEnd: false }; setSubscription(currentSubscription); } // Filter features available for current app and update based on current subscription const availableFeatures = mockIndividualFeatures.filter(feature => feature.applicableApps.includes(currentAppPackage)); const updatedFeatures = availableFeatures.map(feature => { const isIncludedInCurrentPlan = !!(currentSubscription && feature.includedInPlans.includes(currentSubscription.planId)); return { ...feature, isIncludedInCurrentPlan, isSubscribed: isIncludedInCurrentPlan ? true : false // Mock some individual subscriptions }; }); setIndividualFeatures(updatedFeatures); } catch (error) { console.error('Failed to load subscription data:', error); _sonner.toast.error('Failed to load subscription information'); } finally { setLoading(false); } }; const handlePlanSelection = planId => { setSelectedPlan(planId); }; const handleSubscribe = async planId => { try { // Check if plan is available for current app const selectedPlan = mockPlans.find(plan => plan.id === planId); if (!selectedPlan?.applicableApps.includes(currentAppPackage)) { console.log(`❌ Subscription blocked: Plan "${selectedPlan?.name}" not available for app "${currentAppPackage}"`); _sonner.toast.error(`This plan is not available for the current app (${currentAppPackage})`); return; } // Special restriction for Mention+ plan - only available in mention app if (planId === 'mention-plus' && currentAppPackage !== 'mention') { console.log(`❌ Subscription blocked: Mention+ plan requires app to be "mention", current app is "${currentAppPackage}"`); _sonner.toast.error('Mention+ is only available in the Mention app'); return; } console.log(`✅ Subscription allowed: Plan "${selectedPlan.name}" is available for app "${currentAppPackage}"`); setProcessingPayment(true); // Mock payment processing await new Promise(resolve => setTimeout(resolve, 2000)); _sonner.toast.success('Subscription activated successfully!'); // Mock subscription update setSubscription({ id: 'sub_' + Date.now(), planId, status: 'active', currentPeriodStart: new Date().toISOString(), currentPeriodEnd: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(), cancelAtPeriodEnd: false }); // Reload data to update feature states loadSubscriptionData(); } catch (error) { console.error('Payment failed:', error); _sonner.toast.error('Payment failed. Please try again.'); } finally { setProcessingPayment(false); } }; const handleCancelSubscription = () => { _reactNative.Alert.alert('Cancel Subscription', 'Are you sure you want to cancel your subscription? You will lose access to premium features at the end of your current billing period.', [{ text: 'Keep Subscription', style: 'cancel' }, { text: 'Cancel Subscription', style: 'destructive', onPress: async () => { try { // Mock cancellation setSubscription(prev => prev ? { ...prev, cancelAtPeriodEnd: true } : null); _sonner.toast.success('Subscription will be canceled at the end of the billing period'); } catch (error) { _sonner.toast.error('Failed to cancel subscription'); } } }]); }; const handleReactivateSubscription = async () => { try { setSubscription(prev => prev ? { ...prev, cancelAtPeriodEnd: false } : null); _sonner.toast.success('Subscription reactivated successfully'); } catch (error) { _sonner.toast.error('Failed to reactivate subscription'); } }; const formatPrice = (price, currency, interval) => { const yearlyPrice = interval === 'year' ? price : price * 12 * 0.8; // 20% discount for yearly const displayPrice = billingInterval === 'year' ? yearlyPrice : price; return { price: displayPrice, formatted: `$${displayPrice.toFixed(2)}`, interval: billingInterval === 'year' ? 'year' : 'month' }; }; const getCurrentPlan = () => { if (!subscription) return null; return plans.find(plan => plan.id === subscription.planId); }; const handleFeatureSubscribe = async featureId => { try { // Check if feature is available for current app const selectedFeature = mockIndividualFeatures.find(feature => feature.id === featureId); if (!selectedFeature?.applicableApps.includes(currentAppPackage)) { _sonner.toast.error(`This feature is not available for the current app (${currentAppPackage})`); return; } // Special restrictions for app-specific features if (selectedFeature.appScope === 'specific') { // For features that are only available in specific apps, enforce strict matching const hasExactMatch = selectedFeature.applicableApps.length === 1 && selectedFeature.applicableApps[0] === currentAppPackage; if (!hasExactMatch && selectedFeature.applicableApps.length === 1) { const requiredApp = selectedFeature.applicableApps[0]; _sonner.toast.error(`${selectedFeature.name} is only available in the ${requiredApp} app`); return; } } setProcessingPayment(true); // Mock feature subscription await new Promise(resolve => setTimeout(resolve, 1500)); setIndividualFeatures(prev => prev.map(feature => feature.id === featureId ? { ...feature, isSubscribed: true } : feature)); const feature = individualFeatures.find(f => f.id === featureId); _sonner.toast.success(`Subscribed to ${feature?.name} successfully!`); } catch (error) { console.error('Feature subscription failed:', error); _sonner.toast.error('Feature subscription failed. Please try again.'); } finally { setProcessingPayment(false); } }; const handleFeatureUnsubscribe = async featureId => { const feature = individualFeatures.find(f => f.id === featureId); _reactNative.Alert.alert('Unsubscribe Feature', `Are you sure you want to unsubscribe from ${feature?.name}?`, [{ text: 'Keep Feature', style: 'cancel' }, { text: 'Unsubscribe', style: 'destructive', onPress: async () => { try { setIndividualFeatures(prev => prev.map(f => f.id === featureId ? { ...f, isSubscribed: false } : f)); _sonner.toast.success(`Unsubscribed from ${feature?.name}`); } catch (error) { _sonner.toast.error('Failed to unsubscribe from feature'); } } }]); }; const renderHeader = () => { const getAppDisplayName = packageName => { const appNames = { 'mention': 'Mention', 'oxy-social': 'Oxy Social', 'oxy-workspace': 'Oxy Workspace', 'oxy-creator': 'Oxy Creator', 'oxy-analytics': 'Oxy Analytics', 'oxy-studio': 'Oxy Studio' }; return appNames[packageName] || packageName; }; return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: [styles.header, { borderBottomColor: borderColor }], children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, { style: styles.backButton, onPress: goBack, children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, { name: "arrow-back", size: 24, color: textColor }) }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.headerTitleContainer, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.headerTitle, { color: textColor }], children: "Oxy+ Subscriptions" }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, { style: [styles.currentAppText, { color: isDarkTheme ? '#BBBBBB' : '#666666' }], children: ["for ", getAppDisplayName(currentAppPackage)] })] }), onClose && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, { style: styles.closeButton, onPress: onClose, children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, { name: "close", size: 24, color: textColor }) })] }); }; const renderCurrentSubscription = () => { if (!subscription) return null; const currentPlan = getCurrentPlan(); if (!currentPlan) return null; const statusColor = subscription.status === 'active' ? successColor : subscription.status === 'trialing' ? warningColor : dangerColor; return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.section, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.sectionTitle, { color: textColor }], children: "Current Subscription" }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: [styles.currentSubscriptionCard, { backgroundColor: secondaryBackgroundColor, borderColor }], children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.subscriptionHeader, children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.planName, { color: textColor }], children: currentPlan.name }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, { style: [styles.planPrice, { color: primaryColor }], children: ["$", currentPlan.price, "/month"] })] }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, { style: [styles.statusBadge, { backgroundColor: statusColor }], children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: styles.statusText, children: subscription.status.toUpperCase() }) })] }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, { style: [styles.subscriptionDetail, { color: isDarkTheme ? '#BBBBBB' : '#666666' }], children: ["Renews on ", new Date(subscription.currentPeriodEnd).toLocaleDateString()] }), subscription.cancelAtPeriodEnd && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.cancelNotice, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, { name: "warning", size: 16, color: warningColor }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, { style: [styles.cancelText, { color: warningColor }], children: ["Subscription will cancel on ", new Date(subscription.currentPeriodEnd).toLocaleDateString()] })] }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.subscriptionActions, children: [subscription.cancelAtPeriodEnd ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, { style: [styles.actionButton, { backgroundColor: successColor }], onPress: handleReactivateSubscription, children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: styles.actionButtonText, children: "Reactivate" }) }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, { style: [styles.actionButton, { backgroundColor: dangerColor }], onPress: handleCancelSubscription, children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: styles.actionButtonText, children: "Cancel Subscription" }) }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, { style: [styles.actionButton, styles.secondaryButton, { borderColor }], onPress: () => navigate && navigate('BillingManagement'), children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.actionButtonText, { color: textColor }], children: "Manage Billing" }) })] })] })] }); }; const renderBillingToggle = () => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.section, children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.billingToggle, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, { style: [styles.billingOption, billingInterval === 'month' && { backgroundColor: primaryColor }], onPress: () => setBillingInterval('month'), children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.billingOptionText, { color: billingInterval === 'month' ? '#FFFFFF' : textColor }], children: "Monthly" }) }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, { style: [styles.billingOption, billingInterval === 'year' && { backgroundColor: primaryColor }], onPress: () => setBillingInterval('year'), children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.billingOptionText, { color: billingInterval === 'year' ? '#FFFFFF' : textColor }], children: "Yearly" }) })] }), billingInterval === 'year' && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.savingsText, { color: successColor }], children: "\uD83D\uDCB0 Save 20% with yearly billing" })] }); const renderPlanCard = plan => { const pricing = formatPrice(plan.price, plan.currency, plan.interval); const isSelected = selectedPlan === plan.id; const isCurrentPlan = subscription?.planId === plan.id; const isAppSpecific = plan.appScope === 'specific' && plan.applicableApps.length === 1; const isAvailableForCurrentApp = plan.applicableApps.includes(currentAppPackage); const getAppScopeText = () => { if (plan.appScope === 'ecosystem') { return 'Works across all Oxy apps'; } else if (isAppSpecific) { const appName = plan.applicableApps[0]; return `Exclusive to ${appName} app`; } else { return `Available in: ${plan.applicableApps.join(', ')}`; } }; const getAvailabilityStatus = () => { if (isAppSpecific && !isAvailableForCurrentApp) { const requiredApp = plan.applicableApps[0]; return { available: false, reason: `Only available in ${requiredApp} app` }; } return { available: true, reason: null }; }; const availability = getAvailabilityStatus(); return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: [styles.planCard, { backgroundColor: secondaryBackgroundColor, borderColor }, isSelected && { borderColor: primaryColor, borderWidth: 2 }, plan.isPopular && styles.popularPlan, !availability.available && { opacity: 0.6 }], children: [plan.isPopular && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, { style: [styles.popularBadge, { backgroundColor: primaryColor }], children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: styles.popularText, children: "MOST POPULAR" }) }), isAppSpecific && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, { style: [styles.appSpecificBadge, { backgroundColor: isAvailableForCurrentApp ? successColor : warningColor }], children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: styles.appSpecificText, children: isAvailableForCurrentApp ? 'App Exclusive' : 'Not Available' }) }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.planHeader, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.planName, { color: textColor }], children: plan.name }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.planDescription, { color: isDarkTheme ? '#BBBBBB' : '#666666' }], children: plan.description }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.planAppScope, { color: isDarkTheme ? '#888888' : '#999999' }], children: getAppScopeText() }), !availability.available && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.planRestrictionText, { color: dangerColor }], children: availability.reason })] }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.planPricing, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.planPrice, { color: textColor }], children: pricing.formatted }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, { style: [styles.planInterval, { color: isDarkTheme ? '#BBBBBB' : '#666666' }], children: ["per ", pricing.interval] })] }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, { style: styles.planFeatures, children: plan.features.map((feature, index) => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.featureItem, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, { name: "checkmark", size: 16, color: successColor }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.featureText, { color: textColor }], children: feature })] }, index)) }), isCurrentPlan ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, { style: [styles.currentPlanButton, { backgroundColor: successColor }], children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: styles.currentPlanText, children: "Current Plan" }) }) : !availability.available ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, { style: [styles.unavailablePlanButton, { backgroundColor: isDarkTheme ? '#444444' : '#E0E0E0' }], children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.unavailablePlanText, { color: isDarkTheme ? '#888888' : '#999999' }], children: "Not Available in Current App" }) }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, { style: [styles.selectPlanButton, { backgroundColor: plan.isPopular ? primaryColor : borderColor }], onPress: () => handleSubscribe(plan.id), disabled: processingPayment, children: processingPayment ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, { color: "#FFFFFF", size: "small" }) : /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, { style: [styles.selectPlanText, { color: plan.isPopular ? '#FFFFFF' : textColor }], children: ["Subscribe to ", plan.name] }) })] }, plan.id); }; const renderTabNavigation = () => /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, { style: styles.section, children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: [styles.tabContainer, { borderBottomColor: borderColor }], children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, { style: [styles.tab, activeTab === 'plans' && { borderBottomColor: primaryColor, borderBottomWidth: 2 }], onPress: () => setActiveTab('plans'), children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.tabText, { color: activeTab === 'plans' ? primaryColor : textColor }], children: "Full Plans" }) }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, { style: [styles.tab, activeTab === 'features' && { borderBottomColor: primaryColor, borderBottomWidth: 2 }], onPress: () => setActiveTab('features'), children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.tabText, { color: activeTab === 'features' ? primaryColor : textColor }], children: "Individual Features" }) })] }) }); const renderFeatureCard = feature => { const pricing = formatPrice(feature.price, feature.currency, feature.interval); const isSubscribed = feature.isSubscribed; const isIncludedInCurrentPlan = feature.isIncludedInCurrentPlan; const canPurchase = feature.canBePurchasedSeparately && !isIncludedInCurrentPlan; const getCategoryColor = category => { switch (category) { case 'analytics': return '#FF9500'; case 'customization': return '#5856D6'; case 'content': return '#30D158'; case 'networking': return '#007AFF'; case 'productivity': return '#FF3B30'; default: return primaryColor; } }; const getCategoryIcon = category => { switch (category) { case 'analytics': return 'analytics'; case 'customization': return 'color-palette'; case 'content': return 'document-text'; case 'networking': return 'people'; case 'productivity': return 'briefcase'; default: return 'star'; } }; const getAppScopeText = () => { if (feature.appScope === 'ecosystem') { return 'Available across all Oxy apps'; } else { return `Available in: ${feature.applicableApps.join(', ')}`; } }; return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: [styles.featureCard, { backgroundColor: secondaryBackgroundColor, borderColor }, isSubscribed && { borderColor: successColor, borderWidth: 2 }, isIncludedInCurrentPlan && { borderColor: primaryColor, borderWidth: 2 }], children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.featureHeader, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, { style: styles.featureIconContainer, children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, { name: getCategoryIcon(feature.category), size: 24, color: getCategoryColor(feature.category) }) }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.featureInfo, children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.featureNameRow, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.featureName, { color: textColor }], children: feature.name }), isIncludedInCurrentPlan && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, { style: [styles.includedBadge, { backgroundColor: primaryColor }], children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: styles.includedBadgeText, children: "Included" }) })] }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.featureDescription, { color: isDarkTheme ? '#BBBBBB' : '#666666' }], children: feature.description }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.appScopeText, { color: isDarkTheme ? '#888888' : '#999999' }], children: getAppScopeText() })] })] }), !isIncludedInCurrentPlan && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.featurePricing, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.featurePrice, { color: textColor }], children: pricing.formatted }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, { style: [styles.featureInterval, { color: isDarkTheme ? '#BBBBBB' : '#666666' }], children: ["per ", pricing.interval] })] }), isIncludedInCurrentPlan ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: [styles.includedInPlanButton, { backgroundColor: primaryColor }], children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, { name: "checkmark-circle", size: 16, color: "#FFFFFF" }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: styles.includedInPlanText, children: "Included in your plan" })] }) : isSubscribed ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.featureActions, children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: [styles.subscribedButton, { backgroundColor: successColor }], children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, { name: "checkmark", size: 16, color: "#FFFFFF" }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: styles.subscribedText, children: "Subscribed" })] }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, { style: [styles.unsubscribeButton, { borderColor: dangerColor }], onPress: () => handleFeatureUnsubscribe(feature.id), children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.unsubscribeText, { color: dangerColor }], children: "Unsubscribe" }) })] }) : canPurchase ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, { style: [styles.subscribeFeatureButton, { backgroundColor: primaryColor }], onPress: () => handleFeatureSubscribe(feature.id), disabled: processingPayment, children: processingPayment ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, { color: "#FFFFFF", size: "small" }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: styles.subscribeFeatureText, children: "Subscribe" }) }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, { style: [styles.unavailableButton, { backgroundColor: isDarkTheme ? '#444444' : '#E0E0E0' }], children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.unavailableText, { color: isDarkTheme ? '#888888' : '#999999' }], children: "Only available in subscription plans" }) })] }, feature.id); }; const renderIndividualFeatures = () => { const categories = ['analytics', 'customization', 'content', 'networking', 'productivity']; return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.section, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.sectionTitle, { color: textColor }], children: "Individual Features" }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.sectionSubtitle, { color: isDarkTheme ? '#BBBBBB' : '#666666' }], children: "Subscribe to specific features you need. Some features are included in subscription plans." }), categories.map(category => { const categoryFeatures = individualFeatures.filter(f => f.category === category); if (categoryFeatures.length === 0) return null; return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.categorySection, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.categoryTitle, { color: textColor }], children: category.charAt(0).toUpperCase() + category.slice(1) }), categoryFeatures.map(renderFeatureCard)] }, category); })] }); }; // Add this for testing different app contexts (remove in production) const [showAppSwitcher, setShowAppSwitcher] = (0, _react.useState)(__DEV__); // Only show in development const renderAppSwitcher = () => { if (!showAppSwitcher) return null; const testApps = ['mention', 'oxy-social', 'oxy-workspace', 'oxy-creator']; return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: [styles.appSwitcher, { backgroundColor: isDarkTheme ? '#333333' : '#F0F0F0', borderColor }], children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.appSwitcherTitle, { color: textColor }], children: "\uD83E\uDDEA Test App Context (Dev Only)" }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, { style: styles.appSwitcherButtons, children: testApps.map(app => /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, { style: [styles.appSwitcherButton, { backgroundColor: currentAppPackage === app ? primaryColor : 'transparent', borderColor: primaryColor }], onPress: () => { setCurrentAppPackage(app); console.log('Switched to app context:', app); }, children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.appSwitcherButtonText, { color: currentAppPackage === app ? '#FFFFFF' : textColor }], children: app }) }, app)) }) })] }); }; if (loading) { return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: [styles.container, { backgroundColor, justifyContent: 'center' }], children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, { size: "large", color: primaryColor }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.loadingText, { color: textColor }], children: "Loading subscription plans..." })] }); } return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: [styles.container, { backgroundColor }], children: [renderHeader(), renderAppSwitcher(), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.ScrollView, { style: styles.content, showsVerticalScrollIndicator: false, children: [subscription && renderCurrentSubscription(), !subscription && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.section, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.sectionTitle, { color: textColor }], children: "Choose Your Plan" }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.sectionSubtitle, { color: isDarkTheme ? '#BBBBBB' : '#666666' }], children: "Unlock premium features and take your experience to the next level" })] }), !subscription && renderTabNavigation(), !subscription && activeTab === 'plans' && renderBillingToggle(), activeTab === 'plans' ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.section, children: [!subscription && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.sectionTitle, { color: textColor }], children: "Available Plans" }), plans.map(renderPlanCard)] }) : renderIndividualFeatures(), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.section, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.sectionTitle, { color: textColor }], children: "Why Go Premium?" }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: [styles.benefitsCard, { backgroundColor: secondaryBackgroundColor, borderColor }], children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.benefitItem, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, { name: "flash", size: 24, color: primaryColor }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.benefitContent, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.benefitTitle, { color: textColor }], children: "Enhanced Performance" }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.benefitDescription, { color: isDarkTheme ? '#BBBBBB' : '#666666' }], children: "Faster processing and priority access to our servers" })] })] }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.benefitItem, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, { name: "shield-checkmark", size: 24, color: successColor }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.benefitContent, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.benefitTitle, { color: textColor }], children: "Advanced Security" }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.benefitDescription, { color: isDarkTheme ? '#BBBBBB' : '#666666' }], children: "Enhanced encryption and security features" })] })] }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.benefitItem, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, { name: "headset", size: 24, color: warningColor }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.benefitContent, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.benefitTitle, { color: textColor }], children: "Priority Support" }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.benefitDescription, { color: isDarkTheme ? '#BBBBBB' : '#666666' }], children: "Get help faster with our premium support team" })] })] })] })] }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, { style: styles.bottomSpacing })] })] }); }; const styles = _reactNative.StyleSheet.create({ container: { flex: 1 }, header: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingHorizontal: 20, paddingTop: 20, paddingBottom: 16, borderBottomWidth: 1 }, backButton: { padding: 8 }, headerTitle: { fontSize: 20, fontWeight: '600', fontFamily: _fonts.fontFamilies.phuduSemiBold }, headerTitleContainer: { flex: 1, alignItems: 'center' }, currentAppText: { fontSize: 14, marginTop: 2, fontStyle: 'italic' }, closeButton: { padding: 8 }, content: { flex: 1 }, section: { padding: 20 }, sectionTitle: { fontSize: 24, fontWeight: 'bold', fontFamily: _fonts.fontFamilies.phuduBold, marginBottom: 8 }, sectionSubtitle: { fontSize: 16, lineHeight: 22, marginBottom: 20 }, loadingText: { fontSize: 16, textAlign: 'center', marginTop: 16 }, currentSubscriptionCard: { borderRadius: 16, padding: 20, borderWidth: 1 }, subscriptionHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 12 }, planName: { fontSize: 20, fontWeight: 'bold', marginBottom: 4 }, planPrice: { fontSize: 16, fontWeight: '600' }, statusBadge: { paddingHorizontal: 12, paddingVertical: 4, borderRadius: 12 }, statusText: { color: '#FFFFFF', fontSize: 12, fontWeight: '600' }, subscriptionDetail: { fontSize: 14, marginBottom: 16 }, cancelNotice: { flexDirection: 'row', alignItems: 'center', padding: 12, backgroundColor: 'rgba(255, 149, 0, 0.1)', borderRadius: 8, marginBo