test 优化
44
App.tsx
@@ -1,11 +1,32 @@
|
||||
import React from 'react';
|
||||
import { Text } from 'react-native';
|
||||
import { NavigationContainer } from '@react-navigation/native';
|
||||
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
|
||||
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
||||
import Svg, { Path, Circle, Rect } from 'react-native-svg';
|
||||
import HomeScreen from './screens/HomeScreen';
|
||||
import TestScreen from './screens/TestScreen';
|
||||
import MessageScreen from './screens/MessageScreen';
|
||||
|
||||
const WalletIcon = ({ color, size }: { color: string; size: number }) => (
|
||||
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
|
||||
<Rect x="2" y="5" width="20" height="15" rx="2" stroke={color} strokeWidth="1.8" />
|
||||
<Path d="M2 10h20" stroke={color} strokeWidth="1.8" />
|
||||
<Circle cx="17" cy="15" r="1.5" fill={color} />
|
||||
</Svg>
|
||||
);
|
||||
|
||||
const ToolIcon = ({ color, size }: { color: string; size: number }) => (
|
||||
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
|
||||
<Path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3-3a6 6 0 0 1-7 7l-6.4 6.4a2 2 0 1 1-2.8-2.8L10.9 10a6 6 0 0 1 7-7l-3.2 3.3z" stroke={color} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</Svg>
|
||||
);
|
||||
|
||||
const MessageIcon = ({ color, size }: { color: string; size: number }) => (
|
||||
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
|
||||
<Path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" stroke={color} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</Svg>
|
||||
);
|
||||
|
||||
const Tab = createBottomTabNavigator();
|
||||
|
||||
export default function App() {
|
||||
@@ -30,11 +51,18 @@ export default function App() {
|
||||
name="Home"
|
||||
component={HomeScreen}
|
||||
options={{
|
||||
title: '首页',
|
||||
tabBarLabel: '首页',
|
||||
tabBarIcon: ({ color, size }) => (
|
||||
<Text style={{ fontSize: size, color }}>🏠</Text>
|
||||
),
|
||||
title: '钱包列表',
|
||||
tabBarLabel: '钱包列表',
|
||||
tabBarIcon: ({ color, size }) => <WalletIcon color={color} size={size} />,
|
||||
}}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="Test"
|
||||
component={TestScreen}
|
||||
options={{
|
||||
title: '测试',
|
||||
tabBarLabel: '测试',
|
||||
tabBarIcon: ({ color, size }) => <ToolIcon color={color} size={size} />,
|
||||
}}
|
||||
/>
|
||||
<Tab.Screen
|
||||
@@ -43,9 +71,7 @@ export default function App() {
|
||||
options={{
|
||||
title: 'IM消息',
|
||||
tabBarLabel: 'IM消息',
|
||||
tabBarIcon: ({ color, size }) => (
|
||||
<Text style={{ fontSize: size, color }}>💬</Text>
|
||||
),
|
||||
tabBarIcon: ({ color, size }) => <MessageIcon color={color} size={size} />,
|
||||
}}
|
||||
/>
|
||||
</Tab.Navigator>
|
||||
|
||||
6
declarations.d.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
declare module '*.svg' {
|
||||
import React from 'react';
|
||||
import { SvgProps } from 'react-native-svg';
|
||||
const content: React.FC<SvgProps>;
|
||||
export default content;
|
||||
}
|
||||
@@ -1,11 +1,16 @@
|
||||
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
|
||||
|
||||
/**
|
||||
* Metro configuration
|
||||
* https://facebook.github.io/metro/docs/configuration
|
||||
*
|
||||
* @type {import('metro-config').MetroConfig}
|
||||
*/
|
||||
const config = {};
|
||||
const defaultConfig = getDefaultConfig(__dirname);
|
||||
const { assetExts, sourceExts } = defaultConfig.resolver;
|
||||
|
||||
module.exports = mergeConfig(getDefaultConfig(__dirname), config);
|
||||
const config = {
|
||||
transformer: {
|
||||
babelTransformerPath: require.resolve('react-native-svg-transformer'),
|
||||
},
|
||||
resolver: {
|
||||
assetExts: assetExts.filter(ext => ext !== 'svg'),
|
||||
sourceExts: [...sourceExts, 'svg'],
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = mergeConfig(defaultConfig, config);
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
"react-native-gesture-handler": "~2.9.0",
|
||||
"react-native-safe-area-context": "^4.4.1",
|
||||
"react-native-screens": "~3.36.0",
|
||||
"react-native-svg": "^14.1.0",
|
||||
"react-native-svg-transformer": "^1.5.3",
|
||||
"react-native-tcp-socket": "^6.4.1",
|
||||
"react-native-webview": "13.6.2",
|
||||
"rnauto": "./libs/rnauto",
|
||||
|
||||
BIN
res/bharatpe-business.webp
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
res/freecharge.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
res/googlepay-business.webp
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
res/mobikwik.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
res/paytm-business.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
res/paytm.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
res/phonepe-business.webp
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
res/phonepe.webp
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
69
screens/TestScreen.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { Alert, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||||
import { onProxyMessage, proxySendMessage, paytmPay } from 'rnwalletman';
|
||||
|
||||
export default function TestScreen() {
|
||||
const subRef = useRef<ReturnType<typeof onProxyMessage> | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
subRef.current = onProxyMessage((msg) => {
|
||||
if (msg.type === 'echo') {
|
||||
Alert.alert('Echo 回来了', JSON.stringify(msg.data));
|
||||
}
|
||||
});
|
||||
return () => {
|
||||
subRef.current?.remove();
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handlePaytmPay = () => {
|
||||
paytmPay('100', 'Gurvir singh', '296001000405', 'ICIC0002960', 'ABCDEF')
|
||||
.then(result => console.log(result))
|
||||
.catch(error => Alert.alert('代付失败', String(error)));
|
||||
};
|
||||
|
||||
const handleEcho = () => {
|
||||
proxySendMessage({ type: 'echo', messageId: `echo_${Date.now()}`, data: { text: `hello_${Date.now()}` } });
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.sectionTitle}>测试工具</Text>
|
||||
<TouchableOpacity style={[styles.btn, { backgroundColor: '#2ecc71' }]} onPress={handlePaytmPay}>
|
||||
<Text style={styles.btnText}>Paytm Pay 代付测试</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity style={[styles.btn, { backgroundColor: '#3498db' }]} onPress={handleEcho}>
|
||||
<Text style={styles.btnText}>Echo 测试</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: '#f0f0f0',
|
||||
padding: 20,
|
||||
alignItems: 'center',
|
||||
},
|
||||
sectionTitle: {
|
||||
fontSize: 16,
|
||||
fontWeight: '600',
|
||||
color: '#333',
|
||||
alignSelf: 'flex-start',
|
||||
marginBottom: 16,
|
||||
marginTop: 8,
|
||||
},
|
||||
btn: {
|
||||
width: '100%',
|
||||
paddingVertical: 14,
|
||||
borderRadius: 8,
|
||||
alignItems: 'center',
|
||||
marginBottom: 12,
|
||||
},
|
||||
btnText: {
|
||||
color: '#fff',
|
||||
fontSize: 15,
|
||||
fontWeight: '600',
|
||||
},
|
||||
});
|
||||
@@ -1,6 +1,14 @@
|
||||
import { WalletType } from 'rnwalletman';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
|
||||
export interface WalletItem {
|
||||
id: string;
|
||||
walletType: string;
|
||||
upi?: string;
|
||||
phone?: string;
|
||||
status?: string;
|
||||
}
|
||||
|
||||
const DEFAULT_DOMAIN = 'aa.pfgame.org';
|
||||
const STORAGE_KEY = 'server_domain';
|
||||
const HTTPS_KEY = 'server_https';
|
||||
@@ -117,6 +125,44 @@ class Api {
|
||||
if (!data.success) throw new Error(data.message);
|
||||
return data;
|
||||
}
|
||||
|
||||
public async listWallets(): Promise<WalletItem[]> {
|
||||
const res = await fetch(`${Api.BASE_URL}/wallets`, { headers: this.headers() });
|
||||
const data = await res.json();
|
||||
if (!data.success) throw new Error(data.message);
|
||||
return data.data?.wallets ?? [];
|
||||
}
|
||||
|
||||
public async getWalletVpas(walletId: string): Promise<string[]> {
|
||||
const res = await fetch(`${Api.BASE_URL}/wallet/vpas?walletId=${encodeURIComponent(walletId)}`, {
|
||||
headers: this.headers(),
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!data.success) throw new Error(data.message);
|
||||
return data.data?.vpas ?? [];
|
||||
}
|
||||
|
||||
public async setCurrentVpa(walletId: string, vpaIndex: number): Promise<string> {
|
||||
const res = await fetch(`${Api.BASE_URL}/wallet/set-vpa`, {
|
||||
method: 'POST',
|
||||
headers: this.headers(),
|
||||
body: JSON.stringify({ walletId, vpaIndex }),
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!data.success) throw new Error(data.message);
|
||||
return data.data?.vpa ?? '';
|
||||
}
|
||||
|
||||
public async generateLink(walletId: string, amount: string): Promise<{ link: string; orderId: string }> {
|
||||
const res = await fetch(`${Api.BASE_URL}/generate-link`, {
|
||||
method: 'POST',
|
||||
headers: this.headers(),
|
||||
body: JSON.stringify({ walletId, amount }),
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!data.success) throw new Error(data.message);
|
||||
return { link: data.data?.link, orderId: data.data?.orderId };
|
||||
}
|
||||
}
|
||||
|
||||
export default Api;
|
||||
|
||||