diff --git a/App.tsx b/App.tsx index 7749325..6eadfb4 100644 --- a/App.tsx +++ b/App.tsx @@ -7,6 +7,7 @@ import { CountrySelect } from "./app/screens/CountrySelect"; import { MainApp } from "./app/screens/MainApp"; import { LoginScreen } from "./app/screens/LoginScreen"; import { EmailLoginScreen } from "./app/screens/EmailLoginScreen"; +import { GoogleScreen } from "./app/screens/login/Google"; import "./app/i18n"; import { TabNavigator } from "./app/navigation/TabNavigator"; import { AuthProvider } from "./app/contexts/AuthContext"; @@ -46,6 +47,7 @@ export type RootStackParamList = { CartScreen:undefined; PaymentSuccessScreen:undefined; MyAccount:undefined; + Google: undefined; }; const Stack = createNativeStackNavigator(); @@ -218,6 +220,15 @@ export default function App() { gestureDirection: "horizontal", }} /> + diff --git a/app.json b/app.json index 640e935..4cc9d50 100644 --- a/app.json +++ b/app.json @@ -7,19 +7,43 @@ "icon": "./assets/icon.png", "userInterfaceStyle": "light", "newArchEnabled": true, + "scheme": "auth0sample", "splash": { "image": "./assets/splash-icon.png", "resizeMode": "contain", "backgroundColor": "#ffffff" }, "ios": { - "supportsTablet": true + "supportsTablet": true, + "bundleIdentifier": "com.brainnel.app", + "infoPlist": { + "CFBundleURLTypes": [ + { + "CFBundleURLSchemes": ["auth0sample"] + } + ] + } }, "android": { "adaptiveIcon": { "foregroundImage": "./assets/adaptive-icon.png", "backgroundColor": "#ffffff" - } + }, + "package": "com.brainnel.app", + "intentFilters": [ + { + "action": "VIEW", + "data": [ + { + "scheme": "auth0sample" + } + ], + "category": [ + "BROWSABLE", + "DEFAULT" + ] + } + ] }, "web": { "favicon": "./assets/favicon.png" diff --git a/app/screens/LoginScreen.tsx b/app/screens/LoginScreen.tsx index fb8865c..749f797 100644 --- a/app/screens/LoginScreen.tsx +++ b/app/screens/LoginScreen.tsx @@ -14,13 +14,25 @@ import { Keyboard, Modal, InteractionManager, + Image, } from 'react-native'; import { useTranslation } from 'react-i18next'; import { useNavigation } from '@react-navigation/native'; import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; -import { RootStackParamList } from '../navigation/types'; import { Country, countries } from '../constants/countries'; import { useAuth } from '../contexts/AuthContext'; +import { settingApi } from "../services/api/setting"; +import { userApi } from "../services/api/userApi"; +import AsyncStorage from "@react-native-async-storage/async-storage"; +type RootStackParamList = { + Login: undefined; + EmailLogin: undefined; + MainTabs: undefined; + Google: undefined; + Home: undefined; +}; + + // 常见邮箱后缀列表 const EMAIL_DOMAINS = [ @@ -74,6 +86,16 @@ export const LoginScreen = ({ onClose, isModal }: LoginScreenProps) => { const [emailLoginMounted, setEmailLoginMounted] = useState(false); const [phoneLoginMounted, setPhoneLoginMounted] = useState(false); + + + + + + + + + + // 处理邮箱输入变化,生成邮箱建议 const handleEmailChange = (text: string) => { setEmail(text); @@ -285,8 +307,9 @@ export const LoginScreen = ({ onClose, isModal }: LoginScreenProps) => { } }; - const handleGoogleLogin = () => { - // 处理Google登录 + // 处理谷歌登录 + const handleGoogleLogin = async () => { + navigation.navigate('Google'); }; const handleFacebookLogin = () => { @@ -306,15 +329,33 @@ export const LoginScreen = ({ onClose, isModal }: LoginScreenProps) => { openEmailLogin(); }; + // const handleEmailContinue = async () => { - if (emailPassword === '123') { - setEmailPasswordError(false); - await login(); - closeEmailLogin(); - navigation.replace('MainTabs'); - } else { - setEmailPasswordError(true); - } + // if (emailPassword === '123') { + // setEmailPasswordError(false); + // await login(); + // closeEmailLogin(); + // navigation.replace('MainTabs'); + // } else { + // setEmailPasswordError(true); + // } + + + + const params = { + grant_type: "password", + username: "test", + password: "string", + client_id: "2", + client_secret: "", + scope: "", + }; + const res = await userApi.login(params); + const token = res.token_type + " " + res.access_token; + await AsyncStorage.setItem("token", token); + navigation.navigate("Home"); + const data = await settingApi.postFirstLogin(221); + console.log(data); }; const handlePhoneLogin = () => { @@ -387,7 +428,7 @@ export const LoginScreen = ({ onClose, isModal }: LoginScreenProps) => { onPress={handleGoogleLogin} > - G + {t('continueWithGoogle')} diff --git a/app/screens/ProfileScreen.tsx b/app/screens/ProfileScreen.tsx index 3a377e0..3b1d42d 100644 --- a/app/screens/ProfileScreen.tsx +++ b/app/screens/ProfileScreen.tsx @@ -26,25 +26,27 @@ type RootStackParamList = { SettingList: undefined; Home: undefined; MyAccount:undefined; + Login:undefined; }; export const ProfileScreen = () => { const handleLogin = async () => { - const data = settingApi.postFirstLogin(221); - console.log(data); + navigation.navigate("Login"); + // const data = settingApi.postFirstLogin(221); + // console.log(data); - const params = { - grant_type: "password", - username: "test", - password: "string", - client_id: "2", - client_secret: "", - scope: "", - }; - const res = await userApi.login(params); - const token = res.token_type + " " + res.access_token; - await AsyncStorage.setItem("token", token); - navigation.navigate("Home"); + // const params = { + // grant_type: "password", + // username: "test", + // password: "string", + // client_id: "2", + // client_secret: "", + // scope: "", + // }; + // const res = await userApi.login(params); + // const token = res.token_type + " " + res.access_token; + // await AsyncStorage.setItem("token", token); + // navigation.navigate("Home"); }; const navigation = diff --git a/app/screens/Recipient/Recipient.tsx b/app/screens/Recipient/Recipient.tsx index acf474e..27c4aec 100644 --- a/app/screens/Recipient/Recipient.tsx +++ b/app/screens/Recipient/Recipient.tsx @@ -26,7 +26,12 @@ import { NativeStackNavigationProp } from "@react-navigation/native-stack"; import { eventBus } from "../../utils/eventBus"; import LocationPinIcon from "../../components/LocationPinIcon"; import fontSize from "../../utils/fontsizeUtils"; -import { ordersApi, OrderData,AddressDataItem ,DomesticShippingFeeData} from "../../services/api/orders"; +import { + ordersApi, + OrderData, + AddressDataItem, + DomesticShippingFeeData, +} from "../../services/api/orders"; import AsyncStorage from "@react-native-async-storage/async-storage"; export function Recipient({ @@ -52,8 +57,22 @@ export function Recipient({ const [addressList, setAddressList] = useState(); const [defaultAddress, setDefaultAddress] = useState(); const [addressId, setAddressId] = useState(); - const [freightForwarderAddress, setFreightForwarderAddress] = useState(); - const [domesticShippingFee, setDomesticShippingFee] = useState(); + const [freightForwarderAddress, setFreightForwarderAddress] = + useState(); + const [domesticShippingFee, setDomesticShippingFee] = + useState(); + const [tabs, setTabs] = useState([ + { + id: "Online Payment", + label: "Online Payment", + }, + { + id: "Offline Payment", + label: "Offline Payment", + }, + ]); + const [currentTab, setCurrentTab] = useState("Online Paymen"); + const getAddress = async () => { const response = await addressApi.addressesDefault(); setAddressId(response.address_id); @@ -78,16 +97,16 @@ export function Recipient({ const getFreightForwarderAddress = async () => { const response = await ordersApi.freightForwarderAddress(1); - setWarehouse(response.current_country_address.country as number) + setWarehouse(response.current_country_address.country as number); setFreightForwarderAddress(response); }; const getDomesticShippingFee = async () => { const data = { items: route.params.items, - } + }; const response = await ordersApi.calcDomesticShippingFee(data); - + setDomesticShippingFee(response); }; @@ -95,8 +114,8 @@ export function Recipient({ getAddress(); getAddressList(); getOrders(); - getFreightForwarderAddress() - getDomesticShippingFee() + getFreightForwarderAddress(); + getDomesticShippingFee(); const listener = (data: any) => { if (data.type === "add") { data.address_id = new Date().getTime(); @@ -207,644 +226,719 @@ export function Recipient({ ); addressApi.deleteAddress(address_id); }; - const changeCountryHandel = async (value:number) => { + const changeCountryHandel = async (value: number) => { const data = { items: route.params.items, - country_code: value - } + country_code: value, + }; const response = await ordersApi.calcShippingFee(data); if (orderData) { setOrderData({ ...orderData, shipping_fee_sea: response?.total_shipping_fee_sea, - shipping_fee_air: response?.total_shipping_fee_air + shipping_fee_air: response?.total_shipping_fee_air, }); } - } + }; return ( - - {/* Header */} - - navigation.goBack()}> - - - Checkout - - - {/* Recipient Info */} - - - 👤 - Recipient Information + + + {/* Header */} + + navigation.goBack()}> + + + Checkout - {defaultAddress && ( - - - - - - - {defaultAddress?.receiver_first_name} .{" "} - {defaultAddress?.receiver_last_name} - - - {defaultAddress?.country} - - - {defaultAddress?.receiver_phone} - - - - )} - - - Add Recipient Information - - - - - {/* Shipping Method */} - - - 🚢 - Shipping Method - - - {[ - { - id: "sea", - label: "Sea Shipping", - icon: "🚢", - detail: "Economical", - }, - { id: "air", label: "Air Shipping", icon: "✈️", detail: "Express" }, - ].map((option, index) => ( - { - setShippingMethod(option.id); - }} - > - {index === 0 && ( - - - - )} - {option.icon} - {option.label} - {option.detail} - - ))} - - - - - {/* Warehouse Selection */} - - - 🏭 - Delivery Warehouse - - - - Select a warehouse: - - { - setWarehouse(value); - changeCountryHandel(value) - }} - > - { - freightForwarderAddress?.other_addresses.map((item,index) => ( - - )) - } - - - - + {/* Recipient Info */} + + + 👤 + Recipient Information - {warehouse && ( - - - - Estimated Arrival:{" "} - + {defaultAddress && ( + + + + + + + {defaultAddress?.receiver_first_name} .{" "} + {defaultAddress?.receiver_last_name} - - {shippingMethod === "sea" - ? orderData?.shipping_fee_sea_time - : orderData?.shipping_fee_air_time} - - - - - - International Fee:{" "} - + + {defaultAddress?.country} + + + {defaultAddress?.receiver_phone} - - {shippingMethod === "sea" - ? orderData?.shipping_fee_sea - : orderData?.shipping_fee_air} - - (Cash on Delivery) )} + + + + Add Recipient Information + + - - - - + + + {/* Shipping Method */} + - 💳 - Payment Method + 🚢 + Shipping Method + + + {[ + { + id: "sea", + label: "Sea Shipping", + icon: "🚢", + detail: "Economical", + }, + { + id: "air", + label: "Air Shipping", + icon: "✈️", + detail: "Express", + }, + ].map((option, index) => ( + { + setShippingMethod(option.id); + }} + > + {index === 0 && ( + + + + )} + {option.icon} + {option.label} + {option.detail} + + ))} + + - {[ - { id: "balance", icon: "💰", label: "Account Balance" }, - { id: "mobile_money", icon: "📱", label: "Mobile Money" }, - { id: "paypal", icon: "🅿️", label: "PayPal" }, - { id: "card", icon: "💳", label: "Credit/Debit Card" }, - ].map((option) => ( - { - setPaymentMethod(option.id); - }} - > - {option.icon} - {option.label} - - ))} - - {/* Mobile Money 表单 */} - {paymentMethod === "mobile_money" && ( - - Mobile Number - - { - const next = - countryCode === "225" - ? "234" - : countryCode === "234" - ? "233" - : "225"; - setCountryCode(next); + {/* Warehouse Selection */} + + + 🏭 + Delivery Warehouse + + + + Select a warehouse: + + { + setWarehouse(value); + changeCountryHandel(value); }} > - {countryCode} - - + {freightForwarderAddress?.other_addresses.map( + (item, index) => ( + + ) + )} + - )} - {/* PayPal Currency 切换 */} - {paymentMethod === "paypal" && ( - - Select Currency - - {["usd", "eur"].map((cur) => ( - { - setCurrency(cur); + {warehouse && ( + + + + Estimated Arrival:{" "} + + + {shippingMethod === "sea" + ? orderData?.shipping_fee_sea_time + : orderData?.shipping_fee_air_time} + + + + + + International Fee:{" "} + + - - {cur.toUpperCase()} - - - ))} + {shippingMethod === "sea" + ? orderData?.shipping_fee_sea + : orderData?.shipping_fee_air} + + + (Cash on Delivery) + - - )} + )} + - - - - - - - - 📦 - Order Summary + + + + 💳 + Payment Method - - - - Products({orderData?.items.length} items) - - - - {expanded ? "Hide Details" : "View Details"} - - + + + Online Payment + + + Offline Payment + - - {orderData?.items.map((item) => ( - - {item.sku_image_url ? ( - - ) : ( - - )} + + {/* {[ + { id: "balance", icon: "💰", label: "Account Balance" }, + { id: "mobile_money", icon: "📱", label: "Mobile Money" }, + { id: "paypal", icon: "🅿️", label: "PayPal" }, + { id: "card", icon: "💳", label: "Credit/Debit Card" }, + ].map((option) => ( + { + setPaymentMethod(option.id); + }} + > + {option.icon} + {option.label} + + ))} */} + {tabs.map((item, index) => ( + { + setCurrentTab(item.id); + }} + > + + {item.label} + + + ))} - - - {item.product_name} - - {item.attributes.map((attribute) => ( - + Mobile Number + + { + const next = + countryCode === "225" + ? "234" + : countryCode === "234" + ? "233" + : "225"; + setCountryCode(next); + }} + > + {countryCode} + + + + + )} + + {/* PayPal Currency 切换 */} + {paymentMethod === "paypal" && ( + + Select Currency + + {["usd", "eur"].map((cur) => ( + { + setCurrency(cur); + }} + style={[ + styles.currencyButton, + currency === cur && styles.currencyButtonSelected, + ]} > - {attribute?.attribute_name}: {attribute?.value} - + + {cur.toUpperCase()} + + ))} - Qty: {item.quantity} - - - - ${item?.total_price} - - {/* +${item?.shipping.toFixed(2)} domestic */} - - - Supplier to warehouse shipping - - ))} + )} - - - - - - 🎟️ - Coupons - setCouponModalVisible(true)}> - - Select - - - - - {appliedCoupons.length === 0 ? ( + + + + + + 📦 + Order Summary + + + - No coupons applied. Click "Select" to browse available coupons. + Products({orderData?.items.length} items) - ) : null} - - - {appliedCoupons.map((coupon) => ( - - {coupon.name} - - {coupon.type === "percent" - ? `${coupon.discount}% Off` - : `$${coupon.discount.toFixed(2)} Off`} - - removeCoupon(coupon.code)}> - × - + + + {expanded ? "Hide Details" : "View Details"} + + + + + {orderData?.items.map((item) => ( + + {item.sku_image_url ? ( + + ) : ( + + )} + + + + {item.product_name} + + {item.attributes.map((attribute) => ( + + {attribute?.attribute_name}: {attribute?.value} + + ))} + + Qty: {item.quantity} + + + + + ${item?.total_price} + + {/* +${item?.shipping.toFixed(2)} domestic */} + + + Supplier to warehouse shipping + + ))} + + + + + + 🎟️ + Coupons + setCouponModalVisible(true)}> + + Select + + + - - - Subtotal - {orderData?.total_amount} + + {appliedCoupons.length === 0 ? ( + + No coupons applied. Click "Select" to browse available + coupons. + + ) : null} + + + {appliedCoupons.map((coupon) => ( + + {coupon.name} + + {coupon.type === "percent" + ? `${coupon.discount}% Off` + : `$${coupon.discount.toFixed(2)} Off`} + + removeCoupon(coupon.code)}> + × + + + ))} + - - Domestic Shipping - { - domesticShippingFee?.currency ? ( + + + + Subtotal + {orderData?.total_amount} + + + Domestic Shipping + {domesticShippingFee?.currency ? ( {domesticShippingFee?.total_shipping_fee} - ):( + ) : ( 报价中... - ) - } - + )} + - - Estimated International Shipping - {shippingMethod === "sea" + + Estimated International Shipping + + {shippingMethod === "sea" ? orderData?.shipping_fee_sea - : orderData?.shipping_fee_air} + : orderData?.shipping_fee_air} + + - - {/* 实际支付金额 */} - - - - Total - - - {((orderData?.total_amount ?? 0) + (shippingMethod === "sea" - ? (orderData?.shipping_fee_sea ?? 0) - : (orderData?.shipping_fee_air ?? 0)) + (domesticShippingFee?.total_shipping_fee ?? 0)).toFixed(2)} - - - - 1 + {/* 实际支付金额 */} + + + + Total + + + {( + (orderData?.total_amount ?? 0) + + (shippingMethod === "sea" + ? orderData?.shipping_fee_sea ?? 0 + : orderData?.shipping_fee_air ?? 0) + + (domesticShippingFee?.total_shipping_fee ?? 0) + ).toFixed(2)} + + + + + + $ + {shippingMethod === "sea" + ? orderData?.shipping_fee_sea + : orderData?.shipping_fee_air}{" "} + Estimated International Shipping + + - - {/* Coupon Modal */} - setCouponModalVisible(false)} - > - - - - Available Coupons - setCouponModalVisible(false)} - > - × - - + {/* Coupon Modal */} + setCouponModalVisible(false)} + > + + + + + Available Coupons + + setCouponModalVisible(false)} + > + × + + - - - - - Welcome 10% Off - - 10% off your total order - - - Valid until: 31/12/2023 - + + + + + Welcome 10% Off + + 10% off your total order + + + Valid until: 31/12/2023 + + + addCoupon("WELCOME10")} + disabled={isCouponApplied("WELCOME10")} + > + + {isCouponApplied("WELCOME10") ? "Used" : "Use"} + + - addCoupon("WELCOME10")} - disabled={isCouponApplied("WELCOME10")} - > - - {isCouponApplied("WELCOME10") ? "Used" : "Use"} - - - - - - $20 Off - - $20 off your order over $100 - - - Valid until: 30/11/2023 - + + + $20 Off + + $20 off your order over $100 + + + Valid until: 30/11/2023 + + + addCoupon("SAVE20")} + disabled={isCouponApplied("SAVE20")} + > + + {isCouponApplied("SAVE20") ? "Used" : "Use"} + + - addCoupon("SAVE20")} - disabled={isCouponApplied("SAVE20")} - > - - {isCouponApplied("SAVE20") ? "Used" : "Use"} - - - - - - - Free Domestic Shipping - - - Free domestic shipping on your order - - - Valid until: 15/12/2023 - + + + + Free Domestic Shipping + + + Free domestic shipping on your order + + + Valid until: 15/12/2023 + + + addCoupon("FREESHIP")} + disabled={isCouponApplied("FREESHIP")} + > + + {isCouponApplied("FREESHIP") ? "Used" : "Use"} + + - addCoupon("FREESHIP")} - disabled={isCouponApplied("FREESHIP")} - > - - {isCouponApplied("FREESHIP") ? "Used" : "Use"} - - - - + + - - + + - - - - {/* Modal 表单 */} - - - - - 选择收件人 - - - - - - - - {addressList?.map((item) => ( - - { - setAddressId(item.address_id); - }} - > - + + + + 选择收件人 + + + + + + + + + {addressList?.map((item) => ( + + { + setAddressId(item.address_id); + }} > - - - - {item.country} {item.receiver_first_name}{" "} - . {item.receiver_last_name} - - - {item.receiver_phone} - - - 设置默认地址 - - deleteAddress(item.address_id) - } + + + + + {item.country}{" "} + {item.receiver_first_name} .{" "} + {item.receiver_last_name} + + - 删除 - + {item.receiver_phone} + + + 设置默认地址 + + deleteAddress(item.address_id) + } + > + 删除 + + + {item.is_default === 1 && ( + + + 默认 + + + )} - {item.is_default === 1 && ( - - - 默认 - + { + setShowModal(false), + navigation.navigate("AddRess", { + address: item, + }); + }} + > + + - )} + - { - setShowModal(false), - navigation.navigate("AddRess", { - address: item, - }); - }} - > - - - - - - + + + ))} + + + { + setShowModal(false), navigation.navigate("AddRess"); + }} + > + + + + + 新增收件人 + - ))} - + + {/* Placeholder for additional button component */} + + + + + {/* Cancel Button */} + setShowModal(false)} + > + 取消 + + + {/* Confirm Button */} { - setShowModal(false), navigation.navigate("AddRess"); + setShowModal(false), + setDefaultAddress( + addressList?.find( + (item) => item.address_id === addressId + ) + ); }} > - - - - - 新增收件人 - - + 确认 - - {/* Placeholder for additional button component */} - - - {/* Cancel Button */} - setShowModal(false)} - > - 取消 - - - {/* Confirm Button */} - { - setShowModal(false), - setDefaultAddress( - addressList?.find( - (item) => item.address_id === addressId - ) - ); - }} - > - 确认 - - - - - + + + + + { + console.log(123); + }} + > + + + {domesticShippingFee?.currency ? "确认订单" : "报价中..."} + + + + + ); } const styles = StyleSheet.create({ - container: { flex: 1,backgroundColor:"#fff" }, + mainContainer: { + flex: 1, + backgroundColor: "#fff", + }, + container: { flex: 1, backgroundColor: "#fff" }, header: { flexDirection: "row", alignItems: "center", @@ -865,7 +959,6 @@ const styles = StyleSheet.create({ borderRadius: 8, paddingLeft: 16, paddingRight: 16, - }, sectionHeader: { flexDirection: "row", @@ -880,6 +973,12 @@ const styles = StyleSheet.create({ fontSize: fontSize(13), fontWeight: "500", }, + paymentOptions:{ + flexDirection: "row", + justifyContent: "space-between", + alignItems: "center", + width: "100%", + }, recipientInfo: { backgroundColor: "#fff", borderRadius: 8, @@ -1028,15 +1127,19 @@ const styles = StyleSheet.create({ borderColor: "#ddd", padding: 12, borderRadius: 6, - marginTop: 10, + width: "50%", }, paymentSelected: { borderColor: "#ff6000", backgroundColor: "#fff8f3", }, + paymentIcon: { fontSize: fontSize(20), marginRight: 10 }, paymentLabel: { fontSize: fontSize(14), fontWeight: "500" }, - + tabContainer: { + width: 100, + flexDirection: "row", + }, mobileForm: { marginTop: 12 }, countryCode: { paddingVertical: 12, @@ -1549,4 +1652,26 @@ const styles = StyleSheet.create({ backgroundColor: "#f5f5f5", marginTop: 12, }, + bottomButton: { + width: "100%", + justifyContent: "center", + alignItems: "center", + marginTop: 12, + paddingHorizontal: 16, + marginBottom: 12, + backgroundColor: "#fff", + }, + bottomButtonContent: { + backgroundColor: "#ff611a", + width: "100%", + justifyContent: "center", + alignItems: "center", + paddingVertical: 16, + borderRadius: 25, + }, + bottomButtonText: { + color: "#fff", + fontSize: fontSize(16), + fontWeight: "500", + }, }); diff --git a/app/screens/login/Google.tsx b/app/screens/login/Google.tsx new file mode 100644 index 0000000..853f6ed --- /dev/null +++ b/app/screens/login/Google.tsx @@ -0,0 +1,89 @@ +import React, { useState, useEffect } from "react"; +import { View, StyleSheet, ActivityIndicator } from "react-native"; +import { WebView } from "react-native-webview"; +import { useNavigation } from "@react-navigation/native"; +import { loginApi } from "../../services/api/login"; +import { useAuth } from "../../contexts/AuthContext"; + +export const GoogleScreen = () => { + const navigation = useNavigation(); + const { login } = useAuth(); + const [loading, setLoading] = useState(true); + const [loginUrl, setLoginUrl] = useState(null); + + useEffect(() => { + const fetchLoginUrl = async () => { + try { + const response = await loginApi.google(); + if (response.data.url) { + setLoginUrl(response.data.url); + } + } catch (error) { + console.error('Failed to fetch login URL:', error); + } + }; + + fetchLoginUrl(); + }, []); + + const handleNavigationStateChange = async (navState: any) => { + console.log(navState.url); + + // 检查URL是否包含重定向URI + if (navState.url.includes('localhost:8000')) { + try { + await login(); + navigation.navigate('MainTabs' as never); + } catch (error) { + console.error('Login failed:', error); + } + } + }; + + if (!loginUrl) { + return ( + + + + ); + } + + return ( + + setLoading(true)} + onLoadEnd={() => setLoading(false)} + javaScriptEnabled={true} + domStorageEnabled={true} + startInLoadingState={true} + scalesPageToFit={true} + 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" + onShouldStartLoadWithRequest={(request) => { + console.log(request); + + // 允许所有请求 + return true; + }} + /> + + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, + loadingContainer: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + backgroundColor: 'white', + }, + webview: { + flex: 1, + }, +}); diff --git a/app/screens/PhoneLoginScreen.tsx b/app/screens/login/PhoneLoginScreen.tsx similarity index 98% rename from app/screens/PhoneLoginScreen.tsx rename to app/screens/login/PhoneLoginScreen.tsx index ae379b4..66e5ea1 100644 --- a/app/screens/PhoneLoginScreen.tsx +++ b/app/screens/login/PhoneLoginScreen.tsx @@ -9,13 +9,12 @@ import { Platform, Modal, FlatList, - SafeAreaView, } from 'react-native'; import { useTranslation } from 'react-i18next'; import { useNavigation } from '@react-navigation/native'; import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; -import { RootStackParamList } from '../navigation/types'; -import { Country, countries } from '../constants/countries'; +import { RootStackParamList } from '../../navigation/types'; +import { Country, countries } from '../../constants/countries'; export const PhoneLoginScreen = () => { const { t } = useTranslation(); @@ -35,6 +34,8 @@ export const PhoneLoginScreen = () => { }; const handleContinue = () => { + console.log(123); + // 处理继续操作,发送验证码 if (phoneNumber.trim()) { // 这里可以添加发送验证码的逻辑 diff --git a/app/services/api/login.tsx b/app/services/api/login.tsx new file mode 100644 index 0000000..1cd6910 --- /dev/null +++ b/app/services/api/login.tsx @@ -0,0 +1,8 @@ +import axios from "axios"; + + +export const loginApi = { + google:() => { + return axios.get<{url:string}>('http://124.70.102.7:8000/api/auth/google') + } +}; \ No newline at end of file diff --git a/assets/img/google.png b/assets/img/google.png new file mode 100644 index 0000000..e57cb2f Binary files /dev/null and b/assets/img/google.png differ diff --git a/package-lock.json b/package-lock.json index 357189d..84264a7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,9 +19,12 @@ "axios": "^1.8.4", "events": "^3.3.0", "expo": "~52.0.41", + "expo-auth-session": "~6.0.3", "expo-linear-gradient": "~14.0.2", "expo-localization": "^16.0.1", + "expo-random": "^14.0.1", "expo-status-bar": "~2.0.1", + "expo-web-browser": "~14.0.2", "i18next": "^24.2.3", "install": "^0.13.0", "npm": "^10.9.2", @@ -46,7 +49,8 @@ "react-native-swiper": "^1.6.0", "react-native-toast-message": "^2.3.0", "react-native-vector-icons": "^10.2.0", - "react-native-web": "~0.19.13" + "react-native-web": "~0.19.13", + "react-native-webview": "^13.13.5" }, "devDependencies": { "@babel/core": "^7.25.2", @@ -6522,6 +6526,15 @@ } } }, + "node_modules/expo-application": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/expo-application/-/expo-application-6.0.2.tgz", + "integrity": "sha512-qcj6kGq3mc7x5yIb5KxESurFTJCoEKwNEL34RdPEvTB/xhl7SeVZlu05sZBqxB1V4Ryzq/LsCb7NHNfBbb3L7A==", + "license": "MIT", + "peerDependencies": { + "expo": "*" + } + }, "node_modules/expo-asset": { "version": "11.0.5", "resolved": "https://registry.npmmirror.com/expo-asset/-/expo-asset-11.0.5.tgz", @@ -6539,6 +6552,24 @@ "react-native": "*" } }, + "node_modules/expo-auth-session": { + "version": "6.0.3", + "resolved": "https://registry.npmmirror.com/expo-auth-session/-/expo-auth-session-6.0.3.tgz", + "integrity": "sha512-s7LmmMPiiY1NXrlcXkc4+09Hlfw9X1CpaQOCDkwfQEodG1uCYGQi/WImTnDzw5YDkWI79uC8F1mB8EIerilkDA==", + "license": "MIT", + "dependencies": { + "expo-application": "~6.0.2", + "expo-constants": "~17.0.5", + "expo-crypto": "~14.0.2", + "expo-linking": "~7.0.5", + "expo-web-browser": "~14.0.2", + "invariant": "^2.2.4" + }, + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, "node_modules/expo-constants": { "version": "17.0.8", "resolved": "https://registry.npmmirror.com/expo-constants/-/expo-constants-17.0.8.tgz", @@ -6553,6 +6584,18 @@ "react-native": "*" } }, + "node_modules/expo-crypto": { + "version": "14.0.2", + "resolved": "https://registry.npmmirror.com/expo-crypto/-/expo-crypto-14.0.2.tgz", + "integrity": "sha512-WRc9PBpJraJN29VD5Ef7nCecxJmZNyRKcGkNiDQC1nhY5agppzwhqh7zEzNFarE/GqDgSiaDHS8yd5EgFhP9AQ==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.0" + }, + "peerDependencies": { + "expo": "*" + } + }, "node_modules/expo-file-system": { "version": "18.0.12", "resolved": "https://registry.npmmirror.com/expo-file-system/-/expo-file-system-18.0.12.tgz", @@ -6600,6 +6643,20 @@ "react-native": "*" } }, + "node_modules/expo-linking": { + "version": "7.0.5", + "resolved": "https://registry.npmmirror.com/expo-linking/-/expo-linking-7.0.5.tgz", + "integrity": "sha512-3KptlJtcYDPWohk0MfJU75MJFh2ybavbtcSd84zEPfw9s1q3hjimw3sXnH03ZxP54kiEWldvKmmnGcVffBDB1g==", + "license": "MIT", + "dependencies": { + "expo-constants": "~17.0.5", + "invariant": "^2.2.4" + }, + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, "node_modules/expo-localization": { "version": "16.0.1", "resolved": "https://registry.npmmirror.com/expo-localization/-/expo-localization-16.0.1.tgz", @@ -6650,6 +6707,19 @@ "invariant": "^2.2.4" } }, + "node_modules/expo-random": { + "version": "14.0.1", + "resolved": "https://registry.npmmirror.com/expo-random/-/expo-random-14.0.1.tgz", + "integrity": "sha512-gX2mtR9o+WelX21YizXUCD/y+a4ZL+RDthDmFkHxaYbdzjSYTn8u/igoje/l3WEO+/RYspmqUFa8w/ckNbt6Vg==", + "deprecated": "This package is now deprecated in favor of expo-crypto, which provides the same functionality. To migrate, replace all imports from expo-random with imports from expo-crypto.", + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.0" + }, + "peerDependencies": { + "expo": "*" + } + }, "node_modules/expo-status-bar": { "version": "2.0.1", "resolved": "https://registry.npmmirror.com/expo-status-bar/-/expo-status-bar-2.0.1.tgz", @@ -6660,6 +6730,16 @@ "react-native": "*" } }, + "node_modules/expo-web-browser": { + "version": "14.0.2", + "resolved": "https://registry.npmmirror.com/expo-web-browser/-/expo-web-browser-14.0.2.tgz", + "integrity": "sha512-Hncv2yojhTpHbP6SGWARBFdl7P6wBHc1O8IKaNsH0a/IEakq887o1eRhLxZ5IwztPQyRDhpqHdgJ+BjWolOnwA==", + "license": "MIT", + "peerDependencies": { + "expo": "*", + "react-native": "*" + } + }, "node_modules/exponential-backoff": { "version": "3.1.2", "resolved": "https://registry.npmmirror.com/exponential-backoff/-/exponential-backoff-3.1.2.tgz", @@ -13482,8 +13562,6 @@ "resolved": "https://registry.npmmirror.com/react-native-webview/-/react-native-webview-13.13.5.tgz", "integrity": "sha512-MfC2B+woL4Hlj2WCzcb1USySKk+SteXnUKmKktOk/H/AQy5+LuVdkPKm8SknJ0/RxaxhZ48WBoTRGaqgR137hw==", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { "escape-string-regexp": "^4.0.0", "invariant": "2.2.4" diff --git a/package.json b/package.json index b84f89b..e625e4e 100644 --- a/package.json +++ b/package.json @@ -20,9 +20,12 @@ "axios": "^1.8.4", "events": "^3.3.0", "expo": "~52.0.41", + "expo-auth-session": "~6.0.3", "expo-linear-gradient": "~14.0.2", "expo-localization": "^16.0.1", + "expo-random": "^14.0.1", "expo-status-bar": "~2.0.1", + "expo-web-browser": "~14.0.2", "i18next": "^24.2.3", "install": "^0.13.0", "npm": "^10.9.2", @@ -47,7 +50,8 @@ "react-native-swiper": "^1.6.0", "react-native-toast-message": "^2.3.0", "react-native-vector-icons": "^10.2.0", - "react-native-web": "~0.19.13" + "react-native-web": "~0.19.13", + "react-native-webview": "^13.13.5" }, "devDependencies": { "@babel/core": "^7.25.2", diff --git a/yarn.lock b/yarn.lock index 45941ef..b573306 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2740,7 +2740,7 @@ balanced-match@^1.0.0: resolved "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -base64-js@^1.2.3, base64-js@^1.3.1, base64-js@^1.5.1: +base64-js@^1.2.3, base64-js@^1.3.0, base64-js@^1.3.1, base64-js@^1.5.1: version "1.5.1" resolved "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -3843,6 +3843,11 @@ execa@^5.1.1: signal-exit "^3.0.3" strip-final-newline "^2.0.0" +expo-application@~6.0.2: + version "6.0.2" + resolved "https://registry.npmmirror.com/expo-application/-/expo-application-6.0.2.tgz" + integrity sha512-qcj6kGq3mc7x5yIb5KxESurFTJCoEKwNEL34RdPEvTB/xhl7SeVZlu05sZBqxB1V4Ryzq/LsCb7NHNfBbb3L7A== + expo-asset@~11.0.5: version "11.0.5" resolved "https://registry.npmmirror.com/expo-asset/-/expo-asset-11.0.5.tgz" @@ -3853,7 +3858,19 @@ expo-asset@~11.0.5: invariant "^2.2.4" md5-file "^3.2.3" -expo-constants@~17.0.8: +expo-auth-session@~6.0.3: + version "6.0.3" + resolved "https://registry.npmmirror.com/expo-auth-session/-/expo-auth-session-6.0.3.tgz" + integrity sha512-s7LmmMPiiY1NXrlcXkc4+09Hlfw9X1CpaQOCDkwfQEodG1uCYGQi/WImTnDzw5YDkWI79uC8F1mB8EIerilkDA== + dependencies: + expo-application "~6.0.2" + expo-constants "~17.0.5" + expo-crypto "~14.0.2" + expo-linking "~7.0.5" + expo-web-browser "~14.0.2" + invariant "^2.2.4" + +expo-constants@~17.0.5, expo-constants@~17.0.8: version "17.0.8" resolved "https://registry.npmmirror.com/expo-constants/-/expo-constants-17.0.8.tgz" integrity sha512-XfWRyQAf1yUNgWZ1TnE8pFBMqGmFP5Gb+SFSgszxDdOoheB/NI5D4p7q86kI2fvGyfTrxAe+D+74nZkfsGvUlg== @@ -3861,6 +3878,13 @@ expo-constants@~17.0.8: "@expo/config" "~10.0.11" "@expo/env" "~0.4.2" +expo-crypto@~14.0.2: + version "14.0.2" + resolved "https://registry.npmmirror.com/expo-crypto/-/expo-crypto-14.0.2.tgz" + integrity sha512-WRc9PBpJraJN29VD5Ef7nCecxJmZNyRKcGkNiDQC1nhY5agppzwhqh7zEzNFarE/GqDgSiaDHS8yd5EgFhP9AQ== + dependencies: + base64-js "^1.3.0" + expo-file-system@~18.0.12: version "18.0.12" resolved "https://registry.npmmirror.com/expo-file-system/-/expo-file-system-18.0.12.tgz" @@ -3885,6 +3909,14 @@ expo-linear-gradient@~14.0.2: resolved "https://registry.npmmirror.com/expo-linear-gradient/-/expo-linear-gradient-14.0.2.tgz" integrity sha512-nvac1sPUfFFJ4mY25UkvubpUV/olrBH+uQw5k+beqSvQaVQiUfFtYzfRr+6HhYBNb4AEsOtpsCRkpDww3M2iGQ== +expo-linking@~7.0.5: + version "7.0.5" + resolved "https://registry.npmmirror.com/expo-linking/-/expo-linking-7.0.5.tgz" + integrity sha512-3KptlJtcYDPWohk0MfJU75MJFh2ybavbtcSd84zEPfw9s1q3hjimw3sXnH03ZxP54kiEWldvKmmnGcVffBDB1g== + dependencies: + expo-constants "~17.0.5" + invariant "^2.2.4" + expo-localization@^16.0.1: version "16.0.1" resolved "https://registry.npmmirror.com/expo-localization/-/expo-localization-16.0.1.tgz" @@ -3913,11 +3945,23 @@ expo-modules-core@2.2.3: dependencies: invariant "^2.2.4" +expo-random@^14.0.1: + version "14.0.1" + resolved "https://registry.npmmirror.com/expo-random/-/expo-random-14.0.1.tgz" + integrity sha512-gX2mtR9o+WelX21YizXUCD/y+a4ZL+RDthDmFkHxaYbdzjSYTn8u/igoje/l3WEO+/RYspmqUFa8w/ckNbt6Vg== + dependencies: + base64-js "^1.3.0" + expo-status-bar@~2.0.1: version "2.0.1" resolved "https://registry.npmmirror.com/expo-status-bar/-/expo-status-bar-2.0.1.tgz" integrity sha512-AkIPX7jWHRPp83UBZ1iXtVvyr0g+DgBVvIXTtlmPtmUsm8Vq9Bb5IGj86PW8osuFlgoTVAg7HI/+Ok7yEYwiRg== +expo-web-browser@~14.0.2: + version "14.0.2" + resolved "https://registry.npmmirror.com/expo-web-browser/-/expo-web-browser-14.0.2.tgz" + integrity sha512-Hncv2yojhTpHbP6SGWARBFdl7P6wBHc1O8IKaNsH0a/IEakq887o1eRhLxZ5IwztPQyRDhpqHdgJ+BjWolOnwA== + expo@*, expo@~52.0.41: version "52.0.41" resolved "https://registry.npmmirror.com/expo/-/expo-52.0.41.tgz" @@ -6726,7 +6770,7 @@ react-native-web@~0.19.13: postcss-value-parser "^4.2.0" styleq "^0.1.3" -react-native-webview@*: +react-native-webview@*, react-native-webview@^13.13.5: version "13.13.5" resolved "https://registry.npmmirror.com/react-native-webview/-/react-native-webview-13.13.5.tgz" integrity sha512-MfC2B+woL4Hlj2WCzcb1USySKk+SteXnUKmKktOk/H/AQy5+LuVdkPKm8SknJ0/RxaxhZ48WBoTRGaqgR137hw==