feat: 样式调整+markdown渲染
This commit is contained in:
parent
6e83165e96
commit
e20aa93e44
@ -339,6 +339,17 @@ export default function TabLayout() {
|
||||
tabBarStyle: { display: 'none' } // 确保在标签栏中不显示
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 设置页面 */}
|
||||
<Tabs.Screen
|
||||
name="setting"
|
||||
options={{
|
||||
title: 'setting',
|
||||
tabBarButton: () => null, // 隐藏底部标签栏
|
||||
headerShown: false, // 隐藏导航栏
|
||||
tabBarStyle: { display: 'none' } // 确保在标签栏中不显示
|
||||
}}
|
||||
/>
|
||||
</Tabs >
|
||||
);
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -9,7 +9,7 @@ import { ThemedText } from '@/components/ThemedText';
|
||||
import { fetchApi } from '@/lib/server-api-util';
|
||||
import { useLocalSearchParams, useRouter } from "expo-router";
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Image, StyleSheet, TouchableOpacity, View } from 'react-native';
|
||||
import { Image, ScrollView, StyleSheet, TouchableOpacity, View } from 'react-native';
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
|
||||
export default function Rights() {
|
||||
@ -43,59 +43,79 @@ export default function Rights() {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<View style={[styles.container, { paddingTop: insets.top, paddingBottom: insets.bottom }]}>
|
||||
{/* 导航栏 */}
|
||||
<View
|
||||
style={styles.header}
|
||||
>
|
||||
<TouchableOpacity onPress={() => { router.push('/owner') }} style={{ padding: 16 }}>
|
||||
<ReturnArrowSvg />
|
||||
</TouchableOpacity>
|
||||
<ThemedText style={styles.headerTitle}>
|
||||
Subscription
|
||||
</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}>
|
||||
Purchase
|
||||
</ThemedText>
|
||||
<View style={styles.cardPoints}>
|
||||
<StarSvg />
|
||||
<ThemedText style={styles.cardPointsText}>{credit}</ThemedText>
|
||||
<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}>
|
||||
Subscription
|
||||
</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}>
|
||||
Purchase
|
||||
</ThemedText>
|
||||
<View style={styles.cardPoints}>
|
||||
<StarSvg />
|
||||
<ThemedText style={styles.cardPointsText}>{credit}</ThemedText>
|
||||
</View>
|
||||
</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" }}>Free</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" }}> Pro</ThemedText>
|
||||
</TouchableOpacity>
|
||||
{/* 会员信息 */}
|
||||
<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" }}>Free</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" }}>Pro</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>
|
||||
{/* 普通权益 */}
|
||||
<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>
|
||||
{/* 会员权益信息 */}
|
||||
<ProRights style={{ display: userType === 'normal' ? "none" : "flex" }} />
|
||||
{/* 会员权益信息 */}
|
||||
<View style={{ flex: 1, marginBottom: 80 }}>
|
||||
<ProRights style={{ display: userType === 'normal' ? "none" : "flex" }} />
|
||||
</View>
|
||||
</ScrollView>
|
||||
{/* 付费按钮 */}
|
||||
<View style={{ padding: 16, position: 'absolute', bottom: 0, left: 0, right: 0, display: userType === 'normal' ? "none" : "flex" }}>
|
||||
<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 () => {
|
||||
@ -153,13 +173,14 @@ const styles = StyleSheet.create({
|
||||
marginVertical: 16,
|
||||
padding: 16,
|
||||
borderRadius: 12,
|
||||
shadowColor: "#000",
|
||||
backgroundColor: '#fff',
|
||||
shadowColor: '#000',
|
||||
shadowOffset: {
|
||||
width: 0,
|
||||
height: 2,
|
||||
},
|
||||
shadowOpacity: 0.25,
|
||||
shadowRadius: 3.84,
|
||||
shadowRadius: 5,
|
||||
elevation: 5,
|
||||
},
|
||||
container: {
|
||||
@ -216,6 +237,7 @@ const styles = StyleSheet.create({
|
||||
cardPointsText: {
|
||||
fontSize: 32,
|
||||
fontWeight: '700',
|
||||
color: '#4C320C'
|
||||
color: '#4C320C',
|
||||
lineHeight: 32
|
||||
}
|
||||
});
|
||||
|
||||
495
app/(tabs)/setting.tsx
Normal file
495
app/(tabs)/setting.tsx
Normal file
@ -0,0 +1,495 @@
|
||||
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 React, { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Linking, 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();
|
||||
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();
|
||||
console.log('当前权限状态:', currentStatus);
|
||||
|
||||
// 2. 如果没有权限,则跳过获取位置
|
||||
if (!currentStatus) {
|
||||
console.log('没有权限,跳过获取位置')
|
||||
return;
|
||||
// const newStatus = await requestLocationPermission();
|
||||
// setLocationEnabled(newStatus);
|
||||
// currentStatus = newStatus;
|
||||
|
||||
// if (!currentStatus) {
|
||||
// // alert('需要位置权限才能继续');
|
||||
// return;
|
||||
// }
|
||||
}
|
||||
|
||||
// 3. 确保位置服务已启用
|
||||
const isEnabled = await Location.hasServicesEnabledAsync();
|
||||
if (!isEnabled) {
|
||||
alert('请先启用位置服务');
|
||||
return;
|
||||
}
|
||||
console.log('位置服务已启用');
|
||||
// 4. 获取当前位置
|
||||
const location = await Location.getCurrentPositionAsync({
|
||||
accuracy: Location.Accuracy.High, // 使用高精度
|
||||
timeInterval: 10000, // 可选:最大等待时间(毫秒)
|
||||
});
|
||||
console.log('位置:', location);
|
||||
|
||||
// 地理位置逆编码
|
||||
const address = await reverseGeocode(location.coords.latitude, location.coords.longitude);
|
||||
// 5. 更新位置状态
|
||||
if (address) {
|
||||
setCurrentLocation(address);
|
||||
}
|
||||
|
||||
return location;
|
||||
} catch (error: any) {
|
||||
if (error.code === 'PERMISSION_DENIED' || error.code === 'PERMISSION_DENIED_ERROR') {
|
||||
alert('位置权限被拒绝,请在设置中启用位置服务');
|
||||
} else if (error.code === 'TIMEOUT') {
|
||||
alert('获取位置超时,请检查网络和位置服务');
|
||||
} else {
|
||||
alert(`无法获取您的位置: ${error.message || '未知错误'}`);
|
||||
}
|
||||
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)) => {
|
||||
console.log('位置权限:', res);
|
||||
setLocationEnabled(res);
|
||||
})
|
||||
// 媒体库权限
|
||||
getPermissions().then((res: boolean | ((prevState: boolean) => boolean)) => {
|
||||
console.log('媒体库权限:', res);
|
||||
setAlbumEnabled(res);
|
||||
})
|
||||
// 通知权限
|
||||
checkNotificationPermission().then((res: boolean | ((prevState: boolean) => boolean)) => {
|
||||
console.log('通知权限:', res);
|
||||
setNotificationsEnabled(res);
|
||||
})
|
||||
}
|
||||
}, [modalVisible])
|
||||
|
||||
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 />
|
||||
<TouchableOpacity style={styles.item} onPress={() => { setLcensesModalVisible(true) }} >
|
||||
<ThemedText style={styles.itemText}>{t('lcenses.qualification', { ns: 'personal' })}</ThemedText>
|
||||
<RightArrowSvg />
|
||||
</TouchableOpacity>
|
||||
<Divider />
|
||||
<TouchableOpacity style={styles.item} onPress={() => Linking.openURL("https://beian.miit.gov.cn/")} >
|
||||
<ThemedText style={styles.itemText}>{t('lcenses.ICP', { ns: 'personal' })}沪ICP备2023032876号-4</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;
|
||||
@ -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 }}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import BlackStarSvg from '@/assets/icons/svg/blackStar.svg';
|
||||
import { ThemedText } from "@/components/ThemedText";
|
||||
import { StyleProp, StyleSheet, TouchableOpacity, View, ViewStyle } from "react-native";
|
||||
import { ScrollView } from 'react-native-gesture-handler';
|
||||
import { ScrollView, StyleProp, StyleSheet, TouchableOpacity, View, ViewStyle } from "react-native";
|
||||
import { maxDiscountProduct } from './utils';
|
||||
|
||||
interface Props {
|
||||
@ -65,7 +64,7 @@ const Premium = (props: Props) => {
|
||||
<ThemedText style={[styles.titleText, { fontSize: 16 }]}>
|
||||
{item.product_code?.split('_')[item.product_code?.split('_')?.length - 1]}
|
||||
</ThemedText>
|
||||
<ThemedText style={[styles.titleText, { fontSize: 32 }]}>
|
||||
<ThemedText style={[styles.titleText, { fontSize: 32, lineHeight: 32 }]}>
|
||||
$ {item.unit_price.amount}
|
||||
</ThemedText>
|
||||
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
|
||||
@ -104,22 +103,9 @@ const styles = StyleSheet.create({
|
||||
borderColor: "#FFB645",
|
||||
borderWidth: 2,
|
||||
borderRadius: 24,
|
||||
width: "48%",
|
||||
width: 200,
|
||||
paddingBottom: 16
|
||||
},
|
||||
quarterly: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
gap: 20,
|
||||
borderColor: "#FAF9F6",
|
||||
borderWidth: 2,
|
||||
borderRadius: 24,
|
||||
width: "48%",
|
||||
paddingBottom: 16,
|
||||
height: "100%",
|
||||
},
|
||||
title: {
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
|
||||
78
package-lock.json
generated
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",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user