Browse Source

谷歌登录和paypal的回转

main
Your Name 3 weeks ago
parent
commit
df832cdd1f
  1. 69
      App.tsx
  2. 10
      app/constants/productStatus.ts
  3. 16
      app/contexts/AuthContext.tsx
  4. 17
      app/i18n/index.ts
  5. 2
      app/navigation/TabNavigator.tsx
  6. 120
      app/screens/BalanceScreen.tsx
  7. 72
      app/screens/CategoryScreen.tsx
  8. 17
      app/screens/ChatScreen.tsx
  9. 52
      app/screens/ProfileScreen.tsx
  10. 671
      app/screens/RechargeScreen.tsx
  11. 62
      app/screens/login/Google.tsx
  12. 2
      app/screens/loginList/PhoneLoginModal.tsx
  13. 22
      app/screens/pay/Pay.tsx
  14. 4
      app/screens/previewOrder/PaymentMethod.tsx
  15. 3
      app/screens/previewOrder/PreviewAddress.tsx
  16. 166
      app/screens/previewOrder/ShippingFee.tsx
  17. 8
      app/screens/previewOrder/perviewOrder.tsx
  18. 2
      app/screens/productStatus/OrderDatails.tsx
  19. 6
      app/screens/productStatus/Progress.tsx
  20. 1
      app/services/api/categories.ts
  21. 10
      app/services/api/chat.ts
  22. 8
      app/services/api/login.tsx
  23. 19
      app/services/api/payApi.ts
  24. 4
      app/services/api/userApi.ts
  25. 12
      app/store/burialPoint.ts
  26. BIN
      assets/img/1034058.png
  27. BIN
      assets/img/category_24.jpg

69
App.tsx

