Browse Source

提交订单

main
Mac 1 month ago
parent
commit
b29469e53a
  1. 11
      App.tsx
  2. 26
      app.json
  3. 65
      app/screens/LoginScreen.tsx
  4. 30
      app/screens/ProfileScreen.tsx
  5. 233
      app/screens/Recipient/Recipient.tsx
  6. 89
      app/screens/login/Google.tsx
  7. 7
      app/screens/login/PhoneLoginScreen.tsx
  8. 8
      app/services/api/login.tsx
  9. BIN
      assets/img/google.png
  10. 84
      package-lock.json
  11. 6
      package.json
  12. 50
      yarn.lock

11
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<RootStackParamList>();
@ -218,6 +220,15 @@ export default function App() {
gestureDirection: "horizontal",
}}
/>
<Stack.Screen
name="Google"
component={GoogleScreen}
options={{
animation: "slide_from_right",
gestureEnabled: true,
gestureDirection: "horizontal",
}}
/>
</Stack.Navigator>
</NavigationContainer>
</AuthProvider>

26
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"

65
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}
>
<View style={styles.loginButtonIcon}>
<Text>G</Text>
<Image source={require('../../assets/img/google.png')} style={{width: 20, height: 20}} />
</View>
<Text style={styles.loginButtonText}>{t('continueWithGoogle')}</Text>
</TouchableOpacity>

30
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 =

