优化
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import React, { Component, useState, useEffect } from 'react';
|
||||
import { View, Text, TextInput, TouchableOpacity, StyleSheet, ActivityIndicator } from 'react-native';
|
||||
import { WalletType, FreechargePersonalBindResult, MobikwikPersonalBindResult, PaytmPersonalBindResult, PhonePePersonalBindResult, BharatPeBusinessBindResult, PaytmBusinessBindResult } from 'rnwalletman';
|
||||
import { WalletType, FreechargePersonalBindResult, MobikwikPersonalBindResult, PaytmPersonalBindResult, PhonePePersonalBindResult, PhonePeBusinessBindResult, BharatPeBusinessBindResult, PaytmBusinessBindResult } from 'rnwalletman';
|
||||
import { OTPBindUI } from './OTPBindUI';
|
||||
|
||||
export class FreeChargeBind extends Component<{
|
||||
@@ -223,6 +223,31 @@ const ptStyles = StyleSheet.create({
|
||||
hint: { fontSize: 14, color: '#555', marginBottom: 18, textAlign: 'center' },
|
||||
});
|
||||
|
||||
export class PhonePeBusinessOTPBind extends Component<{
|
||||
onRequestOTP: (walletType: WalletType, params: any) => Promise<any>;
|
||||
onVerifyOTP: (walletType: WalletType, params: any) => Promise<any>;
|
||||
onSuccess: (result: PhonePeBusinessBindResult) => void;
|
||||
onError: (error: string) => void;
|
||||
isDebug: boolean;
|
||||
initialMobile?: string;
|
||||
}> {
|
||||
render() {
|
||||
return (
|
||||
<OTPBindUI
|
||||
walletType={WalletType.PHONEPE_BUSINESS}
|
||||
title="PhonePe Business 绑定"
|
||||
otpLength={5}
|
||||
onRequestOTP={this.props.onRequestOTP}
|
||||
onVerifyOTP={this.props.onVerifyOTP}
|
||||
onSuccess={this.props.onSuccess}
|
||||
onError={this.props.onError}
|
||||
isDebug={this.props.isDebug}
|
||||
initialMobile={this.props.initialMobile}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class PhonePePersonalOTPBind extends Component<{
|
||||
onRequestOTP: (walletType: WalletType, params: any) => Promise<any>;
|
||||
onVerifyOTP: (walletType: WalletType, params: any) => Promise<any>;
|
||||
|
||||
@@ -3,7 +3,6 @@ import {
|
||||
Alert,
|
||||
AppState,
|
||||
AppStateStatus,
|
||||
FlatList,
|
||||
Image,
|
||||
Modal,
|
||||
ScrollView,
|
||||
@@ -18,8 +17,8 @@ import {
|
||||
import * as Animatable from 'react-native-animatable';
|
||||
import DeviceInfo from 'react-native-device-info';
|
||||
import {
|
||||
PhonePeBusinessBind,
|
||||
GooglePayBusinessBind,
|
||||
PhonePeBusinessBind,
|
||||
WalletType,
|
||||
PaytmBusinessBindResult,
|
||||
PaytmPersonalBind,
|
||||
@@ -48,6 +47,7 @@ import {
|
||||
PhonePePersonalOTPBind,
|
||||
BharatPeBusinessOTPBind,
|
||||
PaytmBusinessOTPBind,
|
||||
PhonePeBusinessOTPBind,
|
||||
} from '../components/WalletBindComponents';
|
||||
|
||||
import Api, {
|
||||
@@ -113,11 +113,17 @@ const WALLET_TYPE_OPTIONS = [
|
||||
mode: 'token',
|
||||
},
|
||||
{
|
||||
key: 'phonepe_business',
|
||||
key: 'phonepe_business_otp',
|
||||
walletType: 'phonepe business',
|
||||
label: 'PhonePe Business (OTP-WEB)',
|
||||
label: 'PhonePe Business (OTP)',
|
||||
mode: 'otp',
|
||||
},
|
||||
{
|
||||
key: 'phonepe_business_web',
|
||||
walletType: 'phonepe business',
|
||||
label: 'PhonePe Business (Web)',
|
||||
mode: 'token',
|
||||
},
|
||||
{
|
||||
key: 'googlepay_business',
|
||||
walletType: 'googlepay business',
|
||||
@@ -150,6 +156,26 @@ const WALLET_TYPE_OPTIONS = [
|
||||
},
|
||||
];
|
||||
|
||||
function formatWalletTypeLabel(walletType: string) {
|
||||
return walletType.replace(/\b\w/g, (c) => c.toUpperCase());
|
||||
}
|
||||
|
||||
function groupBoundWallets(wallets: WalletItem[]) {
|
||||
const order: string[] = [];
|
||||
const map = new Map<string, WalletItem[]>();
|
||||
for (const w of wallets) {
|
||||
if (!map.has(w.walletType)) {
|
||||
map.set(w.walletType, []);
|
||||
order.push(w.walletType);
|
||||
}
|
||||
map.get(w.walletType)!.push(w);
|
||||
}
|
||||
return order.map((walletType) => ({
|
||||
walletType,
|
||||
items: map.get(walletType)!,
|
||||
}));
|
||||
}
|
||||
|
||||
function getBindKeyForWallet(item: WalletItem): string | null {
|
||||
const otp = item.otpMode === true;
|
||||
switch (item.walletType) {
|
||||
@@ -160,7 +186,7 @@ function getBindKeyForWallet(item: WalletItem): string | null {
|
||||
case 'paytm business':
|
||||
return 'paytm_business';
|
||||
case 'phonepe business':
|
||||
return 'phonepe_business';
|
||||
return otp ? 'phonepe_business_otp' : 'phonepe_business_web';
|
||||
case 'googlepay business':
|
||||
return 'googlepay_business';
|
||||
case 'bharatpe business':
|
||||
@@ -182,6 +208,7 @@ interface HomeScreenState {
|
||||
showPhonePePersonalBind: boolean;
|
||||
phonePePersonalBindType: 'otpMode' | 'tokenMode';
|
||||
showPhonePeBusinessBind: boolean;
|
||||
phonePeBusinessBindType: 'otpMode' | 'webviewMode';
|
||||
freechargePersonalBindType: 'otpMode' | 'tokenMode';
|
||||
showGooglePayBusinessBind: boolean;
|
||||
showBharatPeBusinessBind: boolean;
|
||||
@@ -205,6 +232,7 @@ interface HomeScreenState {
|
||||
// add wallet
|
||||
showAddWallet: boolean;
|
||||
bindPrefillMobile: string;
|
||||
expandedBoundWalletGroups: Record<string, boolean>;
|
||||
}
|
||||
|
||||
export default class HomeScreen extends Component<any, HomeScreenState> {
|
||||
@@ -226,6 +254,7 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
|
||||
showPhonePePersonalBind: false,
|
||||
phonePePersonalBindType: 'otpMode',
|
||||
showPhonePeBusinessBind: false,
|
||||
phonePeBusinessBindType: 'otpMode',
|
||||
showGooglePayBusinessBind: false,
|
||||
showBharatPeBusinessBind: false,
|
||||
showMobikwikPersonalBind: false,
|
||||
@@ -243,6 +272,7 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
|
||||
vpaModalSelected: '',
|
||||
showAddWallet: false,
|
||||
bindPrefillMobile: '',
|
||||
expandedBoundWalletGroups: {},
|
||||
};
|
||||
this.deviceId = DeviceInfo.getUniqueIdSync();
|
||||
this.tuneUserId = "yz8mxybytus";//Math.random().toString(36).substring(2, 15);
|
||||
@@ -410,7 +440,7 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
|
||||
renderBindModal = () => {
|
||||
const {
|
||||
showPaytmPersonalBind, paytmPersonalBindType, showPhonePePersonalBind, phonePePersonalBindType,
|
||||
showPaytmBusinessBind, showPhonePeBusinessBind, showGooglePayBusinessBind, showBharatPeBusinessBind,
|
||||
showPaytmBusinessBind, showPhonePeBusinessBind, phonePeBusinessBindType, showGooglePayBusinessBind, showBharatPeBusinessBind,
|
||||
showMobikwikPersonalBind, showFreechargePersonalBind, freechargePersonalBindType,
|
||||
bindPrefillMobile,
|
||||
} = this.state;
|
||||
@@ -606,14 +636,14 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
if (showPhonePeBusinessBind) {
|
||||
if (showPhonePeBusinessBind && phonePeBusinessBindType === 'otpMode') {
|
||||
return (
|
||||
<Modal
|
||||
visible
|
||||
transparent
|
||||
onRequestClose={close('showPhonePeBusinessBind')}
|
||||
>
|
||||
<PhonePeBusinessBind
|
||||
<PhonePeBusinessOTPBind
|
||||
isDebug
|
||||
initialMobile={bindPrefillMobile}
|
||||
onRequestOTP={async (wt, p) => {
|
||||
@@ -628,6 +658,23 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
if (showPhonePeBusinessBind && phonePeBusinessBindType === 'webviewMode') {
|
||||
return (
|
||||
<Modal
|
||||
visible
|
||||
transparent
|
||||
onRequestClose={close('showPhonePeBusinessBind')}
|
||||
>
|
||||
<PhonePeBusinessBind
|
||||
processString="Processing..."
|
||||
isDebug
|
||||
clearCookie
|
||||
onSuccess={this.handleBindSuccess('showPhonePeBusinessBind', WalletType.PHONEPE_BUSINESS, 'PhonePe Business bound successfully') as any}
|
||||
onError={(e: string) => { Alert.alert('Bind Failed', e); close('showPhonePeBusinessBind')(); }}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
if (showGooglePayBusinessBind) {
|
||||
return (
|
||||
<Modal
|
||||
@@ -748,8 +795,11 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
|
||||
case 'phonepe_personal_token':
|
||||
this.setState({ showPhonePePersonalBind: true, phonePePersonalBindType: 'tokenMode' });
|
||||
break;
|
||||
case 'phonepe_business':
|
||||
this.setState({ showPhonePeBusinessBind: true });
|
||||
case 'phonepe_business_otp':
|
||||
this.setState({ showPhonePeBusinessBind: true, phonePeBusinessBindType: 'otpMode' });
|
||||
break;
|
||||
case 'phonepe_business_web':
|
||||
this.setState({ showPhonePeBusinessBind: true, phonePeBusinessBindType: 'webviewMode' });
|
||||
break;
|
||||
case 'googlepay_business':
|
||||
this.setState({ showGooglePayBusinessBind: true });
|
||||
@@ -770,6 +820,15 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
|
||||
}, 300);
|
||||
};
|
||||
|
||||
toggleBoundWalletGroup = (walletType: string) => {
|
||||
this.setState((prev) => ({
|
||||
expandedBoundWalletGroups: {
|
||||
...prev.expandedBoundWalletGroups,
|
||||
[walletType]: !prev.expandedBoundWalletGroups[walletType],
|
||||
},
|
||||
}));
|
||||
};
|
||||
|
||||
renderAddWalletModal() {
|
||||
return (
|
||||
<Modal
|
||||
@@ -885,7 +944,7 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
|
||||
);
|
||||
}
|
||||
|
||||
renderWalletItem = ({ item }: { item: WalletItem }) => {
|
||||
renderWalletItem = (item: WalletItem) => {
|
||||
const color = WALLET_TYPE_COLORS[item.walletType] ?? '#888';
|
||||
const isActive = item.status === 'ACTIVE';
|
||||
return (
|
||||
@@ -928,6 +987,75 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
|
||||
);
|
||||
};
|
||||
|
||||
renderBoundWalletList = () => {
|
||||
const { wallets, loadingWallets, expandedBoundWalletGroups } = this.state;
|
||||
|
||||
if (loadingWallets && wallets.length === 0) {
|
||||
return (
|
||||
<View style={{ alignItems: 'center', marginTop: 60 }}>
|
||||
<Text style={{ color: '#aaa', fontSize: 14 }}>Loading…</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
if (wallets.length === 0) {
|
||||
return (
|
||||
<View style={{ alignItems: 'center', marginTop: 60 }}>
|
||||
<Text style={{ color: '#aaa', fontSize: 14 }}>No wallets. Tap + Add to get started.</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const groups = groupBoundWallets(wallets);
|
||||
|
||||
return (
|
||||
<ScrollView
|
||||
style={{ flex: 1, width: '100%' }}
|
||||
contentContainerStyle={{ paddingHorizontal: 14, paddingBottom: 20 }}
|
||||
bounces={false}
|
||||
>
|
||||
{groups.map(({ walletType, items }) => {
|
||||
if (items.length === 1) {
|
||||
return this.renderWalletItem(items[0]);
|
||||
}
|
||||
|
||||
const expanded = !!expandedBoundWalletGroups[walletType];
|
||||
const color = WALLET_TYPE_COLORS[walletType] ?? '#888';
|
||||
|
||||
return (
|
||||
<View key={walletType} style={s.boundWalletGroup}>
|
||||
<TouchableOpacity
|
||||
style={s.boundWalletGroupHeader}
|
||||
onPress={() => this.toggleBoundWalletGroup(walletType)}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<View style={s.walletBadge}>
|
||||
{WALLET_ICONS[walletType] ? (
|
||||
<Image source={WALLET_ICONS[walletType]} style={s.walletIcon} resizeMode="contain" />
|
||||
) : (
|
||||
<View style={[s.walletIconFallback, { backgroundColor: color }]}>
|
||||
<Text style={s.walletBadgeText}>{walletType.split(' ')[0]}</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
<View style={{ flex: 1, marginLeft: 12 }}>
|
||||
<Text style={s.boundWalletGroupTitle}>{formatWalletTypeLabel(walletType)}</Text>
|
||||
<Text style={s.boundWalletGroupSub}>{items.length} accounts</Text>
|
||||
</View>
|
||||
<Text style={s.walletGroupChevron}>{expanded ? '▼' : '▶'}</Text>
|
||||
</TouchableOpacity>
|
||||
{expanded && items.map((item) => (
|
||||
<View key={`${item.id}-wrap`} style={s.boundWalletGroupItem}>
|
||||
{this.renderWalletItem(item)}
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
);
|
||||
})}
|
||||
</ScrollView>
|
||||
);
|
||||
};
|
||||
|
||||
renderVpaModal() {
|
||||
const { vpaModalWallet, vpaModalVpas, vpaModalLoading, vpaModalSelected } = this.state;
|
||||
const color = WALLET_TYPE_COLORS[vpaModalWallet?.walletType ?? ''] ?? '#3498db';
|
||||
@@ -990,7 +1118,7 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { proxyStatus, proxyError, wallets, loadingWallets } = this.state;
|
||||
const { proxyStatus, proxyError, loadingWallets } = this.state;
|
||||
const proxyCfg: Record<string, { label: string; color: string }> = {
|
||||
idle: { label: 'Idle', color: '#95a5a6' },
|
||||
connecting: { label: 'Connecting…', color: '#f39c12' },
|
||||
@@ -1041,20 +1169,7 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
<FlatList
|
||||
style={{ flex: 1, width: '100%' }}
|
||||
contentContainerStyle={{ paddingHorizontal: 14, paddingBottom: 20 }}
|
||||
data={wallets}
|
||||
keyExtractor={(item) => item.id}
|
||||
renderItem={this.renderWalletItem}
|
||||
ListEmptyComponent={
|
||||
<View style={{ alignItems: 'center', marginTop: 60 }}>
|
||||
<Text style={{ color: '#aaa', fontSize: 14 }}>
|
||||
{loadingWallets ? 'Loading…' : 'No wallets. Tap + Add to get started.'}
|
||||
</Text>
|
||||
</View>
|
||||
}
|
||||
/>
|
||||
{this.renderBoundWalletList()}
|
||||
|
||||
{this.renderBindModal()}
|
||||
{this.renderServerSettingsModal()}
|
||||
@@ -1283,6 +1398,43 @@ const s = StyleSheet.create({
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: '#f0f0f0',
|
||||
},
|
||||
walletTypeLabel: {
|
||||
fontSize: 14,
|
||||
color: '#333',
|
||||
},
|
||||
boundWalletGroup: {
|
||||
marginBottom: 4,
|
||||
},
|
||||
boundWalletGroupHeader: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#fff',
|
||||
borderRadius: 10,
|
||||
padding: 12,
|
||||
marginBottom: 8,
|
||||
borderWidth: 1,
|
||||
borderColor: '#eee',
|
||||
},
|
||||
boundWalletGroupTitle: {
|
||||
fontSize: 15,
|
||||
fontWeight: '600',
|
||||
color: '#222',
|
||||
},
|
||||
boundWalletGroupSub: {
|
||||
fontSize: 12,
|
||||
color: '#888',
|
||||
marginTop: 2,
|
||||
},
|
||||
boundWalletGroupItem: {
|
||||
marginLeft: 10,
|
||||
},
|
||||
walletGroupChevron: {
|
||||
fontSize: 12,
|
||||
color: '#999',
|
||||
marginLeft: 8,
|
||||
width: 16,
|
||||
textAlign: 'right',
|
||||
},
|
||||
walletTypeIcon: {
|
||||
width: 32,
|
||||
height: 32,
|
||||
@@ -1295,10 +1447,6 @@ const s = StyleSheet.create({
|
||||
borderRadius: 5,
|
||||
marginRight: 12,
|
||||
},
|
||||
walletTypeLabel: {
|
||||
fontSize: 14,
|
||||
color: '#333',
|
||||
},
|
||||
settingsBox: {
|
||||
backgroundColor: '#fff',
|
||||
borderRadius: 10,
|
||||
|
||||
Reference in New Issue
Block a user