test 优化
44
App.tsx
@@ -1,11 +1,32 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Text } from 'react-native';
|
|
||||||
import { NavigationContainer } from '@react-navigation/native';
|
import { NavigationContainer } from '@react-navigation/native';
|
||||||
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
|
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
|
||||||
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
||||||
|
import Svg, { Path, Circle, Rect } from 'react-native-svg';
|
||||||
import HomeScreen from './screens/HomeScreen';
|
import HomeScreen from './screens/HomeScreen';
|
||||||
|
import TestScreen from './screens/TestScreen';
|
||||||
import MessageScreen from './screens/MessageScreen';
|
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();
|
const Tab = createBottomTabNavigator();
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
@@ -30,11 +51,18 @@ export default function App() {
|
|||||||
name="Home"
|
name="Home"
|
||||||
component={HomeScreen}
|
component={HomeScreen}
|
||||||
options={{
|
options={{
|
||||||
title: '首页',
|
title: '钱包列表',
|
||||||
tabBarLabel: '首页',
|
tabBarLabel: '钱包列表',
|
||||||
tabBarIcon: ({ color, size }) => (
|
tabBarIcon: ({ color, size }) => <WalletIcon color={color} size={size} />,
|
||||||
<Text style={{ fontSize: size, color }}>🏠</Text>
|
}}
|
||||||
),
|
/>
|
||||||
|
<Tab.Screen
|
||||||
|
name="Test"
|
||||||
|
component={TestScreen}
|
||||||
|
options={{
|
||||||
|
title: '测试',
|
||||||
|
tabBarLabel: '测试',
|
||||||
|
tabBarIcon: ({ color, size }) => <ToolIcon color={color} size={size} />,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Tab.Screen
|
<Tab.Screen
|
||||||
@@ -43,9 +71,7 @@ export default function App() {
|
|||||||
options={{
|
options={{
|
||||||
title: 'IM消息',
|
title: 'IM消息',
|
||||||
tabBarLabel: 'IM消息',
|
tabBarLabel: 'IM消息',
|
||||||
tabBarIcon: ({ color, size }) => (
|
tabBarIcon: ({ color, size }) => <MessageIcon color={color} size={size} />,
|
||||||
<Text style={{ fontSize: size, color }}>💬</Text>
|
|
||||||
),
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Tab.Navigator>
|
</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');
|
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
|
||||||
|
|
||||||
/**
|
const defaultConfig = getDefaultConfig(__dirname);
|
||||||
* Metro configuration
|
const { assetExts, sourceExts } = defaultConfig.resolver;
|
||||||
* https://facebook.github.io/metro/docs/configuration
|
|
||||||
*
|
|
||||||
* @type {import('metro-config').MetroConfig}
|
|
||||||
*/
|
|
||||||
const config = {};
|
|
||||||
|
|
||||||
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-gesture-handler": "~2.9.0",
|
||||||
"react-native-safe-area-context": "^4.4.1",
|
"react-native-safe-area-context": "^4.4.1",
|
||||||
"react-native-screens": "~3.36.0",
|
"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-tcp-socket": "^6.4.1",
|
||||||
"react-native-webview": "13.6.2",
|
"react-native-webview": "13.6.2",
|
||||||
"rnauto": "./libs/rnauto",
|
"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 { WalletType } from 'rnwalletman';
|
||||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
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 DEFAULT_DOMAIN = 'aa.pfgame.org';
|
||||||
const STORAGE_KEY = 'server_domain';
|
const STORAGE_KEY = 'server_domain';
|
||||||
const HTTPS_KEY = 'server_https';
|
const HTTPS_KEY = 'server_https';
|
||||||
@@ -117,6 +125,44 @@ class Api {
|
|||||||
if (!data.success) throw new Error(data.message);
|
if (!data.success) throw new Error(data.message);
|
||||||
return data;
|
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;
|
export default Api;
|
||||||
|
|||||||