UNPKG

@oxyhq/services

Version:

OxyHQ Expo/React Native SDK — UI components, screens, and native features

253 lines (243 loc) 7.99 kB
"use strict"; import React, { useState, useRef, useMemo, useCallback } from 'react'; import { View, Text, ScrollView, Animated, Platform, useWindowDimensions } from 'react-native'; import { useThemeColors } from "../styles/index.js"; import { normalizeTheme } from "../utils/themeUtils.js"; import GroupedPillButtons from "../components/internal/GroupedPillButtons.js"; import { useThemeStyles } from "../hooks/useThemeStyles.js"; import QRCode from 'react-native-qrcode-svg'; import { PaymentSummaryStep, PaymentMethodStep, PaymentDetailsStep, PaymentReviewStep, PaymentSuccessStep, PAYMENT_METHODS, createPaymentStyles } from "../components/payment/index.js"; import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; const getUniqueItemTypes = items => { const types = items.map(item => item.type); return Array.from(new Set(types)); }; const PaymentGatewayScreen = props => { const { navigate, goBack, theme, onPaymentResult, amount, currency = 'FAIR', onClose, paymentItems = [], description = '' } = props; // DEV ENFORCEMENT: Only allow one type of payment item if (process.env.NODE_ENV !== 'production' && paymentItems.length > 0) { const uniqueTypes = getUniqueItemTypes(paymentItems); if (uniqueTypes.length > 1) { throw new Error(`PaymentGatewayScreen: paymentItems contains mixed types (${uniqueTypes.join(', ')}). Only one type is allowed per payment.`); } } // Step states const [currentStep, setCurrentStep] = useState(0); const [paymentMethod, setPaymentMethod] = useState('card'); const [cardDetails, setCardDetails] = useState({ number: '', expiry: '', cvv: '' }); const [isPaying, setIsPaying] = useState(false); // Animations const fadeAnim = useRef(new Animated.Value(1)).current; const slideAnim = useRef(new Animated.Value(0)).current; const scaleAnim = useRef(new Animated.Value(1)).current; const progressAnim = useRef(new Animated.Value(0.2)).current; const normalizedTheme = normalizeTheme(theme); const colors = useThemeColors(normalizedTheme); const themeStyles = useThemeStyles(normalizedTheme); const styles = useMemo(() => createPaymentStyles(colors), [colors]); // Determine if the payment is for a recurring item (subscription) const isRecurring = paymentItems.length > 0 && paymentItems[0].type === 'subscription'; // Filter payment methods: remove 'faircoin' if recurring const availablePaymentMethods = useMemo(() => { if (isRecurring) { return PAYMENT_METHODS.filter(m => m.key !== 'faircoin'); } return PAYMENT_METHODS; }, [isRecurring]); // Animation transitions const animateTransition = useCallback(nextStep => { Animated.timing(scaleAnim, { toValue: 0.95, duration: 150, useNativeDriver: Platform.OS !== 'web' }).start(); Animated.timing(fadeAnim, { toValue: 0, duration: 200, useNativeDriver: Platform.OS !== 'web' }).start(() => { setCurrentStep(nextStep); slideAnim.setValue(-50); scaleAnim.setValue(0.95); Animated.parallel([Animated.timing(fadeAnim, { toValue: 1, duration: 300, useNativeDriver: Platform.OS !== 'web' }), Animated.spring(slideAnim, { toValue: 0, tension: 80, friction: 8, useNativeDriver: Platform.OS !== 'web' }), Animated.spring(scaleAnim, { toValue: 1, tension: 80, friction: 8, useNativeDriver: Platform.OS !== 'web' })]).start(); }); }, [fadeAnim, slideAnim, scaleAnim]); const nextStep = useCallback(() => { if (currentStep < 4) { Animated.timing(progressAnim, { toValue: (currentStep + 2) / 5, duration: 300, useNativeDriver: false }).start(); animateTransition(currentStep + 1); } }, [currentStep, progressAnim, animateTransition]); const prevStep = useCallback(() => { if (currentStep > 0) { Animated.timing(progressAnim, { toValue: currentStep / 5, duration: 300, useNativeDriver: false }).start(); animateTransition(currentStep - 1); } }, [currentStep, progressAnim, animateTransition]); // Pay handler - TODO: Replace with actual payment API const handlePay = useCallback(() => { setIsPaying(true); setTimeout(() => { setIsPaying(false); nextStep(); }, 1500); }, [nextStep]); const handleDone = useCallback(() => { if (onPaymentResult) { onPaymentResult({ success: true }); } navigate?.('AccountOverview'); }, [onPaymentResult, navigate]); const handleClose = useCallback(() => { if (onPaymentResult) { onPaymentResult({ success: false, error: 'cancelled' }); } if (onClose) { onClose(); } else if (goBack) { goBack(); } }, [onPaymentResult, onClose, goBack]); // Validate amount if (!amount || isNaN(Number(amount)) || Number(amount) <= 0) { return /*#__PURE__*/_jsxs(View, { style: styles.errorContainer, children: [/*#__PURE__*/_jsx(Text, { style: styles.errorText, children: "Invalid or missing payment amount." }), /*#__PURE__*/_jsx(GroupedPillButtons, { buttons: [{ text: 'Close', onPress: handleClose, icon: 'close', variant: 'primary' }], colors: colors })] }); } // FairCoin address - TODO: Replace with dynamic address from backend const faircoinAddress = 'f1abc1234FAIRCOINADDRESS'; const { width: windowWidth } = useWindowDimensions(); const isMobile = windowWidth < 600; const qrSize = !isMobile ? Math.min(windowWidth * 0.3, 220) : Math.min(windowWidth * 0.8, 300); const animations = { fadeAnim, slideAnim, scaleAnim }; const renderCurrentStep = () => { switch (currentStep) { case 0: return /*#__PURE__*/_jsx(PaymentSummaryStep, { paymentItems: paymentItems, amount: amount, currency: currency, description: description, colors: colors, animations: animations, onClose: handleClose, onNext: nextStep }); case 1: return /*#__PURE__*/_jsx(PaymentMethodStep, { availablePaymentMethods: availablePaymentMethods, selectedMethod: paymentMethod, onSelectMethod: setPaymentMethod, colors: colors, animations: animations, onBack: prevStep, onNext: nextStep }); case 2: return /*#__PURE__*/_jsx(PaymentDetailsStep, { paymentMethod: paymentMethod, cardDetails: cardDetails, onCardDetailsChange: setCardDetails, colors: colors, animations: animations, faircoinAddress: faircoinAddress, isMobile: isMobile, qrSize: qrSize, onBack: prevStep, onNext: nextStep, QRCodeComponent: QRCode }); case 3: return /*#__PURE__*/_jsx(PaymentReviewStep, { amount: amount, currency: currency, paymentMethod: paymentMethod, cardDetails: cardDetails, colors: colors, animations: animations, isPaying: isPaying, onBack: prevStep, onPay: handlePay }); case 4: return /*#__PURE__*/_jsx(PaymentSuccessStep, { colors: colors, animations: animations, onDone: handleDone }); default: return null; } }; return /*#__PURE__*/_jsx(View, { style: [styles.container, { backgroundColor: themeStyles.backgroundColor }], children: /*#__PURE__*/_jsx(ScrollView, { style: styles.content, showsVerticalScrollIndicator: false, children: renderCurrentStep() }) }); }; export default PaymentGatewayScreen; //# sourceMappingURL=PaymentGatewayScreen.js.map