341 lines
11 KiB
TypeScript
341 lines
11 KiB
TypeScript
// 地理位置逆编码
|
|
import { fetchApi } from '@/lib/server-api-util';
|
|
import * as ImagePicker from 'expo-image-picker';
|
|
import * as Location from 'expo-location';
|
|
import * as Notifications from 'expo-notifications';
|
|
import * as SecureStore from 'expo-secure-store';
|
|
import { Alert, Linking, Platform } from 'react-native';
|
|
|
|
interface Address {
|
|
id: number;
|
|
name: string;
|
|
// Add other address properties as needed
|
|
}
|
|
|
|
// 配置通知处理器
|
|
Notifications.setNotificationHandler({
|
|
handleNotification: async () => ({
|
|
shouldShowAlert: true,
|
|
shouldPlaySound: true,
|
|
shouldSetBadge: false,
|
|
shouldShowBanner: true,
|
|
shouldShowList: true,
|
|
}),
|
|
});
|
|
|
|
// 逆编码
|
|
export const reverseGeocode = async (latitude: number, longitude: number) => {
|
|
try {
|
|
const addressResults = await fetchApi<Address[]>(`/area/gecoding?latitude=${latitude}&longitude=${longitude}`);
|
|
console.log('地址:', addressResults);
|
|
for (let address of addressResults) {
|
|
console.log('地址:', address);
|
|
if (Platform.OS === 'web') {
|
|
localStorage.setItem('location', JSON.stringify(address));
|
|
} else {
|
|
SecureStore.setItemAsync('location', JSON.stringify(address));
|
|
}
|
|
return address;
|
|
}
|
|
} catch (error) {
|
|
console.log('逆地理编码失败:', error);
|
|
}
|
|
};
|
|
|
|
// 获取位置权限
|
|
export const getLocationPermission = async () => {
|
|
const { status } = await Location.getForegroundPermissionsAsync();
|
|
if (status !== 'granted') {
|
|
// Alert.alert('需要位置权限', '请允许访问位置以继续');
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
// 请求位置权限
|
|
export const requestLocationPermission = async () => {
|
|
try {
|
|
// 1. 先检查当前权限状态
|
|
const { status, canAskAgain } = await Location.getForegroundPermissionsAsync();
|
|
console.log('当前权限状态:', { status, canAskAgain });
|
|
console.log("canAskAgain", canAskAgain);
|
|
|
|
// 2. 如果已经有权限,直接返回
|
|
if (status === 'granted') {
|
|
return true;
|
|
}
|
|
|
|
// 3. 如果用户之前选择了"拒绝且不再询问"
|
|
if (status === 'denied' && !canAskAgain) {
|
|
// 显示提示,引导用户去设置
|
|
const openSettings = await new Promise(resolve => {
|
|
Alert.alert(
|
|
'需要位置权限',
|
|
'您之前拒绝了位置权限。要使用此功能,请在设置中启用位置权限。',
|
|
[
|
|
{
|
|
text: '取消',
|
|
style: 'cancel',
|
|
onPress: () => resolve(false)
|
|
},
|
|
{
|
|
text: '去设置',
|
|
onPress: () => resolve(true)
|
|
}
|
|
]
|
|
);
|
|
});
|
|
|
|
if (openSettings) {
|
|
// 打开应用设置
|
|
await Linking.openSettings();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// 4. 如果是第一次请求或可以再次询问,则请求权限
|
|
console.log('请求位置权限...');
|
|
const { status: newStatus } = await Location.requestForegroundPermissionsAsync();
|
|
console.log('新权限状态:', newStatus);
|
|
|
|
if (newStatus !== 'granted') {
|
|
Alert.alert('需要位置权限', '请允许访问位置以使用此功能');
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
} catch (error) {
|
|
console.error('请求位置权限时出错:', error);
|
|
Alert.alert('错误', '请求位置权限时出错');
|
|
return false;
|
|
}
|
|
};
|
|
|
|
// 获取媒体库权限
|
|
export const getPermissions = async () => {
|
|
if (Platform.OS !== 'web') {
|
|
const { status: mediaStatus } = await ImagePicker.getMediaLibraryPermissionsAsync();
|
|
if (mediaStatus !== 'granted') {
|
|
// Alert.alert('需要媒体库权限', '请允许访问媒体库以继续');
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
// 请求媒体库权限
|
|
export const requestPermissions = async () => {
|
|
if (Platform.OS !== 'web') {
|
|
const mediaStatus = await ImagePicker.requestMediaLibraryPermissionsAsync();
|
|
if (!mediaStatus.granted) {
|
|
// Alert.alert('需要媒体库权限', '请允许访问媒体库以继续');
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* 检查相册/媒体库权限
|
|
* @returns 返回权限状态对象
|
|
*/
|
|
export const checkMediaLibraryPermission = async (): Promise<{
|
|
hasPermission: boolean;
|
|
canAskAgain: boolean;
|
|
status: ImagePicker.PermissionStatus;
|
|
}> => {
|
|
if (Platform.OS === 'web') {
|
|
return { hasPermission: true, canAskAgain: true, status: 'granted' };
|
|
}
|
|
|
|
const { status, canAskAgain } = await ImagePicker.getMediaLibraryPermissionsAsync();
|
|
|
|
return {
|
|
hasPermission: status === 'granted',
|
|
canAskAgain,
|
|
status
|
|
};
|
|
};
|
|
|
|
/**
|
|
* 请求相册/媒体库权限
|
|
* @param showAlert 是否在无权限时显示提示
|
|
* @returns 返回是否已授权
|
|
*/
|
|
export const requestMediaLibraryPermission = async (showAlert: boolean = true): Promise<boolean> => {
|
|
if (Platform.OS === 'web') {
|
|
return true;
|
|
}
|
|
|
|
try {
|
|
// 1. 检查当前权限状态
|
|
const { status: existingStatus, canAskAgain } = await checkMediaLibraryPermission();
|
|
|
|
// 2. 如果已经有权限,直接返回
|
|
if (existingStatus === 'granted') {
|
|
return true;
|
|
}
|
|
|
|
// 3. 如果之前被拒绝且不能再次询问
|
|
if (existingStatus === 'denied' && !canAskAgain) {
|
|
if (showAlert) {
|
|
const openSettings = await new Promise<boolean>(resolve => {
|
|
Alert.alert(
|
|
'需要媒体库权限',
|
|
'您之前拒绝了媒体库访问权限。要选择照片,请在设置中启用媒体库权限。',
|
|
[
|
|
{ text: '取消', style: 'cancel', onPress: () => resolve(false) },
|
|
{ text: '去设置', onPress: () => resolve(true) }
|
|
]
|
|
);
|
|
});
|
|
|
|
if (openSettings) {
|
|
await Linking.openSettings();
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// 4. 请求权限
|
|
const { status: newStatus } = await ImagePicker.requestMediaLibraryPermissionsAsync();
|
|
|
|
if (newStatus !== 'granted' && showAlert) {
|
|
Alert.alert('需要媒体库权限', '请允许访问媒体库以方便后续操作');
|
|
}
|
|
|
|
return newStatus === 'granted';
|
|
} catch (error) {
|
|
console.error('请求媒体库权限时出错:', error);
|
|
if (showAlert) {
|
|
Alert.alert('错误', '请求媒体库权限时出错');
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
|
|
// 检查通知权限
|
|
export const checkNotificationPermission = async () => {
|
|
const { status } = await Notifications.getPermissionsAsync();
|
|
console.log('当前通知权限状态:', status);
|
|
|
|
return status === 'granted';
|
|
};
|
|
|
|
// 请求通知权限
|
|
export const requestNotificationPermission = async () => {
|
|
try {
|
|
// 1. 先检查当前权限状态
|
|
const { status, canAskAgain } = await Notifications.getPermissionsAsync();
|
|
console.log('当前通知权限状态:', { status, canAskAgain });
|
|
|
|
// 2. 如果已经有权限,直接返回
|
|
if (status === 'granted') {
|
|
return true;
|
|
}
|
|
|
|
// 3. 如果用户之前选择了"拒绝且不再询问"
|
|
if (status === 'denied' && !canAskAgain) {
|
|
// 显示提示,引导用户去设置
|
|
const openSettings = await new Promise(resolve => {
|
|
Alert.alert(
|
|
'需要通知权限',
|
|
'您之前拒绝了通知权限。要使用此功能,请在设置中启用通知权限。',
|
|
[
|
|
{
|
|
text: '取消',
|
|
style: 'cancel',
|
|
onPress: () => resolve(false)
|
|
},
|
|
{
|
|
text: '去设置',
|
|
onPress: () => resolve(true)
|
|
}
|
|
]
|
|
);
|
|
});
|
|
|
|
if (openSettings) {
|
|
// 打开应用设置
|
|
await Linking.openSettings();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// 4. 如果是第一次请求或可以再次询问,则请求权限
|
|
console.log('请求通知权限...');
|
|
const { status: newStatus } = await Notifications.requestPermissionsAsync();
|
|
console.log('新通知权限状态:', newStatus);
|
|
|
|
if (newStatus !== 'granted') {
|
|
Alert.alert('需要通知权限', '请允许通知以使用此功能');
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
} catch (error) {
|
|
console.error('请求通知权限时出错:', error);
|
|
Alert.alert('错误', '请求通知权限时出错');
|
|
return false;
|
|
}
|
|
};
|
|
|
|
// 发送本地通知的辅助函数
|
|
export const sendLocalNotification = async (title: string, body: string, data: Record<string, any> = {}) => {
|
|
try {
|
|
const hasPermission = await checkNotificationPermission();
|
|
if (!hasPermission) {
|
|
const granted = await requestNotificationPermission();
|
|
if (!granted) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
await Notifications.scheduleNotificationAsync({
|
|
content: {
|
|
title,
|
|
body,
|
|
data,
|
|
priority: 'high',
|
|
},
|
|
trigger: null, // 立即触发
|
|
});
|
|
|
|
return true;
|
|
} catch (error) {
|
|
console.error('发送通知时出错:', error);
|
|
return false;
|
|
}
|
|
};
|
|
|
|
|
|
// 获取定位信息 -- 最子集元素
|
|
export function findInnermostElement(data: any[], targetName: string): { name: string; value: any } | null {
|
|
let result: { name: string; value: any } | null = null;
|
|
|
|
function search(nodes: any[]): boolean {
|
|
for (const node of nodes) {
|
|
if (node.name === targetName) {
|
|
result = { name: node.name, value: node.value };
|
|
// Keep searching to see if there's a deeper match
|
|
let foundDeeper = false;
|
|
if (node.children && node.children.length > 0) {
|
|
foundDeeper = search(node.children);
|
|
}
|
|
// If no deeper match was found, this is the innermost one
|
|
if (!foundDeeper) {
|
|
return true;
|
|
}
|
|
} else if (node.children && node.children.length > 0) {
|
|
const found = search(node.children);
|
|
if (found) return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
search(data);
|
|
return result;
|
|
} |