Mac 2 weeks ago
parent
commit
46aede5804
  1. 1
      FINAL_REDIRECT_FIX.md
  2. 6
      app.json
  3. 4
      app/screens/BalanceScreen/PhoneNumberInputModal.tsx
  4. 4
      app/screens/ProfileScreen.tsx
  5. 1248
      app/screens/banner/ShippingDetailsSection.tsx
  6. 64
      app/screens/loginList/index.tsx
  7. 135
      app/screens/pay/Pay.tsx
  8. 78
      app/screens/previewOrder/perviewOrder.tsx
  9. 4
      app/screens/productStatus/OrderDatails.tsx
  10. 12
      app/services/api/apiClient.ts
  11. 55
      app/services/api/payApi.ts
  12. 96
      package-lock.json
  13. 2
      package.json
  14. 552
      yarn.lock

1
FINAL_REDIRECT_FIX.md

@ -0,0 +1 @@

6
app.json

@ -7,7 +7,7 @@
"icon": "./assets/icon.png",
"userInterfaceStyle": "light",
"newArchEnabled": true,
"scheme": "myapp",
"scheme": "brainnelapp",
"owner":"brainnel",
"deepLinking": true,
"splash": {
@ -22,7 +22,7 @@
"CFBundleURLTypes": [
{
"CFBundleURLSchemes": [
"myapp"
"brainnelapp"
]
}
]
@ -43,7 +43,7 @@
"action": "VIEW",
"data": [
{
"scheme": "myapp"
"scheme": "brainnelapp"
}
],
"category": [

4
app/screens/BalanceScreen/PhoneNumberInputModal.tsx

@ -35,7 +35,7 @@ interface PhoneNumberInputModalProps {
}
type RootStackParamList = {
Pay: { payUrl: string };
Pay: { payUrl: string, method: string, order_id: string };
}
const PhoneNumberInputModal = ({
@ -87,6 +87,8 @@ const PhoneNumberInputModal = ({
navigation.navigate("Pay", {
payUrl: res.payment.payment_url,
method: paymentParams.payment_method,
order_id: res.payment.order_id.toString(),
});
setIsSubmitting(false)

4
app/screens/ProfileScreen.tsx

@ -41,9 +41,13 @@ type RootStackParamList = {
};
export const ProfileScreen = () => {
const handleLogin = async () => {
navigation.navigate("Login");
};
const { user } = useUserStore();
const { t } = useTranslation();

1248
app/screens/banner/ShippingDetailsSection.tsx

File diff suppressed because it is too large Load Diff

64
app/screens/loginList/index.tsx

@ -9,7 +9,7 @@ import {
BackHandler,
Image,
Modal,
SafeAreaView
SafeAreaView,
} from "react-native";
import { useTranslation } from "react-i18next";
import { useNavigation } from "@react-navigation/native";
@ -18,6 +18,12 @@ import fontSize from "../../utils/fontsizeUtils";
import EmailLoginModal from "./EmailLoginModal";
import PhoneLoginModal from "./PhoneLoginModal";
import * as WebBrowser from "expo-web-browser";
import * as AuthSession from "expo-auth-session";
import * as Linking from "expo-linking";
import * as Google from "expo-auth-session/providers/google";
WebBrowser.maybeCompleteAuthSession();
type RootStackParamList = {
Login: undefined;
@ -32,6 +38,12 @@ type LoginScreenProps = {
isModal?: boolean;
};
const CLIENT_ID =
"529750832779-osemooqkar78m8lhrn9dvbvi98fr4g08.apps.googleusercontent.com"; // from Google Cloud
const REDIRECT_URI = AuthSession.makeRedirectUri({
native: "brainnelapp://redirect",
});
export const LoginScreen = ({ onClose, isModal }: LoginScreenProps) => {
const { t } = useTranslation();
const navigation =
@ -77,10 +89,31 @@ export const LoginScreen = ({ onClose, isModal }: LoginScreenProps) => {
}
};
// 处理谷歌登录
const handleGoogleLogin = async () => {
navigation.navigate("Google");
const discovery = {
authorizationEndpoint: "https://accounts.google.com/o/oauth2/v2/auth",
tokenEndpoint: "https://oauth2.googleapis.com/token",
revocationEndpoint: "https://oauth2.googleapis.com/revoke",
};
const [request, response, handleGoogleLogin] = AuthSession.useAuthRequest(
{
clientId: CLIENT_ID,
scopes: ["openid", "profile", "email"],
redirectUri: REDIRECT_URI,
responseType: AuthSession.ResponseType.Code,
},
discovery
);
React.useEffect(() => {
if (response?.type === "success") {
const { code } = response.params;
console.log("授权码:", code);
}
}, [response]);
// 处理谷歌登录
// const handleGoogleLogin = async () => {
// // navigation.navigate("Google");
// };
// 处理Facebook登录
const handleFacebookLogin = () => {
@ -180,7 +213,7 @@ export const LoginScreen = ({ onClose, isModal }: LoginScreenProps) => {
{/* 登录按钮 */}
<TouchableOpacity
style={styles.loginButton}
onPress={handleGoogleLogin}
onPress={() => handleGoogleLogin()}
>
<View style={styles.loginButtonIcon}>
<Image
@ -188,7 +221,9 @@ export const LoginScreen = ({ onClose, isModal }: LoginScreenProps) => {
style={{ width: 20, height: 20 }}
/>
</View>
<Text style={styles.loginButtonText}>{t("continueWithGoogle")}</Text>
<Text style={styles.loginButtonText}>
{t("continueWithGoogle")}
</Text>
</TouchableOpacity>
<TouchableOpacity
@ -211,7 +246,9 @@ export const LoginScreen = ({ onClose, isModal }: LoginScreenProps) => {
<View style={[styles.loginButtonIcon, styles.appleIconBg]}>
<Text>🍎</Text>
</View>
<Text style={styles.loginButtonText}>{t("continueWithApple")}</Text>
<Text style={styles.loginButtonText}>
{t("continueWithApple")}
</Text>
</TouchableOpacity>
)}
@ -252,7 +289,8 @@ export const LoginScreen = ({ onClose, isModal }: LoginScreenProps) => {
{/* 服务条款 */}
<View style={styles.termsContainer}>
<Text style={styles.terms}>
{t("termsText")} <Text style={styles.link}>{t("termsOfUse")}</Text>
{t("termsText")}{" "}
<Text style={styles.link}>{t("termsOfUse")}</Text>
</Text>
<Text style={styles.terms}>
{t("and")} <Text style={styles.link}>{t("privacyPolicy")}</Text>
@ -262,16 +300,10 @@ export const LoginScreen = ({ onClose, isModal }: LoginScreenProps) => {
</View>
{/* 邮箱登录模态框 - 直接渲染 */}
<EmailLoginModal
visible={emailModalVisible}
onClose={hideEmailModal}
/>
<EmailLoginModal visible={emailModalVisible} onClose={hideEmailModal} />
{/* 手机登录模态框 - 直接渲染 */}
<PhoneLoginModal
visible={phoneModalVisible}
onClose={hidePhoneModal}
/>
<PhoneLoginModal visible={phoneModalVisible} onClose={hidePhoneModal} />
</SafeAreaView>
);
};

135
app/screens/pay/Pay.tsx

@ -1,6 +1,6 @@
import { View, StyleSheet, Alert } from "react-native";
import { useRoute, RouteProp, useNavigation } from "@react-navigation/native";
import { useEffect, useState } from "react";
import { useEffect, useState, useRef } from "react";
import { payApi, PaymentInfoResponse } from "../../services/api/payApi";
import { WebView } from "react-native-webview";
import * as Linking from "expo-linking";
@ -8,7 +8,7 @@ import { navigate, navigationRef } from "../../navigation/RootNavigation";
type PayScreenRouteProp = RouteProp<
{
Pay: { payUrl: string };
Pay: { payUrl: string; method: string; order_id: string };
},
"Pay"
>;
@ -19,6 +19,63 @@ export const Pay = () => {
const navigation = useNavigation();
const { payUrl } = route.params;
const [payInfo, setPayInfo] = useState<PaymentInfoResponse>();
const pollIntervalRef = useRef<NodeJS.Timeout | null>(null);
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
// 轮询 wavePay 状态
const pollWavePayStatus = () => {
payApi
.wavePay(route.params.order_id)
.then((res) => {
console.log(res);
if (res.pay_status === 1) {
safeNavigate("PaymentSuccessScreen", res);
// 支付状态为1,停止轮询和超时定时器
if (pollIntervalRef.current) {
clearInterval(pollIntervalRef.current);
pollIntervalRef.current = null;
}
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
timeoutRef.current = null;
}
}
})
.catch((error) => {
console.error("WavePay 轮询错误:", error);
});
};
// 开始轮询
const startPolling = () => {
// 立即执行一次
pollWavePayStatus();
// 设置轮询,每2秒执行一次
if (!pollIntervalRef.current) {
pollIntervalRef.current = setInterval(pollWavePayStatus, 2000);
}
// 设置50秒超时
if (!timeoutRef.current) {
timeoutRef.current = setTimeout(() => {
// 超时处理:停止轮询并导航到错误页面
stopPolling();
safeNavigate("PayError", {});
}, 50000); // 50秒
}
};
// 停止轮询
const stopPolling = () => {
if (pollIntervalRef.current) {
clearInterval(pollIntervalRef.current);
pollIntervalRef.current = null;
}
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
timeoutRef.current = null;
}
};
useEffect(() => {
// 设置处理深度链接的监听器
@ -39,6 +96,8 @@ export const Pay = () => {
return () => {
// 清理订阅
subscription.remove();
// 清理轮询
stopPolling();
};
}, []);
@ -48,13 +107,11 @@ export const Pay = () => {
if (url && url.includes("payment_success=true")) {
// 如果网页URL中包含成功参数,可以在这里处理
Alert.alert("检测到支付成功!");
// navigation.navigate('PaymentConfirmation');
}
};
// 导航辅助函数,尝试使用多种方式导航
const safeNavigate = (routeName: string, params: any) => {
try {
// 尝试使用组件内的navigation
// @ts-ignore 忽略可能的类型错误
@ -93,38 +150,46 @@ export const Pay = () => {
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) => {
// 检查URL是否包含支付成功的参数
const { url } = request;
if (url) {
// 解析参数
const parsed = Linking.parse(url);
const params = parsed.queryParams || {};
// 检查是否存在paymentId参数并且不为null
if (params.paymentId && params.paymentId !== "null") {
if (params.PayerID && params.PayerID !== "null") {
payApi
.paySuccessCallback(
params.paymentId as string,
params.PayerID as string
)
.then((res) => {
if (res.status === 1) {
// 尝试跳转到支付成功页面
safeNavigate("PaymentSuccessScreen", params);
} else {
safeNavigate("PayError", params);
}
});
}
// wave 轮询处理
if (route.params.method === "wave") {
// 开始轮询 wavePay 状态
startPolling();
}
// 检查URL是否包含支付成功的参数 paypal
if (route.params.method === "paypal") {
const { url } = request;
if (url) {
// 解析参数
const parsed = Linking.parse(url);
const params = parsed.queryParams || {};
return false; // 不在WebView中加载
} else {
// console.log("未检测到有效的paymentId,导航到支付失败页面");
// Alert.alert("支付失败");
// // 尝试跳转到支付失败页面
// safeNavigate('PayError', params);
// return false; // 不在WebView中加载
// 检查是否存在paymentId参数并且不为null
if (params.paymentId && params.paymentId !== "null") {
if (params.PayerID && params.PayerID !== "null") {
payApi
.paySuccessCallback(
params.paymentId as string,
params.PayerID as string
)
.then((res) => {
if (res.status === 1) {
// 尝试跳转到支付成功页面
safeNavigate("PaymentSuccessScreen", params);
} else {
safeNavigate("PayError", params);
}
});
}
return false; // 不在WebView中加载
} else {
// console.log("未检测到有效的paymentId,导航到支付失败页面");
// Alert.alert("支付失败");
// // 尝试跳转到支付失败页面
// safeNavigate('PayError', params);
// return false; // 不在WebView中加载
}
}
}

78
app/screens/previewOrder/perviewOrder.tsx

@ -1,4 +1,4 @@
import React from 'react';
import React from "react";
import {
View,
Text,
@ -11,11 +11,16 @@ import {
Platform,
StatusBar,
SafeAreaView,
BackHandler
BackHandler,
} from "react-native";
import useCreateOrderStore from "../../store/createOrder";
import BackIcon from "../../components/BackIcon";
import { useNavigation, useRoute, RouteProp, useFocusEffect } from "@react-navigation/native";
import {
useNavigation,
useRoute,
RouteProp,
useFocusEffect,
} from "@react-navigation/native";
import { NativeStackNavigationProp } from "@react-navigation/native-stack";
import { useState, useEffect } from "react";
import useUserStore from "../../store/user";
@ -34,7 +39,7 @@ type RootStackParamList = {
currency: string;
amount: number;
};
Pay: { payUrl: string };
Pay: { payUrl: string; method: string; order_id: string };
OrderDetails: { orderId?: number };
};
@ -70,9 +75,10 @@ export const PreviewOrder = () => {
};
// 添加返回键监听(Android)
BackHandler.addEventListener('hardwareBackPress', onBackPress);
BackHandler.addEventListener("hardwareBackPress", onBackPress);
return () => BackHandler.removeEventListener('hardwareBackPress', onBackPress);
return () =>
BackHandler.removeEventListener("hardwareBackPress", onBackPress);
}, [navigation])
);
@ -99,11 +105,16 @@ export const PreviewOrder = () => {
.getPayInfo(data)
.then((res) => {
if (res.success) {
logPreviewOrder(navigation.getState().routes[navigation.getState().index - 1]?.name as string);
logPreviewOrder(
navigation.getState().routes[navigation.getState().index - 1]
?.name as string
);
console.log(getBurialPointData());
navigation.replace("Pay", {
payUrl: res.payment_url,
method: route.params.payMethod,
order_id: route.params.data.order_id.toString(),
});
}
})
@ -134,14 +145,18 @@ export const PreviewOrder = () => {
</TouchableOpacity>
</View>
<Text style={styles.titleHeading}>{t("order.preview.pay_now")}</Text>
<Text style={styles.titleHeading}>
{t("order.preview.pay_now")}
</Text>
</View>
<ScrollView style={styles.scrollContainer}>
<View style={styles.mainContent}>
{/* Payment Method Section */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>{t("order.preview.payment_method")}</Text>
<Text style={styles.sectionTitle}>
{t("order.preview.payment_method")}
</Text>
<View style={styles.paymentMethodContainer}>
<Text style={styles.paymentMethodText}>
{route.params.payMethod}
@ -150,7 +165,9 @@ export const PreviewOrder = () => {
{showPhoneInput && (
<View style={styles.phoneInputContainer}>
<Text style={styles.phoneInputLabel}>{t("order.preview.enter_phone")}</Text>
<Text style={styles.phoneInputLabel}>
{t("order.preview.enter_phone")}
</Text>
<TextInput
style={styles.phoneInput}
value={phoneNumber}
@ -164,17 +181,23 @@ export const PreviewOrder = () => {
{/* Order Info Section */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>{t("order.preview.payment_info")}</Text>
<Text style={styles.sectionTitle}>
{t("order.preview.payment_info")}
</Text>
<View style={styles.infoRow}>
<Text style={styles.infoLabel}>{t("order.preview.name")}</Text>
<Text style={styles.infoLabel}>
{t("order.preview.name")}
</Text>
<Text style={styles.infoValue}>
{route.params?.data.receiver_name || "N/A"}
</Text>
</View>
<View style={styles.infoRow}>
<Text style={styles.infoLabel}>{t("order.preview.phone")}</Text>
<Text style={styles.infoLabel}>
{t("order.preview.phone")}
</Text>
<Text style={styles.infoValue}>
{route.params?.data.receiver_phone || "N/A"}
</Text>
@ -190,14 +213,18 @@ export const PreviewOrder = () => {
)}
<View style={styles.infoRow}>
<Text style={styles.infoLabel}>{t("order.preview.country")}</Text>
<Text style={styles.infoLabel}>
{t("order.preview.country")}
</Text>
<Text style={styles.infoValue}>
{route.params?.data.receiver_country || "N/A"}
</Text>
</View>
<View style={styles.infoRow}>
<Text style={styles.infoLabel}>{t("order.preview.shipping_address")}</Text>
<Text style={styles.infoLabel}>
{t("order.preview.shipping_address")}
</Text>
<Text style={styles.infoValue}>
{route.params?.data.receiver_address || "N/A"}
</Text>
@ -224,11 +251,16 @@ export const PreviewOrder = () => {
{/* Order Summary Section */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>{t("order.preview.order_total")}</Text>
<Text style={styles.sectionTitle}>
{t("order.preview.order_total")}
</Text>
<View style={styles.totalRow}>
<Text style={styles.totalLabel}>{t("order.preview.total_amount")}</Text>
<Text style={styles.totalLabel}>
{t("order.preview.total_amount")}
</Text>
<Text style={styles.totalValue}>
{route.params.data.actual_amount} {route.params.data.currency}
{route.params.data.actual_amount}{" "}
{route.params.data.currency}
</Text>
</View>
</View>
@ -249,7 +281,9 @@ export const PreviewOrder = () => {
{loading ? (
<ActivityIndicator size="small" color="#ffffff" />
) : (
<Text style={styles.buttonText}>{t("order.preview.confirm_payment")}</Text>
<Text style={styles.buttonText}>
{t("order.preview.confirm_payment")}
</Text>
)}
</TouchableOpacity>
</View>
@ -262,15 +296,15 @@ export const PreviewOrder = () => {
const styles = StyleSheet.create({
safeArea: {
flex: 1,
backgroundColor: '#fff',
backgroundColor: "#fff",
},
safeAreaContent: {
flex: 1,
paddingTop: Platform.OS === 'android' ? 0 : 0,
paddingTop: Platform.OS === "android" ? 0 : 0,
},
container: {
flex: 1,
backgroundColor: '#fff',
backgroundColor: "#fff",
},
scrollContainer: {
flex: 1,

4
app/screens/productStatus/OrderDatails.tsx

@ -284,6 +284,8 @@ export const OrderDetails = () => {
setShowPaymentModal(false);
navigation.navigate("Pay", {
payUrl: res.payment_url,
method: selectedPayment,
order_id: orderDetails.order_id,
});
}else{
Alert.alert(t("error"), t("pay.payment_failed"));
@ -352,6 +354,8 @@ export const OrderDetails = () => {
// 打开支付页面
navigation.navigate("Pay", {
payUrl: response.payment_url,
method: paymentParams.payment_method,
order_id: orderDetails?.order_id || "",
});
} else {
Alert.alert(t("error"), t("order.error.payment_update"));

12
app/services/api/apiClient.ts

@ -54,6 +54,7 @@ const apiClient: AxiosInstance = axios.create({
// 请求拦截器
apiClient.interceptors.request.use(
async (config: InternalAxiosRequestConfig) => {
const baseUrl = config.baseURL || '';
let fullUrl = baseUrl + config.url;
if (config.params) {
@ -61,6 +62,17 @@ apiClient.interceptors.request.use(
fullUrl += (fullUrl.includes('?') ? '&' : '?') + params.toString();
}
// 根据平台设置请求方法大小写
if (config.method) {
if (Platform.OS === 'ios') {
// iOS使用小写方法
config.method = config.method.toLowerCase();
} else if (Platform.OS === 'android') {
// Android使用大写方法
config.method = config.method.toUpperCase();
}
}
// console.log("环境:", __DEV__ ? "开发环境" : "生产环境");
// console.log("请求方法:", config.method);
// console.log("完整URL:", fullUrl);

55
app/services/api/payApi.ts

@ -1,4 +1,4 @@
import apiService from './apiClient';
import apiService from "./apiClient";
// 支付方式类型定义
export interface PaymentMethod {
@ -65,11 +65,11 @@ export interface ConvertCurrencyBody {
}
export interface PaySuccessCallbackBody {
success: boolean;
msg: string;
order_id: number;
transaction_id: string;
status: number;
success: boolean;
msg: string;
order_id: number;
transaction_id: string;
status: number;
}
export interface rechargeHistory {
@ -89,8 +89,7 @@ export interface RechargeRecommendAmountResponse {
currency: string;
}
export interface Transaction {
export interface Transaction {
transaction_id: string;
type: "order_payment"; // Assuming 'order_payment' is the only possible type for this dataset
amount: number;
@ -107,10 +106,17 @@ export interface TransactionsResponse {
page_size: number;
}
export interface WavePayResponse {
order_id: string;
pay_status: number;
}
export const payApi = {
// 获取当前国家支付方式
getCountryPaymentMethods: () => {
return apiService.get<PaymentMethodsResponse>('/api/payment/country_payment_methods/');
return apiService.get<PaymentMethodsResponse>(
"/api/payment/country_payment_methods/"
);
},
// 获取支付信息
@ -124,27 +130,44 @@ export const payApi = {
},
// 支付成功的回调
paySuccessCallback: (paymentId:string,PayerID:string) => {
return apiService.post<PaySuccessCallbackBody>(`/api/payment/paypal/execute/`,{paymentId,PayerID});
paySuccessCallback: (paymentId: string, PayerID: string) => {
return apiService.post<PaySuccessCallbackBody>(
`/api/payment/paypal/execute/`,
{ paymentId, PayerID }
);
},
// 新增充值接口
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/');
return apiService.get<rechargeHistory[]>("/api/recharge/records/");
},
//获取充值推荐金额
getRechargeRecommendAmount: () => {
return apiService.get<RechargeRecommendAmountResponse>('/api/recharge/recommended-amounts/');
return apiService.get<RechargeRecommendAmountResponse>(
"/api/recharge/recommended-amounts/"
);
},
// 获取流水
getTransactionHistory: (page:number,page_size:number) => {
return apiService.get<TransactionsResponse>(`/api/users/me/transactions/?page=${page}&page_size=${page_size}`);
getTransactionHistory: (page: number, page_size: number) => {
return apiService.get<TransactionsResponse>(
`/api/users/me/transactions/?page=${page}&page_size=${page_size}`
);
},
// wave 支付
wavePay: (order_id: string) => {
return apiService.get<WavePayResponse>(
`/api/orders/${order_id}/payment-status/`
);
},
};

96
package-lock.json generated

@ -10,6 +10,7 @@
"license": "0BSD",
"dependencies": {
"@expo/metro-runtime": "~4.0.1",
"@expo/vector-icons": "^14.1.0",
"@react-native-async-storage/async-storage": "1.23.1",
"@react-native-community/checkbox": "^0.5.17",
"@react-native-community/datetimepicker": "8.2.0",
@ -22,6 +23,7 @@
"expo": "~52.0.41",
"expo-auth-session": "~6.0.3",
"expo-build-properties": "~0.13.3",
"expo-crypto": "~14.0.2",
"expo-image": "~2.0.7",
"expo-image-picker": "~16.0.6",
"expo-linear-gradient": "~14.0.2",
@ -54,6 +56,7 @@
"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-webview": "13.12.5",
"zustand": "^5.0.4"
},
@ -7519,6 +7522,15 @@
"node": ">=4"
}
},
"node_modules/css-in-js-utils": {
"version": "3.1.0",
"resolved": "https://registry.npmmirror.com/css-in-js-utils/-/css-in-js-utils-3.1.0.tgz",
"integrity": "sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A==",
"license": "MIT",
"dependencies": {
"hyphenate-style-name": "^1.0.3"
}
},
"node_modules/css-to-react-native": {
"version": "3.2.0",
"resolved": "https://registry.npmmirror.com/css-to-react-native/-/css-to-react-native-3.2.0.tgz",
@ -9502,6 +9514,12 @@
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
"license": "MIT"
},
"node_modules/fast-loops": {
"version": "1.1.4",
"resolved": "https://registry.npmmirror.com/fast-loops/-/fast-loops-1.1.4.tgz",
"integrity": "sha512-8dbd3XWoKCTms18ize6JmQF1SFnnfj5s0B7rRry22EofgMu7B6LKHVh+XfFqFGsqnbH54xgeO83PzpKI+ODhlg==",
"license": "MIT"
},
"node_modules/fast-uri": {
"version": "3.0.6",
"resolved": "https://registry.npmmirror.com/fast-uri/-/fast-uri-3.0.6.tgz",
@ -10326,6 +10344,12 @@
"node": ">=10.17.0"
}
},
"node_modules/hyphenate-style-name": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/hyphenate-style-name/-/hyphenate-style-name-1.1.0.tgz",
"integrity": "sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==",
"license": "BSD-3-Clause"
},
"node_modules/i18next": {
"version": "24.2.3",
"resolved": "https://registry.npmmirror.com/i18next/-/i18next-24.2.3.tgz",
@ -10475,6 +10499,16 @@
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"license": "ISC"
},
"node_modules/inline-style-prefixer": {
"version": "6.0.4",
"resolved": "https://registry.npmmirror.com/inline-style-prefixer/-/inline-style-prefixer-6.0.4.tgz",
"integrity": "sha512-FwXmZC2zbeeS7NzGjJ6pAiqRhXR0ugUShSNb6GApMl6da0/XGc4MOJsoWAywia52EEWbXNSy0pzkwz/+Y+swSg==",
"license": "MIT",
"dependencies": {
"css-in-js-utils": "^3.1.0",
"fast-loops": "^1.1.3"
}
},
"node_modules/internal-ip": {
"version": "4.3.0",
"resolved": "https://registry.npmmirror.com/internal-ip/-/internal-ip-4.3.0.tgz",
@ -10607,18 +10641,6 @@
"node": ">=0.10.0"
}
},
"node_modules/internal-ip/node_modules/which": {
"version": "1.3.1",
"resolved": "https://registry.npmmirror.com/which/-/which-1.3.1.tgz",
"integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
"license": "ISC",
"dependencies": {
"isexe": "^2.0.0"
},
"bin": {
"which": "bin/which"
}
},
"node_modules/internal-slot": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/internal-slot/-/internal-slot-1.1.0.tgz",
@ -14876,6 +14898,38 @@
"node": ">=10"
}
},
"node_modules/react-native-web": {
"version": "0.19.13",
"resolved": "https://registry.npmmirror.com/react-native-web/-/react-native-web-0.19.13.tgz",
"integrity": "sha512-etv3bN8rJglrRCp/uL4p7l8QvUNUC++QwDbdZ8CB7BvZiMvsxfFIRM1j04vxNldG3uo2puRd6OSWR3ibtmc29A==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.18.6",
"@react-native/normalize-colors": "^0.74.1",
"fbjs": "^3.0.4",
"inline-style-prefixer": "^6.0.1",
"memoize-one": "^6.0.0",
"nullthrows": "^1.1.1",
"postcss-value-parser": "^4.2.0",
"styleq": "^0.1.3"
},
"peerDependencies": {
"react": "^18.0.0",
"react-dom": "^18.0.0"
}
},
"node_modules/react-native-web/node_modules/@react-native/normalize-colors": {
"version": "0.74.89",
"resolved": "https://registry.npmmirror.com/@react-native/normalize-colors/-/normalize-colors-0.74.89.tgz",
"integrity": "sha512-qoMMXddVKVhZ8PA1AbUCk83trpd6N+1nF2A6k1i6LsQObyS92fELuk8kU/lQs6M7BsMHwqyLCpQJ1uFgNvIQXg==",
"license": "MIT"
},
"node_modules/react-native-web/node_modules/memoize-one": {
"version": "6.0.0",
"resolved": "https://registry.npmmirror.com/memoize-one/-/memoize-one-6.0.0.tgz",
"integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==",
"license": "MIT"
},
"node_modules/react-native-webview": {
"version": "13.12.5",
"resolved": "https://registry.npmmirror.com/react-native-webview/-/react-native-webview-13.12.5.tgz",
@ -16404,6 +16458,12 @@
"integrity": "sha512-0MP/Cxx5SzeeZ10p/bZI0S6MpgD+yxAhi1BOQ34jgnMXsCq3j1t6tQnZu+KdlL7dvJTLT3g9xN8tl10TqgFMcg==",
"license": "MIT"
},
"node_modules/styleq": {
"version": "0.1.3",
"resolved": "https://registry.npmmirror.com/styleq/-/styleq-0.1.3.tgz",
"integrity": "sha512-3ZUifmCDCQanjeej1f6kyl/BeP/Vae5EYkQ9iJfUm/QwZvlgnZzyflqAsAWYURdtea8Vkvswu2GrC57h3qffcA==",
"license": "MIT"
},
"node_modules/sucrase": {
"version": "3.35.0",
"resolved": "https://registry.npmmirror.com/sucrase/-/sucrase-3.35.0.tgz",
@ -17639,6 +17699,18 @@
"node": ">=8"
}
},
"node_modules/which": {
"version": "1.3.1",
"resolved": "https://registry.npmmirror.com/which/-/which-1.3.1.tgz",
"integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
"license": "ISC",
"dependencies": {
"isexe": "^2.0.0"
},
"bin": {
"which": "bin/which"
}
},
"node_modules/which-boxed-primitive": {
"version": "1.1.1",
"resolved": "https://registry.npmmirror.com/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz",

2
package.json

@ -10,6 +10,7 @@
},
"dependencies": {
"@expo/metro-runtime": "~4.0.1",
"@expo/vector-icons": "^14.1.0",
"@react-native-async-storage/async-storage": "1.23.1",
"@react-native-community/checkbox": "^0.5.17",
"@react-native-community/datetimepicker": "8.2.0",
@ -22,6 +23,7 @@
"expo": "~52.0.41",
"expo-auth-session": "~6.0.3",
"expo-build-properties": "~0.13.3",
"expo-crypto": "~14.0.2",
"expo-image": "~2.0.7",
"expo-image-picker": "~16.0.6",
"expo-linear-gradient": "~14.0.2",

552
yarn.lock

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save