505 lines
23 KiB
TypeScript
505 lines
23 KiB
TypeScript
import DeleteSvg from '@/assets/icons/svg/delete.svg';
|
||
import LogoutSvg from '@/assets/icons/svg/logout.svg';
|
||
import RightArrowSvg from '@/assets/icons/svg/rightArrow.svg';
|
||
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, Modal, Pressable, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||
import { ThemedText } from '../ThemedText';
|
||
import DeleteModal from './delete';
|
||
import LcensesModal from './qualification/lcenses';
|
||
import PrivacyModal from './qualification/privacy';
|
||
import CustomSwitch from './switch';
|
||
import UserInfo from './userInfo';
|
||
import { checkNotificationPermission, getLocationPermission, getPermissions, requestLocationPermission, requestMediaLibraryPermission, requestNotificationPermission, reverseGeocode } from './utils';
|
||
|
||
const SettingModal = (props: { modalVisible: boolean, setModalVisible: (visible: boolean) => void, userInfo: User }) => {
|
||
const { modalVisible, setModalVisible, userInfo } = props;
|
||
console.log(111111111111111111111111);
|
||
|
||
console.log("hjhkkkkkkkkkkkkkkkkkkkkkkkkkkk111111111", setModalVisible);
|
||
|
||
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) => {
|
||
setNotificationsEnabled(granted);
|
||
});
|
||
setModalVisible(false);
|
||
}
|
||
};
|
||
|
||
// 相册权限
|
||
const [albumEnabled, setAlbumEnabled] = useState(false);
|
||
const toggleAlbum = async () => {
|
||
if (albumEnabled) {
|
||
// 引导去设置关闭权限
|
||
openAppSettings()
|
||
} else {
|
||
requestMediaLibraryPermission()
|
||
.then((granted) => {
|
||
setAlbumEnabled(granted);
|
||
});
|
||
setModalVisible(false);
|
||
}
|
||
}
|
||
|
||
// 位置权限
|
||
const [locationEnabled, setLocationEnabled] = useState(false);
|
||
// 位置权限更改
|
||
const toggleLocation = async () => {
|
||
if (locationEnabled) {
|
||
// 如果权限已开启,点击则引导用户去设置关闭
|
||
openAppSettings();
|
||
} else {
|
||
requestLocationPermission()
|
||
.then((granted) => {
|
||
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) => {
|
||
console.log('位置权限:', res);
|
||
setLocationEnabled(res);
|
||
})
|
||
// 媒体库权限
|
||
getPermissions().then((res) => {
|
||
console.log('媒体库权限:', res);
|
||
setAlbumEnabled(res);
|
||
})
|
||
// 通知权限
|
||
checkNotificationPermission().then((res) => {
|
||
console.log('通知权限:', res);
|
||
setNotificationsEnabled(res);
|
||
})
|
||
}
|
||
}, [modalVisible])
|
||
|
||
return (
|
||
<>
|
||
<Modal
|
||
animationType="slide"
|
||
transparent={true}
|
||
visible={modalVisible}
|
||
onRequestClose={() => {
|
||
setModalVisible(false);
|
||
}}>
|
||
<Pressable
|
||
style={styles.centeredView}
|
||
onPress={() => setModalVisible(false)}>
|
||
<Pressable
|
||
style={styles.modalView}
|
||
onPress={(e) => e.stopPropagation()}>
|
||
<View style={styles.modalHeader}>
|
||
<Text style={{ opacity: 0 }}>Settings</Text>
|
||
<Text style={styles.modalTitle}>{t('generalSetting.allTitle', { ns: 'personal' })}</Text>
|
||
<TouchableOpacity onPress={() => setModalVisible(false)}>
|
||
<Text style={styles.closeButton}>×</Text>
|
||
</TouchableOpacity>
|
||
</View>
|
||
<ScrollView style={styles.modalContent} showsVerticalScrollIndicator={false}>
|
||
{/* 用户信息 */}
|
||
<UserInfo
|
||
userInfo={userInfo}
|
||
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} />
|
||
</Modal>
|
||
</>
|
||
);
|
||
};
|
||
|
||
const styles = StyleSheet.create({
|
||
centeredView: {
|
||
flex: 1,
|
||
justifyContent: 'flex-end',
|
||
backgroundColor: 'rgba(0,0,0,0.5)',
|
||
},
|
||
modalView: {
|
||
width: '100%',
|
||
height: '80%',
|
||
backgroundColor: 'white',
|
||
borderTopLeftRadius: 20,
|
||
borderTopRightRadius: 20,
|
||
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 SettingModal; |