@ -4,16 +4,27 @@ import { useEffect, useState } from "react";
import { userApi } from "./app/services/api/userApi"; import { userApi } from "./app/services/api/userApi";
import useUserStore from "./app/store/user"; import useUserStore from "./app/store/user";
import useBurialPointStore from "./app/store/burialPoint"; import useBurialPointStore from "./app/store/burialPoint";
import { AuthProvider, useAuth } from "./app/contexts/AuthContext"; import { AuthProvider, useAuth, AUTH_EVENTS } from "./app/contexts/AuthContext";
import { GestureHandlerRootView } from "react-native-gesture-handler"; import { GestureHandlerRootView } from "react-native-gesture-handler";
import { AppNavigator } from "./app/navigation/AppNavigator"; import { AppNavigator } from "./app/navigation/AppNavigator";
import { View, ActivityIndicator, Alert } from "react-native"; import { View, ActivityIndicator, Alert } from "react-native";
import AsyncStorage from "@react-native-async-storage/async-storage"; import AsyncStorage from "@react-native-async-storage/async-storage";
import "./app/i18n"; import "./app/i18n";
import * as Linking from 'expo-linking'; import * as Linking from "expo-linking";
import { EventEmitter } from 'events';
// 声明全局事件发射器类型
declare global {
var EventEmitter: EventEmitter;
}
// 创建全局事件发射器
if (!global.EventEmitter) {
global.EventEmitter = new EventEmitter();
}
// 定义全局事件处理支付成功 // 定义全局事件处理支付成功
export const PAYMENT_SUCCESS_EVENT = 'PAYMENT_SUCCESS_EVENT'; export const PAYMENT_SUCCESS_EVENT = "PAYMENT_SUCCESS_EVENT";
function AppContent() { function AppContent() {
const { setUser } = useUserStore(); const { setUser } = useUserStore();
@ -21,12 +32,22 @@ function AppContent() {
const { logAppLaunch } = useBurialPointStore(); const { logAppLaunch } = useBurialPointStore();
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
// 获取用户资料的函数
const fetchUserProfile = async () => {
try {
const user = await userApi.getProfile();
setUser(user);
return true;
} catch (error) {
console.error('Failed to fetch user profile:', error);
return false;
}
};
useEffect(() => { useEffect(() => {
const initApp = async () => { const initApp = async () => {
try { try {
await fetchUserProfile();
const user = await userApi.getProfile();
setUser(user);
// 记录应用启动成功埋点 // 记录应用启动成功埋点
logAppLaunch(true); logAppLaunch(true);
@ -42,24 +63,38 @@ function AppContent() {
initApp(); initApp();
}, []); }, []);
// 监听登录成功事件,刷新用户资料
useEffect(() => {
const handleLoginSuccess = () => {
fetchUserProfile();
};
// 注册事件监听器
global.EventEmitter.on(AUTH_EVENTS.LOGIN_SUCCESS, handleLoginSuccess);
// 清理函数
return () => {
global.EventEmitter.off(AUTH_EVENTS.LOGIN_SUCCESS, handleLoginSuccess);
};
}, []);
// 添加深度链接处理 // 添加深度链接处理
useEffect(() => { useEffect(() => {
// 处理深度链接 // 处理深度链接
const handleDeepLink = ({ url }: { url: string }) => { const handleDeepLink = ({ url }: { url: string }) => {
console.log('Global deep link received:', url);
if ( if (
url.startsWith('myapp://payment-success') || url.startsWith("myapp://payment-success") ||
url.startsWith('exp://192.168.0.101:8084/--/payment-success') url.startsWith("exp://192.168.0.101:8084/--/payment-success")
) { ) {
// 解析参数 // 解析参数
const parsed = Linking.parse(url); const parsed = Linking.parse(url);
const params = parsed.queryParams || {}; const params = parsed.queryParams || {};
const paymentId = params.paymentId || ''; const paymentId = params.paymentId || "";
const token = params.token || ''; const token = params.token || "";
const payerId = params.PayerID || ''; const payerId = params.PayerID || "";
Alert.alert( Alert.alert(
'支付成功!', "支付成功!",
`支付ID: ${paymentId}\nToken: ${token}\nPayerID: ${payerId}` `支付ID: ${paymentId}\nToken: ${token}\nPayerID: ${payerId}`
); );
// 这里可以做页面跳转或业务处理 // 这里可以做页面跳转或业务处理
@ -67,11 +102,11 @@ function AppContent() {
}; };
// 注册深度链接监听器 // 注册深度链接监听器
const subscription = Linking.addEventListener('url', handleDeepLink); const subscription = Linking.addEventListener("url", handleDeepLink);
// 处理应用冷启动的深度链接 // 处理应用冷启动的深度链接
Linking.getInitialURL().then((url) => { Linking.getInitialURL().then((url) => {
if (url && url.startsWith('myapp://payment-success')) { if (url && url.startsWith("myapp://payment-success")) {
handleDeepLink({ url }); handleDeepLink({ url });
} }
}); });
@ -81,7 +116,7 @@ function AppContent() {
if (isLoading) { if (isLoading) {
return ( return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}> <View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<ActivityIndicator size="large" color="#0066FF" /> <ActivityIndicator size="large" color="#0066FF" />
</View> </View>
); );

10
app/constants/productStatus.ts

@ -12,9 +12,9 @@ export const productStatus: ProductStatus[] = [
{ icon: PdfDocumentIcon, text: '待付款', textKey: 'order.status.waiting_payment', status: 0 }, { icon: PdfDocumentIcon, text: '待付款', textKey: 'order.status.waiting_payment', status: 0 },
// { icon: DocumentClockIcon, text: '付运费', textKey: 'order.status.pay_shipping', status: 6 }, // { icon: DocumentClockIcon, text: '付运费', textKey: 'order.status.pay_shipping', status: 6 },
{ icon: DocumentClockIcon, text: '待发货', textKey: 'order.status.waiting_shipment', status: 1 }, { icon: DocumentClockIcon, text: '待发货', textKey: 'order.status.waiting_shipment', status: 1 },
{ icon: DocumentApprovedIcon, text: '运输中', textKey: 'order.status.in_transit', status: 7 }, { icon: DocumentApprovedIcon, text: '运输中', textKey: 'order.status.in_transit', status: 2 },
{ icon: DocumentApprovedIcon, text: '代收货', textKey: 'order.status.waiting_receipt', status: 2 }, { icon: DocumentApprovedIcon, text: '代收货', textKey: 'order.status.waiting_receipt', status: 3 },
{ icon: PdfDocumentIcon, text: '已完成', textKey: 'order.status.completed', status: 3 }, { icon: PdfDocumentIcon, text: '已完成', textKey: 'order.status.completed', status: 4 },
{ icon: DocumentClockIcon, text: '已取消', textKey: 'order.status.cancelled', status: 4 }, { icon: DocumentClockIcon, text: '已取消', textKey: 'order.status.cancelled', status: 5 },
{ icon: DocumentClockIcon, text: '已退款', textKey: 'order.status.refunded', status: 5 } { icon: DocumentClockIcon, text: '已退款', textKey: 'order.status.refunded', status: 6 }
] ]

16
app/contexts/AuthContext.tsx

@ -1,5 +1,16 @@
import React, { createContext, useContext, useState, useEffect } from 'react'; import React, { createContext, useContext, useState, useEffect } from 'react';
import AsyncStorage from '@react-native-async-storage/async-storage'; import AsyncStorage from '@react-native-async-storage/async-storage';
import { EventEmitter } from 'events';
// 声明全局事件发射器类型
declare global {
var EventEmitter: EventEmitter;
}
// 添加自定义事件
export const AUTH_EVENTS = {
LOGIN_SUCCESS: 'LOGIN_SUCCESS'
};
type AuthContextType = { type AuthContextType = {
isLoggedIn: boolean; isLoggedIn: boolean;
@ -30,6 +41,11 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
try { try {
await AsyncStorage.setItem('isLoggedIn', 'true'); await AsyncStorage.setItem('isLoggedIn', 'true');
setIsLoggedIn(true); setIsLoggedIn(true);
// 发出登录成功事件通知
if (global.EventEmitter) {
global.EventEmitter.emit(AUTH_EVENTS.LOGIN_SUCCESS);
}
} catch (error) { } catch (error) {
console.error('Error logging in:', error); console.error('Error logging in:', error);
} }

17
app/i18n/index.ts

@ -12,6 +12,9 @@ const LANGUAGE_KEY = '@app_language';
// 获取设备语言 // 获取设备语言
const deviceLanguage = Localization.locale.split('-')[0]; const deviceLanguage = Localization.locale.split('-')[0];
// 确定使用的语言:仅在系统语言为法语时使用法语,其他语言一律使用英语
const initialLanguage = deviceLanguage === 'fr' ? 'fr' : 'en';
// 初始化 i18n // 初始化 i18n
i18n i18n
.use(initReactI18next) .use(initReactI18next)
@ -27,7 +30,7 @@ i18n
translation: frTranslation translation: frTranslation
} }
}, },
lng: deviceLanguage, // 使用设备语言 lng: initialLanguage, // 根据逻辑设置初始语言
fallbackLng: 'en', // 如果找不到翻译,使用英语 fallbackLng: 'en', // 如果找不到翻译,使用英语
interpolation: { interpolation: {
escapeValue: false // 不需要转义 HTML escapeValue: false // 不需要转义 HTML
@ -40,7 +43,9 @@ const loadLanguage = async () => {
try { try {
const savedLanguage = await AsyncStorage.getItem(LANGUAGE_KEY); const savedLanguage = await AsyncStorage.getItem(LANGUAGE_KEY);
if (savedLanguage) { if (savedLanguage) {
i18n.changeLanguage(savedLanguage); // 仅接受法语或英语作为有效选择
const validLanguage = savedLanguage === 'fr' ? 'fr' : 'en';
i18n.changeLanguage(validLanguage);
} }
} catch (error) { } catch (error) {
console.error('加载语言设置失败:', error); console.error('加载语言设置失败:', error);
@ -58,10 +63,12 @@ const saveLanguage = async (language: string) => {
// 修改语言切换函数 // 修改语言切换函数
export const changeLanguage = async (language: string) => { export const changeLanguage = async (language: string) => {
await saveLanguage(language); // 仅接受法语或英语作为有效选择
i18n.changeLanguage(language); const validLanguage = language === 'fr' ? 'fr' : 'en';
await saveLanguage(validLanguage);
i18n.changeLanguage(validLanguage);
console.log("切换语言:" + language); console.log("切换语言:" + validLanguage);
}; };

2
app/navigation/TabNavigator.tsx

@ -163,7 +163,7 @@ export const TabNavigator = () => {
}} }}
/> />
<Tab.Screen <Tab.Screen
name="Category" name="productCollection"
component={CategoryScreen} component={CategoryScreen}
options={{ options={{
tabBarLabel: t('categories'), tabBarLabel: t('categories'),

120
app/screens/BalanceScreen.tsx

@ -1,5 +1,5 @@
// 余额管理 // 余额管理
import React, { useState } from 'react'; import React, { useState, useEffect } from 'react';
import { View, Text, StyleSheet, Image, ScrollView, TouchableOpacity, Modal, SafeAreaView, StatusBar, Platform } from 'react-native'; import { View, Text, StyleSheet, Image, ScrollView, TouchableOpacity, Modal, SafeAreaView, StatusBar, Platform } from 'react-native';
import fontSize from '../utils/fontsizeUtils'; import fontSize from '../utils/fontsizeUtils';
import widthUtils from '../utils/widthUtils'; import widthUtils from '../utils/widthUtils';
@ -8,15 +8,18 @@ import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { RootStackParamList } from '../navigation/types'; import { RootStackParamList } from '../navigation/types';
import RechargeScreen from './RechargeScreen'; import RechargeScreen from './RechargeScreen';
import BackIcon from '../components/BackIcon'; import BackIcon from '../components/BackIcon';
import useUserStore from '../store/user';
import { rechargeHistory, payApi } from '../services/api/payApi';
type BalanceScreenNavigationProp = NativeStackNavigationProp< type BalanceScreenNavigationProp = NativeStackNavigationProp<
RootStackParamList, RootStackParamList,
'Balance' 'Balance'
>; >;
export const BalanceScreen = () => { export const BalanceScreen = () => {
const { user } = useUserStore();
const navigation = useNavigation<BalanceScreenNavigationProp>(); const navigation = useNavigation<BalanceScreenNavigationProp>();
const [isModalVisible, setIsModalVisible] = useState(false); const [isModalVisible, setIsModalVisible] = useState(false);
const [rechargeHistory, setRechargeHistory] = useState<rechargeHistory[]>([]);
const handleOpenModal = () => { const handleOpenModal = () => {
setIsModalVisible(true); setIsModalVisible(true);
@ -26,6 +29,14 @@ export const BalanceScreen = () => {
setIsModalVisible(false); setIsModalVisible(false);
}; };
useEffect(() => {
const fetchRechargeHistory = async () => {
const response = await payApi.getRechargeHistory();
setRechargeHistory(response);
}
fetchRechargeHistory();
}, []);
return ( return (
<SafeAreaView style={styles.safeArea}> <SafeAreaView style={styles.safeArea}>
<StatusBar barStyle="dark-content" backgroundColor="#fff" /> <StatusBar barStyle="dark-content" backgroundColor="#fff" />
@ -60,13 +71,13 @@ export const BalanceScreen = () => {
<View style={styles.totalBalanceCard}> <View style={styles.totalBalanceCard}>
<View style={styles.cardContainer}> <View style={styles.cardContainer}>
<View style={styles.financialInfoContainer}> <View style={styles.financialInfoContainer}>
<Text style={styles.largeBlackText}>650,000</Text> <Text style={styles.largeBlackText}>{user?.balance}</Text>
<View style={styles.svgContainer}> <View style={styles.svgContainer}>
{/* SVG or icon replacement */} {/* SVG or icon replacement */}
</View> </View>
</View> </View>
<View style={styles.totalSoldInfoContainer}> <View style={styles.totalSoldInfoContainer}>
<Text style={styles.totalSoldeText}>Solde Total (FCFA)</Text> <Text style={styles.totalSoldeText}>Solde Total ({user?.currency})</Text>
<View style={styles.totalBalanceInfoContainer}> <View style={styles.totalBalanceInfoContainer}>
<View style={styles.verticalCenteredTextBox}> <View style={styles.verticalCenteredTextBox}>
<Text style={styles.highlightedText}>50,000</Text> <Text style={styles.highlightedText}>50,000</Text>
@ -90,87 +101,28 @@ export const BalanceScreen = () => {
<View style={styles.balanceDetailContainer}> <View style={styles.balanceDetailContainer}>
<Text style={styles.balanceDetailTitle}>Détail du solde</Text> <Text style={styles.balanceDetailTitle}>Détail du solde</Text>
<View style={styles.transactionDetailsContainer1}> <View style={styles.transactionDetailsContainer1}>
<View style={styles.transactionHistoryList}>
{/* Repeated Transaction Details */}
<View style={styles.transactionDetailsPanel}>
<View style={styles.transactionDetailsRow}>
<Text style={styles.transactionDescriptionBold}>
Frais de transport
</Text>
<Text style={styles.transactionAmountDisplay}>-58 FCFA</Text>
</View>
<View style={styles.transactionInfoRow}>
<Text style={styles.transactionDate}>2025-02-12</Text>
<Text style={styles.shipmentReference}>BR-11-00010</Text>
</View>
</View>
{/* Additional transaction panels can be added here */}
</View>
<View style={styles.transactionHistoryList}> {rechargeHistory.map((item) => (
{/* Repeated Transaction Details */} <View style={styles.transactionHistoryList}>
<View style={styles.transactionDetailsPanel}> {/* Repeated Transaction Details */}
<View style={styles.transactionDetailsRow}> <View style={styles.transactionDetailsPanel}>
<Text style={styles.transactionDescriptionBold}> <View style={styles.transactionDetailsRow}>
Frais de transport <Text style={styles.transactionDescriptionBold}>
</Text> {item.payment_method}
<Text style={styles.transactionAmountDisplay}>-58 FCFA</Text> </Text>
</View> <Text style={styles.transactionAmountDisplay}>{item.amount} {item.currency}</Text>
<View style={styles.transactionInfoRow}> </View>
<Text style={styles.transactionDate}>2025-02-12</Text> <View style={styles.transactionInfoRow}>
<Text style={styles.shipmentReference}>BR-11-00010</Text> <Text style={styles.transactionDate}>{item.create_time}</Text>
</View> <Text style={styles.shipmentReference}>{item.transaction_id}</Text>
</View> </View>
{/* Additional transaction panels can be added here */} </View>
</View> {/* Additional transaction panels can be added here */}
<View style={styles.transactionHistoryList}> </View>
{/* Repeated Transaction Details */} ))}
<View style={styles.transactionDetailsPanel}>
<View style={styles.transactionDetailsRow}>
<Text style={styles.transactionDescriptionBold}>
Frais de transport
</Text>
<Text style={styles.transactionAmountDisplay}>-58 FCFA</Text>
</View>
<View style={styles.transactionInfoRow}>
<Text style={styles.transactionDate}>2025-02-12</Text>
<Text style={styles.shipmentReference}>BR-11-00010</Text>
</View>
</View>
{/* Additional transaction panels can be added here */}
</View>
<View style={styles.transactionHistoryList}>
{/* Repeated Transaction Details */}
<View style={styles.transactionDetailsPanel}>
<View style={styles.transactionDetailsRow}>
<Text style={styles.transactionDescriptionBold}>
Frais de transport
</Text>
<Text style={styles.transactionAmountDisplay}>-58 FCFA</Text>
</View>
<View style={styles.transactionInfoRow}>
<Text style={styles.transactionDate}>2025-02-12</Text>
<Text style={styles.shipmentReference}>BR-11-00010</Text>
</View>
</View>
{/* Additional transaction panels can be added here */}
</View>
<View style={styles.transactionHistoryList}>
{/* Repeated Transaction Details */}
<View style={styles.transactionDetailsPanel}>
<View style={styles.transactionDetailsRow}>
<Text style={styles.transactionDescriptionBold}>
Frais de transport
</Text>
<Text style={styles.transactionAmountDisplay}>-58 FCFA</Text>
</View>
<View style={styles.transactionInfoRow}>
<Text style={styles.transactionDate}>2025-02-12</Text>
<Text style={styles.shipmentReference}>BR-11-00010</Text>
</View>
</View>
{/* Additional transaction panels can be added here */}
</View>
</View> </View>
</View> </View>
</View> </View>

72
app/screens/CategoryScreen.tsx

@ -16,8 +16,20 @@ import {
import fontSize from '../utils/fontsizeUtils'; import fontSize from '../utils/fontsizeUtils';
import widthUtils from '../utils/widthUtils'; import widthUtils from '../utils/widthUtils';
import { categoriesApi, Category } from '../services/api/categories'; import { categoriesApi, Category } from '../services/api/categories';
import { useNavigation } from '@react-navigation/native';
import { NativeStackNavigationProp } from "@react-navigation/native-stack";
import { RootStackParamList } from '../types/navigation';
const { width: SCREEN_WIDTH } = Dimensions.get('window');
const MENU_WIDTH = widthUtils(120, 120).width;
const AVAILABLE_WIDTH = SCREEN_WIDTH - MENU_WIDTH - 20; // 20 for padding
const NUM_COLUMNS = 4;
const ITEM_MARGIN = '2.66%';
const ITEM_WIDTH = (AVAILABLE_WIDTH / NUM_COLUMNS) - (AVAILABLE_WIDTH * 0.0266);
export const CategoryScreen = () => { export const CategoryScreen = () => {
const navigation = useNavigation<NativeStackNavigationProp<RootStackParamList>>();
const [mainCategories, setMainCategories] = useState<Category[]>([]); const [mainCategories, setMainCategories] = useState<Category[]>([]);
const [subCategories, setSubCategories] = useState<Category[]>([]); const [subCategories, setSubCategories] = useState<Category[]>([]);
const [activeMainCategory, setActiveMainCategory] = useState<number | null>(null); const [activeMainCategory, setActiveMainCategory] = useState<number | null>(null);
@ -85,18 +97,25 @@ export const CategoryScreen = () => {
const renderSubCategoryItem: ListRenderItem<Category> = ({ item }) => ( const renderSubCategoryItem: ListRenderItem<Category> = ({ item }) => (
<TouchableOpacity <TouchableOpacity
style={[ style={styles.subCategoryItem}
styles.menuItem, onPress={() => {
]} navigation.navigate('SearchResult', { keyword: item.name_en.trim() });
onPress={() => {}} }}
> >
<Text <Image
style={styles.menuText} source={item.image_url ? { uri: item.image_url } : require('../../assets/img/1034058.png')}
numberOfLines={1} style={styles.subCategoryImage}
ellipsizeMode="tail" resizeMode="cover"
> />
{item.name_cn} <View style={styles.subCategoryInfo}>
</Text> <Text
style={styles.subCategoryName}
numberOfLines={2}
ellipsizeMode="tail"
>
{item.name_cn}
</Text>
</View>
</TouchableOpacity> </TouchableOpacity>
); );
@ -130,8 +149,9 @@ export const CategoryScreen = () => {
data={subCategories} data={subCategories}
renderItem={renderSubCategoryItem} renderItem={renderSubCategoryItem}
keyExtractor={(item) => item.category_id.toString()} keyExtractor={(item) => item.category_id.toString()}
numColumns={3} numColumns={NUM_COLUMNS}
contentContainerStyle={styles.productGrid} contentContainerStyle={styles.productGrid}
columnWrapperStyle={styles.columnWrapper}
/> />
)} )}
</View> </View>
@ -155,7 +175,7 @@ const styles = StyleSheet.create({
flexDirection: 'row', flexDirection: 'row',
}, },
leftMenu: { leftMenu: {
width: widthUtils(100,100).width, width: MENU_WIDTH,
backgroundColor: '#fff', backgroundColor: '#fff',
borderRightWidth: 1, borderRightWidth: 1,
borderColor: '#eee', borderColor: '#eee',
@ -179,7 +199,7 @@ const styles = StyleSheet.create({
}, },
rightContent: { rightContent: {
flex: 1, flex: 1,
backgroundColor: '#f8f8f8', backgroundColor: '#ffffff',
paddingHorizontal: 10, paddingHorizontal: 10,
paddingTop: 12, paddingTop: 12,
}, },
@ -192,4 +212,28 @@ const styles = StyleSheet.create({
alignItems: 'center', alignItems: 'center',
backgroundColor: '#fff', backgroundColor: '#fff',
}, },
columnWrapper: {
justifyContent: 'flex-start',
marginBottom: 15,
},
subCategoryItem: {
width: ITEM_WIDTH,
overflow: 'hidden',
marginRight: ITEM_MARGIN,
},
subCategoryImage: {
width: '100%',
height: ITEM_WIDTH,
},
subCategoryInfo: {
padding: 8,
height: 26,
justifyContent: 'center',
alignItems: 'center',
},
subCategoryName: {
fontSize: fontSize(12),
color: '#333',
textAlign: 'center',
},
}); });

17
app/screens/ChatScreen.tsx

@ -29,7 +29,7 @@ interface Message {
mimetype: string; mimetype: string;
userWs: string; userWs: string;
app_id: string; app_id: string;
country: string; country: number;
body: string; body: string;
text: string; text: string;
type: string; type: string;
@ -130,10 +130,10 @@ export const ChatScreen = () => {
mimetype: "text/plain", mimetype: "text/plain",
userWs: "unknown", userWs: "unknown",
app_id: user.user_id ? user.user_id.toString() : "", app_id: user.user_id ? user.user_id.toString() : "",
country: country, country: user.country_code,
body: "", body: "",
text: inputText, text: inputText,
type: "chat", type: "text",
isMe: true, isMe: true,
timestamp: new Date(), timestamp: new Date(),
id: Date.now().toString(), // Add unique id for keyExtractor id: Date.now().toString(), // Add unique id for keyExtractor
@ -160,7 +160,7 @@ export const ChatScreen = () => {
mimetype: "text/plain", mimetype: "text/plain",
userWs: "system", userWs: "system",
app_id: "system", app_id: "system",
country: country, country: user.country_code,
body: "", body: "",
text: `${t('typingMessage')}...`, text: `${t('typingMessage')}...`,
type: "chat", type: "chat",
@ -174,8 +174,11 @@ export const ChatScreen = () => {
setMessages(prevMessages => [...prevMessages, simulatedResponse]); setMessages(prevMessages => [...prevMessages, simulatedResponse]);
}, 800); }, 800);
const data = {
newMessage:chatServiceMessage,
}
// Send actual message to API // Send actual message to API
chatService.sendMessage(chatServiceMessage) chatService.sendMessage(data)
.then(response => { .then(response => {
// When real response arrives, replace simulated message // When real response arrives, replace simulated message
setMessages(prevMessages => { setMessages(prevMessages => {
@ -187,7 +190,7 @@ export const ChatScreen = () => {
mimetype: "text/plain", mimetype: "text/plain",
userWs: "system", userWs: "system",
app_id: "system", app_id: "system",
country: country, country: user.country_code,
body: "", body: "",
text: response?.text || t('defaultResponse'), text: response?.text || t('defaultResponse'),
type: "chat", type: "chat",
@ -209,7 +212,7 @@ export const ChatScreen = () => {
mimetype: "text/plain", mimetype: "text/plain",
userWs: "system", userWs: "system",
app_id: "system", app_id: "system",
country: country, country: user.country_code,
body: "", body: "",
text: t('errorResponse'), text: t('errorResponse'),
type: "chat", type: "chat",

52
app/screens/ProfileScreen.tsx

@ -94,15 +94,17 @@ export const ProfileScreen = () => {
ID: {user?.user_id} ID: {user?.user_id}
</Text> </Text>
</View> </View>
<TouchableOpacity {user.user_id && (
style={styles.transactionSummaryBox} <TouchableOpacity
onPress={() => navigation.navigate("Balance")} style={styles.transactionSummaryBox}
> onPress={() => navigation.navigate("Balance")}
<View style={styles.svgContainer}> >
<BookmarkIcon size={fontSize(24)} /> <View style={styles.svgContainer}>
</View> <BookmarkIcon size={fontSize(24)} />
<Text style={styles.soldeTextDisplayStyle}></Text> </View>
</TouchableOpacity> <Text style={styles.soldeTextDisplayStyle}></Text>
</TouchableOpacity>
)}
</View> </View>
</View> </View>
</View> </View>
@ -136,7 +138,7 @@ export const ProfileScreen = () => {
<View style={styles.vipExplanation}> <View style={styles.vipExplanation}>
<View style={styles.vipExplanationText}> <View style={styles.vipExplanationText}>
<Text style={styles.vipExplanationText1}> <Text style={styles.vipExplanationText1}>
{t('profile.vip.next_level_info')} {t("profile.vip.next_level_info")}
</Text> </Text>
</View> </View>
<TouchableOpacity <TouchableOpacity
@ -146,7 +148,7 @@ export const ProfileScreen = () => {
} }
> >
<Text style={styles.vipExplanationButtonText}> <Text style={styles.vipExplanationButtonText}>
{t('profile.learn_more')} {t("profile.learn_more")}
</Text> </Text>
</TouchableOpacity> </TouchableOpacity>
</View> </View>
@ -213,14 +215,18 @@ export const ProfileScreen = () => {
<View style={styles.groupContainer}> <View style={styles.groupContainer}>
<View style={styles.groupItemList}> <View style={styles.groupItemList}>
<View style={styles.groupItemTitle}> <View style={styles.groupItemTitle}>
<Text style={styles.groupItemTitleText}>{t("order.title")}</Text> <Text style={styles.groupItemTitleText}>
{t("order.title")}
</Text>
<TouchableOpacity <TouchableOpacity
style={styles.groupItemTitleTextTout} style={styles.groupItemTitleTextTout}
onPress={() => onPress={() =>
navigation.navigate("Status", { status: null }) navigation.navigate("Status", { status: null })
} }
> >
<Text style={styles.groupItemTitleTextTout1}>{t("all")}</Text> <Text style={styles.groupItemTitleTextTout1}>
{t("all")}
</Text>
<LeftArrowIcon size={fontSize(14)} color="#8f8684" /> <LeftArrowIcon size={fontSize(14)} color="#8f8684" />
</TouchableOpacity> </TouchableOpacity>
</View> </View>
@ -253,7 +259,9 @@ export const ProfileScreen = () => {
<View style={styles.groupContainer}> <View style={styles.groupContainer}>
<View style={styles.groupItemList}> <View style={styles.groupItemList}>
<View style={styles.groupItemTitle}> <View style={styles.groupItemTitle}>
<Text style={styles.groupItemTitleText}>{t("tool.title")}</Text> <Text style={styles.groupItemTitleText}>
{t("tool.title")}
</Text>
</View> </View>
<View style={styles.groupItem}> <View style={styles.groupItem}>
@ -267,7 +275,9 @@ export const ProfileScreen = () => {
color="#707070" color="#707070"
/> />
</TouchableOpacity> </TouchableOpacity>
<Text style={styles.groupItemContentText}>{t("browse.history")}</Text> <Text style={styles.groupItemContentText}>
{t("browse.history")}
</Text>
</View> </View>
<View style={styles.groupItemContent}> <View style={styles.groupItemContent}>
@ -277,7 +287,9 @@ export const ProfileScreen = () => {
> >
<PdfDocumentIcon size={fontSize(38)} color="#707070" /> <PdfDocumentIcon size={fontSize(38)} color="#707070" />
</TouchableOpacity> </TouchableOpacity>
<Text style={styles.groupItemContentText}>{t("collection")}</Text> <Text style={styles.groupItemContentText}>
{t("collection")}
</Text>
</View> </View>
<TouchableOpacity <TouchableOpacity
@ -287,7 +299,9 @@ export const ProfileScreen = () => {
<View style={styles.groupItemContentIcon}> <View style={styles.groupItemContentIcon}>
<DocumentClockIcon size={fontSize(38)} color="#707070" /> <DocumentClockIcon size={fontSize(38)} color="#707070" />
</View> </View>
<Text style={styles.groupItemContentText}>{t("address.management")}</Text> <Text style={styles.groupItemContentText}>
{t("address.management")}
</Text>
</TouchableOpacity> </TouchableOpacity>
</View> </View>
</View> </View>
@ -295,8 +309,8 @@ export const ProfileScreen = () => {
</ScrollView> </ScrollView>
{/* Login Button at bottom of screen */} {/* Login Button at bottom of screen */}
<TouchableOpacity <TouchableOpacity
style={styles.fixedLoginButton} style={styles.fixedLoginButton}
onPress={handleLogin} onPress={handleLogin}
> >
<Text style={styles.loginButtonText}>{t("login.now")}</Text> <Text style={styles.loginButtonText}>{t("login.now")}</Text>

671
app/screens/RechargeScreen.tsx

File diff suppressed because it is too large Load Diff

62
app/screens/login/Google.tsx

@ -4,9 +4,10 @@ import { WebView } from "react-native-webview";
import { useNavigation } from "@react-navigation/native"; import { useNavigation } from "@react-navigation/native";
import { loginApi } from "../../services/api/login"; import { loginApi } from "../../services/api/login";
import { useAuth } from "../../contexts/AuthContext"; import { useAuth } from "../../contexts/AuthContext";
import AsyncStorage from "@react-native-async-storage/async-storage";
export const GoogleScreen = () => { export const GoogleScreen = () => {
const navigation = useNavigation(); const navigation = useNavigation<any>();
const { login } = useAuth(); const { login } = useAuth();
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [loginUrl, setLoginUrl] = useState<string | null>(null); const [loginUrl, setLoginUrl] = useState<string | null>(null);
@ -15,27 +16,29 @@ export const GoogleScreen = () => {
const fetchLoginUrl = async () => { const fetchLoginUrl = async () => {
try { try {
const response = await loginApi.google(); const response = await loginApi.google();
if (response.data.url) {
setLoginUrl(response.data.url);
if (response.url) {
setLoginUrl(response.url);
} }
} catch (error) { } catch (error) {
console.error('Failed to fetch login URL:', error); console.error("Failed to fetch login URL:", error);
} }
}; };
fetchLoginUrl(); fetchLoginUrl();
}, []); }, []);
const handleNavigationStateChange = async (navState: any) => { const handleNavigationStateChange = async (navState: any) => {
console.log(navState.url); console.log(navState.url);
// 检查URL是否包含重定向URI // 检查URL是否包含重定向URI
if (navState.url.includes('localhost:8000')) { if (navState.url.includes("localhost:8000")) {
try { try {
await login(); await login();
navigation.navigate('MainTabs' as never); navigation.navigate("MainTabs" as never);
} catch (error) { } catch (error) {
console.error('Login failed:', error); console.error("Login failed:", error);
} }
} }
}; };
@ -60,13 +63,38 @@ export const GoogleScreen = () => {
domStorageEnabled={true} domStorageEnabled={true}
startInLoadingState={true} startInLoadingState={true}
scalesPageToFit={true} scalesPageToFit={true}
originWhitelist={['*']} originWhitelist={["*"]}
incognito={true}
thirdPartyCookiesEnabled={false}
userAgent="Mozilla/5.0 (Linux; Android 10; Pixel 3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Mobile Safari/537.36" userAgent="Mozilla/5.0 (Linux; Android 10; Pixel 3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Mobile Safari/537.36"
onShouldStartLoadWithRequest={(request) => { onShouldStartLoadWithRequest={(request) => {
console.log(request); const { url } = request;
// 拦截 myapp://login-success
// 允许所有请求 if (url.startsWith("myapp://login-success")) {
return true; console.log("拦截成功!跳转地址:", url);
// 提取 token 参数
const urlParams = new URLSearchParams(url.split('?')[1]);
const token = urlParams.get('token');
// 如果有 token 则登录成功,否则跳转回登录页
if (token) {
const tokens = "bearer " + token;
AsyncStorage.setItem("token", tokens);
// 登录成功,更新认证状态
login().then(() => {
// 跳转到主页
navigation.replace("MainTabs", { screen: "Home" });
});
} else {
// 登录失败,跳转回登录页
navigation.replace("Login");
}
return false; // 阻止 WebView 加载这个链接
}
return true; // 其他 URL 继续加载
}} }}
/> />
</View> </View>
@ -79,9 +107,9 @@ const styles = StyleSheet.create({
}, },
loadingContainer: { loadingContainer: {
flex: 1, flex: 1,
justifyContent: 'center', justifyContent: "center",
alignItems: 'center', alignItems: "center",
backgroundColor: 'white', backgroundColor: "white",
}, },
webview: { webview: {
flex: 1, flex: 1,

2
app/screens/loginList/PhoneLoginModal.tsx

@ -240,7 +240,7 @@ const PhoneLoginModal = ({ visible, onClose }: PhoneLoginModalProps) => {
const user = await userApi.getProfile(); const user = await userApi.getProfile();
setUser(user); setUser(user);
setLoading(false); setLoading(false);
navigation.navigate("MainTabs", { screen: "Home" }); navigation.replace("MainTabs", { screen: "Home" });
onClose(); onClose();
logLogin(true, "phone"); logLogin(true, "phone");

22
app/screens/pay/Pay.tsx

@ -21,12 +21,8 @@ export const Pay = () => {
const [payInfo, setPayInfo] = useState<PaymentInfoResponse>(); const [payInfo, setPayInfo] = useState<PaymentInfoResponse>();
useEffect(() => { useEffect(() => {
console.log(route.params);
console.log(payUrl);
// 设置处理深度链接的监听器 // 设置处理深度链接的监听器
const handleDeepLink = ({ url }: { url: string }) => { const handleDeepLink = ({ url }: { url: string }) => {
console.log("Deep link received:", url);
if ( if (
url.includes("myapp://payment-success") || url.includes("myapp://payment-success") ||
url.includes("exp://192.168.0.101:8084/--/payment-success") url.includes("exp://192.168.0.101:8084/--/payment-success")
@ -47,8 +43,6 @@ export const Pay = () => {
}, []); }, []);
const handleNavigationStateChange = (navState: any) => { const handleNavigationStateChange = (navState: any) => {
console.log(navState);
// 检查URL是否包含支付成功的回调参数 // 检查URL是否包含支付成功的回调参数
const { url } = navState; const { url } = navState;
if (url && url.includes("payment_success=true")) { if (url && url.includes("payment_success=true")) {
@ -60,30 +54,22 @@ export const Pay = () => {
// 导航辅助函数,尝试使用多种方式导航 // 导航辅助函数,尝试使用多种方式导航
const safeNavigate = (routeName: string, params: any) => { const safeNavigate = (routeName: string, params: any) => {
console.log(`尝试导航到 ${routeName},参数:`, params);
console.log(`navigationRef准备状态: ${navigationRef.isReady()}`);
try { try {
// 尝试使用组件内的navigation // 尝试使用组件内的navigation
console.log("使用组件内navigation导航");
// @ts-ignore 忽略可能的类型错误 // @ts-ignore 忽略可能的类型错误
navigation.navigate(routeName, params); navigation.navigate(routeName, params);
} catch (e) { } catch (e) {
console.log("组件内导航失败:", e);
try { try {
// 尝试使用全局navigation // 尝试使用全局navigation
console.log("使用全局navigate导航");
navigate(routeName, params); navigate(routeName, params);
} catch (e) { } catch (e) {
console.log("全局导航也失败:", e);
// 最后尝试使用setTimeout延迟导航 // 最后尝试使用setTimeout延迟导航
console.log("尝试延迟导航");
setTimeout(() => { setTimeout(() => {
try { try {
// @ts-ignore 忽略可能的类型错误 // @ts-ignore 忽略可能的类型错误
navigation.navigate(routeName, params); navigation.navigate(routeName, params);
} catch (e) { } catch (e) {
console.log("所有导航方式都失败");
Alert.alert("导航失败", "无法跳转到目标页面"); Alert.alert("导航失败", "无法跳转到目标页面");
} }
}, 500); }, 500);
@ -107,21 +93,15 @@ export const Pay = () => {
originWhitelist={["*"]} originWhitelist={["*"]}
userAgent="Mozilla/5.0 (Linux; Android 10; Pixel 3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Mobile Safari/537.36" userAgent="Mozilla/5.0 (Linux; Android 10; Pixel 3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Mobile Safari/537.36"
onShouldStartLoadWithRequest={(request) => { onShouldStartLoadWithRequest={(request) => {
console.log("Load request:", request);
// 检查URL是否包含支付成功的参数 // 检查URL是否包含支付成功的参数
const { url } = request; const { url } = request;
if (url) { if (url) {
// 解析参数 // 解析参数
const parsed = Linking.parse(url); const parsed = Linking.parse(url);
const params = parsed.queryParams || {}; const params = parsed.queryParams || {};
console.log("解析的URL参数:", params);
// 检查是否存在paymentId参数并且不为null // 检查是否存在paymentId参数并且不为null
if (params.paymentId && params.paymentId !== "null") { if (params.paymentId && params.paymentId !== "null") {
console.log("检测到有效的paymentId:", params.paymentId);
Alert.alert("支付成功");
if (params.PayerID && params.PayerID !== "null") { if (params.PayerID && params.PayerID !== "null") {
payApi payApi
.paySuccessCallback( .paySuccessCallback(

4
app/screens/previewOrder/PaymentMethod.tsx

@ -510,8 +510,10 @@ export const PaymentMethod = () => {
.createOrder(createOrderData as unknown as CreateOrderRequest) .createOrder(createOrderData as unknown as CreateOrderRequest)
.then((res) => { .then((res) => {
setCreateLoading(false); setCreateLoading(false);
logPaymentConfirm(data); logPaymentConfirm(data,navigation.getState().routes[navigation.getState().index - 1]?.name as string);
// go to payment preview // go to payment preview
console.log(getBurialPointData());
navigation.navigate("PreviewOrder", { navigation.navigate("PreviewOrder", {
data: res, data: res,
payMethod: selectedPayment, payMethod: selectedPayment,

3
app/screens/previewOrder/PreviewAddress.tsx

@ -225,7 +225,8 @@ export const PreviewAddress = () => {
phone_number: Number(formData.receiver_phone), phone_number: Number(formData.receiver_phone),
whatsApp_number: Number(formData.whatsapp_phone), whatsApp_number: Number(formData.whatsapp_phone),
} }
logAddressInfo(logData); logAddressInfo(logData,navigation.getState().routes[navigation.getState().index - 1]?.name as string);
console.log(getBurialPointData());
navigation.navigate("ShippingFee",{ navigation.navigate("ShippingFee",{
cart_item_id: route.params, cart_item_id: route.params,

166
app/screens/previewOrder/ShippingFee.tsx

@ -68,13 +68,9 @@ export const ShippingFee = () => {
const [selectedWarehouse, setSelectedWarehouse] = useState<Address>(); const [selectedWarehouse, setSelectedWarehouse] = useState<Address>();
const [domesticShippingFeeData, setDomesticShippingFeeData] = const [domesticShippingFeeData, setDomesticShippingFeeData] =
useState<DomesticShippingFeeData>(); useState<DomesticShippingFeeData>();
const [isDomesticShippingLoading, setIsDomesticShippingLoading] = const [isShippingFeeLoading, setIsShippingFeeLoading] =
useState(false); useState(false);
const [count, setCount] = useState<string>(); const [count, setCount] = useState<string>();
const [apiResponses, setApiResponses] = useState({
shippingFees: false,
domesticShippingFees: false,
});
const { setOrderData, orderData, items } = useCreateOrderStore(); const { setOrderData, orderData, items } = useCreateOrderStore();
const [countryCode, setCountryCode] = useState<number>(); const [countryCode, setCountryCode] = useState<number>();
@ -110,24 +106,21 @@ export const ShippingFee = () => {
useEffect(() => { useEffect(() => {
if (state.shippingFees) { if (state.shippingFees) {
setShippingFeeData(state.shippingFees); setShippingFeeData(state.shippingFees);
setCount(t("order.shipping.calculating"));
setApiResponses((prev) => ({ ...prev, shippingFees: true }));
} }
}, [state.shippingFees, t]); }, [state.shippingFees]);
useEffect(() => { useEffect(() => {
if (state.domesticShippingFees) { if (state.domesticShippingFees) {
setDomesticShippingFeeData(state.domesticShippingFees); setDomesticShippingFeeData(state.domesticShippingFees);
setApiResponses((prev) => ({ ...prev, domesticShippingFees: true }));
} }
}, [state.domesticShippingFees]); }, [state.domesticShippingFees]);
// Effect to handle loading state based on both API responses // 统一处理loading状态
useEffect(() => { useEffect(() => {
if (apiResponses.shippingFees && apiResponses.domesticShippingFees) { if (state.shippingFees && state.domesticShippingFees) {
setIsDomesticShippingLoading(false); setIsShippingFeeLoading(false);
} }
}, [apiResponses]); }, [state.shippingFees, state.domesticShippingFees]);
// Call changeCountryHandel when warehouse changes // Call changeCountryHandel when warehouse changes
useEffect(() => { useEffect(() => {
@ -152,14 +145,9 @@ export const ShippingFee = () => {
// Only calculate if we have the necessary data // Only calculate if we have the necessary data
if (data.items && data.freight_forwarder_address_id) { if (data.items && data.freight_forwarder_address_id) {
// Set loading state to true before making API calls // 设置loading状态为true,开始计算
setIsDomesticShippingLoading(true); setIsShippingFeeLoading(true);
setCount(t("order.shipping.calculating")); setCount(t("order.shipping.calculating"));
// Reset API response tracking
setApiResponses({
shippingFees: false,
domesticShippingFees: false,
});
calculateShippingFee(data); calculateShippingFee(data);
calculateDomesticShippingFee(data); calculateDomesticShippingFee(data);
@ -177,7 +165,7 @@ export const ShippingFee = () => {
const handleSubmit = () => { const handleSubmit = () => {
if ( if (
!isDomesticShippingLoading && !isShippingFeeLoading &&
domesticShippingFeeData?.total_shipping_fee != null domesticShippingFeeData?.total_shipping_fee != null
) { ) {
setOrderData({ setOrderData({
@ -202,8 +190,8 @@ export const ShippingFee = () => {
country_city: selectedWarehouseLabel, country_city: selectedWarehouseLabel,
timestamp: new Date().toISOString(), timestamp: new Date().toISOString(),
}; };
logShippingConfirm(data); logShippingConfirm(data,navigation.getState().routes[navigation.getState().index - 1]?.name as string);
console.log(getBurialPointData());
navigation.navigate("PaymentMethod", { navigation.navigate("PaymentMethod", {
freight_forwarder_address_id: selectedWarehouse?.address_id || 0, freight_forwarder_address_id: selectedWarehouse?.address_id || 0,
}); });
@ -379,84 +367,61 @@ export const ShippingFee = () => {
</Text> </Text>
</Text> </Text>
<View style={styles.shippingInfoRow}> {isShippingFeeLoading ? (
<Text style={styles.shippingInfoLabel}> // 统一显示一个加载状态
{t("order.shipping.domestic_fee")}:{" "} <View style={styles.loadingFeesContainer}>
</Text> <Text style={styles.calculatingText}>
<Text {count}
style={{ </Text>
color: "#ff6000", <ActivityIndicator
flex: 1, size="small"
textAlign: "left", color="#ff6000"
marginLeft: 10, style={{ marginLeft: 5 }}
fontWeight: "600", />
}} </View>
> ) : (
{isDomesticShippingLoading ? ( // 加载完成后同时显示两个费用
<>
<View style={styles.shippingInfoRow}>
<Text style={styles.shippingInfoLabel}>
{t("order.shipping.domestic_fee")}:{" "}
</Text>
<Text <Text
style={{ style={{
color: "#ff6000", color: "#ff6000",
alignItems: "center", flex: 1,
textAlign: "left",
marginLeft: 10,
fontWeight: "600",
}} }}
> >
{count}{" "} <Text style={{ color: "#ff6000" }}>
<ActivityIndicator {domesticShippingFeeData?.total_shipping_fee ||
size="small" 0}{" "}
color="#ff6000" {userStore.user?.currency}
style={{ marginLeft: 5 }} </Text>
/>
</Text> </Text>
) : ( </View>
<Text style={{ color: "#ff6000" }}> <View style={styles.shippingInfoRow}>
{domesticShippingFeeData?.total_shipping_fee || <Text style={styles.shippingInfoLabel}>
0}{" "} {t("order.shipping.international_fee")}:{" "}
</Text>
<Text
style={{
color: "#ff6000",
flex: 1,
textAlign: "left",
marginLeft: 10,
fontWeight: "600",
}}
>
{shippingMethod === "sea"
? shippingFeeData?.total_shipping_fee_sea
: shippingFeeData?.total_shipping_fee_air}{" "}
{userStore.user?.currency} {userStore.user?.currency}
</Text> </Text>
)} </View>
</Text> </>
</View>
{isDomesticShippingLoading ? (
<View style={styles.shippingInfoRow}>
<Text style={styles.shippingInfoLabel}>
{t("order.shipping.international_fee")}:{" "}
</Text>
<Text
style={{
color: "#ff6000",
flex: 1,
textAlign: "left",
marginLeft: 10,
fontWeight: "600",
}}
>
{count}{" "}
<ActivityIndicator
size="small"
color="#ff6000"
style={{ marginLeft: 5 }}
/>
</Text>
</View>
) : (
<View style={styles.shippingInfoRow}>
<Text style={styles.shippingInfoLabel}>
{t("order.shipping.international_fee")}:{" "}
</Text>
<Text
style={{
color: "#ff6000",
flex: 1,
textAlign: "left",
marginLeft: 10,
fontWeight: "600",
}}
>
{shippingMethod === "sea"
? shippingFeeData?.total_shipping_fee_sea
: shippingFeeData?.total_shipping_fee_air}{" "}
{userStore.user?.currency}
</Text>
</View>
)} )}
</View> </View>
)} )}
@ -467,14 +432,14 @@ export const ShippingFee = () => {
<TouchableOpacity <TouchableOpacity
style={[ style={[
styles.primaryButtonStyle, styles.primaryButtonStyle,
isDomesticShippingLoading || isShippingFeeLoading ||
domesticShippingFeeData?.total_shipping_fee == null domesticShippingFeeData?.total_shipping_fee == null
? styles.disabledButtonStyle ? styles.disabledButtonStyle
: {}, : {},
]} ]}
onPress={handleSubmit} onPress={handleSubmit}
disabled={ disabled={
isDomesticShippingLoading || isShippingFeeLoading ||
domesticShippingFeeData?.total_shipping_fee == null domesticShippingFeeData?.total_shipping_fee == null
} }
> >
@ -755,4 +720,15 @@ const styles = StyleSheet.create({
disabledButtonStyle: { disabledButtonStyle: {
backgroundColor: "#ccc", backgroundColor: "#ccc",
}, },
loadingFeesContainer: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
marginVertical: 15,
},
calculatingText: {
color: "#ff6000",
fontSize: fontSize(14),
fontWeight: "500"
},
}); });

8
app/screens/previewOrder/perviewOrder.tsx

@ -23,6 +23,8 @@ import { Order } from "../../services/api/orders";
import { payApi } from "../../services/api/payApi"; import { payApi } from "../../services/api/payApi";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import fontSize from "../../utils/fontsizeUtils"; import fontSize from "../../utils/fontsizeUtils";
import { getBurialPointData } from "../../store/burialPoint";
import useBurialPointStore from "../../store/burialPoint";
// Define the param list for navigation // Define the param list for navigation
type RootStackParamList = { type RootStackParamList = {
@ -46,7 +48,7 @@ export const PreviewOrder = () => {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const { user } = useUserStore(); const { user } = useUserStore();
const { t } = useTranslation(); const { t } = useTranslation();
const { logPreviewOrder } = useBurialPointStore();
useEffect(() => { useEffect(() => {
if (!user.user_id) { if (!user.user_id) {
return Alert.alert(t("order.preview.login_required")); return Alert.alert(t("order.preview.login_required"));
@ -97,8 +99,10 @@ export const PreviewOrder = () => {
.getPayInfo(data) .getPayInfo(data)
.then((res) => { .then((res) => {
if (res.success) { if (res.success) {
logPreviewOrder(navigation.getState().routes[navigation.getState().index - 1]?.name as string);
console.log(getBurialPointData());
navigation.navigate("Pay", { navigation.replace("Pay", {
payUrl: res.payment_url, payUrl: res.payment_url,
}); });
} }

2
app/screens/productStatus/OrderDatails.tsx

@ -248,7 +248,7 @@ export const OrderDetails = () => {
<View style={styles.orderStatusContentPreview}> <View style={styles.orderStatusContentPreview}>
<Progress <Progress
statuses={orderDetails.order_status} statuses={route.params.orderId}
labels={[ labels={[
t("order.status.waiting_payment"), t("order.status.waiting_payment"),
t("order.status.waiting_shipment"), t("order.status.waiting_shipment"),

6
app/screens/productStatus/Progress.tsx

@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next';
import fontSize from '../../utils/fontsizeUtils'; import fontSize from '../../utils/fontsizeUtils';
interface ProgressProps { interface ProgressProps {
statuses: number; statuses: string;
labels?: string[]; labels?: string[];
} }
@ -20,14 +20,14 @@ const Progress: React.FC<ProgressProps> = ({ statuses, labels = [] }) => {
<View <View
style={[ style={[
styles.node, styles.node,
index < statuses + 1 && styles.completedNode index < Number(statuses) + 1 && styles.completedNode
]} ]}
/> />
{index < labels.length - 1 && ( {index < labels.length - 1 && (
<View <View
style={[ style={[
styles.line, styles.line,
index < statuses && styles.completedLine index < Number(statuses) && styles.completedLine
]} ]}
/> />
)} )}

1
app/services/api/categories.ts

@ -7,6 +7,7 @@ export interface Category {
name_en: string; name_en: string;
level: number; level: number;
is_leaf: boolean; is_leaf: boolean;
image_url?: string;
} }
export const categoriesApi = { export const categoriesApi = {

10
app/services/api/chat.ts

@ -4,15 +4,19 @@ export interface ChatMessage {
mimetype: string; mimetype: string;
userWs: string; userWs: string;
app_id: string; app_id: string;
country: string; country: number;
body: string; body: string;
text: string; text: string;
} }
export interface ChatMessageData {
newMessage:ChatMessage;
}
// API methods // API methods
export const chatService = { export const chatService = {
// Send message with retry mechanism // Send message with retry mechanism
async sendMessage(newMessage:ChatMessage): Promise<any> { async sendMessage(newMessage:ChatMessageData): Promise<any> {
return apiService.post('https://api.brainnel.com/app_chat/chat/',newMessage); return apiService.post('https://api.brainnel.com/app_chat/chat',newMessage);
}, },
}; };

8
app/services/api/login.tsx

@ -1,8 +1,10 @@
import axios from "axios"; import apiService from "./apiClient";
const GOOGLE_CLIENT_ID = '529750832779-osemooqkar78m8lhrn9dvbvi98fr4g08.apps.googleusercontent.com';
const GOOGLE_CLIENT_SECRET = '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';
const GOOGLE_REDIRECT_URI = 'https://api.brainnel.com/backend/api/users/auth/callback/google';
export const loginApi = { export const loginApi = {
google:() => { google:() => {
return axios.get<{url:string}>('http://124.70.102.7:8000/api/auth/google') return apiService.get<{url:string}>(`/api/users/auth/google`)
} }
}; };

19
app/services/api/payApi.ts

@ -72,6 +72,18 @@ export interface PaySuccessCallbackBody {
status: number; status: number;
} }
export interface rechargeHistory {
recharge_id: number;
user_id: number;
amount: number;
currency: string;
payment_method: string;
status: number;
transaction_id: string;
create_time: string;
update_time: string;
}
export const payApi = { export const payApi = {
// 获取当前国家支付方式 // 获取当前国家支付方式
@ -91,11 +103,16 @@ export const payApi = {
// 支付成功的回调 // 支付成功的回调
paySuccessCallback: (paymentId:string,PayerID:string) => { paySuccessCallback: (paymentId:string,PayerID:string) => {
return apiService.get<PaySuccessCallbackBody>(`/api/payment/paypal/execute/?paymentId=${paymentId}&PayerID=${PayerID}`); return apiService.post<PaySuccessCallbackBody>(`/api/payment/paypal/execute/`,{paymentId,PayerID});
}, },
// 新增充值接口 // 新增充值接口
initiateRecharge: (data: RechargeInitiateBody) => { initiateRecharge: (data: RechargeInitiateBody) => {
return apiService.post<RechargeInitiateResponse>('/api/recharge/initiate/', data); return apiService.post<RechargeInitiateResponse>('/api/recharge/initiate/', data);
}, },
// 获取充值历史
getRechargeHistory: () => {
return apiService.get<rechargeHistory[]>('/api/recharge/records/');
},
}; };

4
app/services/api/userApi.ts

@ -10,7 +10,9 @@ export interface User {
"user_id": number, "user_id": number,
"last_login": string, "last_login": string,
"create_time": string, "create_time": string,
"update_time": string "update_time": string,
country_code:number,
balance:number,
currency:string, currency:string,
country:string, country:string,
country_en:string, country_en:string,

12
app/store/burialPoint.ts

@ -229,8 +229,8 @@ const useBurialPointStore = create<BurialPointState>((set, get) => ({
// 记录浏览商品事件埋点 // 记录浏览商品事件埋点
logViewProduct: (productInfo: ProductProperty, fromPage = "home") => { logViewProduct: (productInfo: ProductProperty, fromPage = "home") => {
const viewProductEvent: BurialEvent = { const viewProductEvent: BurialEvent = {
event_name: "view-product", event_name: "product_view",
page_name: "product", page_name: "ProductDetail",
referre_page: fromPage, referre_page: fromPage,
event_properties: [productInfo] event_properties: [productInfo]
}; };
@ -259,7 +259,7 @@ const useBurialPointStore = create<BurialPointState>((set, get) => ({
logAddressInfo: (addressInfo: Omit<AddressProperty, "timestamp">, fromPage = "cart") => { logAddressInfo: (addressInfo: Omit<AddressProperty, "timestamp">, fromPage = "cart") => {
const addressEvent: BurialEvent = { const addressEvent: BurialEvent = {
event_name: "fill_information", event_name: "fill_information",
page_name: "address", page_name: "PreviewAddress",
referre_page: fromPage, referre_page: fromPage,
event_properties: [ event_properties: [
{ {
@ -276,7 +276,7 @@ const useBurialPointStore = create<BurialPointState>((set, get) => ({
logShippingConfirm: (shippingInfo: Omit<ShippingProperty, "timestamp">, fromPage = "address") => { logShippingConfirm: (shippingInfo: Omit<ShippingProperty, "timestamp">, fromPage = "address") => {
const shippingEvent: BurialEvent = { const shippingEvent: BurialEvent = {
event_name: "shipping_confirm", event_name: "shipping_confirm",
page_name: "shipping", page_name: "ShippingFee",
referre_page: fromPage, referre_page: fromPage,
event_properties: [ event_properties: [
{ {
@ -293,7 +293,7 @@ const useBurialPointStore = create<BurialPointState>((set, get) => ({
logPaymentConfirm: (paymentInfo: Omit<PaymentProperty, "timestamp">, fromPage = "shipping") => { logPaymentConfirm: (paymentInfo: Omit<PaymentProperty, "timestamp">, fromPage = "shipping") => {
const paymentEvent: BurialEvent = { const paymentEvent: BurialEvent = {
event_name: "payment_confirm", event_name: "payment_confirm",
page_name: "pay_method", page_name: "PaymentMethod",
referre_page: fromPage, referre_page: fromPage,
event_properties: [ event_properties: [
{ {
@ -310,7 +310,7 @@ const useBurialPointStore = create<BurialPointState>((set, get) => ({
logPreviewOrder: (fromPage = "pay_method") => { logPreviewOrder: (fromPage = "pay_method") => {
const previewEvent: BurialEvent = { const previewEvent: BurialEvent = {
event_name: "preview_order", event_name: "preview_order",
page_name: "preview", page_name: "PreviewOrder",
referre_page: fromPage, referre_page: fromPage,
event_properties: [ event_properties: [
{ {

BIN
assets/img/1034058.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 888 KiB

BIN
assets/img/category_24.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Loading…
Cancel
Save