feat: 权益页面 #14
@ -328,6 +328,28 @@ export default function TabLayout() {
|
||||
tabBarStyle: { display: 'none' } // 确保在标签栏中不显示
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 购买权益页面 */}
|
||||
<Tabs.Screen
|
||||
name="rights"
|
||||
options={{
|
||||
title: 'rights',
|
||||
tabBarButton: () => null, // 隐藏底部标签栏
|
||||
headerShown: false, // 隐藏导航栏
|
||||
tabBarStyle: { display: 'none' } // 确保在标签栏中不显示
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 设置页面 */}
|
||||
<Tabs.Screen
|
||||
name="setting"
|
||||
options={{
|
||||
title: 'setting',
|
||||
tabBarButton: () => null, // 隐藏底部标签栏
|
||||
headerShown: false, // 隐藏导航栏
|
||||
tabBarStyle: { display: 'none' } // 确保在标签栏中不显示
|
||||
}}
|
||||
/>
|
||||
</Tabs >
|
||||
);
|
||||
}
|
||||
|
||||
@ -15,7 +15,7 @@ import { CountData, UserInfoDetails } from '@/types/user';
|
||||
import { useRouter } from 'expo-router';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FlatList, StyleSheet, View } from 'react-native';
|
||||
import { FlatList, StyleSheet, TouchableOpacity, View } from 'react-native';
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
|
||||
export default function OwnerPage() {
|
||||
@ -86,13 +86,19 @@ export default function OwnerPage() {
|
||||
<AlbumComponent setModalVisible={setModalVisible} />
|
||||
|
||||
{/* 资源数据 */}
|
||||
<View style={styles.resourceContainer}>
|
||||
<TouchableOpacity
|
||||
onPress={() => router.push({
|
||||
pathname: '/rights',
|
||||
params: { credit: userInfoDetails?.remain_points }
|
||||
})}
|
||||
style={styles.resourceContainer}
|
||||
>
|
||||
<View style={{ gap: 4 }}>
|
||||
<ThemedText style={styles.text}>{t("generalSetting.premium", { ns: "personal" })}</ThemedText>
|
||||
<ThemedText style={styles.secondText}>{t("generalSetting.unlock", { ns: "personal" })}</ThemedText>
|
||||
</View>
|
||||
<ComeinSvg width={24} height={24} />
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
{/* 分类 */}
|
||||
<View style={{ marginHorizontal: -16, marginBottom: -16 }}>
|
||||
<CarouselComponent data={userInfoDetails?.material_counter} />
|
||||
|
||||
@ -2,10 +2,12 @@ import { fetchApi } from "@/lib/server-api-util";
|
||||
import { Policy } from "@/types/personal-info";
|
||||
import { useEffect, useState } from "react";
|
||||
import { ScrollView, StyleSheet, Text, TouchableOpacity, View } from "react-native";
|
||||
import RenderHtml from 'react-native-render-html';
|
||||
import Markdown from 'react-native-markdown-display';
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
|
||||
const PrivacyPolicy = () => {
|
||||
const [article, setArticle] = useState<Policy>({} as Policy);
|
||||
const insets = useSafeAreaInsets();
|
||||
useEffect(() => {
|
||||
const loadArticle = async () => {
|
||||
fetchApi<Policy>(`/system-config/policy/privacy_policy`).then((res: any) => {
|
||||
@ -26,7 +28,7 @@ const PrivacyPolicy = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.centeredView}>
|
||||
<View style={[styles.centeredView, { paddingTop: insets.top, marginBottom: insets.bottom }]}>
|
||||
<View style={styles.modalView}>
|
||||
<View style={styles.modalHeader}>
|
||||
<Text style={{ opacity: 0 }}>Settings</Text>
|
||||
@ -36,14 +38,9 @@ const PrivacyPolicy = () => {
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<ScrollView style={styles.modalContent} showsVerticalScrollIndicator={false}>
|
||||
<RenderHtml
|
||||
source={{ html: article.content }}
|
||||
tagsStyles={{
|
||||
p: { fontSize: 16, lineHeight: 24 },
|
||||
strong: { fontWeight: 'bold' },
|
||||
em: { fontStyle: 'italic' },
|
||||
}}
|
||||
/>
|
||||
<Markdown>
|
||||
{article.content}
|
||||
</Markdown>
|
||||
</ScrollView>
|
||||
</View>
|
||||
</View>
|
||||
@ -86,6 +83,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
modalContent: {
|
||||
flex: 1,
|
||||
paddingHorizontal: 8
|
||||
},
|
||||
modalText: {
|
||||
fontSize: 16,
|
||||
|
||||
245
app/(tabs)/rights.tsx
Normal file
@ -0,0 +1,245 @@
|
||||
import ReturnArrowSvg from '@/assets/icons/svg/returnArrow.svg';
|
||||
import StarSvg from '@/assets/icons/svg/whiteStart.svg';
|
||||
import PrivacyModal from '@/components/owner/qualification/privacy';
|
||||
import Normal from '@/components/owner/rights/normal';
|
||||
import Premium, { PayItem } from '@/components/owner/rights/premium';
|
||||
import ProRights from '@/components/owner/rights/proRights';
|
||||
import { maxDiscountProduct } from '@/components/owner/rights/utils';
|
||||
import { ThemedText } from '@/components/ThemedText';
|
||||
import { fetchApi } from '@/lib/server-api-util';
|
||||
import { useLocalSearchParams, useRouter } from "expo-router";
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Image, ScrollView, StyleSheet, TouchableOpacity, View } from 'react-native';
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
|
||||
export default function Rights() {
|
||||
const insets = useSafeAreaInsets();
|
||||
const router = useRouter();
|
||||
const { t } = useTranslation();
|
||||
// 获取路由参数
|
||||
const { credit } = useLocalSearchParams<{
|
||||
credit: string;
|
||||
}>();
|
||||
// 普通用户,会员
|
||||
const [userType, setUserType] = useState<'normal' | 'premium'>('normal');
|
||||
// 选择权益方式
|
||||
const [payType, setPayType] = useState<string>('');
|
||||
|
||||
// 用户协议弹窗打开
|
||||
const [showTerms, setShowTerms] = useState<boolean>(false);
|
||||
|
||||
// 调接口获取支付信息
|
||||
const [premiumPay, setPremiumPay] = useState<PayItem[]>();
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const getPAy = async () => {
|
||||
setLoading(true);
|
||||
const payInfo = await fetchApi<PayItem[]>(`/order/product-items?product_type=Membership`)
|
||||
let bestValue = maxDiscountProduct(payInfo)
|
||||
setPayType(bestValue?.product_code)
|
||||
setPremiumPay([bestValue, ...payInfo?.filter((item) => item.product_code !== bestValue?.product_code)]);
|
||||
setLoading(false);
|
||||
}
|
||||
useEffect(() => {
|
||||
getPAy();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1 }}>
|
||||
<ScrollView style={[styles.container, { paddingTop: insets.top, paddingBottom: insets.bottom + 80 }]}>
|
||||
{/* 导航栏 */}
|
||||
<View
|
||||
style={styles.header}
|
||||
>
|
||||
<TouchableOpacity onPress={() => { router.push('/owner') }} style={{ padding: 16 }}>
|
||||
<ReturnArrowSvg />
|
||||
</TouchableOpacity>
|
||||
<ThemedText style={styles.headerTitle}>
|
||||
{t('rights.title', { ns: 'personal' })}
|
||||
</ThemedText>
|
||||
<ThemedText className='opacity-0'>123</ThemedText>
|
||||
</View>
|
||||
{/* 会员卡 */}
|
||||
<View style={styles.card}>
|
||||
{userType === 'normal' ? (
|
||||
<Image source={require('@/assets/images/png/owner/normal.png')} style={{ height: 150, objectFit: 'cover', width: '100%' }} />
|
||||
) : (
|
||||
<Image source={require('@/assets/images/png/owner/pro.png')} style={{ height: 150, objectFit: 'cover', width: '100%' }} />
|
||||
)}
|
||||
<View style={styles.cardContent}>
|
||||
<View style={styles.cardinfo}>
|
||||
<ThemedText style={styles.cardTitle}>
|
||||
{t('rights.purchase', { ns: 'personal' })}
|
||||
</ThemedText>
|
||||
<View style={styles.cardPoints}>
|
||||
<StarSvg />
|
||||
<ThemedText style={styles.cardPointsText}>{credit}</ThemedText>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 会员信息 */}
|
||||
<View style={styles.info}>
|
||||
{/* 切换按钮 */}
|
||||
<View style={styles.switchButton}>
|
||||
<TouchableOpacity
|
||||
onPress={() => { setUserType("normal") }}
|
||||
style={[styles.switchButtonItem, { backgroundColor: userType === 'normal' ? "#FFB645" : "#fff", borderColor: userType === 'normal' ? "#FFB645" : "#E2793F" }]}
|
||||
>
|
||||
<ThemedText style={{ color: userType === 'normal' ? "#fff" : "#E2793F" }}> {t('rights.free', { ns: 'personal' })}</ThemedText>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
onPress={() => { setUserType("premium") }}
|
||||
style={[styles.switchButtonItem, { backgroundColor: userType === 'premium' ? "#E2793F" : "#fff", borderColor: userType === 'premium' ? "#E2793F" : "#E2793F" }]}
|
||||
>
|
||||
<ThemedText style={{ color: userType === 'premium' ? "#fff" : "#E2793F" }}>{t('rights.premium', { ns: 'personal' })}</ThemedText>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
{/* 普通权益 */}
|
||||
<Normal setUserType={setUserType} style={{ display: userType === 'normal' ? "flex" : "none" }} />
|
||||
{/* 会员权益 */}
|
||||
<Premium setPayType={setPayType} setShowTerms={setShowTerms} payType={payType} premiumPay={premiumPay} loading={loading} style={{ display: userType === 'normal' ? "none" : "flex" }} />
|
||||
</View>
|
||||
{/* 会员权益信息 */}
|
||||
<View style={{ flex: 1, marginBottom: 80 }}>
|
||||
<ProRights style={{ display: userType === 'normal' ? "none" : "flex" }} />
|
||||
</View>
|
||||
</ScrollView>
|
||||
{/* 付费按钮 */}
|
||||
<View style={{
|
||||
padding: 16,
|
||||
backgroundColor: '#fff',
|
||||
borderTopWidth: 1,
|
||||
borderTopColor: '#eee',
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
display: userType === 'normal' ? "none" : "flex"
|
||||
}}>
|
||||
<TouchableOpacity
|
||||
style={styles.goPay}
|
||||
onPress={async () => {
|
||||
setUserType('premium');
|
||||
}}
|
||||
activeOpacity={0.8}
|
||||
>
|
||||
<ThemedText style={{ color: '#fff', fontWeight: '700', fontSize: 14 }}>
|
||||
{t('rights.subscribe', { ns: 'personal' })}
|
||||
</ThemedText>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
onPress={async () => {
|
||||
setShowTerms(true);
|
||||
}}
|
||||
activeOpacity={0.8}
|
||||
>
|
||||
<ThemedText style={{ color: '#AC7E35', fontWeight: '400', fontSize: 11, textDecorationLine: 'underline', textAlign: 'center' }}>
|
||||
{t('rights.terms', { ns: 'personal' })}
|
||||
</ThemedText>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
{/* 协议弹窗 */}
|
||||
<PrivacyModal modalVisible={showTerms} setModalVisible={setShowTerms} type={"user"} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
goPay: {
|
||||
backgroundColor: '#E2793F',
|
||||
borderRadius: 24,
|
||||
paddingVertical: 10,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
width: "100%",
|
||||
},
|
||||
switchButton: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
gap: 16,
|
||||
marginBottom: 16
|
||||
},
|
||||
switchButtonItem: {
|
||||
width: "48%",
|
||||
borderRadius: 24,
|
||||
paddingVertical: 6,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
borderWidth: 1
|
||||
},
|
||||
info: {
|
||||
marginHorizontal: 16,
|
||||
marginVertical: 16,
|
||||
padding: 16,
|
||||
borderRadius: 12,
|
||||
backgroundColor: '#fff',
|
||||
shadowColor: '#000',
|
||||
shadowOffset: {
|
||||
width: 0,
|
||||
height: 2,
|
||||
},
|
||||
shadowOpacity: 0.25,
|
||||
shadowRadius: 5,
|
||||
elevation: 5,
|
||||
},
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: 'white',
|
||||
},
|
||||
header: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
marginVertical: 16,
|
||||
},
|
||||
headerTitle: {
|
||||
fontSize: 20,
|
||||
fontWeight: '700',
|
||||
color: '#4C320C',
|
||||
},
|
||||
card: {
|
||||
marginHorizontal: 16,
|
||||
marginVertical: 16,
|
||||
backgroundColor: '#FFB645',
|
||||
borderRadius: 12,
|
||||
},
|
||||
cardContent: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
padding: 16,
|
||||
justifyContent: 'space-between'
|
||||
},
|
||||
cardinfo: {
|
||||
alignItems: 'flex-end',
|
||||
},
|
||||
cardTitle: {
|
||||
fontSize: 12,
|
||||
fontWeight: '700',
|
||||
color: '#E2793F',
|
||||
backgroundColor: '#fff',
|
||||
paddingHorizontal: 8,
|
||||
paddingVertical: 2,
|
||||
borderRadius: 20,
|
||||
textAlign: 'center',
|
||||
marginBottom: 24
|
||||
},
|
||||
cardPoints: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: 4
|
||||
},
|
||||
cardPointsText: {
|
||||
fontSize: 32,
|
||||
fontWeight: '700',
|
||||
color: '#4C320C',
|
||||
lineHeight: 32
|
||||
}
|
||||
});
|
||||
502
app/(tabs)/setting.tsx
Normal file
@ -0,0 +1,502 @@
|
||||
import DeleteSvg from '@/assets/icons/svg/delete.svg';
|
||||
import LogoutSvg from '@/assets/icons/svg/logout.svg';
|
||||
import ReturnArrowSvg from '@/assets/icons/svg/returnArrow.svg';
|
||||
import RightArrowSvg from '@/assets/icons/svg/rightArrow.svg';
|
||||
import DeleteModal from '@/components/owner/delete';
|
||||
import LcensesModal from '@/components/owner/qualification/lcenses';
|
||||
import PrivacyModal from '@/components/owner/qualification/privacy';
|
||||
import CustomSwitch from '@/components/owner/switch';
|
||||
import UserInfo from '@/components/owner/userInfo';
|
||||
import { checkNotificationPermission, getLocationPermission, getPermissions, requestLocationPermission, requestMediaLibraryPermission, requestNotificationPermission, reverseGeocode } from '@/components/owner/utils';
|
||||
import { ThemedText } from '@/components/ThemedText';
|
||||
import { useAuth } from '@/contexts/auth-context';
|
||||
import { fetchApi } from '@/lib/server-api-util';
|
||||
import { Address, User } from '@/types/user';
|
||||
import * as Location from 'expo-location';
|
||||
import { useRouter } from 'expo-router';
|
||||
import * as SecureStore from 'expo-secure-store';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Linking, Platform, Pressable, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
|
||||
const Setting = (props: { modalVisible: boolean, setModalVisible: (visible: boolean) => void, userInfo: User }) => {
|
||||
const { modalVisible, setModalVisible, userInfo } = props;
|
||||
const insets = useSafeAreaInsets();
|
||||
const { t } = useTranslation();
|
||||
// 判断当前语言环境
|
||||
let language = "";
|
||||
const getLanguage = async () => {
|
||||
if (Platform.OS === 'web') {
|
||||
language = localStorage.getItem('i18nextLng') || "";
|
||||
} else {
|
||||
language = await SecureStore.getItemAsync('i18nextLng') || "";
|
||||
}
|
||||
}
|
||||
|
||||
const [modalType, setModalType] = useState<'ai' | 'terms' | 'privacy' | 'user'>('ai');
|
||||
// 协议弹窗
|
||||
const [privacyModalVisible, setPrivacyModalVisible] = useState(false);
|
||||
// 许可证弹窗
|
||||
const [lcensesModalVisible, setLcensesModalVisible] = useState(false);
|
||||
|
||||
// 删除弹窗
|
||||
const [deleteModalVisible, setDeleteModalVisible] = useState(false);
|
||||
const { logout } = useAuth();
|
||||
const router = useRouter();
|
||||
// 打开设置
|
||||
const openAppSettings = () => {
|
||||
Linking.openSettings();
|
||||
};
|
||||
// 通知消息权限开关
|
||||
const [notificationsEnabled, setNotificationsEnabled] = useState(false);
|
||||
const toggleNotifications = async () => {
|
||||
if (notificationsEnabled) {
|
||||
// 引导去设置关闭权限
|
||||
openAppSettings()
|
||||
} else {
|
||||
requestNotificationPermission()
|
||||
.then((granted: boolean | ((prevState: boolean) => boolean)) => {
|
||||
setNotificationsEnabled(granted);
|
||||
});
|
||||
setModalVisible(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 相册权限
|
||||
const [albumEnabled, setAlbumEnabled] = useState(false);
|
||||
const toggleAlbum = async () => {
|
||||
if (albumEnabled) {
|
||||
// 引导去设置关闭权限
|
||||
openAppSettings()
|
||||
} else {
|
||||
requestMediaLibraryPermission()
|
||||
.then((granted: boolean | ((prevState: boolean) => boolean)) => {
|
||||
setAlbumEnabled(granted);
|
||||
});
|
||||
setModalVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
// 位置权限
|
||||
const [locationEnabled, setLocationEnabled] = useState(false);
|
||||
// 位置权限更改
|
||||
const toggleLocation = async () => {
|
||||
if (locationEnabled) {
|
||||
// 如果权限已开启,点击则引导用户去设置关闭
|
||||
openAppSettings();
|
||||
} else {
|
||||
requestLocationPermission()
|
||||
.then((granted: boolean | ((prevState: boolean) => boolean)) => {
|
||||
setLocationEnabled(granted);
|
||||
});
|
||||
setModalVisible(false);
|
||||
}
|
||||
};
|
||||
// 正在获取位置信息
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
// 动画开启
|
||||
const [isRefreshing, setIsRefreshing] = useState(false);
|
||||
|
||||
// 当前位置状态
|
||||
const [currentLocation, setCurrentLocation] = useState<Address>({} as Address);
|
||||
|
||||
// 获取当前位置
|
||||
const getCurrentLocation = async () => {
|
||||
setIsLoading(true);
|
||||
setIsRefreshing(true);
|
||||
|
||||
try {
|
||||
// 1. 首先检查当前权限状态 -- 获取当前的位置权限
|
||||
let currentStatus = await getLocationPermission();
|
||||
|
||||
// 2. 如果没有权限,则跳过获取位置
|
||||
if (!currentStatus) {
|
||||
return;
|
||||
// const newStatus = await requestLocationPermission();
|
||||
// setLocationEnabled(newStatus);
|
||||
// currentStatus = newStatus;
|
||||
|
||||
// if (!currentStatus) {
|
||||
// // alert('需要位置权限才能继续');
|
||||
// return;
|
||||
// }
|
||||
}
|
||||
|
||||
// 3. 确保位置服务已启用
|
||||
const isEnabled = await Location.hasServicesEnabledAsync();
|
||||
if (!isEnabled) {
|
||||
alert(t('permission.locationPermissionRequired', { ns: 'common' }));
|
||||
return;
|
||||
}
|
||||
// 4. 获取当前位置
|
||||
const location = await Location.getCurrentPositionAsync({
|
||||
accuracy: Location.Accuracy.High, // 使用高精度
|
||||
timeInterval: 10000, // 可选:最大等待时间(毫秒)
|
||||
});
|
||||
|
||||
// 地理位置逆编码
|
||||
const address = await reverseGeocode(location.coords.latitude, location.coords.longitude);
|
||||
// 5. 更新位置状态
|
||||
if (address) {
|
||||
|
|
||||
setCurrentLocation(address);
|
||||
}
|
||||
|
txcjh
commented
国际化 国际化
|
||||
|
||||
return location;
|
||||
} catch (error: any) {
|
||||
if (error.code === 'TIMEOUT') {
|
||||
alert(t('permission.timeout', { ns: 'common' }));
|
||||
} else {
|
||||
alert(t('permission.notLocation', { ns: 'common' }) + error.message || t('permission.notError', { ns: 'common' }));
|
||||
}
|
||||
throw error; // 重新抛出错误以便上层处理
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
setIsRefreshing(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 退出登录
|
||||
const handleLogout = () => {
|
||||
fetchApi("/iam/logout", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then(async (res) => {
|
||||
await logout();
|
||||
setModalVisible(false);
|
||||
router.replace('/login');
|
||||
})
|
||||
.catch(() => {
|
||||
console.error("jwt has expired.");
|
||||
});
|
||||
};
|
||||
// 检查是否有权限
|
||||
useEffect(() => {
|
||||
if (modalVisible) {
|
||||
// 位置权限
|
||||
getLocationPermission().then((res: boolean | ((prevState: boolean) => boolean)) => {
|
||||
setLocationEnabled(res);
|
||||
})
|
||||
// 媒体库权限
|
||||
getPermissions().then((res: boolean | ((prevState: boolean) => boolean)) => {
|
||||
setAlbumEnabled(res);
|
||||
})
|
||||
// 通知权限
|
||||
checkNotificationPermission().then((res: boolean | ((prevState: boolean) => boolean)) => {
|
||||
setNotificationsEnabled(res);
|
||||
})
|
||||
}
|
||||
}, [modalVisible])
|
||||
|
||||
// 获取语言环境
|
||||
useEffect(() => {
|
||||
getLanguage();
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1, paddingTop: insets.top, marginBottom: insets.bottom }}>
|
||||
<Pressable
|
||||
style={styles.centeredView}
|
||||
>
|
||||
<Pressable
|
||||
style={styles.modalView}
|
||||
onPress={(e) => e.stopPropagation()}>
|
||||
<View style={styles.modalHeader}>
|
||||
<TouchableOpacity onPress={() => { router.push('/owner') }}>
|
||||
<ReturnArrowSvg />
|
||||
</TouchableOpacity>
|
||||
<Text style={styles.modalTitle}>{t('generalSetting.allTitle', { ns: 'personal' })}</Text>
|
||||
<Text style={{ opacity: 0 }}>×</Text>
|
||||
</View>
|
||||
<ScrollView style={styles.modalContent} showsVerticalScrollIndicator={false}>
|
||||
{/* 用户信息 */}
|
||||
<UserInfo
|
||||
userInfo={userInfo}
|
||||
setModalVisible={setModalVisible}
|
||||
modalVisible={modalVisible}
|
||||
setCurrentLocation={setCurrentLocation}
|
||||
getCurrentLocation={getCurrentLocation}
|
||||
isLoading={isLoading}
|
||||
isRefreshing={isRefreshing}
|
||||
currentLocation={currentLocation}
|
||||
/>
|
||||
{/* 升级版本 */}
|
||||
{/* <View style={{ marginTop: 16 }}>
|
||||
<ThemedText style={{ marginLeft: 16, marginVertical: 8, color: '#AC7E35', fontSize: 14, fontWeight: '600' }}>{t('generalSetting.subscription', { ns: 'personal' })}</ThemedText>
|
||||
<View style={styles.premium}>
|
||||
<View>
|
||||
<ThemedText style={styles.itemText}>{t('generalSetting.subscriptionTitle', { ns: 'personal' })}</ThemedText>
|
||||
<ThemedText style={{ color: '#AC7E35', fontSize: 12 }}>{t('generalSetting.subscriptionText', { ns: 'personal' })}</ThemedText>
|
||||
</View>
|
||||
<TouchableOpacity
|
||||
style={styles.upgradeButton}
|
||||
onPress={async () => {
|
||||
|
||||
}}
|
||||
>
|
||||
<Text style={styles.upgradeButtonText}>
|
||||
{t('generalSetting.upgrade', { ns: 'personal' })}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View> */}
|
||||
{/* 消息通知 */}
|
||||
{/* <View style={{ marginTop: 16 }}>
|
||||
<ThemedText style={{ marginLeft: 16, marginVertical: 8, color: '#AC7E35', fontSize: 14, fontWeight: '600' }}>{t('permission.pushNotification', { ns: 'personal' })}</ThemedText>
|
||||
<View style={styles.premium}>
|
||||
<View>
|
||||
<ThemedText style={styles.itemText}>{t('permission.pushNotification', { ns: 'personal' })}</ThemedText>
|
||||
</View>
|
||||
<CustomSwitch
|
||||
isEnabled={notificationsEnabled}
|
||||
toggleSwitch={toggleNotifications}
|
||||
/>
|
||||
</View>
|
||||
</View> */}
|
||||
{/* 权限信息 */}
|
||||
<View style={{ marginTop: 16 }}>
|
||||
<ThemedText style={{ marginLeft: 16, marginVertical: 8, color: '#AC7E35', fontSize: 14, fontWeight: '600' }}>{t('permission.permissionManagement', { ns: 'personal' })}</ThemedText>
|
||||
<View style={styles.content}>
|
||||
{/* 相册权限 */}
|
||||
<View style={styles.item}>
|
||||
<ThemedText style={styles.itemText}>{t('permission.galleryAccess', { ns: 'personal' })}</ThemedText>
|
||||
<CustomSwitch
|
||||
isEnabled={albumEnabled}
|
||||
toggleSwitch={toggleAlbum}
|
||||
/>
|
||||
</View>
|
||||
{/* 分割线 */}
|
||||
<Divider />
|
||||
{/* 位置权限 */}
|
||||
<View style={styles.item}>
|
||||
<View>
|
||||
<ThemedText style={styles.itemText}>{t('permission.locationPermission', { ns: 'personal' })}</ThemedText>
|
||||
</View>
|
||||
<CustomSwitch
|
||||
isEnabled={locationEnabled}
|
||||
toggleSwitch={toggleLocation}
|
||||
/>
|
||||
</View>
|
||||
<Divider />
|
||||
<View style={styles.item}>
|
||||
<View>
|
||||
<ThemedText style={styles.itemText}>{t('permission.pushNotification', { ns: 'personal' })}</ThemedText>
|
||||
</View>
|
||||
<CustomSwitch
|
||||
isEnabled={notificationsEnabled}
|
||||
toggleSwitch={toggleNotifications}
|
||||
/>
|
||||
</View>
|
||||
{/* 相册成片权限 */}
|
||||
{/* <View style={styles.item}>
|
||||
<View>
|
||||
<ThemedText style={styles.itemText}>Opus Permission</ThemedText>
|
||||
</View>
|
||||
<CustomSwitch
|
||||
isEnabled={albumEnabled}
|
||||
toggleSwitch={toggleAlbum}
|
||||
/>
|
||||
</View> */}
|
||||
</View>
|
||||
</View>
|
||||
{/* 账号 */}
|
||||
{/* <View style={{ marginTop: 16 }}>
|
||||
<ThemedText style={{ marginLeft: 16, marginVertical: 8, color: '#AC7E35', fontSize: 14, fontWeight: '600' }}>Account</ThemedText>
|
||||
<View style={styles.content}>
|
||||
<View style={styles.item}>
|
||||
<View>
|
||||
<ThemedText style={styles.itemText}>Notifications</ThemedText>
|
||||
</View>
|
||||
<CustomSwitch
|
||||
isEnabled={notificationsEnabled}
|
||||
toggleSwitch={toggleNotifications}
|
||||
/>
|
||||
</View>
|
||||
<Divider />
|
||||
<View style={styles.item}>
|
||||
<ThemedText style={styles.itemText}>Delete Account</ThemedText>
|
||||
<DeleteSvg />
|
||||
</View>
|
||||
</View>
|
||||
</View> */}
|
||||
{/* 协议 */}
|
||||
<View style={{ marginTop: 16 }}>
|
||||
<ThemedText style={{ marginLeft: 16, marginVertical: 8, color: '#AC7E35', fontSize: 14, fontWeight: '600' }}>{t('lcenses.title', { ns: 'personal' })}</ThemedText>
|
||||
<View style={styles.content}>
|
||||
<TouchableOpacity style={styles.item} onPress={() => { setModalType('privacy'); setPrivacyModalVisible(true) }} >
|
||||
<ThemedText style={styles.itemText}>{t('lcenses.privacyPolicy', { ns: 'personal' })}</ThemedText>
|
||||
<RightArrowSvg />
|
||||
</TouchableOpacity>
|
||||
<Divider />
|
||||
<TouchableOpacity style={styles.item} onPress={() => { setModalType('terms'); setPrivacyModalVisible(true) }} >
|
||||
<ThemedText style={styles.itemText}>{t('lcenses.applyPermission', { ns: 'personal' })}</ThemedText>
|
||||
<RightArrowSvg />
|
||||
</TouchableOpacity>
|
||||
<Divider />
|
||||
<TouchableOpacity style={styles.item} onPress={() => { setModalType('user'); setPrivacyModalVisible(true) }} >
|
||||
<ThemedText style={styles.itemText}>{t('lcenses.userAgreement', { ns: 'personal' })}</ThemedText>
|
||||
<RightArrowSvg />
|
||||
</TouchableOpacity>
|
||||
<Divider />
|
||||
<TouchableOpacity style={styles.item} onPress={() => { setModalType('ai'); setPrivacyModalVisible(true) }} >
|
||||
<ThemedText style={styles.itemText}>{t('lcenses.aiPolicy', { ns: 'personal' })}</ThemedText>
|
||||
<RightArrowSvg />
|
||||
</TouchableOpacity>
|
||||
<Divider />
|
||||
|
txcjh
commented
沪ICP备2025133004号-2A, 沪ICP备2025133004号-2A,
i18n非中文不展示
|
||||
<TouchableOpacity style={styles.item} onPress={() => { setLcensesModalVisible(true) }} >
|
||||
<ThemedText style={styles.itemText}>{t('lcenses.qualification', { ns: 'personal' })}</ThemedText>
|
||||
<RightArrowSvg />
|
||||
</TouchableOpacity>
|
||||
<Divider />
|
||||
<TouchableOpacity style={[styles.item, { display: language == "en" ? 'none' : 'flex' }]} onPress={() => Linking.openURL("https://beian.miit.gov.cn/")} >
|
||||
<ThemedText style={styles.itemText}>{t('lcenses.ICP', { ns: 'personal' })}沪ICP备2025133004号-2A</ThemedText>
|
||||
<RightArrowSvg />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
{/* 其他信息 */}
|
||||
<View style={{ marginTop: 16 }}>
|
||||
<ThemedText style={{ marginLeft: 16, marginVertical: 8, color: '#AC7E35', fontSize: 14, fontWeight: '600' }}>{t('generalSetting.otherInformation', { ns: 'personal' })}</ThemedText>
|
||||
<View style={styles.content}>
|
||||
<TouchableOpacity style={styles.item} onPress={() => Linking.openURL("https://work.weixin.qq.com/kfid/kfca0ac87f4e05e8bfd")} >
|
||||
<ThemedText style={styles.itemText}>{t('generalSetting.contactUs', { ns: 'personal' })}</ThemedText>
|
||||
{/* <RightArrowSvg /> */}
|
||||
</TouchableOpacity>
|
||||
<Divider />
|
||||
<View style={styles.item}>
|
||||
<ThemedText style={styles.itemText}>{t('generalSetting.version', { ns: 'personal' })}</ThemedText>
|
||||
<ThemedText style={styles.itemText}>{"0.5.0"}</ThemedText>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
{/* 退出 */}
|
||||
<TouchableOpacity style={[styles.premium, { marginVertical: 8 }]} onPress={handleLogout}>
|
||||
<ThemedText style={{ color: '#E2793F', fontSize: 14, fontWeight: '600' }}>{t('generalSetting.logout', { ns: 'personal' })}</ThemedText>
|
||||
<LogoutSvg />
|
||||
</TouchableOpacity>
|
||||
{/* 注销账号 */}
|
||||
<TouchableOpacity style={[styles.premium, { marginVertical: 8 }]} onPress={() => setDeleteModalVisible(true)}>
|
||||
<ThemedText style={{ color: '#E2793F', fontSize: 14, fontWeight: '600' }}>{t('generalSetting.deleteAccount', { ns: 'personal' })}</ThemedText>
|
||||
<DeleteSvg />
|
||||
</TouchableOpacity>
|
||||
</ScrollView>
|
||||
</Pressable>
|
||||
</Pressable>
|
||||
<PrivacyModal modalVisible={privacyModalVisible} setModalVisible={setPrivacyModalVisible} type={modalType} />
|
||||
<LcensesModal modalVisible={lcensesModalVisible} setModalVisible={setLcensesModalVisible} />
|
||||
<DeleteModal modalVisible={deleteModalVisible} setModalVisible={setDeleteModalVisible} setSettingModalVisible={setModalVisible} />
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
centeredView: {
|
||||
flex: 1,
|
||||
justifyContent: 'flex-end',
|
||||
backgroundColor: 'rgba(0,0,0,0.5)',
|
||||
},
|
||||
modalView: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
backgroundColor: 'white',
|
||||
paddingHorizontal: 16,
|
||||
},
|
||||
modalHeader: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
marginBottom: 20,
|
||||
},
|
||||
modalTitle: {
|
||||
fontSize: 20,
|
||||
fontWeight: 'bold',
|
||||
color: '#4C320C',
|
||||
},
|
||||
closeButton: {
|
||||
fontSize: 28,
|
||||
color: '#4C320C',
|
||||
padding: 10,
|
||||
},
|
||||
modalContent: {
|
||||
flex: 1,
|
||||
},
|
||||
modalText: {
|
||||
fontSize: 16,
|
||||
color: '#4C320C',
|
||||
},
|
||||
premium: {
|
||||
backgroundColor: "#FAF9F6",
|
||||
padding: 16,
|
||||
borderRadius: 24,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
},
|
||||
content: {
|
||||
flex: 1,
|
||||
flexDirection: 'column',
|
||||
gap: 4,
|
||||
backgroundColor: '#FAF9F6',
|
||||
borderRadius: 24,
|
||||
paddingVertical: 8
|
||||
},
|
||||
item: {
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 8,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
},
|
||||
itemText: {
|
||||
fontSize: 14,
|
||||
fontWeight: '600',
|
||||
color: '#4C320C',
|
||||
},
|
||||
upgradeButton: {
|
||||
backgroundColor: '#E2793F',
|
||||
borderRadius: 20,
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 8,
|
||||
},
|
||||
upgradeButtonText: {
|
||||
color: '#fff',
|
||||
fontSize: 14,
|
||||
fontWeight: "600"
|
||||
},
|
||||
switchContainer: {
|
||||
width: 50,
|
||||
height: 30,
|
||||
borderRadius: 15,
|
||||
justifyContent: 'center',
|
||||
paddingHorizontal: 2,
|
||||
},
|
||||
switchOn: {
|
||||
backgroundColor: '#E2793F',
|
||||
alignItems: 'flex-end',
|
||||
},
|
||||
switchOff: {
|
||||
backgroundColor: '#E5E5E5',
|
||||
alignItems: 'flex-start',
|
||||
},
|
||||
switchCircle: {
|
||||
width: 26,
|
||||
height: 26,
|
||||
borderRadius: 13,
|
||||
},
|
||||
switchCircleOn: {
|
||||
backgroundColor: 'white',
|
||||
},
|
||||
switchCircleOff: {
|
||||
backgroundColor: '#A5A5A5',
|
||||
},
|
||||
});
|
||||
|
||||
const Divider = () => {
|
||||
return (
|
||||
<View className='w-full h-[1px] bg-[#B5977F]'></View>
|
||||
)
|
||||
}
|
||||
export default Setting;
|
||||
3
assets/icons/svg/blackStar.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="12" height="15" viewBox="0 0 12 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6 0C6 0 6.33105 5.36245 7.62054 6.97432C8.91004 8.58619 12 9 12 9C12 9 8.91004 9.41381 7.62054 11.0257C6.33105 12.6375 6 15 6 15C6 15 5.66895 12.6375 4.37946 11.0257C3.08996 9.41381 0 9 0 9C0 9 3.08996 8.58619 4.37946 6.97432C5.66895 5.36245 6 0 6 0Z" fill="#4C320C"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 382 B |
3
assets/icons/svg/get.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="12" height="8" viewBox="0 0 12 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1 4.00001L4.33357 7L11 1" stroke="#FFB645" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 220 B |
62
assets/icons/svg/proCard.svg
Normal file
@ -0,0 +1,62 @@
|
||||
<svg width="365" height="140" viewBox="0 0 365 140" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<mask id="mask0_3223_1003" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="365" height="140">
|
||||
<rect width="365" height="140" rx="26" fill="#FFB645"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_3223_1003)">
|
||||
<g filter="url(#filter0_i_3223_1003)">
|
||||
<path d="M61.4295 45.6599C60.8472 41.3476 66.5965 39.3378 68.8277 43.0737L71.3125 47.2342C73.0132 50.082 77.2417 49.7415 78.4648 46.6582L88.4108 21.5851C89.7463 18.2183 94.5115 18.2183 95.8471 21.5851L105.793 46.6582C107.016 49.7415 111.245 50.082 112.945 47.2342L115.43 43.0737C117.661 39.3378 123.411 41.3476 122.828 45.6599L120.386 63.7507C120.118 65.735 118.424 67.2155 116.422 67.2155H67.8363C65.8341 67.2155 64.1402 65.735 63.8723 63.7507L61.4295 45.6599Z" fill="#FFF8DE"/>
|
||||
</g>
|
||||
<circle cx="59.1272" cy="28.4803" r="9.12722" fill="white" stroke="#FFB645" stroke-width="4"/>
|
||||
<circle cx="92.2844" cy="16.1272" r="9.12722" fill="#E2793F" stroke="#FFB645" stroke-width="4"/>
|
||||
<circle cx="125.441" cy="28.4803" r="9.12722" fill="white" stroke="#FFB645" stroke-width="4"/>
|
||||
<path d="M49.5755 61.4144C50.4475 58.5036 57.1744 61.1692 60.4288 62.8658L52.5625 66.8182C50.6468 66.7604 48.7034 64.3251 49.5755 61.4144Z" fill="#FFDBA3"/>
|
||||
<path d="M51.0981 61.8437C51.5796 59.3845 56.005 61.8494 58.1575 63.3893L54.2174 65.2282C52.977 65.1247 50.6165 64.3029 51.0981 61.8437Z" fill="#AC7E35"/>
|
||||
<path d="M133.568 60.0186C132.303 57.2559 126.008 60.8237 123.019 62.9529L131.355 65.7827C133.244 65.4612 134.833 62.7813 133.568 60.0186Z" fill="#FFDBA3"/>
|
||||
<path d="M132.118 60.6536C131.302 58.2843 127.259 61.3359 125.339 63.1579L129.495 64.436C130.709 64.1624 132.934 63.0229 132.118 60.6536Z" fill="#AC7E35"/>
|
||||
<path d="M27.1969 92.9743C56.4427 42.319 129.557 42.3191 158.803 92.9744L178.751 127.526C207.997 178.181 171.44 241.5 112.948 241.5H73.0518C14.5601 241.5 -21.9971 178.181 7.24869 127.526L27.1969 92.9743Z" fill="#FFD18A"/>
|
||||
<rect x="88.5132" y="89.6838" width="2.99145" height="4.18803" rx="1.49573" transform="rotate(-180 88.5132 89.6838)" fill="#4C320C"/>
|
||||
<rect x="99.2822" y="89.6838" width="2.99145" height="4.18803" rx="1.49573" transform="rotate(-180 99.2822 89.6838)" fill="#4C320C"/>
|
||||
<path d="M50.1157 120.318C70.7994 90.2494 115.202 90.2493 135.886 120.318L159.443 154.565C183.198 189.1 158.474 236.115 116.558 236.115H69.4435C27.5268 236.115 2.8027 189.1 26.5585 154.565L50.1157 120.318Z" fill="#FFF8DE"/>
|
||||
<g filter="url(#filter1_i_3223_1003)">
|
||||
<ellipse cx="134.581" cy="115.111" rx="49.0598" ry="35" fill="#FFF8DE"/>
|
||||
</g>
|
||||
<g filter="url(#filter2_i_3223_1003)">
|
||||
<ellipse cx="51.1196" cy="115.111" rx="48.7607" ry="35" fill="#FFF8DE"/>
|
||||
</g>
|
||||
<ellipse cx="92.7008" cy="97.7608" rx="3.58974" ry="2.69231" transform="rotate(180 92.7008 97.7608)" fill="#FFB8B9"/>
|
||||
<ellipse cx="8.5474" cy="3.40976" rx="8.5474" ry="3.40976" transform="matrix(1 0 0 -1 108.647 142)" fill="#FFD38D"/>
|
||||
<ellipse cx="65.5473" cy="138.59" rx="8.5474" ry="3.40976" transform="rotate(-180 65.5473 138.59)" fill="#FFD38D"/>
|
||||
<path d="M91.9591 101.026C92.2223 100.57 92.8803 100.57 93.1434 101.026L93.7356 102.051C93.9988 102.507 93.6698 103.077 93.1434 103.077H91.9591C91.4328 103.077 91.1038 102.507 91.367 102.051L91.9591 101.026Z" fill="#4C320C"/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_i_3223_1003" x="61.3882" y="19.0601" width="61.4814" height="48.1555" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="21"/>
|
||||
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.886275 0 0 0 0 0.47451 0 0 0 0 0.247059 0 0 0 1 0"/>
|
||||
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_3223_1003"/>
|
||||
</filter>
|
||||
<filter id="filter1_i_3223_1003" x="80.1369" y="80.1111" width="103.504" height="71.7949" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dx="-5.38462" dy="1.79487"/>
|
||||
<feGaussianBlur stdDeviation="4.9359"/>
|
||||
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 0.713726 0 0 0 0 0.270588 0 0 0 1 0"/>
|
||||
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_3223_1003"/>
|
||||
</filter>
|
||||
<filter id="filter2_i_3223_1003" x="2.35889" y="80.1111" width="103.504" height="70" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dx="5.98291"/>
|
||||
<feGaussianBlur stdDeviation="3.2906"/>
|
||||
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 0.713974 0 0 0 0 0.272498 0 0 0 1 0"/>
|
||||
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_3223_1003"/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.3 KiB |
47
assets/icons/svg/rightCard.svg
Normal file
@ -0,0 +1,47 @@
|
||||
<svg width="365" height="140" viewBox="0 0 365 140" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<mask id="mask0_3223_1545" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="365" height="140">
|
||||
<rect width="365" height="140" rx="26" fill="#FFB645"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_3223_1545)">
|
||||
<path d="M49.5755 61.4144C50.4475 58.5036 57.1744 61.1692 60.4288 62.8658L52.5625 66.8182C50.6468 66.7604 48.7034 64.3251 49.5755 61.4144Z" fill="#FFDBA3"/>
|
||||
<path d="M51.0981 61.8437C51.5796 59.3845 56.005 61.8494 58.1575 63.3893L54.2174 65.2282C52.977 65.1247 50.6165 64.3029 51.0981 61.8437Z" fill="#AC7E35"/>
|
||||
<path d="M133.568 60.0186C132.303 57.2559 126.008 60.8237 123.019 62.9529L131.355 65.7827C133.244 65.4612 134.833 62.7813 133.568 60.0186Z" fill="#FFDBA3"/>
|
||||
<path d="M132.118 60.6536C131.302 58.2843 127.259 61.3359 125.339 63.1579L129.495 64.436C130.709 64.1624 132.934 63.0229 132.118 60.6536Z" fill="#AC7E35"/>
|
||||
<path d="M27.1969 92.9743C56.4427 42.319 129.557 42.3191 158.803 92.9744L178.751 127.526C207.997 178.181 171.44 241.5 112.948 241.5H73.0518C14.5601 241.5 -21.9971 178.181 7.24869 127.526L27.1969 92.9743Z" fill="#FFD18A"/>
|
||||
<rect x="88.5132" y="89.6838" width="2.99145" height="4.18803" rx="1.49573" transform="rotate(-180 88.5132 89.6838)" fill="#4C320C"/>
|
||||
<rect x="99.2822" y="89.6838" width="2.99145" height="4.18803" rx="1.49573" transform="rotate(-180 99.2822 89.6838)" fill="#4C320C"/>
|
||||
<path d="M50.1157 120.318C70.7994 90.2494 115.202 90.2493 135.886 120.318L159.443 154.565C183.198 189.1 158.474 236.115 116.558 236.115H69.4435C27.5268 236.115 2.8027 189.1 26.5585 154.565L50.1157 120.318Z" fill="#FFF8DE"/>
|
||||
<g filter="url(#filter0_i_3223_1545)">
|
||||
<ellipse cx="134.581" cy="115.111" rx="49.0598" ry="35" fill="#FFF8DE"/>
|
||||
</g>
|
||||
<g filter="url(#filter1_i_3223_1545)">
|
||||
<ellipse cx="51.1196" cy="115.111" rx="48.7607" ry="35" fill="#FFF8DE"/>
|
||||
</g>
|
||||
<ellipse cx="92.7008" cy="97.7608" rx="3.58974" ry="2.69231" transform="rotate(180 92.7008 97.7608)" fill="#FFB8B9"/>
|
||||
<ellipse cx="8.5474" cy="3.40976" rx="8.5474" ry="3.40976" transform="matrix(1 0 0 -1 108.647 142)" fill="#FFD38D"/>
|
||||
<ellipse cx="65.5473" cy="138.59" rx="8.5474" ry="3.40976" transform="rotate(-180 65.5473 138.59)" fill="#FFD38D"/>
|
||||
<path d="M91.9591 101.026C92.2223 100.57 92.8803 100.57 93.1434 101.026L93.7356 102.051C93.9988 102.507 93.6698 103.077 93.1434 103.077H91.9591C91.4328 103.077 91.1038 102.507 91.367 102.051L91.9591 101.026Z" fill="#4C320C"/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_i_3223_1545" x="80.1369" y="80.1111" width="103.504" height="71.7949" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dx="-5.38462" dy="1.79487"/>
|
||||
<feGaussianBlur stdDeviation="4.9359"/>
|
||||
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 0.713726 0 0 0 0 0.270588 0 0 0 1 0"/>
|
||||
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_3223_1545"/>
|
||||
</filter>
|
||||
<filter id="filter1_i_3223_1545" x="2.35889" y="80.1111" width="103.504" height="70" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dx="5.98291"/>
|
||||
<feGaussianBlur stdDeviation="3.2906"/>
|
||||
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 0.713974 0 0 0 0 0.272498 0 0 0 1 0"/>
|
||||
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_3223_1545"/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.9 KiB |
3
assets/icons/svg/whiteStart.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="26" height="26" viewBox="0 0 26 26" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13 0C13 0 13.7173 9.29492 16.5112 12.0888C19.3051 14.8827 26 15.6 26 15.6C26 15.6 19.3051 16.3173 16.5112 19.1112C13.7173 21.9051 13 26 13 26C13 26 12.2827 21.9051 9.48882 19.1112C6.69492 16.3173 0 15.6 0 15.6C0 15.6 6.69492 14.8827 9.48882 12.0888C12.2827 9.29492 13 0 13 0Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 405 B |
BIN
assets/images/png/owner/normal.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
assets/images/png/owner/pro.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 96 B After Width: | Height: | Size: 34 KiB |
@ -11,10 +11,9 @@ interface CategoryProps {
|
||||
const AlbumComponent = ({ setModalVisible, style }: CategoryProps) => {
|
||||
const { t } = useTranslation();
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<View style={[styles.container, style]}>
|
||||
<TouchableOpacity style={{ flex: 3 }} onPress={() => { router.push("/download") }}>
|
||||
<TouchableOpacity style={{ flex: 3 }}>
|
||||
<ThemedText style={styles.text}>{t('generalSetting.album', { ns: 'personal' })}</ThemedText>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity style={{ flex: 3 }}>
|
||||
@ -22,7 +21,8 @@ const AlbumComponent = ({ setModalVisible, style }: CategoryProps) => {
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
setModalVisible(true);
|
||||
// setModalVisible(true);
|
||||
router.push('/setting');
|
||||
}}
|
||||
activeOpacity={0.7}
|
||||
hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
|
||||
|
||||
74
components/owner/rights/normal.tsx
Normal file
@ -0,0 +1,74 @@
|
||||
import GetSvg from "@/assets/icons/svg/get.svg";
|
||||
import { ThemedText } from "@/components/ThemedText";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { StyleProp, StyleSheet, TouchableOpacity, View, ViewStyle } from "react-native";
|
||||
|
||||
interface Props {
|
||||
setUserType: (type: 'normal' | 'premium') => void;
|
||||
style?: StyleProp<ViewStyle>;
|
||||
}
|
||||
|
||||
const Normal = (props: Props) => {
|
||||
const { setUserType } = props;
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<View style={[styles.normalInfo, props.style]}>
|
||||
<View style={styles.normalItem}>
|
||||
<View style={styles.normalItemContent}>
|
||||
<View style={{ flexDirection: "row", alignItems: "center", gap: 8 }}>
|
||||
|
txcjh
commented
国际化 国际化
|
||||
<GetSvg style={{ marginTop: 8 }} />
|
||||
<ThemedText style={{ fontSize: 12, fontWeight: '500', color: "#4C320C" }}>{t('rights.100Bonus', { ns: 'personal' })}</ThemedText>
|
||||
</View>
|
||||
<ThemedText style={{ fontSize: 10, color: "#AC7E35", marginLeft: 20 }}>{t('rights.100BonusText', { ns: 'personal' })}</ThemedText>
|
||||
</View>
|
||||
<View style={styles.normalItemContent}>
|
||||
<View style={{ flexDirection: "row", alignItems: "center", gap: 8 }}>
|
||||
<GetSvg style={{ marginTop: 8 }} />
|
||||
<ThemedText style={{ fontSize: 12, fontWeight: '500', color: "#4C320C" }}>{t('rights.10G', { ns: 'personal' })}</ThemedText>
|
||||
</View>
|
||||
<ThemedText style={{ fontSize: 10, color: "#AC7E35", marginLeft: 20 }}>{t('rights.10GText', { ns: 'personal' })}</ThemedText>
|
||||
</View>
|
||||
</View>
|
||||
<TouchableOpacity
|
||||
style={styles.goPro}
|
||||
onPress={async () => {
|
||||
setUserType('premium');
|
||||
}}
|
||||
activeOpacity={0.8}
|
||||
>
|
||||
<ThemedText style={{ color: '#fff', fontWeight: '700', fontSize: 14 }}>
|
||||
{t('rights.purchase', { ns: 'personal' })}
|
||||
</ThemedText>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
export default Normal;
|
||||
const styles = StyleSheet.create({
|
||||
goPro: {
|
||||
backgroundColor: '#E2793F',
|
||||
borderRadius: 24,
|
||||
paddingVertical: 6,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
width: "100%",
|
||||
},
|
||||
normalInfo: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
gap: 16
|
||||
},
|
||||
normalItem: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "flex-start",
|
||||
gap: 16
|
||||
},
|
||||
normalItemContent: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
}
|
||||
});
|
||||
127
components/owner/rights/premium.tsx
Normal file
@ -0,0 +1,127 @@
|
||||
import BlackStarSvg from '@/assets/icons/svg/blackStar.svg';
|
||||
import { ThemedText } from "@/components/ThemedText";
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ScrollView, StyleProp, StyleSheet, TouchableOpacity, View, ViewStyle } from "react-native";
|
||||
import { maxDiscountProduct } from './utils';
|
||||
|
||||
interface Props {
|
||||
style?: StyleProp<ViewStyle>;
|
||||
payType: string;
|
||||
setPayType: (type: string) => void;
|
||||
premiumPay: any;
|
||||
loading: boolean;
|
||||
setShowTerms: (visible: boolean) => void;
|
||||
}
|
||||
|
||||
export interface PayItem {
|
||||
id: number;
|
||||
product_id: number;
|
||||
product_type: string;
|
||||
product_code: string;
|
||||
product_name: string;
|
||||
unit_price: {
|
||||
amount: string;
|
||||
currency: string;
|
||||
},
|
||||
discount_amount: {
|
||||
amount: string;
|
||||
currency: string;
|
||||
}
|
||||
|
||||
}
|
||||
const Premium = (props: Props) => {
|
||||
const { style, payType, setPayType, premiumPay, loading, setShowTerms } = props;
|
||||
const bestValue = maxDiscountProduct(premiumPay)?.product_code
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<View style={[styles.proInfo, style]}>
|
||||
<ScrollView
|
||||
contentContainerStyle={{ gap: 16 }}
|
||||
showsVerticalScrollIndicator={false}
|
||||
horizontal={true}
|
||||
showsHorizontalScrollIndicator={false}
|
||||
>
|
||||
{loading
|
||||
?
|
||||
<ThemedText style={{ fontSize: 12, color: "#4C320C", fontWeight: "700", width: "100%", textAlign: "center" }}>
|
||||
{t('loading', { ns: 'common' })}
|
||||
</ThemedText>
|
||||
:
|
||||
premiumPay?.map((item: PayItem) => {
|
||||
return <TouchableOpacity
|
||||
onPress={async () => {
|
||||
setPayType(item?.product_code);
|
||||
}}
|
||||
style={[styles.yearly, { borderColor: payType === item?.product_code ? '#FFB645' : '#E1E1E1', opacity: payType === item?.product_code ? 1 : 0.5 }]}
|
||||
activeOpacity={0.8}
|
||||
>
|
||||
<View style={[styles.title, { opacity: item?.product_code === bestValue ? 1 : 0 }]}>
|
||||
<BlackStarSvg />
|
||||
<ThemedText style={[styles.titleText, { fontSize: 14 }]}>
|
||||
{t('rights.bestValue', { ns: 'personal' })}
|
||||
</ThemedText>
|
||||
<BlackStarSvg />
|
||||
</View>
|
||||
<ThemedText style={[styles.titleText, { fontSize: 16 }]}>
|
||||
{item.product_code?.split('_')[item.product_code?.split('_')?.length - 1]}
|
||||
</ThemedText>
|
||||
<ThemedText style={[styles.titleText, { fontSize: 32, lineHeight: 32 }]}>
|
||||
$ {item.unit_price.amount}
|
||||
</ThemedText>
|
||||
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
|
||||
<ThemedText style={[styles.titleText, { fontSize: 12, color: "#AC7E35", textDecorationLine: 'line-through' }]}>
|
||||
$ {item.discount_amount.amount}
|
||||
</ThemedText>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
})
|
||||
}
|
||||
</ScrollView>
|
||||
<View style={{ flexDirection: 'row', gap: 8, marginLeft: 4 }}>
|
||||
<ThemedText style={{ color: '#AC7E35', fontSize: 10 }}>
|
||||
{t('rights.cancelAnytimeBeforeRenewal', { ns: 'personal' })}
|
||||
</ThemedText>
|
||||
<ThemedText style={{ color: '#E2793F', fontSize: 10, textDecorationLine: 'underline' }} onPress={() => setShowTerms(true)}>
|
||||
{t('rights.terms', { ns: 'personal' })}
|
||||
</ThemedText>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
export default Premium;
|
||||
const styles = StyleSheet.create({
|
||||
proInfo: {
|
||||
borderRadius: 24,
|
||||
display: "flex",
|
||||
width: "100%",
|
||||
},
|
||||
yearly: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
gap: 16,
|
||||
borderColor: "#FFB645",
|
||||
borderWidth: 2,
|
||||
borderRadius: 24,
|
||||
width: 200,
|
||||
paddingBottom: 16
|
||||
},
|
||||
title: {
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
gap: 8,
|
||||
backgroundColor: "#FFB645",
|
||||
borderTopLeftRadius: 20,
|
||||
borderTopRightRadius: 20,
|
||||
padding: 4,
|
||||
width: "100%"
|
||||
},
|
||||
titleText: {
|
||||
color: '#4C320C',
|
||||
fontWeight: '700'
|
||||
}
|
||||
});
|
||||
49
components/owner/rights/proRights.tsx
Normal file
@ -0,0 +1,49 @@
|
||||
import GetSvg from "@/assets/icons/svg/get.svg";
|
||||
import { ThemedText } from "@/components/ThemedText";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { StyleProp, StyleSheet, View, ViewStyle } from "react-native";
|
||||
const ProRights = (props: { style?: StyleProp<ViewStyle> }) => {
|
||||
const { style } = props;
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<View style={[styles.proRights, style]}>
|
||||
<ThemedText style={{ fontSize: 12, color: "#4C320C", fontWeight: "700", width: "100%", textAlign: "center" }}>
|
||||
{t('rights.proTitle', { ns: 'personal' })}
|
||||
</ThemedText>
|
||||
<View style={{ display: "flex", flexDirection: "column", }}>
|
||||
<View style={{ flexDirection: "row", alignItems: "center", gap: 8 }}>
|
||||
<GetSvg style={{ marginTop: 8 }} />
|
||||
<ThemedText style={{ fontSize: 12, fontWeight: '500', color: "#4C320C" }}>{t('rights.noAd', { ns: 'personal' })}</ThemedText>
|
||||
|
txcjh
commented
国际化 国际化
|
||||
</View>
|
||||
<ThemedText style={{ fontSize: 10, color: "#AC7E35", marginLeft: 20 }}>{t('rights.noAdText', { ns: 'personal' })}</ThemedText>
|
||||
</View>
|
||||
<View style={styles.itemContent}>
|
||||
<View style={{ flexDirection: "row", alignItems: "center", gap: 8 }}>
|
||||
<GetSvg style={{ marginTop: 8 }} />
|
||||
<ThemedText style={{ fontSize: 12, fontWeight: '500', color: "#4C320C" }}>{t('rights.bonus', { ns: 'personal' })}</ThemedText>
|
||||
</View>
|
||||
<ThemedText style={{ fontSize: 10, color: "#AC7E35", marginLeft: 20 }}>{t('rights.bonusText', { ns: 'personal' })}</ThemedText>
|
||||
</View>
|
||||
<View style={styles.itemContent}>
|
||||
<View style={{ flexDirection: "row", alignItems: "center", gap: 8 }}>
|
||||
<GetSvg style={{ marginTop: 8 }} />
|
||||
<ThemedText style={{ fontSize: 12, fontWeight: '500', color: "#4C320C" }}>{t('rights.storage', { ns: 'personal' })}</ThemedText>
|
||||
</View>
|
||||
<ThemedText style={{ fontSize: 10, color: "#AC7E35", marginLeft: 20 }}>{t('rights.storageText', { ns: 'personal' })}</ThemedText>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
proRights: {
|
||||
padding: 16,
|
||||
gap: 8
|
||||
},
|
||||
itemContent: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
}
|
||||
})
|
||||
|
||||
export default ProRights;
|
||||
12
components/owner/rights/utils.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { PayItem } from "./premium";
|
||||
|
||||
// 使用 reduce 方法获取 discount_amount 的 amount 值最大的对象
|
||||
export const maxDiscountProduct = (products: PayItem[]) => {
|
||||
return products?.reduce((max, current) => {
|
||||
// 将 amount 转换为数字进行比较
|
||||
const maxAmount = parseFloat(max.discount_amount.amount);
|
||||
const currentAmount = parseFloat(current.discount_amount.amount);
|
||||
|
||||
return currentAmount > maxAmount ? current : max;
|
||||
});
|
||||
}
|
||||
@ -111,5 +111,11 @@
|
||||
"required": "You must agree to the Terms and Privacy Policy"
|
||||
}
|
||||
},
|
||||
"loading": "Loading..."
|
||||
"loading": "Loading...",
|
||||
"permission": {
|
||||
"locationPermissionRequired": "Location permission is required, please enable location service in settings",
|
||||
"timeout": "Location timeout, please check network and location service",
|
||||
"notLocation": "Unable to get your location: ",
|
||||
"notError": "Unknown error"
|
||||
}
|
||||
}
|
||||
@ -88,5 +88,27 @@
|
||||
"unlock": "Unlock more memory magic",
|
||||
"delete": "Are you sure you want to delete your account?",
|
||||
"cancel": "Cancel"
|
||||
},
|
||||
"rights": {
|
||||
"title": "Subscription",
|
||||
"premium": "Pro",
|
||||
"purchase": "Purchase",
|
||||
"free": "Free",
|
||||
"subscribe": "Subscribe",
|
||||
"terms": "Terms",
|
||||
"100Bonus": "Enjoy 100 Bonus Credits Every Month",
|
||||
"100BonusText": "Generate more memory pictures & videos and explore your past.",
|
||||
"10G": "10GB of Cloud Storage",
|
||||
"10GText": "Safely store your cherished photos, videos, and generated memories.",
|
||||
"goPremium": "Go Premium",
|
||||
"bestValue": "Best Value",
|
||||
"cancelAnytimeBeforeRenewal": "Cancel anytime before renewal. Learn more",
|
||||
"proTitle": "Enjoy MemoWake Pro Benefits",
|
||||
"noAd": "No advertisements",
|
||||
"noAdText": "There are no advertisements, so you can use the product with peace of mind.",
|
||||
"bonus": "Enjoy 100 Bonus Credits Every Month",
|
||||
"bonusText": "Generate more memory pictures & videos and explore your past.",
|
||||
"storage": "10GB of Cloud Storage",
|
||||
"storageText": "Safely store your cherished photos, videos, and generated memories."
|
||||
}
|
||||
}
|
||||
@ -110,5 +110,11 @@
|
||||
"required": "您必须同意服务条款和隐私政策"
|
||||
}
|
||||
},
|
||||
"loading": "加载中..."
|
||||
"loading": "加载中...",
|
||||
"permission": {
|
||||
"locationPermissionRequired": "位置权限被拒绝,请在设置中启用位置服务",
|
||||
"timeout": "获取位置超时,请检查网络和位置服务",
|
||||
"notLocation": "无法获取您的位置: ",
|
||||
"notError": "未知错误"
|
||||
}
|
||||
}
|
||||
@ -88,5 +88,27 @@
|
||||
"unlock": "解锁更多记忆魔法",
|
||||
"delete": "确定要注销账号吗?",
|
||||
"cancel": "取消"
|
||||
},
|
||||
"rights": {
|
||||
"title": "权益",
|
||||
"purchase": "购买",
|
||||
"free": "免费用户",
|
||||
"premium": "会员",
|
||||
"subscribe": "订阅",
|
||||
"terms": "用户协议",
|
||||
"100Bonus": "每月享受100积分",
|
||||
"100BonusText": "生成更多记忆照片和视频,探索你的过去。",
|
||||
"10G": "10GB的云存储",
|
||||
"10GText": "安全存储你的珍贵照片、视频和生成的记忆。",
|
||||
"goPremium": "升级至会员",
|
||||
"bestValue": "最佳值",
|
||||
"cancelAnytimeBeforeRenewal": "在续订前随时取消。了解更多",
|
||||
"proTitle": "享受MemoWake Pro权益",
|
||||
"noAd": "无广告",
|
||||
"noAdText": "没有广告,所以你可以安心使用产品。",
|
||||
"bonus": "每月享受100积分",
|
||||
"bonusText": "生成更多记忆照片和视频,探索你的过去。",
|
||||
"storage": "10GB的云存储",
|
||||
"storageText": "安全存储你的珍贵照片、视频和生成的记忆。"
|
||||
}
|
||||
}
|
||||
78
package-lock.json
generated
@ -57,6 +57,7 @@
|
||||
"react-native": "0.79.5",
|
||||
"react-native-gesture-handler": "~2.24.0",
|
||||
"react-native-linear-gradient": "^2.8.3",
|
||||
"react-native-markdown-display": "^7.0.2",
|
||||
"react-native-modal": "^14.0.0-rc.1",
|
||||
"react-native-picker-select": "^9.3.1",
|
||||
"react-native-progress": "^5.0.1",
|
||||
@ -12471,6 +12472,15 @@
|
||||
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/linkify-it": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz",
|
||||
"integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"uc.micro": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/locate-path": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
|
||||
@ -12704,6 +12714,37 @@
|
||||
"tmpl": "1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/markdown-it": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-10.0.0.tgz",
|
||||
"integrity": "sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"argparse": "^1.0.7",
|
||||
"entities": "~2.0.0",
|
||||
"linkify-it": "^2.0.0",
|
||||
"mdurl": "^1.0.1",
|
||||
"uc.micro": "^1.0.5"
|
||||
},
|
||||
"bin": {
|
||||
"markdown-it": "bin/markdown-it.js"
|
||||
}
|
||||
},
|
||||
"node_modules/markdown-it/node_modules/argparse": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"sprintf-js": "~1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/markdown-it/node_modules/entities": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz",
|
||||
"integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==",
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/marky": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/marky/-/marky-1.3.0.tgz",
|
||||
@ -12725,6 +12766,12 @@
|
||||
"integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==",
|
||||
"license": "CC0-1.0"
|
||||
},
|
||||
"node_modules/mdurl": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
|
||||
"integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/memoize-one": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
|
||||
@ -14964,6 +15011,15 @@
|
||||
"react-native": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-fit-image": {
|
||||
"version": "1.5.5",
|
||||
"resolved": "https://registry.npmjs.org/react-native-fit-image/-/react-native-fit-image-1.5.5.tgz",
|
||||
"integrity": "sha512-Wl3Vq2DQzxgsWKuW4USfck9zS7YzhvLNPpkwUUCF90bL32e1a0zOVQ3WsJILJOwzmPdHfzZmWasiiAUNBkhNkg==",
|
||||
"license": "Beerware",
|
||||
"dependencies": {
|
||||
"prop-types": "^15.5.10"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-gesture-handler": {
|
||||
"version": "2.24.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.24.0.tgz",
|
||||
@ -14999,6 +15055,22 @@
|
||||
"react-native": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-markdown-display": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-native-markdown-display/-/react-native-markdown-display-7.0.2.tgz",
|
||||
"integrity": "sha512-Mn4wotMvMfLAwbX/huMLt202W5DsdpMO/kblk+6eUs55S57VVNni1gzZCh5qpznYLjIQELNh50VIozEfY6fvaQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"css-to-react-native": "^3.0.0",
|
||||
"markdown-it": "^10.0.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"react-native-fit-image": "^1.5.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.2.0",
|
||||
"react-native": ">=0.50.4"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-modal": {
|
||||
"version": "14.0.0-rc.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-modal/-/react-native-modal-14.0.0-rc.1.tgz",
|
||||
@ -17510,6 +17582,12 @@
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/uc.micro": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
|
||||
"integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/unbox-primitive": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz",
|
||||
|
||||
@ -63,6 +63,7 @@
|
||||
"react-native": "0.79.5",
|
||||
"react-native-gesture-handler": "~2.24.0",
|
||||
"react-native-linear-gradient": "^2.8.3",
|
||||
"react-native-markdown-display": "^7.0.2",
|
||||
"react-native-modal": "^14.0.0-rc.1",
|
||||
"react-native-picker-select": "^9.3.1",
|
||||
"react-native-progress": "^5.0.1",
|
||||
@ -75,10 +76,10 @@
|
||||
"react-native-svg": "^15.11.2",
|
||||
"react-native-toast-message": "^2.3.0",
|
||||
"react-native-uuid": "^2.0.3",
|
||||
"react-native-view-shot": "4.0.3",
|
||||
"react-native-web": "~0.20.0",
|
||||
"react-native-webview": "13.13.5",
|
||||
"react-redux": "^9.2.0",
|
||||
"react-native-view-shot": "4.0.3"
|
||||
"react-redux": "^9.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.25.2",
|
||||
|
||||
这个去掉