You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1821 lines
51 KiB

2 months ago
// 支付组件
3 weeks ago
import React, { useState, useEffect, useCallback, useRef } from "react";
2 months ago
import {
View,
Text,
StyleSheet,
ScrollView,
Image,
TouchableOpacity,
SafeAreaView,
3 weeks ago
ActivityIndicator,
Alert,
TextInput,
2 months ago
} from "react-native";
3 weeks ago
import fontSize from "../../utils/fontsizeUtils";
import widthUtils from "../../utils/widthUtils";
import CircleOutlineIcon from "../../components/CircleOutlineIcon";
import CloseIcon from "../../components/CloseIcon";
import CheckIcon from "../../components/CheckIcon";
import PhoneNumberInputModal from "./PhoneNumberInputModal";
import useUserStore from "../../store/user";
3 weeks ago
// 添加导航相关导入
import { useNavigation } from "@react-navigation/native";
3 weeks ago
// 添加API服务
3 weeks ago
import {
payApi,
RechargeRecommendAmountResponse,
PaymentMethod,
} from "../../services/api/payApi";
import payMap from "../../utils/payMap";
2 weeks ago
import { useTranslation } from "react-i18next";
3 weeks ago
2 months ago
interface RechargeScreenProps {
onClose: () => void;
}
3 weeks ago
const RechargeScreen = ({ onClose }: RechargeScreenProps) => {
2 weeks ago
const { t } = useTranslation();
3 weeks ago
const [selectedPrice, setSelectedPrice] = useState<string>("");
2 months ago
const [selectedOperator, setSelectedOperator] = useState<string | null>(null);
const [activeTab, setActiveTab] = useState(0);
3 weeks ago
const [recommendedAmounts, setRecommendedAmounts] =
useState<RechargeRecommendAmountResponse>();
const [showCustomAmountInput, setShowCustomAmountInput] = useState(false);
const [customAmount, setCustomAmount] = useState("");
const [customAmountDisplayText, setCustomAmountDisplayText] = useState("");
const [paymentMethods, setPaymentMethods] = useState<PaymentMethod[]>([]);
const customAmountInputRef = useRef<TextInput>(null);
3 weeks ago
// 添加货币转换相关状态
const [isConverting, setIsConverting] = useState(false);
3 weeks ago
const { user } = useUserStore();
3 weeks ago
// 指定导航类型为any
const navigation = useNavigation<any>();
const [convertedAmount, setConvertedAmount] = useState<
{
converted_amount: number;
item_key: string;
original_amount: number;
}[]
>([]);
3 weeks ago
const [currentCurrency, setCurrentCurrency] = useState("USD");
const [isSubmitting, setIsSubmitting] = useState(false);
const [paymentParams, setPaymentParams] = useState<{
originalAmount: number;
amount: number;
currency: string;
payment_method: string;
selectedPriceLabel: string;
3 weeks ago
onCloses: () => void;
3 weeks ago
} | null>(null);
3 weeks ago
const [showPhoneModal, setShowPhoneModal] = useState(false);
// 添加PayPal展开视图的状态
const [isPaypalExpanded, setIsPaypalExpanded] = useState(false);
useEffect(() => {
payApi.getRechargeRecommendAmount().then((res) => {
setRecommendedAmounts(res);
// Set the first amount as the default selected price if available
if (res && res.amounts && res.amounts.length > 0) {
setSelectedPrice(res.amounts[0].toString());
}
});
payApi.getCountryPaymentMethods().then((res) => {
2 weeks ago
const currentCountryMethods = res.current_country_methods.filter(
(method) => method.key !== "balance"
);
setPaymentMethods(currentCountryMethods);
3 weeks ago
});
}, []);
2 months ago
const handlePriceSelect = (price: string) => {
setSelectedPrice(price);
3 weeks ago
setShowCustomAmountInput(false);
setCustomAmountDisplayText("");
3 weeks ago
// 如果当前已选择了Paypal支付方式,则重新计算转换后的金额
if (selectedOperator === "paypal") {
handleCurrencyConversion(price, currentCurrency);
}
// 保留原有的逻辑
2 weeks ago
else if (selectedOperator === "currency") {
3 weeks ago
handleCurrencyConversion(price, currentCurrency);
}
2 months ago
};
const handleOperatorSelect = (operator: string) => {
3 weeks ago
// 如果选择的不是之前选中的支付方式,则重置PayPal展开状态
if (operator !== selectedOperator) {
setIsPaypalExpanded(false);
}
2 months ago
setSelectedOperator(operator === selectedOperator ? null : operator);
3 weeks ago
// 查找是否选择了PayPal支付方式
const isPaypal = paymentMethods.some(
(method) => method.key === operator && method.key === "paypal"
);
console.log(isPaypal);
// 如果选择了PayPal支付方式,立即进行货币转换并设置展开状态
if (isPaypal && operator !== selectedOperator) {
setIsPaypalExpanded(true); // 设置PayPal展开状态为true
// 无条件触发货币转换,使用默认的USD
let amountToConvert = selectedPrice;
// 如果用户还没有选择金额,使用推荐金额中的第一个
if (
!amountToConvert &&
recommendedAmounts &&
recommendedAmounts.amounts &&
recommendedAmounts.amounts.length > 0
) {
amountToConvert = recommendedAmounts.amounts[0].toString();
// 自动选择第一个推荐金额
setSelectedPrice(amountToConvert);
}
if (amountToConvert) {
setCurrentCurrency("USD");
setIsConverting(true);
handleCurrencyConversion(amountToConvert, "USD");
}
} else if (operator === "currency" && operator !== selectedOperator) {
// 旧的逻辑保留作为备用
3 weeks ago
handleCurrencySelect("USD");
}
2 months ago
};
3 weeks ago
const handleCustomAmountChange = (text: string) => {
// Remove commas and non-numeric characters except for the first decimal point
const formattedText = text.replace(/,/g, "").replace(/[^0-9.]/g, "");
setCustomAmount(formattedText);
};
const handleCustomAmountSubmit = () => {
if (customAmount && customAmount.trim() !== "") {
// Format the custom amount with commas
const numericAmount = parseFloat(customAmount);
if (!isNaN(numericAmount) && numericAmount > 0) {
const formattedAmount = numericAmount.toLocaleString();
// 设置自定义金额作为显示文本
setCustomAmountDisplayText(formattedAmount);
// 设置充值金额为用户输入的金额,但不选中任何预设金额按钮
setSelectedPrice(formattedAmount);
// 如果当前已选择了Paypal支付方式,则重新计算转换后的金额
if (selectedOperator === "paypal") {
handleCurrencyConversion(formattedAmount, currentCurrency);
}
// 保留原有的逻辑
2 weeks ago
else if (selectedOperator === "currency") {
3 weeks ago
handleCurrencyConversion(formattedAmount, currentCurrency);
}
}
}
setShowCustomAmountInput(false);
};
const toggleCustomAmountInput = () => {
setShowCustomAmountInput(!showCustomAmountInput);
// If opening the input, clear previous value and focus
if (!showCustomAmountInput) {
setCustomAmount("");
// Focus the input after a short delay to ensure it's visible
setTimeout(() => {
customAmountInputRef.current?.focus();
}, 100);
} else {
// 如果关闭输入框,则保存当前输入的金额
handleCustomAmountSubmit();
}
2 months ago
};
const handleButtonClick = () => {
if (selectedOperator) {
3 weeks ago
// 准备支付参数,方便后续发送
const params = {
originalAmount: parseFloat(selectedPrice.replace(/,/g, "")),
amount: parseFloat(selectedPrice.replace(/,/g, "")),
3 weeks ago
currency: user?.currency,
3 weeks ago
payment_method: "",
3 weeks ago
selectedPriceLabel: selectedPrice + " " + user?.currency,
onCloses: () => onClose(), // Close parent modal
3 weeks ago
};
3 weeks ago
// 根据selectedOperator确定支付方式
3 weeks ago
// 查找选中的支付方式
const selectedMethod = paymentMethods.find(
(method) => method.key === selectedOperator
);
if (selectedMethod) {
// 使用key作为支付方式标识,而不是不存在的name属性
params.payment_method = selectedMethod.key;
// 如果是paypal,设置货币转换相关参数
if (selectedMethod.key === "paypal") {
params.currency = currentCurrency;
// 使用转换后的金额,如果有
if (convertedAmount.length > 0) {
const convertedTotal = convertedAmount.find(
(item) => item.item_key === "total_amount"
);
if (convertedTotal) {
params.amount = convertedTotal.converted_amount;
}
}
}
3 weeks ago
} else if (selectedOperator === "balance") {
params.payment_method = "Balance";
} else if (selectedOperator === "currency") {
3 weeks ago
// 当选择了货币转换时(这是旧的处理逻辑,保留作为备用)
3 weeks ago
params.payment_method = "paypal";
params.currency = currentCurrency; // 使用选择的货币
3 weeks ago
// 使用转换后的金额,如果有
if (convertedAmount.length > 0) {
const convertedTotal = convertedAmount.find(
(item) => item.item_key === "total_amount"
);
3 weeks ago
if (convertedTotal) {
params.amount = convertedTotal.converted_amount;
}
}
}
3 weeks ago
// 保存支付参数
setPaymentParams(params);
3 weeks ago
setShowPhoneModal(true);
2 months ago
}
};
3 weeks ago
// 提取一个专门用于货币转换的函数
const handleCurrencyConversion = (price: string, currency: string) => {
setIsConverting(true);
3 weeks ago
// 格式化金额,去除逗号
const amount = parseFloat(price.replace(/,/g, ""));
3 weeks ago
// 如果金额为0或无效,则不进行转换
if (!amount || isNaN(amount)) {
console.warn("Invalid amount for currency conversion");
setIsConverting(false);
return;
}
3 weeks ago
console.log(`Converting ${amount} FCFA to ${currency}...`);
3 weeks ago
// 调用货币转换API
const data = {
3 weeks ago
from_currency: user?.currency,
3 weeks ago
to_currency: currency,
amounts: {
total_amount: amount,
domestic_shipping_fee: 0,
shipping_fee: 0,
},
};
payApi
.convertCurrency(data)
3 weeks ago
.then((res) => {
if (
res &&
res.converted_amounts_list &&
res.converted_amounts_list.length > 0
) {
3 weeks ago
console.log("Conversion successful:", res.converted_amounts_list);
setConvertedAmount(res.converted_amounts_list);
} else {
console.error("Conversion response invalid:", res);
// 使用近似汇率作为备用
const fallbackRate = currency === "USD" ? 580.0 : 655.96; // 1 USD = 580 FCFA, 1 EUR = 655.96 FCFA
const convertedValue = amount / fallbackRate;
setConvertedAmount([
{
converted_amount: convertedValue,
item_key: "total_amount",
original_amount: amount,
},
]);
3 weeks ago
}
})
.catch((error) => {
console.error("Currency conversion failed:", error);
3 weeks ago
// 使用近似汇率作为备用
const fallbackRate = currency === "USD" ? 580.0 : 655.96;
const convertedValue = amount / fallbackRate;
setConvertedAmount([
{
converted_amount: convertedValue,
item_key: "total_amount",
original_amount: amount,
},
]);
3 weeks ago
})
.finally(() => {
setIsConverting(false);
});
};
// 修改货币选择函数,调用通用的转换函数
const handleCurrencySelect = (currency: string) => {
3 weeks ago
// 如果货币没有变化,则不重新计算
if (currency === currentCurrency) {
return;
}
3 weeks ago
setCurrentCurrency(currency);
3 weeks ago
// 确保我们有选定的金额可以转换
if (selectedPrice) {
// 显示转换中状态
setIsConverting(true);
handleCurrencyConversion(selectedPrice, currency);
}
3 weeks ago
};
3 weeks ago
// 处理支付提交的函数,现在作为回调传递给PhoneNumberInputModal
const handlePaySubmit = async (phoneNumber: string) => {
3 weeks ago
if (!paymentParams) {
return;
}
3 weeks ago
// 验证电话号码(添加更严格的验证)
if (!phoneNumber || phoneNumber.length < 8) {
Alert.alert(
"Erreur",
"Veuillez entrer un numéro de téléphone valide (au moins 8 chiffres)"
);
3 weeks ago
return;
}
3 weeks ago
// 显示提交中状态
setIsSubmitting(true);
3 weeks ago
try {
// 准备请求数据,添加电话号码
const rechargeData = {
amount: paymentParams.amount,
currency: paymentParams.currency,
payment_method: paymentParams.payment_method,
3 weeks ago
phone: phoneNumber,
3 weeks ago
};
3 weeks ago
console.log("Submitting recharge request:", rechargeData);
3 weeks ago
// 调用充值接口(使用可选链避免错误)
const response = await payApi.initiateRecharge(rechargeData);
if (response && response.success) {
const paymentInfo = response.payment;
3 weeks ago
// 检查是否有支付URL
if (paymentInfo && paymentInfo.payment_url) {
3 weeks ago
setShowPhoneModal(false);
onClose();
3 weeks ago
// 打开支付页面
setTimeout(() => {
navigation.navigate("Pay", {
payUrl: paymentInfo.payment_url,
});
}, 1000);
3 weeks ago
} else {
Alert.alert("Succès", "Votre recharge a été traitée avec succès!", [
3 weeks ago
{
text: "OK",
onPress: () => {
setShowPhoneModal(false);
onClose();
},
},
]);
3 weeks ago
}
} else {
// 处理失败情况,显示错误消息
const errorMessage =
response?.msg ||
"Une erreur s'est produite lors du traitement de la recharge. Veuillez réessayer.";
Alert.alert("Erreur", errorMessage);
3 weeks ago
}
} catch (error) {
// 处理异常
console.error("Recharge error:", error);
let errorMessage =
"Une erreur s'est produite lors du traitement de la recharge. Veuillez réessayer.";
3 weeks ago
// 尝试从错误对象中提取更具体的错误信息
if (error instanceof Error) {
errorMessage = error.message || errorMessage;
}
Alert.alert("Erreur", errorMessage);
3 weeks ago
} finally {
// 无论成功失败,都取消提交状态
setIsSubmitting(false);
}
};
3 weeks ago
// 添加一个函数来判断确认按钮是否应该被禁用
const isConfirmButtonDisabled = () => {
// 如果没有选择支付方式,禁用按钮
if (!selectedOperator) {
return true;
}
// 如果没有选择金额,禁用按钮
if (!selectedPrice) {
return true;
}
// 如果正在进行货币转换,禁用按钮
if (isConverting) {
return true;
}
// 如果选择了Paypal支付方式,但还没有转换结果,禁用按钮
if (
selectedOperator === "paypal" &&
(convertedAmount.length === 0 ||
!convertedAmount.find((item) => item.item_key === "total_amount"))
) {
return true;
}
// 其他情况下,启用按钮
return false;
};
2 months ago
return (
<SafeAreaView style={styles.safeArea}>
3 weeks ago
<View style={styles.mainContainer}>
2 months ago
<View style={styles.header}>
2 weeks ago
<Text style={styles.title}>{t("balance.recharge.title")}</Text>
3 weeks ago
<TouchableOpacity onPress={onClose} style={styles.closeButton}>
<CloseIcon size={fontSize(15)} />
</TouchableOpacity>
2 months ago
</View>
3 weeks ago
<ScrollView contentContainerStyle={styles.scrollContent}>
<View style={styles.container2}>
<TouchableOpacity
style={styles.amountRechargeContainer}
onPress={toggleCustomAmountInput}
>
{showCustomAmountInput ? (
<View style={styles.customAmountInputContainer}>
<TextInput
ref={customAmountInputRef}
style={styles.customAmountInput}
value={customAmount}
onChangeText={handleCustomAmountChange}
keyboardType="numeric"
2 weeks ago
placeholder={t("balance.recharge.choose_amount")}
3 weeks ago
placeholderTextColor="#999"
onSubmitEditing={handleCustomAmountSubmit}
onBlur={handleCustomAmountSubmit}
autoFocus
/>
<Text style={styles.currencyLabel}>{user?.currency}</Text>
2 months ago
</View>
3 weeks ago
) : (
<Text
style={
customAmountDisplayText
? styles.customAmountText
: styles.rechargePromptTextStyle
}
>
{customAmountDisplayText ||
2 weeks ago
t("balance.recharge.choose_amount")}
3 weeks ago
</Text>
)}
</TouchableOpacity>
</View>
2 months ago
3 weeks ago
{/* 金额选择 */}
<View style={styles.priceGroup}>
{recommendedAmounts && recommendedAmounts.amounts && (
<>
2 months ago
<View style={styles.row}>
3 weeks ago
{recommendedAmounts.amounts
.slice(0, 3)
.map((amount, index) => {
// 如果有自定义金额,则不显示任何选中状态
const isSelected = customAmountDisplayText
? false
: selectedPrice === amount.toString();
return (
<TouchableOpacity
key={index}
style={[
index === 0
? styles.priceBoxBlue
: styles.priceBoxWhite,
isSelected
? styles.priceBoxSelected
: styles.priceBoxUnselected,
]}
onPress={() => {
setCustomAmountDisplayText("");
handlePriceSelect(amount.toString());
}}
>
<Text
style={[
index === 0
? styles.priceTextBlue
: styles.priceText,
isSelected
? styles.priceTextSelected
: styles.priceTextUnselected,
]}
>
{amount.toLocaleString()}
</Text>
<Text
style={[
index === 0
? styles.currencyTextBlue
: styles.currencyText,
isSelected
? styles.currencyTextSelected
: styles.currencyTextUnselected,
]}
>
{recommendedAmounts.currency || "FCFA"}
</Text>
</TouchableOpacity>
);
})}
2 months ago
</View>
3 weeks ago
{recommendedAmounts.amounts.length > 3 && (
<View style={styles.row}>
{recommendedAmounts.amounts
.slice(3)
.map((amount, index) => {
// 如果有自定义金额,则不显示任何选中状态
const isSelected = customAmountDisplayText
? false
: selectedPrice === amount.toString();
2 months ago
3 weeks ago
return (
<TouchableOpacity
key={index + 3}
style={[
styles.priceBoxWhite,
isSelected
? styles.priceBoxSelected
: styles.priceBoxUnselected,
]}
onPress={() => {
setCustomAmountDisplayText("");
handlePriceSelect(amount.toString());
}}
>
<Text
style={[
styles.priceText,
isSelected
? styles.priceTextSelected
: styles.priceTextUnselected,
]}
>
{amount.toLocaleString()}
</Text>
<Text
style={[
styles.currencyText,
isSelected
? styles.currencyTextSelected
: styles.currencyTextUnselected,
]}
>
{recommendedAmounts.currency || "FCFA"}
</Text>
</TouchableOpacity>
);
})}
</View>
)}
</>
)}
</View>
2 months ago
3 weeks ago
{/* 支付方式标题 */}
<View style={styles.section}>
<Text style={styles.subtitle}>
2 weeks ago
{t("balance.recharge.payment_method")}
3 weeks ago
</Text>
</View>
2 months ago
3 weeks ago
{/* Tab Bar */}
<View style={styles.tabContainer}>
<TouchableOpacity
style={[styles.tab, activeTab === 0 && styles.activeTab]}
onPress={() => setActiveTab(0)}
>
<Text
style={[
styles.tabText,
activeTab === 0 && styles.activeTabText,
]}
>
2 weeks ago
{t("balance.recharge.payment_mode")}
3 weeks ago
</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.tab, activeTab === 1 && styles.activeTab]}
onPress={() => setActiveTab(1)}
>
<Text
style={[
styles.tabText,
activeTab === 1 && styles.activeTabText,
]}
>
2 weeks ago
{t("balance.recharge.other")}
3 weeks ago
</Text>
</TouchableOpacity>
</View>
{/* Tab Content */}
{activeTab === 0 ? (
<>
{/* 显示所有在线支付方式 */}
{paymentMethods.map((method, index) => (
<View key={index}>
<View
style={
method.key === "balance"
? styles.balanceInfoContainer
: styles.cardContainer
}
>
{method.key === "balance" ? (
// 余额支付特殊显示
<View style={styles.leftInfo}>
<View style={styles.blueBox}>
<Image
source={payMap(method.key) as any}
style={styles.operatorImage}
/>
</View>
<Text style={styles.balanceText}>
2 weeks ago
{t("balance.recharge.balance_remaining")}
{"\n"}
{user.balance}
{user.currency}
3 weeks ago
</Text>
</View>
) : (
// 普通支付方式
<View style={styles.iconRow}>
2 weeks ago
<View style={styles.imageContainer}>
<Image
source={payMap(method.key) as any}
style={styles.operatorImage}
/>
<View style={styles.mobileMoneyTextContainer}>
{method.key === "mobile_money" &&
(Array.isArray(method.value) ? (
method.value.map((item, index) => (
<View style={styles.mobileMoneyImgContainer} key={index}>
<Image
key={index}
source={payMap(item) as any}
style={styles.mobileMoneyImg}
/>
</View>
))
) : (
<Text style={styles.mobileMoneyText}>1234</Text>
))}
</View>
</View>
2 months ago
</View>
3 weeks ago
)}
2 months ago
{/* 右侧圆圈图标 */}
<TouchableOpacity
3 weeks ago
onPress={() => handleOperatorSelect(method.key)}
>
2 months ago
<View style={styles.checkboxContainer}>
<CircleOutlineIcon
size={fontSize(24)}
strokeColor={
3 weeks ago
selectedOperator === method.key
? "#007efa"
: undefined
}
fillColor={
3 weeks ago
selectedOperator === method.key
? "#007efa"
: undefined
}
2 months ago
/>
3 weeks ago
{selectedOperator === method.key && (
2 months ago
<View style={styles.checkmarkContainer}>
<CheckIcon size={fontSize(12)} color="#FFFFFF" />
</View>
)}
</View>
</TouchableOpacity>
</View>
3 weeks ago
{/* PayPal展开视图 */}
{method.key === "paypal" &&
selectedOperator === "paypal" &&
isPaypalExpanded && (
<View style={styles.paypalExpandedContainer}>
<View style={styles.paypalCurrencyContainer}>
<Text style={styles.currencyTitle}>
2 weeks ago
{t("balance.recharge.currency_title")}
3 weeks ago
</Text>
<View style={styles.currencyButtonsContainer}>
<TouchableOpacity
style={[
styles.currencyButton,
currentCurrency === "USD" &&
styles.currencyButtonActive,
]}
onPress={() => handleCurrencySelect("USD")}
>
<Text
style={[
styles.currencyButtonText,
currentCurrency === "USD" &&
styles.currencyButtonTextActive,
]}
>
USD
</Text>
</TouchableOpacity>
<TouchableOpacity
style={[
styles.currencyButton,
currentCurrency === "EUR" &&
styles.currencyButtonActive,
]}
onPress={() => handleCurrencySelect("EUR")}
>
<Text
style={[
styles.currencyButtonText,
currentCurrency === "EUR" &&
styles.currencyButtonTextActive,
]}
>
EUR
</Text>
</TouchableOpacity>
</View>
{/* 显示转换后的金额 */}
{isConverting ? (
<View style={styles.convertingContainer}>
<ActivityIndicator size="small" color="#007efa" />
<Text style={styles.convertingText}>
2 weeks ago
{t("balance.recharge.converting")}
3 weeks ago
</Text>
</View>
) : convertedAmount.length > 0 ? (
<View style={styles.convertedAmountContainer}>
<Text style={styles.convertedAmountLabel}>
2 weeks ago
{t("balance.recharge.equivalent_amount")}
3 weeks ago
</Text>
<Text style={styles.convertedAmountValue}>
{convertedAmount
.find(
(item) => item.item_key === "total_amount"
)
?.converted_amount.toFixed(2)}{" "}
{currentCurrency}
</Text>
</View>
) : null}
</View>
2 months ago
</View>
3 weeks ago
)}
</View>
))}
</>
) : (
<View style={styles.outerContainer}>
<View style={styles.flexContainer}>
<View style={styles.imageContainer}>
<Image
source={require("../../../assets/img/image_c6aa9539.png")}
style={styles.imageStyle}
resizeMode="cover"
/>
</View>
<View style={styles.verticalAlignEndContent}>
<View style={styles.svgContainer}>
<TouchableOpacity
3 weeks ago
onPress={() => handleOperatorSelect("mtn")}
>
2 months ago
<View style={styles.checkboxContainer}>
<CircleOutlineIcon
size={fontSize(24)}
strokeColor={
3 weeks ago
selectedOperator === "mtn" ? "#007efa" : undefined
}
fillColor={
3 weeks ago
selectedOperator === "mtn" ? "#007efa" : undefined
2 months ago
}
/>
3 weeks ago
{selectedOperator === "mtn" && (
2 months ago
<View style={styles.checkmarkContainer}>
<CheckIcon size={fontSize(12)} color="#FFFFFF" />
</View>
)}
</View>
</TouchableOpacity>
</View>
3 weeks ago
</View>
</View>
2 months ago
3 weeks ago
<View style={styles.flexContainer}>
<View style={styles.imageContainer}>
<Image
source={require("../../../assets/img/Global 1.png")}
style={styles.imageStyle}
resizeMode="cover"
/>
</View>
<View style={styles.verticalAlignEndContent}>
<View style={styles.svgContainer}>
<TouchableOpacity
onPress={() => handleOperatorSelect("mtn")}
>
2 months ago
<View style={styles.checkboxContainer}>
<CircleOutlineIcon
size={fontSize(24)}
strokeColor={
selectedOperator === "mtn" ? "#007efa" : undefined
}
fillColor={
selectedOperator === "mtn" ? "#007efa" : undefined
}
2 months ago
/>
{selectedOperator === "mtn" && (
<View style={styles.checkmarkContainer}>
<CheckIcon size={fontSize(12)} color="#FFFFFF" />
</View>
)}
</View>
</TouchableOpacity>
</View>
</View>
</View>
</View>
3 weeks ago
)}
</ScrollView>
3 weeks ago
{/* 操作按钮 - 固定在底部 */}
<View style={styles.actionButtonsContainer}>
<View style={styles.actionButtons}>
<TouchableOpacity style={styles.cancelButton} onPress={onClose}>
2 weeks ago
<Text style={styles.buttonTextDark}>
{t("balance.recharge.cancel")}
</Text>
3 weeks ago
</TouchableOpacity>
3 weeks ago
<TouchableOpacity
style={[
3 weeks ago
styles.confirmButton,
isConfirmButtonDisabled() && styles.confirmButtonDisabled,
]}
3 weeks ago
onPress={handleButtonClick}
disabled={isConfirmButtonDisabled()}
3 weeks ago
>
3 weeks ago
<Text style={styles.buttonTextWhite}>
2 weeks ago
{isConverting
? t("balance.recharge.converting")
: t("balance.recharge.confirm")}
3 weeks ago
</Text>
3 weeks ago
</TouchableOpacity>
2 months ago
</View>
3 weeks ago
</View>
2 months ago
</View>
3 weeks ago
{/* Phone Number Input Modal */}
<PhoneNumberInputModal
isVisible={showPhoneModal}
onClose={() => setShowPhoneModal(false)}
paymentParams={paymentParams}
onSubmit={handlePaySubmit}
onCloses={onClose}
/>
2 months ago
</SafeAreaView>
);
};
const styles = StyleSheet.create({
safeArea: {
flex: 1,
backgroundColor: "#f0f0f0",
},
mainContainer: {
flex: 1,
backgroundColor: "#f0f0f0",
borderTopLeftRadius: 10,
borderTopRightRadius: 10,
},
header: {
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
padding: 24,
paddingBottom: 0,
position: "relative",
marginTop: 20,
marginBottom: 20,
},
scrollContent: {
padding: 24,
paddingTop: 0,
},
container: {
padding: 24,
backgroundColor: "#f0f0f0",
borderTopLeftRadius: 10,
borderTopRightRadius: 10,
},
title: {
fontSize: 24,
fontWeight: "700",
textTransform: "capitalize",
color: "black",
position: "absolute",
left: 0,
right: 0,
textAlign: "center",
},
section: {
marginTop: 44,
},
subtitle: {
fontSize: 16,
fontWeight: "700",
color: "black",
textTransform: "capitalize",
marginBottom: 12,
},
priceGroup: {
marginTop: 10,
},
row: {
flexDirection: "row",
justifyContent: "flex-start",
marginTop: 15,
},
priceBoxBlue: {
width: "30%",
backgroundColor: "#edf2fa",
borderColor: "#002fa7",
borderWidth: 1,
borderRadius: 5,
paddingVertical: 9,
paddingHorizontal: 22,
alignItems: "center",
marginRight: "5%",
},
priceBoxWhite: {
width: "30%",
backgroundColor: "white",
borderRadius: 5,
paddingVertical: 9,
paddingHorizontal: 22,
alignItems: "center",
marginRight: "5%",
},
priceTextBlue: {
fontSize: fontSize(16),
fontWeight: "700",
color: "#002fa7",
},
currencyTextBlue: {
fontSize: fontSize(10),
color: "#002fa7",
marginTop: 1,
},
priceText: {
fontSize: fontSize(16),
fontWeight: "700",
color: "#333",
},
currencyText: {
fontSize: fontSize(10),
color: "#7f7e7e",
marginTop: 1,
},
operatorCard: {
backgroundColor: "white",
padding: 16,
borderRadius: 5,
marginTop: 18,
},
operatorImage: {
width: 80,
height: 30,
resizeMode: "contain",
},
operatorImage1: {
width: 228,
height: 30,
resizeMode: "contain",
},
orangeText: {
color: "#ff5100",
fontSize: fontSize(12),
fontWeight: "500",
marginLeft: 12,
},
iconRow: {
flexDirection: "row",
gap: 10,
},
operatorIcon: {
width: 60,
height: 22,
resizeMode: "contain",
},
currencyContainer: {
backgroundColor: "white",
3 weeks ago
padding: 15,
2 months ago
borderRadius: 5,
},
currencyRow: {
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
paddingRight: 6,
2 months ago
},
currencyImage: {
width: 80,
height: 30,
resizeMode: "contain",
},
radioCircle: {
width: 24,
height: 24,
borderRadius: 12,
borderWidth: 2,
borderColor: "#007AFF",
},
currencyButtonActive: {
backgroundColor: "#002fa7",
},
currencyButtonInactive: {
backgroundColor: "#eeeeee",
borderRadius: 18,
width: widthUtils(36, 100).width,
height: widthUtils(36, 100).height,
justifyContent: "center",
alignItems: "center",
marginLeft: 10,
},
buttonTextWhite: {
color: "white",
fontWeight: "600",
},
buttonTextDark: {
color: "black",
fontWeight: "500",
},
totalText: {
marginTop: 16,
fontWeight: "900",
fontSize: fontSize(16),
color: "#ff5100",
},
actionButtonsContainer: {
padding: 24,
paddingTop: 0,
backgroundColor: "#f0f0f0",
},
actionButtons: {
flexDirection: "row",
justifyContent: "space-between",
padding: 10,
},
cancelButton: {
backgroundColor: "white",
borderRadius: 25,
width: widthUtils(50, 160).width,
height: widthUtils(50, 160).height,
justifyContent: "center",
alignItems: "center",
borderColor: "#ccc",
borderWidth: 1,
},
confirmButton: {
backgroundColor: "#002fa7",
borderRadius: 25,
width: widthUtils(50, 160).width,
height: widthUtils(50, 160).height,
justifyContent: "center",
alignItems: "center",
},
3 weeks ago
confirmButtonDisabled: {
backgroundColor: "#b0bfdf",
opacity: 0.7,
},
2 months ago
balanceInfoContainer: {
flexDirection: "row",
backgroundColor: "white",
borderRadius: 5,
paddingHorizontal: 16,
height: 50,
alignItems: "center",
justifyContent: "space-between",
marginTop: 14,
},
leftInfo: {
flexDirection: "row",
alignItems: "center",
flex: 1,
},
blueBox: {
flexDirection: "row",
backgroundColor: "#3955f6",
paddingHorizontal: 7,
paddingLeft: 6,
alignItems: "center",
borderRadius: 4,
},
saleText: {
fontSize: 16,
fontFamily: "Timmana",
color: "white",
marginLeft: 4,
},
balanceText: {
marginLeft: 17,
fontSize: fontSize(11),
lineHeight: 14,
fontWeight: "500",
color: "#333333",
},
cardContainer: {
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
backgroundColor: "white",
borderRadius: 5,
paddingHorizontal: 16,
marginTop: 15,
2 weeks ago
paddingVertical: 10,
2 months ago
},
imageSmall: {
width: widthUtils(30, 24).width,
height: widthUtils(30, 24).height,
resizeMode: "contain",
},
imageLarge: {
width: widthUtils(26, 44).width,
height: widthUtils(26, 44).height,
resizeMode: "contain",
},
emptyBox: {
backgroundColor: "transparent",
borderWidth: 0,
},
container1: {
flexDirection: "row",
gap: 10,
alignItems: "center",
marginTop: 10,
},
button: {
width: widthUtils(36, 190).width,
height: widthUtils(36, 190).height,
borderRadius: 18,
justifyContent: "center",
alignItems: "center",
},
buttonActive: {
backgroundColor: "#002fa7",
},
buttonInactive: {
backgroundColor: "#fdfefe",
},
textActive: {
color: "white",
fontWeight: "600",
fontSize: fontSize(15),
},
textInactive: {
color: "black",
fontSize: fontSize(15),
},
priceBoxSelected: {
backgroundColor: "#edf2fa",
borderColor: "#002fa7",
borderWidth: 1,
},
priceBoxUnselected: {
backgroundColor: "white",
borderColor: "#dddddd",
borderWidth: 1,
},
priceTextSelected: {
color: "#002fa7",
},
priceTextUnselected: {
color: "#333",
},
currencyTextSelected: {
color: "#002fa7",
},
currencyTextUnselected: {
color: "#7f7e7e",
},
closeButton: {
padding: 5,
position: "absolute",
right: 24,
zIndex: 1,
},
checkboxContainer: {
position: "relative",
width: fontSize(24),
height: fontSize(24),
},
checkmarkContainer: {
position: "absolute",
top: 0,
left: 0,
right: 0,
bottom: 0,
justifyContent: "center",
alignItems: "center",
},
container2: {
width: "100%",
2 months ago
marginTop: 20,
},
amountRechargeContainer: {
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
width: "100%",
2 months ago
height: 60,
backgroundColor: "white",
2 months ago
borderRadius: 5,
},
rechargePromptTextStyle: {
flex: 0,
padding: 0,
margin: 0,
fontSize: 14,
lineHeight: 14,
color: "#747474",
2 months ago
// Note: PingFang SC font might not be available by default in React Native
// You may need to load custom fonts using expo-font or other methods
},
tabContainer: {
flexDirection: "row",
backgroundColor: "white",
2 months ago
borderRadius: 5,
marginTop: 10,
padding: 5,
},
tab: {
flex: 1,
paddingVertical: 10,
alignItems: "center",
2 months ago
borderRadius: 5,
},
activeTab: {
backgroundColor: "#002fa7",
2 months ago
},
tabText: {
fontSize: fontSize(14),
color: "#333",
fontWeight: "500",
2 months ago
},
activeTabText: {
color: "white",
fontWeight: "600",
2 months ago
},
emptyTab: {
backgroundColor: "white",
2 months ago
borderRadius: 5,
padding: 20,
marginTop: 10,
alignItems: "center",
justifyContent: "center",
2 months ago
height: 200,
},
emptyTabText: {
color: "#666",
2 months ago
fontSize: fontSize(14),
},
outerContainer: {
width: "100%",
2 months ago
},
flexContainer: {
flexDirection: "row",
alignItems: "center",
justifyContent: "flex-start",
2 months ago
height: 50,
paddingRight: 16,
paddingLeft: 16,
backgroundColor: "white",
2 months ago
borderRadius: 5,
marginTop: 18,
},
imageContainer: {
2 weeks ago
flexDirection: "column",
},
mobileMoneyTextContainer: {
width: "100%",
marginTop: 3,
alignItems: "flex-start",
flexDirection: "row",
},
mobileMoneyImgContainer:{
width: 60,
height: 22,
borderWidth: 0,
marginRight: 5,
},
mobileMoneyImg:{
width: 60,
height: 22,
borderWidth: 0,
2 months ago
},
imageStyle: {
width: 80,
height: 30,
borderWidth: 0,
},
verticalAlignEndContent: {
flexDirection: "column",
alignItems: "flex-end",
width: "73.46%",
2 months ago
},
svgContainer: {
width: 24,
height: 24,
},
backButton: {
position: "absolute",
left: 24,
zIndex: 1,
},
backButtonText: {
fontSize: fontSize(14),
color: "#007AFF",
fontWeight: "500",
},
blankPage: {
flex: 1,
justifyContent: "center",
alignItems: "center",
backgroundColor: "#f0f0f0",
},
blankPageText: {
fontSize: fontSize(16),
color: "#666",
},
paymentSection2: {
paddingTop: 24,
paddingRight: 20,
paddingBottom: 333,
paddingLeft: 20,
backgroundColor: "white",
2 months ago
borderTopLeftRadius: 10,
borderTopRightRadius: 10,
},
paymentSectionContainer: {
flexDirection: "row",
alignItems: "center",
justifyContent: "flex-start",
width: "100%",
2 months ago
},
paymentSection: {
width: "8.48%",
2 months ago
paddingRight: 15,
},
svgContainer1: {
width: 18,
height: 18,
},
paymentSection1: {
flexDirection: "column",
alignItems: "center",
justifyContent: "flex-start",
width: "91.52%",
2 months ago
paddingRight: 33,
},
paymentHeadingStyle: {
padding: 0,
margin: 0,
fontSize: 24,
lineHeight: 22,
color: "black",
textTransform: "capitalize",
fontFamily: "Montserrat-Bold",
2 months ago
},
transactionSummaryContainer1: {
width: "100%",
2 months ago
},
transactionSummaryContainer: {
width: "100%",
2 months ago
paddingRight: 15,
paddingLeft: 15,
backgroundColor:
"linear-gradient(90deg, rgba(206, 229, 255, 1) 0%, rgba(238, 244, 255, 1) 100%",
2 months ago
borderRadius: 5,
},
flexContainerWithImages: {
flexDirection: "row",
alignItems: "flex-start",
justifyContent: "flex-start",
2 months ago
},
imageContainerStyled: {
width: 99,
height: 36,
borderWidth: 0,
},
imageContainerWithBorder: {
width: 135,
height: 140,
marginLeft: 141,
borderWidth: 0,
},
amountContainer: {
width: "100%",
2 months ago
paddingTop: 8,
paddingRight: 11,
paddingBottom: 10,
paddingLeft: 11,
marginTop: -83,
backgroundColor: "white",
2 months ago
borderWidth: 1,
borderRadius: 5,
},
amountLabel: {
padding: 0,
margin: 0,
fontSize: 12,
color: "#7f7e7e",
fontFamily: "PingFangSC-Medium",
2 months ago
},
amountContainer1: {
flexDirection: "row",
alignItems: "flex-start",
justifyContent: "flex-start",
2 months ago
marginTop: 1,
},
priceHeading: {
padding: 0,
paddingBottom: 5,
margin: 0,
fontSize: 24,
lineHeight: 22,
color: "#161616",
fontFamily: "Montserrat-Bold",
2 months ago
},
priceLabel: {
padding: 0,
paddingTop: 5,
margin: 0,
marginLeft: 3,
fontSize: 12,
color: "#7f7e7e",
fontFamily: "PingFangSC-Medium",
2 months ago
},
mobileInfoSection: {
width: "100%",
2 months ago
marginTop: 30,
},
mobileNumberSection: {
width: "100%",
2 months ago
},
mobileNumberLabel: {
padding: 0,
margin: 0,
fontSize: 14,
lineHeight: 18,
color: "black",
fontFamily: "PingFangSC-Regular",
2 months ago
},
infoCard: {
flexDirection: "row",
alignItems: "center",
justifyContent: "flex-start",
width: "100%",
2 months ago
height: 50,
paddingRight: 12,
paddingLeft: 12,
marginTop: 12,
borderWidth: 1,
borderColor: "#d8d8d8",
2 months ago
borderRadius: 25,
},
flexRowWithIcon: {
flexDirection: "row",
alignItems: "center",
justifyContent: "flex-start",
2 months ago
},
maskedImageWithText: {
width: 20,
height: 12,
borderWidth: 0,
},
highlightText: {
padding: 0,
margin: 0,
marginLeft: 3,
fontSize: 16,
lineHeight: 22,
color: "#1c284e",
fontFamily: "PingFangSC-Semibold",
2 months ago
},
svgContainer2: {
width: 12,
height: 12,
marginLeft: 12,
},
verticalDivider: {
width: 1,
height: 30,
marginLeft: 8.5,
borderLeftWidth: 1,
borderColor: "#b1b1b1",
2 months ago
},
statisticText: {
padding: 0,
margin: 0,
marginLeft: 19.5,
fontSize: 16,
lineHeight: 22,
color: "#1c284e",
fontFamily: "PingFangSC-Semibold",
2 months ago
},
mobileOperatorsContainer: {
width: "100%",
2 months ago
marginTop: 20,
},
operatorSupportContainer: {
flexDirection: "row",
2 months ago
gap: 10,
alignItems: "center",
justifyContent: "flex-start",
2 months ago
marginTop: 12,
},
imageContainerWithBorder1: {
width: 70,
height: 26,
borderWidth: 0,
},
blueBoxContainer: {
flexDirection: "column",
alignItems: "center",
justifyContent: "flex-start",
width: "100%",
2 months ago
marginTop: 50,
backgroundColor: "#002fa7",
2 months ago
borderRadius: 25,
},
paymentNotice1: {
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
2 months ago
height: 50,
},
paymentNotice: {
padding: 0,
margin: 0,
fontSize: 16,
color: "white",
fontFamily: "Montserrat-Bold",
2 months ago
},
3 weeks ago
paymentConfirmContainer: {
flex: 1,
padding: 20,
backgroundColor: "#fff",
},
paymentSummaryCard: {
backgroundColor: "#f5f9ff",
borderRadius: 10,
padding: 20,
marginBottom: 20,
},
paymentSummaryTitle: {
fontSize: fontSize(18),
fontWeight: "700",
marginBottom: 15,
color: "#333",
},
paymentSummaryRow: {
flexDirection: "row",
justifyContent: "space-between",
marginBottom: 10,
},
paymentSummaryLabel: {
fontSize: fontSize(14),
color: "#666",
},
paymentSummaryValue: {
fontSize: fontSize(14),
fontWeight: "500",
color: "#333",
},
paymentSummaryValueHighlight: {
fontSize: fontSize(14),
fontWeight: "600",
color: "#ff5100",
},
phoneInputContainer: {
marginBottom: 20,
},
phoneInputLabel: {
fontSize: fontSize(16),
fontWeight: "500",
marginBottom: 10,
color: "#333",
},
phoneInputWrapper: {
flexDirection: "row",
borderWidth: 1,
borderColor: "#ddd",
borderRadius: 25,
overflow: "hidden",
},
countryCodeContainer: {
backgroundColor: "#f5f5f5",
paddingHorizontal: 15,
justifyContent: "center",
borderRightWidth: 1,
borderRightColor: "#ddd",
},
countryCodeText: {
fontSize: fontSize(16),
color: "#333",
},
phoneInput: {
flex: 1,
height: 50,
paddingHorizontal: 15,
fontSize: fontSize(16),
color: "#333",
},
supportedOperatorsContainer: {
marginBottom: 30,
},
supportedOperatorsTitle: {
fontSize: fontSize(16),
fontWeight: "500",
marginBottom: 10,
color: "#333",
},
operatorsRow: {
flexDirection: "row",
alignItems: "center",
},
operatorSmallIcon: {
width: 70,
height: 26,
resizeMode: "contain",
marginRight: 15,
},
payButton: {
backgroundColor: "#002fa7",
borderRadius: 25,
height: 50,
justifyContent: "center",
alignItems: "center",
marginTop: 20,
},
payButtonDisabled: {
backgroundColor: "#8da0d4",
opacity: 0.7,
},
payButtonText: {
color: "white",
fontSize: fontSize(16),
fontWeight: "700",
},
3 weeks ago
customAmountInputContainer: {
flexDirection: "row",
alignItems: "center",
width: "100%",
},
customAmountInput: {
flex: 1,
height: 40,
paddingHorizontal: 10,
fontSize: fontSize(16),
color: "#333",
},
currencyLabel: {
fontSize: fontSize(14),
color: "#7f7e7e",
marginLeft: 10,
marginRight: 10,
},
customAmountText: {
fontSize: fontSize(16),
fontWeight: "600",
color: "#002fa7",
textAlign: "center",
},
paypalExpandedContainer: {
backgroundColor: "#f9f9f9",
marginTop: 0,
marginBottom: 15,
borderBottomLeftRadius: 5,
borderBottomRightRadius: 5,
padding: 15,
borderWidth: 1,
borderTopWidth: 0,
borderColor: "#ddd",
},
paypalCurrencyContainer: {
backgroundColor: "white",
padding: 15,
borderRadius: 5,
},
currencyTitle: {
fontSize: fontSize(14),
fontWeight: "600",
color: "#333",
marginBottom: 10,
},
currencyButtonsContainer: {
flexDirection: "row",
marginBottom: 15,
},
currencyButton: {
backgroundColor: "#f0f0f0",
paddingVertical: 8,
paddingHorizontal: 20,
borderRadius: 5,
marginRight: 10,
},
currencyButtonText: {
fontSize: fontSize(14),
fontWeight: "500",
color: "#333",
},
currencyButtonTextActive: {
color: "white",
},
convertingContainer: {
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
padding: 10,
},
convertingText: {
marginLeft: 10,
fontSize: fontSize(14),
color: "#333",
},
convertedAmountContainer: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
paddingVertical: 10,
borderTopWidth: 1,
borderTopColor: "#eee",
},
convertedAmountLabel: {
fontSize: fontSize(14),
color: "#666",
},
convertedAmountValue: {
fontSize: fontSize(16),
fontWeight: "700",
color: "#002fa7",
},
2 weeks ago
mobileMoneyText: {
fontSize: fontSize(12),
color: "#7f7e7e",
marginTop: 5,
textAlign: "center",
},
2 months ago
});
export default RechargeScreen;