2025-08-08 18:55:18 +08:00

192 lines
5.9 KiB
TypeScript

import UserSvg from "@/assets/icons/svg/ataver.svg";
import EditSvg from "@/assets/icons/svg/edit.svg";
import LocationSvg from "@/assets/icons/svg/location.svg";
import RefreshSvg from "@/assets/icons/svg/refresh.svg";
import { Address, User } from "@/types/user";
import { useRouter } from "expo-router";
import * as SecureStore from 'expo-secure-store';
import { useEffect, useRef } from "react";
import { useTranslation } from "react-i18next";
import { Animated, Easing, Image, Platform, StyleSheet, TouchableOpacity, View } from "react-native";
import { ThemedText } from "../ThemedText";
interface UserInfoProps {
userInfo: User;
getCurrentLocation: () => void;
isLoading: boolean;
isRefreshing: boolean;
currentLocation: Address;
setCurrentLocation: (location: Address) => void;
}
const UserInfo = (props: UserInfoProps) => {
const { userInfo, getCurrentLocation, isLoading, isRefreshing, currentLocation, setCurrentLocation } = props;
const router = useRouter();
const { t } = useTranslation();
// 获取本地存储的location
const getLocation = async () => {
if (Platform.OS === 'web') {
const location = localStorage.getItem('location');
if (location) {
setCurrentLocation(JSON.parse(location));
}
} else {
const location = await SecureStore.getItemAsync('location');
if (location) {
setCurrentLocation(JSON.parse(location));
}
}
};
// 添加旋转动画值
const spinValue = useRef(new Animated.Value(0)).current;
// 旋转动画
const startSpin = () => {
spinValue.setValue(0);
Animated.loop(
Animated.timing(spinValue, {
toValue: 1,
duration: 1000,
easing: Easing.linear,
useNativeDriver: true,
})
).start();
};
// 停止旋转
const stopSpin = () => {
spinValue.stopAnimation();
spinValue.setValue(0);
};
// 当开始加载时启动旋转
useEffect(() => {
if (isLoading) {
startSpin();
} else {
stopSpin();
}
}, [isLoading]);
// 在组件挂载时自动获取位置(可选)
useEffect(() => {
getLocation();
if (currentLocation && Object?.keys(currentLocation)?.length === 0) {
getCurrentLocation();
}
}, [])
return (
<View style={styles.container}>
<View style={styles.info}>
<ThemedText style={styles.nickname} type="sfPro">{userInfo?.nickname}</ThemedText>
<ThemedText style={styles.userId} type="inter">{t('generalSetting.userId', { ns: 'personal' })}{userInfo?.user_id}</ThemedText>
<View style={styles.location}>
<LocationSvg />
<ThemedText style={styles.userId} type="inter">
{currentLocation?.country}-{currentLocation?.city}-{currentLocation?.district}
</ThemedText>
<TouchableOpacity
onPress={getCurrentLocation}
disabled={isRefreshing}
style={styles.refreshContainer}
>
<Animated.View
style={[
styles.refreshIcon,
{
transform: [{
rotate: spinValue.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg']
})
}]
}
]}
>
<RefreshSvg color="#4C320C" width={16} height={16} />
</Animated.View>
</TouchableOpacity>
</View>
</View>
<View style={styles.avatar}>
{userInfo?.avatar_file_url
?
<Image
className='rounded-full'
style={{ width: 80, height: 80 }}
source={{ uri: userInfo?.avatar_file_url }}
/>
:
<UserSvg width={80} height={80} />
}
<TouchableOpacity style={styles.edit} onPress={() => {
// 携带参数跳转
router.push({
pathname: '/user-message',
params: {
username: "true"
}
});
}}>
<EditSvg />
</TouchableOpacity>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
backgroundColor: "#FAF9F6",
padding: 16,
borderRadius: 24,
},
info: {
flexDirection: 'column',
gap: 8,
},
nickname: {
fontSize: 20,
fontWeight: 'bold',
color: '#4C320C',
},
userId: {
fontSize: 12,
color: '#4C320C',
},
location: {
flexDirection: 'row',
alignItems: 'center',
marginTop: 4,
gap: 4
},
refreshContainer: {
width: 24,
height: 24,
justifyContent: 'center',
alignItems: 'center',
marginLeft: 4
},
refreshIcon: {
width: 16,
height: 16,
},
avatar: {
position: 'relative',
},
edit: {
position: 'absolute',
padding: 8,
backgroundColor: '#fff',
borderRadius: 50,
right: -5,
bottom: -5,
}
})
export default UserInfo;