233
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<AddressItem[]>();
const [defaultAddress, setDefaultAddress] = useState<addressData>();
const [addressId, setAddressId] = useState<number>();
const [freightForwarderAddress, setFreightForwarderAddress] = useState<AddressDataItem>();
const [domesticShippingFee, setDomesticShippingFee] = useState<DomesticShippingFeeData>();
const [freightForwarderAddress, setFreightForwarderAddress] =
useState<AddressDataItem>();
const [domesticShippingFee, setDomesticShippingFee] =
useState<DomesticShippingFeeData>();
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,14 +97,14 @@ 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,22 +226,23 @@ 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 (
<View style={styles.mainContainer}>
<ScrollView style={styles.container}>
{/* Header */}
<View style={styles.header}>
@ -260,7 +280,9 @@ export function Recipient({
)}
<TouchableOpacity style={styles.addRecipient} onPress={addRessHandel}>
<Text style={styles.addRecipientIcon}></Text>
<Text style={styles.addRecipientText}>Add Recipient Information</Text>
<Text style={styles.addRecipientText}>
Add Recipient Information
</Text>
</TouchableOpacity>
</View>
<View style={styles.border}></View>
@ -279,7 +301,12 @@ export function Recipient({
icon: "🚢",
detail: "Economical",
},
{ id: "air", label: "Air Shipping", icon: "✈", detail: "Express" },
{
id: "air",
label: "Air Shipping",
icon: "✈",
detail: "Express",
},
].map((option, index) => (
<TouchableOpacity
key={option.id}
@ -311,7 +338,7 @@ export function Recipient({
<Text style={styles.sectionIcon}>🏭</Text>
<Text style={styles.sectionTitle}>Delivery Warehouse</Text>
</View>
<View style={{ marginTop: 12, }}>
<View style={{ marginTop: 12 }}>
<View style={styles.selectBox}>
<Text style={styles.selectLabel}>Select a warehouse:</Text>
<View style={styles.selectWrapper}>
@ -319,16 +346,18 @@ export function Recipient({
selectedValue={warehouse}
onValueChange={(value) => {
setWarehouse(value);
changeCountryHandel(value)
changeCountryHandel(value);
}}
>
{
freightForwarderAddress?.other_addresses.map((item,index) => (
<Picker.Item label={item.country} value={item.country_code} key={index}/>
))
}
{freightForwarderAddress?.other_addresses.map(
(item, index) => (
<Picker.Item
label={item.country}
value={item.country_code}
key={index}
/>
)
)}
</Picker>
</View>
</View>
@ -338,9 +367,8 @@ export function Recipient({
<Text style={styles.shippingInfoRow}>
<Text style={styles.shippingInfoLabel}>
Estimated Arrival:{" "}
</Text>
<Text style={{ flex:1,textAlign:"left",marginLeft:10 }}>
<Text style={{ flex: 1, textAlign: "left", marginLeft: 10 }}>
{shippingMethod === "sea"
? orderData?.shipping_fee_sea_time
: orderData?.shipping_fee_air_time}
@ -350,12 +378,20 @@ export function Recipient({
<View style={styles.shippingInfoRow}>
<Text style={styles.shippingInfoLabel}>
International Fee:{" "}
</Text>
<Text style={{ color: "#ff6000",flex:1,textAlign:"left",marginLeft:10,fontWeight:'600'}}>
<Text
style={{
color: "#ff6000",
flex: 1,
textAlign: "left",
marginLeft: 10,
fontWeight: "600",
}}
>
{shippingMethod === "sea"
? orderData?.shipping_fee_sea
: orderData?.shipping_fee_air}</Text>
: orderData?.shipping_fee_air}
</Text>
<Text style={{ color: "#ff6000" }}>(Cash on Delivery)</Text>
</View>
@ -365,13 +401,20 @@ export function Recipient({
</View>
<View style={styles.border}></View>
<View style={styles.section}>
<View>
<View style={styles.sectionHeader}>
<Text style={styles.sectionIcon}>💳</Text>
<Text style={styles.sectionTitle}>Payment Method</Text>
</View>
{[
<View style={styles.paymentOptions}>
<View style={styles.paymentOption}>
<Text style={styles.paymentLabel}>Online Payment</Text>
</View>
<View style={styles.paymentOption}>
<Text style={styles.paymentLabel}>Offline Payment</Text>
</View>
</View>
<View>
{/* {[
{ id: "balance", icon: "💰", label: "Account Balance" },
{ id: "mobile_money", icon: "📱", label: "Mobile Money" },
{ id: "paypal", icon: "🅿", label: "PayPal" },
@ -390,6 +433,18 @@ export function Recipient({
<Text style={styles.paymentIcon}>{option.icon}</Text>
<Text style={styles.paymentLabel}>{option.label}</Text>
</TouchableOpacity>
))} */}
{tabs.map((item, index) => (
<TouchableOpacity
key={index}
onPress={() => {
setCurrentTab(item.id);
}}
>
<View style={styles.tabContainer}>
<Text style={styles.paymentLabel}>{item.label}</Text>
</View>
</TouchableOpacity>
))}
{/* Mobile Money 表单 */}
@ -496,7 +551,9 @@ export function Recipient({
{attribute?.attribute_name}: {attribute?.value}
</Text>
))}
<Text style={styles.itemQuantity}>Qty: {item.quantity}</Text>
<Text style={styles.itemQuantity}>
Qty: {item.quantity}
</Text>
</View>
<View style={styles.itemPrices}>
@ -529,7 +586,8 @@ export function Recipient({
<View style={styles.sectionContent}>
{appliedCoupons.length === 0 ? (
<Text style={styles.noCouponsMessage}>
No coupons applied. Click "Select" to browse available coupons.
No coupons applied. Click "Select" to browse available
coupons.
</Text>
) : null}
@ -557,20 +615,20 @@ export function Recipient({
</View>
<View style={styles.priceBox1}>
<Text>Domestic Shipping</Text>
{
domesticShippingFee?.currency ? (
{domesticShippingFee?.currency ? (
<Text>{domesticShippingFee?.total_shipping_fee}</Text>
):(
) : (
<Text>...</Text>
)
}
)}
</View>
<View style={styles.priceBox1}>
<Text>Estimated International Shipping</Text>
<Text>{shippingMethod === "sea"
<Text>
{shippingMethod === "sea"
? orderData?.shipping_fee_sea
: orderData?.shipping_fee_air}</Text>
: orderData?.shipping_fee_air}
</Text>
</View>
</View>
@ -593,13 +651,23 @@ export function Recipient({
color: "#ff6000",
}}
>
{((orderData?.total_amount ?? 0) + (shippingMethod === "sea"
? (orderData?.shipping_fee_sea ?? 0)
: (orderData?.shipping_fee_air ?? 0)) + (domesticShippingFee?.total_shipping_fee ?? 0)).toFixed(2)}
{(
(orderData?.total_amount ?? 0) +
(shippingMethod === "sea"
? orderData?.shipping_fee_sea ?? 0
: orderData?.shipping_fee_air ?? 0) +
(domesticShippingFee?.total_shipping_fee ?? 0)
).toFixed(2)}
</Text>
</View>
<View style={styles.remarks}>
<Text style={styles.remarksText}>1</Text>
<Text style={styles.remarksText}>
+ $
{shippingMethod === "sea"
? orderData?.shipping_fee_sea
: orderData?.shipping_fee_air}{" "}
Estimated International Shipping
</Text>
</View>
</View>
@ -613,7 +681,9 @@ export function Recipient({
<View style={styles.couponModal}>
<View style={styles.couponModalContainer}>
<View style={styles.couponModalHeader}>
<Text style={styles.couponModalTitle}>Available Coupons</Text>
<Text style={styles.couponModalTitle}>
Available Coupons
</Text>
<TouchableOpacity
onPress={() => setCouponModalVisible(false)}
>
@ -636,7 +706,8 @@ export function Recipient({
<TouchableOpacity
style={[
styles.couponUseBtn,
isCouponApplied("WELCOME10") && styles.couponUsedBtn,
isCouponApplied("WELCOME10") &&
styles.couponUsedBtn,
]}
onPress={() => addCoupon("WELCOME10")}
disabled={isCouponApplied("WELCOME10")}
@ -703,7 +774,6 @@ export function Recipient({
</Modal>
</View>
</View>
<View style={styles.border}></View>
{/* Modal 表单 */}
<Modal visible={showModal} animationType="slide" transparent>
@ -744,8 +814,9 @@ export function Recipient({
numberOfLines={1}
ellipsizeMode="tail"
>
{item.country} {item.receiver_first_name}{" "}
. {item.receiver_last_name}
{item.country}{" "}
{item.receiver_first_name} .{" "}
{item.receiver_last_name}
</Text>
<Text
style={styles.userCardInfo1}
@ -767,7 +838,9 @@ export function Recipient({
</View>
{item.is_default === 1 && (
<View style={styles.centeredBoxWithText}>
<Text style={styles.blueHeadingTextStyle}>
<Text
style={styles.blueHeadingTextStyle}
>
</Text>
</View>
@ -839,12 +912,33 @@ export function Recipient({
</View>
</View>
</Modal>
<View style={styles.border}></View>
<TouchableOpacity
style={styles.bottomButton}
disabled={!domesticShippingFee?.currency}
onPress={() => {
console.log(123);
}}
>
<View style={styles.bottomButtonContent}>
<Text style={styles.bottomButtonText}>
{domesticShippingFee?.currency ? "确认订单" : "报价中..."}
</Text>
</View>
</TouchableOpacity>
</ScrollView>
</View>
);
}
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",
},
});

89
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<string | null>(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 (
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color="#4285F4" />
</View>
);
}
return (
<View style={styles.container}>
<WebView
source={{ uri: loginUrl }}
style={styles.webview}
onNavigationStateChange={handleNavigationStateChange}
onLoadStart={() => 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;
}}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
},
loadingContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'white',
},
webview: {
flex: 1,
},
});

7
app/screens/PhoneLoginScreen.tsx → 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()) {
// 这里可以添加发送验证码的逻辑

8
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')
}
};

BIN
assets/img/google.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

84
package-lock.json generated

@ -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"

6
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",

50
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==

Loading…
Cancel
Save